waffensachkunde

Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.

/ app tests energie.test.ts

4,7 KB Rohdatei
app/tests/energie.test.ts — 153 Zeilen
1 // @vitest-environment node
2 // Reiner Main-Prozess-Test; jsdom wird hier nicht gebraucht.
3
4 import { EventEmitter } from 'node:events';
5
6 import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
7
8 import { KANAL_ENERGIESPAREN_ENDE } from '../src/shared/ipc';
9
10 /**
11 * Die Uhr der Prüfungssimulation im Energiesparmodus.
12 *
13 * Bis 0.22.0 hing die gesamte Zeitrechnung an der Wanduhr: `endeMs` und
14 * `verbrauchtMs` leiten sich aus `startMs` ab, und niemand fragte, ob der
15 * Rechner zwischendurch schlief. Wer mitten in einem 120-Minuten-Bogen den
16 * Laptop zuklappte, verbrauchte die Mittagspause als Bearbeitungszeit – der
17 * Bogen war beim Aufwachen abgelaufen, und der Herzschlag schrieb die
18 * aufgeblähte Dauer sogar in die Datenbank.
19 *
20 * Geprüft wird hier die Meldeseite: dass die Schlafdauer überhaupt und
21 * richtig gemessen an die Fenster geht. Dass die Oberfläche daraufhin ihren
22 * Zeitursprung verschiebt, prüft `renderer-pruefung.test.tsx`.
23 */
24
25 /** Nachbildung von `powerMonitor` – ein Ereignissender genügt. */
26 const powerMonitor = new EventEmitter();
27
28 /** Was die Fenster zu sehen bekommen. */
29 let gesendet: { kanal: string; nutzlast: unknown }[] = [];
30
31 /** Ob das Fenster sich für zerstört hält – der Randfall beim Beenden. */
32 let zerstoert = false;
33
34 const fenster = {
35 isDestroyed: (): boolean => zerstoert,
36 webContents: {
37 send: (kanal: string, nutzlast: unknown): void => {
38 gesendet.push({ kanal, nutzlast });
39 },
40 },
41 };
42
43 vi.mock('electron', () => ({
44 powerMonitor,
45 BrowserWindow: {
46 getAllWindows: (): unknown[] => [fenster],
47 },
48 }));
49
50 const { energieUeberwachen, energieVergessen } = await import('../src/main/energie');
51
52 /** Führt einen Schlaf von `dauerMs` vor, ohne wirklich zu warten. */
53 function schlafen(dauerMs: number): void {
54 const beginn = Date.UTC(2026, 7, 29, 10, 0, 0);
55 const uhr = vi.spyOn(Date, 'now');
56 uhr.mockReturnValue(beginn);
57 powerMonitor.emit('suspend');
58 uhr.mockReturnValue(beginn + dauerMs);
59 powerMonitor.emit('resume');
60 uhr.mockRestore();
61 }
62
63 beforeEach(() => {
64 gesendet = [];
65 zerstoert = false;
66 energieUeberwachen();
67 });
68
69 afterEach(() => {
70 energieVergessen();
71 vi.restoreAllMocks();
72 });
73
74 describe('Energiesparmodus melden', () => {
75 it('meldet die Schlafdauer nach dem Aufwachen', () => {
76 schlafen(30 * 60 * 1000);
77
78 expect(gesendet).toEqual([{ kanal: KANAL_ENERGIESPAREN_ENDE, nutzlast: 30 * 60 * 1000 }]);
79 });
80
81 it('schweigt bei einem Schlaf von Bruchteilen einer Sekunde', () => {
82 /* Zwischen `suspend` und `resume` liegt selbst bei einem sofort
83 abgebrochenen Standby ein Rest an Zeit. Den gutzuschreiben verschöbe
84 den Zeitursprung ohne Anlass und ließe die Ansage grundlos erscheinen. */
85 schlafen(200);
86
87 expect(gesendet).toEqual([]);
88 });
89
90 it('meldet nichts bei einem Aufwachen ohne vorheriges Einschlafen', () => {
91 /* `resume` kann ohne passendes `suspend` eintreffen – etwa wenn die
92 Anwendung erst währenddessen gestartet wurde. Eine Gutschrift „seit
93 Anbeginn“ wäre der schlimmste Fall: Sie schöbe den Zeitursprung um
94 eine willkürliche Menge. */
95 powerMonitor.emit('resume');
96
97 expect(gesendet).toEqual([]);
98 });
99
100 it('schreibt nicht in ein zerstörtes Fenster', () => {
101 zerstoert = true;
102 schlafen(30 * 60 * 1000);
103
104 expect(gesendet).toEqual([]);
105 });
106
107 it('meldet auch bei einem Uhrsprung zurück nichts Negatives', () => {
108 /* Stellt jemand die Systemuhr während des Schlafs zurück, ergäbe die
109 Differenz eine negative Dauer. Gutgeschrieben verkürzte sie die
110 Restzeit – das Gegenteil der Absicht. */
111 const beginn = Date.UTC(2026, 7, 29, 10, 0, 0);
112 const uhr = vi.spyOn(Date, 'now');
113 uhr.mockReturnValue(beginn);
114 powerMonitor.emit('suspend');
115 uhr.mockReturnValue(beginn - 60 * 60 * 1000);
116 powerMonitor.emit('resume');
117 uhr.mockRestore();
118
119 expect(gesendet).toEqual([]);
120 });
121
122 it('meldet zwei Pausen einzeln', () => {
123 schlafen(10 * 60 * 1000);
124 schlafen(5 * 60 * 1000);
125
126 expect(gesendet.map((eintrag) => eintrag.nutzlast)).toEqual([10 * 60 * 1000, 5 * 60 * 1000]);
127 });
128
129 it('hängt die Zuhörer nur einmal, auch bei mehrfachem Anmelden', () => {
130 /* Ein zweites Anmelden meldete jede Pause doppelt – und die Oberfläche
131 schriebe den Zeitursprung zweimal vor. */
132 energieUeberwachen();
133 energieUeberwachen();
134
135 schlafen(30 * 60 * 1000);
136
137 expect(gesendet).toHaveLength(1);
138 });
139
140 it('schweigt nach dem Abmelden', () => {
141 energieVergessen();
142 schlafen(30 * 60 * 1000);
143
144 expect(gesendet).toEqual([]);
145 });
146
147 it('lässt keine Zuhörer zurück', () => {
148 energieVergessen();
149
150 expect(powerMonitor.listenerCount('suspend')).toBe(0);
151 expect(powerMonitor.listenerCount('resume')).toBe(0);
152 });
153 });