lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ tests app neuesProjektUngespeichert.test.ts
| 1 | import { describe, expect, it } from 'vitest'; |
| 2 | import { ProjectStore } from '@/app/store'; |
| 3 | import { createEmptyProject, createStandardIntersectionProject } from '@/domain/model/factory'; |
| 4 | |
| 5 | /** |
| 6 | * Ein neu gesetzter Stand ohne Datei ist nicht "gespeichert" (Befund 38). |
| 7 | * |
| 8 | * `replace()` loeschte den Aenderungsmerker bedingungslos - auch dort, wo kein |
| 9 | * Dateipfad mitkam. Nach "Neu" > "Gefuehrter Einstieg" steht das Ergebnis von |
| 10 | * drei bis vier beantworteten Fragen samt Signalgruppen, Konflikten und Phasen |
| 11 | * in keiner Datei, die Kopfzeile meldete trotzdem "gespeichert", und die |
| 12 | * Waechter vor dem Verwerfen ("Neu", "Öffnen", Fenster schliessen) haengen |
| 13 | * alle an diesem Merker. Ein zweites Strg+N lief deshalb ohne Rueckfrage |
| 14 | * durch, `replace` leerte die Rueckgaengig-Kette, und der anschliessende |
| 15 | * `flush()` schrieb ueber den einen Sitzungssatz. |
| 16 | * |
| 17 | * Dieselbe Lage war fuer den Wiederanlauf aus dem Sitzungsspeicher bereits |
| 18 | * behoben (src/main.ts, `store.markDirty()`); der zweite Weg zu einem Projekt |
| 19 | * ohne Datei hatte sie nicht. |
| 20 | * |
| 21 | * GEGEN DEN ALTSTAND schlagen der erste und der vierte Fall fehl (dirty war |
| 22 | * false; die Benachrichtigung trug false statt true). Der zweite und der dritte |
| 23 | * halten die Grenzen der Regel fest und bestehen auch dort - sie sind der |
| 24 | * Nachweis, dass die Behebung nicht ueber das Ziel hinausschiesst. |
| 25 | */ |
| 26 | |
| 27 | const FESTES_DATUM = new Date('2026-01-01T00:00:00Z'); |
| 28 | |
| 29 | describe('Neu gesetzter Stand ohne Datei', () => { |
| 30 | it('gilt als ungespeichert, sobald etwas darin steht', () => { |
| 31 | const store = new ProjectStore(createEmptyProject()); |
| 32 | |
| 33 | // Genau das, was "Neu" mit dem Ergebnis des gefuehrten Einstiegs tut: |
| 34 | // ersetzen ohne Dateipfad. |
| 35 | store.replace(createStandardIntersectionProject('Gefuehrter Einstieg', FESTES_DATUM)); |
| 36 | |
| 37 | expect(store.getState().dirty).toBe(true); |
| 38 | expect(store.getState().filePath).toBe(null); |
| 39 | }); |
| 40 | |
| 41 | it('gilt als gespeichert, wenn ein Dateipfad mitkommt', () => { |
| 42 | const store = new ProjectStore(createEmptyProject()); |
| 43 | |
| 44 | store.replace( |
| 45 | createStandardIntersectionProject('Geoeffnet', FESTES_DATUM), |
| 46 | 'C:/tmp/knotenpunkt.lsap', |
| 47 | ); |
| 48 | |
| 49 | // Beim Oeffnen steht der Stand in einer Datei - da ist nichts ungesichert. |
| 50 | expect(store.getState().dirty).toBe(false); |
| 51 | expect(store.getState().filePath).toBe('C:/tmp/knotenpunkt.lsap'); |
| 52 | }); |
| 53 | |
| 54 | it('laesst ein unberuehrtes Projekt sauber', () => { |
| 55 | const store = new ProjectStore(createStandardIntersectionProject('Vorher', FESTES_DATUM)); |
| 56 | |
| 57 | store.replace(createEmptyProject()); |
| 58 | |
| 59 | // "Neu" > "Leeres Projekt": Es gibt keine Arbeit, die verloren gehen |
| 60 | // koennte. Eine Rueckfrage, die immer kommt, schuetzt nichts. |
| 61 | expect(store.getState().dirty).toBe(false); |
| 62 | }); |
| 63 | |
| 64 | it('meldet den Merker in derselben Benachrichtigung wie den Stand', () => { |
| 65 | const store = new ProjectStore(createEmptyProject()); |
| 66 | const gesehen: boolean[] = []; |
| 67 | store.subscribe((state) => { |
| 68 | gesehen.push(state.dirty); |
| 69 | }); |
| 70 | |
| 71 | store.replace(createStandardIntersectionProject('Assistent', FESTES_DATUM)); |
| 72 | |
| 73 | // Kopfzeile, Fenstertitel und Statusleiste lesen genau diesen Zustand; |
| 74 | // ein nachgereichter zweiter Merker liesse sie kurz etwas anderes sagen. |
| 75 | expect(gesehen).toEqual([true]); |
| 76 | }); |
| 77 | }); |