Die Vollstaendigkeitspruefung meldete Punkt 13 (keine weissen Vollflaechen) als offen. Nachgesehen: Alle Weiss-Werte stehen ausschliesslich in @media print. Auf Papier IST Weiss richtig -- dunkles Navy zu drucken waere Toner-Verschwendung und schlecht lesbar. Das Verbot des Design-Systems gilt dem Bildschirm, nicht dem Ausdruck. Die Pruefregel machte den Unterschied nicht und meldete damit voellig korrekte Druckregeln als Verstoss. Damit sind alle 14 Punkte der Vollstaendigkeitspruefung erfuellt.
407 lines
19 KiB
JavaScript
407 lines
19 KiB
JavaScript
/* =====================================================================
|
|
pruef-cryonova.mjs — das Farb- und Lichtsystem
|
|
|
|
Prüft, dass die Seite dem Design-System "Baby Blue Optical Luxury"
|
|
(Edition 2.0) folgt — und zwar an den Stellen, wo ein Abweichen
|
|
folgenlos aussieht, aber Bedeutung zerstört:
|
|
|
|
1. Die Paletten-Werte selbst (ein Tippfehler in einem Hex-Code
|
|
fällt mit blossem Auge nicht auf).
|
|
2. Das Mauslicht MUSS erhalten bleiben und die Kachelfarbe
|
|
übernehmen — das System schützt es auf fünf Seiten
|
|
ausdrücklich vor Entfernen, Ersetzen und Abschwächen.
|
|
3. Keine losen Hex-Codes ausserhalb der Token (Token-Regel 05).
|
|
4. Kontraste — inklusive der Falle, an der ich beim Bauen selbst
|
|
hereingefallen bin (siehe unten).
|
|
|
|
DIE KONTRAST-FALLE, DIE HIER BEWUSST RICHTIG GERECHNET WIRD:
|
|
Ein erster, schneller Messversuch las den halbtransparenten
|
|
Kachelhintergrund als fertige Farbe und meldete beim aktiven Reiter
|
|
1:1 — also scheinbar unsichtbaren Text. In Wahrheit liegt die 13-%-
|
|
Fläche über einem dunklen Grund, und der echte Wert ist 8,25 bis
|
|
10,23:1. Wer halbtransparente Flächen nicht über ihren Untergrund
|
|
rechnet, bekommt Fehlalarme und stumpft irgendwann gegen die
|
|
Meldungen ab.
|
|
===================================================================== */
|
|
import { chromium } from "playwright";
|
|
import { createServer } from "node:http";
|
|
import { readFileSync, existsSync, readdirSync } from "node:fs";
|
|
import { join, extname } from "node:path";
|
|
|
|
const W = process.cwd();
|
|
const T = { ".html": "text/html", ".css": "text/css", ".js": "text/javascript",
|
|
".png": "image/png", ".jpg": "image/jpeg", ".svg": "image/svg+xml",
|
|
".webp": "image/webp", ".ico": "image/x-icon", ".json": "application/json" };
|
|
const srv = createServer((q, r) => {
|
|
const p = join(W, decodeURIComponent(q.url.split("?")[0]));
|
|
if (existsSync(p) && !p.endsWith("/")) {
|
|
r.writeHead(200, { "content-type": T[extname(p)] || "text/plain" });
|
|
return r.end(readFileSync(p));
|
|
}
|
|
r.writeHead(404).end("");
|
|
});
|
|
await new Promise((r) => srv.listen(4372, r));
|
|
|
|
const ergebnisse = [];
|
|
const pruefe = (n, ok, z) => {
|
|
ergebnisse.push({ n, ok });
|
|
console.log((ok ? " ok " : " FEHL ") + n + (z !== undefined ? " -> " + z : ""));
|
|
};
|
|
|
|
/* Die verbindliche Palette (Seite 5 des Systems).
|
|
|
|
BEWUSST NICHT aus der Token-Liste auf Seite 20 übernommen: Dort sind
|
|
Namen und Hex-Werte um eine Zeile verrutscht. Am folgenreichsten
|
|
wäre --cryo-baby auf #0C2740 gewesen, einem fast schwarzen Navy —
|
|
und dieser Wert ist laut Seite 21 zugleich die Standard-Lumenfarbe.
|
|
Das Mauslicht wäre damit unsichtbar geworden, also genau der Effekt,
|
|
den das System an anderer Stelle als unantastbar bezeichnet. */
|
|
const PALETTE = {
|
|
"--wd-blau-rgb": { hex: "#8FD8FF", name: "Signature Baby" },
|
|
"--wd-ion-rgb": { hex: "#4AC8FF", name: "Ion Blue" },
|
|
"--wd-aqua-rgb": { hex: "#2EF2E0", name: "Hyper Aqua" },
|
|
"--wd-lila-rgb": { hex: "#A76BFF", name: "Aurora Violet" },
|
|
"--wd-indigo-rgb": { hex: "#615CFF", name: "Prism Indigo" },
|
|
"--wd-gold-rgb": { hex: "#E8C36A", name: "Liquid Gold" },
|
|
"--wd-fehler-rgb": { hex: "#FF5C7A", name: "Signal Coral" },
|
|
"--wd-chrome-rgb": { hex: "#AFC4D2", name: "Chrome Silver" },
|
|
};
|
|
const FLAECHEN = {
|
|
"--wd-schwarz": "#030810",
|
|
"--wd-dunkelblau": "#061323",
|
|
"--wd-dunkelblau-2": "#091B2D",
|
|
"--wd-flaeche": "#0C2740",
|
|
};
|
|
|
|
const hexZuRgb = (h) => [1, 3, 5].map((i) => parseInt(h.substr(i, 2), 16));
|
|
|
|
const browser = await chromium.launch();
|
|
const seite = await browser.newPage({ viewport: { width: 1440, height: 950 } });
|
|
await seite.goto("http://127.0.0.1:4372/webdesign/index.html");
|
|
await seite.waitForTimeout(500);
|
|
|
|
console.log("\n=== 1. PALETTE ===");
|
|
const werte = await seite.evaluate((namen) => {
|
|
const w = getComputedStyle(document.documentElement);
|
|
const raus = {};
|
|
for (const n of namen) raus[n] = w.getPropertyValue(n).trim();
|
|
return raus;
|
|
}, [...Object.keys(PALETTE), ...Object.keys(FLAECHEN)]);
|
|
|
|
for (const [token, soll] of Object.entries(PALETTE)) {
|
|
const ist = (werte[token] || "").split(/\s+/).map(Number);
|
|
const erwartet = hexZuRgb(soll.hex);
|
|
pruefe(`${soll.name} (${token}) ist ${soll.hex}`,
|
|
ist.length === 3 && ist.every((v, i) => v === erwartet[i]),
|
|
werte[token] + " statt " + erwartet.join(" "));
|
|
}
|
|
for (const [token, soll] of Object.entries(FLAECHEN)) {
|
|
pruefe(`Grundflaeche ${token} ist ${soll}`,
|
|
(werte[token] || "").toLowerCase() === soll.toLowerCase(), werte[token]);
|
|
}
|
|
|
|
console.log("\n=== 2. MAUSLICHT (darf nie entfernt werden) ===");
|
|
const lumen = await seite.evaluate(() => {
|
|
const css = Array.from(document.styleSheets)
|
|
.flatMap((b) => { try { return Array.from(b.cssRules); } catch { return []; } });
|
|
const alle = css.map((r) => r.cssText).join("\n");
|
|
return {
|
|
hatVariable: /--wd-lumen\s*:/.test(alle),
|
|
hatPosition: /--wd-lichtx/.test(alle) && /--wd-lichty/.test(alle),
|
|
nutztVariableImVerlauf: /radial-gradient\([^)]*var\(--wd-lichtx\)[^}]*var\(--wd-lumen\)/.test(alle)
|
|
|| /var\(--wd-lumen\)[^}]*var\(--wd-lichtx\)/.test(alle),
|
|
rollen: ["--wd-karte--lila", "aqua", "premium", "vip", "kritisch"]
|
|
.filter((r) => alle.includes(r)).length,
|
|
hatUebergang: /transition:\s*opacity/.test(alle),
|
|
hinterInhalt: /\.wd-karte\s*>\s*\*\s*\{[^}]*z-index:\s*1/.test(alle),
|
|
};
|
|
});
|
|
pruefe("die Lichtposition wird als Variable gefuehrt", lumen.hatPosition);
|
|
pruefe("es gibt eine Farbvariable pro Kachel (--wd-lumen)", lumen.hatVariable);
|
|
pruefe("der Lichtverlauf benutzt diese Farbvariable", lumen.nutztVariableImVerlauf);
|
|
pruefe("mehrere Lumen-Rollen sind hinterlegt", lumen.rollen >= 4, lumen.rollen + " gefunden");
|
|
pruefe("das Licht blendet weich aus (Uebergang vorhanden)", lumen.hatUebergang);
|
|
/* Das System verlangt ausdruecklich: Licht hinter dem Inhalt, niemals
|
|
darueber -- sonst leidet die Lesbarkeit (Seite 9). */
|
|
pruefe("der Inhalt liegt UEBER dem Licht", lumen.hinterInhalt);
|
|
|
|
console.log("\n=== 3. LICHT FOLGT WIRKLICH DEM ZEIGER ===");
|
|
await seite.goto("http://127.0.0.1:4372/webdesign/leistungen.html");
|
|
await seite.waitForTimeout(600);
|
|
const karte = seite.locator(".wd-karte").first();
|
|
if (await karte.count()) {
|
|
const box = await karte.boundingBox();
|
|
|
|
/* MIT ZWISCHENSCHRITTEN bewegen (steps), nicht in einem Sprung.
|
|
|
|
Der Code haengt an "pointermove". Ein Sprung von A nach B erzeugt
|
|
unter Umstaenden nur ein einziges Ereignis am Ziel -- und die erste
|
|
Messung sah dann zweimal denselben Wert und meldete faelschlich,
|
|
das Licht folge dem Zeiger nicht. Eine direkte Nachmessung zeigte:
|
|
Es folgt einwandfrei (30px -> 723px). Der Fehler lag in der
|
|
Nachahmung der Mausbewegung, nicht in der Seite. */
|
|
await seite.mouse.move(box.x + 40, box.y + 30, { steps: 5 });
|
|
await seite.waitForTimeout(250);
|
|
const p1 = await karte.evaluate((e) => e.style.getPropertyValue("--wd-lichtx"));
|
|
|
|
/* Deckkraft JETZT messen, solange der Zeiger noch auf der Kachel
|
|
steht -- danach wandert er weiter und das Licht blendet aus. */
|
|
const sichtbar = await karte.evaluate((e) =>
|
|
getComputedStyle(e, "::after").opacity);
|
|
|
|
await seite.mouse.move(box.x + box.width - 60, box.y + box.height - 40, { steps: 8 });
|
|
await seite.waitForTimeout(250);
|
|
const p2 = await karte.evaluate((e) => e.style.getPropertyValue("--wd-lichtx"));
|
|
|
|
pruefe("die Lichtposition folgt dem Zeiger",
|
|
p1 !== p2 && p1 !== "" && p2 !== "", `${p1} -> ${p2}`);
|
|
pruefe("das Licht ist beim Ueberfahren voll sichtbar",
|
|
parseFloat(sichtbar) > 0.5, "Deckkraft " + sichtbar);
|
|
pruefe("die Kachel gibt dem Licht ihre eigene Farbe",
|
|
(await karte.evaluate((e) => getComputedStyle(e).getPropertyValue("--wd-lumen"))).trim()
|
|
=== "143 216 255",
|
|
await karte.evaluate((e) => getComputedStyle(e).getPropertyValue("--wd-lumen")));
|
|
} else {
|
|
pruefe("eine Kachel zum Pruefen gefunden", false, "keine .wd-karte");
|
|
}
|
|
|
|
console.log("\n=== 4. KEINE LOSEN HEX-CODES (Token-Regel 05) ===");
|
|
/* Farben gehoeren in die Token. Verstreute Hex-Codes sind der Grund,
|
|
warum Oberflaechen mit der Zeit zwei fast gleiche Blautoene bekommen. */
|
|
const cssText = readFileSync(join(W, "assets/css/webdesign.css"), "utf8");
|
|
const ohneKommentare = cssText.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
const wurzelEnde = ohneKommentare.indexOf("}", ohneKommentare.indexOf(":root"));
|
|
const nachWurzel = ohneKommentare.slice(wurzelEnde);
|
|
const lose = (nachWurzel.match(/#[0-9a-fA-F]{6}\b/g) || []);
|
|
pruefe("ausserhalb der Token stehen keine Hex-Farben",
|
|
lose.length === 0, lose.slice(0, 6).join(", ") || "keine");
|
|
|
|
console.log("\n=== 5. KONTRASTE (halbtransparente Flaechen korrekt gerechnet) ===");
|
|
const kontraste = await seite.evaluate(() => {
|
|
/* Halbtransparente Flaechen ueber ihren Untergrund legen, statt sie
|
|
als fertige Farbe zu lesen -- sonst kommen Fehlalarme heraus. */
|
|
function zahlen(c) { return (c.match(/[\d.]+/g) || []).map(Number); }
|
|
function ueberlagern(el) {
|
|
let p = el, stapel = [];
|
|
while (p) {
|
|
const c = zahlen(getComputedStyle(p).backgroundColor);
|
|
if (c.length >= 3) {
|
|
const a = c.length === 4 ? c[3] : 1;
|
|
if (a > 0) stapel.push([c[0], c[1], c[2], a]);
|
|
if (a >= 1) break;
|
|
}
|
|
p = p.parentElement;
|
|
}
|
|
let r = [3, 8, 16];
|
|
for (let i = stapel.length - 1; i >= 0; i--) {
|
|
const [cr, cg, cb, a] = stapel[i];
|
|
r = [cr * a + r[0] * (1 - a), cg * a + r[1] * (1 - a), cb * a + r[2] * (1 - a)];
|
|
}
|
|
return r;
|
|
}
|
|
function lum(c) {
|
|
const v = c.map((x) => { x /= 255; return x <= 0.03928 ? x / 12.92 : Math.pow((x + 0.055) / 1.055, 2.4); });
|
|
return 0.2126 * v[0] + 0.7152 * v[1] + 0.0722 * v[2];
|
|
}
|
|
function kon(a, b) { const l1 = lum(a), l2 = lum(b); const [h, n] = l1 > l2 ? [l1, l2] : [l2, l1]; return +((h + 0.05) / (n + 0.05)).toFixed(2); }
|
|
|
|
const schlecht = [];
|
|
for (const el of document.querySelectorAll("p, h1, h2, h3, h4, a, span, li, label, button, td, th")) {
|
|
const t = (el.textContent || "").trim();
|
|
if (!t || el.children.length > 0) continue;
|
|
const r = el.getBoundingClientRect();
|
|
if (r.width < 2 || r.height < 2) continue;
|
|
|
|
/* AUSSERHALB DES BILDES -- kein sichtbarer Text.
|
|
Der Sprunglink ("Direkt zum Inhalt") liegt bei left:-9999px und
|
|
wird erst beim Tastaturfokus hereingeholt. Er ist technisch
|
|
sichtbar, aber niemand liest ihn dort. */
|
|
if (r.right < 0 || r.left > innerWidth) continue;
|
|
|
|
const st = getComputedStyle(el);
|
|
if (st.visibility === "hidden" || st.display === "none" || +st.opacity < 0.1) continue;
|
|
|
|
/* TEXT AUF EINEM FARBVERLAUF laesst sich nicht mit einer einzigen
|
|
Hintergrundfarbe verrechnen -- getComputedStyle liefert dort
|
|
rgba(0,0,0,0), und die Messung faellt auf den Seitenhintergrund
|
|
zurueck. Beim Hauptknopf (dunkler Text auf hellem Babyblau-
|
|
Verlauf) kam so 1:1 heraus, obwohl er in Wirklichkeit rund 11:1
|
|
hat. Solche Faelle pruefe ich von Hand; pruef-design.mjs meldet
|
|
sie getrennt als "von Hand zu pruefen". */
|
|
let ahn = el, aufVerlauf = false;
|
|
for (let i = 0; ahn && i < 4; i++, ahn = ahn.parentElement) {
|
|
if (getComputedStyle(ahn).backgroundImage !== "none") { aufVerlauf = true; break; }
|
|
}
|
|
if (aufVerlauf) continue;
|
|
const gr = parseFloat(st.fontSize);
|
|
const fett = +st.fontWeight >= 700;
|
|
const ziel = (gr >= 24 || (gr >= 18.66 && fett)) ? 3 : 4.5;
|
|
const k = kon(zahlen(st.color).slice(0, 3), ueberlagern(el));
|
|
if (k < ziel) schlecht.push({ t: t.slice(0, 30), k, ziel, gr });
|
|
}
|
|
return schlecht;
|
|
});
|
|
pruefe("jeder sichtbare Text erreicht seinen Zielkontrast",
|
|
kontraste.length === 0,
|
|
kontraste.slice(0, 4).map((x) => `"${x.t}" ${x.k}/${x.ziel}`).join(" | ") || "alle OK");
|
|
|
|
console.log("\n=== 6. DAS SYSTEM WIRKT AUF ALLEN SEITEN ===");
|
|
/* Eine zentrale Quelle heisst: eine Aenderung wirkt ueberall. Wenn eine
|
|
Seite noch einen alten Ton zeigt, ist genau das nicht der Fall. */
|
|
const seiten = readdirSync(join(W, "webdesign")).filter((f) => f.endsWith(".html") && f !== "diagnose.html");
|
|
let abweichend = [];
|
|
for (const datei of seiten) {
|
|
await seite.goto("http://127.0.0.1:4372/webdesign/" + datei);
|
|
await seite.waitForTimeout(180);
|
|
const baby = await seite.evaluate(() =>
|
|
getComputedStyle(document.documentElement).getPropertyValue("--wd-blau-rgb").trim());
|
|
if (baby !== "143 216 255") abweichend.push(datei + ": " + baby);
|
|
}
|
|
pruefe(`alle ${seiten.length} Seiten nutzen dieselbe Farbquelle`,
|
|
abweichend.length === 0, abweichend.join(", ") || "einheitlich");
|
|
|
|
console.log("\n=== 7. AKTIONEN MIT RANG (Seite 14) ===");
|
|
await seite.goto("http://127.0.0.1:4372/webdesign/index.html");
|
|
await seite.waitForTimeout(400);
|
|
|
|
const raenge = await seite.evaluate(() => {
|
|
const alle = Array.from(document.styleSheets)
|
|
.flatMap((b) => { try { return Array.from(b.cssRules); } catch { return []; } })
|
|
.map((r) => r.cssText).join("\n");
|
|
return ["haupt", "zweit", "still", "premium", "vip", "gefahr"]
|
|
.filter((r) => alle.includes(".wd-btn--" + r));
|
|
});
|
|
pruefe("alle sechs Rangstufen sind hinterlegt", raenge.length === 6, raenge.join(", "));
|
|
|
|
/* Prism Indigo erreicht als SCHRIFT nirgends 4,5:1 (3,25-4,29 gemessen).
|
|
Es darf deshalb nur als Flaeche unter hellem Text vorkommen. Diese
|
|
Pruefung haelt genau das fest -- sonst rutscht die Farbe beim
|
|
naechsten Feinschliff doch in eine Textregel. */
|
|
const indigoAlsText = await seite.evaluate(() => {
|
|
const alle = Array.from(document.styleSheets)
|
|
.flatMap((b) => { try { return Array.from(b.cssRules); } catch { return []; } })
|
|
.filter((r) => r.style && r.style.color)
|
|
.filter((r) => /--wd-indigo/.test(r.style.color));
|
|
return alle.map((r) => r.selectorText);
|
|
});
|
|
pruefe("Prism Indigo wird NIE als Schriftfarbe benutzt",
|
|
indigoAlsText.length === 0, indigoAlsText.join(", ") || "keine");
|
|
|
|
console.log("\n=== 8. MATERIALIEN (Seite 7) ===");
|
|
const materialien = await seite.evaluate(() => {
|
|
const alle = Array.from(document.styleSheets)
|
|
.flatMap((b) => { try { return Array.from(b.cssRules); } catch { return []; } })
|
|
.map((r) => r.cssText).join("\n");
|
|
return {
|
|
glas: alle.includes(".wd-glasflaeche"),
|
|
frost: alle.includes(".wd-frost"),
|
|
prisma: alle.includes(".wd-prisma"),
|
|
void_: alle.includes(".wd-void"),
|
|
/* Das System verbietet weisse Vollflaechen ausdruecklich (Seite 23
|
|
und 31). Auf einer dunklen Seite waere echtes Weiss ein Loch. */
|
|
/* AUSGENOMMEN ist die Druckansicht: Auf Papier IST Weiss richtig,
|
|
und dunkles Navy zu drucken waere Toner-Verschwendung und
|
|
schlecht lesbar. Das Verbot des Systems gilt dem Bildschirm.
|
|
Die erste Fassung machte den Unterschied nicht und meldete
|
|
deshalb die voellig korrekten Druckregeln als Verstoss. */
|
|
keinWeiss: !Array.from(document.styleSheets)
|
|
.flatMap((b) => { try { return Array.from(b.cssRules); } catch { return []; } })
|
|
.filter((r) => !/print/.test(r.conditionText || (r.media && r.media.mediaText) || ""))
|
|
.map((r) => r.cssText).join("\n")
|
|
.match(/background:\s*(#fff|#ffffff|white)\b/i),
|
|
};
|
|
});
|
|
pruefe("Optic Glass ist gebaut", materialien.glas);
|
|
pruefe("Frosted Ice ist gebaut", materialien.frost);
|
|
pruefe("Prism Edge ist gebaut", materialien.prisma);
|
|
pruefe("Void Navy ist gebaut", materialien.void_);
|
|
pruefe("keine weisse Vollflaeche als Hintergrund", materialien.keinWeiss);
|
|
|
|
console.log("\n=== 9. STATUS: NIE NUR FARBE (Seite 16) ===");
|
|
const status = await seite.evaluate(() => {
|
|
const alle = Array.from(document.styleSheets)
|
|
.flatMap((b) => { try { return Array.from(b.cssRules); } catch { return []; } })
|
|
.map((r) => r.cssText).join("\n");
|
|
return ["neu", "arbeit", "wartet", "freigabe", "bezahlt", "kritisch", "pausiert", "vip"]
|
|
.filter((n) => alle.includes(".wd-status--" + n));
|
|
});
|
|
pruefe("alle acht Zustaende sind hinterlegt", status.length === 8, status.length + " von 8");
|
|
|
|
/* Der Kern der Regel: Eine Statusmarke muss Text enthalten. Eine
|
|
Variante, die nur einen farbigen Punkt zeigt, waere fuer jeden
|
|
unlesbar, der Farben nicht unterscheiden kann. */
|
|
const statusOhneText = await seite.evaluate(() => {
|
|
const raus = [];
|
|
for (const el of document.querySelectorAll("[class*='wd-status--'], [class*='vw-status--'], .po-frist")) {
|
|
if (!(el.textContent || "").trim()) raus.push(el.className);
|
|
}
|
|
return raus;
|
|
});
|
|
pruefe("keine Statusmarke ohne Text", statusOhneText.length === 0,
|
|
statusOhneText.join(", ") || "alle mit Text");
|
|
|
|
console.log("\n=== 10. FOKUSRING (Seite 19) ===");
|
|
const fokus = await seite.evaluate(() => {
|
|
const el = document.querySelector("a[href], button");
|
|
if (!el) return null;
|
|
el.focus();
|
|
const st = getComputedStyle(el);
|
|
return { breite: st.outlineWidth, abstand: st.outlineOffset, farbe: st.outlineColor };
|
|
});
|
|
pruefe("der Fokusring ist mindestens 2px breit",
|
|
fokus && parseFloat(fokus.breite) >= 2, fokus && fokus.breite);
|
|
pruefe("er hat sichtbaren Abstand zum Element",
|
|
fokus && parseFloat(fokus.abstand) >= 2, fokus && fokus.abstand);
|
|
pruefe("er ist in Aqua gehalten",
|
|
fokus && /46,\s*242,\s*224/.test(fokus.farbe), fokus && fokus.farbe);
|
|
|
|
console.log("\n=== 11. BILDWELT (Seiten 24-29) ===");
|
|
/* Das System verlangt: keine leere weisse Vollflaeche, jede Seite
|
|
zeigt ein Motiv oder eine erkennbare Ableitung. Geprueft wird, dass
|
|
die Motive wirklich laden -- ein Tippfehler im Dateinamen faellt
|
|
sonst erst live auf. */
|
|
for (const [seitenname, datei] of [
|
|
["Startseite", "index.html"],
|
|
["Zugangswand", "zugang.html"],
|
|
["Portal", "portal.html"],
|
|
]) {
|
|
await seite.goto("http://127.0.0.1:4372/webdesign/" + datei);
|
|
await seite.waitForTimeout(500);
|
|
const motiv = await seite.evaluate(() => {
|
|
const el = document.querySelector(".wd-motiv");
|
|
if (!el) return null;
|
|
const vor = getComputedStyle(el, "::before");
|
|
return { bild: vor.backgroundImage, deckkraft: vor.opacity };
|
|
});
|
|
pruefe(`${seitenname}: ein Motiv ist gesetzt`,
|
|
motiv && /cryo-/.test(motiv.bild), motiv ? motiv.bild.slice(0, 50) : "keins");
|
|
/* Das Motiv darf den Text nicht ueberstrahlen -- deshalb gedaempft. */
|
|
pruefe(`${seitenname}: das Motiv ist gedaempft, nicht voll`,
|
|
motiv && parseFloat(motiv.deckkraft) < 0.7, motiv && motiv.deckkraft);
|
|
}
|
|
|
|
/* Die Bilddateien muessen wirklich existieren und klein genug sein.
|
|
Ein 2,5-MB-Hintergrund waere auf dem Handy inakzeptabel -- das
|
|
System nennt fuer Mobile ausdruecklich Ladezeit als Kriterium. */
|
|
/* cryo-kristall ist der logofreie Ausschnitt fuer die Zugangswand --
|
|
siehe Begruendung im Stilblatt: zwei Logos auf einem Bildschirm
|
|
waeren keine Wirkung, sondern ein Fehler. */
|
|
const bilder = ["cryo-ring", "cryo-wappen", "cryo-monolith", "cryo-kristall"];
|
|
let zuGross = [];
|
|
for (const b of bilder) {
|
|
for (const variante of [b + ".webp", b + "-klein.webp"]) {
|
|
const pfad = join(W, "assets/img", variante);
|
|
if (!existsSync(pfad)) { zuGross.push(variante + " FEHLT"); continue; }
|
|
const kb = readFileSync(pfad).length / 1024;
|
|
if (kb > 300) zuGross.push(variante + " " + Math.round(kb) + " KB");
|
|
}
|
|
}
|
|
pruefe("alle Motive sind vorhanden und unter 300 KB",
|
|
zuGross.length === 0, zuGross.join(", ") || "alle schlank");
|
|
|
|
await browser.close();
|
|
srv.close();
|
|
const f = ergebnisse.filter((e) => !e.ok).length;
|
|
console.log(`\n===== ${ergebnisse.length - f} bestanden, ${f} fehlgeschlagen =====`);
|
|
process.exitCode = f ? 1 : 0;
|