lsa-planer

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

/ tests electron schriftgroesse.test.ts

8,2 KB Rohdatei
tests/electron/schriftgroesse.test.ts — 224 Zeilen
1 import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
2 import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3 import { tmpdir } from 'node:os';
4 import path from 'node:path';
5 import {
6 SCHRIFT_STANDARD,
7 SCHRIFT_STUFEN,
8 gueltigeStufe,
9 liesSchriftgroesse,
10 nachbarstufe,
11 schreibeSchriftgroesse,
12 schriftsicherung,
13 } from '../../electron/schriftgroesse';
14
15 /**
16 * Befund L11 (EN 301 549 Kap. 11.7).
17 *
18 * Der Altbestand hatte weder Stufen noch eine Sicherung - dieses Modul gab es
19 * nicht. Geprueft wird deshalb nicht nur, dass es die Stufen gibt, sondern die
20 * beiden Eigenschaften, an denen der Befund haengt: Die Einstellung ueberlebt
21 * den Programmstart, und ein unbrauchbarer Dateiinhalt haelt die Anwendung nicht
22 * auf.
23 */
24
25 describe('Schriftgroesse: Stufen', () => {
26 it('enthält die von WCAG 1.4.4 geforderten 200 % und die im Befund genannten 175 %', () => {
27 expect(SCHRIFT_STUFEN).toContain(200);
28 expect(SCHRIFT_STUFEN).toContain(175);
29 expect(SCHRIFT_STUFEN).toContain(SCHRIFT_STANDARD);
30 });
31
32 it('steigt lückenlos an und lässt auch kleiner als 100 % zu', () => {
33 const sortiert = [...SCHRIFT_STUFEN].sort((a, b) => a - b);
34 expect(SCHRIFT_STUFEN).toEqual(sortiert);
35 expect(new Set(SCHRIFT_STUFEN).size).toBe(SCHRIFT_STUFEN.length);
36 expect(Math.min(...SCHRIFT_STUFEN)).toBeLessThan(SCHRIFT_STANDARD);
37 });
38
39 it('rundet Zwischenwerte auf die nächstgelegene Stufe', () => {
40 // Ein Zwischenwert entsteht durch Strg+Mausrad oder durch Handarbeit an der
41 // Datei. Er darf nicht bestehen bleiben - sonst zeigt die Auswahlmarke im
42 // Menue auf eine Stufe, die gar nicht gilt.
43 expect(gueltigeStufe(160)).toBe(150);
44 expect(gueltigeStufe(170)).toBe(175);
45 expect(gueltigeStufe(5)).toBe(Math.min(...SCHRIFT_STUFEN));
46 expect(gueltigeStufe(5000)).toBe(Math.max(...SCHRIFT_STUFEN));
47 });
48
49 it('weist alles zurück, was keine brauchbare Zahl ist', () => {
50 expect(gueltigeStufe('175')).toBe(SCHRIFT_STANDARD);
51 expect(gueltigeStufe(null)).toBe(SCHRIFT_STANDARD);
52 expect(gueltigeStufe(undefined)).toBe(SCHRIFT_STANDARD);
53 expect(gueltigeStufe(Number.NaN)).toBe(SCHRIFT_STANDARD);
54 expect(gueltigeStufe(Number.POSITIVE_INFINITY)).toBe(SCHRIFT_STANDARD);
55 expect(gueltigeStufe({ prozent: 175 })).toBe(SCHRIFT_STANDARD);
56 });
57
58 it('geht eine Stufe weiter und bleibt an den Enden stehen', () => {
59 const kleinste = Math.min(...SCHRIFT_STUFEN);
60 const groesste = Math.max(...SCHRIFT_STUFEN);
61 expect(nachbarstufe(100, 1)).toBe(125);
62 expect(nachbarstufe(125, -1)).toBe(100);
63 expect(nachbarstufe(groesste, 1)).toBe(groesste);
64 expect(nachbarstufe(kleinste, -1)).toBe(kleinste);
65 });
66
67 it('führt von 100 % in fünf Schritten mindestens auf 200 %', () => {
68 let stufe = SCHRIFT_STANDARD;
69 for (let i = 0; i < 5; i += 1) stufe = nachbarstufe(stufe, 1);
70 expect(stufe).toBeGreaterThanOrEqual(200);
71 });
72 });
73
74 describe('Schriftgroesse: Sicherung', () => {
75 let ordner = '';
76 let datei = '';
77
78 beforeEach(() => {
79 ordner = mkdtempSync(path.join(tmpdir(), 'lsa-ansicht-'));
80 datei = path.join(ordner, 'ansicht.json');
81 });
82
83 afterEach(() => {
84 rmSync(ordner, { recursive: true, force: true });
85 });
86
87 it('gibt die Vorgabe zurück, solange nichts gesichert wurde', () => {
88 expect(liesSchriftgroesse(datei)).toBe(SCHRIFT_STANDARD);
89 });
90
91 it('überlebt den Programmstart - das ist der Kern des Befunds', () => {
92 schreibeSchriftgroesse(datei, 175);
93 expect(liesSchriftgroesse(datei)).toBe(175);
94 });
95
96 it('behält fremde Schlüssel der Datei', () => {
97 // Fenstergroesse und Fensterlage gehoeren spaeter in dieselbe Datei; sie
98 // duerfen beim Sichern der Schriftgroesse nicht verlorengehen.
99 writeFileSync(datei, JSON.stringify({ fensterbreite: 1200 }), 'utf8');
100 schreibeSchriftgroesse(datei, 150);
101 const gelesen: unknown = JSON.parse(readFileSync(datei, 'utf8'));
102 expect(gelesen).toMatchObject({ fensterbreite: 1200, schriftgroesseProzent: 150 });
103 });
104
105 it('schreibt ohne Byte-Reihenfolge-Marke und mit abschließendem Zeilenende', () => {
106 schreibeSchriftgroesse(datei, 125);
107 const roh = readFileSync(datei);
108 expect(roh[0]).not.toBe(0xef);
109 expect(roh.toString('utf8').endsWith('\n')).toBe(true);
110 });
111
112 it('hält bei beschädigtem Inhalt an der Vorgabe fest, statt zu scheitern', () => {
113 writeFileSync(datei, '{ das ist kein JSON', 'utf8');
114 expect(liesSchriftgroesse(datei)).toBe(SCHRIFT_STANDARD);
115 writeFileSync(datei, '[]', 'utf8');
116 expect(liesSchriftgroesse(datei)).toBe(SCHRIFT_STANDARD);
117 writeFileSync(datei, JSON.stringify({ schriftgroesseProzent: 'sehr groß' }), 'utf8');
118 expect(liesSchriftgroesse(datei)).toBe(SCHRIFT_STANDARD);
119 });
120
121 it('scheitert nicht, wenn der Ort nicht beschreibbar ist', () => {
122 const unerreichbar = path.join(ordner, 'gibt-es-nicht', 'ansicht.json');
123 expect(() => {
124 schreibeSchriftgroesse(unerreichbar, 175);
125 }).not.toThrow();
126 expect(liesSchriftgroesse(unerreichbar)).toBe(SCHRIFT_STANDARD);
127 });
128 });
129
130 /**
131 * Verzoegerte Sicherung (Befund B5).
132 *
133 * `zoom-changed` feuert bei jeder Raste von Strg+Mausrad, und jede Raste lief
134 * in ein `writeFileSync` im HAUPTPROZESS - also in den Prozess, der die Fenster
135 * bedient. Auf einem servergespeicherten Benutzerprofil blockiert das die
136 * Oberflaeche pro Raste. Geschrieben wurde ausserdem auch dann, wenn sich gar
137 * nichts geaendert hatte: An beiden Enden der Stufenliste gibt `nachbarstufe`
138 * denselben Wert zurueck.
139 */
140 describe('Schriftgroesse: Sicherung wartet die Ruhezeit ab', () => {
141 let ordner = '';
142 let datei = '';
143
144 beforeEach(() => {
145 vi.useFakeTimers();
146 ordner = mkdtempSync(path.join(tmpdir(), 'lsa-sicherung-'));
147 datei = path.join(ordner, 'ansicht.json');
148 });
149
150 afterEach(() => {
151 vi.useRealTimers();
152 rmSync(ordner, { recursive: true, force: true });
153 });
154
155 /** Wie oft die Datei seit dem letzten Aufruf neu geschrieben wurde. */
156 function schreibungen(): number {
157 try {
158 readFileSync(datei, 'utf8');
159 return 1;
160 } catch {
161 return 0;
162 }
163 }
164
165 it('macht aus einem Dutzend Rasten eine einzige Schreibung', () => {
166 const sicherung = schriftsicherung(datei, 100, 750);
167 for (const stufe of [125, 150, 175, 200, 250]) sicherung.plane(stufe);
168
169 expect(schreibungen(), 'schon vor der Ruhezeit geschrieben').toBe(0);
170 vi.advanceTimersByTime(800);
171 expect(liesSchriftgroesse(datei)).toBe(250);
172 });
173
174 it('schreibt gar nicht, wenn sich die Stufe nicht aendert', () => {
175 // An beiden Enden der Liste gibt `nachbarstufe` denselben Wert zurueck -
176 // wer am Anschlag weiterdreht, loeste bis hierher Schreibung um Schreibung
177 // aus.
178 const sicherung = schriftsicherung(datei, 250, 750);
179 for (let i = 0; i < 12; i += 1) sicherung.plane(nachbarstufe(250, 1));
180
181 vi.advanceTimersByTime(2000);
182 expect(schreibungen(), 'ohne Aenderung geschrieben').toBe(0);
183 });
184
185 it('verwirft eine Aenderung, die vor Ablauf wieder zurueckgenommen wird', () => {
186 const sicherung = schriftsicherung(datei, 100, 750);
187 sicherung.plane(150);
188 vi.advanceTimersByTime(300);
189 sicherung.plane(100);
190 vi.advanceTimersByTime(2000);
191 expect(schreibungen(), 'hin und zurueck ist keine Aenderung').toBe(0);
192 });
193
194 it('schreibt beim Programmende sofort, was noch aussteht', () => {
195 // Eine Einstellung, die eine halbe Sekunde vor dem Beenden getroffen wurde,
196 // darf nicht verlorengehen.
197 const sicherung = schriftsicherung(datei, 100, 750);
198 sicherung.plane(175);
199 sicherung.jetzt();
200 expect(liesSchriftgroesse(datei)).toBe(175);
201
202 // Und ein zweites `jetzt()` schreibt nicht noch einmal.
203 rmSync(datei);
204 sicherung.jetzt();
205 expect(schreibungen()).toBe(0);
206 });
207
208 it('haelt die Buchfuehrung ueber mehrere Vorgaenge hinweg', () => {
209 const sicherung = schriftsicherung(datei, 100, 750);
210 sicherung.plane(150);
211 vi.advanceTimersByTime(800);
212 expect(liesSchriftgroesse(datei)).toBe(150);
213
214 // Derselbe Wert noch einmal: keine zweite Schreibung.
215 rmSync(datei);
216 sicherung.plane(150);
217 vi.advanceTimersByTime(800);
218 expect(schreibungen()).toBe(0);
219
220 sicherung.plane(200);
221 vi.advanceTimersByTime(800);
222 expect(liesSchriftgroesse(datei)).toBe(200);
223 });
224 });