lsa-planer

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

/ tests electron ansichtsmenue.test.ts

7,2 KB Rohdatei
tests/electron/ansichtsmenue.test.ts — 175 Zeilen
1 import { describe, expect, it, vi } from 'vitest';
2 import type { MenuItemConstructorOptions } from 'electron';
3
4 /*
5 * `electron/menu.ts` bindet `Menu`, `app` und `shell` ein. Ausserhalb von
6 * Electron gibt es die nicht; die Vorlage selbst braucht sie auch nicht - sie
7 * ist reiner Inhalt. Der Ersatz erlaubt, sie zu pruefen, ohne ein Fenster zu
8 * oeffnen.
9 */
10 vi.mock('electron', () => ({
11 Menu: { buildFromTemplate: (vorlage: unknown) => vorlage },
12 app: { getVersion: () => '5.6.0' },
13 shell: { openExternal: () => undefined },
14 }));
15
16 const { menuevorlage, schriftgroesseMenueId } = await import('../../electron/menu');
17 const { SCHRIFT_STANDARD, SCHRIFT_STUFEN } = await import('../../electron/schriftgroesse');
18
19 interface Aufzeichnung {
20 gesetzt: number[];
21 schritte: number[];
22 }
23
24 function vorlageFuer(aktuell: number): {
25 menue: MenuItemConstructorOptions[];
26 auf: Aufzeichnung;
27 } {
28 const auf: Aufzeichnung = { gesetzt: [], schritte: [] };
29 const menue = menuevorlage(
30 () => undefined,
31 false,
32 {
33 aktuell: () => aktuell,
34 setze: (prozent) => auf.gesetzt.push(prozent),
35 schritt: (richtung) => auf.schritte.push(richtung),
36 },
37 '5.6.0',
38 () => undefined,
39 () => undefined,
40 );
41 return { menue, auf };
42 }
43
44 function untermenue(
45 eintraege: MenuItemConstructorOptions[] | undefined,
46 beschriftung: string,
47 ): MenuItemConstructorOptions[] {
48 const eintrag = (eintraege ?? []).find((e) => e.label === beschriftung);
49 expect(eintrag, `Menüeintrag "${beschriftung}" fehlt`).toBeDefined();
50 const inhalt = eintrag?.submenu;
51 expect(Array.isArray(inhalt), `"${beschriftung}" hat kein Untermenü`).toBe(true);
52 return inhalt as MenuItemConstructorOptions[];
53 }
54
55 function eintrag(
56 eintraege: MenuItemConstructorOptions[],
57 beschriftung: string,
58 ): MenuItemConstructorOptions {
59 const gefunden = eintraege.find((e) => e.label === beschriftung);
60 expect(gefunden, `Menüeintrag "${beschriftung}" fehlt`).toBeDefined();
61 return gefunden as MenuItemConstructorOptions;
62 }
63
64 /**
65 * Befund L11 (EN 301 549 Kap. 11.7).
66 *
67 * Im Altbestand bestand "Ansicht" aus den drei Chromium-Rollen `resetZoom`,
68 * `zoomIn`, `zoomOut`. Sie kannten keine benannten Stufen, zeigten die
69 * eingestellte Groesse nicht an und sicherten nichts.
70 */
71 describe('Ansichtsmenü: Schriftgröße', () => {
72 it('bietet eine Auswahlliste "Schriftgröße" mit allen Stufen', () => {
73 const { menue } = vorlageFuer(SCHRIFT_STANDARD);
74 const stufen = untermenue(untermenue(menue, '&Ansicht'), 'Schriftgröße');
75 expect(stufen).toHaveLength(SCHRIFT_STUFEN.length);
76 for (const stufe of stufen) {
77 expect(stufe.type).toBe('radio');
78 expect(stufe.label).toMatch(/^\d+ %/);
79 }
80 });
81
82 it('markiert genau die eingestellte Stufe', () => {
83 const { menue } = vorlageFuer(175);
84 const stufen = untermenue(untermenue(menue, '&Ansicht'), 'Schriftgröße');
85 const markiert = stufen.filter((s) => s.checked === true);
86 expect(markiert).toHaveLength(1);
87 expect(markiert[0]?.id).toBe(schriftgroesseMenueId(175));
88 expect(markiert[0]?.label).toBe('175 %');
89 });
90
91 it('gibt jeder Stufe die Kennung, unter der der Hauptprozess sie nachführt', () => {
92 const { menue } = vorlageFuer(SCHRIFT_STANDARD);
93 const stufen = untermenue(untermenue(menue, '&Ansicht'), 'Schriftgröße');
94 expect(stufen.map((s) => s.id)).toEqual(SCHRIFT_STUFEN.map((p) => schriftgroesseMenueId(p)));
95 });
96
97 it('meldet beim Anklicken genau diese Stufe', () => {
98 const { menue, auf } = vorlageFuer(SCHRIFT_STANDARD);
99 const stufen = untermenue(untermenue(menue, '&Ansicht'), 'Schriftgröße');
100 const zweihundert = stufen.find((s) => s.id === schriftgroesseMenueId(200));
101 zweihundert?.click?.(undefined as never, undefined, undefined as never);
102 expect(auf.gesetzt).toEqual([200]);
103 });
104
105 it('lässt "Vergrößern" und "Verkleinern" durch dieselbe Stufenliste laufen', () => {
106 const { menue, auf } = vorlageFuer(150);
107 const ansicht = untermenue(menue, '&Ansicht');
108 eintrag(ansicht, 'Vergrößern').click?.(undefined as never, undefined, undefined as never);
109 eintrag(ansicht, 'Verkleinern').click?.(undefined as never, undefined, undefined as never);
110 expect(auf.schritte).toEqual([1, -1]);
111 expect(auf.gesetzt).toEqual([]);
112 });
113
114 it('behält die gewohnten Tastenkürzel', () => {
115 const ansicht = untermenue(vorlageFuer(SCHRIFT_STANDARD).menue, '&Ansicht');
116 expect(eintrag(ansicht, 'Vergrößern').accelerator).toBe('CmdOrCtrl+Plus');
117 expect(eintrag(ansicht, 'Verkleinern').accelerator).toBe('CmdOrCtrl+-');
118 expect(eintrag(ansicht, 'Standardgröße').accelerator).toBe('CmdOrCtrl+0');
119 });
120
121 it('behält auch Strg+= zum Vergrößern, verborgen im Menü', () => {
122 /*
123 * Die abgeloeste Chromium-Rolle `zoomIn` brachte BEIDE Kuerzel mit
124 * (Befund B6). Auf einer Belegung, bei der "+" die Umschalttaste verlangt -
125 * US, UK, viele Notebooktastaturen -, ist Strg+= der Weg, der mit einer
126 * Hand geht; mit dem eigenen Eintrag entfiel er. Electron nimmt je Eintrag
127 * genau einen Accelerator, also braucht es einen zweiten - und der gehoert
128 * nicht ins aufgeklappte Menue, wo er ein zweites "Vergroessern" ohne
129 * eigenen Zweck waere.
130 */
131 const { menue, auf } = vorlageFuer(150);
132 const ansicht = untermenue(menue, '&Ansicht');
133 const beide = ansicht.filter((e) => e.label === 'Vergrößern');
134 expect(beide.map((e) => e.accelerator)).toEqual(['CmdOrCtrl+Plus', 'CmdOrCtrl+=']);
135 expect(beide[0]!.visible).toBeUndefined();
136 expect(beide[1]!.visible).toBe(false);
137
138 // Und er fuehrt durch dieselbe Stufenliste, nicht an ihr vorbei.
139 beide[1]!.click?.(undefined as never, undefined, undefined as never);
140 expect(auf.schritte).toEqual([1]);
141 });
142
143 it('setzt "Standardgröße" auf die Vorgabestufe zurück', () => {
144 const { menue, auf } = vorlageFuer(200);
145 const ansicht = untermenue(menue, '&Ansicht');
146 eintrag(ansicht, 'Standardgröße').click?.(undefined as never, undefined, undefined as never);
147 expect(auf.gesetzt).toEqual([SCHRIFT_STANDARD]);
148 });
149
150 it('überlässt die Vergrößerung keiner Chromium-Rolle mehr', () => {
151 // Eine Rolle aendert den Vergroesserungsgrad an der eigenen Buchfuehrung
152 // vorbei: Die Auswahlmarke zeigte danach auf eine Stufe, die nicht gilt,
153 // und gesichert wuerde die Aenderung auch nicht.
154 const ansicht = untermenue(vorlageFuer(SCHRIFT_STANDARD).menue, '&Ansicht');
155 const rollen = ansicht.map((e) => e.role);
156 expect(rollen).not.toContain('zoomIn');
157 expect(rollen).not.toContain('zoomOut');
158 expect(rollen).not.toContain('resetZoom');
159 });
160 });
161
162 /**
163 * Hinweis ohne Bezug auf ein Erfolgskriterium: "Rückgängig" bezeichnete im Menü
164 * etwas anderes als in der Kopfzeile.
165 */
166 describe('Bearbeitenmenü: Rückgängig', () => {
167 it('benennt die Chromium-Rollen als Texteingabe', () => {
168 const bearbeiten = untermenue(vorlageFuer(SCHRIFT_STANDARD).menue, '&Bearbeiten');
169 const beschriftungen = bearbeiten.map((e) => e.label);
170 expect(beschriftungen).not.toContain('Rückgängig');
171 expect(beschriftungen).not.toContain('Wiederherstellen');
172 expect(bearbeiten.find((e) => e.role === 'undo')?.label).toBe('Texteingabe rückgängig');
173 expect(bearbeiten.find((e) => e.role === 'redo')?.label).toBe('Texteingabe wiederherstellen');
174 });
175 });