lsa-planer

LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.

/ tests ui exportSperre.test.ts

5,6 KB Rohdatei
tests/ui/exportSperre.test.ts — 138 Zeilen
1 import { afterEach, describe, expect, it } from 'vitest';
2 import { ProjectStore } from '@/app/store';
3 import { buildSignalPlan } from '@/domain/plan/signalPlan';
4 import { validateProject } from '@/domain/validation';
5 import { createStandardIntersectionProject } from '@/domain/model/factory';
6 import type { Project } from '@/domain/model/project';
7 import type { ViewDefinition } from '@/ui/shell';
8 import { exportView, sperrgrundText } from '@/ui/views/exportView';
9
10 /**
11 * Ausgabe-Ansicht zum Befund C7: "Export gesperrt" galt nur fuer das PDF; die
12 * vier Plan-Tabellen blieben frei.
13 *
14 * Jetzt: Bei Fehlern sind PDF und die vier Plan-Tabellen deaktiviert, der
15 * Grund steht sichtbar am Abschnitt und als Kurzhinweis am Knopf, und der
16 * Pruefbericht bleibt als einzige Tabelle ausgebbar. Ohne Fehler ist alles
17 * frei. Gegen den Altstand schlagen die Faelle fehl: Dort waren die vier
18 * Tabellenknoepfe bei Sperre bedienbar, und einen Grund gab es nicht.
19 */
20
21 const FESTES_DATUM = new Date('2026-01-01T00:00:00Z');
22
23 function zeichne(
24 view: ViewDefinition,
25 project: Project,
26 ): { wurzel: HTMLElement; aufraeumen: () => void } {
27 const wurzel = document.createElement('div');
28 document.body.append(wurzel);
29 const store = new ProjectStore(project);
30 const aufraeumen = view.render(wurzel, {
31 store,
32 refresh: () => {},
33 navigate: () => {},
34 target: null,
35 });
36 return {
37 wurzel,
38 aufraeumen: () => {
39 aufraeumen();
40 wurzel.remove();
41 },
42 };
43 }
44
45 function knopf(wurzel: ParentNode, beschriftung: string): HTMLButtonElement {
46 const gefunden = [...wurzel.querySelectorAll<HTMLButtonElement>('button')].find(
47 (b) => b.textContent?.trim() === beschriftung,
48 );
49 if (!gefunden) throw new Error(`Kein Knopf "${beschriftung}"`);
50 return gefunden;
51 }
52
53 const PLAN_TABELLEN = [
54 'Signalgruppen',
55 'Zwischenzeitenmatrix',
56 'Zwischenzeiten mit Rechenweg',
57 'Phasen',
58 ];
59
60 /** Ein Projekt mit Fehlern: Alle Kfz-Verkehrsstaerken so hoch, dass die Anlage uebersaettigt ist. */
61 function gesperrt(): Project {
62 const p = createStandardIntersectionProject('Gesperrt', FESTES_DATUM);
63 const mitFehlern = { ...p, demands: p.demands.map((d) => ({ ...d, volume: 1500 })) };
64 const bericht = validateProject(mitFehlern, buildSignalPlan(mitFehlern), FESTES_DATUM);
65 expect(bericht.exportBlocked, 'Vorbedingung: Export gesperrt').toBe(true);
66 return mitFehlern;
67 }
68
69 afterEach(() => {
70 document.body.replaceChildren();
71 });
72
73 describe('Ausgabe: Sperre gilt fuer PDF und Plan-Tabellen, nicht fuer den Pruefbericht (Befund C7)', () => {
74 it('deaktiviert bei Fehlern die vier Plan-Tabellen mit Grund und laesst den Pruefbericht frei', () => {
75 const p = gesperrt();
76 const fehler = validateProject(p, buildSignalPlan(p), FESTES_DATUM).errorCount;
77 const { wurzel, aufraeumen } = zeichne(exportView, p);
78 try {
79 expect(knopf(wurzel, 'PDF erzeugen').disabled).toBe(true);
80 for (const name of PLAN_TABELLEN) {
81 const k = knopf(wurzel, name);
82 expect(k.disabled, name).toBe(true);
83 // Der Grund am Knopf nennt Tabelle und Fehlerzahl.
84 expect(k.title, name).toBe(`${name}: ${sperrgrundText(fehler)}`);
85 }
86 const bericht = knopf(wurzel, 'Prüfbericht');
87 expect(bericht.disabled).toBe(false);
88 expect(bericht.title).toBe('');
89
90 // Der Grund steht sichtbar am Abschnitt "Tabellen".
91 const grund = wurzel.querySelector<HTMLElement>('.export-sperrgrund');
92 expect(grund, 'sichtbarer Sperrgrund fehlt').not.toBeNull();
93 expect(grund!.textContent).toContain(`Plan-Tabellen ${sperrgrundText(fehler)}`);
94 expect(grund!.textContent).toContain('Der Prüfbericht bleibt ausgebbar');
95 /*
96 * Der sichtbare Absatz traegt selbst KEINE Live-Rolle mehr (Befund L5):
97 * Er entsteht in `draw()` und wird als neuer Knoten eingehaengt -
98 * angesagt wird die Aenderung eines vorhandenen Live-Bereichs, nicht das
99 * Auftauchen eines neuen. Die Ansage laeuft ueber den Ansageraum der
100 * Ansicht; dass der den Neuaufbau ueberlebt, prueft
101 * bfFensterUndMeldungen.test.ts.
102 */
103 expect(grund!.getAttribute('role')).toBeNull();
104 // Und der Balken oben sagt, dass die Sperre auch die Tabellen meint.
105 expect(wurzel.textContent).toContain('weder als PDF noch als Tabelle');
106 } finally {
107 aufraeumen();
108 }
109 });
110
111 it('gibt ohne Fehler alle Tabellen und das PDF frei - und nennt den Warnungsvermerk', () => {
112 const p = createStandardIntersectionProject('Frei', FESTES_DATUM);
113 const bericht = validateProject(p, buildSignalPlan(p), FESTES_DATUM);
114 expect(bericht.exportBlocked).toBe(false);
115 expect(bericht.warningCount).toBeGreaterThan(0);
116 const { wurzel, aufraeumen } = zeichne(exportView, p);
117 try {
118 expect(knopf(wurzel, 'PDF erzeugen').disabled).toBe(false);
119 for (const name of [...PLAN_TABELLEN, 'Prüfbericht']) {
120 const k = knopf(wurzel, name);
121 expect(k.disabled, name).toBe(false);
122 expect(k.title, name).toBe('');
123 }
124 expect(wurzel.querySelector('.export-sperrgrund')).toBeNull();
125 // Bei Warnungen erfaehrt der Anwender, dass jede Plan-Tabelle den
126 // Vermerk auf den Pruefbericht traegt.
127 expect(wurzel.textContent).toContain('Jede Plan-Tabelle endet mit einem Hinweis auf die');
128 expect(wurzel.textContent).toContain(`${bericht.warningCount} Warnungen des Prüfberichts`);
129 } finally {
130 aufraeumen();
131 }
132 });
133
134 it('formuliert den Sperrgrund mit der Fehlerzahl', () => {
135 expect(sperrgrundText(3)).toBe('gesperrt – 3 Fehler im Prüfbericht');
136 expect(sperrgrundText(1)).toBe('gesperrt – 1 Fehler im Prüfbericht');
137 });
138 });