Erklärt nicht nur, wo man zieht, sondern warum der Kalender immer gewinnt, warum sich das nicht aufschaukelt und was das Warnzeichen im Block bedeutet. Dazu der ehrliche Abschnitt "Was noch nicht geht": die Dauer lässt sich nicht am Balken ziehen, und ein Zeitblock landet immer im ersten verbundenen Kalender. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
95 lines
2.9 KiB
TypeScript
95 lines
2.9 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: '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: '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()
|
|
})
|
|
}
|
|
}
|