waffensachkunde
Waffensachkunde – Lernsoftware für die Sachkundeprüfung nach § 7 WaffG. Barrierefrei, offline, EUPL-1.2.
/ app tests hilfe-zahlen.test.ts
| 1 | // @vitest-environment node |
| 2 | /** |
| 3 | * Die Zahlen im Handbuch gegen den **echten** Katalog. |
| 4 | * |
| 5 | * Seit 0.26.4 sagt das Kapitel „Die Lernsitzung“, wie eine frei zu |
| 6 | * formulierende Antwort gebaut ist – und begründet es mit gezählten |
| 7 | * Anfangswörtern: 26 Fragen beginnen mit „Was“, 25 mit „Welche“, 7 mit |
| 8 | * „Wie“, je 5 mit „Wann“ und „Nennen Sie“. Solche Sätze sind Zusagen: Ein |
| 9 | * neuer Katalogstand verschiebt sie, ohne dass es jemandem auffiele, und |
| 10 | * dann steht im Handbuch eine Zahl, die niemand mehr nachzählen kann. |
| 11 | * |
| 12 | * Deshalb zählt dieser Test sie bei jedem Lauf nach. Er liest die Zahlen aus |
| 13 | * dem Handbuchtext selbst – nicht aus einer zweiten Liste daneben, die |
| 14 | * ihrerseits auseinanderlaufen könnte. |
| 15 | */ |
| 16 | |
| 17 | import { readFileSync } from 'node:fs'; |
| 18 | import { join } from 'node:path'; |
| 19 | import { fileURLToPath } from 'node:url'; |
| 20 | |
| 21 | import { describe, expect, it } from 'vitest'; |
| 22 | |
| 23 | import { HILFE_KAPITEL } from '../src/shared/hilfe'; |
| 24 | import type { Katalog } from '../src/shared/katalog'; |
| 25 | |
| 26 | const wurzel = fileURLToPath(new URL('..', import.meta.url)); |
| 27 | |
| 28 | const KATALOG = JSON.parse( |
| 29 | readFileSync(join(wurzel, '..', 'content', 'katalog', 'katalog.json'), 'utf8'), |
| 30 | ) as Katalog; |
| 31 | |
| 32 | /** Alle Fließtexte des Handbuchs, zu einer Zeichenkette verbunden. */ |
| 33 | function handbuchtext(): string { |
| 34 | return HILFE_KAPITEL.flatMap((kapitel) => |
| 35 | kapitel.bloecke.map((block) => { |
| 36 | if (block.art === 'absatz') return block.text; |
| 37 | if (block.art === 'liste' || block.art === 'schritte') return block.punkte.join(' '); |
| 38 | return ''; |
| 39 | }), |
| 40 | ).join(' '); |
| 41 | } |
| 42 | |
| 43 | /** Die offenen Fragen: die ohne Antwortmöglichkeiten. */ |
| 44 | function offeneFragen() { |
| 45 | return KATALOG.fragen.filter((frage) => (frage.optionen ?? []).length === 0); |
| 46 | } |
| 47 | |
| 48 | /** Das erste großgeschriebene Wort des Fragetextes. */ |
| 49 | function anfangswort(text: string): string { |
| 50 | return /[A-ZÄÖÜ][a-zäöüß]+/u.exec(text)?.[0] ?? ''; |
| 51 | } |
| 52 | |
| 53 | describe('Die Zahlen des Handbuchs stimmen mit dem Katalog überein', () => { |
| 54 | it('nennt so viele offene Fragen, wie der Katalog hat', () => { |
| 55 | const text = handbuchtext(); |
| 56 | const anzahl = offeneFragen().length; |
| 57 | |
| 58 | expect(text, `Der Katalog hat ${String(anzahl)} offene Fragen.`).toContain( |
| 59 | `${String(anzahl)} offenen Fragen des Katalogs`, |
| 60 | ); |
| 61 | expect(text).toContain(`Nur 5 der ${String(anzahl)} Fragen nennen selbst eine Anzahl.`); |
| 62 | }); |
| 63 | |
| 64 | it('zählt die Anfangswörter richtig ab', () => { |
| 65 | const text = handbuchtext(); |
| 66 | const gezaehlt = new Map<string, number>(); |
| 67 | for (const frage of offeneFragen()) { |
| 68 | const wort = anfangswort(frage.frage.text); |
| 69 | gezaehlt.set(wort, (gezaehlt.get(wort) ?? 0) + 1); |
| 70 | } |
| 71 | |
| 72 | /* Genau die fünf, die der Satz im Handbuch aufzählt. */ |
| 73 | expect(gezaehlt.get('Was')).toBe(26); |
| 74 | expect(gezaehlt.get('Welche')).toBe(25); |
| 75 | expect(gezaehlt.get('Wie')).toBe(7); |
| 76 | expect(gezaehlt.get('Wann')).toBe(5); |
| 77 | expect(gezaehlt.get('Nennen')).toBe(5); |
| 78 | |
| 79 | expect(text).toContain( |
| 80 | '26 beginnen mit „Was“, 25 mit „Welche“, 7 mit „Wie“, je 5 mit „Wann“ und „Nennen Sie“', |
| 81 | ); |
| 82 | }); |
| 83 | |
| 84 | it('nennt so viele Fragen mit ausgeschriebener Anzahl, wie es gibt', () => { |
| 85 | /* „zwei Fälle“, „drei Voraussetzungen“ – ausgeschrieben, nicht als Ziffer. */ |
| 86 | const mitAnzahl = offeneFragen().filter((frage) => |
| 87 | /\b(zwei|drei|vier|fünf|sechs)\b/iu.test(frage.frage.text), |
| 88 | ); |
| 89 | |
| 90 | expect(mitAnzahl).toHaveLength(5); |
| 91 | expect(handbuchtext()).toContain('Nur 5 der'); |
| 92 | }); |
| 93 | }); |