waffensachkunde

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

/ app tests fehlerauffang.test.tsx

4,9 KB Rohdatei
app/tests/fehlerauffang.test.tsx — 144 Zeilen
1 // @vitest-environment jsdom
2 /**
3 * Der Auffangbereich der Oberfläche.
4 *
5 * Er ist die letzte Sperre vor einem weißen Fenster – und war bis 0.24.1 der
6 * einzige Baustein dieser Größe, den kein Test je aufgerufen hat. Genau dort
7 * fiel dann auch etwas heraus: `autoFocus` an einer `h1` tut nichts. React
8 * wendet das Attribut nur auf `button`, `input`, `select` und `textarea` an;
9 * an jedem anderen Element verschwindet es ersatzlos, und der Fokus blieb
10 * nach einem Absturz auf `<body>` stehen.
11 *
12 * Für jemanden am Bildschirmleser ist das die Sackgasse, die dieser Bereich
13 * verhindern soll: Der Fokus steht auf einem Element, das es nicht mehr gibt,
14 * und die Tastatur hat keinen Anker.
15 */
16
17 import { render, screen } from '@testing-library/react';
18 import userEvent from '@testing-library/user-event';
19 import { beforeEach, describe, expect, it, vi } from 'vitest';
20
21 import { Fehlerauffang } from '../src/renderer/src/components/Fehlerauffang';
22
23 /** Eine Komponente, die beim Rendern wirft – wie ein echter Absturz. */
24 function Kaputt({ was }: { readonly was: unknown }): never {
25 throw was;
26 }
27
28 beforeEach(() => {
29 /* React meldet den gefangenen Fehler selbst auf der Konsole, und der
30 Auffangbereich schreibt ihn absichtlich dorthin. Beides ist hier
31 erwartet. `restoreMocks` in `vitest.config.ts` räumt danach auf. */
32 vi.spyOn(console, 'error').mockImplementation(() => undefined);
33 });
34
35 describe('Fehlerauffang', () => {
36 it('lässt den Inhalt stehen, solange nichts schiefgeht', () => {
37 render(
38 <Fehlerauffang>
39 <p>Alles in Ordnung</p>
40 </Fehlerauffang>,
41 );
42
43 expect(screen.getByText('Alles in Ordnung')).toBeInTheDocument();
44 });
45
46 it('zeigt statt eines leeren Fensters eine Seite mit Überschrift und Ausweg', () => {
47 render(
48 <Fehlerauffang>
49 <Kaputt was={new Error('Etwas ist zerbrochen')} />
50 </Fehlerauffang>,
51 );
52
53 expect(screen.getByRole('heading', { name: 'Die Anzeige ist abgestürzt' })).toBeInTheDocument();
54 expect(screen.getByRole('button', { name: 'Anwendung neu laden' })).toBeInTheDocument();
55 });
56
57 it('meldet sich als Warnung, damit ein Bildschirmleser es ungefragt vorliest', () => {
58 render(
59 <Fehlerauffang>
60 <Kaputt was={new Error('Etwas ist zerbrochen')} />
61 </Fehlerauffang>,
62 );
63
64 expect(screen.getByRole('alert')).toBeInTheDocument();
65 });
66
67 /* Der eigentliche Befund. */
68 it('holt den Fokus auf die Überschrift, statt ihn auf <body> fallen zu lassen', () => {
69 render(
70 <Fehlerauffang>
71 <Kaputt was={new Error('Etwas ist zerbrochen')} />
72 </Fehlerauffang>,
73 );
74
75 const titel = screen.getByRole('heading', { name: 'Die Anzeige ist abgestürzt' });
76 expect(document.activeElement).toBe(titel);
77 expect(document.activeElement).not.toBe(document.body);
78 });
79
80 it('nennt den Grund im Klartext', () => {
81 render(
82 <Fehlerauffang>
83 <Kaputt was={new Error('Etwas ist zerbrochen')} />
84 </Fehlerauffang>,
85 );
86
87 expect(screen.getByText('Etwas ist zerbrochen')).toBeInTheDocument();
88 });
89
90 it('kommt auch mit einem geworfenen Wert zurecht, der kein Fehler ist', () => {
91 render(
92 <Fehlerauffang>
93 <Kaputt was="bloß eine Zeichenkette" />
94 </Fehlerauffang>,
95 );
96
97 expect(screen.getByText('bloß eine Zeichenkette')).toBeInTheDocument();
98 });
99
100 it('nennt „Unbekannter Fehler“, wenn der geworfene Wert nichts hergibt', () => {
101 render(
102 <Fehlerauffang>
103 <Kaputt was="" />
104 </Fehlerauffang>,
105 );
106
107 expect(screen.getByText('Unbekannter Fehler')).toBeInTheDocument();
108 });
109
110 it('sagt ausdrücklich, dass der Lernstand nicht betroffen ist', () => {
111 /* Wer eine Fehlerseite sieht, fragt sich zuerst, ob die Arbeit weg ist.
112 Die Antwort gehört auf die Seite und nicht in eine Anleitung. */
113 render(
114 <Fehlerauffang>
115 <Kaputt was={new Error('Etwas ist zerbrochen')} />
116 </Fehlerauffang>,
117 );
118
119 expect(screen.getByText(/Ihr Lernstand ist nicht betroffen/u)).toBeInTheDocument();
120 });
121
122 it('bietet den Ausweg als Schaltfläche und nennt den zweiten Weg dazu', async () => {
123 /* Der Klick selbst ruft `window.location.reload` – in jsdom ist die
124 Eigenschaft weder ersetzbar noch überwachbar, und ein echter Neuladen
125 risse die Testumgebung ab. Geprüft wird deshalb, dass der Ausweg da und
126 bedienbar ist, und dass die Seite den zweiten Weg nennt, falls der
127 erste nicht hilft. */
128 const nutzer = userEvent.setup();
129 render(
130 <Fehlerauffang>
131 <Kaputt was={new Error('Etwas ist zerbrochen')} />
132 </Fehlerauffang>,
133 );
134
135 const knopf = screen.getByRole('button', { name: 'Anwendung neu laden' });
136 expect(knopf).toBeEnabled();
137 await nutzer.tab();
138 expect(document.activeElement).toBe(knopf);
139
140 expect(
141 screen.getByText(/schließen Sie das Fenster und starten Sie das Programm neu/u),
142 ).toBeInTheDocument();
143 });
144 });