Files
dogfather-universe/server/mess-vanvan-karte.mjs
T
DogFatherGitandClaude Opus 5 f6225d7341 Ports: auch die Messwerkzeuge leiten ihre Nummer ab
GEMESSEN, BEVOR ICH ETWAS ANGEFASST HABE:

  204 pruef-Dateien, abgeleitete Ports 5000-5409 (dicht belegt)
  24 mess-Dateien, Nummern von Hand: 4471 bis 5493
  davon IM Pruefbereich: 5387, 5397, 5397, 5399, 5399, 5401, 5403, 5405
  untereinander doppelt: 5397, 5399, 5461, 5483, 5491

Aufgefallen ist es, weil mess-buehne und mess-reaktion beide auf 5483
lagen und ein haengengebliebener Lauf gestern einen ganzen Messlauf
gekostet hat.

Beim Umbau kam das Groessere heraus: EINUNDZWANZIG der 24 Messdateien
hatten nicht nur eine Nummer von Hand, sondern ueberhaupt keinen
Waechter -- schlicht `const PORT = 5397;`. Liegt dort schon ein
Server, startet der eigene still nicht, und gemessen wird ab da ein
fremder Stand. Genau der Fehler, gegen den helfer-port.mjs gebaut
wurde; die Messdateien standen die ganze Zeit ausserhalb.

WAS JETZT GILT

Zwei Sorten, zwei Bereiche, beide abgeleitet aus der Stelle im
Alphabet -- jede Sorte unter ihresgleichen, sonst verschoebe eine
neue Pruefung die Nummern aller Messungen. Pruefungen ab 5000,
Messungen ab MESS_BASIS = 5900.

5900 und nicht 5500: dazwischen bleibt Platz fuer 245 weitere
Pruefdateien (bei 5500 waeren es 45). Nach oben 5948 + AUSWEICHEN
4000 = 9948, also unter 10080, der naechsten gesperrten Nummer.
Nachgerechnet, nicht geschaetzt -- eine geschaetzte 4500 hatte bei
BASIS schon einmal danebengelegen.

Eine Wache dazu: Waechst der Pruefbereich bis an MESS_BASIS heran,
bricht die Ableitung ab und sagt, was zu tun ist. Eine stille
Ueberschneidung waere genau der Fehler, den das hier beseitigt.

Die zweite Nummer kommt ueber nr=1, nie ueber `PORT + 1`:
portNummer ueberspringt gesperrte Nummern, deshalb kann die naechste
Zahl die Nummer der naechsten DATEI sein, sobald einmal eine Sperre
dazwischenliegt. Heute liegt dort keine -- das ist Glueck, kein
Entwurf.

NEBENBEI GEFUNDEN UND MIT REPARIERT

Sechs bild-*.mjs riefen den Waechter und warfen seine Antwort weg:

    await portMussFreiSein(4315, "das Bildwerkzeug");
    process.env.PORT = "4315";
    const BASIS = "http://127.0.0.1:4315";

Er lief, meldete nichts und wirkte nicht. Gibt das System den Port
dauerhaft nicht her, weicht er auf Port + 4000 aus und GIBT DIE NEUE
NUMMER ZURUECK -- diese Werkzeuge hoerten danach trotzdem auf der
alten und stuerzten mit `listen EACCES` ab, also mit genau dem
Fehler, gegen den er gebaut wurde. Dazu stand die Zahl dreimal je
Datei. Jetzt einmal, und die Antwort wird benutzt.

tiktok-videos.mjs hatte den Waechter ABGESCHRIEBEN -- eine kurze
eigene Fassung ohne den dritten Ausgang: Bei EACCES meldete sie
"belegt" und brach ab, statt auszuweichen. Auf diesem Rechner ist
genau das am 23.09. eingetreten (Port 5040, Windows-Dienst).
mess-fokus und mess-notizblock hatten dieselbe Abschrift. Eine
abgeschriebene Sicherung ist dieselbe Falle wie eine abgeschriebene
Liste.

GEPRUEFT

  pruef-portnummern  15 -> 41 Pruefungen, 0 Fehler
  pruef-ports         8 -> 10 Pruefungen, 456 statt 408 Ports geprobt
  node --check auf allen 33 geaenderten Dateien

Gegenproben, die wirklich rot werden:
  - eine Messdatei auf eine feste Nummer zurueckgesetzt -> 2 FEHL,
    danach wieder 41/0
  - die Wache: in einem Wegwerf-Ordner mit 452 pruef-Dateien bricht
    eigenerPort ab statt still zu ueberlappen; eine Datei knapp
    darunter bekommt weiter ihre Nummer (5846)
  - die Erkennungen fuer feste Nummern, PORT + 1 und weggeworfene
    Waechterantworten je gegen einen gebauten Rueckschritt

Am echten Verhalten gemessen:
  - mess-chat-liste und mess-alle-einzelsicht (beide vorher 5397)
    GLEICHZEITIG gestartet: 5906 und 5900, beide exit=0. Vorher war
    das unmoeglich.
  - die 48 neuen Nummern 5900-5947 auf diesem Rechner durchprobiert:
    keine belegt, keine vom System gesperrt
  - bild-chat.mjs durchgelaufen, drei Bilder, exit=0

Kein Eingriff am laufenden Dienst: helfer-port.mjs wird von index.js
und workspace.js nicht geladen (nachgesehen), nur von Pruef- und
Messwerkzeugen. Beide Haeuser unberuehrt -- es wird keine Zeile
angefasst, die eine Seite ausliefert.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-10-01 01:19:41 +02:00

493 lines
23 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* =====================================================================
VANVANS MELDUNG AUS DEM SUPPORT (30.09.2026)
„Wenn ich im Bereich eure Aufgaben runterscrolle und dort bei den 68
Aufgaben … Aufgaben an einen Modi zutrage und auch bewerte, dann
sieht der Modi zwar die Auswertung im Bereich Entwicklung, aber bei
ihm taucht nichts im Bereich eure Aufgaben unten in der Karte auf.
Bei mir zum Beispiel steht in der Karte im Bereich eure Aufgaben
bei jedem Punkt läuft obwohl auch wenn die Aufgaben darüber gar
nicht zugetragen wurde."
---------------------------------------------------------------------
ZWEI FRAGEN, UND BEIDE LASSEN SICH NUR AM BILDSCHIRM BEANTWORTEN
1. Was sieht der MODI unten in seiner eigenen Karte, nachdem ihm
etwas zugetragen und bewertet wurde?
2. Was steht bei der LEITUNG an einem Punkt, der gar nicht
zugetragen ist?
Der Quelltext allein sagt das nicht. Er sagt, was gerendert wird --
nicht, was ein Mensch daraus liest. Und genau darum geht es in ihrer
Meldung.
---------------------------------------------------------------------
DER AUFBAU IST IHR FALL, NICHT EIN AEHNLICHER
Filipe (admin) und VanVan (hand) sind die beiden Beurteiler --
genauso wie im Haus. Diene ist ein Modi. VanVan traegt ihm ZWEI von
68 Punkten zu und bewertet sie; einen DRITTEN bewertet sie, ohne ihn
zuzutragen. Filipe bewertet ABSICHTLICH NICHTS -- denn genau so war
es bei ihr, und wenn das der Grund ist, muss er hier sichtbar
werden.
Sie prueft nichts und meldet keinen Fehlschlag. Sie sagt, was auf
dem Bildschirm steht.
Aufrufen mit: node server/mess-vanvan-karte.mjs
===================================================================== */
import { mkdtempSync, rmSync } from "node:fs";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { eigenerPort } from "./helfer-port.mjs";
const PORT = await eigenerPort(import.meta);
const ordner = mkdtempSync(join(tmpdir(), "ws-vanvan-"));
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
process.env.PORT = String(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 });
};
import { notbremse } from "./helfer-notbremse.mjs";
await import("./index.js");
notbremse(180_000, "mess-vanvan-karte");
await new Promise((r) => setTimeout(r, 800));
const BASIS = `http://127.0.0.1:${PORT}`;
process.on("uncaughtException", (f) => { console.error("ABSTURZ:", f?.stack || f); process.exit(7); });
process.on("unhandledRejection", (f) => { console.error("ABSTURZ:", f?.stack || f); process.exit(7); });
const { DatabaseSync } = await import("node:sqlite");
const { scryptSync, randomBytes, createHmac } = await import("node:crypto");
const { ENTWICKLUNG_FREMD } = await import("./workspace-entwicklungs-punkte.js");
const d = new DatabaseSync(process.env.WORKSPACE_DB);
const jetzt = new Date().toISOString();
const geheim = randomBytes(32).toString("hex");
d.prepare("INSERT INTO einstellungen (schluessel, wert, geaendert, von)"
+ " VALUES (?,?,?,NULL) ON CONFLICT(schluessel) DO NOTHING")
.run("code_kennung_schluessel", geheim, jetzt);
function anlegen(name, rolle, code) {
const salz = randomBytes(16).toString("hex");
const hash = scryptSync(code, salz, 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, salz, 32768,
createHmac("sha256", geheim).update(code).digest("hex"), jetzt);
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
}
const idDogi = anlegen("Filipe", "admin", "CODE-DOGI-0001");
const idVan = anlegen("VanVan", "hand", "CODE-VANVAN-01");
const idModi = anlegen("Diene", "modi", "CODE-DIENE-001");
d.close();
/* ---- Anmelden ---------------------------------------------------- */
async function anmelden(rolle, code) {
const a = await fetch(BASIS + "/workspace/api/anmelden", {
method: "POST", headers: { "Content-Type": "application/json" },
body: JSON.stringify({ rolle, code, alter_ok: true }),
});
return (a.headers.getSetCookie?.() || []).map((z) => z.split(";")[0]).join("; ");
}
const kVan = await anmelden("hand", "CODE-VANVAN-01");
const kModi = await anmelden("modi", "CODE-DIENE-001");
const kDogi = await anmelden("admin", "CODE-DOGI-0001");
if (!kVan || !kModi || !kDogi) {
console.error("KONNTE NICHT NACHSEHEN: die Anmeldung hat nicht geklappt.");
process.exit(1);
}
const ruf = (keks, weg, rumpf, art = "GET") => fetch(BASIS + weg, {
method: art,
headers: { "Content-Type": "application/json", Cookie: keks },
body: rumpf === undefined ? undefined : JSON.stringify(rumpf),
});
/* ---- VanVans Handgriffe ------------------------------------------ */
const P1 = ENTWICKLUNG_FREMD[0].schluessel;
const P2 = ENTWICKLUNG_FREMD[1].schluessel;
const P3 = ENTWICKLUNG_FREMD[2].schluessel; // bewertet, NICHT zugetragen
console.log("");
console.log("=== Was VanVan getan hat ===");
for (const [p, wort] of [[P1, "zugetragen"], [P2, "zugetragen"]]) {
const a = await ruf(kVan, `/workspace/api/entwicklung/person/${idModi}/punkt/${p}`,
{ an: true }, "PUT");
console.log(` ${wort}: ${p} (HTTP ${a.status})`);
}
for (const p of [P1, P2, P3]) {
const a = await ruf(kVan, `/workspace/api/entwicklung/person/${idModi}/${p}`,
{ stufe: "laeuft", anlass: "Im Stream gesehen, passt." }, "PUT");
console.log(` bewertet „Läuft": ${p} (HTTP ${a.status})`);
}
console.log(` Filipe bewertet NICHTS -- genau wie in ihrem Fall.`);
console.log(` Von ${ENTWICKLUNG_FREMD.length} Punkten sind 2 zugetragen und 3 bewertet.`);
/* =====================================================================
FRAGE 1: WAS SIEHT DER MODI UNTEN IN SEINER KARTE?
===================================================================== */
console.log("");
console.log("=== 1. Die eigene Karte des Modi (API) ===");
{
const k = await (await ruf(kModi, "/workspace/api/entwicklung/meine-karte")).json();
console.log(` hat_karte=${k.hat_karte} Beurteiler=${k.beurteiler} `
+ `fertig=${k.fertig} von ${k.gesamt}`);
const alle = (k.bloecke || []).flatMap((b) => b.punkte);
const offen = alle.filter((p) => p.offen).length;
console.log(` ${offen} von ${alle.length} Punkten stehen auf „offen"`);
for (const s of [P1, P2, P3]) {
const p = alle.find((x) => x.schluessel === s);
console.log(` ${s.padEnd(26)} offen=${p?.offen} `
+ `zugetragen=${"zugetragen" in (p || {}) ? p.zugetragen : "(gibt es nicht)"} `
+ `antworten=${JSON.stringify(p?.antworten ?? null)}`);
}
/* DIE KERNFRAGE: Steht in seiner Karte ueberhaupt irgendwo, WELCHE
Punkte ihm als Aufgabe zugetragen wurden? */
const kennt = alle.some((p) => "zugetragen" in p);
console.log(` Kennt seine Karte das Wort „zugetragen"? ${kennt ? "JA" : "NEIN"}`);
}
/* =====================================================================
FRAGE 2: WAS STEHT BEI VANVAN AN EINEM PUNKT?
===================================================================== */
console.log("");
console.log("=== 2. VanVans Karte ueber Diene (API) ===");
{
const k = await (await ruf(kVan, `/workspace/api/entwicklung/person/${idModi}`)).json();
const alle = (k.bloecke || []).flatMap((b) => b.punkte);
console.log(` ${alle.length} Punkte insgesamt, `
+ `${alle.filter((p) => p.zugetragen).length} davon zugetragen, `
+ `${alle.filter((p) => p.meins).length} bewertet, `
+ `${alle.filter((p) => p.schritt).length} mit einem Schritt`);
for (const s of [P1, P3]) {
const p = alle.find((x) => x.schluessel === s);
console.log(` ${s.padEnd(26)} zugetragen=${p?.zugetragen} `
+ `meins=${p?.meins?.stufe ?? "-"} schritt=${p?.schritt ? p.schritt.status : "-"}`);
}
}
/* =====================================================================
UND JETZT DER BILDSCHIRM
===================================================================== */
const { chromium } = await import(
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
const browser = await chromium.launch();
async function alsWer(keks, breite = 1280, hoehe = 1000) {
const kontext = await browser.newContext({ viewport: { width: breite, height: hoehe } });
await kontext.addCookies(keks.split("; ").filter(Boolean).map((z) => {
const [n, ...r] = z.split("=");
return { name: n, value: r.join("="), domain: "127.0.0.1", path: "/" };
}));
const seite = await kontext.newPage();
const konsole = [];
seite.on("console", (e) => { if (e.type() === "error") konsole.push(e.text()); });
seite.on("pageerror", (f) => konsole.push(String(f?.message || f)));
return { kontext, seite, konsole };
}
console.log("");
console.log("=== 3. Was der Modi auf der Seite Eure Aufgaben WIRKLICH sieht ===");
{
const { kontext, seite, konsole } = await alsWer(kModi);
await seite.goto(`${BASIS}/workspace/entwicklung.html`, { waitUntil: "networkidle" });
await seite.waitForTimeout(1500);
const w = await seite.evaluate(() => {
/* GEMESSEN WIRD IN SEINER KARTE, nicht auf der ganzen Seite. Das
Wort „Aufgabe" steht oben in der Kopfleiste und in der
Vorlagen-Kachel -- eine Suche ueber die ganze Seite haette
„ja" gesagt, ohne dass in der Karte irgendetwas steht. Genau
die Sorte gruener Haken, die nichts geprueft hat. */
const karte = document.getElementById("karte-block");
const auf = karte?.querySelector(".e-block--auftrag") || null;
return {
ueberschriften: [...document.querySelectorAll("h1, h2")]
.map((x) => x.textContent.trim()).filter(Boolean).slice(0, 12),
karteDa: !!karte && !karte.hidden,
/* SEIT DEM 30.09.2026 IST DER KOPF EIN SCHALTER. Der Name steht
in `.e-block__name`, die Zahl daneben in `.e-block__stand` --
`.e-block__titel` gibt es hier nicht mehr. Die Zeile meldete
`undefined` und haette das stillschweigend weiter getan. */
auftragsblock: auf
? [auf.querySelector(".e-block__name")?.textContent.trim(),
auf.querySelector(".e-block__stand")?.textContent.trim()].join(" ")
: null,
auftragKlappbar: !!auf?.querySelector(".e-block__schalter"),
aufgabenTitel: auf
? [...auf.querySelectorAll(".e-punkt__titel")].map((x) => x.textContent.trim())
: [],
aufgabenLage: auf
? [...auf.querySelectorAll(".e-punkt__wann")].map((x) => x.textContent.trim())
: [],
satz: (document.getElementById("karte-satz")?.textContent || "").trim().slice(0, 160),
punkte: karte ? karte.querySelectorAll(".e-punkt").length : 0,
};
});
console.log(` Ueberschriften: ${w.ueberschriften.join(" | ")}`);
console.log(` Karte sichtbar: ${w.karteDa}, Punktzeilen darin: ${w.punkte}`);
console.log(` Abschnitt: ${JSON.stringify(w.auftragsblock)} `
+ `(einklappbar=${w.auftragKlappbar})`);
console.log(` Seine Aufgaben: ${JSON.stringify(w.aufgabenTitel)}`);
console.log(` Lage dazu: ${JSON.stringify(w.aufgabenLage)}`);
console.log(` Satz: ${w.satz}`);
if (!w.aufgabenTitel.length) {
console.log(" ACHTUNG: In seiner Karte steht nicht, welche Punkte seine "
+ "Aufgaben sind -- genau VanVans Meldung.");
}
await seite.screenshot({ path: "server/mess-vanvan-karte-modi.png", fullPage: true });
console.log(" Bild: server/mess-vanvan-karte-modi.png");
if (konsole.length) console.log(" KONSOLE:", konsole.slice(0, 3).join(" | "));
await kontext.close();
}
console.log("");
console.log("=== 4. Was VanVan an einem Punkt sieht ===");
{
const { kontext, seite, konsole } = await alsWer(kVan);
await seite.goto(`${BASIS}/workspace/entwicklung.html`, { waitUntil: "networkidle" });
await seite.waitForTimeout(1200);
/* Dienes Kachel aufmachen. */
const auf = await seite.evaluate((name) => {
const k = [...document.querySelectorAll("#personen .e-person")]
.find((x) => (x.querySelector(".e-person__name")?.textContent || "").includes(name));
if (!k) return false;
(k.querySelector("button, a") || k).click();
return true;
}, "Diene");
console.log(` Dienes Kachel geoeffnet: ${auf}`);
await seite.waitForTimeout(2000);
const z = await seite.evaluate(() => {
const zeilen = [...document.querySelectorAll(".e-punkt")].slice(0, 6).map((p) => ({
titel: (p.querySelector(".e-punkt__titel")?.textContent || "").trim().slice(0, 34),
zugetragen: p.dataset.zugetragen,
auftrag: (p.querySelector(".e-punkt__auftrag")?.textContent || "").trim(),
/* WAS WIRKLICH AN DER ZEILE STEHT -- ohne die Knopfbeschriftungen
vorher zu kennen. Genau das ist die Frage: Was liest ein
Mensch dort? */
woerter: (p.innerText || "").replace(/\s+/g, " ").trim().slice(0, 150),
gedrueckt: [...p.querySelectorAll("[aria-pressed='true']")]
.map((b) => b.textContent.trim()),
}));
return {
anzahl: document.querySelectorAll(".e-punkt").length,
zeilen,
/* Sieht SIE, dass es bei ihm noch nicht ankommt? */
wartet: [...document.querySelectorAll(".e-punkt__wartet")]
.map((x) => x.textContent.trim()),
/* Und gibt es den Knopf, mit dem sie auf das Zugetragene
eingrenzen kann? */
knoepfe: [...document.querySelectorAll(".e-stufen__knopf")]
.map((k) => `${k.querySelector(".e-stufen__name")?.textContent.trim()}`
+ `=${k.querySelector(".e-stufen__zahl")?.textContent.trim()}`),
};
});
console.log(` ${z.anzahl} Punktzeilen sichtbar. Die ersten sechs:`);
for (const r of z.zeilen) {
console.log(` · ${r.titel.padEnd(36)} zugetragen=${r.zugetragen} `
+ `Knopf=„${r.auftrag}" gedrueckt=${JSON.stringify(r.gedrueckt)}`);
console.log(` „${r.woerter}"`);
}
console.log(` „Er sieht das noch nicht" steht an ${z.wartet.length} Punkten`);
if (z.wartet[0]) console.log(` „${z.wartet[0]}"`);
console.log(` Filterknoepfe: ${z.knoepfe.join(" ")}`);
if (!z.wartet.length) {
console.log(" ACHTUNG: Sie hat bewertet, es kommt beim Modi nicht an, und "
+ "nichts auf ihrem Bildschirm sagt ihr das.");
}
await seite.screenshot({ path: "server/mess-vanvan-karte-leitung.png", fullPage: true });
console.log(" Bild: server/mess-vanvan-karte-leitung.png");
/* ---- WOMIT GEHT DIE KARTE AUF? (01.10.2026) -----------------
Filipe: „mach alles los" -- damit auch: „Zugetragen" ist die
Vorgabe, wenn es etwas zu zeigen gibt.
VORHER KLICKTE DIESE MESSUNG AUF DEN FILTER und zaehlte nach.
Seit er beim Aufmachen schon steht, haette derselbe Klick ihn
AUSgeschaltet -- die Messung haette das Gegenteil gemessen und
trotzdem eine Zahl gemeldet. Genau die Sorte gruener Haken, die
nichts prueft.
Gefragt wird deshalb, was DASTEHT, bevor jemand etwas anfasst. */
const beimAufmachen = await seite.evaluate(() => {
const gedrueckt = [...document.querySelectorAll(".e-stufen__knopf")]
.find((k) => k.getAttribute("aria-pressed") === "true");
return {
filter: gedrueckt?.querySelector(".e-stufen__name")?.textContent.trim() || null,
punkte: document.querySelectorAll(".e-punkt").length,
alleZugetragen: [...document.querySelectorAll(".e-punkt")]
.every((p) => p.dataset.zugetragen === "ja"),
};
});
console.log(` Beim Aufmachen steht der Filter auf „${beimAufmachen.filter}": `
+ `${beimAufmachen.punkte} Punkte, alle zugetragen=${beimAufmachen.alleZugetragen}`);
if (beimAufmachen.filter !== "Zugetragen" || beimAufmachen.punkte !== 2
|| !beimAufmachen.alleZugetragen) {
console.log(" ACHTUNG: Die Karte geht nicht auf dem Zugetragenen auf.");
}
/* GEGENPROBE: „Alle" ist einen Klick entfernt und zeigt wieder alle
68. Ohne sie hiesse „2 Punkte" womoeglich nur, dass es nicht
mehr gibt. */
const zurueck = await seite.evaluate(() => {
const k = [...document.querySelectorAll(".e-stufen__knopf")]
.find((x) => x.querySelector(".e-stufen__name")?.textContent.trim() === "Alle");
if (!k) return null;
k.click();
return true;
});
if (zurueck) {
await seite.waitForTimeout(1200);
const alle = await seite.evaluate(() => document.querySelectorAll(".e-punkt").length);
console.log(` Nach „Alle": ${alle} Punkte`);
if (alle !== 68) {
console.log(` ACHTUNG: „Alle“ zeigt nicht mehr alle 68.`);
}
} else {
console.log(` ACHTUNG: Den Knopf „Alle“ gibt es nicht.`);
}
await seite.screenshot({ path: "server/mess-vanvan-karte-filter.png", fullPage: true });
console.log(" Bild: server/mess-vanvan-karte-filter.png");
/* ==== UND WER NICHTS ZUGETRAGEN HAT? (01.10.2026) ==============
Das ist die Gegenprobe, die der alte Kommentar verlangt: „Ein
Filter, der beim Öffnen schon etwas versteckt, lässt einen
Punkte suchen, die gestern noch da waren." Bei einem Menschen
ohne zugetragene Punkte zeigte „Zugetragen" eine LEERE Karte --
und eine leere Karte sieht aus wie ein Fehler.
VanVan hat in diesem Aufbau nichts zugetragen bekommen; ihre
Karte muss deshalb auf „Alle" aufgehen. */
const beiVanVan = await seite.evaluate(async () => {
const k = [...document.querySelectorAll("#personen .e-person")]
.find((x) => (x.querySelector(".e-person__name")?.textContent || "").includes("VanVan"));
if (!k) return null;
(k.querySelector("button, a") || k).click();
await new Promise((r) => setTimeout(r, 2200));
const gedrueckt = [...document.querySelectorAll(".e-stufen__knopf")]
.find((x) => x.getAttribute("aria-pressed") === "true");
return {
filter: gedrueckt?.querySelector(".e-stufen__name")?.textContent.trim() || null,
punkte: document.querySelectorAll(".e-punkt").length,
};
});
if (!beiVanVan) {
console.log(" KONNTE NICHT NACHSEHEN: VanVans Kachel war nicht zu finden.");
} else {
console.log(` Bei jemandem OHNE Zugetragenes: Filter „${beiVanVan.filter}", `
+ `${beiVanVan.punkte} Punkte`);
if (beiVanVan.filter !== "Alle" || beiVanVan.punkte !== 68) {
console.log(" ACHTUNG: Wer nichts zugetragen hat, bekommt eine leere Karte.");
}
}
if (konsole.length) console.log(" KONSOLE:", konsole.slice(0, 3).join(" | "));
await kontext.close();
}
/* =====================================================================
5. VANVANS ZWEITE MELDUNG: „Deine Karte kann man nicht einklappen"
„was die Seite unnötig lang zieht und unübersichtlich macht."
IHRE EIGENE KARTE, nicht die eines Modi. Im Bildschirmfoto steht an
jedem Punkt „1 × Läuft" -- eine einzige Antwort. Ueber die rechte
Hand urteilt nur DogFather, also ist bei ihr alles „fertig" und die
Karte entsprechend lang. Ein Modi braucht zwei Stimmen und sieht
deshalb meist gar nichts.
GEMESSEN WIRD DIE HOEHE. „Unnötig lang" ist eine Zahl, keine
Meinung -- und ohne sie laesst sich nachher nicht sagen, ob es
besser geworden ist.
===================================================================== */
console.log("");
console.log("=== 5. VanVans eigene Karte: wie lang wird die Seite? ===");
{
/* Filipe bewertet VanVan durch -- alle Punkte, wie im echten Haus
ueber Monate gewachsen. */
let gesetzt = 0;
for (const pt of ENTWICKLUNG_FREMD) {
const a = await ruf(kDogi, `/workspace/api/entwicklung/person/${idVan}/${pt.schluessel}`,
{ stufe: "laeuft", anlass: "" }, "PUT");
if (a.status === 200) gesetzt++;
}
console.log(` Filipe hat ${gesetzt} von ${ENTWICKLUNG_FREMD.length} Punkten gesetzt.`);
const { kontext, seite, konsole } = await alsWer(kVan, 1280, 1000);
await seite.goto(`${BASIS}/workspace/entwicklung.html`, { waitUntil: "networkidle" });
await seite.waitForTimeout(1800);
const m = await seite.evaluate(() => {
const karte = document.getElementById("karte-block");
const r = karte?.getBoundingClientRect();
return {
seitenhoehe: document.documentElement.scrollHeight,
kartenhoehe: r ? Math.round(r.height) : 0,
bloecke: karte ? karte.querySelectorAll(".e-block").length : 0,
zeilen: karte ? karte.querySelectorAll(".e-punkt").length : 0,
/* Gibt es ueberhaupt einen Schalter zum Zuklappen? */
schalter: karte ? karte.querySelectorAll(".e-block__schalter").length : 0,
alleKnopf: karte ? karte.querySelectorAll(".e-bloecke__alle").length : 0,
};
});
console.log(` Seite gesamt: ${m.seitenhoehe} px`);
console.log(` Davon „Deine Karte": ${m.kartenhoehe} px `
+ `(${m.bloecke} Abschnitte, ${m.zeilen} Zeilen)`);
console.log(` Schalter zum Zuklappen: ${m.schalter}, „Alle"-Knopf: ${m.alleKnopf}`);
if (!m.schalter) {
console.log(" ACHTUNG: Kein einziger Abschnitt laesst sich zuklappen -- "
+ "genau VanVans Meldung.");
} else {
/* ZUKLAPPEN UND NACHMESSEN. Ohne diesen Griff hiesse „es gibt
Schalter" nur, dass es Schalter gibt. */
const nach = await seite.evaluate(() => {
const k = document.querySelector("#karte-block .e-bloecke__alle");
if (k && k.textContent.includes("zuklappen")) k.click();
else document.querySelectorAll("#karte-block .e-block__schalter")
.forEach((s) => { if (s.getAttribute("aria-expanded") === "true") s.click(); });
const r = document.getElementById("karte-block")?.getBoundingClientRect();
return {
kartenhoehe: r ? Math.round(r.height) : 0,
sichtbar: [...document.querySelectorAll("#karte-block .e-punkt")]
.filter((z) => z.getBoundingClientRect().height > 0).length,
};
});
console.log(` Zugeklappt: ${nach.kartenhoehe} px `
+ `(${nach.sichtbar} Zeilen noch sichtbar)`);
const gespart = m.kartenhoehe - nach.kartenhoehe;
const anteil = Math.round(gespart / Math.max(1, m.kartenhoehe) * 100);
console.log(` Gespart: ${gespart} px (${anteil} %)`);
/* EIN ANTEIL UND KEINE FESTE PIXELZAHL. Wie hoch die Karte ist,
haengt am Bestand -- 68 Punkte heute, vielleicht 90 im
naechsten Jahr. Eine feste Grenze waere die Rechnung von
gestern; „mehr als die Haelfte weg" gilt auch dann noch. */
if (anteil < 50 || nach.sichtbar > 0) {
console.log(" ACHTUNG: Zuklappen bringt kaum etwas "
+ `(${anteil} %, ${nach.sichtbar} Zeilen bleiben stehen).`);
}
}
if (m.schalter && !m.alleKnopf) {
console.log(" ACHTUNG: Es gibt Schalter, aber keinen Knopf, der alle "
+ "auf einmal zuklappt -- bei sechs Abschnitten ist das sechsmal tippen.");
}
await seite.screenshot({ path: "server/mess-vanvan-karte-eigene.png", fullPage: true });
console.log(" Bild: server/mess-vanvan-karte-eigene.png");
if (konsole.length) console.log(" KONSOLE:", konsole.slice(0, 3).join(" | "));
await kontext.close();
}
await browser.close();
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Rest im Temp */ }
console.log("");
console.log("Fertig.");
process.exit(0);