lsa-planer

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

/ tests app wertgleicheAenderung.test.ts

4,7 KB Rohdatei
tests/app/wertgleicheAenderung.test.ts — 123 Zeilen
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 });