Files
dogfather-universe/server/pruefe-webdesign-handy.mjs
T
DogFatherGitandClaude Opus 5 29a054c359 Bilder waren hochkant gestreckt und ungleich hoch, Abstaende halbiert
Rueckmeldung 22.08.2026 per Bildschirmfoto: "die sollen alle gleich
aussehen und nicht einer groesser oder kleiner" und "ich will nicht soooo
viel abstand".

1) Verzerrte, ungleich hohe Projektbilder
   Nachgemessen: 576px breit, aber 750px bzw. 696px hoch -- also hochkant
   gestreckt und unterschiedlich, obwohl im CSS sauber "aspect-ratio:
   16/10" stand. Ursache: die width/height-Attribute im HTML (die dort
   bewusst stehen, damit der Browser vor dem Laden den Platz reserviert
   und die Seite nicht springt) wirken wie eine CSS-Hoehe und schlagen
   aspect-ratio. Fix zentral ueber ".wd img[width][height] { height:
   auto }" statt in jeder einzelnen Regel -- so kann es bei einem neuen
   Bild nicht vergessen werden.
   Jetzt beide 576x360, Karten beide 853px hoch.

2) Zu viel Leerraum
   .wd-abschnitt hatte 100,8px oben UND unten, also gut 200px zwischen
   zwei Abschnitten. Halbiert auf 57,6px, Hero von 78svh auf 68svh.
   Seitenlaenge dadurch 8798px -> 7089px bei gleichem Inhalt.

3) Zwei neue Dauerpruefungen in pruefe-webdesign-handy.mjs, damit genau
   diese beiden Fehlerarten nicht wieder per Bildschirmfoto auffallen
   muessen:
   - verzerrte Bilder (gewuenschtes vs. tatsaechlich gerendertes
     Seitenverhaeltnis, 2 % Toleranz)
   - ungleich hohe Karten innerhalb EINER Rasterzeile

40/40 Abnahme weiterhin sauber.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-22 18:26:07 +02:00

219 lines
9.3 KiB
JavaScript

/* =====================================================================
pruefe-webdesign-handy.mjs — Handy-Abnahme für alle Webdesign-Seiten.
Prüft je Seite und je Bildschirmbreite (320 / 390 / 430 / 768 / 1440):
- kein waagerechter Überlauf (der häufigste Handy-Fehler überhaupt)
- kein Element ragt über den rechten Rand hinaus
- alle Knöpfe und Links sind mindestens 44px hoch (Touch-Ziel)
- keine Konsolenfehler
- in allen fünf Sprachen: keine unübersetzten Schlüssel sichtbar
320px ist bewusst dabei: das ist ein iPhone SE der ersten Generationen
und immer noch die Breite, bei der Layouts zuerst brechen.
Aufruf (lokaler Testserver muss laufen):
node pruefe-webdesign-handy.mjs
===================================================================== */
import { chromium } from "playwright";
const BASIS = process.env.WD_BASIS || "http://127.0.0.1:4199";
const CODE = process.env.WD_CODE || "DOGI-TEST-1";
const SEITEN = [
"/webdesign/",
"/webdesign/leistungen.html",
"/webdesign/preise.html",
"/webdesign/portfolio.html",
"/webdesign/ablauf.html",
"/webdesign/ueber.html",
"/webdesign/faq.html",
];
const BREITEN = [320, 390, 430, 768, 1440];
const SPRACHEN = ["de", "de-CH", "en", "fr", "pt"];
let ok = 0;
const probleme = [];
const browser = await chromium.launch();
const kontext = await browser.newContext();
const seite = await kontext.newPage();
const konsole = [];
seite.on("console", (m) => { if (m.type() === "error") konsole.push(m.text()); });
seite.on("pageerror", (e) => konsole.push("pageerror: " + e.message));
// --- Anmelden ---
await seite.goto(BASIS + "/webdesign/zugang.html", { waitUntil: "networkidle" });
await seite.fill("#zg-code-dogfather", CODE);
await Promise.all([
seite.waitForURL((u) => !u.pathname.endsWith("zugang.html"), { timeout: 15000 }),
seite.click("#zg-form-dogfather button[type=submit]"),
]);
console.log("\n=== Handy-Abnahme ===\n");
for (const pfad of SEITEN) {
console.log(pfad);
for (const breite of BREITEN) {
await seite.setViewportSize({ width: breite, height: 900 });
await seite.goto(BASIS + pfad, { waitUntil: "networkidle" });
// Marken-Intro wegklicken, es liegt sonst über allem.
const skip = seite.locator("#wd-intro-skip");
if (await skip.isVisible().catch(() => false)) await skip.click();
await seite.waitForTimeout(250);
const befund = await seite.evaluate((vp) => {
const raus = [];
// Waagerechter Überlauf des ganzen Dokuments
const ueberlauf = document.documentElement.scrollWidth > vp + 1;
// Einzelne Elemente, die über den rechten Rand ragen
for (const el of document.querySelectorAll("body *")) {
const s = getComputedStyle(el);
if (s.display === "none" || s.visibility === "hidden") continue;
// Absichtlich waagerecht scrollbare Leisten ausnehmen
if (el.closest(".ls-sprung, .fq-sprung, .wd-tabelle-rahmen")) continue;
const r = el.getBoundingClientRect();
if (r.width === 0 || r.height === 0) continue;
if (r.right > vp + 1.5) {
raus.push((el.tagName.toLowerCase() + "." + (el.className || "").toString().split(" ")[0])
.slice(0, 50) + " bis " + Math.round(r.right) + "px");
}
}
// Zu kleine Touch-Ziele
const klein = [];
for (const el of document.querySelectorAll("a, button, summary, input, select")) {
const s = getComputedStyle(el);
if (s.display === "none" || s.visibility === "hidden") continue;
const r = el.getBoundingClientRect();
if (r.height === 0 || r.width === 0) continue;
if (r.height < 44) {
klein.push((el.tagName.toLowerCase() + " „" + (el.textContent || "").trim().slice(0, 24) + "“ ")
+ Math.round(r.height) + "px");
}
}
// Unübersetzte Schlüssel: erscheinen als roher Schlüsselname im Text
const roh = [];
for (const el of document.querySelectorAll("[data-i18n]")) {
const txt = (el.textContent || "").trim();
if (txt === el.getAttribute("data-i18n")) roh.push(txt);
}
/* Verzerrte Bilder.
Am 22.08.2026 gemeldet: zwei Projektbilder standen 576px breit,
aber 750px bzw. 696px hoch da -- hochkant gestreckt und ungleich,
obwohl im CSS 16:10 stand. Ursache waren die height-Attribute im
HTML, die wie eine CSS-Hoehe wirken und aspect-ratio schlagen.
Diese Pruefung vergleicht das im CSS gewuenschte Verhaeltnis mit
dem tatsaechlich gerenderten -- damit faellt so etwas sofort auf
und nicht erst per Bildschirmfoto. */
const verzerrt = [];
for (const bild of document.querySelectorAll("img")) {
const s = getComputedStyle(bild);
if (s.display === "none") continue;
const r = bild.getBoundingClientRect();
if (r.width < 2 || r.height < 2) continue;
const gewuenscht = s.aspectRatio;
if (!gewuenscht || gewuenscht === "auto") continue;
const teile = String(gewuenscht).split("/").map((t) => parseFloat(t));
if (teile.length !== 2 || !teile[0] || !teile[1]) continue;
const soll = teile[0] / teile[1];
const ist = r.width / r.height;
// 2 % Toleranz für Rundung auf ganze Pixel
if (Math.abs(soll - ist) / soll > 0.02) {
verzerrt.push(
(bild.className || bild.src.split("/").pop()).toString().slice(0, 40) +
` soll ${soll.toFixed(2)}, ist ${ist.toFixed(2)} (${Math.round(r.width)}x${Math.round(r.height)})`
);
}
}
/* Gleich große Karten in einer Reihe. "die sollen alle gleich
aussehen und nicht einer größer oder kleiner" -- ungleiche Höhen
in EINER Zeile sind der haeufigste Grund, warum ein Raster
unaufgeraeumt wirkt. */
const ungleich = [];
for (const raster of document.querySelectorAll(".wd-grid")) {
const kinder = [...raster.children].filter((k) => k.getBoundingClientRect().height > 0);
if (kinder.length < 2) continue;
// Nur Elemente vergleichen, die tatsächlich nebeneinander stehen
const reihen = new Map();
for (const k of kinder) {
const oben = Math.round(k.getBoundingClientRect().top);
const schluessel = Math.round(oben / 8) * 8;
if (!reihen.has(schluessel)) reihen.set(schluessel, []);
reihen.get(schluessel).push(Math.round(k.getBoundingClientRect().height));
}
for (const [, hoehen] of reihen) {
if (hoehen.length < 2) continue;
if (Math.max(...hoehen) - Math.min(...hoehen) > 2) {
ungleich.push(`${raster.className.slice(0, 30)}: ${hoehen.join(", ")}px`);
}
}
}
return {
ueberlauf,
raus: raus.slice(0, 5),
klein: klein.slice(0, 5),
roh: roh.slice(0, 5),
verzerrt: verzerrt.slice(0, 5),
ungleich: ungleich.slice(0, 5),
};
}, breite);
const zeile = ` ${String(breite).padStart(4)}px`;
const fehler = [];
if (befund.ueberlauf) fehler.push("waagerechter Ueberlauf");
if (befund.raus.length) fehler.push("ragt heraus: " + befund.raus.join(", "));
if (befund.klein.length) fehler.push("Touch-Ziel < 44px: " + befund.klein.join(", "));
if (befund.roh.length) fehler.push("unuebersetzt: " + befund.roh.join(", "));
if (befund.verzerrt.length) fehler.push("Bild verzerrt: " + befund.verzerrt.join(" | "));
if (befund.ungleich.length) fehler.push("ungleich hohe Karten: " + befund.ungleich.join(" | "));
if (fehler.length) {
probleme.push(`${pfad} @ ${breite}px -> ${fehler.join(" | ")}`);
console.log(`${zeile} FEHLER: ${fehler.join(" | ")}`);
} else {
ok++;
console.log(`${zeile} ok`);
}
}
}
// --- Sprachen: einmal je Sprache die Startseite prüfen ---
console.log("\nSprachen (Startseite, 390px)");
await seite.setViewportSize({ width: 390, height: 900 });
for (const sprache of SPRACHEN) {
await seite.goto(BASIS + "/webdesign/", { waitUntil: "networkidle" });
const skip = seite.locator("#wd-intro-skip");
if (await skip.isVisible().catch(() => false)) await skip.click();
await seite.evaluate((s) => window.WD.setSprache(s), sprache);
await seite.waitForTimeout(350);
const befund = await seite.evaluate((vp) => {
const roh = [];
for (const el of document.querySelectorAll("[data-i18n]")) {
const txt = (el.textContent || "").trim();
if (txt === el.getAttribute("data-i18n")) roh.push(txt);
}
return { roh: roh.slice(0, 5), ueberlauf: document.documentElement.scrollWidth > vp + 1, lang: document.documentElement.lang };
}, 390);
const fehler = [];
if (befund.lang !== sprache) fehler.push("lang-Attribut ist " + befund.lang);
if (befund.roh.length) fehler.push("unuebersetzt: " + befund.roh.join(", "));
if (befund.ueberlauf) fehler.push("waagerechter Ueberlauf");
if (fehler.length) { probleme.push(`Sprache ${sprache} -> ${fehler.join(" | ")}`); console.log(` ${sprache.padEnd(6)} FEHLER: ${fehler.join(" | ")}`); }
else { ok++; console.log(` ${sprache.padEnd(6)} ok`); }
}
await browser.close();
console.log(konsole.length ? "\nKONSOLENFEHLER:\n" + [...new Set(konsole)].join("\n") : "\nKeine Konsolenfehler.");
console.log(`\n=== ${ok} ok, ${probleme.length} Probleme ===`);
if (probleme.length) probleme.forEach((p) => console.log(" " + p));
process.exit(probleme.length === 0 && konsole.length === 0 ? 0 : 1);