"ich will dass die linke hand auch ueberall zu sehen ist. reihenfolge
immer. rechte hand, dan linke hand und dan modis. perfektionier das
bitte."
DER BEFUND: In workspace-teamlage.js stand
WHERE rolle IN ('hand','modi')
Eine abgeschriebene Rollenliste, und die linke Hand kam darin nicht
vor. Sie fehlte damit nicht nur als KARTE: Der Eingang derselben
Seite sammelt die Rueckmeldungen aus genau dieser Liste. Was sie
meldet, waere nirgends angekommen -- dasselbe, was am 11.09. schon
der rechten Hand passiert ist, an derselben Zeile.
Dieselbe Liste ein zweites Mal in workspace-bereiche.js: Ein Eintrag
im Entwicklungsbereich liess sich ihr nicht zuordnen, mit der Meldung
"Diese Person gehoert nicht zum Team." -- ueber jemanden, der sehr
wohl dazugehoert.
Beide lesen jetzt TEAM_DOGI_ROLLEN, die eine Liste des Hauses
({hand, linke, modi}). Kaeme morgen eine weitere Team-Rolle, waere
sie von selbst dabei.
DIE REIHENFOLGE STIMMTE SCHON. ROLLEN_REIHE nennt hand vor linke vor
modi, ROLLEN_SORTIERUNG leitet daraus das CASE ab. Sie war nur nie zu
sehen, weil eine der drei fehlte.
WARUM DIE PRUEFUNG DAS NICHT GEFUNDEN HAT -- und was daran mein
Fehler war: pruef-teamlage-karten legte eine rechte Hand und fuenf
Modis an, aber KEINE linke Hand. Eine Rolle, die in den Testdaten
nicht vorkommt, kann auch nicht vermisst werden. Sie ist jetzt dabei,
und drei neue Zeilen messen die Reihenfolge gegen ROLLEN_REIHE statt
gegen drei hier hingeschriebene Namen:
hand → linke → modi
"Rechte Hand 1 · Linke Hand 1 · Modi 5"
25 Pruefungen, 0 Fehler.
NEBENBEI: EIN EIGENER MESSFEHLER, DEN ICH RICHTIGSTELLEN MUSS
Auf dem Weg dorthin habe ich behauptet, der linken Hand fehlten
FUENFZEHN Seiten in der Rechtetafel. Das war falsch gemessen: Ich
habe den QUELLTEXT von rechte.js gelesen statt das Verhalten. Die
Tafel leitet seit dem 21.09. ab -- „die linke Hand bekommt die Seiten
der rechten", mit fuenf ausdruecklich begruendeten Ausnahmen
(Personen anlegen, Talente, Bewerbungen, vertraulicher Meldeweg,
Rechtetafel). Ueber `darfSeite` gemessen fehlen ihr genau diese fuenf,
und das ist so gewollt.
Derselbe Fehler wie heute Nacht bei pruef-haus-seiten, wo ich zwei
Listen mit einem regulaeren Ausdruck aus dem Quelltext lesen wollte.
Eine Regel steht im Code; ob sie WIRKT, sagt nur eine Messung.
Co-Authored-By: Claude Opus 5 <[email protected]>
421 lines
20 KiB
JavaScript
421 lines
20 KiB
JavaScript
/* =====================================================================
|
|
DIE PERSONENKARTEN DER TEAM-LAGE — im echten Browser
|
|
=====================================================================
|
|
|
|
ENTSTANDEN AUS ZWEI MELDUNGEN AM 22.09.2026.
|
|
|
|
(1) „wenn ich auf diese sachen drücke werd ich auf die startseite
|
|
geschickt, scheck das bitte ab."
|
|
(2) „was verstehst du nicht wenn ich sage die sollen viel krasser
|
|
geiler und übersichtlicher sein. und auch den rollen
|
|
entsprechend reihenfolge. nicht einfach so sondern die rolle
|
|
soll die reihenfolge ausmachen."
|
|
|
|
---------------------------------------------------------------------
|
|
WARUM DAS IM BROWSER GEMESSEN WIRD UND NICHT AM SERVER
|
|
|
|
Beide Befunde waren am Server NICHT SICHTBAR.
|
|
|
|
Zu (1): Der Server antwortete völlig korrekt mit 404 — die rechte
|
|
Hand hat keine Stufe, also gibt es dort nichts zu setzen. Den
|
|
Schaden richtete der Browser an: Er machte aus jedem 404 einen
|
|
Rauswurf. `pruef-team-stufen.mjs` misst den 404 und ist grün; sie
|
|
kann den Sprung gar nicht sehen.
|
|
|
|
Zu (2): Die Reihenfolge war am Server seit Tagen richtig
|
|
(ROLLEN_SORTIERUNG in der Abfrage) — und Filipe hat sie trotzdem
|
|
nicht wiedererkannt, weil auf der Karte nur die STUFE stand, nie die
|
|
Rolle. Eine Ordnung, die man nicht sehen kann, sieht aus wie Zufall.
|
|
Auch das ist am Server nicht messbar: Die Antwort war richtig.
|
|
|
|
Beides zusammen ist dieselbe Lehre: Wo die Antwort stimmt und das
|
|
Ergebnis trotzdem falsch ankommt, muss dort gemessen werden, wo der
|
|
Mensch steht.
|
|
|
|
---------------------------------------------------------------------
|
|
DIE GEGENPROBE STECKT IM ZWEITEN DRUCK
|
|
|
|
Ein Druck auf einen Knopf, der schon gewählt ist, darf NICHTS tun —
|
|
kein Abruf, keine Änderung. Ohne diesen zweiten Druck wäre nicht
|
|
bewiesen, dass die Messung „hat sich etwas geändert" überhaupt einen
|
|
Unterschied bemerkt: Eine Seite, die auf jeden Druck alles neu malt,
|
|
bestünde den ersten Teil genauso.
|
|
===================================================================== */
|
|
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-teamlage-karten");
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-tlk-"));
|
|
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(240_000, "pruef-teamlage-karten");
|
|
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); };
|
|
|
|
/* ---------- Leute anlegen --------------------------------------------- */
|
|
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 schluessel = randomBytes(32).toString("hex");
|
|
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
|
|
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
|
|
.run("code_kennung_schluessel", schluessel, jetzt);
|
|
|
|
function anlegen(name, rolle, code) {
|
|
const salt = randomBytes(16).toString("hex");
|
|
const hash = scryptSync(code, 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(name, rolle, hash, salt, 32768,
|
|
createHmac("sha256", schluessel).update(code).digest("hex"), jetzt);
|
|
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
|
}
|
|
|
|
anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
|
anlegen("Rieke", "hand", "CODE-HAND-0001");
|
|
/* DIE LINKE HAND GEHOERT IN DIESE PROBE (23.09.2026).
|
|
|
|
Sie fehlte hier -- und deshalb war die Pruefung gruen, waehrend die
|
|
Seite sie gar nicht anzeigte: `WHERE rolle IN ('hand','modi')` in
|
|
workspace-teamlage.js. Eine Rolle, die in den Testdaten nicht
|
|
vorkommt, kann auch nicht vermisst werden.
|
|
|
|
Filipe, mit dem Bildschirmfoto: „ich will dass die linke hand auch
|
|
ueberall zu sehen ist. reihenfolge immer. rechte hand, dan linke
|
|
hand und dan modis." */
|
|
anlegen("Lino", "linke", "CODE-LINK-0001");
|
|
|
|
/* DIE STUFEN KOMMEN AUS DEM SERVERMODUL, nicht aus einer Liste hier.
|
|
Genau so bleibt die Pruefung gueltig, wenn morgen eine sechste
|
|
dazukommt -- und sie prueft dann auch die sechste mit. */
|
|
const { STUFEN, STUFEN_NAME } = await import("./workspace-teamlage.js");
|
|
const modiNamen = ["Frida", "Nele", "Ghost", "Maja", "Liv", "Tara", "Juno", "Pia"];
|
|
const modis = STUFEN.map((stufe, i) => {
|
|
const name = modiNamen[i] || `Modi${i + 1}`;
|
|
const id = anlegen(name, "modi", `CODE-MODI-00${10 + i}`);
|
|
d.prepare("UPDATE personen SET stufe = ? WHERE id = ?").run(stufe, id);
|
|
return { id, name, stufe };
|
|
});
|
|
d.close();
|
|
|
|
/* ---------- Der Vorbau, damit der Host-Kopf ankommt ------------------- */
|
|
/* `fetch` in Node verwirft den Host-Kopf, und Chromium hebt crew.… auf
|
|
https. Ein selbstsigniertes Zertifikat plus MAP-Regel ist der Weg,
|
|
den auch die anderen Browserpruefungen dieses Hauses gehen. */
|
|
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}`;
|
|
|
|
/* ---------- Browser --------------------------------------------------- */
|
|
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: 1420, height: 1200 } });
|
|
const seite = await ctx.newPage();
|
|
const meckern = [];
|
|
const abrufe = [];
|
|
seite.on("pageerror", (f) => meckern.push("Absturz: " + String(f).slice(0, 160)));
|
|
seite.on("console", (m) => {
|
|
if (m.type() === "error" && !/favicon|ERR_CERT|net::ERR/i.test(m.text())) {
|
|
meckern.push(m.text().slice(0, 160));
|
|
}
|
|
});
|
|
seite.on("response", (r) => {
|
|
if (/\/workspace\/api\/teamlage\//.test(r.url())) {
|
|
abrufe.push(`${r.status()} ${r.url().split("/workspace")[1]}`);
|
|
}
|
|
});
|
|
|
|
console.log("=== Die Personenkarten der Team-Lage ===");
|
|
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);
|
|
await seite.goto(`${SEITE}/workspace/teamlage.html`, { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(1600);
|
|
|
|
const angekommen = new URL(seite.url()).pathname;
|
|
if (angekommen !== "/workspace/teamlage.html") {
|
|
unklar(`die Seite hat uns nach ${angekommen} geschickt —`
|
|
+ " ab hier laesst sich nichts messen.");
|
|
} else {
|
|
/* ===================================================================
|
|
1. DIE KARTEN UND IHRE REIHENFOLGE
|
|
=================================================================== */
|
|
console.log("--- Die Reihenfolge macht die Rolle ---");
|
|
|
|
const lage = await seite.evaluate(() => {
|
|
const karten = [...document.querySelectorAll(".t-leute .tperson")];
|
|
return {
|
|
karten: karten.map((k) => ({
|
|
name: k.querySelector(".tperson__name")?.textContent.trim() || "?",
|
|
rolle: k.dataset.rolle || "",
|
|
stufe: k.dataset.stufe || "",
|
|
rollenwort: k.querySelector(".tmerkmal--rolle")?.textContent.trim() || "",
|
|
stufenwort: k.querySelector(".tmerkmal--stufe")?.textContent.trim() || "",
|
|
knoepfe: k.querySelectorAll(".tstufen__knopf").length,
|
|
})),
|
|
gruppen: [...document.querySelectorAll(".t-gruppe")].map((g) => ({
|
|
rolle: g.dataset.rolle || "",
|
|
wort: g.querySelector(".t-gruppe__wort")?.textContent.trim() || "",
|
|
zahl: Number(g.querySelector(".t-gruppe__zahl")?.textContent.trim()),
|
|
})),
|
|
/* Wie viele Karten stehen in demselben Block wie die Ueberschrift
|
|
davor? Gemessen am Baum, nicht gezaehlt aus den Daten. */
|
|
bloecke: [...document.querySelectorAll(".t-leute")]
|
|
.map((l) => l.querySelectorAll(".tperson").length),
|
|
};
|
|
});
|
|
|
|
ok(lage.karten.length === 2 + modis.length,
|
|
`${lage.karten.length} Karten stehen da (erwartet ${2 + modis.length})`);
|
|
|
|
|
|
/* DIE ROLLE STEHT AUF JEDER KARTE. Ohne das Wort ist die Sortierung
|
|
unsichtbar -- genau der Befund vom 22.09. */
|
|
const ohneWort = lage.karten.filter((k) => !k.rollenwort);
|
|
ok(lage.karten.length > 0 && ohneWort.length === 0,
|
|
`jede Karte nennt ihre Rolle im Klartext`
|
|
+ ` (${ohneWort.map((k) => k.name).join(", ") || "keine Luecke"})`);
|
|
|
|
/* GRUPPIERT, NICHT NUR SORTIERT: Keine Rolle darf zweimal auftauchen,
|
|
nachdem eine andere dazwischenstand. Abgeleitet aus dem, was da
|
|
steht -- ohne eine Erwartung, welche Rollen es gibt. */
|
|
const folge = lage.karten.map((k) => k.rolle);
|
|
const bloecke = folge.filter((r, i) => r !== folge[i - 1]);
|
|
ok(folge.length > 0 && new Set(bloecke).size === bloecke.length,
|
|
`die Rollen stehen in geschlossenen Bloecken (${bloecke.join(" → ")})`);
|
|
|
|
/* Und jede Gruppe hat ihre Ueberschrift mit der richtigen Zahl. */
|
|
ok(lage.gruppen.length === bloecke.length,
|
|
`${lage.gruppen.length} Ueberschriften fuer ${bloecke.length} Bloecke`);
|
|
const zahlenStimmen = lage.gruppen.length > 0
|
|
&& lage.gruppen.every((g, i) => g.zahl === lage.bloecke[i])
|
|
&& lage.gruppen.every((g, i) => g.rolle === bloecke[i]);
|
|
ok(zahlenStimmen,
|
|
`jede Ueberschrift nennt ihre Rolle und ihre Anzahl richtig`
|
|
+ ` (${lage.gruppen.map((g) => `${g.wort} ${g.zahl}`).join(" · ")})`);
|
|
|
|
/* DIE REIHENFOLGE IST DIE AUSSAGE, nicht nur die Gruppierung.
|
|
Geprueft gegen ROLLEN_REIHE -- die eine Liste des Hauses -- und
|
|
nicht gegen drei hier hingeschriebene Namen. Wer die Reihenfolge
|
|
dort aendert, aendert sie hier mit. */
|
|
{
|
|
const { ROLLEN_REIHE } = await import("./workspace.js");
|
|
const erwartet = ROLLEN_REIHE.filter((r) => bloecke.includes(r));
|
|
ok(bloecke.length === 3,
|
|
`drei Rollen stehen auf der Seite (${bloecke.join(" → ")})`);
|
|
ok(JSON.stringify(bloecke) === JSON.stringify(erwartet),
|
|
`und zwar in der Reihenfolge des Hauses: ${erwartet.join(" → ")}`);
|
|
ok(bloecke[0] === "hand" && bloecke[1] === "linke",
|
|
"rechte Hand zuerst, dann die linke -- genau so gewuenscht");
|
|
}
|
|
|
|
/* ===================================================================
|
|
2. DIE STUFE STEHT NUR DA, WO ES EINE GIBT
|
|
=================================================================== */
|
|
console.log("");
|
|
console.log("--- Wer eine Stufe hat, und wer nicht ---");
|
|
|
|
const mitStufe = lage.karten.filter((k) => k.rolle === "modi");
|
|
const ohneStufe = lage.karten.filter((k) => k.rolle !== "modi");
|
|
ok(mitStufe.length === modis.length && ohneStufe.length >= 1,
|
|
`${mitStufe.length} Modis und ${ohneStufe.length} ohne Stufe im Bild`);
|
|
ok(mitStufe.length > 0 && mitStufe.every((k) => k.knoepfe === STUFEN.length),
|
|
`jeder Modi hat alle ${STUFEN.length} Stufenknoepfe`
|
|
+ ` (${[...new Set(mitStufe.map((k) => k.knoepfe))].join("/")})`);
|
|
/* DER EIGENTLICHE BEFUND VOM 22.09.: Hier standen die Knoepfe auch an
|
|
der rechten Hand. Der Server lehnte mit 404 ab, und der 404 warf
|
|
von der Seite. */
|
|
ok(ohneStufe.length > 0 && ohneStufe.every((k) => k.knoepfe === 0),
|
|
`wer keine Stufe hat, hat auch keine Knoepfe`
|
|
+ ` (${ohneStufe.map((k) => `${k.name}:${k.knoepfe}`).join(", ")})`);
|
|
ok(ohneStufe.length > 0 && ohneStufe.every((k) => !k.stufenwort),
|
|
`und auch kein Stufen-Schild — "Probe" an der rechten Hand waere`
|
|
+ ` eine Auskunft, die niemand aendern kann`
|
|
+ ` (${ohneStufe.map((k) => k.stufenwort || "leer").join(", ")})`);
|
|
|
|
/* Jede Stufe traegt ihren lesbaren Namen. */
|
|
const falschBeschriftet = mitStufe.filter(
|
|
(k) => k.stufenwort !== (STUFEN_NAME[k.stufe] || ""));
|
|
ok(mitStufe.length > 0 && falschBeschriftet.length === 0,
|
|
`jede Stufe steht mit ihrem Namen auf der Karte`
|
|
+ ` (${mitStufe.map((k) => k.stufenwort).join(", ")})`);
|
|
|
|
/* ===================================================================
|
|
3. EIN DRUCK AUF EINEN STUFENKNOPF
|
|
=================================================================== */
|
|
console.log("");
|
|
console.log("--- Was ein Druck auf die Stufe tut ---");
|
|
|
|
/* Die erste Karte, die Knoepfe hat, und darin ein Knopf, der noch
|
|
nicht gewaehlt ist. */
|
|
const ziel = await seite.evaluate(() => {
|
|
const k = [...document.querySelectorAll(".t-leute .tperson")]
|
|
.find((x) => x.querySelectorAll(".tstufen__knopf").length > 0);
|
|
if (!k) return null;
|
|
const b = [...k.querySelectorAll(".tstufen__knopf")]
|
|
.find((x) => x.dataset.an !== "ja");
|
|
if (!b) return null;
|
|
k.dataset.pruefziel = "ja";
|
|
b.dataset.pruefknopf = "ja";
|
|
return { name: k.querySelector(".tperson__name")?.textContent.trim(),
|
|
vorher: k.dataset.stufe, nach: b.dataset.stufe, wort: b.textContent.trim() };
|
|
});
|
|
|
|
if (!ziel) {
|
|
unklar("kein waehlbarer Stufenknopf gefunden — ohne ihn ist der Druck nicht messbar");
|
|
} else {
|
|
const adresseVorher = new URL(seite.url()).pathname;
|
|
abrufe.length = 0;
|
|
await seite.click('.tstufen__knopf[data-pruefknopf="ja"]');
|
|
await seite.waitForTimeout(1400);
|
|
|
|
const adresseNachher = new URL(seite.url()).pathname;
|
|
ok(adresseNachher === adresseVorher,
|
|
`die Adresse bleibt ${adresseNachher} — kein Sprung zur Startseite`);
|
|
ok(abrufe.length === 1 && abrufe[0].startsWith("200"),
|
|
`genau ein Abruf, und er geht durch (${abrufe.join(" | ") || "keiner"})`);
|
|
|
|
const danach = await seite.evaluate(() => {
|
|
const k = document.querySelector('.tperson[data-pruefziel="ja"]');
|
|
return k ? {
|
|
stufe: k.dataset.stufe,
|
|
schild: k.querySelector(".tmerkmal--stufe")?.textContent.trim() || "",
|
|
gewaehlt: [...k.querySelectorAll('.tstufen__knopf[data-an="ja"]')]
|
|
.map((b) => b.dataset.stufe),
|
|
meldung: document.getElementById("fehler")?.textContent.trim() || "",
|
|
} : null;
|
|
});
|
|
ok(danach?.gewaehlt.length === 1 && danach.gewaehlt[0] === ziel.nach,
|
|
`der gedrueckte Knopf ist gewaehlt und nur er (${(danach?.gewaehlt || []).join(", ")})`);
|
|
ok(danach?.schild === (STUFEN_NAME[ziel.nach] || ziel.nach),
|
|
`das Schild zieht mit (${danach?.schild})`);
|
|
/* DIE KARTENFARBE HAENGT AN data-stufe. Blieb sie stehen, sah die
|
|
Karte nach dem Speichern aus wie vorher -- und "gespeichert"
|
|
fuehlt sich dann an wie "nicht gespeichert". */
|
|
ok(danach?.stufe === ziel.nach,
|
|
`und die Karte traegt die neue Stufe, faerbt sich also mit um (${danach?.stufe})`);
|
|
ok(!danach?.meldung,
|
|
`keine Fehlermeldung auf der Seite (${danach?.meldung || "leer"})`);
|
|
|
|
/* ---- GEGENPROBE: derselbe Knopf noch einmal ---------------------- */
|
|
abrufe.length = 0;
|
|
await seite.click('.tstufen__knopf[data-pruefknopf="ja"]');
|
|
await seite.waitForTimeout(900);
|
|
const nochmal = await seite.evaluate(() => {
|
|
const k = document.querySelector('.tperson[data-pruefziel="ja"]');
|
|
return { stufe: k?.dataset.stufe,
|
|
gewaehlt: k ? [...k.querySelectorAll('.tstufen__knopf[data-an="ja"]')].length : -1 };
|
|
});
|
|
ok(abrufe.length === 0 && nochmal.stufe === ziel.nach && nochmal.gewaehlt === 1,
|
|
`Gegenprobe: derselbe Knopf noch einmal ruft nichts ab und aendert nichts`
|
|
+ ` (${abrufe.length} Abrufe)`);
|
|
}
|
|
}
|
|
|
|
/* =====================================================================
|
|
4. DIE BAENDER AUF EINEM SCHMALEN SCHIRM
|
|
|
|
Das Kopfband und das Zahlenband laufen mit NEGATIVEN Aussenabstaenden
|
|
bis an die Kanten der Karte. Solche Werte sind Gegenmasse zum Polster
|
|
der Karte -- und die Karte hat auf schmalen Schirmen ein anderes
|
|
(14/15 px statt 17/19). Passt das nicht zusammen, steht das Band
|
|
ueber, und waagerechtes Scrollen ist auf einem Handy der schlimmste
|
|
Fehler.
|
|
|
|
Gemessen statt gerechnet: 390 px ist die Breite, auf der Filipe
|
|
nachsieht. Geprueft wird die SEITE (scrollt sie waagerecht?) und
|
|
jedes einzelne Band (steht es ueber seiner Karte?) -- das zweite
|
|
deshalb, weil `clip-path` an der Karte ein ueberstehendes Band
|
|
abschneiden wuerde: Die Seite waere still in Ordnung, und der Text
|
|
im Band waere trotzdem weg.
|
|
===================================================================== */
|
|
console.log("");
|
|
console.log("--- Die Baender auf 390 px ---");
|
|
|
|
await seite.setViewportSize({ width: 390, height: 844 });
|
|
await seite.reload({ waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(1500);
|
|
|
|
const schmal = await seite.evaluate(() => {
|
|
const karten = [...document.querySelectorAll(".t-leute .tperson")];
|
|
const ueber = [];
|
|
for (const k of karten) {
|
|
const kr = k.getBoundingClientRect();
|
|
for (const wahl of [".tperson__kopf", ".tperson__zahlen"]) {
|
|
const b = k.querySelector(wahl);
|
|
if (!b) continue;
|
|
const br = b.getBoundingClientRect();
|
|
/* Ein halber Pixel Spiel: Die Fase rechnet mit Bruchteilen. */
|
|
if (br.left < kr.left - 0.5 || br.right > kr.right + 0.5) {
|
|
ueber.push(`${wahl} ${Math.round(br.left - kr.left)}/${Math.round(br.right - kr.right)}`);
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
karten: karten.length,
|
|
scrollBreite: document.documentElement.scrollWidth,
|
|
sichtbar: window.innerWidth,
|
|
ueber,
|
|
/* Bei einer Spalte darf keine Karte breiter sein als der Schirm. */
|
|
breiteste: Math.round(Math.max(0, ...karten.map((k) => k.getBoundingClientRect().width))),
|
|
};
|
|
});
|
|
|
|
ok(schmal.karten > 0, `${schmal.karten} Karten auch auf 390 px`);
|
|
ok(schmal.karten > 0 && schmal.scrollBreite <= schmal.sichtbar + 1,
|
|
`die Seite scrollt nicht waagerecht (${schmal.scrollBreite} von ${schmal.sichtbar})`);
|
|
ok(schmal.karten > 0 && schmal.ueber.length === 0,
|
|
`kein Band steht ueber seiner Karte (${schmal.ueber.join(", ") || "keines"})`);
|
|
ok(schmal.karten > 0 && schmal.breiteste <= schmal.sichtbar,
|
|
`die breiteste Karte passt (${schmal.breiteste} von ${schmal.sichtbar})`);
|
|
|
|
ok(meckern.length === 0,
|
|
`der Browser meldet nichts (${meckern.slice(0, 2).join(" | ") || "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);
|