Files
dogfather-universe/server/pruef-kopfleiste-farbe.mjs
T
DogFatherGitandClaude Opus 5 6be24e204f B3: Jede Seite traegt ihre Farbe -- auch ganz oben in der Leiste
"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]>
2026-09-22 23:52:27 +02:00

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);