import { afterEach, describe, expect, it } from 'vitest'; import { ProjectStore } from '@/app/store'; import { buildSignalPlan } from '@/domain/plan/signalPlan'; import { validateProject } from '@/domain/validation'; import { createStandardIntersectionProject } from '@/domain/model/factory'; import type { Project } from '@/domain/model/project'; import type { ViewDefinition } from '@/ui/shell'; import { exportView, sperrgrundText } from '@/ui/views/exportView'; /** * Ausgabe-Ansicht zum Befund C7: "Export gesperrt" galt nur fuer das PDF; die * vier Plan-Tabellen blieben frei. * * Jetzt: Bei Fehlern sind PDF und die vier Plan-Tabellen deaktiviert, der * Grund steht sichtbar am Abschnitt und als Kurzhinweis am Knopf, und der * Pruefbericht bleibt als einzige Tabelle ausgebbar. Ohne Fehler ist alles * frei. Gegen den Altstand schlagen die Faelle fehl: Dort waren die vier * Tabellenknoepfe bei Sperre bedienbar, und einen Grund gab es nicht. */ const FESTES_DATUM = new Date('2026-01-01T00:00:00Z'); function zeichne( view: ViewDefinition, project: Project, ): { wurzel: HTMLElement; aufraeumen: () => void } { const wurzel = document.createElement('div'); document.body.append(wurzel); const store = new ProjectStore(project); const aufraeumen = view.render(wurzel, { store, refresh: () => {}, navigate: () => {}, target: null, }); return { wurzel, aufraeumen: () => { aufraeumen(); wurzel.remove(); }, }; } function knopf(wurzel: ParentNode, beschriftung: string): HTMLButtonElement { const gefunden = [...wurzel.querySelectorAll('button')].find( (b) => b.textContent?.trim() === beschriftung, ); if (!gefunden) throw new Error(`Kein Knopf "${beschriftung}"`); return gefunden; } const PLAN_TABELLEN = [ 'Signalgruppen', 'Zwischenzeitenmatrix', 'Zwischenzeiten mit Rechenweg', 'Phasen', ]; /** Ein Projekt mit Fehlern: Alle Kfz-Verkehrsstaerken so hoch, dass die Anlage uebersaettigt ist. */ function gesperrt(): Project { const p = createStandardIntersectionProject('Gesperrt', FESTES_DATUM); const mitFehlern = { ...p, demands: p.demands.map((d) => ({ ...d, volume: 1500 })) }; const bericht = validateProject(mitFehlern, buildSignalPlan(mitFehlern), FESTES_DATUM); expect(bericht.exportBlocked, 'Vorbedingung: Export gesperrt').toBe(true); return mitFehlern; } afterEach(() => { document.body.replaceChildren(); }); describe('Ausgabe: Sperre gilt fuer PDF und Plan-Tabellen, nicht fuer den Pruefbericht (Befund C7)', () => { it('deaktiviert bei Fehlern die vier Plan-Tabellen mit Grund und laesst den Pruefbericht frei', () => { const p = gesperrt(); const fehler = validateProject(p, buildSignalPlan(p), FESTES_DATUM).errorCount; const { wurzel, aufraeumen } = zeichne(exportView, p); try { expect(knopf(wurzel, 'PDF erzeugen').disabled).toBe(true); for (const name of PLAN_TABELLEN) { const k = knopf(wurzel, name); expect(k.disabled, name).toBe(true); // Der Grund am Knopf nennt Tabelle und Fehlerzahl. expect(k.title, name).toBe(`${name}: ${sperrgrundText(fehler)}`); } const bericht = knopf(wurzel, 'Prüfbericht'); expect(bericht.disabled).toBe(false); expect(bericht.title).toBe(''); // Der Grund steht sichtbar am Abschnitt "Tabellen". const grund = wurzel.querySelector('.export-sperrgrund'); expect(grund, 'sichtbarer Sperrgrund fehlt').not.toBeNull(); expect(grund!.textContent).toContain(`Plan-Tabellen ${sperrgrundText(fehler)}`); expect(grund!.textContent).toContain('Der Prüfbericht bleibt ausgebbar'); /* * Der sichtbare Absatz traegt selbst KEINE Live-Rolle mehr (Befund L5): * Er entsteht in `draw()` und wird als neuer Knoten eingehaengt - * angesagt wird die Aenderung eines vorhandenen Live-Bereichs, nicht das * Auftauchen eines neuen. Die Ansage laeuft ueber den Ansageraum der * Ansicht; dass der den Neuaufbau ueberlebt, prueft * bfFensterUndMeldungen.test.ts. */ expect(grund!.getAttribute('role')).toBeNull(); // Und der Balken oben sagt, dass die Sperre auch die Tabellen meint. expect(wurzel.textContent).toContain('weder als PDF noch als Tabelle'); } finally { aufraeumen(); } }); it('gibt ohne Fehler alle Tabellen und das PDF frei - und nennt den Warnungsvermerk', () => { const p = createStandardIntersectionProject('Frei', FESTES_DATUM); const bericht = validateProject(p, buildSignalPlan(p), FESTES_DATUM); expect(bericht.exportBlocked).toBe(false); expect(bericht.warningCount).toBeGreaterThan(0); const { wurzel, aufraeumen } = zeichne(exportView, p); try { expect(knopf(wurzel, 'PDF erzeugen').disabled).toBe(false); for (const name of [...PLAN_TABELLEN, 'Prüfbericht']) { const k = knopf(wurzel, name); expect(k.disabled, name).toBe(false); expect(k.title, name).toBe(''); } expect(wurzel.querySelector('.export-sperrgrund')).toBeNull(); // Bei Warnungen erfaehrt der Anwender, dass jede Plan-Tabelle den // Vermerk auf den Pruefbericht traegt. expect(wurzel.textContent).toContain('Jede Plan-Tabelle endet mit einem Hinweis auf die'); expect(wurzel.textContent).toContain(`${bericht.warningCount} Warnungen des Prüfberichts`); } finally { aufraeumen(); } }); it('formuliert den Sperrgrund mit der Fehlerzahl', () => { expect(sperrgrundText(3)).toBe('gesperrt – 3 Fehler im Prüfbericht'); expect(sperrgrundText(1)).toBe('gesperrt – 1 Fehler im Prüfbericht'); }); });