npm run screenshots startet eine Wegwerf-Instanz in einem eigenen Compose-Projekt mit eigener Datenbank, spielt ausschließlich erfundene Daten ein, fährt die Seiten mit Playwright ab und räumt am Ende alles wieder weg. Je Seite entstehen vier Bilder: hell und dunkel, Schreibtisch und Handy. Die Uhr steht fest, damit sich zwischen zwei Läufen nur das ändert, was sich wirklich geändert hat. Der Demo-Seed verweigert den Dienst ohne DEMO_MODUS=true — er würde sonst erfundene Daten in eine echte Datenbank schreiben. Test- und Werkzeugordner sind jetzt aus dem Docker-Kontext ausgenommen. Ein Typfehler in scripts/screenshots.ts hatte den Bau des Abbilds blockiert, obwohl die Datei dort nichts zu suchen hat. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
89 lines
2.7 KiB
TypeScript
89 lines
2.7 KiB
TypeScript
import { mkdir } from 'node:fs/promises'
|
|
import { test, type Page } from '@playwright/test'
|
|
|
|
/**
|
|
* Nimmt die Bilder für das Handbuch auf.
|
|
*
|
|
* Je Seite vier Bilder: hell und dunkel, Schreibtisch und Handy. Die Uhr steht
|
|
* auf einem festen Zeitpunkt, damit sich zwischen zwei Läufen nur das ändert,
|
|
* was sich wirklich geändert hat.
|
|
*/
|
|
|
|
const ZIEL = 'docs/bilder'
|
|
const SCHREIBTISCH = { width: 1440, height: 900 }
|
|
const HANDY = { width: 390, height: 844 }
|
|
|
|
// Dienstag, 17. März 2026, 09:20 Uhr in Berlin.
|
|
const FESTE_ZEIT = new Date('2026-03-17T08:20:00.000Z')
|
|
|
|
const EMAIL = 'martina.beispiel@planer.invalid'
|
|
const PASSWORT = 'demo-instanz-nur-fuer-bilder'
|
|
|
|
type Aufnahme = {
|
|
name: string
|
|
pfad: string
|
|
/** Wird vor der Aufnahme ausgeführt, z. B. um ein Formular aufzuklappen. */
|
|
vorbereiten?: (seite: Page) => Promise<void>
|
|
}
|
|
|
|
const AUFNAHMEN: Aufnahme[] = [
|
|
{ name: 'anmelden', pfad: '/anmelden' },
|
|
{ name: 'start', pfad: '/' },
|
|
{ name: 'bereiche', pfad: '/bereiche' },
|
|
{ name: 'einstellungen', pfad: '/einstellungen' },
|
|
{ name: 'protokoll', pfad: '/verwaltung/protokoll' },
|
|
]
|
|
|
|
async function anmelden(seite: Page): Promise<void> {
|
|
await seite.goto('/anmelden')
|
|
await seite.getByLabel('E-Mail-Adresse').fill(EMAIL)
|
|
await seite.getByLabel('Passwort').fill(PASSWORT)
|
|
await seite.getByRole('button', { name: 'Mit Passwort anmelden' }).click()
|
|
await seite.waitForURL('/')
|
|
}
|
|
|
|
test.beforeAll(async () => {
|
|
await mkdir(ZIEL, { recursive: true })
|
|
})
|
|
|
|
for (const schema of ['hell', 'dunkel'] as const) {
|
|
for (const [geraet, groesse] of [
|
|
['schreibtisch', SCHREIBTISCH],
|
|
['handy', HANDY],
|
|
] as const) {
|
|
test(`Bilder: ${schema}, ${geraet}`, async ({ browser }) => {
|
|
const kontext = await browser.newContext({
|
|
viewport: groesse,
|
|
colorScheme: schema === 'dunkel' ? 'dark' : 'light',
|
|
deviceScaleFactor: 2,
|
|
})
|
|
const seite = await kontext.newPage()
|
|
await seite.clock.setFixedTime(FESTE_ZEIT)
|
|
|
|
for (const aufnahme of AUFNAHMEN) {
|
|
if (aufnahme.pfad !== '/anmelden') {
|
|
// Für alles außer der Anmeldeseite braucht es eine Sitzung.
|
|
if (
|
|
!(await seite.context().cookies()).some((k) => k.name.includes('session'))
|
|
) {
|
|
await anmelden(seite)
|
|
}
|
|
}
|
|
|
|
await seite.goto(aufnahme.pfad)
|
|
await seite.waitForLoadState('networkidle')
|
|
await aufnahme.vorbereiten?.(seite)
|
|
// Der Farbschema-Umschalter und Schriften sollen fertig sein.
|
|
await seite.waitForTimeout(300)
|
|
|
|
await seite.screenshot({
|
|
path: `${ZIEL}/${aufnahme.name}-${schema}-${geraet}.png`,
|
|
fullPage: true,
|
|
})
|
|
}
|
|
|
|
await kontext.close()
|
|
})
|
|
}
|
|
}
|