/** FEEDBACK: "Fehler melden" Dialog + Stammbaum-Rechtsklick ("ID kopieren" / "Fehler melden"). */ import { de, expect, skipUnlessMock, test } from './fixtures' const f = de.feedback const st = de.pages.stammbaum test('Tierakte: "Fehler melden"-Button öffnet den Dialog, Senden zeigt Erfolg', async ({ page, mockDb }) => { skipUnlessMock() await page.goto('/rennmaeuse/kruemel') // Button in den Aktionen öffnet den Dialog. await page.getByRole('button', { name: f.button }).click() const dialog = page.getByRole('dialog', { name: f.dialogTitle }) await expect(dialog).toBeVisible() // Debug-Kontext ist sichtbar (automatisch erfasst). await expect(dialog).toContainText(f.debugTitle) await expect(dialog).toContainText(f.contexts['gerbil-detail']) // Beschreibung eintragen + senden. await dialog.getByLabel(f.label).fill('Der Farbschlag stimmt nicht.') await dialog.getByRole('button', { name: f.submit }).click() // Erfolgs-Toast + Dialog schließt. await expect(page.getByText(f.success)).toBeVisible() await expect(dialog).not.toBeVisible() // Der Bericht wurde mit Debug-Kontext gespeichert (zuletzt eingereichter Eintrag; // die Mock-DB ist mit Beispiel-Tickets vorbefüllt). expect(mockDb).not.toBeNull() const report = mockDb!.feedback.find((r) => r.message === 'Der Farbschlag stimmt nicht.') expect(report).toMatchObject({ message: 'Der Farbschlag stimmt nicht.', context: 'gerbil-detail', gerbilId: 'kruemel', }) expect(report!.url).toContain('/rennmaeuse/kruemel') expect(typeof report!.clientTimestamp).toBe('string') }) test('Entwurf bleibt nach Schließen + Navigation + Wiederöffnen erhalten', async ({ page }) => { skipUnlessMock() // Auf einer Tier-Seite anfangen zu schreiben … await page.goto('/rennmaeuse/kruemel') await page.getByRole('button', { name: f.button }).click() const dialog = page.getByRole('dialog', { name: f.dialogTitle }) await dialog.getByLabel(f.label).fill('Halb fertiger Text, will kurz was nachschauen …') // … Fenster (versehentlich) schließen … await dialog.getByRole('button', { name: f.close }).click() await expect(dialog).not.toBeVisible() // … in der App woanders hin navigieren (anderer Kontext) … await page.goto('/wuerfe/w-kruemel') // … „Fehler melden" erneut öffnen: der Text ist noch da. await page.getByRole('button', { name: f.button }).click() const dialog2 = page.getByRole('dialog', { name: f.dialogTitle }) await expect(dialog2.getByLabel(f.label)).toHaveValue('Halb fertiger Text, will kurz was nachschauen …') }) test('Wurf-Ansicht: "Fehler melden"-Button öffnet den Dialog und sendet mit Wurf-Kontext', async ({ page, mockDb }) => { skipUnlessMock() await page.goto('/wuerfe/w-kruemel') await page.getByRole('button', { name: f.button }).click() const dialog = page.getByRole('dialog', { name: f.dialogTitle }) await expect(dialog).toBeVisible() await expect(dialog).toContainText(f.contexts['litter-detail']) await dialog.getByLabel(f.label).fill('Die Wurfstärke ist falsch.') await dialog.getByRole('button', { name: f.submit }).click() await expect(page.getByText(f.success)).toBeVisible() const report = mockDb!.feedback.find((r) => r.message === 'Die Wurfstärke ist falsch.') expect(report).toMatchObject({ context: 'litter-detail', litterId: 'w-kruemel' }) }) test('Kontakt-Detail: "Fehler melden"-Button sendet mit Kontakt-Kontext', async ({ page, mockDb }) => { skipUnlessMock() await page.goto('/kontakte/con-meier') await page.getByRole('button', { name: f.button }).click() const dialog = page.getByRole('dialog', { name: f.dialogTitle }) await expect(dialog).toBeVisible() await expect(dialog).toContainText(f.contexts['contact-detail']) await dialog.getByLabel(f.label).fill('Die Adresse stimmt nicht.') await dialog.getByRole('button', { name: f.submit }).click() await expect(page.getByText(f.success)).toBeVisible() const report = mockDb!.feedback.find((r) => r.message === 'Die Adresse stimmt nicht.') expect(report).toMatchObject({ context: 'contact-detail', contactId: 'con-meier' }) }) test('Stammbaum: Rechtsklick auf eine Karte zeigt "ID kopieren" + "Fehler melden"', async ({ page, mockDb }) => { skipUnlessMock() await page.goto('/rennmaeuse/kruemel/stammbaum') await expect(page.locator('.pedigree-card').first()).toBeVisible() // Rechtsklick auf die Wurzelkarte (Krümel). await page.locator('.pedigree-card--root').click({ button: 'right' }) const menu = page.locator('.stammbaum-context-menu') await expect(menu).toBeVisible() await expect(menu.getByRole('menuitem', { name: st.contextMenu.copyId })).toBeVisible() await expect(menu.getByRole('menuitem', { name: st.contextMenu.reportError })).toBeVisible() // "Fehler melden" öffnet den Dialog mit Stammbaum-Kontext. await menu.getByRole('menuitem', { name: st.contextMenu.reportError }).click() const dialog = page.getByRole('dialog', { name: f.dialogTitle }) await expect(dialog).toBeVisible() await expect(dialog).toContainText(f.contexts.stammbaum) await dialog.getByLabel(f.label).fill('Stammbaum-Bug.') await dialog.getByRole('button', { name: f.submit }).click() await expect(page.getByText(f.success)).toBeVisible() const report = mockDb!.feedback.find((r) => r.message === 'Stammbaum-Bug.') expect(report).toMatchObject({ context: 'stammbaum', gerbilId: 'kruemel' }) }) test('Stammbaum: langer Druck (Long-Press) öffnet dasselbe Kontextmenü', async ({ page }, testInfo) => { skipUnlessMock() // Der Long-Press-Code-Pfad (onTouchStart → Timer → Menü) ist projekt-unabhängig // identisch; im Touch-emulierten phone-Projekt ist das Auslösen eines // synthetischen TouchEvents unzuverlässig, daher hier auf das desktop-Projekt // beschränkt (validiert dieselbe Logik). testInfo.skip(testInfo.project.name === 'phone', 'synthetischer Long-Press im phone-Projekt instabil') await page.goto('/rennmaeuse/kruemel/stammbaum') const card = page.locator('.pedigree-card--root') await expect(card).toBeVisible() // Long-Press simulieren: echtes touchstart (mit Touch-Objekt) auslösen; der // Timer im PedigreeCard öffnet nach ~500 ms das Menü. const dispatched = await card.evaluate((el) => { if (typeof Touch === 'undefined' || typeof TouchEvent === 'undefined') return false const r = el.getBoundingClientRect() const t = new Touch({ identifier: 1, target: el, clientX: r.left + 10, clientY: r.top + 10 }) el.dispatchEvent( new TouchEvent('touchstart', { bubbles: true, cancelable: true, touches: [t], targetTouches: [t], changedTouches: [t] }), ) return true }) test.skip(!dispatched, 'TouchEvent in diesem Projekt nicht verfügbar') await expect(page.locator('.stammbaum-context-menu')).toBeVisible({ timeout: 2000 }) await expect( page.locator('.stammbaum-context-menu').getByRole('menuitem', { name: st.contextMenu.copyId }), ).toBeVisible() })