lsa-planer
LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.
/ tests electron ansichtsmenue.test.ts
| 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 | }); |