lsa-planer

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

/ tests ui rueckfallwert.test.ts

6,5 KB Rohdatei
tests/ui/rueckfallwert.test.ts — 170 Zeilen
1 import { afterEach, describe, expect, it } from 'vitest';
2 import { numberCell } from '@/ui/inputs';
3
4 /**
5 * Der Rueckfallwert des Zahlenfelds ist der zuletzt UEBERNOMMENE Wert, nicht
6 * der beim Bau uebergebene.
7 *
8 * `numberCell` hielt den Parameter `value` fest und las ihn in beiden
9 * Wiederherstellungswegen sowie im Meldungssatz. Fuer jeden Aufrufer, der die
10 * Ansicht nicht nach jeder Eingabe neu baut, ist dieser Wert veraltet: Wer 60
11 * eintraegt und danach etwas Ungueltiges, bekommt den Bauwert 120 zurueck - im
12 * Feld und im Satz -, obwohl 60 der zuletzt gueltige Wert war und der Aufrufer
13 * genau damit weiterrechnet.
14 *
15 * Gefunden im gefuehrten Einstieg (Befund 21 zur Fassung 5.8.0): Bei der
16 * einstreifigen Verkehrsfuehrung IST die Engstellenlaenge der Raeumweg beider
17 * Fahrtrichtungen; Feld und Rechnung wichen um 60 m voneinander ab, die
18 * Zwischenzeit fiel um 6,0 s kuerzer aus als die angezeigte Laenge hergab.
19 * Der Assistent fing das mit einer eigenen Bauhilfe ab - hier ist die Ursache
20 * bewacht, unabhaengig von jedem Aufrufer.
21 *
22 * Nachgestellt wird ein Aufrufer OHNE Neuaufbau: Das Feld wird einmal gebaut
23 * und bleibt stehen; nur der Empfaenger schreibt mit.
24 */
25
26 afterEach(() => {
27 document.body.replaceChildren();
28 });
29
30 /** Der Text der zuletzt gezeigten Warnung. */
31 function letzteWarnung(): string {
32 const meldungen = [...document.querySelectorAll('.meldung-warnung')];
33 return meldungen[meldungen.length - 1]?.textContent ?? '';
34 }
35
36 /** Eine Eingabe so absetzen, wie sie beim Verlassen des Feldes ankommt. */
37 function setzeZahl(feld: HTMLInputElement, wert: string): void {
38 feld.value = wert;
39 feld.dispatchEvent(new Event('change'));
40 }
41
42 /**
43 * Eine unlesbare Eingabe absetzen.
44 *
45 * Ein `input[type=number]` gibt sie als leere Zeichenkette heraus - in jsdom wie
46 * im Browser. Um den zweiten Rueckfallweg zu erreichen, wird der Feldtyp fuer
47 * die Eingabe kurz auf `text` gestellt; der Empfaenger liest nur `input.value`
48 * und sieht davon nichts.
49 */
50 function setzeUnlesbar(feld: HTMLInputElement, wert: string): void {
51 const zuvor = feld.getAttribute('type') ?? 'number';
52 feld.setAttribute('type', 'text');
53 setzeZahl(feld, wert);
54 feld.setAttribute('type', zuvor);
55 }
56
57 describe('Zahlenfeld: Rueckfall auf den zuletzt uebernommenen Wert', () => {
58 it('stellt beim geleerten Pflichtfeld den uebernommenen Wert her, nicht den Bauwert', () => {
59 // Engstellenlaenge im gefuehrten Einstieg: gebaut mit 120, eingetragen 60.
60 let gerechnet = 120;
61 const feld = numberCell(
62 120,
63 (wert) => {
64 if (wert !== null) gerechnet = wert;
65 },
66 { min: 10, max: 400, step: 10, einheit: 'm' },
67 );
68 feld.setAttribute('aria-label', 'Länge der Engstelle');
69 document.body.append(feld);
70
71 setzeZahl(feld, '60');
72 expect(gerechnet, 'der Aufrufer rechnet ab hier mit 60').toBe(60);
73
74 setzeZahl(feld, '');
75
76 expect(feld.value, 'im Feld steht der zuletzt uebernommene Wert').toBe('60');
77 expect(letzteWarnung()).toContain('Der vorherige Wert 60 wurde wiederhergestellt');
78 expect(letzteWarnung(), 'der Bauwert ist nicht mehr der vorherige Wert').not.toContain('120');
79 expect(gerechnet, 'eine abgelehnte Eingabe aendert die Rechnung nicht').toBe(60);
80 });
81
82 it('nennt bei unlesbarer Eingabe den uebernommenen Wert im Feld und im Satz', () => {
83 const feld = numberCell(120, () => {}, { min: 10, max: 400, step: 10, einheit: 'm' });
84 feld.setAttribute('aria-label', 'Länge der Engstelle');
85 document.body.append(feld);
86
87 setzeZahl(feld, '60');
88 setzeUnlesbar(feld, 'abc');
89
90 expect(feld.value).toBe('60');
91 expect(letzteWarnung()).toContain('Der vorherige Wert 60 gilt weiter');
92 expect(letzteWarnung()).not.toContain('120');
93 });
94
95 it('faellt nach einer begrenzten Eingabe auf den begrenzten Wert zurueck', () => {
96 // Zulaessige Hoechstgeschwindigkeit: 200 wird auf 130 begrenzt und genau so
97 // weitergegeben - 130 ist ab da der zuletzt uebernommene Wert.
98 let gerechnet = 50;
99 const feld = numberCell(
100 50,
101 (wert) => {
102 if (wert !== null) gerechnet = wert;
103 },
104 { min: 5, max: 130, step: 5, einheit: 'km/h' },
105 );
106 feld.setAttribute('aria-label', 'Zulässige Höchstgeschwindigkeit');
107 document.body.append(feld);
108
109 setzeZahl(feld, '200');
110 expect(feld.value, 'begrenzt auf die Obergrenze').toBe('130');
111 expect(gerechnet).toBe(130);
112
113 setzeZahl(feld, '');
114
115 expect(feld.value).toBe('130');
116 expect(letzteWarnung()).toContain('Der vorherige Wert 130 wurde wiederhergestellt');
117 });
118
119 it('faellt bei uebernommenen Werten ausserhalb der Grenzen auf diesen Wert zurueck', () => {
120 // `ausserhalbUebernehmen`: Der Wert wird NICHT begrenzt, sondern
121 // unveraendert weitergereicht - dann ist er auch der Rueckfall.
122 const feld = numberCell(15, () => {}, {
123 min: 0,
124 max: 100,
125 step: 1,
126 einheit: '%',
127 ausserhalbUebernehmen: 'Außerhalb wird die Angabe verworfen.',
128 });
129 feld.setAttribute('aria-label', 'Lastzüge am SV');
130 document.body.append(feld);
131
132 setzeZahl(feld, '120');
133 expect(feld.value, 'der Wert bleibt unveraendert stehen').toBe('120');
134
135 setzeUnlesbar(feld, 'abc');
136
137 expect(feld.value).toBe('120');
138 expect(letzteWarnung()).toContain('Der vorherige Wert 120 gilt weiter');
139 expect(letzteWarnung(), 'der Bauwert ist nicht mehr der vorherige Wert').not.toContain('15');
140 });
141
142 it('hat nach einem zulaessig geleerten Feld keinen vorherigen Wert mehr', () => {
143 // `allowEmpty`: Das leere Feld wird als `null` uebernommen. Danach gibt es
144 // keinen vorherigen Wert - der Satz darf keinen nennen.
145 const feld = numberCell(12, () => {}, { min: 0, max: 100, allowEmpty: true });
146 feld.setAttribute('aria-label', 'Lastzüge am SV');
147 document.body.append(feld);
148
149 setzeZahl(feld, '');
150 expect(feld.value).toBe('');
151
152 setzeUnlesbar(feld, 'abc');
153
154 expect(feld.value, 'ohne vorherigen Wert bleibt das Feld leer').toBe('');
155 expect(letzteWarnung(), 'ohne vorherigen Wert steht keine Zahl im Satz').not.toMatch(/\d/);
156 });
157
158 it('laesst den Bauwert stehen, solange nichts uebernommen wurde', () => {
159 // Gegenprobe: Wo noch keine Eingabe angekommen ist, IST der Bauwert der
160 // zuletzt gueltige Wert - an diesem Fall aendert sich nichts.
161 const feld = numberCell(120, () => {}, { min: 10, max: 400, step: 10, einheit: 'm' });
162 feld.setAttribute('aria-label', 'Länge der Engstelle');
163 document.body.append(feld);
164
165 setzeZahl(feld, '');
166
167 expect(feld.value).toBe('120');
168 expect(letzteWarnung()).toContain('Der vorherige Wert 120 wurde wiederhergestellt');
169 });
170 });