Umsetzung des "Website Masterplan" (16 Seiten) unter /webdesign. Zugangsschutz mit EIGENER Schranke (server/webdesign-gate.js) statt gate.js: gate.js laesst seit dem oeffentlichen Start am 21.08.2026 jeden durch, weil die Pruefung auf SITE_PUBLIC_LAUNCH_AT als allererste Zeile steht. Haette man /webdesign dahintergehaengt, waere der ausdruecklich nicht-oeffentliche Bereich inklusive Preisen und spaeteren Kundendaten ab der ersten Sekunde fuer jeden lesbar gewesen. Eigenes Sitzungs-Cookie, bereich="webdesign" im Token, damit ein gueltiges Universe-Cookie hier NICHT gilt. 37/37 Tests. Sieben oeffentliche Seiten in fuenf Sprachen (de, de-CH mit echtem Dialekt, en, fr, pt). Preise, Zeitrahmen, Paketnamen und die 30-%-Regel stehen an genau EINER Stelle in wd-core.js -- der Masterplan verlangt "ueberall widerspruchsfrei", und vier Kopien laufen bei der ersten Preisaenderung auseinander. Als App installierbar auf Handy und PC. Der Service Worker speichert bewusst KEINE HTML-Seite zwischen: nach dem Abmelden wuerden sonst geschuetzte Seiten weiter ausgeliefert, ohne dass der Server je gefragt wird. 18/18 Tests. Handy-Abnahme ueber alle Seiten in fuenf Breiten (320-1440) und fuenf Sprachen: 40/40. Der Test fand 35 echte Fehler (Touch-Ziele unter 44px), behoben im Designsystem statt einzeln pro Seite. PayPal (Wunsch 22.08.2026 "sofort auf meinem paypal"): Orders API mit intent=CAPTURE, also sofortiger Einzug statt blosser Reservierung. Gebuehr und Nettobetrag getrennt gespeichert. Betraege durchgehend als Ganzzahl in Cent. Gefaelschte Webhooks werden abgewiesen. Fail closed solange Zugangsdaten fehlen. 28/28 Tests gegen einen nachgebauten PayPal-Server. Datenbank: 15 Tabellen mit Praefix wd_, fachlich vollstaendig vom Universe getrennt. Co-Authored-By: Claude Opus 5 <[email protected]>
63 lines
2.8 KiB
JavaScript
63 lines
2.8 KiB
JavaScript
/* Temporäres Prüfskript (kein Deploy-Bestandteil): macht Screenshots des
|
|
Webdesign-Bereichs im Handy- und Desktopformat und meldet Konsolenfehler.
|
|
Aufruf: node tmp-screenshot.mjs <pfad> <name> [--code]
|
|
--code = vorher an der Zugangswand anmelden. */
|
|
import { chromium } from "playwright";
|
|
|
|
const pfad = process.argv[2] || "/webdesign/zugang.html";
|
|
const name = process.argv[3] || "seite";
|
|
const mitCode = process.argv.includes("--code");
|
|
const BASIS = "http://127.0.0.1:4199";
|
|
|
|
const browser = await chromium.launch();
|
|
const kontext = await browser.newContext();
|
|
const seite = await kontext.newPage();
|
|
|
|
const fehler = [];
|
|
seite.on("console", (m) => { if (m.type() === "error") fehler.push(m.text()); });
|
|
seite.on("pageerror", (e) => fehler.push("pageerror: " + e.message));
|
|
|
|
if (mitCode) {
|
|
await seite.goto(BASIS + "/webdesign/zugang.html", { waitUntil: "networkidle" });
|
|
await seite.fill("#zg-code-dogfather", "DOGI-TEST-1");
|
|
/* Auf die durch location.href ausgeloeste Navigation warten, statt nur
|
|
auf networkidle -- sonst startet der naechste goto() mitten in einer
|
|
laufenden Navigation und Chrome bricht beide mit ERR_ABORTED ab. */
|
|
await Promise.all([
|
|
seite.waitForURL((u) => !u.pathname.endsWith("zugang.html"), { timeout: 15000 }),
|
|
seite.click("#zg-form-dogfather button[type=submit]"),
|
|
]);
|
|
await seite.waitForLoadState("networkidle");
|
|
}
|
|
|
|
for (const [breite, hoehe, kuerzel] of [[390, 844, "handy"], [1440, 900, "desktop"]]) {
|
|
await seite.setViewportSize({ width: breite, height: hoehe });
|
|
await seite.goto(BASIS + pfad, { waitUntil: "networkidle" });
|
|
|
|
/* Marken-Intro wegklicken, falls es laeuft -- sonst liegt es auf dem Bild. */
|
|
const skip = seite.locator("#wd-intro-skip");
|
|
if (await skip.isVisible().catch(() => false)) await skip.click();
|
|
|
|
/* WICHTIG: Ein fullPage-Screenshot loest KEINEN IntersectionObserver aus.
|
|
Ohne dieses Durchscrollen bleiben alle .wd-auf-Elemente unsichtbar und
|
|
das Bild zeigt eine praktisch leere Seite -- was wie ein schwerer
|
|
Fehler aussieht, aber nur ein Messfehler waere. Deshalb einmal in
|
|
Schritten nach unten scrollen, damit jeder Abschnitt eingeblendet
|
|
wird, danach zurueck nach oben. */
|
|
await seite.evaluate(async () => {
|
|
const schritt = window.innerHeight * 0.8;
|
|
for (let y = 0; y < document.body.scrollHeight; y += schritt) {
|
|
window.scrollTo(0, y);
|
|
await new Promise((r) => setTimeout(r, 120));
|
|
}
|
|
window.scrollTo(0, 0);
|
|
await new Promise((r) => setTimeout(r, 300));
|
|
});
|
|
await seite.waitForTimeout(900); // Einblend-Animationen zu Ende laufen lassen
|
|
await seite.screenshot({ path: `tmp-${name}-${kuerzel}.png`, fullPage: kuerzel === "desktop" });
|
|
console.log(`tmp-${name}-${kuerzel}.png`);
|
|
}
|
|
|
|
console.log(fehler.length ? "KONSOLENFEHLER:\n" + fehler.join("\n") : "Keine Konsolenfehler.");
|
|
await browser.close();
|