Files
lernbar/scripts/pruefungen/musterloesung-dichtheit.mjs
T
Florian WildeandClaude Opus 5 d226a6bae8 Baustein 8: Lernzielkontrollen mit Freigabe, Abgabe und Korrektur
- Ausbilder gibt frei; vorher sieht der Azubi die Fragen nicht
- Freigabe laesst sich zuruecknehmen, solange nicht abgegeben wurde
- Antworten werden beim Tippen je Frage zwischengespeichert
- Musterloesungen erst nach dem Absenden: sichtbareKontrolle() entfernt sie
  serverseitig, bevor die Daten in die Ausgabe gelangen
- Korrektur mit Punkten und Anmerkung je Frage, Summe und Note laufen mit
- Notenschluessel der IHK; solange eine Frage offen ist, bleibt das Ergebnis
  vorlaeufig und die Kontrolle gilt nicht als korrigiert
- Ergebnis bei beiden sichtbar, Schnitt ueber alle Kontrollen
- Abgegebene Kontrollen unter "Was auf dich wartet" und im Wochenfortschritt
- scripts/pruefungen/musterloesung-dichtheit.mjs prueft die Sperre gegen den
  echten Wortlaut aus der Inhaltsdatei im rohen Seitenquelltext

36 neue Tests, davon 21 fuer die Sichtbarkeit der Musterloesung.
Insgesamt 277.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 08:57:00 +02:00

105 lines
4.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);