Vier Wuensche auf einmal, alle auf der Startseite und der Kopfleiste. BEGRUESSUNG. Dort stand "Angemeldet" ueber dem Namen -- eine Zeile, die nichts hinzufuegt, weil oben rechts ohnehin Name und Rolle stehen. Jetzt beantwortet der Kopf die drei Fragen, die man beim Reinkommen hat: WANN (Wochentag, Datum, Kalenderwoche nach ISO 8601, selbst gerechnet), WER (Plakette mit dem Anfangsbuchstaben in der Farbe der Rolle -- der einzige runde Koerper der Seite, dadurch sofort als Person lesbar) und WIE es steht (ein Satz aus denselben Hinweisen, die darunter stehen -- keine zweite Zaehlung). An Tagen mit Charakter kommt ein Wort dazu: Neue Woche, Endspurt, Wochenende. Der Gruss ist feiner gestuft und der Name eigenstaendig ausgezeichnet: das Grusswort leise, der Name kraeftig. SCHRIFTZUG oben links. Vorher alles gleich laut, der Trenner ein Satzzeichen wie jedes andere. Jetzt hat er Rang: der Name vorn und kraeftig, der Ort dahinter und leiser, dazwischen eine kleine Raute in der Hausfarbe. Aufgeteilt zentral in kopf.js statt in sechzehn HTML- Dateien, und nur die Textknoten -- Verweise bleiben unberuehrt. HINTERGRUND heller, wie gewuenscht: Der Schleier liegt bei .86/.58/.44 statt .95/.72/.60, dazu zwei sehr weiche Farbschimmer, die dem Bild das reine Schwarz nehmen. Der Kontrast wurde danach an echten Bildpunkten nachgemessen und haelt ueberall (schlechtester Wert 4,78:1). "WISSEN" statt "Team & System" -- aber nur fuer Creator. Von der Gruppe bleibt fuer ihn genau eine Kachel uebrig; eine Ueberschrift, die Team und System verspricht und nur die Bibliothek zeigt, verspricht etwas Falsches. DREI FUNDE DURCH DIE PRUEFUNGEN: 1. Die Kontrastmessung war kaputt, seit Text neben Text steht. Sie tastete stur 4 px rechts vom Text ab -- und traf dort den NACHBARTEXT statt den Untergrund (1,10:1 gemeldet, ohne dass etwas schlecht lesbar war). Jetzt werden sechs Stellen rings um den Text angeboten und jede zuerst gefragt, ob dort wirklich Untergrund liegt. Strenger, nicht lockerer. Texte ohne freie Stelle werden GEZAEHLT und ausgegeben, nicht still uebersprungen. 2. bereich.html hatte als einzige Seite kein span.marke__text. Damit griffen dort weder die Ueberlauf-Kuerzung noch der neue Rang. Stand seit Monaten so, aufgefallen erst, als die Seitenpruefung die Marke auf allen dreizehn Seiten nachgesehen hat. 3. Die Datumspruefung haette im Maerz stillschweigend versagt: \w kennt ohne Unicode-Schalter kein "ae". Ein Fehler, der sieben Monate wartet. Co-Authored-By: Claude Opus 5 <[email protected]>
124 lines
6.4 KiB
JavaScript
124 lines
6.4 KiB
JavaScript
/* Kurzpruefung: Steht die Kopfleiste auf dem Handy ueber -- und zwar
|
|
auf ALLEN Seiten oder nur auf der neuen? Beantwortet die Frage, ob
|
|
ich einen eigenen Fehler gebaut oder einen alten gefunden habe. */
|
|
import { mkdtempSync, rmSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-kopf-"));
|
|
process.env.WORKSPACE_DB = join(ordner, "pruef.sqlite");
|
|
process.env.PORT = "4178";
|
|
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
|
process.env.SITE_ACCESS_CODE_DOGI = "DOGI-TEST-1";
|
|
process.env.SITE_ACCESS_CODE_VANVAN = "VANVAN-TEST-1";
|
|
process.env.SITE_PUBLIC_LAUNCH_AT = "2020-01-01T00:00:00+01:00";
|
|
|
|
const express = (await import("express")).default;
|
|
const echteCookie = express.response.cookie;
|
|
express.response.cookie = function (n, w, o) {
|
|
return echteCookie.call(this, n, w, { ...(o || {}), secure: false });
|
|
};
|
|
await import("./index.js");
|
|
await new Promise((r) => setTimeout(r, 700));
|
|
const BASIS = "http://127.0.0.1:4178";
|
|
setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 150_000).unref?.();
|
|
|
|
await fetch(BASIS + "/workspace/api/anmelden", {
|
|
method: "POST", headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ rolle: "admin", code: "x" }),
|
|
}).catch(() => {});
|
|
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes } = await import("node:crypto");
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
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, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)")
|
|
.run("Test-DogFather", "admin", hash, salt, 32768, new Date().toISOString());
|
|
d.close();
|
|
|
|
const { chromium } = await import(
|
|
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
|
const browser = await chromium.launch();
|
|
|
|
/* Alle Seiten, die eine angemeldete Person zu sehen bekommt. Geprueft
|
|
wird auf beiden Groessen, weil die geteilten Klassen (.kopf-zeile,
|
|
.inhalt--breit, .leise) am 31.08.2026 von aufgaben.css nach start.css
|
|
gewandert sind -- faellt dabei etwas herunter, sieht man es hier. */
|
|
const SEITEN = ["start.html", "uebersicht.html", "aufgaben.html", "kalender.html",
|
|
"personen.html", "profil.html", "wissen.html", "dateien.html",
|
|
"calls.html", "startcheck.html", "report.html", "scouting.html",
|
|
"automation.html", "content.html", "bereich.html?b=live"];
|
|
|
|
let fehler = 0;
|
|
for (const [breite, hoehe, wie] of [[1440, 1000, "Computer"], [390, 844, "Handy"]]) {
|
|
console.log(`\n=== ${wie} (${breite}px)`);
|
|
const kontext = await browser.newContext({ viewport: { width: breite, height: hoehe } });
|
|
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));
|
|
|
|
await seite.goto(BASIS + "/workspace/", { waitUntil: "domcontentloaded" });
|
|
await seite.click('.rolle[data-rolle="admin"]');
|
|
await seite.fill("#code", "CODE-DOGI-0001");
|
|
await seite.click("button[type=submit]");
|
|
await seite.waitForURL("**/start.html", { timeout: 15000 });
|
|
|
|
for (const s of SEITEN) {
|
|
konsole.length = 0;
|
|
await seite.goto(BASIS + "/workspace/" + s, { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(300);
|
|
const r = await seite.evaluate(() => ({
|
|
ueber: document.documentElement.scrollWidth - document.documentElement.clientWidth,
|
|
schuld: [...document.querySelectorAll("body *")]
|
|
.filter((e) => e.getBoundingClientRect().right > window.innerWidth + 1)
|
|
.map((e) => (e.tagName + "." + e.className).slice(0, 40)).slice(0, 4),
|
|
/* Die verschobenen Klassen muessen weiterhin greifen. .inhalt--breit
|
|
setzt 1240px -- kommt nur 880px an, ist die Regel verloren. */
|
|
breit: document.querySelector(".inhalt--breit")
|
|
? getComputedStyle(document.querySelector(".inhalt--breit")).maxWidth : null,
|
|
kopfzeile: document.querySelector(".kopf-zeile")
|
|
? getComputedStyle(document.querySelector(".kopf-zeile")).display : null,
|
|
abmelden: (() => {
|
|
const a = document.querySelector(".abmelden")?.getBoundingClientRect();
|
|
return a ? a.right <= window.innerWidth && a.left >= 0 : null;
|
|
})(),
|
|
/* Der Schriftzug oben links wird von kopf.js in Rang aufgeteilt.
|
|
Auf den Unterseiten ist der vordere Teil ein VERWEIS zurück zur
|
|
Startseite -- der muss die Aufteilung überleben, sonst ist der
|
|
Rückweg weg. Genau das wäre beim Zerlegen von Textknoten leicht
|
|
zu übersehen. */
|
|
markeHaupt: document.querySelector(".kopfleiste .marke__haupt")?.textContent
|
|
.replace(/ /g, " ") || null,
|
|
markeLeise: document.querySelector(".kopfleiste .marke__leise")?.textContent
|
|
.replace(/ /g, " ") || null,
|
|
markeRaute: document.querySelectorAll(".kopfleiste .marke__raute").length,
|
|
/* Ist der vordere Teil auf einer Unterseite noch ein klickbarer
|
|
Verweis auf die Startseite? */
|
|
rueckweg: document.querySelector(".kopfleiste .marke__haupt")?.tagName === "A"
|
|
? document.querySelector(".kopfleiste .marke__haupt").getAttribute("href") : null,
|
|
}));
|
|
const istStart = s === "start.html";
|
|
const markeGut = r.markeHaupt && r.markeLeise && r.markeRaute === 1
|
|
&& (istStart || r.rueckweg === "start.html");
|
|
const gut = r.ueber <= 0 && konsole.length === 0 && r.abmelden !== false
|
|
&& markeGut
|
|
&& (r.breit === null || r.breit === "1240px")
|
|
&& (r.kopfzeile === null || r.kopfzeile === "flex");
|
|
if (!gut) fehler++;
|
|
console.log(` ${gut ? "ok " : "FEHL"} ${String(s).padEnd(20)}`
|
|
+ `über=${String(r.ueber).padStart(3)}px abmelden=${r.abmelden}`
|
|
+ ` marke=${r.markeHaupt ? `"${r.markeHaupt}"◆"${r.markeLeise}"` : "FEHLT"}`
|
|
+ `${istStart ? "" : ` zurück=${r.rueckweg || "FEHLT"}`}`
|
|
+ ` breit=${r.breit} kopfzeile=${r.kopfzeile}`
|
|
+ (konsole.length ? " KONSOLE: " + konsole.join(" | ") : "")
|
|
+ (r.schuld.length ? " -> " + r.schuld.join(" | ") : ""));
|
|
}
|
|
await kontext.close();
|
|
}
|
|
await browser.close();
|
|
console.log(`\n${fehler === 0 ? "Alles in Ordnung." : fehler + " Seite(n) fehlerhaft."}`);
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch {}
|
|
process.exit(fehler === 0 ? 0 : 1);
|