lsa-planer

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

/ tests ui hilfeAmFeld.test.ts

4,1 KB Rohdatei
tests/ui/hilfeAmFeld.test.ts — 102 Zeilen
1 import { afterEach, describe, expect, it } from 'vitest';
2 import { ProjectStore } from '@/app/store';
3 import { createStandardIntersectionProject } from '@/domain/model/factory';
4 import type { Project } from '@/domain/model/project';
5 import { beschriftetesFeld, el } from '@/ui/dom';
6 import type { ViewContext } from '@/ui/shell';
7 import { phasesView } from '@/ui/views/phasesView';
8
9 /**
10 * Befund 62 zur Fassung 5.9.0, der Rest.
11 *
12 * Der Hilfeeintrag "Feste Freigabezeit einer Phase" ist ueber den Verweis aus
13 * `freigabezeit.siehe` erreichbar - das Feld selbst hat aber kein
14 * Fragezeichen. Es ist das einzige Bedienfeld der Phasenansicht, das eine
15 * Vorgabe der bedarfsgerechten Verteilung uebergeht; wer wissen will, was der
16 * Eintrag bewirkt, muss dazu einen anderen Eintrag oeffnen.
17 *
18 * `beschriftetesFeld` nahm bislang nur `{ hinweis, einheit }` entgegen. Der
19 * Knopf gehoert NEBEN die Beschriftung und nicht hinein: Was im `label` steht,
20 * ist der Name des Feldes - es hiesse sonst "Feste Freigabezeit der 1. Phase
21 * Hilfe zu Feste Freigabezeit einer Phase". Denselben Weg gehen `feld()` im
22 * Assistenten und `mitHilfe()` in der Projektansicht.
23 */
24
25 const FESTES_DATUM = new Date('2026-01-01T00:00:00Z');
26
27 let aufraeumen: (() => void) | null = null;
28 afterEach(() => {
29 aufraeumen?.();
30 aufraeumen = null;
31 document.body.replaceChildren();
32 });
33
34 function zeichne(project: Project): HTMLElement {
35 const wurzel = document.createElement('div');
36 document.body.append(wurzel);
37 const store = new ProjectStore(project);
38 const context: ViewContext = {
39 store,
40 refresh: () => {},
41 navigate: () => {},
42 target: null,
43 };
44 const abmelden = phasesView.render(wurzel, context);
45 aufraeumen = () => {
46 abmelden();
47 wurzel.remove();
48 };
49 return wurzel;
50 }
51
52 /** Das Feld mit dieser Beschriftung - samt seiner Umgebung. */
53 function feldMitBeschriftung(
54 wurzel: HTMLElement,
55 beschriftung: string,
56 ): { readonly label: HTMLLabelElement; readonly feld: HTMLElement } {
57 const label = [...wurzel.querySelectorAll('label')].find(
58 (l) => l.textContent?.trim() === beschriftung,
59 );
60 if (label === undefined) throw new Error(`Keine Beschriftung "${beschriftung}"`);
61 const feld = label.closest('.feld');
62 if (!(feld instanceof HTMLElement)) throw new Error(`Beschriftung "${beschriftung}" ohne Feld`);
63 return { label, feld };
64 }
65
66 describe('Phasen: das Feld "Feste Freigabezeit" trägt einen Hilfeknopf', () => {
67 it('stellt den Knopf neben die Beschriftung, nicht hinein', () => {
68 const wurzel = zeichne(createStandardIntersectionProject('Phasen', FESTES_DATUM));
69 const { label, feld } = feldMitBeschriftung(wurzel, 'Feste Freigabezeit der 1. Phase');
70
71 const knopf = feld.querySelector<HTMLButtonElement>('button.hilfe-knopf');
72 expect(knopf, 'Das Feld hat kein Fragezeichen').not.toBeNull();
73 expect(knopf?.getAttribute('aria-label')).toBe('Hilfe zu Feste Freigabezeit einer Phase');
74 expect(
75 label.contains(knopf),
76 'Der Knopf steht in der Beschriftung und geht damit in den Namen des Feldes ein',
77 ).toBe(false);
78 });
79
80 it('lässt die Beschriftung mit dem Eingabefeld verknüpft', () => {
81 // Der Knopf darf die Verknuepfung nicht zerreissen: Ohne sie meldet eine
82 // Sprachausgabe wieder nur "Eingabefeld".
83 const wurzel = zeichne(createStandardIntersectionProject('Phasen', FESTES_DATUM));
84 const { label } = feldMitBeschriftung(wurzel, 'Feste Freigabezeit der 1. Phase');
85 const ziel = document.getElementById(label.htmlFor);
86
87 expect(ziel).toBeInstanceOf(HTMLInputElement);
88 });
89 });
90
91 describe('beschriftetesFeld: der Hilfeknopf ist eine Zugabe', () => {
92 it('lässt ein Feld ohne Hilfeknopf unverändert', () => {
93 // Grenzwache: Alle bestehenden Aufrufe geben keinen Knopf mit. Fuer sie
94 // bleibt die Beschriftung unmittelbares Kind des Feldes - ohne Kopfzeile.
95 const eingabe = el('input', { type: 'number' });
96 const feld = beschriftetesFeld('Umlaufzeit', eingabe, { einheit: 's' });
97
98 expect(feld.querySelector(':scope > label')).not.toBeNull();
99 expect(feld.querySelector('.feld-kopf')).toBeNull();
100 expect(feld.querySelector('button')).toBeNull();
101 });
102 });