- Kontakt-Detailseite: „Fehler melden"- und „Datenherkunft"-Button. - Wurf-Ansicht: „Datenherkunft"-Button (Feedback war bereits vorhanden). - Feedback-Entity um loses, nullable ContactId erweitert (kein FK → übersteht Ingest-Wipe); Migration AddFeedbackContactId. - Contact.Provenance + Litter.Provenance (nullable text); Migration AddContactLitterProvenance; im Ingest gemappt und in den DTOs zurückgegeben. - Import: build_entity_provenance() generalisiert; Kontakte (sourceFiles, Züchter/Abnehmer-Hinweise) und Würfe (Wurfchronik vs. Diagramm-rekonstruiert, Geschwister-Merge) erhalten Herkunftsdaten in resolved_import.json. - Frontend: ProvenanceDialog generalisiert (EntityProvenance + entityLabel). Tests erweitert (Ingest-Round-trip Kontakt/Wurf, contact-scoped Feedback übersteht Wipe). dotnet(212)/vitest(129)/playwright(36)/tsc/eslint grün. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
86 lines
3.6 KiB
TypeScript
86 lines
3.6 KiB
TypeScript
/** NACHVERFOLGUNG: "Datenherkunft"-Button in der Tierakte öffnet den
|
|
* Nachverfolgungs-Dialog mit Quelldateien, Datensatzanzahl und Hinweisen. */
|
|
import { de, expect, skipUnlessMock, test } from './fixtures'
|
|
|
|
const p = de.provenance
|
|
|
|
test('Tierakte: "Datenherkunft"-Button öffnet den Nachverfolgungs-Dialog', async ({ page }) => {
|
|
skipUnlessMock()
|
|
await page.goto('/rennmaeuse/kruemel')
|
|
|
|
// Button in den Aktionen öffnet den Dialog.
|
|
await page.getByRole('button', { name: p.button }).click()
|
|
const dialog = page.getByRole('dialog', { name: p.dialogTitle })
|
|
await expect(dialog).toBeVisible()
|
|
|
|
// Quelldateien werden angezeigt.
|
|
await expect(dialog).toContainText(p.sourceFilesTitle)
|
|
await expect(dialog).toContainText('Stammbaum von Krümel.xlsx')
|
|
await expect(dialog).toContainText('Wurfchronik-Detail.docx')
|
|
|
|
// Zusammenführungs-Info + Wurfchronik-Hinweis.
|
|
await expect(dialog).toContainText(p.mergedCount(2))
|
|
await expect(dialog).toContainText(p.fromWurfchronik)
|
|
|
|
// Eltern-Herkunft (übersetzte Methode/Konfidenz).
|
|
await expect(dialog).toContainText(p.methods['chart-position'])
|
|
await expect(dialog).toContainText(p.confidences.medium)
|
|
|
|
// Hinweis aus dem Import.
|
|
await expect(dialog).toContainText('aus 2 Datensätzen zusammengeführt')
|
|
|
|
// Schließen (Footer-Button — Header-Button trägt dasselbe aria-label).
|
|
await dialog.locator('.provenance__actions').getByRole('button', { name: p.close }).click()
|
|
await expect(dialog).not.toBeVisible()
|
|
})
|
|
|
|
test('Tierakte: Tier ohne Importdaten zeigt "Keine Herkunftsdaten"', async ({ page }) => {
|
|
skipUnlessMock()
|
|
// Luna hat keine provenance im Mock → Leer-Zustand.
|
|
await page.goto('/rennmaeuse/luna')
|
|
|
|
await page.getByRole('button', { name: p.button }).click()
|
|
const dialog = page.getByRole('dialog', { name: p.dialogTitle })
|
|
await expect(dialog).toBeVisible()
|
|
await expect(dialog).toContainText(p.empty)
|
|
})
|
|
|
|
test('Kontakt: "Datenherkunft"-Button öffnet den Nachverfolgungs-Dialog', async ({ page }) => {
|
|
skipUnlessMock()
|
|
await page.goto('/kontakte/con-meier')
|
|
|
|
await page.getByRole('button', { name: p.button }).click()
|
|
const dialog = page.getByRole('dialog', { name: p.dialogTitle })
|
|
await expect(dialog).toBeVisible()
|
|
|
|
// Quelldateien + Zusammenführung + Kontakt-Rolle-Hinweise.
|
|
await expect(dialog).toContainText(p.sourceFilesTitle)
|
|
await expect(dialog).toContainText('Wurfchronik Teil 1_page_0001.md')
|
|
await expect(dialog).toContainText(p.mergedCount(2))
|
|
await expect(dialog).toContainText(p.fromWurfchronik)
|
|
await expect(dialog).toContainText('als Züchter erkannt')
|
|
await expect(dialog).toContainText('als Abnehmer erkannt')
|
|
|
|
await dialog.locator('.provenance__actions').getByRole('button', { name: p.close }).click()
|
|
await expect(dialog).not.toBeVisible()
|
|
})
|
|
|
|
test('Wurf: "Datenherkunft"-Button öffnet den Nachverfolgungs-Dialog', async ({ page }) => {
|
|
skipUnlessMock()
|
|
await page.goto('/wuerfe/w-kruemel')
|
|
|
|
await page.getByRole('button', { name: p.button }).click()
|
|
const dialog = page.getByRole('dialog', { name: p.dialogTitle })
|
|
await expect(dialog).toBeVisible()
|
|
|
|
await expect(dialog).toContainText(p.sourceFilesTitle)
|
|
await expect(dialog).toContainText('Wurfchronik Teil 1_page_0009.md')
|
|
await expect(dialog).toContainText(p.mergedCount(2))
|
|
await expect(dialog).toContainText(p.fromWurfchronik)
|
|
await expect(dialog).toContainText('aus Wurfchronik')
|
|
await expect(dialog).toContainText('Geschwister-Würfe zusammengeführt')
|
|
|
|
await dialog.locator('.provenance__actions').getByRole('button', { name: p.close }).click()
|
|
await expect(dialog).not.toBeVisible()
|
|
})
|