import { afterEach, describe, expect, it } from 'vitest'; import { openProjectFromFile, serializeProject } from '@/services/storage'; import { createEmptyProject } from '@/domain/model/factory'; import type { DesktopBridge, OpenFileResult } from '@/platform/bridge'; /** * Befund 24 zur Fassung 5.9.0. * * Der Hauptprozess liest die bestaetigte Datei mit 'utf8' und reicht den Text * unveraendert durch; `parseFileContent` gab ihn ohne Vorbehandlung an * `JSON.parse`. Steht am Dateianfang das Byteordnungszeichen U+FEFF - unter * Windows der Regelfall bei allem, was mit "UTF-8 mit BOM" geschrieben wurde, * etwa im Editor oder ueber `Out-File` der Windows PowerShell 5.1 -, wirft * `JSON.parse`. Der Anwender las "Die Datei enthält kein gültiges JSON: ..." * und musste annehmen, seine Planunterlage sei beschaedigt, obwohl der Inhalt * vollstaendig und gueltig ist. * * Auf der Schreibseite kennt das Projekt die Windows-Eigenheit laengst: * `src/services/export/csv.ts` setzt das Kennzeichen absichtlich vor jede CSV, * damit Excel die Umlaute richtig liest. Auf der Leseseite fehlte die * Entsprechung. * * `parseFileContent` ist nicht ausgefuehrt und nur ueber `openProjectFromFile` * erreichbar; die Bruecke wird dafuer nachgebildet - wie in * tests/services/projektdatei.test.ts. */ /** Das Byteordnungszeichen, ueber seinen Kennwert geschrieben. */ const KENNZEICHEN = '\uFEFF'; function mitBruecke(inhalt: string): void { const antwort: OpenFileResult = { ok: true, canceled: false, filePath: 'C:/Plaene/knotenpunkt.lsap', content: inhalt, error: null, }; const bruecke = { openFile: () => Promise.resolve(antwort), } as unknown as DesktopBridge; (globalThis as unknown as { window?: { lsaPlaner?: DesktopBridge } }).window = { lsaPlaner: bruecke, }; } afterEach(() => { delete (globalThis as unknown as { window?: unknown }).window; }); describe('Projektdatei mit UTF-8-Kennzeichen am Anfang', () => { it('liest sie ein, statt sie als "kein gültiges JSON" abzuweisen', async () => { const original = createEmptyProject('Bahnhofstraße / Ringstraße'); mitBruecke(KENNZEICHEN + serializeProject(original)); const ergebnis = await openProjectFromFile(); expect(ergebnis.message, 'Die unversehrte Planunterlage wurde abgewiesen').not.toContain( 'kein gültiges JSON', ); expect(ergebnis.ok).toBe(true); expect(ergebnis.result?.project.meta.name).toBe('Bahnhofstraße / Ringstraße'); expect(ergebnis.filePath).toBe('C:/Plaene/knotenpunkt.lsap'); }); it('schneidet genau ein Zeichen ab und keines mehr', async () => { // Ein zweites Kennzeichen ist kein Kodierungsvorzeichen mehr, sondern // Inhalt - und der ist dann tatsaechlich unbrauchbar. Die Nachsicht darf // nicht zur Reparatur beliebiger Vorspaenne werden. mitBruecke(KENNZEICHEN + KENNZEICHEN + serializeProject(createEmptyProject('Zweimal'))); const ergebnis = await openProjectFromFile(); expect(ergebnis.ok).toBe(false); expect(ergebnis.result).toBeNull(); expect(ergebnis.message).toContain('kein gültiges JSON'); }); it('weist eine wirklich unlesbare Datei weiterhin ab', async () => { mitBruecke(`${KENNZEICHEN}Das ist kein JSON, sondern Text.`); const ergebnis = await openProjectFromFile(); expect(ergebnis.ok).toBe(false); expect(ergebnis.result).toBeNull(); expect(ergebnis.message).toContain('kein gültiges JSON'); }); });