Files
planer/screenshots/bilder.spec.ts
T
Florian WildeandClaude Opus 5 3c7cdbbbf3 chore: Demo-Daten, Bildaufnahme und Platz in der Kopfleiste
Die Demo-Instanz redet mit niemandem mehr: Solange DEMO_MODUS gilt,
starten die Jobs fuer Zammad, Telegram und den Spiegel gar nicht erst.
Sie ist mit erfundenen Daten gefuellt und steht nur fuer Bilder — ein
Aufruf nach draussen waere bestenfalls sinnlos.

Die Kopfleiste ist breiter als die Inhaltsspalte. Mit dem elften Eintrag
passte sie dort nicht mehr, und "Einstellungen" verschwand
stillschweigend am rechten Rand. Aufgefallen ist das erst im
aufgenommenen Bild.

Der Kartentext beschrieb noch den Weg von vor dem Kopplungscode.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 04:51:37 +02:00

110 lines
3.5 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>
/**
* Nur diesen Ausschnitt aufnehmen statt der ganzen Seite.
*
* Für Themen, die auf einer gemeinsamen Seite wohnen: ein Bild der ganzen
* Einstellungsseite zeigt im Handbuchkapitel zu wenig vom Eigentlichen.
*/
ausschnitt?: string
}
const AUFNAHMEN: Aufnahme[] = [
{ name: 'anmelden', pfad: '/anmelden' },
{ name: 'start', pfad: '/' },
{ name: 'inbox', pfad: '/inbox' },
{ name: 'aufgaben', pfad: '/aufgaben' },
{ name: 'woche', pfad: '/woche' },
{ name: 'projekte', pfad: '/projekte' },
{ name: 'ideen', pfad: '/ideen' },
{ name: 'bereiche', pfad: '/bereiche' },
{ name: 'kalenderkonten', pfad: '/kalenderkonten' },
{ name: 'integrationen', pfad: '/integrationen' },
{ name: 'einstellungen', pfad: '/einstellungen' },
{
name: 'benachrichtigungen',
pfad: '/einstellungen',
ausschnitt: '[data-bild="benachrichtigungen"]',
},
{ 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)
const ziel = `${ZIEL}/${aufnahme.name}-${schema}-${geraet}.png`
if (aufnahme.ausschnitt) {
await seite.locator(aufnahme.ausschnitt).screenshot({ path: ziel })
} else {
await seite.screenshot({ path: ziel, fullPage: true })
}
}
await kontext.close()
})
}
}