lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ tests ui einlesemeldungen.test.ts
| 1 | import { readFileSync } from 'node:fs'; |
| 2 | import path from 'node:path'; |
| 3 | import { beforeAll, describe, expect, it, vi } from 'vitest'; |
| 4 | import { createStandardIntersectionProject } from '@/domain/model/factory'; |
| 5 | import { ladeSitzung, saveSession } from '@/services/storage'; |
| 6 | |
| 7 | /** |
| 8 | * Einlesemeldungen erreichen den Bearbeiter auch auf dem Sitzungsweg |
| 9 | * (zweiter Anteil von Befund 12). |
| 10 | * |
| 11 | * Beim Oeffnen einer Datei zeigt `showImportIssues` jede einzelne Meldung mit |
| 12 | * Art, Bereich und Wortlaut. Der Wiederanlauf aus dem Sitzungsspeicher nannte |
| 13 | * dagegen nur die ANZAHL - "(1 Hinweise beim Einlesen)". Gerade der Wortlaut |
| 14 | * ist aber die Auskunft: Wurde ein Raeumweg beim Einlesen auf den zulaessigen |
| 15 | * Hoechstwert der Anlagenart begrenzt, aendert das die Zwischenzeiten und damit |
| 16 | * den ganzen Signalzeitenplan. Wer nur eine Zahl liest, erfaehrt nie, welcher |
| 17 | * Wert angefasst wurde. |
| 18 | * |
| 19 | * Geprueft wird am echten Einstiegspunkt: `src/main.ts` wird eingelesen und |
| 20 | * startet die ganze Anwendung; nur der Sitzungsspeicher ist ersetzt. |
| 21 | */ |
| 22 | |
| 23 | const MELDUNG = |
| 24 | 'Räumweg (Anlagenart Fußgängerschutzanlage) liegt mit 400 über dem zulässigen ' + |
| 25 | 'Höchstwert 100; der Wert wurde begrenzt'; |
| 26 | |
| 27 | vi.mock('@/services/storage', async (importOriginal) => { |
| 28 | const echt = await importOriginal<typeof import('@/services/storage')>(); |
| 29 | return { |
| 30 | ...echt, |
| 31 | ladeSitzung: vi.fn(), |
| 32 | saveSession: vi.fn(), |
| 33 | addRecoveryPoint: vi.fn(), |
| 34 | }; |
| 35 | }); |
| 36 | |
| 37 | async function warte(ms = 0): Promise<void> { |
| 38 | await new Promise((fertig) => setTimeout(fertig, ms)); |
| 39 | } |
| 40 | |
| 41 | /** Die Ueberschriften aller gerade offenen Fenster. */ |
| 42 | function offeneFenster(): string[] { |
| 43 | return [...document.querySelectorAll('.dialog-hintergrund h2')].map( |
| 44 | (h) => h.textContent?.trim() ?? '', |
| 45 | ); |
| 46 | } |
| 47 | |
| 48 | beforeAll(async () => { |
| 49 | vi.mocked(ladeSitzung).mockResolvedValue({ |
| 50 | art: 'geladen', |
| 51 | stand: { |
| 52 | project: createStandardIntersectionProject(), |
| 53 | issues: [{ path: 'signalGroups[0].clearingPath', severity: 'warnung', message: MELDUNG }], |
| 54 | repaired: true, |
| 55 | migrated: false, |
| 56 | sourceVersion: '13', |
| 57 | intergreenComparison: [], |
| 58 | }, |
| 59 | }); |
| 60 | vi.mocked(saveSession).mockResolvedValue({ ok: true, message: '' }); |
| 61 | |
| 62 | localStorage.clear(); |
| 63 | document.body.replaceChildren(); |
| 64 | const wurzel = document.createElement('div'); |
| 65 | wurzel.id = 'anwendung'; |
| 66 | document.body.append(wurzel); |
| 67 | |
| 68 | await import('@/main'); |
| 69 | // Laenger als die 400 ms, mit denen der Wiederanlauf seine Kurzmeldungen |
| 70 | // nachreicht - sonst waere der alte Zaehlhinweis noch gar nicht da. |
| 71 | await warte(500); |
| 72 | }); |
| 73 | |
| 74 | describe('Einlesemeldungen beim Wiederanlauf', () => { |
| 75 | it('zeigt dieselbe Aufstellung wie der Öffnen-Weg', () => { |
| 76 | expect(offeneFenster()).toContain('Hinweise zum Import'); |
| 77 | }); |
| 78 | |
| 79 | it('nennt den Wortlaut der Meldung, nicht nur ihre Anzahl', () => { |
| 80 | const fenster = document.querySelector('.dialog-hintergrund'); |
| 81 | expect(fenster?.textContent).toContain(MELDUNG); |
| 82 | }); |
| 83 | }); |
| 84 | |
| 85 | /** |
| 86 | * WANN die Aufstellung erscheint, wird an einer Stelle entschieden. |
| 87 | * |
| 88 | * Seit der Sitzungsweg dieselbe Aufstellung zeigt wie der Oeffnen-Weg, stand |
| 89 | * die Bedingung "Meldungen ODER Gegenueberstellung" wortgleich an beiden |
| 90 | * Aufrufstellen. Die Begruendung fuer ihren zweiten Teil - bei einem |
| 91 | * uebernommenen 4.x-Projekt ist die Gegenueberstellung die wichtigste Auskunft, |
| 92 | * auch wenn sonst nichts auffiel - stand nur an einer davon. Wer die Bedingung |
| 93 | * spaeter erweitert, findet die zweite Stelle nicht, und die beiden Wege zeigen |
| 94 | * wieder Unterschiedliches. Deshalb steht die Entscheidung an einer Stelle. |
| 95 | * |
| 96 | * Am Quelltext festgehalten und nicht am Verhalten: Beide Fassungen zeigen |
| 97 | * heute dasselbe Fenster - die Doppelung ist kein Unterschied im Ergebnis, |
| 98 | * sondern die Anlage dafuer, dass einer entsteht. Dieselbe Ueberlegung traegt |
| 99 | * `tests/ui/stilblattZusagen.test.ts`. |
| 100 | */ |
| 101 | describe('Die Entscheidung über die Aufstellung "Hinweise zum Import"', () => { |
| 102 | const quelltext = (): string => |
| 103 | readFileSync(path.resolve(__dirname, '..', '..', 'src', 'main.ts'), 'utf8'); |
| 104 | |
| 105 | it('steht an keiner Aufrufstelle mehr', () => { |
| 106 | const stellen = quelltext().split('intergreenComparison.length').length - 1; |
| 107 | expect( |
| 108 | stellen, |
| 109 | 'Eine Aufrufstelle entscheidet wieder selbst, ob die Aufstellung erscheint.', |
| 110 | ).toBe(0); |
| 111 | }); |
| 112 | |
| 113 | it('steht in showImportIssues selbst', () => { |
| 114 | const text = quelltext(); |
| 115 | const beginn = text.indexOf('function showImportIssues('); |
| 116 | expect(beginn, 'showImportIssues nicht gefunden').toBeGreaterThanOrEqual(0); |
| 117 | const bisFenster = text.slice(beginn, text.indexOf('new Panel(', beginn)); |
| 118 | // Vor dem ersten Fenster steht der fruehe Ruecksprung, und er wiegt beides |
| 119 | // ab: die Meldungen und die Gegenueberstellung. |
| 120 | expect(bisFenster).toContain('issues.length'); |
| 121 | expect(bisFenster).toContain('vergleich.length'); |
| 122 | expect(bisFenster).toContain('return;'); |
| 123 | }); |
| 124 | }); |