lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ tests app wertgleicheAenderung.test.ts
| 1 | import { describe, expect, it } from 'vitest'; |
| 2 | import { ProjectStore } from '@/app/store'; |
| 3 | import * as actions from '@/app/actions'; |
| 4 | import { createStandardIntersectionProject } from '@/domain/model/factory'; |
| 5 | |
| 6 | /** |
| 7 | * Eine Aenderung, die nichts aendert, ist keine Aenderung (Befund 59). |
| 8 | * |
| 9 | * Der Waechter in `update()` prueft nur, ob der Aenderungsschritt dasselbe |
| 10 | * Objekt zurueckgibt. Die Aenderungsfunktionen in `actions.ts` liefern aber |
| 11 | * immer ein neues Objekt - `updateSignalGroup` etwa baut Projekt und |
| 12 | * Signalgruppenliste in jedem Fall neu auf. Kommt aus der Oberflaeche ein |
| 13 | * bereits eingetragener Wert an - der Zahleneingabe passiert das bei jeder |
| 14 | * begrenzten Eingabe -, entstand daraus ein Rueckgaengig-Schritt, der nichts |
| 15 | * rueckgaengig macht, der Aenderungsmerker wurde gesetzt, und `touch()` schrieb |
| 16 | * `meta.modifiedAt` hoch. |
| 17 | * |
| 18 | * Der letzte Punkt ist der schwerste: Das Aenderungsdatum wird als "Zuletzt |
| 19 | * geändert" in die Planunterlage gedruckt. Der Quelltext nennt genau diese |
| 20 | * Verfaelschung als Grund, `markDirty()` nicht ueber `update()` zu fuehren - |
| 21 | * derselbe Massstab gilt fuer den Bedienweg. |
| 22 | * |
| 23 | * GEGEN DEN ALTSTAND schlagen die ersten drei Faelle fehl: der |
| 24 | * Rueckgaengig-Schritt samt Merker, das hochgeschriebene `meta.modifiedAt` und |
| 25 | * die eine Benachrichtigung, die keine sein duerfte. Die letzten drei sind die |
| 26 | * Gegenprobe: Der Vergleich darf keine echte Aenderung verschlucken, auch keine |
| 27 | * tief im Baum und keine, die nur ein Feld entfernt. |
| 28 | */ |
| 29 | |
| 30 | const FESTES_DATUM = new Date('2026-01-01T00:00:00Z'); |
| 31 | |
| 32 | function store(): ProjectStore { |
| 33 | return new ProjectStore(createStandardIntersectionProject('Prueffall', FESTES_DATUM)); |
| 34 | } |
| 35 | |
| 36 | describe('Wertgleiche Aenderung', () => { |
| 37 | it('erzeugt weder Rueckgaengig-Schritt noch Aenderungsmerker', () => { |
| 38 | const s = store(); |
| 39 | const gruppe = s.getProject().signalGroups[0]!; |
| 40 | const vorher = s.getProject(); |
| 41 | |
| 42 | // Genau das, was die Signalgruppenansicht nach einer begrenzten Eingabe im |
| 43 | // Feld "Fahrstreifen" schickt: Der Wert 0 wird auf die Untergrenze 1 |
| 44 | // gezogen - und 1 steht dort bereits. |
| 45 | s.update((p) => actions.updateSignalGroup(p, gruppe.id, { lanes: gruppe.lanes }), { |
| 46 | label: 'Fahrstreifenzahl geändert', |
| 47 | }); |
| 48 | |
| 49 | expect(s.getState().canUndo).toBe(false); |
| 50 | expect(s.getState().undoLabel).toBe(null); |
| 51 | expect(s.getState().dirty).toBe(false); |
| 52 | // Der Stand bleibt derselbe - auch als Objekt, sonst zeichnete jede |
| 53 | // Ansicht ohne Grund neu. |
| 54 | expect(s.getProject()).toBe(vorher); |
| 55 | }); |
| 56 | |
| 57 | it('schreibt das Aenderungsdatum des Projekts nicht hoch', () => { |
| 58 | const s = store(); |
| 59 | const gruppe = s.getProject().signalGroups[0]!; |
| 60 | const vorher = s.getProject().meta.modifiedAt; |
| 61 | |
| 62 | s.update((p) => actions.updateSignalGroup(p, gruppe.id, { vZul: gruppe.vZul }), { |
| 63 | label: 'Höchstgeschwindigkeit geändert', |
| 64 | }); |
| 65 | |
| 66 | // Die Zahl geht als "Zuletzt geändert" in die Planunterlage. |
| 67 | expect(s.getProject().meta.modifiedAt).toBe(vorher); |
| 68 | }); |
| 69 | |
| 70 | it('benachrichtigt die Empfaenger nicht', () => { |
| 71 | const s = store(); |
| 72 | const gruppe = s.getProject().signalGroups[0]!; |
| 73 | let gemeldet = 0; |
| 74 | s.subscribe(() => { |
| 75 | gemeldet += 1; |
| 76 | }); |
| 77 | |
| 78 | s.update((p) => actions.updateSignalGroup(p, gruppe.id, { name: gruppe.name }), { |
| 79 | label: 'Signalgruppe geaendert', |
| 80 | }); |
| 81 | |
| 82 | expect(gemeldet).toBe(0); |
| 83 | }); |
| 84 | |
| 85 | it('laesst eine echte Aenderung unveraendert durch', () => { |
| 86 | const s = store(); |
| 87 | const gruppe = s.getProject().signalGroups[0]!; |
| 88 | |
| 89 | s.update((p) => actions.updateSignalGroup(p, gruppe.id, { lanes: gruppe.lanes + 1 }), { |
| 90 | label: 'Fahrstreifenzahl geändert', |
| 91 | }); |
| 92 | |
| 93 | expect(s.getState().canUndo).toBe(true); |
| 94 | expect(s.getState().dirty).toBe(true); |
| 95 | expect(s.getProject().signalGroups[0]!.lanes).toBe(gruppe.lanes + 1); |
| 96 | }); |
| 97 | |
| 98 | it('erkennt einen Unterschied tief im Baum', () => { |
| 99 | const s = store(); |
| 100 | const gruppe = s.getProject().signalGroups[0]!; |
| 101 | const vorher = s.getProject().demands.find((d) => d.signalGroupId === gruppe.id)!; |
| 102 | |
| 103 | s.update((p) => actions.setDemand(p, gruppe.id, { volume: vorher.volume + 1 }), { |
| 104 | label: 'Verkehrsstärke geändert', |
| 105 | }); |
| 106 | |
| 107 | expect(s.getState().dirty).toBe(true); |
| 108 | expect(s.getProject().demands.find((d) => d.signalGroupId === gruppe.id)!.volume).toBe( |
| 109 | vorher.volume + 1, |
| 110 | ); |
| 111 | }); |
| 112 | |
| 113 | it('erkennt eine entfernte Angabe', () => { |
| 114 | const s = store(); |
| 115 | const gruppe = s.getProject().signalGroups[0]!; |
| 116 | expect(s.getProject().demands.some((d) => d.signalGroupId === gruppe.id)).toBe(true); |
| 117 | |
| 118 | s.update((p) => actions.removeDemand(p, gruppe.id), { label: 'Verkehrsstärke entfernt' }); |
| 119 | |
| 120 | expect(s.getState().dirty).toBe(true); |
| 121 | expect(s.getProject().demands.some((d) => d.signalGroupId === gruppe.id)).toBe(false); |
| 122 | }); |
| 123 | }); |