"je nachdem auf welcher seite ich bin wechseln gaaanz oben in der
leiste gewissene symbole und sachen mit ... ich will das auf jeder
seite ... nicht mehr wie auf screen3 sondern genau gleich wie da."
DER BEFUND WAR EINE LADEREIHENFOLGE, KEIN FEHLENDES BAUTEIL
kopf.js setzt `data-ton` am <body> -- auf JEDER Seite, seit Langem.
Die Regel, die daraus eine Akzentfarbe macht, stand aber in
bereich.css:
body[data-ton] { --akzent: color-mix(in srgb, var(--ton) 88%, #6f8cab); }
und bereich.css laden genau DREI von fuenfunddreissig Seiten
(bereich.html, bewerben.html, teilen.html). Auf den anderen
zweiunddreissig war die Farbe da und wurde nicht gelesen.
Das erklaert auch, warum ausgerechnet die Highlights-Seite sein
Vorbild war: Highlights IST bereich.html -- eine der drei.
Die Regel steht jetzt in start.css. Die liegt auf allen
fuenfunddreissig.
UND DIE LEISTE SELBST. "gaaanz oben in der leiste" ist woertlich zu
nehmen: Der Akzent wirkte bisher weiter unten (Knopfraender,
Fokusringe, Linien an Karten), die Leiste blieb auf jeder Seite
gleich. Sie bekommt jetzt eine Kante unten in der Farbe der Seite,
nach rechts auslaufend, und die Knoepfe rechts nehmen die Farbe beim
Beruehren auf. Dauerhaft eingefaerbt waeren es sechs bunte Knoepfe in
derselben Farbe -- dann traegt nicht mehr die SEITE die Farbe,
sondern die Leiste.
GEMESSEN, NICHT BEHAUPTET (pruef-kopfleiste-farbe.mjs, NEU, 9/0)
Im echten Browser, mit getComputedStyle -- ob eine CSS-Regel WIRKT,
steht nicht in der Datei:
aufgaben.html Ton 13 --akzent #12b37e (gruen)
chat.html Ton 4 --akzent #c16302 (orange)
kalender.html Ton 8 --akzent #ab68ff (violett)
Welche Seiten gemessen werden, steht NICHT in der Pruefung: Sie liest
die Kacheln der Startseite und nimmt drei mit verschiedenen Toenen.
Eine Liste dort waere die, die beim naechsten Umbau eine Seite nennt,
die es nicht mehr gibt.
Zwei Gegenproben: dass die Farben VERSCHIEDEN sind (vorher war
--akzent auf 32 von 35 Seiten dieselbe -- eine Pruefung ohne diesen
Teil waere auch im alten Zustand gruen gewesen), und dass ohne
`data-ton` wieder die Hausfarbe gilt (#8ec9ff). Eine Regel, die auch
dort zuschlaegt, wuerde eine Farbe erfinden.
EIGENER FEHLER, beim ersten Lauf dieser Pruefung
------------------------------------------------
Der Kachel-Selektor war falsch (`.kachel[href]` statt `li.kachel` mit
dem Verweis darin) -- sie fand null Kacheln. Das haette auffallen
muessen, tat es aber fast nicht: DREI Zeilen waren trotzdem gruen,
weil `every()` auf einem leeren Feld `true` liefert. Genau die Falle,
vor der die Hausregel warnt, und ich bin hineingelaufen. Die Zahl
steht jetzt in jeder dieser Bedingungen.
Mitgelaufen und gruen: pruef-css-klassen (keine Stilvorlage verliert
still eine Regel), pruef-chatkachel (33), pruef-teamlage-karten (22).
Co-Authored-By: Claude Opus 5 <[email protected]>
226 lines
10 KiB
JavaScript
226 lines
10 KiB
JavaScript
/* =====================================================================
|
|
JEDE SEITE TRÄGT IHRE FARBE — auch ganz oben in der Leiste
|
|
=====================================================================
|
|
|
|
Filipe am 22.09.2026:
|
|
|
|
„je nachdem auf welcher seite ich bin wechseln gaaanz oben in der
|
|
leiste gewissene symbole und sachen mit ... ich will das auf
|
|
jeder seite ... nicht mehr wie auf screen3 sondern genau gleich
|
|
wie da."
|
|
|
|
Sein Vorbild war die Highlights-Seite. Und das ist der Schlüssel zum
|
|
Befund: Highlights ist `bereich.html`, und `bereich.html` ist eine
|
|
von DREI Seiten, die `bereich.css` laden. Dort stand die Regel, die
|
|
aus `data-ton` eine Akzentfarbe macht.
|
|
|
|
Gemessen: `kopf.js` setzt `data-ton` am <body> auf jeder Seite. Auf
|
|
zweiunddreißig von fünfunddreißig las es niemand — die Farbe war da
|
|
und kam nicht an. Seit dem 22.09. steht die Regel in `start.css`,
|
|
und die liegt auf allen fünfunddreißig.
|
|
|
|
---------------------------------------------------------------------
|
|
WAS HIER GEMESSEN WIRD — und warum nicht der Quelltext
|
|
|
|
Ob eine CSS-Regel WIRKT, steht nicht in der Datei. `--akzent` könnte
|
|
von einer späteren Regel überschrieben werden, `data-ton` könnte
|
|
fehlen, das Haus könnte etwas anderes sagen. Deshalb ein echter
|
|
Browser und `getComputedStyle` — die Farbe, die auf dem Schirm
|
|
ankommt.
|
|
|
|
ZWEI SEITEN MIT VERSCHIEDENEN TÖNEN, und beide Richtungen:
|
|
* auf jeder kommt eine Farbe an
|
|
* und es ist nicht dieselbe
|
|
Ohne den zweiten Teil wäre eine Seite, die überall dieselbe Farbe
|
|
zeigt, ebenfalls grün — und genau das war der Zustand vorher.
|
|
===================================================================== */
|
|
import { mkdtempSync, rmSync, readFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { request as httpAnfrage } from "node:http";
|
|
import { execFileSync } from "node:child_process";
|
|
import { createServer as httpsServer } from "node:https";
|
|
import { notbremse } from "./helfer-notbremse.mjs";
|
|
import { eigenerPort } from "./helfer-port.mjs";
|
|
|
|
const CREW = "crew.dogfather-universe.com";
|
|
const PORT = await eigenerPort(import.meta, "pruef-kopfleiste-farbe");
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-kopffarbe-"));
|
|
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
|
process.env.PORT = `${PORT}`;
|
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
|
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
|
|
|
const express = (await import("express")).default;
|
|
const ec = express.response.cookie;
|
|
express.response.cookie = function (n, w, o) { return ec.call(this, n, w, { ...(o || {}), secure: false }); };
|
|
await import("./index.js");
|
|
notbremse(180_000, "pruef-kopfleiste-farbe");
|
|
await new Promise((r) => setTimeout(r, 900));
|
|
|
|
let fehler = 0, geprueft = 0, offen = 0;
|
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
|
const unklar = (t) => { offen++; console.log(" -- " + t); };
|
|
|
|
/* ---------- Ein Mensch ------------------------------------------------- */
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
const jetzt = new Date().toISOString();
|
|
const k = randomBytes(32).toString("hex");
|
|
d.prepare("INSERT INTO einstellungen (schluessel,wert,geaendert,von) VALUES (?,?,?,NULL)"
|
|
+ " ON CONFLICT(schluessel) DO NOTHING").run("code_kennung_schluessel", k, jetzt);
|
|
{
|
|
const salt = randomBytes(16).toString("hex");
|
|
const hash = scryptSync("CODE-DOGI-0001", salt, 64,
|
|
{ N: 32768, r: 8, p: 1, maxmem: 96 * 1024 * 1024 }).toString("hex");
|
|
d.prepare("INSERT INTO personen (name,rolle,code_hash,code_salt,code_n,code_kennung,aktiv,erstellt)"
|
|
+ " VALUES (?,?,?,?,?,?,1,?)")
|
|
.run("Filipe", "admin", hash, salt, 32768,
|
|
createHmac("sha256", k).update("CODE-DOGI-0001").digest("hex"), jetzt);
|
|
}
|
|
d.close();
|
|
|
|
/* ---------- Der Vorbau ------------------------------------------------- */
|
|
const schl = join(ordner, "p.key"), zert = join(ordner, "p.crt");
|
|
execFileSync("openssl", ["req", "-x509", "-newkey", "rsa:2048", "-nodes", "-keyout", schl,
|
|
"-out", zert, "-days", "2", "-subj", `/CN=${CREW}`, "-addext", `subjectAltName=DNS:${CREW}`],
|
|
{ stdio: "ignore" });
|
|
const HP = PORT + 1;
|
|
const vorbau = httpsServer({ key: readFileSync(schl), cert: readFileSync(zert) }, (von, nach) => {
|
|
const w = httpAnfrage({ host: "127.0.0.1", port: PORT, path: von.url, method: von.method,
|
|
headers: { ...von.headers, host: `${CREW}:${HP}` } },
|
|
(a) => { nach.writeHead(a.statusCode || 502, a.headers); a.pipe(nach); });
|
|
von.pipe(w);
|
|
});
|
|
await new Promise((r) => vorbau.listen(HP, "127.0.0.1", r));
|
|
const SEITE = `https://${CREW}:${HP}`;
|
|
|
|
const pw = await import(
|
|
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
|
const browser = await pw.chromium.launch({
|
|
args: [`--host-resolver-rules=MAP ${CREW} 127.0.0.1`, "--ignore-certificate-errors"] });
|
|
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
const seite = await ctx.newPage();
|
|
const meckern = [];
|
|
seite.on("pageerror", (f) => meckern.push(String(f).slice(0, 140)));
|
|
|
|
console.log("=== Jede Seite traegt ihre Farbe ===");
|
|
console.log("");
|
|
|
|
await seite.goto(`${SEITE}/workspace/`, { waitUntil: "networkidle" });
|
|
await seite.click('.rolle[data-rolle="admin"]');
|
|
await seite.fill("#code", "CODE-DOGI-0001");
|
|
await seite.click("button[type=submit]");
|
|
await seite.waitForTimeout(900);
|
|
|
|
/* WELCHE SEITEN GEMESSEN WERDEN, STEHT NICHT HIER.
|
|
-----------------------------------------------------------------
|
|
Die Kacheln der Startseite sagen, welche Seiten es gibt und welchen
|
|
Ton jede hat. Eine Liste in dieser Datei waere die, die beim
|
|
naechsten Umbau eine Seite nennt, die es nicht mehr gibt -- oder,
|
|
schlimmer, die neue nicht kennt. */
|
|
await seite.goto(`${SEITE}/workspace/start.html`, { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(1800);
|
|
/* DIE KACHEL IST DAS <li>, DER VERWEIS LIEGT DARIN.
|
|
Beim ersten Anlauf stand hier `.kachel[href]` -- das trifft nichts,
|
|
und die Pruefung meldete "0 Kacheln". Das war richtig gemeldet und
|
|
trotzdem ein Fehler von mir: Die Struktur steht in start.js
|
|
(`li.kachel` mit `a.kachel__link` darin), nachgesehen statt
|
|
geraten. */
|
|
const kacheln = await seite.evaluate(() =>
|
|
[...document.querySelectorAll('li.kachel[data-ton]')]
|
|
.map((li) => ({
|
|
ziel: li.querySelector('a')?.getAttribute("href") || null,
|
|
ton: li.dataset.ton || null,
|
|
}))
|
|
.filter((x) => x.ziel && /^[a-z-]+\.html$/.test(x.ziel) && x.ton !== null));
|
|
|
|
ok(kacheln.length >= 4,
|
|
`${kacheln.length} Kacheln mit eigenem Ton auf der Startseite gefunden`);
|
|
|
|
/* Zwei mit VERSCHIEDENEN Toenen -- ohne Unterschied liesse sich nicht
|
|
zeigen, dass die Farbe der Seite folgt und nicht der Leiste. */
|
|
const nachTon = new Map();
|
|
for (const x of kacheln) if (!nachTon.has(x.ton)) nachTon.set(x.ton, x);
|
|
const auswahl = [...nachTon.values()].slice(0, 3);
|
|
ok(auswahl.length >= 2,
|
|
`davon ${auswahl.length} mit verschiedenen Toenen (${auswahl.map((x) => x.ton).join(", ")})`);
|
|
|
|
const gemessen = [];
|
|
for (const x of auswahl) {
|
|
await seite.goto(`${SEITE}/workspace/${x.ziel}`, { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(1500);
|
|
const w = await seite.evaluate(() => {
|
|
const koerper = getComputedStyle(document.body);
|
|
const leiste = document.querySelector(".kopfleiste");
|
|
return {
|
|
adresse: location.pathname.split("/").pop(),
|
|
ton: document.body.dataset.ton || null,
|
|
akzent: koerper.getPropertyValue("--akzent").trim(),
|
|
tonFarbe: koerper.getPropertyValue("--ton").trim(),
|
|
leisteDa: !!leiste,
|
|
/* Die Kante unter der Leiste -- gemessen an dem, was wirklich
|
|
gezeichnet wird, nicht an der Regel im Quelltext. */
|
|
kante: leiste ? getComputedStyle(leiste, "::before").backgroundImage : "",
|
|
};
|
|
});
|
|
gemessen.push(w);
|
|
}
|
|
|
|
console.log("");
|
|
console.log("--- Was auf den Seiten ankommt ---");
|
|
for (const w of gemessen) {
|
|
console.log(` ${w.adresse.padEnd(18)} Ton ${String(w.ton).padStart(2)}`
|
|
+ ` --akzent ${w.akzent}`);
|
|
}
|
|
|
|
ok(gemessen.length >= 2 && gemessen.every((w) => w.ton !== null),
|
|
`auf allen ${gemessen.length} Seiten steht ein Ton am Koerper`);
|
|
/* DIE ZAHL GEHOERT IN DIE BEDINGUNG. `every()` auf einem leeren Feld
|
|
liefert `true` -- beim ersten Lauf waren drei Zeilen hier gruen,
|
|
ohne dass eine einzige Seite gemessen worden war. Genau der Fehler,
|
|
vor dem die Hausregel warnt, und er ist mir trotzdem passiert. */
|
|
ok(gemessen.length >= 2 && gemessen.every((w) => w.akzent && w.akzent !== "none"),
|
|
`auf allen ${gemessen.length} kommt daraus eine Akzentfarbe an`);
|
|
|
|
/* DER ENTSCHEIDENDE VERGLEICH. Vorher war --akzent auf 32 von 35
|
|
Seiten die feste Hausfarbe -- also ueberall dieselbe. */
|
|
const verschieden = new Set(gemessen.map((w) => w.akzent)).size;
|
|
ok(gemessen.length >= 2 && verschieden === gemessen.length,
|
|
`und sie ist auf jeder Seite eine andere (${verschieden} von ${gemessen.length})`);
|
|
|
|
ok(gemessen.length >= 2 && gemessen.every((w) => w.leisteDa),
|
|
`die Kopfleiste steht auf allen ${gemessen.length} gemessenen Seiten`);
|
|
ok(gemessen.length >= 2 && gemessen.every((w) => /gradient/i.test(w.kante || "")),
|
|
"und traegt unten eine Kante in der Farbe der Seite");
|
|
|
|
/* GEGENPROBE: Eine Seite OHNE Ton darf die Hausfarbe behalten. Eine
|
|
Regel, die auch dort zuschlaegt, wuerde eine Farbe erfinden. */
|
|
console.log("");
|
|
console.log("--- Gegenprobe ---");
|
|
{
|
|
const ohne = await seite.evaluate(() => {
|
|
const vorher = document.body.dataset.ton;
|
|
delete document.body.dataset.ton;
|
|
const a = getComputedStyle(document.body).getPropertyValue("--akzent").trim();
|
|
if (vorher !== undefined) document.body.dataset.ton = vorher;
|
|
return a;
|
|
});
|
|
const mit = gemessen.at(-1)?.akzent || null;
|
|
ok(!!ohne && !!mit && ohne !== mit,
|
|
`ohne Ton gilt wieder die Hausfarbe (${ohne} statt ${mit})`);
|
|
}
|
|
|
|
ok(meckern.length === 0, `der Browser meldet nichts (${meckern[0] || "still"})`);
|
|
|
|
/* ---------- Ende ------------------------------------------------------ */
|
|
await browser.close();
|
|
await new Promise((r) => vorbau.close(r));
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Windows haelt manchmal */ }
|
|
|
|
console.log("");
|
|
console.log(`${fehler === 0 ? "BESTANDEN" : "FEHLGESCHLAGEN"}`
|
|
+ ` -- ${geprueft} geprueft, ${fehler} Fehler, ${offen} nicht nachsehbar`);
|
|
process.exit(fehler === 0 ? 0 : 1);
|