lsa-planer

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

/ tests ui assistentRueckfallwert.test.ts

6,0 KB Rohdatei
tests/ui/assistentRueckfallwert.test.ts — 155 Zeilen
1 import { afterEach, describe, expect, it } from 'vitest';
2 import {
3 ASSISTENT_VORGABE,
4 engstellenFolgen,
5 starteAssistent,
6 vorgabenDerNeuenPlanung,
7 } from '@/ui/assistent';
8 import * as fmt from '@/ui/format';
9
10 /**
11 * Gefuehrter Einstieg: Was nach einer verworfenen Eingabe im Feld steht, muss
12 * die Zahl sein, mit der der Assistent weiterrechnet (Befund 21 zur
13 * Fassung 5.8.0).
14 *
15 * `numberCell` fuehrt den zuletzt uebernommenen Wert selbst mit und stellt ihn
16 * wieder her, wenn eine Eingabe verworfen wird - ohne `onChange` zu rufen
17 * (Fassung 5.9.0). Bis dahin merkte es sich den beim Bau uebergebenen Wert.
18 * Ueberall sonst fiel das nicht auf, weil sich die Ansicht nach jeder Aenderung
19 * neu aufbaut und das Feld dabei mit dem gespeicherten Wert neu entsteht. Der
20 * Assistent zeichnet den Schritt bewusst NICHT neu; der Rueckfallwert blieb
21 * dort dauerhaft der Startwert des Schrittes.
22 *
23 * Was der Anwender sonst erlebt: Er traegt bei der einstreifigen
24 * Verkehrsfuehrung 60 m ein, leert das Feld versehentlich und liest "Der
25 * vorherige Wert 120 wurde wiederhergestellt". Im Feld stehen 120 m - angelegt
26 * wird die Planung aber aus 60 m. Der Raeumweg beider Fahrtrichtungen ist bei
27 * einer Engstelle die ganze Laenge; die Zwischenzeit faellt damit um rund
28 * 6,0 s kuerzer aus, als die angezeigte Laenge hergibt, und die Gegenrichtung
29 * bekommt Freigabe, waehrend noch geraeumt wird.
30 *
31 * Bewacht ist hier, was im FELD steht und womit angelegt wird; den WORTLAUT der
32 * Meldung bewacht `tests/ui/rueckfallwert.test.ts`.
33 */
34
35 const dialog = (): HTMLElement => {
36 const knoten = document.querySelector<HTMLElement>('.dialog');
37 expect(knoten, 'kein Fenster des gefuehrten Einstiegs').not.toBeNull();
38 return knoten ?? document.body;
39 };
40
41 const knopf = (beschriftung: string): HTMLButtonElement => {
42 const gefunden = [
43 ...dialog().querySelectorAll<HTMLButtonElement>('.dialog-aktionen button'),
44 ].find((b) => (b.textContent ?? '').trim() === beschriftung);
45 expect(gefunden, `Schaltflaeche "${beschriftung}" fehlt`).toBeDefined();
46 return gefunden as HTMLButtonElement;
47 };
48
49 const feldMit = (beschriftung: string): HTMLInputElement => {
50 const label = [...dialog().querySelectorAll('label')].find(
51 (l) => (l.textContent ?? '').trim() === beschriftung,
52 );
53 expect(label, `Feld "${beschriftung}" fehlt`).toBeDefined();
54 return dialog().querySelector<HTMLInputElement>(`#${(label as HTMLLabelElement).htmlFor}`)!;
55 };
56
57 /**
58 * Der Satz des Folgenkastens zur Zwischenzeit einer Engstelle dieser Laenge -
59 * gerechnet mit denselben Vorgaben wie der Assistent, statt eine Sekundenzahl
60 * in den Test zu schreiben.
61 */
62 const zwischenzeitSatz = (laenge: number): string => {
63 const angaben = { ...ASSISTENT_VORGABE, form: 'einstreifig' as const, engstellenlaenge: laenge };
64 const folgen = engstellenFolgen(laenge, angaben.vZul, vorgabenDerNeuenPlanung(angaben));
65 return `Die Zwischenzeit beträgt je Fahrtrichtung ${fmt.seconds(folgen.zwischenzeit)}`;
66 };
67
68 /** Eine Eingabe so absetzen, wie sie beim Verlassen des Feldes ankommt. */
69 const setzeZahl = (feld: HTMLInputElement, wert: string): void => {
70 feld.value = wert;
71 feld.dispatchEvent(new Event('change'));
72 };
73
74 /** Anlagenform im ersten Schritt waehlen. */
75 const waehleForm = (form: string): void => {
76 const auswahl = dialog().querySelector<HTMLSelectElement>('select')!;
77 auswahl.value = form;
78 auswahl.dispatchEvent(new Event('change'));
79 };
80
81 afterEach(() => {
82 // Ein Fenster, das ein fehlgeschlagener Testfall stehen laesst, wuerde der
83 // naechste ueber `.dialog` erwischen.
84 for (const knoten of document.querySelectorAll('.dialog-hintergrund, .meldung')) knoten.remove();
85 });
86
87 describe('Gefuehrter Einstieg: Rueckfallwert nach einer verworfenen Eingabe (Befund 21)', () => {
88 it('zeigt bei der Engstellenlaenge den zuletzt uebernommenen Wert und legt genau damit an', async () => {
89 const ergebnis = starteAssistent();
90
91 waehleForm('einstreifig');
92 knopf('Weiter').click();
93
94 const laenge = feldMit('Länge der Engstelle');
95 expect(laenge.value, 'Startwert des Schrittes').toBe('120');
96
97 setzeZahl(laenge, '60');
98 setzeZahl(laenge, '');
99
100 expect(
101 laenge.value,
102 'Nach der verworfenen Eingabe steht der Startwert im Feld, nicht der zuletzt uebernommene',
103 ).toBe('60');
104
105 // Der Folgenkasten rechnet mit dem uebernommenen Wert - Feld und Kasten
106 // duerfen sich nicht widersprechen.
107 const kasten = dialog().querySelector<HTMLElement>('.hinweisbalken');
108 expect(kasten?.textContent ?? '').toContain(zwischenzeitSatz(60));
109 expect(kasten?.textContent ?? '').not.toContain(zwischenzeitSatz(120));
110
111 knopf('Weiter').click();
112 knopf('Verkehrsführung anlegen').click();
113
114 const projekt = await ergebnis;
115 expect(projekt).not.toBeNull();
116 for (const konflikt of projekt!.conflicts) {
117 expect(konflikt.clearingDistance, 'Raeumweg beider Richtungen = angezeigte Laenge').toBe(60);
118 }
119 });
120
121 it('zeigt bei der zulaessigen Hoechstgeschwindigkeit den zuletzt uebernommenen Wert', async () => {
122 const ergebnis = starteAssistent();
123
124 const vZul = feldMit('Zulässige Höchstgeschwindigkeit');
125 expect(vZul.value).toBe('50');
126
127 setzeZahl(vZul, '30');
128 setzeZahl(vZul, '');
129
130 expect(vZul.value, 'Startwert statt des zuletzt uebernommenen Wertes').toBe('30');
131
132 waehleForm('einstreifig');
133 knopf('Weiter').click();
134 knopf('Weiter').click();
135 knopf('Verkehrsführung anlegen').click();
136
137 const projekt = await ergebnis;
138 expect(projekt!.intersection.vZul, 'angelegt wird mit der angezeigten Geschwindigkeit').toBe(
139 30,
140 );
141 });
142
143 it('laesst eine verworfene Eingabe ohne vorherige Aenderung beim Startwert', async () => {
144 // Gegenprobe: Wo noch nichts uebernommen wurde, IST der Startwert der
145 // zuletzt gueltige Wert - an diesem Fall aendert sich nichts.
146 const ergebnis = starteAssistent();
147
148 const vZul = feldMit('Zulässige Höchstgeschwindigkeit');
149 setzeZahl(vZul, '');
150 expect(vZul.value).toBe('50');
151
152 knopf('Abbrechen').click();
153 expect(await ergebnis).toBeNull();
154 });
155 });