lsa-planer

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

/ tests export fahrstreifenAusdruck.test.ts

4,2 KB Rohdatei
tests/export/fahrstreifenAusdruck.test.ts — 108 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 type { Project } from '@/domain/model/project';
8
9 /*
10 * Befund 55 zur Fassung 5.9.0, dieselbe Stelle im Ausdruck.
11 *
12 * In der Tabellenausgabe ist der englische Dezimalpunkt der Fahrstreifenzahl
13 * behoben (csv.ts, `fmt.numShort(group.lanes)`; Wache
14 * tests/export/fahrstreifenkomma.test.ts). Die Signalgruppentabelle
15 * des Ausdrucks fuehrte weiter `String(group.lanes)` und druckte damit "2.5".
16 *
17 * `num()` in schema.ts rundet `lanes` seit der Fassung 5.10.0 auf eine
18 * ganze Zahl AB und meldet die Ersetzung (`ganzzahlAbrunden`; Wache
19 * tests/domain/fahrstreifenzahl.test.ts) - aus einer Projektdatei kommt
20 * eine 2,5 also nicht mehr durch. Der Fall laesst sich hier trotzdem bauen:
21 * Diese Datei setzt das Projekt unmittelbar zusammen, ohne `parseProject`, und
22 * `SignalGroup.lanes` traegt im Typ keine Ganzzahlschranke. Bewacht wird die
23 * Schreibweise und nicht der Weg, auf dem der Wert entsteht: Jede andere Zahl
24 * der Unterlage steht in deutscher Schreibweise - diese eine nicht. Der
25 * Ausdruck ist die Unterlage, die zur Behoerde geht; "2.5" liest sich dort je
26 * nach Gewohnheit als 25.
27 *
28 * MUTATIONSNACHWEIS: Gegen den Altstand faellt der erste Fall. Der zweite haelt
29 * fest, dass sich an ganzen Fahrstreifenzahlen nichts aendert, und ist in
30 * beiden Staenden gruen.
31 */
32
33 const STICHTAG = new Date('2026-01-01T12:00:00Z');
34
35 /**
36 * Die einzelnen Textstuecke des PDF; die Inhaltsstroeme sind komprimiert.
37 *
38 * Stueckweise und nicht als ein Text: Eine Tabellenzelle ist ein Stueck, und
39 * nur so laesst sich pruefen, dass sie GENAU "2,5" lautet.
40 */
41 function pdfStuecke(bytes: Uint8Array): string[] {
42 const roh = new TextDecoder('latin1').decode(bytes);
43 const teile: string[] = [];
44 const suche = /stream\r?\n/g;
45 let treffer: RegExpExecArray | null;
46
47 while ((treffer = suche.exec(roh)) !== null) {
48 const start = treffer.index + treffer[0].length;
49 const ende = roh.indexOf('endstream', start);
50 if (ende < 0) continue;
51 let inhalt: string;
52 try {
53 inhalt = inflateSync(Buffer.from(roh.slice(start, ende), 'latin1')).toString('latin1');
54 } catch {
55 continue;
56 }
57 for (const t of inhalt.matchAll(/\(((?:[^()\\]|\\.)*)\)\s*Tj/g)) {
58 teile.push(t[1]!.replace(/\\([()\\])/g, '$1'));
59 }
60 }
61 return teile;
62 }
63
64 function ausdruck(project: Project): Uint8Array {
65 const plan = buildSignalPlan(project);
66 return buildProjectPdf(
67 project,
68 plan,
69 validateProject(project, plan, STICHTAG),
70 DEFAULT_PDF_OPTIONS,
71 STICHTAG,
72 );
73 }
74
75 /** Knotenpunkt, dessen erste Signalgruppe eine gebrochene Fahrstreifenzahl traegt. */
76 function mitGebrochenerFahrstreifenzahl(): Project {
77 const basis = createStandardIntersectionProject('Fahrstreifen', STICHTAG);
78 return {
79 ...basis,
80 signalGroups: basis.signalGroups.map((g, index) => (index === 0 ? { ...g, lanes: 2.5 } : g)),
81 };
82 }
83
84 describe('Befund 55 - die Fahrstreifenspalte des Ausdrucks trägt das deutsche Dezimalkomma', () => {
85 it('druckt 2,5 statt 2.5', () => {
86 const stuecke = pdfStuecke(ausdruck(mitGebrochenerFahrstreifenzahl()));
87
88 expect(stuecke, 'Vorbedingung: der Ausdruck traegt Text').not.toHaveLength(0);
89 expect(stuecke).toContain('2,5');
90 expect(stuecke).not.toContain('2.5');
91 });
92
93 it('lässt ganze Fahrstreifenzahlen unverändert', () => {
94 const projekt = createStandardIntersectionProject('Fahrstreifen', STICHTAG);
95 const stuecke = pdfStuecke(ausdruck(projekt));
96
97 for (const gruppe of projekt.signalGroups) {
98 expect(stuecke, gruppe.name).toContain(String(gruppe.lanes));
99 }
100 });
101
102 it('nennt die Fahrstreifenzahl weiterhin in der Signalgruppentabelle', () => {
103 // Zusammenhangspruefung: Die Spalte bleibt, sie wechselt nur die
104 // Schreibweise. Ohne sie fehlte n in der Kette qS0 · n · fA / fSV.
105 const stuecke = pdfStuecke(ausdruck(mitGebrochenerFahrstreifenzahl()));
106 expect(stuecke).toContain('Fahrstr.');
107 });
108 });