// @vitest-environment node /** * Was ausgeliefert wird, muss auch genannt werden. * * MIT, BSD, Apache und ISC verlangen sämtlich, dass Lizenztext und * Urheberrechtsvermerk mitgeliefert werden. Die Anzeige unter „Über diese * Software" speist sich aus `content/drittlizenzen.json`, erzeugt von * `tools/lizenzen_erzeugen.py`. * * Diese Liste war einmal fest verdrahtet und lief prompt auseinander: * better-sqlite3 zieht node-addon-api nach, das mit ausgeliefert wurde, in der * Anzeige aber fehlte. Der Test vergleicht deshalb bei jedem Lauf gegen den * tatsächlichen Abhängigkeitsbaum – so fällt eine Verschiebung beim nächsten * `npm install` auf, statt still zu bleiben. */ import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; const wurzel = fileURLToPath(new URL('..', import.meta.url)); interface Sperreintrag { readonly version?: string; readonly dev?: boolean; readonly devOptional?: boolean; } interface Paketsperre { readonly packages?: Record; } interface Drittlizenz { readonly name: string; readonly version: string | null; readonly lizenz: string; readonly lizenztext: string | null; readonly hinweis?: string; } function lies(pfad: string): unknown { return JSON.parse(readFileSync(join(wurzel, pfad), 'utf8')); } const sperre = lies('package-lock.json') as Paketsperre; const uebersicht = lies('../content/drittlizenzen.json') as { pakete: Drittlizenz[]; stand: string; anwendungsversion: string; }; /** Pakete, die electron-builder in das Installationspaket kopiert. */ function produktionsabhaengigkeiten(): string[] { return Object.entries(sperre.packages ?? {}) .filter(([pfad, eintrag]) => { if (!pfad.startsWith('node_modules/')) { return false; } return eintrag.dev !== true && eintrag.devOptional !== true; }) .map(([pfad]) => pfad.slice('node_modules/'.length)) .sort(); } describe('Übersicht der Fremdkomponenten', () => { const genannt = new Map(uebersicht.pakete.map((p) => [p.name, p])); it('nennt jede Produktionsabhängigkeit', () => { const fehlend = produktionsabhaengigkeiten().filter((name) => !genannt.has(name)); expect( fehlend, 'Diese Pakete werden ausgeliefert, stehen aber nicht in der Übersicht. ' + 'Erneuern mit: python tools/lizenzen_erzeugen.py', ).toEqual([]); }); it('nennt zu jeder Produktionsabhängigkeit die ausgelieferte Version', () => { for (const [pfad, eintrag] of Object.entries(sperre.packages ?? {})) { if (!pfad.startsWith('node_modules/') || eintrag.dev === true) { continue; } const name = pfad.slice('node_modules/'.length); const paket = genannt.get(name); if (paket === undefined) { continue; // vom vorigen Test abgedeckt } expect(paket.version, `Version von ${name}`).toBe(eintrag.version); } }); it('liefert zu jedem echten Paket auch den Lizenztext mit', () => { /* Sammelposten (Chromium, Node.js, SQLite) haben keinen eigenen Text im Baum – sie tragen stattdessen einen Hinweis, wo er zu finden ist. */ for (const paket of uebersicht.pakete) { if (paket.hinweis !== undefined) { continue; } expect(paket.lizenztext, `Lizenztext von ${paket.name}`).toBeTruthy(); expect((paket.lizenztext ?? '').length).toBeGreaterThan(200); } }); it('trägt ein echtes Datum, nicht den Vermerk „automatisch erzeugt“', () => { // Nur mit Datum lässt sich der Stand der Datei einem Paket zuordnen. expect(uebersicht.stand).toMatch(/^\d{4}-\d{2}-\d{2}$/u); }); it('nennt die Fassung, zu der sie gehört', () => { /* Die Datei stempelt sich selbst mit der Fassung aus `package.json`. Geprüft hat das bis 0.26.1 niemand – der Test darüber sah nur nach, ob im Feld `stand` ein Datum steht, nicht ob es das richtige ist. Die Übersicht wurde zuletzt zu 0.8.0 erzeugt und reiste seitdem in jedem Paket mit: achtzehn Fassungen lang mit einer Herkunftsangabe, die nicht stimmte. Wer wissen will, ob die Liste zum Paket gehört, das er vor sich hat, kann sich nur auf diese beiden Felder stützen. */ const { version } = lies('package.json') as { version: string }; expect(uebersicht.anwendungsversion).toBe(version); }); });