waffensachkunde

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

/ app tests gesperrt-sichtbar.test.ts

3,9 KB Rohdatei
app/tests/gesperrt-sichtbar.test.ts — 91 Zeilen
1 // @vitest-environment node
2 /**
3 * „Gesperrt sieht gesperrt aus“ – auch ohne Farbe.
4 *
5 * **Der Anlass.** Die Projektregel `aria-disabled` statt `disabled` ist
6 * richtig: Ein natives `disabled` nähme dem Element den Fokus, und der
7 * erklärende Satz daneben würde nie vorgelesen. Der Zustand wurde aber
8 * **ausschließlich über Farbtoken** gezeichnet – heller Text auf versetztem
9 * Grund. Im eigenen Hochkontrast-Thema ist `--farbe-text-leise` mit
10 * `--farbe-text` identisch (beides `#ffffff`) und `--farbe-grund-versetzt`
11 * mit dem Grund (`#000000`): Ein gesperrter Knopf sah dort **pixelgleich**
12 * aus wie ein aktiver. Unter erzwungenen Farben fallen alle beteiligten Token
13 * ohnehin auf `CanvasText` und `Canvas` zusammen.
14 *
15 * Wer Hochkontrast braucht, drückte also auf scheinbar bereite Knöpfe, ohne
16 * dass etwas geschah – genau die Verwirrung, die der Kommentar in `lernen.css`
17 * zu verhindern verspricht.
18 *
19 * Geprüft wird hier am Stilblatt, nicht im Browser: Ob eine gestrichelte
20 * Linie im echten Fenster auffällt, sagt keine Messung – das ist Sichtprüfung
21 * H8. Was sich hier feststellen lässt, ist, dass der Zustand überhaupt an
22 * etwas anderem hängt als an einer Farbe, die in zwei von vier Darstellungen
23 * mit ihrem Gegenstück zusammenfällt.
24 */
25
26 import { readFileSync } from 'node:fs';
27 import { join } from 'node:path';
28 import { fileURLToPath } from 'node:url';
29
30 import { describe, expect, it } from 'vitest';
31
32 const wurzel = fileURLToPath(new URL('..', import.meta.url));
33
34 function stil(datei: string): string {
35 return readFileSync(join(wurzel, 'src', 'renderer', 'src', 'styles', datei), 'utf8');
36 }
37
38 const LERNEN_CSS = stil('lernen.css');
39 const TOKENS_CSS = stil('tokens.css');
40
41 /** Der Regelblock ab einem Selektor bis zur schließenden Klammer. */
42 function block(css: string, selektor: string): string {
43 const anfang = css.indexOf(selektor);
44 expect(anfang, `Selektor nicht gefunden: ${selektor}`).toBeGreaterThan(-1);
45 const auf = css.indexOf('{', anfang);
46 const zu = css.indexOf('}', auf);
47 return css.slice(auf, zu);
48 }
49
50 describe('Der Gesperrt-Zustand hängt nicht allein an der Farbe', () => {
51 it('gibt gesperrten Knöpfen ein farbunabhängiges Merkmal', () => {
52 const regel = block(LERNEN_CSS, ".knopf[aria-disabled='true']");
53
54 expect(regel).toMatch(/border-style:\s*dashed/u);
55 });
56
57 it('belegt, warum die Farbe allein nicht trägt', () => {
58 /* Die Gegenprobe zur Behebung: Wären die beiden Token im
59 Hochkontrast-Thema verschieden, bräuchte es das zusätzliche Merkmal
60 nicht – und dieser Test dürfte gelöscht werden. Solange sie
61 zusammenfallen, ist er der Beleg für den Befund. */
62 const hochkontrast = block(TOKENS_CSS, ":root[data-thema='hochkontrast']");
63 const werte = new Map(
64 [...hochkontrast.matchAll(/--([a-z-]+):\s*(#[0-9a-f]{6})\b/gu)].map((t) => [t[1], t[2]]),
65 );
66
67 expect(werte.get('farbe-text-leise')).toBe(werte.get('farbe-text'));
68 expect(werte.get('farbe-grund-versetzt')).toBe(werte.get('farbe-grund'));
69 });
70 });
71
72 describe('Erzwungene Farben (Windows-Kontrastdesign)', () => {
73 it('kennzeichnet gesperrte Knöpfe mit der Systemfarbe GrayText', () => {
74 /* Die einzige Farbe, die unter erzwungenen Farben einen Zustand
75 ausdrücken darf – und die einzige, die Windows-Nutzer dafür kennen.
76 Sie muss ausdrücklich gesetzt werden: Die Token fallen dort alle auf
77 CanvasText zusammen. */
78 const bereich = LERNEN_CSS.slice(LERNEN_CSS.indexOf('@media (forced-colors: active)'));
79 const regel = block(bereich, ".knopf[aria-disabled='true']");
80
81 expect(regel).toMatch(/color:\s*GrayText/u);
82 expect(regel).toMatch(/border-color:\s*GrayText/u);
83 });
84
85 it('kennzeichnet auch gesperrte Schalter', () => {
86 const bereich = LERNEN_CSS.slice(LERNEN_CSS.indexOf('@media (forced-colors: active)'));
87 const regel = block(bereich, ".schalter__feld[aria-disabled='true'] + .schalter__beschriftung");
88
89 expect(regel).toMatch(/color:\s*GrayText/u);
90 });
91 });