import { describe, expect, it } from 'vitest'; import { ProjectStore } from '@/app/store'; import { ASSISTENT_VORGABE, projektAusAngaben } from '@/ui/assistent'; import type { Project } from '@/domain/model/project'; import type { ViewDefinition } from '@/ui/shell'; import { compatibilityView } from '@/ui/views/compatibilityView'; import { phasesView } from '@/ui/views/phasesView'; import { zugaenglicherName } from './zugaenglicherName'; /** * Zwei Zusagen, die eine Sprachausgabe betreffen und sich still verlieren. * * Beide Faelle sind schon einmal eingetreten und beide Male war der Schaden * derselbe: Eine Angabe, die ueber die Sicherheit der Schaltung entscheidet, * kam beim Anwender falsch oder gar nicht an. */ const FESTES_DATUM = '2026-01-01T00:00:00.000Z'; function gefuelltesProjekt(): Project { return projektAusAngaben( { ...ASSISTENT_VORGABE, fussgaenger: true, rad: true, linksabbieger: true }, FESTES_DATUM, ); } function zeichne( view: ViewDefinition, project: Project, ): { wurzel: HTMLElement; aufraeumen: () => void } { const wurzel = document.createElement('div'); document.body.append(wurzel); const aufraeumen = view.render(wurzel, { store: new ProjectStore(project), refresh: () => {}, navigate: () => {}, target: null, }); return { wurzel, aufraeumen: () => { aufraeumen(); wurzel.remove(); }, }; } describe('Vertraeglichkeitsmatrix meldet ihren Zustand genau einmal', () => { it('nennt den Zustand in der Beschriftung und nicht zusaetzlich als aria-pressed', () => { // Beides zusammen kippt gemeinsam: "vertraeglich, Umschaltflaeche, nicht // gedrueckt". Das "nicht" laesst sich auf den Namen beziehen, und damit // klingt ein vertraegliches Paar wie ein feindliches - in dieser Matrix // die Entscheidung darueber, ob zwei kreuzende Stroeme gemeinsam Freigabe // bekommen duerfen. const { wurzel, aufraeumen } = zeichne(compatibilityView, gefuelltesProjekt()); try { const zellen = [...wurzel.querySelectorAll('.vertraeglichkeit-zelle')]; expect(zellen.length, 'Die Matrix hat keine bedienbaren Zellen').toBeGreaterThan(0); for (const zelle of zellen) { const zustand = (zelle.textContent ?? '').trim(); expect(['feindlich', 'verträglich']).toContain(zustand); expect( zelle.hasAttribute('aria-pressed'), `"${zustand}" traegt zusaetzlich aria-pressed`, ).toBe(false); /* * Der vorgelesene Name enthaelt den sichtbaren Zustand UND das Paar, * um das es geht (Rest zu Befund M3). * * Zuvor stand hier `toBe(zustand)` - der Name war der blosse Zustand. * Gemessen hiessen damit in einer Matrix mit sechzehn Signalgruppen * 64 Knoepfe "feindlich" und 56 "verträglich": eine Folge gleicher * Woerter, in der niemand weiss, welches Paar er gerade umschaltet. * * Der sichtbare Text muss im Namen VORKOMMEN (WCAG 2.5.3): Wer die * Anwendung mit der Stimme bedient, spricht, was er liest. */ const name = zugaenglicherName(zelle); expect(name, 'Der sichtbare Zustand fehlt im Namen').toContain(zustand); expect(name, 'Der Name nennt das Paar nicht').toMatch(/^\S+ und \S+: /); } } finally { aufraeumen(); } }); }); describe('Wertebereich der Umlaufzeit', () => { it('steht sichtbar unter dem Feld und nennt dieselben Grenzen wie das Feld', () => { // Nur vorgelesen erfuehre ein Sehender die Grenzen erst, nachdem seine // Eingabe stillschweigend auf die naechste Schranke zurechtgestutzt wurde. const basis = gefuelltesProjekt(); const projekt: Project = { ...basis, program: { ...basis.program, method: 'manuell' } }; const { wurzel, aufraeumen } = zeichne(phasesView, projekt); try { const beschriftung = [...wurzel.querySelectorAll('label')].find( (l) => (l.textContent ?? '').trim() === 'Umlaufzeit', ); expect(beschriftung, 'Kein Feld "Umlaufzeit" in der Ansicht').toBeDefined(); const feld = wurzel.querySelector(`#${beschriftung?.htmlFor ?? ''}`); expect(feld, 'Die Beschriftung "Umlaufzeit" verweist auf kein Feld').not.toBeNull(); const hinweisId = feld?.getAttribute('aria-describedby') ?? ''; const hinweis = wurzel.querySelector(`#${hinweisId}`); expect(hinweis, 'Das Feld "Umlaufzeit" hat keinen Hinweis').not.toBeNull(); expect( hinweis?.classList.contains('nur-vorlesen'), 'Der Wertebereich der Umlaufzeit ist nur noch hoerbar', ).toBe(false); // Dieselben Grenzen wie das Feld selbst - ein von Hand danebengesetzter // Satz liefe beim naechsten Grenzwechsel auseinander und loege. const text = hinweis?.textContent ?? ''; expect(text).toContain(feld?.getAttribute('min') ?? ''); expect(text).toContain(feld?.getAttribute('max') ?? ''); } finally { aufraeumen(); } }); });