/** QA-1: Becken + Kontakte (FEAT-2) — CRUD, Belegung, 409-Konfliktpfad. */ import { acceptNextDialog, de, expect, gotoSection, skipUnlessMock, test, uniqueName } from './fixtures' const tb = de.pages.becken const tk = de.pages.kontakte test.describe('Becken', () => { test('Belegung: Detail zeigt Bewohner mit Link zur Tierakte', async ({ page }) => { skipUnlessMock() await page.goto('/gehege') await page.getByRole('link', { name: /Großbecken/ }).click() await expect(page.getByRole('heading', { name: 'Großbecken' })).toBeVisible() await expect(page.getByText(`2 ${tb.occupancy.countMany}`)).toBeVisible() await expect(page.getByText(tb.occupancy.title)).toBeVisible() await page.getByRole('link', { name: /Krümel/ }).click() await expect(page.getByText(de.pages.gerbils.detail.masterData)).toBeVisible() }) test('Löschen eines belegten Beckens meldet den 409-Konflikt auf Deutsch', async ({ page }) => { skipUnlessMock() await page.goto('/gehege') await page.getByRole('link', { name: /Großbecken/ }).click() acceptNextDialog(page) await page.getByRole('button', { name: tb.delete.action }).click() await expect(page.getByText(tb.delete.conflict)).toBeVisible() }) test('Becken anlegen, bearbeiten und (leer) löschen', async ({ page }) => { await gotoSection(page, de.nav.enclosures) await page.getByRole('link', { name: tb.newButton }).click() // Pflichtfeld-Validierung await page.getByRole('button', { name: tb.form.save, exact: true }).click() await expect(page.getByText(tb.validation.nameRequired)).toBeVisible() const name = uniqueName('Becken') await page.getByLabel(`${tb.fields.name} *`).fill(name) await page.getByLabel(tb.fields.notes).fill('Übergangsbecken') await page.getByRole('button', { name: tb.form.save, exact: true }).click() await expect(page.getByRole('heading', { name })).toBeVisible() await expect(page.getByText(tb.occupancy.empty)).toBeVisible() // Leeres Becken löschen -> zurück zur Liste acceptNextDialog(page) await page.getByRole('button', { name: tb.delete.action }).click() await expect(page.getByRole('heading', { name: tb.title, exact: true })).toBeVisible() await expect(page.getByRole('link', { name })).toBeHidden() }) }) test.describe('Kontakte', () => { test('Detail zeigt verknüpfte Tiere mit Rollen-Beschriftung', async ({ page }) => { skipUnlessMock() await page.goto('/kontakte') await page.getByRole('link', { name: /Zoohandlung Meier/ }).click() await expect(page.getByRole('heading', { name: 'Zoohandlung Meier' })).toBeVisible() await expect(page.getByText(tk.linked.title)).toBeVisible() // "Herkunft von Krümel" await expect(page.getByText(`${tk.linked.originRole} Krümel`)).toBeVisible() }) test('Löschen eines referenzierten Kontakts meldet den 409-Konflikt auf Deutsch', async ({ page }) => { skipUnlessMock() await page.goto('/kontakte') await page.getByRole('link', { name: /Familie Huber/ }).click() // Pauli (abgegeben) referenziert Familie Huber als Abnehmer await expect(page.getByText(`${tk.linked.receiverRole} Pauli`)).toBeVisible() acceptNextDialog(page) await page.getByRole('button', { name: tk.delete.action }).click() await expect(page.getByText(tk.delete.conflict)).toBeVisible() }) test('Kontakt anlegen und (unverknüpft) wieder löschen', async ({ page }) => { await gotoSection(page, de.nav.contacts) // Explizit auf die Listenüberschrift warten, bevor wir klicken — verhindert den // CI-Flake, der auftrat wenn der Vite-Server unter paralleler Last noch nicht // alle Routen der Seite gerendert hatte (gotoSection klickt nur den Nav-Link, // wartet aber nicht auf den vollständigen Seitenaufbau). await expect(page.getByRole('heading', { name: tk.title, exact: true })).toBeVisible() await page.getByRole('link', { name: tk.newButton }).click() await expect(page.getByRole('heading', { name: tk.form.createTitle })).toBeVisible() const name = uniqueName('Kontakt') await page.getByLabel(`${tk.fields.name} *`).fill(name) // FEAT-13: strukturierte Felder statt Freitext-Kontaktdaten await page.getByLabel(tk.fields.email).fill('test@example.de') await page.getByLabel(tk.fields.phone).fill('0151 9876543') await page.getByRole('button', { name: tk.form.save, exact: true }).click() await expect(page.getByRole('heading', { name })).toBeVisible() await expect(page.getByText(tk.linked.empty)).toBeVisible() // die gespeicherten Felder erscheinen auf der Detailseite await expect(page.getByText('test@example.de')).toBeVisible() await expect(page.getByText('0151 9876543')).toBeVisible() acceptNextDialog(page) await page.getByRole('button', { name: tk.delete.action }).click() await expect(page.getByRole('heading', { name: tk.title, exact: true })).toBeVisible() }) // FEAT-13: Abdeckung der neuen strukturierten Kontaktfelder test('Detail zeigt E-Mail, Telefon und Adresse mit deutschen Beschriftungen', async ({ page }) => { skipUnlessMock() await page.goto('/kontakte') await page.getByRole('link', { name: /Zoohandlung Meier/ }).click() await expect(page.getByText(tk.fields.email, { exact: true })).toBeVisible() await expect(page.getByText('meier@example.de')).toBeVisible() await expect(page.getByText(tk.fields.address, { exact: true })).toBeVisible() await expect(page.getByText('Hauptstraße 1, 12345 Musterstadt')).toBeVisible() }) })