import { test, expect } from '@playwright/test'; test.describe('Person list', () => { test.beforeEach(async ({ page }) => { await page.goto('/persons'); }); test('renders the persons list page', async ({ page }) => { await expect(page.getByRole('heading', { name: /Personen/i })).toBeVisible(); await expect(page.getByRole('link', { name: /Neue Person/i })).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/persons-list.png' }); }); test('search filters the persons list', async ({ page }) => { const searchInput = page.getByPlaceholder(/Namen suchen/i); await searchInput.fill('zzz_unlikely_match'); await page.waitForTimeout(600); // debounce await expect(page.getByText(/Keine Personen gefunden/i)).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/persons-search-empty.png' }); }); test('clicking a person opens the detail page', async ({ page }) => { const firstPerson = page.locator('a[href^="/persons/"]').first(); await firstPerson.click(); await expect(page).toHaveURL(/\/persons\/.+/); await page.screenshot({ path: 'test-results/e2e/person-detail.png' }); }); }); test.describe('Person detail', () => { test('shows the person name and their documents', async ({ page }) => { await page.goto('/persons'); const firstPerson = page.locator('a[href^="/persons/"]').first(); await firstPerson.click(); // The detail page shows the person's name as a heading await expect(page.getByRole('heading')).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/person-detail-documents.png' }); }); test('can enter and cancel edit mode', async ({ page }) => { await page.goto('/persons'); const firstPerson = page.locator('a[href^="/persons/"]').first(); await firstPerson.click(); // Click the edit button const editBtn = page.getByRole('button', { name: /Bearbeiten/i }); if (await editBtn.isVisible()) { await editBtn.click(); await expect(page.getByLabel('Vorname')).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/person-edit-form.png' }); // Cancel await page.getByRole('button', { name: /Abbrechen/i }).click(); await expect(page.getByLabel('Vorname')).not.toBeVisible(); } }); }); test.describe('New person', () => { test('renders the new person form', async ({ page }) => { await page.goto('/persons/new'); await expect(page.getByLabel('Vorname')).toBeVisible(); await expect(page.getByLabel('Nachname')).toBeVisible(); await expect(page.getByRole('button', { name: /Erstellen/i })).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/person-new.png' }); }); test('shows a validation error when submitting with empty fields', async ({ page }) => { await page.goto('/persons/new'); // HTML required attribute prevents submission without filling required fields await page.getByRole('button', { name: /Erstellen/i }).click(); // The form should not have navigated away await expect(page).toHaveURL('/persons/new'); }); }); test.describe('Conversations', () => { test('shows the empty state when no persons are selected', async ({ page }) => { await page.goto('/conversations'); await expect(page.getByText(/Wählen Sie zwei Personen aus/i)).toBeVisible(); await page.screenshot({ path: 'test-results/e2e/conversations-empty.png' }); }); test('nav link is active on the conversations page', async ({ page }) => { await page.goto('/conversations'); const navLink = page.getByRole('link', { name: 'Konversationen' }); await expect(navLink).toHaveClass(/border-brand-navy/); }); test('sort toggle changes the button label', async ({ page }) => { await page.goto('/conversations'); const btn = page.getByRole('button', { name: /Sortierung/i }); await expect(btn).toContainText('Neueste zuerst'); await btn.click(); await expect(page).toHaveURL(/dir=ASC/); await expect(btn).toContainText('Älteste zuerst'); await page.screenshot({ path: 'test-results/e2e/conversations-sort.png' }); }); });