lsa-planer

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

/ tests ui fenstertabellen.test.ts

6,1 KB Rohdatei
tests/ui/fenstertabellen.test.ts — 165 Zeilen
1 import { beforeAll, describe, expect, it, vi } from 'vitest';
2 import { createStandardIntersectionProject } from '@/domain/model/factory';
3 import { ladeSitzung, saveSession } from '@/services/storage';
4
5 /**
6 * Auch die Tabellen in den Fenstern von `src/main.ts` tragen Beschriftung und
7 * Zellbezug.
8 *
9 * Befund 45 zur Fassung 5.10.0. Zu EK 1.3.1 ist ohne Einschraenkung zugesagt:
10 * "Tabellen tragen `scope` an jedem Spalten- und Zeilenkopf und eine
11 * Beschriftung." Zwoelf Tabellen der Anwendung loesen das ein - alle laufen
12 * durch `beschrifteTabelle()` oder setzen beides von Hand. Die vier in
13 * `src/main.ts` taten es nicht: die Aufstellung der Einlesemeldungen, die
14 * Gegenueberstellung "Zwischenzeiten: bisher und jetzt" und die beiden
15 * Tastenkuerzeltafeln der Kurzhilfe.
16 *
17 * `tests/ui/barrierefreiheit.test.ts` kann das nicht bemerken - er laeuft ueber
18 * die elf Ansichten und erreicht diese Fenster nicht. Geprueft wird deshalb wie
19 * in `tests/ui/einlesemeldungen.test.ts` am echten Einstiegspunkt:
20 * `src/main.ts` wird eingelesen und startet die ganze Anwendung; ersetzt ist
21 * allein der Sitzungsspeicher.
22 */
23
24 vi.mock('@/services/storage', async (importOriginal) => {
25 const echt = await importOriginal<typeof import('@/services/storage')>();
26 return {
27 ...echt,
28 ladeSitzung: vi.fn(),
29 saveSession: vi.fn(),
30 addRecoveryPoint: vi.fn(),
31 };
32 });
33
34 async function warte(ms = 0): Promise<void> {
35 await new Promise((fertig) => setTimeout(fertig, ms));
36 }
37
38 function fenster(ueberschrift: string): HTMLElement | null {
39 for (const kasten of document.querySelectorAll<HTMLElement>('.dialog-hintergrund')) {
40 if (kasten.querySelector('h2')?.textContent?.trim() === ueberschrift) return kasten;
41 }
42 return null;
43 }
44
45 /** Beschriftung einer Tabelle - sichtbar oder nur fuer die Sprachausgabe. */
46 function beschriftung(tabelle: HTMLTableElement): string | null {
47 const caption = tabelle.querySelector('caption')?.textContent?.trim();
48 if (caption !== undefined && caption !== '') return caption;
49 return tabelle.getAttribute('aria-label');
50 }
51
52 function kopfzellenOhneBezug(tabelle: HTMLTableElement): string[] {
53 return [...tabelle.querySelectorAll('th')]
54 .filter((th) => !th.hasAttribute('scope'))
55 .map((th) => th.textContent?.trim() ?? '');
56 }
57
58 beforeAll(async () => {
59 vi.mocked(ladeSitzung).mockResolvedValue({
60 art: 'geladen',
61 stand: {
62 project: createStandardIntersectionProject(),
63 issues: [
64 {
65 path: 'signalGroups[0].clearingPath',
66 severity: 'warnung',
67 message: 'Räumweg wurde auf den zulässigen Höchstwert begrenzt',
68 },
69 ],
70 repaired: true,
71 // Wie bei einem uebernommenen 4.x-Projekt: Erst damit erscheint die
72 // Gegenueberstellung der Zwischenzeiten.
73 migrated: true,
74 sourceVersion: '4.3.3',
75 intergreenComparison: [{ from: 'K1', to: 'F1', legacyValue: 4 }],
76 },
77 });
78 vi.mocked(saveSession).mockResolvedValue({ ok: true, message: '' });
79
80 localStorage.clear();
81 document.body.replaceChildren();
82 const wurzel = document.createElement('div');
83 wurzel.id = 'anwendung';
84 document.body.append(wurzel);
85
86 await import('@/main');
87 await warte(500);
88 });
89
90 describe('Fenster "Projekt übernommen"', () => {
91 it('beschriftet beide Tabellen und ordnet jeden Spaltenkopf zu', () => {
92 const kasten = fenster('Projekt übernommen');
93 expect(kasten, 'das Fenster steht nicht offen').not.toBeNull();
94
95 const tabellen = [...kasten!.querySelectorAll('table')];
96 expect(tabellen).toHaveLength(2);
97
98 for (const tabelle of tabellen) {
99 const koepfe = [...tabelle.querySelectorAll('th')].map((th) => th.textContent?.trim());
100 expect(beschriftung(tabelle), `ohne Beschriftung: ${koepfe.join('|')}`).not.toBeNull();
101 expect(kopfzellenOhneBezug(tabelle), 'Spaltenkopf ohne scope').toEqual([]);
102 }
103 });
104
105 it('nennt in der Beschriftung, was die Tabelle zeigt', () => {
106 const kasten = fenster('Projekt übernommen');
107 const beschriftungen = [...kasten!.querySelectorAll('table')].map((t) => beschriftung(t));
108
109 expect(beschriftungen[0]).toContain('Einlesen');
110 expect(beschriftungen[1]).toContain('Zwischenzeiten');
111 });
112
113 it('ordnet in der Gegenueberstellung jede der vier Spalten zu', () => {
114 // Vier Spalten mit Sekundenwerten: Ohne Spaltenbezug liest eine
115 // Sprachausgabe beim Wandern nur "4 s, 6 s, +2 s" vor.
116 const kasten = fenster('Projekt übernommen');
117 const zweite = [...kasten!.querySelectorAll('table')][1]!;
118 const koepfe = [...zweite.querySelectorAll('thead th')];
119
120 expect(koepfe.map((th) => th.getAttribute('scope'))).toEqual(['col', 'col', 'col', 'col']);
121 expect(koepfe.map((th) => th.textContent?.trim())).toEqual([
122 'Beziehung',
123 'bisher',
124 'jetzt',
125 'Unterschied',
126 ]);
127 });
128 });
129
130 describe('Fenster "Kurzhilfe"', () => {
131 beforeAll(async () => {
132 // Das Einlesefenster erst schliessen: `showHelp` legt sonst ein zweites
133 // Fenster auf den Stapel.
134 const kasten = fenster('Projekt übernommen');
135 const verstanden = [...(kasten?.querySelectorAll('button') ?? [])].find(
136 (b) => b.textContent?.trim() === 'Verstanden',
137 );
138 verstanden?.click();
139 await warte(20);
140 window.dispatchEvent(new KeyboardEvent('keydown', { key: 'F1', bubbles: true }));
141 await warte(20);
142 });
143
144 it('beschriftet beide Tastenkuerzeltafeln und gibt ihnen eine Kopfzeile', () => {
145 const kasten = fenster('Kurzhilfe');
146 expect(kasten, 'die Kurzhilfe steht nicht offen').not.toBeNull();
147
148 const tabellen = [...kasten!.querySelectorAll('table')];
149 expect(tabellen).toHaveLength(2);
150
151 for (const tabelle of tabellen) {
152 expect(beschriftung(tabelle)).not.toBeNull();
153 expect(tabelle.querySelectorAll('thead th').length).toBe(2);
154 expect(kopfzellenOhneBezug(tabelle)).toEqual([]);
155 }
156 });
157
158 it('unterscheidet die beiden Tafeln in der Beschriftung', () => {
159 const kasten = fenster('Kurzhilfe');
160 const beschriftungen = [...kasten!.querySelectorAll('table')].map((t) => beschriftung(t));
161
162 expect(beschriftungen[0]).not.toBe(beschriftungen[1]);
163 expect(beschriftungen[1]).toContain('Zeichenfläche');
164 });
165 });