277 lines
8.4 KiB
TypeScript
277 lines
8.4 KiB
TypeScript
import { test, expect } from '@playwright/test';
|
|
|
|
const EVENT_TOKEN = 'limit-event';
|
|
|
|
function nowIso(): string {
|
|
return new Date().toISOString();
|
|
}
|
|
|
|
test.describe('Guest PWA limit experiences', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await page.addInitScript(
|
|
({ token }) => {
|
|
try {
|
|
window.localStorage.setItem(`guestName_${token}`, 'Playwright Gast');
|
|
window.localStorage.setItem(`guestCameraPrimerDismissed_${token}`, '1');
|
|
} catch (error) {
|
|
console.warn('Failed to seed guest storage', error);
|
|
}
|
|
|
|
if (!navigator.mediaDevices) {
|
|
Object.defineProperty(navigator, 'mediaDevices', {
|
|
configurable: true,
|
|
value: {},
|
|
});
|
|
}
|
|
|
|
navigator.mediaDevices.getUserMedia = () => Promise.resolve(new MediaStream());
|
|
},
|
|
{ token: EVENT_TOKEN }
|
|
);
|
|
|
|
const timestamp = nowIso();
|
|
|
|
await page.route(`**/api/v1/events/${EVENT_TOKEN}`, async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
id: 1,
|
|
slug: EVENT_TOKEN,
|
|
name: 'Limit Experience Event',
|
|
default_locale: 'de',
|
|
created_at: timestamp,
|
|
updated_at: timestamp,
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route(`**/api/v1/events/${EVENT_TOKEN}/tasks`, async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify([
|
|
{
|
|
id: 1,
|
|
title: 'Playwright Mission',
|
|
description: 'Test mission for upload limits',
|
|
instructions: 'Mach ein Testfoto',
|
|
duration: 2,
|
|
},
|
|
]),
|
|
});
|
|
});
|
|
|
|
await page.route(`**/api/v1/events/${EVENT_TOKEN}/stats`, async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
online_guests: 5,
|
|
tasks_solved: 12,
|
|
latest_photo_at: timestamp,
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.route(`**/api/v1/events/${EVENT_TOKEN}/photos**`, async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
data: [
|
|
{
|
|
id: 101,
|
|
file_path: '/photos/101.jpg',
|
|
thumbnail_path: '/photos/101-thumb.jpg',
|
|
created_at: timestamp,
|
|
likes_count: 3,
|
|
},
|
|
],
|
|
latest_photo_at: timestamp,
|
|
}),
|
|
});
|
|
});
|
|
});
|
|
|
|
test('shows limit warnings and countdown before limits are reached', async ({ page }) => {
|
|
const expiresAt = new Date(Date.now() + 2 * 86_400_000).toISOString();
|
|
|
|
await page.route(`**/api/v1/events/${EVENT_TOKEN}/package`, async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
id: 42,
|
|
event_id: 1,
|
|
used_photos: 95,
|
|
expires_at: expiresAt,
|
|
package: {
|
|
id: 77,
|
|
name: 'Starter',
|
|
max_photos: 100,
|
|
max_guests: 150,
|
|
gallery_days: 30,
|
|
},
|
|
limits: {
|
|
photos: {
|
|
limit: 100,
|
|
used: 95,
|
|
remaining: 5,
|
|
percentage: 95,
|
|
state: 'warning',
|
|
threshold_reached: 95,
|
|
next_threshold: 100,
|
|
thresholds: [80, 95, 100],
|
|
},
|
|
guests: null,
|
|
gallery: {
|
|
state: 'warning',
|
|
expires_at: expiresAt,
|
|
days_remaining: 2,
|
|
warning_thresholds: [7, 1],
|
|
warning_triggered: 2,
|
|
warning_sent_at: null,
|
|
expired_notified_at: null,
|
|
},
|
|
can_upload_photos: true,
|
|
can_add_guests: true,
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.goto(`/e/${EVENT_TOKEN}/upload?task=1`);
|
|
await expect(page.getByText(/Nur noch 5 von 100 Fotos möglich/i)).toBeVisible();
|
|
await expect(page.getByText(/Galerie läuft in 2 Tagen ab/i)).toBeVisible();
|
|
|
|
await page.goto(`/e/${EVENT_TOKEN}/gallery`);
|
|
await expect(page.getByText(/Noch 2 Tage online/i)).toBeVisible();
|
|
await expect(page.getByText(/Nur noch 5 von 100 Fotos möglich/i)).toBeVisible();
|
|
await expect(page.getByText(/Galerie läuft in 2 Tagen ab/i)).toBeVisible();
|
|
await expect(page.getByRole('button', { name: /Letzte Fotos hochladen/i })).toBeVisible();
|
|
});
|
|
|
|
test('marks uploads as blocked and highlights expired gallery state', async ({ page }) => {
|
|
const expiredAt = new Date(Date.now() - 86_400_000).toISOString();
|
|
|
|
await page.route(`**/api/v1/events/${EVENT_TOKEN}/package`, async (route) => {
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
id: 43,
|
|
event_id: 1,
|
|
used_photos: 100,
|
|
expires_at: expiredAt,
|
|
package: {
|
|
id: 77,
|
|
name: 'Starter',
|
|
max_photos: 100,
|
|
max_guests: 150,
|
|
gallery_days: 30,
|
|
},
|
|
limits: {
|
|
photos: {
|
|
limit: 100,
|
|
used: 100,
|
|
remaining: 0,
|
|
percentage: 100,
|
|
state: 'limit_reached',
|
|
threshold_reached: 100,
|
|
next_threshold: null,
|
|
thresholds: [80, 95, 100],
|
|
},
|
|
guests: null,
|
|
gallery: {
|
|
state: 'expired',
|
|
expires_at: expiredAt,
|
|
days_remaining: 0,
|
|
warning_thresholds: [7, 1],
|
|
warning_triggered: 0,
|
|
warning_sent_at: null,
|
|
expired_notified_at: expiredAt,
|
|
},
|
|
can_upload_photos: false,
|
|
can_add_guests: true,
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.goto(`/e/${EVENT_TOKEN}/upload?task=1`);
|
|
await expect(page.getByText(/Upload-Limit erreicht/i)).toBeVisible();
|
|
|
|
await page.goto(`/e/${EVENT_TOKEN}/gallery`);
|
|
await expect(page.getByText(/Galerie abgelaufen/i)).toBeVisible();
|
|
await expect(page.getByText(/Die Galerie ist abgelaufen\. Uploads sind nicht mehr möglich\./i)).toBeVisible();
|
|
});
|
|
|
|
test('blocks uploads and guest access once all limits are exhausted', async ({ page }) => {
|
|
await page.route(`**/api/v1/events/${EVENT_TOKEN}/package`, async (route) => {
|
|
const exhaustedAt = new Date().toISOString();
|
|
|
|
await route.fulfill({
|
|
status: 200,
|
|
contentType: 'application/json',
|
|
body: JSON.stringify({
|
|
id: 44,
|
|
event_id: 1,
|
|
used_photos: 120,
|
|
expires_at: exhaustedAt,
|
|
package: {
|
|
id: 90,
|
|
name: 'Starter',
|
|
max_photos: 120,
|
|
max_guests: 3,
|
|
gallery_days: 30,
|
|
},
|
|
limits: {
|
|
photos: {
|
|
limit: 120,
|
|
used: 120,
|
|
remaining: 0,
|
|
percentage: 100,
|
|
state: 'limit_reached',
|
|
threshold_reached: 120,
|
|
next_threshold: null,
|
|
thresholds: [80, 95, 120],
|
|
},
|
|
guests: {
|
|
limit: 3,
|
|
used: 3,
|
|
remaining: 0,
|
|
percentage: 100,
|
|
state: 'limit_reached',
|
|
threshold_reached: 3,
|
|
next_threshold: null,
|
|
thresholds: [2, 3],
|
|
},
|
|
gallery: {
|
|
state: 'ok',
|
|
expires_at: exhaustedAt,
|
|
days_remaining: 10,
|
|
warning_thresholds: [7, 1],
|
|
warning_triggered: null,
|
|
warning_sent_at: null,
|
|
expired_notified_at: null,
|
|
},
|
|
can_upload_photos: false,
|
|
can_add_guests: false,
|
|
},
|
|
}),
|
|
});
|
|
});
|
|
|
|
await page.goto(`/e/${EVENT_TOKEN}/upload?task=1`);
|
|
|
|
await expect(page.getByText(/Upload-Limit erreicht/i)).toBeVisible();
|
|
await expect(page.getByRole('button', { name: /Upload/i })).toBeDisabled();
|
|
await expect(page.getByText(/Limit erreicht/i)).toBeVisible();
|
|
|
|
await page.goto(`/e/${EVENT_TOKEN}/gallery`);
|
|
await expect(page.getByText(/Upload-Limit erreicht/i)).toBeVisible();
|
|
await expect(page.getByText(/Limit erreicht/i)).toBeVisible();
|
|
});
|
|
});
|