lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ tests ui begrenzteEingabe.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 type { Project } from '@/domain/model/project'; |
| 5 | import { meldungsverlauf } from '@/ui/feedback'; |
| 6 | import { signalGroupsView } from '@/ui/views/signalGroupsView'; |
| 7 | |
| 8 | /** |
| 9 | * Eine auf den bereits eingetragenen Wert begrenzte Eingabe aendert nichts |
| 10 | * (Befund 59). |
| 11 | * |
| 12 | * Was der Anwender sonst erlebt: In der Signalgruppenansicht steht im Feld |
| 13 | * "Fahrstreifen" die 1 - der Werkwert und zugleich die Untergrenze. Er traegt |
| 14 | * eine 0 ein und tabbt weiter. Die Eingabe wird auf 1 begrenzt, im Feld steht |
| 15 | * wieder 1 - und trotzdem meldet die Kopfzeile "ungespeicherte Änderungen", |
| 16 | * "Rueckgaengig" wird mit dem Schritt "Fahrstreifenzahl geändert" aktiv, und |
| 17 | * das Aenderungsdatum des Projekts ist neu. Letzteres wird als "Zuletzt |
| 18 | * geändert" in die Planunterlage gedruckt. |
| 19 | * |
| 20 | * Geprueft wird ueber den Bedienweg: die echte Ansicht, das echte Zahlenfeld, |
| 21 | * das echte `change`-Ereignis. Der Vergleich, der es abfaengt, sitzt in |
| 22 | * `ProjectStore.update` - siehe tests/app/wertgleicheAenderung.test.ts. |
| 23 | * |
| 24 | * GEGEN DEN ALTSTAND schlaegt der erste Fall fehl. Der zweite haelt fest, dass |
| 25 | * die Rueckmeldung an den Anwender bleibt - sie ist die einzige Auskunft |
| 26 | * darueber, dass die Eingabe abgelehnt wurde -, der dritte ist die Gegenprobe |
| 27 | * mit einer Zahl, die wirklich etwas aendert. |
| 28 | */ |
| 29 | |
| 30 | const FESTES_DATUM = new Date('2026-01-01T00:00:00Z'); |
| 31 | |
| 32 | let aufraeumen: (() => void) | null = null; |
| 33 | |
| 34 | afterEach(() => { |
| 35 | aufraeumen?.(); |
| 36 | aufraeumen = null; |
| 37 | }); |
| 38 | |
| 39 | function zeichne(project: Project): { wurzel: HTMLElement; store: ProjectStore } { |
| 40 | const wurzel = document.createElement('div'); |
| 41 | document.body.append(wurzel); |
| 42 | const store = new ProjectStore(project); |
| 43 | const abmelden = signalGroupsView.render(wurzel, { |
| 44 | store, |
| 45 | refresh: () => {}, |
| 46 | navigate: () => {}, |
| 47 | target: null, |
| 48 | }); |
| 49 | aufraeumen = () => { |
| 50 | abmelden(); |
| 51 | wurzel.remove(); |
| 52 | }; |
| 53 | return { wurzel, store }; |
| 54 | } |
| 55 | |
| 56 | function fahrstreifenfeld(wurzel: HTMLElement, gruppe: string): HTMLInputElement { |
| 57 | const feld = wurzel.querySelector<HTMLInputElement>( |
| 58 | `input[aria-label="Anzahl der Fahrstreifen, ${gruppe}"]`, |
| 59 | ); |
| 60 | if (!feld) throw new Error(`Feld "Fahrstreifen" der Gruppe ${gruppe} fehlt.`); |
| 61 | return feld; |
| 62 | } |
| 63 | |
| 64 | function trageEin(feld: HTMLInputElement, text: string): void { |
| 65 | feld.value = text; |
| 66 | feld.dispatchEvent(new Event('change', { bubbles: true })); |
| 67 | } |
| 68 | |
| 69 | describe('Auf den vorhandenen Wert begrenzte Eingabe', () => { |
| 70 | it('laesst Aenderungsmerker, Rueckgaengig und Aenderungsdatum unberuehrt', () => { |
| 71 | const { wurzel, store } = zeichne(createStandardIntersectionProject('Prueffall', FESTES_DATUM)); |
| 72 | const feld = fahrstreifenfeld(wurzel, 'K1'); |
| 73 | expect(feld.value).toBe('1'); |
| 74 | const geaendertAm = store.getProject().meta.modifiedAt; |
| 75 | |
| 76 | trageEin(feld, '0'); |
| 77 | |
| 78 | // Die Begrenzung wirkt weiter - im Feld steht wieder der alte Wert. |
| 79 | expect(feld.value).toBe('1'); |
| 80 | expect(store.getState().dirty).toBe(false); |
| 81 | expect(store.getState().canUndo).toBe(false); |
| 82 | // Diese Zahl steht als "Zuletzt geändert" in der Planunterlage. |
| 83 | expect(store.getProject().meta.modifiedAt).toBe(geaendertAm); |
| 84 | }); |
| 85 | |
| 86 | it('meldet dem Anwender trotzdem, was mit seiner Eingabe geschehen ist', () => { |
| 87 | const { wurzel } = zeichne(createStandardIntersectionProject('Prueffall', FESTES_DATUM)); |
| 88 | |
| 89 | trageEin(fahrstreifenfeld(wurzel, 'K1'), '0'); |
| 90 | |
| 91 | const meldung = meldungsverlauf()[0]!; |
| 92 | expect(meldung.art).toBe('warnung'); |
| 93 | expect(meldung.text).toContain('außerhalb des zulässigen Bereichs'); |
| 94 | expect(meldung.text).toContain('Eingetragen wurde 1'); |
| 95 | }); |
| 96 | |
| 97 | it('nimmt eine Eingabe an, die den Wert wirklich aendert', () => { |
| 98 | const { wurzel, store } = zeichne(createStandardIntersectionProject('Prueffall', FESTES_DATUM)); |
| 99 | |
| 100 | trageEin(fahrstreifenfeld(wurzel, 'K1'), '2'); |
| 101 | |
| 102 | expect(store.getState().dirty).toBe(true); |
| 103 | expect(store.getState().undoLabel).toBe('Fahrstreifenzahl geändert'); |
| 104 | expect(store.getProject().signalGroups[0]!.lanes).toBe(2); |
| 105 | }); |
| 106 | }); |