import { test, expect } from '@playwright/test'; // Test 1: Services page renders with correct heading test('services page renders with correct heading', async ({ page }) => { await page.goto('/services'); await page.waitForLoadState('networkidle'); // Verify we're on services page await expect(page).toHaveURL(/.*services/); // Verify heading is visible (use role selector to avoid ambiguity) await expect(page.getByRole('heading', { name: 'Services' })).toBeVisible(); // Verify description text is visible await expect(page.getByText('Hier siehst du alle heutigen und kommenden Services.')).toBeVisible(); }); // Test 2: Service list shows table structure (if services exist) or empty state test('service list shows table structure or empty state', async ({ page }) => { await page.goto('/services'); await page.waitForLoadState('networkidle'); // Check if services exist or empty state is shown const emptyState = page.getByTestId('service-list-empty'); const serviceTable = page.getByTestId('service-list-table'); const hasServices = await serviceTable.isVisible().catch(() => false); const isEmpty = await emptyState.isVisible().catch(() => false); // Either table exists OR empty state exists (but not both) expect(hasServices || isEmpty).toBe(true); }); // Test 3: Service row shows structural elements (title, date, status indicators) test('service row shows title, date, and status indicators', async ({ page }) => { await page.goto('/services'); await page.waitForLoadState('networkidle'); // Check if services exist const serviceTable = page.getByTestId('service-list-table'); const hasServices = await serviceTable.isVisible().catch(() => false); if (!hasServices) { // Skip test if no services exist test.skip(); } // Get first service row const firstServiceRow = page.locator('[data-testid^="service-list-row-"]').first(); await expect(firstServiceRow).toBeVisible(); // Verify status indicators exist with correct format patterns // Pattern: "x/y Songs zugeordnet" const songMappingStatus = firstServiceRow.locator('text=/\\d+\\/\\d+ Songs zugeordnet/'); await expect(songMappingStatus).toBeVisible(); // Pattern: "x/y Arrangements geprueft" const arrangementStatus = firstServiceRow.locator('text=/\\d+\\/\\d+ Arrangements geprueft/'); await expect(arrangementStatus).toBeVisible(); // Verify other status indicators exist const sermonSlidesStatus = firstServiceRow.locator('text=Predigtfolien'); await expect(sermonSlidesStatus).toBeVisible(); const infoSlidesStatus = firstServiceRow.locator('text=/\\d+ Infofolien/'); await expect(infoSlidesStatus).toBeVisible(); const finalizedStatus = firstServiceRow.locator('text=Abgeschlossen am'); await expect(finalizedStatus).toBeVisible(); }); // Test 4: Unfinalized service shows "Bearbeiten" and "Abschließen" buttons test('unfinalized service shows edit and finalize buttons', async ({ page }) => { await page.goto('/services'); await page.waitForLoadState('networkidle'); // Check if services exist const serviceTable = page.getByTestId('service-list-table'); const hasServices = await serviceTable.isVisible().catch(() => false); if (!hasServices) { // Skip test if no services exist test.skip(); } // Find first unfinalized service (one with edit button) const editButton = page.getByTestId('service-list-edit-button').first(); const editButtonVisible = await editButton.isVisible().catch(() => false); if (!editButtonVisible) { // Skip test if no unfinalized services exist test.skip(); } // Get the parent row of the edit button const serviceRow = editButton.locator('xpath=ancestor::tr'); // Verify "Bearbeiten" button exists and is visible const bearbeitenButton = serviceRow.getByTestId('service-list-edit-button'); await expect(bearbeitenButton).toBeVisible(); await expect(bearbeitenButton).toContainText('Bearbeiten'); // Verify "Abschließen" button exists and is visible const abschliessenButton = serviceRow.getByTestId('service-list-finalize-button'); await expect(abschliessenButton).toBeVisible(); await expect(abschliessenButton).toContainText('Abschließen'); }); // Test 5: Finalized service shows "Wieder öffnen" and "Herunterladen" buttons test('finalized service shows reopen and download buttons', async ({ page }) => { await page.goto('/services'); await page.waitForLoadState('networkidle'); // Check if services exist const serviceTable = page.getByTestId('service-list-table'); const hasServices = await serviceTable.isVisible().catch(() => false); if (!hasServices) { // Skip test if no services exist test.skip(); } // Find first finalized service (one with reopen button) const reopenButton = page.getByTestId('service-list-reopen-button').first(); const reopenButtonVisible = await reopenButton.isVisible().catch(() => false); if (!reopenButtonVisible) { // Skip test if no finalized services exist test.skip(); } // Get the parent row of the reopen button const serviceRow = reopenButton.locator('xpath=ancestor::tr'); // Verify "Wieder öffnen" button exists and is visible const wiederOeffnenButton = serviceRow.getByTestId('service-list-reopen-button'); await expect(wiederOeffnenButton).toBeVisible(); await expect(wiederOeffnenButton).toContainText('Wieder öffnen'); // Verify "Herunterladen" button exists and is visible const herunterladenButton = serviceRow.getByTestId('service-list-download-button'); await expect(herunterladenButton).toBeVisible(); await expect(herunterladenButton).toContainText('Herunterladen'); }); // Test 6: Status indicators show correct format patterns test('status indicators display correct format patterns', async ({ page }) => { await page.goto('/services'); await page.waitForLoadState('networkidle'); // Check if services exist const serviceTable = page.getByTestId('service-list-table'); const hasServices = await serviceTable.isVisible().catch(() => false); if (!hasServices) { // Skip test if no services exist test.skip(); } // Get first service row const firstServiceRow = page.locator('[data-testid^="service-list-row-"]').first(); await expect(firstServiceRow).toBeVisible(); // Verify "x/y Songs zugeordnet" format const songMapping = firstServiceRow.locator('text=/\\d+\\/\\d+ Songs zugeordnet/'); await expect(songMapping).toBeVisible(); const songMappingText = await songMapping.textContent(); expect(songMappingText).toMatch(/^\d+\/\d+ Songs zugeordnet$/); // Verify "x/y Arrangements geprueft" format const arrangements = firstServiceRow.locator('text=/\\d+\\/\\d+ Arrangements geprueft/'); await expect(arrangements).toBeVisible(); const arrangementsText = await arrangements.textContent(); expect(arrangementsText).toMatch(/^\d+\/\d+ Arrangements geprueft$/); });