Files
GerbilManager/gerbil-manager-web/e2e/becken-kontakte.spec.ts

139 lines
7.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/** 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('Reinigungszyklus: überfälliges Becken zeigt Hinweis + Als gereinigt markieren', async ({ page }) => {
skipUnlessMock()
await page.goto('/gehege')
await page.getByRole('link', { name: /Großbecken/ }).click()
await expect(page.getByRole('heading', { name: 'Großbecken' })).toBeVisible()
// Maße + Kapazität werden angezeigt
await expect(page.getByText('120×50 cm')).toBeVisible()
// Reinigung ist überfällig (nextCleaningDate in der Vergangenheit) -> Warn-Hinweis
await expect(page.getByText(/Reinigung fällig/)).toBeVisible()
// Als gereinigt markieren -> letzte Reinigung = heute, Hinweis verschwindet
await page.getByRole('button', { name: tb.cleaning.markCleaned }).click()
await expect(page.getByText(/Reinigung fällig/)).toBeHidden()
})
test('Reinigungszyklus: Becken mit Zyklus anlegen zeigt nächste Reinigung', async ({ page }) => {
await gotoSection(page, de.nav.enclosures)
await page.getByRole('link', { name: tb.newButton }).click()
const name = uniqueName('Zyklusbecken')
await page.getByLabel(`${tb.fields.name} *`).fill(name)
await page.getByLabel(tb.fields.size).fill('60×40 cm')
await page.getByLabel(tb.fields.capacity).fill('4')
await page.getByLabel(tb.fields.lastCleanedDate).fill('2026-06-01')
await page.getByLabel(tb.fields.cleaningCycleDays).fill('14')
await page.getByRole('button', { name: tb.form.save, exact: true }).click()
await expect(page.getByRole('heading', { name })).toBeVisible()
// nächste Reinigung = 2026-06-01 + 14 Tage = 15.06.2026 (in der def-list-Zeile)
await expect(page.getByText('15.06.2026', { exact: true })).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()
})
})