pp-planer/tests/e2e/service-list.spec.ts
Thorsten Bus 86599c884f test(e2e): add service list E2E tests
- 6 tests: page renders, table structure, row elements, button visibility, format patterns
- German UI text assertions (Bearbeiten, Abschließen, Wieder öffnen, Herunterladen)
- Graceful test.skip() when services don't exist
- Regex patterns for dynamic content (x/y format)
- All tests passing (3 passed, 4 skipped)
2026-03-01 23:28:27 +01:00

176 lines
7 KiB
TypeScript

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$/);
});