/** * QA-2: Abgabe-Wizard (FEAT-12a + FEAT-13) — Abgabetiere gruppieren, * KI-Inserat generieren, und Vertrag-Handoff via ?tiere= Query-Parameter. * * Mock-gebunden (Seed: 2 ForSale-Tiere im Großbecken) → skipUnlessMock. */ import { acceptNextDialog, de, expect, gotoSection, skipUnlessMock, test } from './fixtures' const ta = de.pages.abgabe const tw = de.pages.vertraege.wizard const tk = de.pages.kontakte test.describe('Abgabe', () => { test('Abgabe-Seite zeigt ForSale-Tiere gruppiert nach Becken', async ({ page }) => { skipUnlessMock() await gotoSection(page, de.nav.forSale) await expect(page.getByRole('heading', { name: ta.title, exact: true })).toBeVisible() await expect(page.getByText(ta.grouping.title)).toBeVisible() // Die beiden ForSale-Tiere müssen sichtbar sein await expect(page.getByText('Balu Abgabe')).toBeVisible() await expect(page.getByText('Benny Abgabe')).toBeVisible() // Inserat-Vorschau (listing-preview) wird gerendert await expect(page.locator('.listing-preview').first()).toBeVisible() // Status-Dropdown standard: FREI await expect( page.locator('label.field').filter({ hasText: ta.listing.status }).locator('select').first() ).toHaveValue('free') }) test('KI-Inserat: "Text mit KI verbessern" — Mock liefert Inserat-Text', async ({ page }) => { skipUnlessMock() await gotoSection(page, de.nav.forSale) await expect(page.getByText('Balu Abgabe')).toBeVisible() await page.getByRole('button', { name: ta.ai.improve }).first().click() // Mock antwortet synchron; der generierte Text erscheint in der Vorschau await expect(page.locator('.listing-preview').first()).toContainText('Generierter Inserat-Text') }) test('KI-Inserat: 503 zeigt deutschen Hinweis (saleAdConfigured=false)', async ({ page, mockDb }) => { skipUnlessMock() mockDb!.saleAdConfigured = false await gotoSection(page, de.nav.forSale) await expect(page.getByText('Balu Abgabe')).toBeVisible() await page.getByRole('button', { name: ta.ai.improve }).first().click() await expect(page.getByText(ta.ai.notConfigured)).toBeVisible() }) test('Vertrag-Handoff: "Abgabe abschließen" öffnet Wizard mit vorausgewählten Tieren', async ({ page, }) => { skipUnlessMock() await gotoSection(page, de.nav.forSale) await expect(page.getByText('Balu Abgabe')).toBeVisible() // Wizard öffnen (navigiert zu /vertraege/neu?tiere=sale-balu,sale-benny) await page.getByRole('button', { name: ta.export.finish }).first().click() await expect(page).toHaveURL(/\/vertraege\/neu\?tiere=/) await expect(page.getByRole('heading', { name: tw.title })).toBeVisible() // Schritt 1: Abnehmer auswählen await expect(page.getByText(tw.pickContact)).toBeVisible() }) }) test.describe('Vertrag-Wizard', () => { test('4-Schritte-Wizard: Abnehmer → Tiere → Preis → Vertrag erzeugen', async ({ page }) => { skipUnlessMock() // Direkt mit vorausgewählten Tieren öffnen (wie über Abgabe-Seite) await page.goto('/vertraege/neu?tiere=sale-balu,sale-benny') await expect(page.getByRole('heading', { name: tw.title })).toBeVisible() // Schritt 1: Abnehmer wählen — Zoohandlung Meier aus dem Seed await expect(page.getByText(tw.pickContact)).toBeVisible() await page.locator('.wizard-pick').filter({ hasText: 'Zoohandlung Meier' }).getByRole('radio').check() await page.getByRole('button', { name: tw.next }).click() // Schritt 2: Tiere — Vorauswahl aus ?tiere= ist aktiv await expect(page.getByText(tw.pickAnimals)).toBeVisible() // Beide ForSale-Tiere sind vorausgewählt (Checkboxen angehakt) await expect( page.locator('.wizard-pick').filter({ hasText: 'Balu Abgabe' }).getByRole('checkbox') ).toBeChecked() await expect( page.locator('.wizard-pick').filter({ hasText: 'Benny Abgabe' }).getByRole('checkbox') ).toBeChecked() await page.getByRole('button', { name: tw.next }).click() // Schritt 3: Preis & Datum await page.getByLabel(tw.priceLabel).fill('50,00') await page.getByRole('button', { name: tw.next }).click() // Schritt 4: Zusammenfassung + Erzeugen await expect(page.getByText(tw.summaryTitle)).toBeVisible() await expect(page.getByText('Zoohandlung Meier')).toBeVisible() await expect(page.getByText('Balu Abgabe')).toBeVisible() await page.getByRole('button', { name: tw.generate }).click() // Erfolg: Vertrag erstellt, Download-Link vorhanden await expect(page.getByRole('heading', { name: tw.successTitle })).toBeVisible() await expect(page.getByRole('link', { name: tw.downloadDocx })).toBeVisible() }) test('Schritt 1: fehlender Abnehmer zeigt Validierungsmeldung', async ({ page }) => { skipUnlessMock() await page.goto('/vertraege/neu?tiere=sale-balu') await expect(page.getByText(tw.pickContact)).toBeVisible() // Ohne Auswahl direkt auf Weiter klicken await page.getByRole('button', { name: tw.next }).click() await expect(page.getByText(tw.pickContact, { exact: true })).toBeVisible() }) test('Schritt 3: ungültiger Preis zeigt Validierungsmeldung', async ({ page }) => { skipUnlessMock() await page.goto('/vertraege/neu?tiere=sale-balu') // Schritt 1: Abnehmer wählen await page.locator('.wizard-pick').filter({ hasText: 'Zoohandlung Meier' }).getByRole('radio').check() await page.getByRole('button', { name: tw.next }).click() // Schritt 2: Tiere bestätigen await page.getByRole('button', { name: tw.next }).click() // Schritt 3: leeres Preisfeld -> Fehler await page.getByRole('button', { name: tw.next }).click() await expect(page.getByText(tw.priceInvalid)).toBeVisible() }) })