/** * Erzeugt die Bilder für docs/ANLEITUNG.md. * * Läuft gegen die laufende Anwendung, meldet sich mit dem Seed-Administrator an * und legt die Bilder unter docs/screenshots ab — jeweils in heller und dunkler * Darstellung, damit die Anleitung zu beiden Einstellungen passt. * * docker compose up -d * npm run dev * npm run screenshots * * Die Bilder werden mit eingecheckt: die Anleitung soll auch dann lesbar sein, * wenn gerade kein Stack läuft. */ import "dotenv/config"; import { mkdir } from "node:fs/promises"; import path from "node:path"; import { chromium, type Page } from "@playwright/test"; const BASIS = process.env.E2E_BASIS_URL ?? "http://localhost:3100"; const ZIEL = path.join(process.cwd(), "docs", "screenshots"); const ADMIN_EMAIL = process.env.SEED_ADMIN_EMAIL ?? "admin@beispielwehr.de"; const ADMIN_PASSWORT = process.env.SEED_ADMIN_PASSWORT ?? "Feuerwehr2026"; interface Aufnahme { name: string; pfad: string; beschreibung: string; /** true, wenn vorher angemeldet werden muss. */ angemeldet: boolean; } const AUFNAHMEN: Aufnahme[] = [ { name: "01-anmeldung", pfad: "/anmelden", beschreibung: "Anmeldeseite mit lokalem Login", angemeldet: false, }, { name: "02-uebersicht", pfad: "/", beschreibung: "Übersicht nach der Anmeldung", angemeldet: true, }, ]; async function anmelden(seite: Page): Promise { await seite.goto(`${BASIS}/anmelden`, { waitUntil: "networkidle" }); await seite.getByLabel("E-Mail-Adresse").fill(ADMIN_EMAIL); await seite.getByLabel("Passwort").fill(ADMIN_PASSWORT); await seite.getByRole("button", { name: "Anmelden" }).click(); await seite.waitForURL(`${BASIS}/`, { timeout: 20_000 }); await seite.waitForLoadState("networkidle"); } async function main(): Promise { await mkdir(ZIEL, { recursive: true }); const browser = await chromium.launch(); for (const darstellung of ["light", "dark"] as const) { const kontext = await browser.newContext({ viewport: { width: 1440, height: 900 }, deviceScaleFactor: 2, locale: "de-DE", timezoneId: "Europe/Berlin", colorScheme: darstellung, }); const seite = await kontext.newPage(); let istAngemeldet = false; for (const aufnahme of AUFNAHMEN) { if (aufnahme.angemeldet && !istAngemeldet) { await anmelden(seite); istAngemeldet = true; } await seite.goto(`${BASIS}${aufnahme.pfad}`, { waitUntil: "networkidle" }); // Kurz warten, damit Übergänge und Symbole fertig gezeichnet sind. await seite.waitForTimeout(400); const datei = path.join(ZIEL, `${aufnahme.name}-${darstellung}.png`); await seite.screenshot({ path: datei, fullPage: true }); console.info(`[screenshots] ${aufnahme.beschreibung} (${darstellung}) -> ${datei}`); } await kontext.close(); } await browser.close(); console.info("[screenshots] Fertig."); } main().catch((fehler) => { console.error("[screenshots] Abbruch:", fehler); process.exit(1); });