import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { ProjectStore } from '@/app/store'; import { createStandardIntersectionProject } from '@/domain/model/factory'; import type { Project } from '@/domain/model/project'; import { saveDataToFile } from '@/services/storage'; import type { ViewContext } from '@/ui/shell'; import { exportView } from '@/ui/views/exportView'; /** * Befund 23 zur Fassung 5.9.0, Folge in der Ausgabeansicht. * * Ohne Desktop-Bruecke - `npm run dev` im Browser, und im Auslieferungsstand * dann, wenn `preload.js` unter `sandbox: true` stumm scheitert - faellt * `saveDataToFile` auf das Herunterladen im Browser zurueck. Seit Befund 23 * traegt das Ergebnis dafuer `unbestaetigt: true` und die Meldung * "Herunterladen angestoßen"; ob die Datei wirklich abgelegt wurde, erfaehrt * die Anwendung nicht (der Anwender bricht den Speichern-unter-Kasten ab, eine * Unternehmensrichtlinie sperrt den Download). * * Alle drei Ausgabewege dieser Ansicht meldeten das Ergebnis dennoch als * 'erfolg' - in Gruen, wie einen abgelegten Ausdruck. `speichereEinmal` in * src/main.ts behandelt denselben Fall laengst als Warnung und sagt dazu, dass * sich die Ablage nicht feststellen laesst. Wer die Planunterlage einer * Behoerde vorlegen soll, darf eine gruene Meldung nicht als Nachweis lesen. */ vi.mock('@/services/storage', async (importOriginal) => { const echt = await importOriginal(); return { ...echt, saveDataToFile: vi.fn() }; }); const FESTES_DATUM = new Date('2026-01-01T00:00:00Z'); let aufraeumen: (() => void) | null = null; beforeEach(() => { vi.mocked(saveDataToFile).mockReset(); }); 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 = exportView.render(wurzel, context); aufraeumen = () => { abmelden(); wurzel.remove(); }; return wurzel; } function knopf(wurzel: ParentNode, beschriftung: string): HTMLButtonElement { const gefunden = [...wurzel.querySelectorAll('button')].find( (b) => b.textContent?.trim() === beschriftung, ); if (gefunden === undefined) throw new Error(`Kein Knopf "${beschriftung}"`); return gefunden; } async function warte(ms = 0): Promise { await new Promise((fertig) => setTimeout(fertig, ms)); } /** Die zuletzt angezeigte Meldung: Art und Text. */ function letzteMeldung(): { readonly art: string; readonly text: string } { const meldungen = [...document.querySelectorAll('.meldung')]; const letzte = meldungen[meldungen.length - 1]; if (letzte === undefined) throw new Error('Keine Meldung angezeigt'); const art = [...letzte.classList].find((k) => k.startsWith('meldung-')) ?? ''; return { art: art.replace('meldung-', ''), text: letzte.textContent ?? '' }; } const OHNE_NACHWEIS = { ok: true, canceled: false, filePath: null, unbestaetigt: true, message: 'Herunterladen angestoßen: Knotenpunkt - Prüfbericht.csv', } as const; const ABGELEGT = { ok: true, canceled: false, filePath: 'C:/Plaene/Knotenpunkt - Prüfbericht.csv', message: 'Gespeichert: C:/Plaene/Knotenpunkt - Prüfbericht.csv', } as const; describe('Ausgabe ohne Desktop-Brücke: die Meldung behauptet keine Ablage', () => { it('meldet eine Tabelle als Warnung mit dem Vorbehalt', async () => { vi.mocked(saveDataToFile).mockResolvedValue(OHNE_NACHWEIS); const wurzel = zeichne(createStandardIntersectionProject('Knotenpunkt', FESTES_DATUM)); knopf(wurzel, 'Prüfbericht').click(); await warte(10); const meldung = letzteMeldung(); expect(meldung.text).toContain('Herunterladen angestoßen'); expect(meldung.text, 'Der Vorbehalt aus speichereEinmal fehlt').toContain('nicht feststellen'); expect(meldung.art, 'Ein unbestätigter Vorgang wird in Grün gemeldet').toBe('warnung'); }); it('meldet die Projektdatei ebenso', async () => { vi.mocked(saveDataToFile).mockResolvedValue(OHNE_NACHWEIS); const wurzel = zeichne(createStandardIntersectionProject('Knotenpunkt', FESTES_DATUM)); knopf(wurzel, 'Projektdatei ausgeben').click(); await warte(10); const meldung = letzteMeldung(); expect(meldung.text).toContain('nicht feststellen'); expect(meldung.art).toBe('warnung'); }); it('meldet einen nachgewiesenen Schreibvorgang weiterhin als Erfolg', async () => { // Grenzwache: Der Regelweg ueber die Desktop-Bruecke meldet einen Pfad // zurueck; dort ist die Ablage belegt und der Vorbehalt waere falsch. vi.mocked(saveDataToFile).mockResolvedValue(ABGELEGT); const wurzel = zeichne(createStandardIntersectionProject('Knotenpunkt', FESTES_DATUM)); knopf(wurzel, 'Prüfbericht').click(); await warte(10); const meldung = letzteMeldung(); expect(meldung.text).toContain('Gespeichert'); expect(meldung.text).not.toContain('nicht feststellen'); expect(meldung.art).toBe('erfolg'); }); });