/** * Nimmt die Bildschirmfotos für die Anleitung auf. * * npm run build && npm run start (in einer zweiten Konsole) * npm run screenshots * * Die Bilder landen unter docs/bilder/ und werden bei jedem Lauf überschrieben. * So veralten sie nicht – wer die Oberfläche ändert, lässt das Skript neu laufen. * * Umgebungsvariablen: * LERNBAR_BASIS Adresse der laufenden Anwendung (Vorgabe http://localhost:3000) * LERNBAR_AUSBILDER E-Mail des Ausbilderkontos * LERNBAR_AZUBI E-Mail des Azubikontos * LERNBAR_PASSWORT_* zugehörige Passwörter */ import fs from 'node:fs'; import path from 'node:path'; import { chromium, type Page } from '@playwright/test'; const BASIS = process.env.LERNBAR_BASIS ?? 'http://localhost:3000'; const ZIEL = path.join(process.cwd(), 'docs', 'bilder'); const AUSBILDER = { email: process.env.LERNBAR_AUSBILDER ?? 'ausbilder@betrieb.de', passwort: process.env.LERNBAR_PASSWORT_AUSBILDER ?? 'ausbilder-start', }; const AZUBI = { email: process.env.LERNBAR_AZUBI ?? 'azubi@betrieb.de', passwort: process.env.LERNBAR_PASSWORT_AZUBI ?? 'azubi-start-2026', }; const SCHREIBTISCH = { width: 1280, height: 900 }; const HANDY = { width: 390, height: 844 }; async function anmelden(seite: Page, konto: { email: string; passwort: string }) { await seite.goto(`${BASIS}/anmelden`); await seite.waitForSelector('#email'); await seite.fill('#email', konto.email); await seite.fill('#passwort', konto.passwort); await seite.click('button[type=submit]'); await seite.waitForURL(`${BASIS}/`); } async function abmelden(seite: Page) { await seite.click('button:has-text("Abmelden")'); await seite.waitForURL(/anmelden/); } async function bild(seite: Page, name: string, ganzeSeite = true) { const datei = path.join(ZIEL, `${name}.png`); await seite.screenshot({ path: datei, fullPage: ganzeSeite }); console.log(` ${path.relative(process.cwd(), datei)}`); } async function main() { fs.mkdirSync(ZIEL, { recursive: true }); const browser = await chromium.launch(); const umgebung = await browser.newContext({ viewport: SCHREIBTISCH, locale: 'de-DE', colorScheme: 'light' }); const seite = await umgebung.newPage(); console.log('Bildschirmfotos werden aufgenommen …'); // --- Anmeldung --- await seite.goto(`${BASIS}/anmelden`); await seite.waitForSelector('#email'); await bild(seite, '01-anmelden'); // --- Ausbilder --- await anmelden(seite, AUSBILDER); await bild(seite, '02-ausbilder-ueberblick'); await seite.goto(`${BASIS}/verwaltung`); await seite.waitForSelector('#neu-name'); await bild(seite, '03-verwaltung'); // Module: für das Bild ein Modul abschalten, danach den alten Stand herstellen. const umschalten = async (modul: string) => { await seite.goto(`${BASIS}/verwaltung/module`); await seite.click(`form:has(input[name="modul"][value="${modul}"]) button[type=submit]`); await seite.waitForLoadState('networkidle'); }; await umschalten('berichtsheft'); await seite.goto(`${BASIS}/verwaltung/module`); await seite.waitForSelector('#modul-karten'); await bild(seite, '25-module'); await umschalten('berichtsheft'); await seite.goto(`${BASIS}/wochenplan`); await seite.waitForSelector('h1'); await bild(seite, '04-wochenplan'); await seite.goto(`${BASIS}/aufgaben`); await seite.waitForSelector('h1'); const abnahme = await seite.$('a[href*="/aufgaben/1.1?azubi="]'); if (abnahme) { await abnahme.click(); await seite.waitForSelector('#abnahme'); await bild(seite, '05-aufgabe-abnahme'); } // --- Auswertung und Feedbackgespräch (Ausbildersicht) --- await seite.goto(`${BASIS}/auswertung`); await seite.waitForSelector('h1'); await bild(seite, '06-auswertung'); const luecke = await seite.$('[data-stufe="ohne_inhalt"]'); if (luecke) { await luecke.scrollIntoViewIfNeeded(); await bild(seite, '07-rahmenplanabdeckung', false); } await seite.goto(`${BASIS}/feedback`); await seite.waitForSelector('h1'); await bild(seite, '08-feedback-liste'); const gespraech = await seite.$('.gespraechszeile'); if (gespraech) { await gespraech.click(); await seite.waitForSelector('#bogen'); await bild(seite, '09-feedbackgespraech'); } await abmelden(seite); // --- Azubi --- await anmelden(seite, AZUBI); await bild(seite, '10-azubi-woche'); await seite.goto(`${BASIS}/handouts`); await seite.waitForSelector('h1'); await bild(seite, '11-handouts-liste'); await seite.goto(`${BASIS}/handouts?suche=subnetting`); await seite.waitForSelector('h2'); await bild(seite, '12-handouts-suche'); await seite.goto(`${BASIS}/handouts/N3`); await seite.waitForSelector('h1'); await seite.click('.pruefungsfrage > summary'); await bild(seite, '13-handout-n3'); await seite.goto(`${BASIS}/karten`); await seite.waitForSelector('h1'); await bild(seite, '14-merkkarten'); await seite.goto(`${BASIS}/aufgaben`); await seite.waitForSelector('h1'); await bild(seite, '16-aufgaben-liste'); await seite.goto(`${BASIS}/aufgaben/1.1`); await seite.waitForSelector('h1'); await bild(seite, '17-aufgabe-azubi'); await seite.goto(`${BASIS}/checklisten`); await seite.waitForSelector('h1'); await bild(seite, '18-checklisten'); await seite.goto(`${BASIS}/berichtsheft`); await seite.waitForSelector('h1'); await bild(seite, '21-berichtsheft'); await seite.goto(`${BASIS}/kontrollen`); await seite.waitForSelector('h1'); await bild(seite, '23-kontrollen'); await seite.goto(`${BASIS}/kontrollen/woche-03`); await seite.waitForSelector('h1'); await bild(seite, '24-kontrolle-ergebnis'); // Einzelblatt und Checklisten-Durchlauf jeweils von ihrer eigenen Liste aus – // vorher wurde hier auf der Kontrollseite gesucht, wo es sie gar nicht gibt. await seite.goto(`${BASIS}/berichtsheft`); await seite.waitForSelector('h1'); const blatt = await seite.$('.blattzeile-link'); if (blatt) { await blatt.click(); await seite.waitForSelector('h1'); await bild(seite, '22-wochenblatt'); } await seite.goto(`${BASIS}/checklisten`); await seite.waitForSelector('h1'); const lauf = await seite.$('.laufzeile-haupt'); if (lauf) { await lauf.click(); await seite.waitForSelector('.schrittliste'); await bild(seite, '19-checklisten-lauf'); } await seite.goto(`${BASIS}/glossar`); await seite.waitForSelector('h1'); await bild(seite, '15-glossar'); // --- Dunkle Darstellung --- await seite.goto(`${BASIS}/handouts/N3`); await seite.waitForSelector('button[aria-pressed]'); await seite.click('button[aria-pressed="false"]'); await seite.waitForTimeout(200); await bild(seite, '20-dunkel'); await seite.click('button[aria-pressed="true"]'); // --- Handy --- const handy = await browser.newContext({ viewport: HANDY, locale: 'de-DE', isMobile: true, hasTouch: true, deviceScaleFactor: 2, }); const handySeite = await handy.newPage(); await anmelden(handySeite, AZUBI); await handySeite.goto(`${BASIS}/handouts/N3`); await handySeite.waitForSelector('h1'); await bild(handySeite, '30-handy-handout'); await handySeite.goto(`${BASIS}/karten/lernen`); await handySeite.waitForSelector('.lernkarte, h1'); if (await handySeite.$('.lernkarte')) { await bild(handySeite, '31-handy-lernmodus-frage', false); await handySeite.tap('.lernkarte'); await handySeite.waitForSelector('.lernkarte-antwort'); await bild(handySeite, '32-handy-lernmodus-antwort', false); } await browser.close(); console.log('Fertig.'); } main().catch((fehler) => { console.error('Die Bildschirmfotos konnten nicht aufgenommen werden.'); console.error(fehler instanceof Error ? fehler.message : fehler); console.error(`Läuft die Anwendung unter ${BASIS}? Stimmen LERNBAR_AUSBILDER und LERNBAR_AZUBI?`); process.exit(1); });