import type { Page } from '@playwright/test'; import { test, expectFixture as expect } from '../helpers/test-fixtures'; const futureDate = (daysAhead = 10): string => { const date = new Date(); date.setDate(date.getDate() + daysAhead); return date.toISOString().slice(0, 10); }; async function ensureOnDashboard(page: Page): Promise { await page.goto('/event-admin/dashboard'); await page.waitForLoadState('networkidle'); if (page.url().includes('/event-admin/welcome')) { const directButton = page.getByRole('button', { name: /Direkt zum Dashboard/i }); if (await directButton.isVisible()) { await directButton.click(); await page.waitForURL(/\/event-admin\/dashboard$/, { timeout: 15_000 }); } } } test.describe('Tenant Admin PWA – end-to-end coverage', () => { test.beforeEach(async ({ signInTenantAdmin }) => { await signInTenantAdmin(); }); test('dashboard highlights core stats and quick actions', async ({ page }) => { await ensureOnDashboard(page); await expect(page.getByRole('heading', { name: /Hallo/i })).toBeVisible(); await expect(page.getByRole('button', { name: /Neues Event/i })).toBeVisible(); await expect(page.getByText(/Schnellaktionen/i)).toBeVisible(); await expect(page.getByText(/Kommende Events/i)).toBeVisible(); }); test('event creation flow and detail subsections', async ({ page }) => { const eventName = `Playwright Event ${Date.now()}`; const eventDate = futureDate(14); await page.goto('/event-admin/events/new'); await page.waitForLoadState('networkidle'); await expect(page.getByRole('heading', { name: /Eventdetails/i })).toBeVisible(); await page.getByLabel(/Eventname/i).fill(eventName); await page.getByLabel(/Datum/i).fill(eventDate); const eventTypeTrigger = page.getByRole('combobox', { name: /Event-Typ/i }); await eventTypeTrigger.click(); const firstOption = page.getByRole('option').first(); await expect(firstOption).toBeVisible({ timeout: 5_000 }); await firstOption.click(); await page.getByRole('button', { name: /^Speichern/i }).click(); await expect(page).toHaveURL(/\/event-admin\/events\/[a-z0-9-]+$/, { timeout: 20_000 }); const createdSlug = page.url().split('/').pop() ?? ''; await expect(page.getByRole('heading', { name: /Eventdetails/i })).toBeVisible(); await page.goto('/event-admin/events'); await page.waitForLoadState('networkidle'); await expect(page.getByText(eventName, { exact: false })).toBeVisible(); await page.goto(`/event-admin/events/${createdSlug}/photos`); await expect(page.getByRole('heading', { name: /Fotos moderieren/i })).toBeVisible(); await expect(page.getByText(/Noch keine Fotos vorhanden/i)).toBeVisible(); await page.goto(`/event-admin/events/${createdSlug}/members`); await expect(page.getByRole('heading', { name: /Event-Mitglieder/i })).toBeVisible(); await page.goto(`/event-admin/events/${createdSlug}/tasks`); await expect(page.getByRole('heading', { name: /Event-Tasks/i })).toBeVisible(); await expect(page.getByText(/Noch keine Tasks zugewiesen/i)).toBeVisible(); }); test('task library allows creating custom tasks', async ({ page }) => { await page.goto('/event-admin/tasks'); await page.waitForLoadState('networkidle'); await expect(page.getByRole('heading', { name: /Task Bibliothek/i })).toBeVisible(); const taskTitle = `Playwright Task ${Date.now()}`; await page.getByRole('button', { name: /^Neu$/i }).click(); await page.getByLabel(/Titel/i).fill(taskTitle); await page.getByLabel(/Beschreibung/i).fill('Automatisierter Testfall'); await page.getByRole('button', { name: /^Speichern$/i }).click(); await expect(page.getByText(taskTitle)).toBeVisible({ timeout: 10_000 }); await page.goto('/event-admin/task-collections'); await page.waitForLoadState('networkidle'); await expect(page.getByRole('heading', { name: /Aufgabenvorlagen/i })).toBeVisible(); }); test('supporting sections (emotions, billing, settings) load successfully', async ({ page }) => { await page.goto('/event-admin/emotions'); await page.waitForLoadState('networkidle'); await expect(page.getByRole('heading', { name: /Emotionen/i })).toBeVisible(); await page.goto('/event-admin/billing'); await page.waitForLoadState('networkidle'); await expect(page.getByRole('heading', { name: /Pakete & Abrechnung/i })).toBeVisible(); await page.goto('/event-admin/settings'); await page.waitForLoadState('networkidle'); await expect(page.getByRole('heading', { name: /Einstellungen/i })).toBeVisible(); }); test('wedding event workflow assigns tasks and exposes join token', async ({ page, fetchJoinToken }) => { const eventName = `Playwright Hochzeit ${Date.now()}`; const eventDate = futureDate(21); await page.goto('/event-admin/events/new'); await page.waitForLoadState('networkidle'); await page.getByLabel(/Eventname/i).fill(eventName); await page.getByLabel(/Datum/i).fill(eventDate); const eventTypeCombo = page.getByRole('combobox', { name: /Event-Typ/i }); await eventTypeCombo.click(); const weddingOption = page.getByRole('option', { name: /Hochzeit|Wedding/i }).first(); await expect(weddingOption).toBeVisible(); await weddingOption.click(); await page.getByRole('button', { name: /^Speichern/i }).click(); await page.waitForURL(/\/event-admin\/events\/[a-z0-9-]+$/i, { timeout: 20_000 }); const createdSlug = page.url().split('/').pop() ?? ''; await expect(page.getByText(/Hochzeit|Wedding/i)).toBeVisible(); await page.goto(`/event-admin/events/${createdSlug}/tasks`); await expect(page.getByRole('heading', { name: /Event-Tasks/i })).toBeVisible(); const librarySection = page .locator('section') .filter({ hasText: /Tasks aus Bibliothek hinzufügen|Add tasks/i }) .first(); const availableTaskLabels = librarySection.locator('label'); const availableCount = await availableTaskLabels.count(); test.skip(availableCount === 0, 'No task library entries available to assign'); const firstLabel = availableTaskLabels.first(); const taskTitle = ((await firstLabel.locator('p').first().textContent()) ?? '').trim(); await firstLabel.click(); await page .getByRole('button', { name: /Ausgewählte Tasks zuweisen|Assign selected tasks/i }) .click(); await expect( page.locator('section').filter({ hasText: /Zugeordnete Tasks|Assigned tasks/i }).getByText(taskTitle, { exact: false }), ).toBeVisible({ timeout: 15_000 }); const joinToken = await fetchJoinToken({ slug: createdSlug }); expect(joinToken.token).toBeTruthy(); expect(joinToken.join_url).toContain(joinToken.token); expect(joinToken.qr_svg).toContain('