waffensachkunde

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

/ app tests verzoegerte-ansage.test.ts

4,1 KB Rohdatei
app/tests/verzoegerte-ansage.test.ts — 119 Zeilen
1 /**
2 * Die Ruhepause vor einer Live-Region.
3 *
4 * **Der Anlass.** Die Fragensuche schickte ihre Trefferansage seit jeher erst
5 * nach 400 Millisekunden Ruhe an die Live-Region – mit der Begründung, die der
6 * Prüfplan zu WCAG 4.1.3 gibt: Eine höfliche Region, die bei jedem
7 * Tastenanschlag eine neue Zahl spricht, erzeugt beim Tippen eine Kette
8 * einander unterbrechender Ansagen zwischen den Zeichen-Echos.
9 *
10 * Die Glossaransicht rendert dieselbe Art Auskunft („N von 87 Einträgen“) –
11 * und tat das bis 0.22.0 **unmittelbar**. Dieselbe Sache, zwei
12 * Verhaltensweisen; die eine davon war die, vor der die eigene Begründung
13 * warnt.
14 *
15 * Geprüft wird hier die Mechanik und daneben, dass beide Ansichten wirklich
16 * aus derselben Quelle schöpfen – sonst stünde die gemeinsame Regel zwar da,
17 * bliebe aber wirkungslos.
18 */
19
20 import { readFileSync } from 'node:fs';
21 import { join } from 'node:path';
22
23 import { act, renderHook } from '@testing-library/react';
24 import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
25
26 import { RUHE_MS, useVerzoegerteAnsage } from '../src/renderer/src/hooks/useVerzoegerteAnsage';
27
28 /* `process.cwd()` und nicht `import.meta.url`: Diese Datei braucht jsdom für
29 den Hook, und dort ist `import.meta.url` eine http-Adresse – derselbe
30 Stolperstein, den `tests/sicherheit.test.ts` in seinem Kopf benennt.
31 Vitest läuft aus `app/`. */
32 function quelle(...teile: string[]): string {
33 return readFileSync(join(process.cwd(), 'src', 'renderer', 'src', ...teile), 'utf8');
34 }
35
36 beforeEach(() => {
37 vi.useFakeTimers();
38 });
39
40 afterEach(() => {
41 vi.useRealTimers();
42 });
43
44 describe('Ansage mit Ruhepause', () => {
45 it('zeigt den ersten Text sofort', () => {
46 /* Beim Aufbau der Ansicht wartet niemand: Die Region muss von der ersten
47 Darstellung an im Baum stehen und etwas sagen. */
48 const { result } = renderHook(() => useVerzoegerteAnsage('87 Einträge'));
49
50 expect(result.current).toBe('87 Einträge');
51 });
52
53 it('schweigt, solange getippt wird', () => {
54 const { result, rerender } = renderHook(({ text }) => useVerzoegerteAnsage(text), {
55 initialProps: { text: '87 Einträge' },
56 });
57
58 for (const zwischenstand of ['12 von 87', '5 von 87', '2 von 87']) {
59 rerender({ text: zwischenstand });
60 act(() => {
61 vi.advanceTimersByTime(RUHE_MS - 100);
62 });
63 expect(result.current).toBe('87 Einträge');
64 }
65 });
66
67 it('zieht nach der Ruhepause nach', () => {
68 const { result, rerender } = renderHook(({ text }) => useVerzoegerteAnsage(text), {
69 initialProps: { text: '87 Einträge' },
70 });
71
72 rerender({ text: '2 von 87 Einträgen' });
73 act(() => {
74 vi.advanceTimersByTime(RUHE_MS + 10);
75 });
76
77 expect(result.current).toBe('2 von 87 Einträgen');
78 });
79
80 it('überspringt die Zwischenstände, statt sie nachzuholen', () => {
81 /* Die Zwischenstände dürfen nicht nachträglich hintereinander gesprochen
82 werden – das wäre dieselbe Kette, nur später. */
83 const { result, rerender } = renderHook(({ text }) => useVerzoegerteAnsage(text), {
84 initialProps: { text: 'a' },
85 });
86
87 rerender({ text: 'b' });
88 rerender({ text: 'c' });
89 rerender({ text: 'd' });
90 act(() => {
91 vi.advanceTimersByTime(RUHE_MS + 10);
92 });
93
94 expect(result.current).toBe('d');
95 });
96 });
97
98 describe('Beide Filterfelder schöpfen aus derselben Quelle', () => {
99 it('die Fragensuche', () => {
100 expect(quelle('hooks', 'useFragensuche.ts')).toContain('useVerzoegerteAnsage');
101 });
102
103 it('die Glossaransicht', () => {
104 /* Der eigentliche Befund: Hier stand die Zahl unmittelbar am
105 `role="status"`-Element. */
106 expect(quelle('components', 'lernen', 'Glossaransicht.tsx')).toContain('useVerzoegerteAnsage');
107 });
108
109 it('und keine von beiden führt eine eigene Ruhezahl', () => {
110 /* Zwei Zahlen an zwei Stellen laufen auseinander, und eine davon wäre
111 dann die falsche. */
112 for (const datei of [
113 quelle('hooks', 'useFragensuche.ts'),
114 quelle('components', 'lernen', 'Glossaransicht.tsx'),
115 ]) {
116 expect(datei).not.toMatch(/RUHE_MS\s*=\s*\d/u);
117 }
118 });
119 });