lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ tests ui versatzbereich.test.ts
| 1 | import { afterEach, describe, expect, it } from 'vitest'; |
| 2 | import { ProjectStore } from '@/app/store'; |
| 3 | import { createStandardIntersectionProject } from '@/domain/model/factory'; |
| 4 | import { bereichsHinweis } from '@/ui/inputs'; |
| 5 | import { phasesView } from '@/ui/views/phasesView'; |
| 6 | |
| 7 | /** |
| 8 | * Strittiger Befund S5 zur Fassung 5.10.0 - das Feld "Versatz zur |
| 9 | * Koordinierung" nannte seinen Wertebereich nirgends. |
| 10 | * |
| 11 | * Das Feld traegt `min: 0, max: 300` und ist Pflichtangabe. Sein eigener |
| 12 | * Hinweis verdraengte aber den aus `numberCell` abgeleiteten Bereichssatz: |
| 13 | * `beschriftetesFeld` bildet `optionen.hinweis ?? eigenerBereich` und entfernt |
| 14 | * `data-bereich` unbenutzt (ui/dom.ts). Vor der Eingabe erfuhr weder ein |
| 15 | * Sehender noch eine Sprachausgabe, welche Grenzen gelten; wer 400 eintrug, |
| 16 | * bekam den Bereich erst in der Warnmeldung genannt - nachdem der Wert schon |
| 17 | * auf 300 gekuerzt war. Genau diesen Zustand beschreibt der Kommentar bei |
| 18 | * `bereichsHinweis` als den zu behebenden, und zu WCAG 3.3.2 ist ohne |
| 19 | * Einschraenkung zugesagt, JEDES Zahlenfeld fuehre seinen |
| 20 | * zulaessigen Bereich als ganzen Satz mit Einheit und Angabe, ob es leer |
| 21 | * bleiben darf. |
| 22 | * |
| 23 | * Die drei Nachbarstellen mit eigenem Hinweis machen es vor (Umlaufzeit und |
| 24 | * feste Freigabezeit in dieser Ansicht, vZul in der Projektansicht); der |
| 25 | * Versatz war die einzige Ausnahme im ganzen Programm. |
| 26 | * |
| 27 | * ZUM EINWAND ("keine fachliche Wirkung, und der Satz waere fachlich zu weit"): |
| 28 | * Der Satz spricht ueber das FELD, und was er sagt, setzt das Feld auch durch. |
| 29 | * Dass der Wert in keine Rechnung eingeht, steht im selben Hinweis unmittelbar |
| 30 | * dahinter - deshalb pruefen die Faelle unten beide Saetze zusammen. |
| 31 | */ |
| 32 | |
| 33 | const STICHTAG = new Date('2026-01-01T00:00:00Z'); |
| 34 | |
| 35 | let aufraeumen: (() => void) | null = null; |
| 36 | afterEach(() => { |
| 37 | aufraeumen?.(); |
| 38 | aufraeumen = null; |
| 39 | document.body.replaceChildren(); |
| 40 | }); |
| 41 | |
| 42 | function zeichne(): HTMLElement { |
| 43 | const wurzel = document.createElement('div'); |
| 44 | document.body.append(wurzel); |
| 45 | const schliessen = phasesView.render(wurzel, { |
| 46 | store: new ProjectStore(createStandardIntersectionProject('Versatz', STICHTAG)), |
| 47 | refresh: () => {}, |
| 48 | navigate: () => {}, |
| 49 | target: null, |
| 50 | }); |
| 51 | aufraeumen = () => { |
| 52 | schliessen(); |
| 53 | wurzel.remove(); |
| 54 | }; |
| 55 | return wurzel; |
| 56 | } |
| 57 | |
| 58 | /** Feld samt Beschriftung und Hinweis, an seiner Beschriftung gesucht. */ |
| 59 | function feld(wurzel: HTMLElement, beschriftung: string): HTMLElement { |
| 60 | const knoten = [...wurzel.querySelectorAll<HTMLElement>('div.feld')].find( |
| 61 | (f) => f.querySelector('label')?.textContent === beschriftung, |
| 62 | ); |
| 63 | expect(knoten, `Feld "${beschriftung}" fehlt`).toBeDefined(); |
| 64 | return knoten as HTMLElement; |
| 65 | } |
| 66 | |
| 67 | describe('Befund S5 - das Versatzfeld nennt seinen Wertebereich', () => { |
| 68 | it('stellt den Bereichssatz vor den fachlichen Satz', () => { |
| 69 | const versatz = feld(zeichne(), 'Versatz zur Koordinierung'); |
| 70 | const eingabe = versatz.querySelector<HTMLInputElement>('input[type="number"]'); |
| 71 | const hinweis = versatz.querySelector('.feld-hinweis')?.textContent ?? ''; |
| 72 | |
| 73 | expect(hinweis.startsWith('Zulässig sind 0 s bis 300 s.')).toBe(true); |
| 74 | expect(hinweis).toContain('Pflichtangabe – das Feld darf nicht leer bleiben.'); |
| 75 | /* |
| 76 | * Der fachliche Satz bleibt - aber nur zur HAELFTE. Bis 5.34.0 hiess er |
| 77 | * "noch nicht ausgewertet: Diese Fassung berechnet keine Gruene Welle". |
| 78 | * Seit 5.35.0 rechnet sie eine (Ansicht "Koordinierung"); was bleibt, ist |
| 79 | * die Aussage, dass der Versatz den EIGENEN Signalzeitenplan nicht |
| 80 | * verschiebt. Eine halb zurueckgenommene Zusage waere hier der Fehler. |
| 81 | */ |
| 82 | expect(hinweis).not.toContain('noch nicht ausgewertet'); |
| 83 | expect(hinweis).toContain('verschiebt den Signalzeitenplan dieses Knotenpunkts nicht'); |
| 84 | expect(hinweis).toContain('Ansicht „Koordinierung"'); |
| 85 | // Und er wird mit vorgelesen, nicht nur gedruckt. |
| 86 | expect(eingabe?.getAttribute('aria-describedby')).toBe( |
| 87 | versatz.querySelector('.feld-hinweis')?.id, |
| 88 | ); |
| 89 | }); |
| 90 | |
| 91 | it('bildet Satz und Feldgrenzen aus denselben Zahlen', () => { |
| 92 | // Von Hand danebengeschrieben liefe der Satz beim naechsten Grenzwechsel |
| 93 | // auseinander - genau davor warnt der Kommentar am Umlaufzeitfeld daneben. |
| 94 | const versatz = feld(zeichne(), 'Versatz zur Koordinierung'); |
| 95 | const eingabe = versatz.querySelector<HTMLInputElement>('input[type="number"]'); |
| 96 | const min = Number(eingabe?.getAttribute('min')); |
| 97 | const max = Number(eingabe?.getAttribute('max')); |
| 98 | expect(min).toBe(0); |
| 99 | expect(max).toBe(300); |
| 100 | |
| 101 | const hinweis = versatz.querySelector('.feld-hinweis')?.textContent ?? ''; |
| 102 | expect(hinweis.startsWith(bereichsHinweis({ min, max, step: 1, einheit: 's' }))).toBe(true); |
| 103 | }); |
| 104 | |
| 105 | it('laesst die Nachbarfelder unveraendert', () => { |
| 106 | const wurzel = zeichne(); |
| 107 | const freigabe = feld(wurzel, 'Feste Freigabezeit der 1. Phase'); |
| 108 | expect(freigabe.querySelector('.feld-hinweis')?.textContent).toBe( |
| 109 | 'Zulässig sind 0 s bis 200 s. Leer lassen, um bedarfsgerecht zu verteilen.', |
| 110 | ); |
| 111 | }); |
| 112 | }); |