import fs from 'node:fs'; import path from 'node:path'; import { expectFixture as expect, test } from '../helpers/test-fixtures'; const guestCount = 15; const uploadFixturePath = ensureUploadFixture(); test.describe('Guest PWA multi-guest journey', () => { test('15 guests can onboard, explore tasks, trigger upload review, and reach gallery', async ({ browser, fetchJoinToken, }) => { const eventSlug = process.env.E2E_GUEST_EVENT_SLUG; test.skip(!eventSlug, 'Set E2E_GUEST_EVENT_SLUG to point the guest suite at an existing event.'); const joinToken = await fetchJoinToken({ slug: eventSlug!, ensureActive: true }); const baseUrl = (process.env.E2E_GUEST_BASE_URL ?? 'http://localhost:8000').replace(/\/+$/, ''); const landingUrl = `${baseUrl}/event`; const eventBaseUrl = `${baseUrl}/e/${joinToken.token}`; for (let index = 0; index < guestCount; index += 1) { const context = await browser.newContext(); const page = await context.newPage(); const guestName = `Gast ${index + 1}`; await page.goto(landingUrl, { waitUntil: 'domcontentloaded' }); await page.getByPlaceholder(/Event-Code eingeben|Enter event code/i).fill(joinToken.token); await page.getByRole('button', { name: /Event beitreten|Join event/i }).click(); await completeProfileSetup(page, guestName, joinToken.token); await page.goto(`${eventBaseUrl}/tasks`, { waitUntil: 'domcontentloaded' }); await expect(page.locator('body')).toContainText(/Aufgaben|Tasks/); await page.goto(`${eventBaseUrl}/upload`, { waitUntil: 'domcontentloaded' }); const fileInput = page.locator('input[type="file"]'); await expect(fileInput).toBeVisible({ timeout: 15_000 }); await fileInput.setInputFiles(uploadFixturePath); await expect( page.getByRole('button', { name: /Nochmal aufnehmen|Retake/i }) ).toBeVisible({ timeout: 15_000 }); await page.getByRole('button', { name: /Nochmal aufnehmen|Retake/i }).click(); // Simulate offline queue testing for the last five guests. if (index >= guestCount - 5) { await context.setOffline(true); await page.goto(`${eventBaseUrl}/tasks`, { waitUntil: 'domcontentloaded' }).catch(() => undefined); await context.setOffline(false); } await page.goto(`${eventBaseUrl}/gallery`, { waitUntil: 'domcontentloaded' }); await expect(page.locator('body')).toContainText(/Galerie|Gallery/); const likeButtons = page.getByLabel(/Foto liken|Like photo/i); if (await likeButtons.count()) { await likeButtons.first().click(); } await context.close(); } }); }); async function completeProfileSetup(page: import('@playwright/test').Page, guestName: string, token: string) { await page.waitForLoadState('domcontentloaded'); if (page.url().includes('/setup/')) { await page.getByLabel(/Dein Name|Your name/i).fill(guestName); await page.getByRole('button', { name: /Los gehts|Let's go/i }).click(); } await page.waitForURL(new RegExp(`/e/${token}`), { timeout: 60_000, }); } function ensureUploadFixture(): string { const fixtureDir = path.join(process.cwd(), 'tests/ui/guest/fixtures'); const fixturePath = path.join(fixtureDir, 'sample-upload.png'); if (!fs.existsSync(fixtureDir)) { fs.mkdirSync(fixtureDir, { recursive: true }); } if (!fs.existsSync(fixturePath)) { const png1x1 = Buffer.from( 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg==', 'base64' ); fs.writeFileSync(fixturePath, png1x1); } return fixturePath; }