import { afterEach, describe, expect, it } from 'vitest'; import { ProjectStore } from '@/app/store'; import { createStandardIntersectionProject } from '@/domain/model/factory'; import { bereichsHinweis } from '@/ui/inputs'; import { phasesView } from '@/ui/views/phasesView'; /** * Strittiger Befund S5 zur Fassung 5.10.0 - das Feld "Versatz zur * Koordinierung" nannte seinen Wertebereich nirgends. * * Das Feld traegt `min: 0, max: 300` und ist Pflichtangabe. Sein eigener * Hinweis verdraengte aber den aus `numberCell` abgeleiteten Bereichssatz: * `beschriftetesFeld` bildet `optionen.hinweis ?? eigenerBereich` und entfernt * `data-bereich` unbenutzt (ui/dom.ts). Vor der Eingabe erfuhr weder ein * Sehender noch eine Sprachausgabe, welche Grenzen gelten; wer 400 eintrug, * bekam den Bereich erst in der Warnmeldung genannt - nachdem der Wert schon * auf 300 gekuerzt war. Genau diesen Zustand beschreibt der Kommentar bei * `bereichsHinweis` als den zu behebenden, und zu WCAG 3.3.2 ist ohne * Einschraenkung zugesagt, JEDES Zahlenfeld fuehre seinen * zulaessigen Bereich als ganzen Satz mit Einheit und Angabe, ob es leer * bleiben darf. * * Die drei Nachbarstellen mit eigenem Hinweis machen es vor (Umlaufzeit und * feste Freigabezeit in dieser Ansicht, vZul in der Projektansicht); der * Versatz war die einzige Ausnahme im ganzen Programm. * * ZUM EINWAND ("keine fachliche Wirkung, und der Satz waere fachlich zu weit"): * Der Satz spricht ueber das FELD, und was er sagt, setzt das Feld auch durch. * Dass der Wert in keine Rechnung eingeht, steht im selben Hinweis unmittelbar * dahinter - deshalb pruefen die Faelle unten beide Saetze zusammen. */ const STICHTAG = new Date('2026-01-01T00:00:00Z'); let aufraeumen: (() => void) | null = null; afterEach(() => { aufraeumen?.(); aufraeumen = null; document.body.replaceChildren(); }); function zeichne(): HTMLElement { const wurzel = document.createElement('div'); document.body.append(wurzel); const schliessen = phasesView.render(wurzel, { store: new ProjectStore(createStandardIntersectionProject('Versatz', STICHTAG)), refresh: () => {}, navigate: () => {}, target: null, }); aufraeumen = () => { schliessen(); wurzel.remove(); }; return wurzel; } /** Feld samt Beschriftung und Hinweis, an seiner Beschriftung gesucht. */ function feld(wurzel: HTMLElement, beschriftung: string): HTMLElement { const knoten = [...wurzel.querySelectorAll('div.feld')].find( (f) => f.querySelector('label')?.textContent === beschriftung, ); expect(knoten, `Feld "${beschriftung}" fehlt`).toBeDefined(); return knoten as HTMLElement; } describe('Befund S5 - das Versatzfeld nennt seinen Wertebereich', () => { it('stellt den Bereichssatz vor den fachlichen Satz', () => { const versatz = feld(zeichne(), 'Versatz zur Koordinierung'); const eingabe = versatz.querySelector('input[type="number"]'); const hinweis = versatz.querySelector('.feld-hinweis')?.textContent ?? ''; expect(hinweis.startsWith('Zulässig sind 0 s bis 300 s.')).toBe(true); expect(hinweis).toContain('Pflichtangabe – das Feld darf nicht leer bleiben.'); /* * Der fachliche Satz bleibt - aber nur zur HAELFTE. Bis 5.34.0 hiess er * "noch nicht ausgewertet: Diese Fassung berechnet keine Gruene Welle". * Seit 5.35.0 rechnet sie eine (Ansicht "Koordinierung"); was bleibt, ist * die Aussage, dass der Versatz den EIGENEN Signalzeitenplan nicht * verschiebt. Eine halb zurueckgenommene Zusage waere hier der Fehler. */ expect(hinweis).not.toContain('noch nicht ausgewertet'); expect(hinweis).toContain('verschiebt den Signalzeitenplan dieses Knotenpunkts nicht'); expect(hinweis).toContain('Ansicht „Koordinierung"'); // Und er wird mit vorgelesen, nicht nur gedruckt. expect(eingabe?.getAttribute('aria-describedby')).toBe( versatz.querySelector('.feld-hinweis')?.id, ); }); it('bildet Satz und Feldgrenzen aus denselben Zahlen', () => { // Von Hand danebengeschrieben liefe der Satz beim naechsten Grenzwechsel // auseinander - genau davor warnt der Kommentar am Umlaufzeitfeld daneben. const versatz = feld(zeichne(), 'Versatz zur Koordinierung'); const eingabe = versatz.querySelector('input[type="number"]'); const min = Number(eingabe?.getAttribute('min')); const max = Number(eingabe?.getAttribute('max')); expect(min).toBe(0); expect(max).toBe(300); const hinweis = versatz.querySelector('.feld-hinweis')?.textContent ?? ''; expect(hinweis.startsWith(bereichsHinweis({ min, max, step: 1, einheit: 's' }))).toBe(true); }); it('laesst die Nachbarfelder unveraendert', () => { const wurzel = zeichne(); const freigabe = feld(wurzel, 'Feste Freigabezeit der 1. Phase'); expect(freigabe.querySelector('.feld-hinweis')?.textContent).toBe( 'Zulässig sind 0 s bis 200 s. Leer lassen, um bedarfsgerecht zu verteilen.', ); }); });