From ca6937683d5d8cb70a98b9a85b8512994f24f30f Mon Sep 17 00:00:00 2001 From: Marcel Date: Sun, 10 May 2026 02:19:35 +0200 Subject: [PATCH] test(ocr): cover TrainingHistory branches Empty placeholder, all four status pill branches (QUEUED/DONE/FAILED/ RUNNING), error-detail disclosure on FAILED, Personalisiert vs Basis type label, COLLAPSED_COUNT visible runs, person columns visibility toggle, em-dash CER fallback. 11 tests covering ~25 of TrainingHistory's branches. Refs #496. Co-Authored-By: Claude Sonnet 4.6 --- .../lib/ocr/TrainingHistory.svelte.test.ts | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 frontend/src/lib/ocr/TrainingHistory.svelte.test.ts diff --git a/frontend/src/lib/ocr/TrainingHistory.svelte.test.ts b/frontend/src/lib/ocr/TrainingHistory.svelte.test.ts new file mode 100644 index 00000000..576bde0e --- /dev/null +++ b/frontend/src/lib/ocr/TrainingHistory.svelte.test.ts @@ -0,0 +1,101 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { cleanup, render } from 'vitest-browser-svelte'; +import { page } from 'vitest/browser'; +import TrainingHistory from './TrainingHistory.svelte'; + +afterEach(cleanup); + +const makeRun = (overrides: Record = {}) => ({ + id: 'r1', + createdAt: '2026-04-15T10:00:00Z', + status: 'DONE' as 'DONE' | 'FAILED' | 'QUEUED' | 'RUNNING', + blockCount: 100, + documentCount: 5, + personId: null as string | null, + cer: 0.05, + errorMessage: null as string | null, + ...overrides +}); + +describe('TrainingHistory', () => { + it('renders the empty placeholder when runs is empty', async () => { + render(TrainingHistory, { props: { runs: [] } }); + + await expect.element(page.getByText('Noch keine Trainings-Läufe.')).toBeVisible(); + }); + + it('renders the QUEUED status pill', async () => { + render(TrainingHistory, { props: { runs: [makeRun({ status: 'QUEUED' })] } }); + + await expect.element(page.getByText('Warteschlange')).toBeVisible(); + }); + + it('renders the DONE status pill', async () => { + render(TrainingHistory, { props: { runs: [makeRun({ status: 'DONE' })] } }); + + await expect.element(page.getByText('Fertig')).toBeVisible(); + }); + + it('renders the FAILED status pill', async () => { + render(TrainingHistory, { props: { runs: [makeRun({ status: 'FAILED' })] } }); + + // "Fehler" might match multiple things — check for the pill specifically + const pill = Array.from(document.querySelectorAll('span')).find( + (el) => el.textContent?.trim() === 'Fehler' + ); + expect(pill).toBeDefined(); + }); + + it('renders the RUNNING status pill for unknown statuses', async () => { + render(TrainingHistory, { props: { runs: [makeRun({ status: 'RUNNING' as const })] } }); + + await expect.element(page.getByText('Läuft…')).toBeVisible(); + }); + + it('shows the error-detail disclosure when a FAILED run has errorMessage', async () => { + render(TrainingHistory, { + props: { runs: [makeRun({ status: 'FAILED', errorMessage: 'Network timeout' })] } + }); + + await expect.element(page.getByText('Network timeout')).toBeInTheDocument(); + }); + + it('renders Personalisiert label when personId is set', async () => { + render(TrainingHistory, { + props: { + runs: [makeRun({ personId: 'p-1' })], + personNames: { 'p-1': 'Anna Schmidt' } + } + }); + + await expect.element(page.getByText('Personalisiert')).toBeVisible(); + }); + + it('renders Basis label when personId is null', async () => { + render(TrainingHistory, { props: { runs: [makeRun()] } }); + + await expect.element(page.getByText('Basis')).toBeVisible(); + }); + + it('limits visible runs to COLLAPSED_COUNT (3) by default', async () => { + const runs = Array.from({ length: 7 }, (_, i) => makeRun({ id: `r${i}` })); + render(TrainingHistory, { props: { runs } }); + + const rows = document.querySelectorAll('#training-history-rows > tr'); + expect(rows.length).toBeLessThanOrEqual(4); // 3 visible + maybe expand row + }); + + it('hides person columns when showPersonColumns is false', async () => { + render(TrainingHistory, { + props: { runs: [makeRun({ personId: 'p1' })], showPersonColumns: false } + }); + + await expect.element(page.getByText('Personalisiert')).not.toBeInTheDocument(); + }); + + it('renders em-dash CER for runs without cer', async () => { + render(TrainingHistory, { props: { runs: [makeRun({ cer: null })] } }); + + expect(document.body.textContent).toContain('—'); + }); +});