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(); }); });