import { chromium } from '@playwright/test'; import fs from 'node:fs'; import path from 'node:path'; /** * Gezielte Prüfung der einen Zusage, auf die es bei den Lernzielkontrollen * ankommt: Die Musterlösung darf vor dem Absenden nirgends herauskommen. * * Geprüft wird gegen den **echten** Wortlaut aus der Inhaltsdatei, nicht gegen * ausgedachte Zeichenketten – und im rohen Quelltext samt RSC-Nutzlast, nicht * im sichtbaren Text. */ const BASIS = 'http://localhost:3311'; const AZUBI = { email: 'jonas.berger@betrieb.de', pw: 'azubi-start-2026' }; const AUSBILDER = { email: 'fw@wildeflorian.de', pw: 'ausbilder-start' }; // Musterlösungen direkt aus der Datei lesen – so kann der Test nicht veralten. const yaml = fs.readFileSync(path.join(process.cwd(), 'content/lernzielkontrollen/woche-01.yaml'), 'utf8'); const loesungen = [...yaml.matchAll(/musterloesung:\s*>-\s*\n((?:\s{6}.+\n)+)/g)].map((t) => t[1].replace(/\s+/g, ' ').trim(), ); // Aussagekräftige Bruchstücke: lang genug, um nicht zufällig vorzukommen. const bruchstuecke = loesungen.map((l) => l.slice(40, 110)).filter((b) => b.length > 40); console.log(`${loesungen.length} Musterlösungen aus woche-01.yaml gelesen.`); console.log('Geprüft wird auf Bruchstücke wie:', JSON.stringify(bruchstuecke[0]?.slice(0, 60))); const browser = await chromium.launch(); const seite = await (await browser.newContext({ locale: 'de-DE' })).newPage(); async function anmelden(k) { await seite.goto(`${BASIS}/anmelden`); await seite.fill('#email', k.email); await seite.fill('#passwort', k.pw); await seite.click('button[type=submit]'); await seite.waitForURL(`${BASIS}/`); } async function abmelden() { await seite.click('button:has-text("Abmelden")'); await seite.waitForURL(/anmelden/); } /** Sucht im rohen HTML **und** in der RSC-Nutzlast. */ async function lecksuche(beschriftung) { const html = await seite.content(); const rsc = await seite.evaluate(() => // Next legt die Server-Nutzlast in self.__next_f ab – dort steht alles, // was der Server an den Browser geschickt hat. (globalThis.self?.__next_f ?? []).map((t) => (Array.isArray(t) ? String(t[1] ?? '') : '')).join(''), ); const treffer = bruchstuecke.filter((b) => html.includes(b) || rsc.includes(b)); const nullFelder = (rsc.match(/musterloesung.{0,12}null/g) ?? []).length; console.log( ` ${beschriftung.padEnd(34)} Leck: ${treffer.length === 0 ? 'nein' : `JA (${treffer.length})`}` + ` | RSC-Größe ${rsc.length} | "musterloesung":null ${nullFelder}x`, ); return treffer; } console.log('\n1) Vor der Freigabe'); await anmelden(AZUBI); await seite.goto(`${BASIS}/kontrollen/woche-01`); await seite.waitForSelector('h1'); await lecksuche('gesperrt'); console.log('\n2) Ausbilder gibt frei'); await abmelden(); await anmelden(AUSBILDER); await seite.goto(`${BASIS}/kontrollen`); await seite.waitForSelector('.kontrollzeile'); await seite.locator('.kontrollzeile', { hasText: 'Woche 1' }).locator('button:has-text("Freigeben")').click(); await seite.waitForTimeout(1500); console.log('\n3) Nach der Freigabe, vor der Abgabe – der entscheidende Fall'); await abmelden(); await anmelden(AZUBI); await seite.goto(`${BASIS}/kontrollen/woche-01`); await seite.waitForSelector('.frage-text'); console.log(` Fragen sichtbar: ${(await seite.$$('.frage-text')).length}`); const leck = await lecksuche('freigegeben, nicht abgegeben'); console.log('\n4) Nach dem Zwischenspeichern einer Antwort'); await seite.locator('textarea[id^=antwort-]').first().fill('Person, Gerät, Fehlerbild, Zeitpunkt.'); await seite.waitForTimeout(1600); await seite.reload(); await seite.waitForSelector('.frage-text'); const leck2 = await lecksuche('nach Zwischenspeichern'); console.log('\n5) Nach der Abgabe muss sie da sein'); await seite.click('button:has-text("Kontrolle abgeben")'); await seite.waitForTimeout(2500); await seite.reload(); await seite.waitForSelector('h1'); const nachher = await lecksuche('abgegeben'); console.log(` Musterlösungen jetzt sichtbar: ${nachher.length} von ${bruchstuecke.length}`); await browser.close(); const gescheitert = leck.length + leck2.length; console.log(`\n${gescheitert === 0 ? 'BESTANDEN' : 'DURCHGEFALLEN'}: vor der Abgabe ${gescheitert} Leck(s).`); process.exit(gescheitert === 0 && nachher.length === bruchstuecke.length ? 0 : 1);