waffensachkunde

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

/ app tests kernpunkte.test.ts

4,7 KB Rohdatei
app/tests/kernpunkte.test.ts — 121 Zeilen
1 /**
2 * Die Prüfliste zur Selbstbewertung offener Fragen.
3 *
4 * **Der Anlass.** Bei 63 der 104 offenen Fragen ist in der amtlichen
5 * Musterantwort nichts unterstrichen, und die Anwendung gibt die
6 * Unterstreichungen seit 0.17.0 zu Recht nicht mehr als Pflichtinhalt aus.
7 * Damit stand der Lernende bei der vierstufigen Selbstbewertung – die
8 * unmittelbar die Wiedervorlage steuert – ohne jedes Kriterium da.
9 *
10 * **Die Leitplanke.** `docs/entscheidung-freitext.md` Abschnitt 8 verlangt,
11 * dass über einer solchen Liste **nie wieder** „muss Ihre Antwort enthalten“
12 * steht. Der letzte Test dieser Datei hält das am ganzen Bestand fest.
13 */
14
15 import { readFileSync } from 'node:fs';
16 import { join } from 'node:path';
17
18 import { describe, expect, it } from 'vitest';
19
20 import type { Erklaerungen } from '../src/shared/erklaerungen';
21 import type { Katalog } from '../src/shared/katalog';
22
23 const WURZEL = join(process.cwd(), '..');
24 const ERKLAERUNGEN = JSON.parse(
25 readFileSync(join(WURZEL, 'content', 'erklaerungen.json'), 'utf8'),
26 ) as Erklaerungen;
27 const KATALOG = JSON.parse(
28 readFileSync(join(WURZEL, 'content', 'katalog', 'katalog.json'), 'utf8'),
29 ) as Katalog;
30
31 const OFFEN = new Set(KATALOG.fragen.filter((frage) => frage.typ !== 'mc').map((f) => f.id));
32 const MIT_PUNKTEN = Object.entries(ERKLAERUNGEN.zuFrage).filter(
33 ([, eintrag]) => eintrag.kernpunkte !== undefined,
34 );
35
36 describe('Die ausgelieferten Kernpunkte', () => {
37 it('decken den größeren Teil der offenen Fragen ab', () => {
38 /* Nicht alle: Wo die Musterantwort aus einer einzigen Tatsache besteht
39 („Unbefristet“, „Nein“), wäre eine Prüfliste mit einem Punkt kein
40 Werkzeug, sondern Beiwerk. */
41 expect(OFFEN.size).toBe(104);
42 expect(MIT_PUNKTEN.length).toBeGreaterThan(70);
43 expect(MIT_PUNKTEN.length).toBeLessThanOrEqual(OFFEN.size);
44 });
45
46 it('stehen ausschließlich an offenen Fragen', () => {
47 /* Bei einer Auswahlfrage stehen die Antwortmöglichkeiten schon da – eine
48 Prüfliste wäre dort die Lösung, nicht eine Hilfe beim Bewerten. */
49 const falschePlatzierung = MIT_PUNKTEN.filter(([frageId]) => !OFFEN.has(frageId)).map(
50 ([frageId]) => frageId,
51 );
52
53 expect(falschePlatzierung).toEqual([]);
54 });
55
56 it('führen zwischen zwei und acht Punkte', () => {
57 const daneben = MIT_PUNKTEN.filter(([, eintrag]) => {
58 const anzahl = eintrag.kernpunkte?.length ?? 0;
59 return anzahl < 2 || anzahl > 8;
60 }).map(([frageId]) => frageId);
61
62 expect(daneben).toEqual([]);
63 });
64
65 it('wiederholen keinen Punkt innerhalb einer Frage', () => {
66 const doppelt = MIT_PUNKTEN.filter(([, eintrag]) => {
67 const punkte = eintrag.kernpunkte ?? [];
68 return new Set(punkte).size !== punkte.length;
69 }).map(([frageId]) => frageId);
70
71 expect(doppelt).toEqual([]);
72 });
73
74 it('bleiben Stichworte statt Satzgefüge', () => {
75 const zuLang = MIT_PUNKTEN.flatMap(([frageId, eintrag]) =>
76 (eintrag.kernpunkte ?? [])
77 .filter((punkt) => punkt.length > 140 || punkt.trim().length === 0)
78 .map((punkt) => `${frageId}: ${punkt.slice(0, 40)}`),
79 );
80
81 expect(zuLang).toEqual([]);
82 });
83
84 it('behaupten nirgends, was eine Antwort enthalten muss', () => {
85 /* Die Leitplanke aus `docs/entscheidung-freitext.md` Abschnitt 8. Bis
86 Fassung 0.17.0 stand über einer ähnlichen Liste „Diese Kernelemente muss
87 Ihre Antwort enthalten“ – unbelegt, und ausdrücklich abgeschafft. Dieser
88 Test hält fest, dass die Behauptung nicht durch die Hintertür
89 zurückkommt. */
90 const verboten = /muss\s+(ihre|die)\s+antwort|zwingend\s+erforderlich/iu;
91 const verstoesse = MIT_PUNKTEN.flatMap(([frageId, eintrag]) =>
92 (eintrag.kernpunkte ?? []).filter((punkt) => verboten.test(punkt)).map(() => frageId),
93 );
94
95 expect(verstoesse).toEqual([]);
96 });
97
98 it('sind bei den Fragen gesetzt, an denen der Befund hing', () => {
99 // Vier Beispiele quer durch die Kapitel, damit ein stilles Verschwinden
100 // auffällt.
101 expect(ERKLAERUNGEN.zuFrage['I.5-02']?.kernpunkte).toContain(
102 'gegen einen gegenwärtigen rechtswidrigen Angriff',
103 );
104 expect(ERKLAERUNGEN.zuFrage['I.1-30']?.kernpunkte).toHaveLength(4);
105 expect(ERKLAERUNGEN.zuFrage['II-20']?.kernpunkte).toEqual([
106 'Hülse',
107 'Geschoss',
108 'Zündsatz',
109 'Treibladung',
110 ]);
111 expect(ERKLAERUNGEN.zuFrage['IV-41']?.kernpunkte).toHaveLength(6);
112 });
113
114 it('lassen die einteiligen Musterantworten aus', () => {
115 /* Beispiele: „Unbefristet“, „Ja“, „Mindestens alle drei Jahre“. Eine
116 Prüfliste mit einem Punkt wäre Beiwerk. */
117 for (const frageId of ['I.1-71', 'I.1-72', 'I.1-65']) {
118 expect(ERKLAERUNGEN.zuFrage[frageId]?.kernpunkte, frageId).toBeUndefined();
119 }
120 });
121 });