lsa-planer

LSA-Planer Professional – Planungssoftware für Lichtsignalanlagen nach RiLSA 2015 und § 45 StVO. EUPL-1.2.

/ tests export bfUnterlagen.test.ts

19,9 KB Rohdatei
tests/export/bfUnterlagen.test.ts — 482 Zeilen
1 import { inflateSync } from 'node:zlib';
2 import { describe, expect, it } from 'vitest';
3 import { buildProjectPdf, DEFAULT_PDF_OPTIONS } from '@/services/export/pdf';
4 import { buildSignalPlan } from '@/domain/plan/signalPlan';
5 import { validateProject } from '@/domain/validation';
6 import { createStandardIntersectionProject } from '@/domain/model/factory';
7 import { toWinAnsi } from '@/render/pdfSurface';
8 import type { Project, SignalGroup } from '@/domain/model/project';
9
10 /*
11 * Sofortmassnahmen an den erzeugten Unterlagen (Befunde L10, M12, S1/D-3,
12 * M5/D-6, D-5).
13 *
14 * WAS DIESE PRUEFUNG NICHT BEHAUPTET: Sie weist nicht nach, dass die erzeugten
15 * Unterlagen barrierefrei sind. Sie sind es nicht - sie sind ungetaggt, und mit
16 * jsPDF 2.5.2 ist daran nichts zu aendern. Geprueft wird ausschliesslich, was
17 * im Bestand zugesagt wurde: Titelausweisung, Lesezeichen, der Verweis auf die
18 * gleichwertige Textfassung, die Fussnote zu den unbeschrifteten Abschnitten,
19 * die Erklaerung zur Barrierefreiheit im Dokument - und dass die Zeichnung
20 * samt Legende auf der Seite bleibt.
21 *
22 * Der letzte Punkt ist der einzige mit einer Geometrie: Befund M12 war nicht
23 * daran zu erkennen, dass etwas fehlt, sondern daran, dass gezeichnet wurde,
24 * wo keine Seite mehr war. Ein Prueffall, der nur die Legendentexte im
25 * Dokument sucht, geht auch gegen den Altstand durch - die Texte SIND dort,
26 * nur ausserhalb des Papiers. Deshalb wird hier gemessen.
27 */
28
29 const STICHTAG = new Date('2026-01-01T12:00:00Z');
30
31 const PAGE_MARGIN = 36;
32 const FOOTER_HEIGHT = 26;
33 /** Unterkante des bedruckbaren Bereichs, in PDF-Koordinaten (Ursprung unten). */
34 const UNTERKANTE = PAGE_MARGIN + FOOTER_HEIGHT;
35
36 /** So, wie der Text im PDF ankommt - Anfuehrungszeichen und Striche gewandelt. */
37 function gedruckt(text: string): string {
38 return toWinAnsi(text);
39 }
40
41 interface Seite {
42 readonly nummer: number;
43 readonly text: string;
44 /** Unterster Rand aller gezeichneten Rechtecke dieser Seite. */
45 readonly unterster: number;
46 }
47
48 function entpacke(roh: string, von: number): string | null {
49 const anfang = roh.indexOf('stream', von);
50 if (anfang < 0) return null;
51 const start = anfang + (roh.startsWith('stream\r\n', anfang) ? 8 : 7);
52 const ende = roh.indexOf('endstream', start);
53 if (ende < 0) return null;
54 try {
55 return inflateSync(Buffer.from(roh.slice(start, ende), 'latin1')).toString('latin1');
56 } catch {
57 return null;
58 }
59 }
60
61 /**
62 * Seiten des Dokuments mit ihrem sichtbaren Text und ihrer Zeichengeometrie.
63 *
64 * Die Inhaltsstroeme sind komprimiert; eine Pruefung, die nur die Rohbytes
65 * durchsucht, findet keinen einzigen Buchstaben und prueft nichts.
66 */
67 function seiten(bytes: Uint8Array): readonly Seite[] {
68 const roh = new TextDecoder('latin1').decode(bytes);
69 const raus: Seite[] = [];
70
71 for (const treffer of roh.matchAll(/\/Type \/Page\b[^>]*?\/Contents (\d+) 0 R/g)) {
72 const objekt = roh.indexOf(`\n${treffer[1]!} 0 obj`);
73 const inhalt = objekt < 0 ? null : entpacke(roh, objekt);
74 if (inhalt === null) continue;
75
76 const stuecke: string[] = [];
77 for (const t of inhalt.matchAll(/\(((?:[^()\\]|\\.)*)\)\s*Tj/g)) {
78 stuecke.push(t[1]!.replace(/\\([()\\])/g, '$1'));
79 }
80
81 let unterster = Number.POSITIVE_INFINITY;
82 for (const t of inhalt.matchAll(/(-?[\d.]+) (-?[\d.]+) (-?[\d.]+) (-?[\d.]+) re/g)) {
83 unterster = Math.min(unterster, Number(t[2]));
84 }
85
86 raus.push({
87 nummer: raus.length + 1,
88 text: stuecke.join(' ').replace(/\s+/g, ' '),
89 unterster,
90 });
91 }
92 return raus;
93 }
94
95 function volltext(bytes: Uint8Array): string {
96 return seiten(bytes)
97 .map((s) => s.text)
98 .join(' ');
99 }
100
101 /** Klartextanteile der Datei - Katalog, Lesezeichen, Anzeigevorgaben. */
102 function struktur(bytes: Uint8Array): string {
103 return new TextDecoder('latin1').decode(bytes);
104 }
105
106 function mitSignalgruppen(anzahl: number): Project {
107 const basis = createStandardIntersectionProject('Musterkreuzung', STICHTAG);
108 const gruppen: SignalGroup[] = [];
109 for (let i = 0; i < anzahl; i += 1) {
110 const vorlage = basis.signalGroups[i % basis.signalGroups.length]!;
111 gruppen.push({ ...vorlage, id: `sg${i + 1}`, name: `K${i + 1}` });
112 }
113 return { ...basis, signalGroups: gruppen };
114 }
115
116 /**
117 * Dieselbe Anlage mit Namen, wie sie in einer Anordnungsunterlage stehen.
118 *
119 * Die Namen sind kein Kunstgriff: Wer eine Zufahrt samt Fahrbeziehung und
120 * Furtlage in den Signalgruppennamen schreibt, kommt muehelos auf diese Laenge -
121 * und die Blattangabe des Verweisabsatzes nennt den ersten und den letzten.
122 */
123 function mitLangenNamen(anzahl: number, teile: number): Project {
124 const basis = mitSignalgruppen(anzahl);
125 const zusatz = [
126 'Zufahrt Nord, Hauptstraße stadteinwärts, Geradeaus und Rechtsabbieger gemeinsam geführt',
127 'Zufahrt Süd, Bahnhofstraße Richtung Innenstadt, Linksabbieger mit eigener Signalgruppe',
128 'Furt West mit Mittelinsel, gemeinsame Führung von Fuß- und Radverkehr, Blindenzusatz',
129 ];
130 return {
131 ...basis,
132 signalGroups: basis.signalGroups.map((g) => ({
133 ...g,
134 name: `${g.name} ${zusatz.slice(0, teile).join(', ')}`,
135 })),
136 };
137 }
138
139 function ausdruck(project: Project): Uint8Array {
140 const plan = buildSignalPlan(project);
141 const report = validateProject(project, plan, STICHTAG);
142 return buildProjectPdf(project, plan, report, DEFAULT_PDF_OPTIONS, STICHTAG);
143 }
144
145 /** Blaetter, auf denen die Zeichnung steht - erkennbar am Verweisabsatz. */
146 function zeichenblaetter(bytes: Uint8Array): readonly Seite[] {
147 return seiten(bytes).filter((s) => s.text.includes('Balkendiagramm'));
148 }
149
150 describe('L10 - Dokumenttitel im Anzeigeprogramm', () => {
151 it('setzt DisplayDocTitle, damit nicht der Dateiname angesagt wird', () => {
152 const roh = struktur(ausdruck(createStandardIntersectionProject('Musterkreuzung', STICHTAG)));
153
154 expect(roh).toContain('/ViewerPreferences');
155 expect(roh).toContain('/DisplayDocTitle true');
156 // Der Titel selbst war schon immer gesetzt; ohne ihn bewirkt der Schalter
157 // nichts.
158 expect(roh).toContain(gedruckt('/Title (Musterkreuzung – Signalzeitenplan)'));
159 });
160 });
161
162 describe('S1 - keine Auszeichnung behaupten, die es nicht gibt', () => {
163 /*
164 * Diese Pruefung behebt nichts; sie haelt eine Zusage fest. "/MarkInfo <<
165 * /Marked true >>" liesse sich ueber die Ereignishaken von jsPDF in drei
166 * Zeilen setzen, und das Dokument bestuende danach manche automatische
167 * Pruefung. Ein Strukturbaum entstuende dabei nicht, und Sprachausgaben
168 * schalten bei "/Marked true" von ihren Positionsheuristiken auf den - dann
169 * leeren - Strukturbaum um: Sie lesen danach WENIGER vor als heute.
170 */
171 it('behauptet weder Marked Content noch einen Strukturbaum', () => {
172 const roh = struktur(ausdruck(createStandardIntersectionProject('Musterkreuzung', STICHTAG)));
173
174 expect(roh).not.toContain('/MarkInfo');
175 expect(roh).not.toContain('/Marked');
176 expect(roh).not.toContain('/StructTreeRoot');
177 });
178 });
179
180 describe('D-5 - Lesezeichen', () => {
181 it('legt fuer jeden Abschnitt ein Lesezeichen an', () => {
182 const roh = struktur(ausdruck(createStandardIntersectionProject('Musterkreuzung', STICHTAG)));
183
184 expect(roh).toContain('/Outlines');
185 expect(roh).toContain('/Type /Outlines');
186
187 for (const titel of [
188 'Deckblatt',
189 'Bestandteile der Anordnungsunterlage',
190 'Signalzeitenplan',
191 'Signalzeiten je Signalgruppe',
192 'Zwischenzeitenmatrix',
193 'Prüfbericht',
194 'Barrierefreiheit dieser Datei',
195 ]) {
196 expect(roh, titel).toContain(`/Title (${gedruckt(titel)})`);
197 }
198 });
199 });
200
201 describe('S1/D-3 - Verweis auf die gleichwertige Textfassung der Zeichnung', () => {
202 it('benennt die Zeichnung als Zeichnung und die Tabelle als ihre Textfassung', () => {
203 const bytes = ausdruck(createStandardIntersectionProject('Musterkreuzung', STICHTAG));
204 const text = volltext(bytes);
205
206 expect(text).toContain(gedruckt('Über diesem Absatz steht eine Zeichnung'));
207 expect(text).toContain(
208 gedruckt('Tabelle „Signalzeiten je Signalgruppe", die auf die Zeichnung folgt'),
209 );
210 expect(text).toContain(gedruckt('gleichwertige Textfassung der Zeichnung'));
211 // Der Textschluessel zu den Farben steht im Absatz selbst, nicht nur in der
212 // Zeichnung - er muss auch dann tragen, wenn die Zeichnung nicht ankommt.
213 expect(text).toContain(gedruckt('(R = Rot, RG = Rot-Gelb, F = Freigabe, GE = Gelb)'));
214 });
215
216 /*
217 * Ein Verweis, den man erst auf der naechsten Seite findet, ist keiner.
218 * Der Platz fuer beide Absaetze wird deshalb abgezogen, BEVOR die Hoehe der
219 * Zeichnung feststeht. Beim ersten Anlauf fehlte in dieser Reserve der
220 * Abstand von 14 pt zwischen Zeichnung und Absatz; bei 13 Signalgruppen
221 * landete die Fussnote daraufhin allein auf einer eigenen Seite. Deshalb
222 * wird hier ueber die Zahl der Signalgruppen geprueft und nicht nur am
223 * Beispielprojekt.
224 */
225 /*
226 * UND MIT NAMEN, WIE SIE IN EINER ANORDNUNGSUNTERLAGE VORKOMMEN
227 * (Befund B1).
228 *
229 * Die Reserve unter der Zeichnung wurde mit der Fassung OHNE Blattangabe
230 * gemessen, plus pauschal 18 pt "fuer eine Zeile und eine halbe zum
231 * Abrunden". Gedruckt wird bei mehreren Blaettern aber "Blatt 1 von 2 der
232 * Zeichnung, Signalgruppen <erste> bis <letzte>" - und Signalgruppennamen
233 * sind freier Anwendertext. Bei 16 langen Namen wuchs die Blattangabe ueber
234 * die eine reservierte Zeile hinaus, und die Fussnote landete umgeblaettert:
235 * gemessen Zeichnung auf 20 und 22, Fussnote auf 21 und 23. Auf dem
236 * Zeichnungsblatt stand dann nur noch der uneingeschraenkte Satz "die
237 * farbigen Abschnitte tragen ihr Kürzel und ihre Dauer" - die
238 * Richtigstellung fehlte.
239 *
240 * Die Prueffaelle darunter fuehrten ausschliesslich K1 bis Kn; mit so kurzen
241 * Namen tritt der Fall nicht auf.
242 */
243 for (const [anzahl, teile] of [
244 [20, 2],
245 [16, 3],
246 ] as const) {
247 it(`${anzahl} Signalgruppen mit langen Namen: Fussnote bleibt beim Blatt`, () => {
248 // Nachgestellt gegen die alte Reserve: Dort steht der Verweis auf
249 // Blatt 35 und 37, die Fussnote auf 36 und 38 (20 Gruppen) bzw. 43/45
250 // gegen 44/46 (16 Gruppen).
251 const blaetter = zeichenblaetter(ausdruck(mitLangenNamen(anzahl, teile)));
252
253 expect(blaetter.length).toBeGreaterThanOrEqual(1);
254 for (const blatt of blaetter) {
255 expect(blatt.text, `Blatt ${blatt.nummer}, Verweis`).toContain(
256 gedruckt('gleichwertige Textfassung der Zeichnung'),
257 );
258 expect(blatt.text, `Blatt ${blatt.nummer}, Fussnote`).toContain(
259 gedruckt('in ihnen steht dann keine Beschriftung'),
260 );
261 }
262 });
263 }
264
265 for (const anzahl of [1, 6, 9, 12, 13, 16, 20, 26]) {
266 it(`${anzahl} Signalgruppen: Verweis und Fussnote stehen auf dem Blatt der Zeichnung`, () => {
267 const blaetter = zeichenblaetter(ausdruck(mitSignalgruppen(anzahl)));
268
269 expect(blaetter.length).toBeGreaterThanOrEqual(1);
270 for (const blatt of blaetter) {
271 expect(blatt.text, `Blatt ${blatt.nummer}, Verweis`).toContain(
272 gedruckt('gleichwertige Textfassung der Zeichnung'),
273 );
274 expect(blatt.text, `Blatt ${blatt.nummer}, Fussnote`).toContain(
275 gedruckt('in ihnen steht dann keine Beschriftung'),
276 );
277 }
278 });
279 }
280 });
281
282 describe('M5/D-6 - Fussnote zu den unbeschrifteten Abschnitten', () => {
283 it('sagt, dass zu schmale Abschnitte unbeschriftet bleiben und wo ihre Dauer steht', () => {
284 const bytes = ausdruck(createStandardIntersectionProject('Musterkreuzung', STICHTAG));
285 const blaetter = zeichenblaetter(bytes);
286
287 expect(blaetter[0]!.text).toContain(
288 gedruckt(
289 'ergeben Abschnitte, die dafür zu schmal sind; in ihnen steht dann keine Beschriftung',
290 ),
291 );
292 expect(blaetter[0]!.text).toContain(
293 gedruckt(
294 'führt die Tabelle „Signalzeiten je Signalgruppe" das Signalbild mit seiner Dauer auf',
295 ),
296 );
297 expect(blaetter[0]!.text).toContain(
298 gedruckt(
299 'Bei Farbfehlsichtigkeit und im Schwarzweißausdruck ist sie deshalb die verlässliche Quelle',
300 ),
301 );
302 });
303
304 it('nimmt der Zeichnung ihren eigenen Hinweis nicht vorweg', () => {
305 /*
306 * Die Zeichnung benennt seit Fassung 5.7.0 unter ihrer Legende, welche
307 * Signalbilder es diesmal getroffen hat - das kann nur sie, weil nur dort
308 * gemessen wird, was noch hineinpasst. Die Fussnote hier sagt die Regel
309 * und nennt die Tabelle. Beruefe sie sich auf den Hinweis der Zeichnung,
310 * waere sie falsch, sobald die Zeichnung ihn weglaesst (und sie laesst ihn
311 * weg, wenn jeder Abschnitt sein Kuerzel bekommen hat).
312 */
313 const bytes = ausdruck(createStandardIntersectionProject('Musterkreuzung', STICHTAG));
314 const blatt = zeichenblaetter(bytes)[0]!;
315
316 expect(blatt.text).not.toContain(gedruckt('vermerkt die Zeichnung'));
317 expect(blatt.text).not.toContain(gedruckt('unter ihrer Legende'));
318 });
319 });
320
321 describe('M12 - die Zeichnung bleibt mit ihrer Legende auf der Seite', () => {
322 /*
323 * Der Altstand kappte die Hoehe mit Math.min, ohne der Zeichnung davon zu
324 * sagen: drawSignalPlan rechnet seine Zeilen aus dem Layout, nicht aus der
325 * Flaechenhoehe, und PdfSurface beschneidet nicht. Gemessen am Altstand lag
326 * der unterste gezeichnete Rand bei 16 Signalgruppen 11,7 pt UNTERHALB der
327 * Blattkante (-11,7 in PDF-Koordinaten), bei 20 Signalgruppen 123,7 pt.
328 * Die Legende steht ganz unten und fiel als Erstes weg; danach trug in der
329 * Zeichnung nur noch die Farbe.
330 */
331 for (const anzahl of [6, 12, 13, 16, 20, 26]) {
332 it(`${anzahl} Signalgruppen: nichts wird unter die Blattkante gezeichnet`, () => {
333 const bytes = ausdruck(mitSignalgruppen(anzahl));
334 const blaetter = zeichenblaetter(bytes);
335
336 expect(blaetter.length).toBeGreaterThanOrEqual(1);
337 for (const blatt of blaetter) {
338 expect(blatt.unterster, `Blatt ${blatt.nummer}`).toBeGreaterThanOrEqual(UNTERKANTE);
339 }
340 });
341
342 it(`${anzahl} Signalgruppen: die Legende ist auf jedem Blatt lesbar`, () => {
343 const bytes = ausdruck(mitSignalgruppen(anzahl));
344 const blaetter = zeichenblaetter(bytes);
345
346 // Ohne diese Zeile prueft die Schleife darunter nichts, sobald es kein
347 // Zeichenblatt gibt - und ginge damit auch gegen den Altstand durch.
348 expect(blaetter.length).toBeGreaterThanOrEqual(1);
349 for (const blatt of blaetter) {
350 for (const eintrag of ['R = Rot', 'RG = Rot-Gelb', 'F = Freigabe', 'GE = Gelb']) {
351 expect(blatt.text, `Blatt ${blatt.nummer}, ${eintrag}`).toContain(gedruckt(eintrag));
352 }
353 }
354 });
355 }
356
357 it('teilt erst auf, wenn es sein muss - und dann gleichmaessig', () => {
358 // Bis zu 13 Signalgruppen traegt ein Blatt; darueber wird geteilt, aber
359 // nicht in 14 und 2: Zeilen zweier so verschiedener Blaetter liessen sich
360 // nicht mehr nebeneinanderlegen.
361 expect(zeichenblaetter(ausdruck(mitSignalgruppen(6)))).toHaveLength(1);
362 expect(zeichenblaetter(ausdruck(mitSignalgruppen(13)))).toHaveLength(1);
363
364 const bytes = ausdruck(mitSignalgruppen(16));
365 const blaetter = zeichenblaetter(bytes);
366 expect(blaetter).toHaveLength(2);
367 expect(blaetter[0]!.text).toContain(
368 gedruckt('Blatt 1 von 2 der Zeichnung, Signalgruppen K1 bis K8'),
369 );
370 expect(blaetter[1]!.text).toContain(
371 gedruckt('Blatt 2 von 2 der Zeichnung, Signalgruppen K9 bis K16'),
372 );
373 // Jedes Blatt traegt seinen eigenen Verweis auf die Textfassung.
374 for (const blatt of blaetter) {
375 expect(blatt.text).toContain(gedruckt('gleichwertige Textfassung der Zeichnung'));
376 }
377 });
378
379 it('nennt das Fortsetzungsblatt im Lesezeichenverzeichnis', () => {
380 const roh = struktur(ausdruck(mitSignalgruppen(16)));
381 expect(roh).toContain('/Title (Signalzeitenplan \\(Fortsetzung\\))');
382 });
383 });
384
385 describe('Erklaerung zur Barrierefreiheit im Dokument', () => {
386 const bytes = ausdruck(createStandardIntersectionProject('Musterkreuzung', STICHTAG));
387 const text = volltext(bytes);
388
389 it('nennt die Luecke beim Namen, statt sie zu verschweigen', () => {
390 expect(text).toContain(gedruckt('Barrierefreiheit dieser Datei'));
391 expect(text).toContain(gedruckt('Diese Datei ist nicht getaggt'));
392 expect(text).toContain(gedruckt('erfüllt PDF/UA (DIN ISO 14289-1) nicht'));
393 expect(text).toContain(
394 gedruckt('1.3.1 Info und Beziehungen, 1.3.2 Bedeutungstragende Reihenfolge und 1.1.1'),
395 );
396 });
397
398 it('sagt, was das fuer Bildschirmleseprogramme bedeutet', () => {
399 expect(text).toContain(gedruckt('Überschriften werden nicht als Überschriften angesagt'));
400 expect(text).toContain(gedruckt('ohne Bezug zu Zeilen- und Spaltenköpfen vorgelesen'));
401 expect(text).toContain(gedruckt('Kopf- und Fußzeile jeder Seite stehen im Lesefluss'));
402 });
403
404 it('weist die Signalzeitentabelle als Textfassung der Zeichnung aus', () => {
405 expect(text).toContain(
406 gedruckt('Die Signalbilder der Zeichnung stehen vollständig in Textform in der Tabelle'),
407 );
408 // Und sagt zugleich, was sie NICHT ist - sonst waere der Satz eine Zusage,
409 // die das Dokument nicht halten kann.
410 expect(text).toContain(
411 gedruckt('ein Alternativtext im Sinne von 1.1.1 ist sie mangels Auszeichnung nicht'),
412 );
413 });
414
415 /*
416 * Die Zusage ist auf das eingegrenzt, was die Tabelle wirklich enthaelt.
417 *
418 * Zuvor stand da: "Der Inhalt der Zeichnung steht VOLLSTAENDIG in Textform in
419 * der Tabelle „Signalzeiten je Signalgruppe"". Die Zeichnung traegt ueber der
420 * Zeitachse aber ein Phasenband - Phasennamen, Phasenbeginn,
421 * Uebergangszeiten -, und davon steht in dieser Tabelle nichts. Der Abschnitt
422 * ist ein Lueckenausweis; er darf als einziger Teil der Datei keine Angabe
423 * machen, die einer Nachpruefung nicht standhaelt.
424 */
425 it('nennt fuer das Phasenband die zweite Tabelle und behauptet nicht, eine reiche', () => {
426 expect(text).toContain(gedruckt('Das Phasenband über der Zeitachse'));
427 expect(text).toContain(gedruckt('Abschnitt „Phasen und Phasenübergänge"'));
428 expect(text).toContain(gedruckt('Diese beiden Tabellen zusammen sind die gleichwertige'));
429 // Die zu weit gefasste Fassung darf nicht wieder auftauchen.
430 expect(text).not.toContain(gedruckt('Der Inhalt der Zeichnung steht vollständig'));
431 });
432
433 it('benennt die Luecke, wenn der Phasenabschnitt abgewaehlt wurde', () => {
434 // Ein Verweis auf einen Abschnitt, den diese Ausgabe gar nicht enthaelt,
435 // waere genau die Art Zusage, die der Abschnitt vermeiden soll.
436 const projekt = createStandardIntersectionProject('Musterkreuzung', STICHTAG);
437 const plan = buildSignalPlan(projekt);
438 const ohnePhasen = volltext(
439 buildProjectPdf(
440 projekt,
441 plan,
442 validateProject(projekt, plan, STICHTAG),
443 { ...DEFAULT_PDF_OPTIONS, includePhases: false },
444 STICHTAG,
445 ),
446 );
447 expect(ohnePhasen).toContain(gedruckt('wurde für diese Ausgabe abgewählt'));
448 expect(ohnePhasen).not.toContain(gedruckt('Diese beiden Tabellen zusammen'));
449 });
450
451 it('sagt der veroeffentlichenden Stelle, was sie zu tun hat', () => {
452 expect(text).toContain(gedruckt('§ 27a VwVfG'));
453 expect(text).toContain(gedruckt('EN 301 549 Kapitel 10'));
454 expect(text).toContain(gedruckt('PAC oder veraPDF'));
455 expect(text).toContain(gedruckt('zugängliche Fassung des Inhalts bereitzustellen'));
456 expect(text).toContain(gedruckt('keine Rechtsberatung'));
457 });
458
459 it('behauptet an keiner Stelle Konformitaet', () => {
460 // Erst nachweisen, dass die Erklaerung ueberhaupt da ist: Ein Dokument
461 // ohne sie besteht die Suche nach unzulaessigen Zusagen von selbst.
462 expect(text).toContain(gedruckt('Barrierefreiheit dieser Datei'));
463
464 for (const zusage of [
465 'ist barrierefrei',
466 'barrierefreie PDF',
467 'WCAG 2.1 AA erfüllt',
468 'erfüllt die Anforderungen der EN 301 549',
469 'PDF/UA-konform',
470 ]) {
471 expect(text, zusage).not.toContain(gedruckt(zusage));
472 }
473 });
474
475 it('steht in der Uebersicht der Bestandteile, damit man sie findet', () => {
476 expect(text).toContain(
477 gedruckt(
478 'Erklärung zur Barrierefreiheit dieser Datei ja – Abschnitt „Barrierefreiheit dieser Datei"',
479 ),
480 );
481 });
482 });