import { beforeAll, describe, expect, it, vi } from 'vitest'; import { createStandardIntersectionProject } from '@/domain/model/factory'; import { ladeSitzung, saveSession } from '@/services/storage'; /** * Auch die Tabellen in den Fenstern von `src/main.ts` tragen Beschriftung und * Zellbezug. * * Befund 45 zur Fassung 5.10.0. Zu EK 1.3.1 ist ohne Einschraenkung zugesagt: * "Tabellen tragen `scope` an jedem Spalten- und Zeilenkopf und eine * Beschriftung." Zwoelf Tabellen der Anwendung loesen das ein - alle laufen * durch `beschrifteTabelle()` oder setzen beides von Hand. Die vier in * `src/main.ts` taten es nicht: die Aufstellung der Einlesemeldungen, die * Gegenueberstellung "Zwischenzeiten: bisher und jetzt" und die beiden * Tastenkuerzeltafeln der Kurzhilfe. * * `tests/ui/barrierefreiheit.test.ts` kann das nicht bemerken - er laeuft ueber * die elf Ansichten und erreicht diese Fenster nicht. Geprueft wird deshalb wie * in `tests/ui/einlesemeldungen.test.ts` am echten Einstiegspunkt: * `src/main.ts` wird eingelesen und startet die ganze Anwendung; ersetzt ist * allein der Sitzungsspeicher. */ vi.mock('@/services/storage', async (importOriginal) => { const echt = await importOriginal(); return { ...echt, ladeSitzung: vi.fn(), saveSession: vi.fn(), addRecoveryPoint: vi.fn(), }; }); async function warte(ms = 0): Promise { await new Promise((fertig) => setTimeout(fertig, ms)); } function fenster(ueberschrift: string): HTMLElement | null { for (const kasten of document.querySelectorAll('.dialog-hintergrund')) { if (kasten.querySelector('h2')?.textContent?.trim() === ueberschrift) return kasten; } return null; } /** Beschriftung einer Tabelle - sichtbar oder nur fuer die Sprachausgabe. */ function beschriftung(tabelle: HTMLTableElement): string | null { const caption = tabelle.querySelector('caption')?.textContent?.trim(); if (caption !== undefined && caption !== '') return caption; return tabelle.getAttribute('aria-label'); } function kopfzellenOhneBezug(tabelle: HTMLTableElement): string[] { return [...tabelle.querySelectorAll('th')] .filter((th) => !th.hasAttribute('scope')) .map((th) => th.textContent?.trim() ?? ''); } beforeAll(async () => { vi.mocked(ladeSitzung).mockResolvedValue({ art: 'geladen', stand: { project: createStandardIntersectionProject(), issues: [ { path: 'signalGroups[0].clearingPath', severity: 'warnung', message: 'Räumweg wurde auf den zulässigen Höchstwert begrenzt', }, ], repaired: true, // Wie bei einem uebernommenen 4.x-Projekt: Erst damit erscheint die // Gegenueberstellung der Zwischenzeiten. migrated: true, sourceVersion: '4.3.3', intergreenComparison: [{ from: 'K1', to: 'F1', legacyValue: 4 }], }, }); vi.mocked(saveSession).mockResolvedValue({ ok: true, message: '' }); localStorage.clear(); document.body.replaceChildren(); const wurzel = document.createElement('div'); wurzel.id = 'anwendung'; document.body.append(wurzel); await import('@/main'); await warte(500); }); describe('Fenster "Projekt übernommen"', () => { it('beschriftet beide Tabellen und ordnet jeden Spaltenkopf zu', () => { const kasten = fenster('Projekt übernommen'); expect(kasten, 'das Fenster steht nicht offen').not.toBeNull(); const tabellen = [...kasten!.querySelectorAll('table')]; expect(tabellen).toHaveLength(2); for (const tabelle of tabellen) { const koepfe = [...tabelle.querySelectorAll('th')].map((th) => th.textContent?.trim()); expect(beschriftung(tabelle), `ohne Beschriftung: ${koepfe.join('|')}`).not.toBeNull(); expect(kopfzellenOhneBezug(tabelle), 'Spaltenkopf ohne scope').toEqual([]); } }); it('nennt in der Beschriftung, was die Tabelle zeigt', () => { const kasten = fenster('Projekt übernommen'); const beschriftungen = [...kasten!.querySelectorAll('table')].map((t) => beschriftung(t)); expect(beschriftungen[0]).toContain('Einlesen'); expect(beschriftungen[1]).toContain('Zwischenzeiten'); }); it('ordnet in der Gegenueberstellung jede der vier Spalten zu', () => { // Vier Spalten mit Sekundenwerten: Ohne Spaltenbezug liest eine // Sprachausgabe beim Wandern nur "4 s, 6 s, +2 s" vor. const kasten = fenster('Projekt übernommen'); const zweite = [...kasten!.querySelectorAll('table')][1]!; const koepfe = [...zweite.querySelectorAll('thead th')]; expect(koepfe.map((th) => th.getAttribute('scope'))).toEqual(['col', 'col', 'col', 'col']); expect(koepfe.map((th) => th.textContent?.trim())).toEqual([ 'Beziehung', 'bisher', 'jetzt', 'Unterschied', ]); }); }); describe('Fenster "Kurzhilfe"', () => { beforeAll(async () => { // Das Einlesefenster erst schliessen: `showHelp` legt sonst ein zweites // Fenster auf den Stapel. const kasten = fenster('Projekt übernommen'); const verstanden = [...(kasten?.querySelectorAll('button') ?? [])].find( (b) => b.textContent?.trim() === 'Verstanden', ); verstanden?.click(); await warte(20); window.dispatchEvent(new KeyboardEvent('keydown', { key: 'F1', bubbles: true })); await warte(20); }); it('beschriftet beide Tastenkuerzeltafeln und gibt ihnen eine Kopfzeile', () => { const kasten = fenster('Kurzhilfe'); expect(kasten, 'die Kurzhilfe steht nicht offen').not.toBeNull(); const tabellen = [...kasten!.querySelectorAll('table')]; expect(tabellen).toHaveLength(2); for (const tabelle of tabellen) { expect(beschriftung(tabelle)).not.toBeNull(); expect(tabelle.querySelectorAll('thead th').length).toBe(2); expect(kopfzellenOhneBezug(tabelle)).toEqual([]); } }); it('unterscheidet die beiden Tafeln in der Beschriftung', () => { const kasten = fenster('Kurzhilfe'); const beschriftungen = [...kasten!.querySelectorAll('table')].map((t) => beschriftung(t)); expect(beschriftungen[0]).not.toBe(beschriftungen[1]); expect(beschriftungen[1]).toContain('Zeichenfläche'); }); });