import { afterEach, describe, expect, it } from 'vitest'; import { ProjectStore } from '@/app/store'; import { createStandardIntersectionProject } from '@/domain/model/factory'; import type { Project } from '@/domain/model/project'; import { beschriftetesFeld, el } from '@/ui/dom'; import type { ViewContext } from '@/ui/shell'; import { phasesView } from '@/ui/views/phasesView'; /** * Befund 62 zur Fassung 5.9.0, der Rest. * * Der Hilfeeintrag "Feste Freigabezeit einer Phase" ist ueber den Verweis aus * `freigabezeit.siehe` erreichbar - das Feld selbst hat aber kein * Fragezeichen. Es ist das einzige Bedienfeld der Phasenansicht, das eine * Vorgabe der bedarfsgerechten Verteilung uebergeht; wer wissen will, was der * Eintrag bewirkt, muss dazu einen anderen Eintrag oeffnen. * * `beschriftetesFeld` nahm bislang nur `{ hinweis, einheit }` entgegen. Der * Knopf gehoert NEBEN die Beschriftung und nicht hinein: Was im `label` steht, * ist der Name des Feldes - es hiesse sonst "Feste Freigabezeit der 1. Phase * Hilfe zu Feste Freigabezeit einer Phase". Denselben Weg gehen `feld()` im * Assistenten und `mitHilfe()` in der Projektansicht. */ const FESTES_DATUM = new Date('2026-01-01T00:00:00Z'); let aufraeumen: (() => void) | null = null; afterEach(() => { aufraeumen?.(); aufraeumen = null; document.body.replaceChildren(); }); function zeichne(project: Project): HTMLElement { const wurzel = document.createElement('div'); document.body.append(wurzel); const store = new ProjectStore(project); const context: ViewContext = { store, refresh: () => {}, navigate: () => {}, target: null, }; const abmelden = phasesView.render(wurzel, context); aufraeumen = () => { abmelden(); wurzel.remove(); }; return wurzel; } /** Das Feld mit dieser Beschriftung - samt seiner Umgebung. */ function feldMitBeschriftung( wurzel: HTMLElement, beschriftung: string, ): { readonly label: HTMLLabelElement; readonly feld: HTMLElement } { const label = [...wurzel.querySelectorAll('label')].find( (l) => l.textContent?.trim() === beschriftung, ); if (label === undefined) throw new Error(`Keine Beschriftung "${beschriftung}"`); const feld = label.closest('.feld'); if (!(feld instanceof HTMLElement)) throw new Error(`Beschriftung "${beschriftung}" ohne Feld`); return { label, feld }; } describe('Phasen: das Feld "Feste Freigabezeit" trägt einen Hilfeknopf', () => { it('stellt den Knopf neben die Beschriftung, nicht hinein', () => { const wurzel = zeichne(createStandardIntersectionProject('Phasen', FESTES_DATUM)); const { label, feld } = feldMitBeschriftung(wurzel, 'Feste Freigabezeit der 1. Phase'); const knopf = feld.querySelector('button.hilfe-knopf'); expect(knopf, 'Das Feld hat kein Fragezeichen').not.toBeNull(); expect(knopf?.getAttribute('aria-label')).toBe('Hilfe zu Feste Freigabezeit einer Phase'); expect( label.contains(knopf), 'Der Knopf steht in der Beschriftung und geht damit in den Namen des Feldes ein', ).toBe(false); }); it('lässt die Beschriftung mit dem Eingabefeld verknüpft', () => { // Der Knopf darf die Verknuepfung nicht zerreissen: Ohne sie meldet eine // Sprachausgabe wieder nur "Eingabefeld". const wurzel = zeichne(createStandardIntersectionProject('Phasen', FESTES_DATUM)); const { label } = feldMitBeschriftung(wurzel, 'Feste Freigabezeit der 1. Phase'); const ziel = document.getElementById(label.htmlFor); expect(ziel).toBeInstanceOf(HTMLInputElement); }); }); describe('beschriftetesFeld: der Hilfeknopf ist eine Zugabe', () => { it('lässt ein Feld ohne Hilfeknopf unverändert', () => { // Grenzwache: Alle bestehenden Aufrufe geben keinen Knopf mit. Fuer sie // bleibt die Beschriftung unmittelbares Kind des Feldes - ohne Kopfzeile. const eingabe = el('input', { type: 'number' }); const feld = beschriftetesFeld('Umlaufzeit', eingabe, { einheit: 's' }); expect(feld.querySelector(':scope > label')).not.toBeNull(); expect(feld.querySelector('.feld-kopf')).toBeNull(); expect(feld.querySelector('button')).toBeNull(); }); });