Gemessen: 165 Pruefdateien, 129 verschiedene Nummern -- NEUNZEHN
doppelt, vier davon dreifach. Niemand hatte das gewollt; jede neue
Pruefung wurde von einer vorhandenen abgeschrieben, und die Nummer kam
mit. Meine eigene Notiz sagte "sieben" -- auch eine Bestandsliste
altert.
Der Waechter faengt den Schaden ab, aber er kann nur melden, was schon
passiert ist: Zwei Pruefungen mit derselben Nummer koennen nie
gleichzeitig laufen, und ein liegengebliebener Prozess der einen laesst
die andere abbrechen mit einer Meldung, die wie ein Befund aussieht.
Genau das ist mir am 20.09. zweimal passiert.
Jetzt leitet jede Datei ihre Nummer aus ihrer STELLE IM ALPHABET ab
(eigenerPort in helfer-port.mjs), zwei je Datei. Nicht ueber eine
Pruefsumme: Bei 165 Namen in 4900 Nummern waeren nach dem
Geburtstagsproblem rund DREI Zusammenstoesse zu erwarten -- ein Hash
tauscht eine sichtbare Doppelung gegen eine unsichtbare. Die Stelle im
Alphabet ist eindeutig von der Bauart her.
--- ZWEI FEHLER AUF DEM WEG, BEIDE LEHRREICH -------------------------
1. DER ERSTE VERSUCH WAR GRUEN UND KAPUTT. Ersetzt wurde mit einem
Muster: "([^"]*4231[^"]*)". Das hielt
{ host: "127.0.0.1", port: 4231, path: "/404.html" }
fuer eine Zeichenkette -- ein Muster kann eine oeffnende nicht von
einer schliessenden Anfuehrung unterscheiden. Heraus kam
{ host: "127.0.0.1`, port: ${PORT}, path: `/404.html" }
also GUELTIGER Code ohne port-Feld. `node --check` sagte gruen fuer
alle 164 Dateien. Aufgefallen ist es erst, weil ich vier Vertreter
gegen eine vorher gemessene Grundlinie laufen liess: pruef-schranke
39/0 vorher, 38/1 nachher.
Alles zurueckgenommen und mit einem Zerleger neu gemacht, der weiss,
ob eine Stelle Code, Zeichenkette, Vorlage, Kommentar oder
regulaerer Ausdruck ist. In pruef-ics stand die Nummer in einem
regulaeren Ausdruck -- der wird jetzt gebaut statt hingeschrieben.
2. EIN MODUL, DAS BEIM IMPORTIEREN ARBEITET, IST EINE FALLE. Der
zweite Durchgang importierte den ersten, um seine Mechanik zu
benutzen -- und fuehrte dessen Hauptlauf gleich mit aus. Die
zweiten Nummern wurden dadurch als erste behandelt, zwei Aufrufe
bekamen dieselbe Nummer, und in pruef-content stand `const PORT`
zweimal.
--- WAS DAS DAUERHAFT HAELT -----------------------------------------
pruef-portnummern.mjs (neu, 8 Pruefungen) fragt nicht "welche Nummern
sind doppelt", sondern "wer traegt ueberhaupt noch eine von Hand ein"
und "wer startet einen Server, ohne seine Nummer abzuleiten". Die
zweite Frage hat sofort etwas gefunden, das in KEINER Doppelungsliste
stand: pruef-push-weg belegte 4341 und 4342, rief den Waechter aber
gar nicht auf -- dieselbe Nummer wie pruef-agentur. Eine Liste zeigt
nur, was auf ihr steht.
Mit Gegenprobe: Eine unbekannte Datei bekommt keine geratene Nummer,
sondern einen Abbruch, und eine dritte Nummer je Datei gibt es nicht.
--- NACHGEMESSEN ----------------------------------------------------
Sechzehn Pruefungen gegen ihre vorher gemessene Grundlinie, je eine
Vertreterin jeder umgestellten Bauweise (eine Nummer, zwei Nummern,
Nummer in einer Zeichenkette, in einer Vorlage, in einem regulaeren
Ausdruck, dynamische Einfuhr, Nachtlauf mit zwei Laeufen):
crew-adresse 132/0 · schranke 39/0 · content 45/0 · entwicklung 46/0
anruf 127/0 · ics 37/0 · push-weg 20/0 · arten 28/0 · video 67/0
kanalzeile 14/0 · agentur 62/0 · spicy 83/0 · creator-anlegen 50/0
push-ziel 10/0 · portnummern 8/0
Alle exakt wie vorher. Zwei waren schon vorher rot und sind es
unveraendert geblieben (crew-wand-bild 41/4, teilen 14/3) -- per
`git stash` belegt, nicht angenommen.
Co-Authored-By: Claude Opus 5 <[email protected]>
377 lines
17 KiB
JavaScript
377 lines
17 KiB
JavaScript
/* =====================================================================
|
|
JEDER HAT EINE EIGENE SEITE -- UND ALLES STEHT IN DER RICHTIGEN
|
|
REIHENFOLGE (19.09.2026)
|
|
|
|
Zwei Auftraege von Filipe, die dieselbe Frage stellen: Was sieht wer,
|
|
und in welcher Ordnung?
|
|
|
|
"jeder soll ein steckbrief haben, jeder der ein account hat,
|
|
rechte hand, modis und community, jeder soll genau wie ich foto
|
|
und so hinzufuegen koennen."
|
|
|
|
"ich will das oben die sachen fuer dogfather sind, dan die sachen
|
|
fuer modis und dan community bereich ... was sie nicht sehen sehen
|
|
sie nicht."
|
|
|
|
WAS HIER SCHIEFGEHEN KANN, UND ZWAR LAUTLOS:
|
|
|
|
1. KACHEL OHNE TUER. Die Kachel steht da, die Seite ist zu -- ein
|
|
Knopf, der auf die Startseite zurueckwirft. Umgekehrt genauso:
|
|
Tuer offen, keine Kachel, also findet es niemand.
|
|
|
|
2. WER IN KEINEN ABSCHNITT PASST, VERSCHWINDET. Genau das war der
|
|
Fall: assets/js/steckbrief.js kannte drei Gruppen (Management,
|
|
Scouting, Creator). Die rechte Hand, die Modis und die Community
|
|
fielen heraus -- ohne Fehler, ohne leere Liste. Sie waren einfach
|
|
nicht da.
|
|
|
|
3. DIE REIHENFOLGE ENTSTEHT AUS VERSEHEN. Sie hing davon ab, welche
|
|
Kachelliste an welche angehaengt wurde. So landete die Moderation
|
|
hinter allen sieben Brettern der Community.
|
|
|
|
HINWEIS AN MICH SELBST: In den Meldungstexten stehen hier bewusst
|
|
KEINE typografischen Anfuehrungszeichen. Ein oeffnendes Zeichen, das
|
|
versehentlich mit einem geraden geschlossen wird, beendet den String
|
|
mitten im Satz -- das ist mir beim Schreiben dieser Datei zweimal
|
|
passiert. In Kommentaren sind sie harmlos, in Strings nicht.
|
|
|
|
Aufrufen mit: node server/pruef-jeder-hat-eine-seite.mjs
|
|
===================================================================== */
|
|
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 { eigenerPort } from "./helfer-port.mjs";
|
|
|
|
const PORT = await eigenerPort(import.meta, "pruef-jeder-hat-eine-seite");
|
|
const ordner = mkdtempSync(join(tmpdir(), "ws-seite-"));
|
|
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(150_000, "pruef-jeder-hat-eine-seite");
|
|
await new Promise((r) => setTimeout(r, 700));
|
|
|
|
process.on("uncaughtException", (f) => { console.error("ABSTURZ:", f); process.exit(7); });
|
|
|
|
let fehler = 0, geprueft = 0;
|
|
const melde = (t) => console.log(t);
|
|
const ok = (b, t) => { geprueft++; console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
|
|
|
const CREW = "crew.dogfather-universe.com";
|
|
const WURZEL = join(import.meta.dirname, "..");
|
|
|
|
function roh(pfad, host, kopf = {}, koerper = null) {
|
|
return new Promise((fertig, schief) => {
|
|
const a = httpAnfrage({
|
|
host: "127.0.0.1", port: PORT, path: pfad,
|
|
method: koerper ? "POST" : "GET",
|
|
headers: { Host: host, ...(koerper ? { "Content-Type": "application/json" } : {}), ...kopf },
|
|
}, (antwort) => {
|
|
let text = "";
|
|
antwort.on("data", (s) => { text += s; });
|
|
antwort.on("end", () => fertig({ code: antwort.statusCode, text, kopf: antwort.headers }));
|
|
});
|
|
a.on("error", schief);
|
|
if (koerper) a.write(JSON.stringify(koerper));
|
|
a.end();
|
|
});
|
|
}
|
|
const json = (a) => { try { return JSON.parse(a.text); } catch { return {}; } };
|
|
|
|
const { DatabaseSync } = await import("node:sqlite");
|
|
const { scryptSync, randomBytes } = await import("node:crypto");
|
|
|
|
/* Eine Person je Rolle, die im Treff vorkommt -- plus eine aus der
|
|
Agentur als Gegenprobe. */
|
|
const LEUTE = [
|
|
["Filipe", "admin", "CODE-DOGI-0001"],
|
|
["Rieke", "hand", "CODE-HAND-0001"],
|
|
["Kessi", "modi", "CODE-MODI-0001"],
|
|
["Mara", "gast", "CODE-GAST-0001"],
|
|
["Nico", "creator", "CODE-CREA-0001"],
|
|
];
|
|
{
|
|
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
|
for (const [name, rolle, code] of LEUTE) {
|
|
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, aktiv, erstellt)"
|
|
+ " VALUES (?,?,?,?,?,1,?)")
|
|
.run(name, rolle, hash, salz, 32768, new Date().toISOString());
|
|
}
|
|
d.close();
|
|
}
|
|
|
|
async function anmelden(rolle, code, host) {
|
|
let a = await roh("/workspace/api/anmelden", host, {}, { rolle, code });
|
|
if (a.code === 400 && a.text.includes("alter_offen")) {
|
|
a = await roh("/workspace/api/anmelden", host, {}, { rolle, code, alter_ok: true });
|
|
}
|
|
return [].concat(a.kopf["set-cookie"] || []).map((z) => z.split(";")[0]).join("; ");
|
|
}
|
|
|
|
/* WELCHE ADRESSE GEHOERT ZU WELCHER ROLLE. Die Trennung der beiden
|
|
Haeuser ist eine Hausregel, keine Nebensache: Team Dogi und die
|
|
Community leben auf crew.*, die Agentur auf workspace.*. Wer das in
|
|
einer Pruefung uebersieht, misst eine Schranke und nennt sie Fehler. */
|
|
const AGENTUR = "workspace.dogfather-universe.com";
|
|
const HAUS_VON = {
|
|
admin: CREW, hand: CREW, modi: CREW, gast: CREW,
|
|
creator: AGENTUR, scout: AGENTUR, manager: AGENTUR, spicy: AGENTUR,
|
|
};
|
|
|
|
const kekse = {};
|
|
for (const [, rolle, code] of LEUTE) kekse[rolle] = await anmelden(rolle, code, HAUS_VON[rolle]);
|
|
|
|
/* =======================================================================
|
|
1. JEDER KOMMT AUF SEINE EIGENE SEITE
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 1. Die Tuer zum Steckbrief ===");
|
|
{
|
|
/* JEDE ROLLE AUF IHRER EIGENEN ADRESSE.
|
|
|
|
Der erste Lauf hat hier einen echten Unterschied sichtbar gemacht:
|
|
Ein Creator bekommt auf crew.dogfather-universe.com eine 302 --
|
|
die Hausregel trennt Agentur und Team Dogi, und das ist richtig
|
|
so. Haette ich die Pruefung nicht danach getrennt, haette ich ein
|
|
funktionierendes Schloss fuer einen Fehler gehalten. */
|
|
for (const [name, rolle] of LEUTE) {
|
|
const haus = HAUS_VON[rolle];
|
|
const a = await roh("/workspace/steckbrief.html", haus, { cookie: kekse[rolle] });
|
|
ok(a.code === 200, `${name} kommt auf den Steckbrief (${a.code}, ${haus})`);
|
|
}
|
|
const b = await roh("/workspace/steckbrief.html", CREW);
|
|
ok(b.code === 302 || b.code === 401 || b.code === 403,
|
|
`ohne Anmeldung nicht (${b.code})`);
|
|
}
|
|
|
|
melde("");
|
|
melde("=== 2. Und bekommt dort seinen eigenen Eintrag ===");
|
|
{
|
|
for (const [name, rolle] of LEUTE) {
|
|
const a = json(await roh("/workspace/api/steckbrief/mein", HAUS_VON[rolle], { cookie: kekse[rolle] }));
|
|
ok(a.steckbrief?.name === name,
|
|
`${name} sieht seinen eigenen (${a.steckbrief?.name})`);
|
|
/* DAS FELD FUERS BILD MUSS DA SEIN -- sonst gibt es nichts
|
|
hochzuladen, und Filipes Satz (genau wie ich foto und so
|
|
hinzufuegen) waere nicht erfuellt. */
|
|
ok("bild" in (a.steckbrief || {}), " und hat ein Feld fuers Bild");
|
|
ok(Array.isArray(a.kanaele) && a.kanaele.length > 0,
|
|
` und ${a.kanaele?.length} Kanaele zum Eintragen`);
|
|
}
|
|
}
|
|
|
|
/* =======================================================================
|
|
3. NIEMAND FAELLT AUS DER UEBERSICHT
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 3. Jede Rolle hat einen Abschnitt ===");
|
|
{
|
|
const { ROLLEN_NAME } = await import("./workspace.js");
|
|
const alleRollen = Object.keys(ROLLEN_NAME);
|
|
ok(alleRollen.length >= 6, `es gibt ${alleRollen.length} Rollen im Haus`);
|
|
|
|
const a = json(await roh("/workspace/api/steckbrief", CREW, { cookie: kekse.admin }));
|
|
const liste = a.steckbriefe || [];
|
|
/* NICHT ALLE FUENF -- UND DAS IST RICHTIG SO.
|
|
|
|
Der erste Lauf erwartete hier fuenf und bekam vier. Der Fehler lag
|
|
in der Erwartung: Auf der Crew-Adresse gehoert der Creator nicht
|
|
dazu, die beiden Haeuser sind getrennt. Haette ich die Zahl auf
|
|
`>= 1` gesetzt, waere die Pruefung gruen geworden und haette
|
|
nichts mehr gemessen -- also wird genau das geprueft, was gelten
|
|
soll: die vier aus dem Haus, und der Creator NICHT. */
|
|
const drin = liste.map((z) => z.name).sort();
|
|
ok(drin.length === 4 && !drin.includes("Nico"),
|
|
`auf der Crew-Adresse sieht DogFather die vier des Hauses: ${drin.join(", ")}`);
|
|
|
|
let ohneAbschnitt = 0;
|
|
const unterWeitere = [];
|
|
for (const p of liste) {
|
|
if (!p.abschnitt) ohneAbschnitt++;
|
|
else if (p.abschnitt === "Weitere") unterWeitere.push(`${p.name} (${p.rolle})`);
|
|
}
|
|
ok(liste.length > 0 && ohneAbschnitt === 0,
|
|
`alle ${liste.length} haben einen Abschnitt (${ohneAbschnitt} ohne)`);
|
|
ok(unterWeitere.length === 0,
|
|
`keiner landet im Sammelplatz Weitere${unterWeitere.length ? ": " + unterWeitere.join(", ") : ""}`);
|
|
|
|
/* JEDE ROLLE DES HAUSES, nicht nur die fuenf angelegten Leute.
|
|
Sonst waere dieser Abschnitt eine Aussage ueber meine Testdaten. */
|
|
const { abschnittFuer } = await import("./workspace-steckbrief.js");
|
|
const vergessen = alleRollen.filter((r) => abschnittFuer(r).name === "Weitere");
|
|
ok(vergessen.length === 0,
|
|
`alle ${alleRollen.length} Rollen sind zugeordnet${vergessen.length ? " -- fehlt: " + vergessen.join(", ") : ""}`);
|
|
|
|
/* GEGENPROBE. Ohne sie waere "niemand faellt heraus" auch dann wahr,
|
|
wenn gar nichts zugeordnet wuerde. */
|
|
ok(abschnittFuer("gibtsnicht").name === "Weitere",
|
|
"eine unbekannte Rolle landet sichtbar im Sammelplatz, statt zu verschwinden");
|
|
|
|
/* UND KEINE ROLLENNAMEN DER COMMUNITY IM BROWSER. */
|
|
const jsDatei = readFileSync(join(WURZEL, "workspace/assets/js/steckbrief.js"), "utf8");
|
|
let gefunden = [];
|
|
for (const wort of ["'modi'", "'hand'", "'gast'", '"modi"', '"hand"', '"gast"']) {
|
|
if (jsDatei.includes(wort)) gefunden.push(wort);
|
|
}
|
|
ok(gefunden.length === 0,
|
|
`keine Rollennamen in der ausgelieferten Datei${gefunden.length ? ": " + gefunden.join(", ") : ""}`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
4. DIE REIHENFOLGE DER GRUPPEN
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 4. Oben die Arbeit, dann Moderation, dann Community ===");
|
|
{
|
|
const { bereicheFuer } = await import("./workspace.js");
|
|
|
|
const folgeVon = (rolle) => {
|
|
const k = bereicheFuer({ rolle });
|
|
if (!Array.isArray(k)) return null;
|
|
const gesehen = [];
|
|
for (const x of k) if (!gesehen.includes(x.gruppe)) gesehen.push(x.gruppe);
|
|
return gesehen;
|
|
};
|
|
|
|
const modi = folgeVon("modi");
|
|
ok(!!modi?.includes("Moderation"),
|
|
`ein Modi hat eine eigene Moderationsgruppe (${modi?.join(" | ")})`);
|
|
ok(!!modi?.includes("Community"), "und die Community daneben");
|
|
/* SIE MUESSEN GETRENNT SEIN -- vorher trug die Moderationskachel
|
|
dieselbe Gruppe wie die Bretter und verschwand darin. */
|
|
ok(modi?.indexOf("Moderation") !== modi?.indexOf("Community"),
|
|
"und beide sind wirklich zwei Gruppen, nicht eine");
|
|
|
|
const gast = folgeVon("gast");
|
|
ok(!!gast?.includes("Community"), `ein Mitglied hat den Treff (${gast?.join(" | ")})`);
|
|
ok(!!gast?.includes("Für dich"), "und eine eigene Seite");
|
|
/* Was sie nicht sehen, sehen sie nicht. */
|
|
ok(!gast?.includes("Moderation"), "aber KEINE Moderation");
|
|
ok(!gast?.includes("Täglich"), "und keine Arbeitsgruppe des Teams");
|
|
|
|
/* DIE SORTIERREGEL IM BROWSER MUSS DIESELBEN NAMEN KENNEN. Eine
|
|
Gruppe, die dort fehlt, landet bei den Arbeitsgruppen -- fuer die
|
|
Community waere das genau falsch herum. */
|
|
const startJs = readFileSync(join(WURZEL, "workspace/assets/js/start.js"), "utf8");
|
|
const zuletzt = /GRUPPEN_ZULETZT\s*=\s*\[([^\]]*)\]/s.exec(startJs)?.[1] || "";
|
|
ok(zuletzt.length > 0, "die Sortierliste steht in start.js");
|
|
for (const g of ["Moderation", "Community", "Für dich"]) {
|
|
ok(zuletzt.includes(g), `${g} steht in der Liste am Ende`);
|
|
}
|
|
/* UND DIE ARBEITSGRUPPEN STEHEN NICHT DARIN. Sie behalten ihre
|
|
gewachsene Reihenfolge -- eine Liste, die auch sie festschreibt,
|
|
ueberschreibt bestehende Entscheidungen, ohne sie gelesen zu haben. */
|
|
for (const g of ["Täglich", "Rund ums Live", "Rund um den Creator"]) {
|
|
ok(!zuletzt.includes(g), `${g} wird NICHT ans Ende geschoben`);
|
|
}
|
|
|
|
/* JEDE GRUPPE, DIE ES GIBT, MUSS IN EINER DER BEIDEN LISTEN STEHEN.
|
|
Sonst landet sie stillschweigend bei der Arbeit -- was fuer die
|
|
meisten richtig ist, aber nicht fuer alle. Diese Zeile findet die
|
|
naechste neue Gruppe, bevor sie an der falschen Stelle auftaucht. */
|
|
const alleGruppen = new Set();
|
|
for (const rolle of ["admin", "hand", "modi", "gast"]) {
|
|
for (const g of folgeVon(rolle) || []) alleGruppen.add(g);
|
|
}
|
|
ok(alleGruppen.size >= 5,
|
|
`${alleGruppen.size} Gruppen im Haus: ${[...alleGruppen].join(", ")}`);
|
|
}
|
|
|
|
/* =======================================================================
|
|
5. IM BROWSER -- steht die Kachel da, und in welcher Reihenfolge?
|
|
======================================================================= */
|
|
melde("");
|
|
melde("=== 5. Im Browser ===");
|
|
{
|
|
let pw = null;
|
|
try {
|
|
pw = await import("file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
|
} catch { /* dritter Ausgang */ }
|
|
|
|
if (!pw) {
|
|
melde(" -- Playwright ist nicht da -- im Browser wurde NICHT nachgesehen.");
|
|
melde(" Das ist kein Befund. Die Abschnitte 1-4 gelten weiter.");
|
|
} else {
|
|
const browser = await pw.chromium.launch();
|
|
try {
|
|
for (const [name, rolle] of [["Mara", "gast"], ["Kessi", "modi"], ["Filipe", "admin"]]) {
|
|
const ctx = await browser.newContext({ viewport: { width: 1400, height: 1000 } });
|
|
await ctx.addCookies(kekse[rolle].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 ctx.newPage();
|
|
const kaputt = [];
|
|
seite.on("pageerror", (e) => kaputt.push(String(e).slice(0, 140)));
|
|
|
|
await seite.goto(`http://127.0.0.1:${PORT}/workspace/start.html`, { waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(900);
|
|
|
|
const gruppen = await seite.evaluate(() =>
|
|
[...document.querySelectorAll(".gruppe")].map((g) =>
|
|
(g.querySelector("h2, .gruppe__name")?.textContent || "").trim()).filter(Boolean));
|
|
|
|
melde(` --- ${name} sieht: ${gruppen.join(" | ")}`);
|
|
ok(gruppen.length > 0, `${name}: es werden Gruppen angezeigt (${gruppen.length})`);
|
|
|
|
const hatSteckbrief = await seite.evaluate(() =>
|
|
!!document.querySelector('a[href*="steckbrief.html"]'));
|
|
ok(hatSteckbrief, `${name}: die Steckbrief-Kachel ist da`);
|
|
|
|
const platz = (n) => gruppen.findIndex((g) => g.includes(n));
|
|
const pFuer = platz("Für dich"), pCom = platz("Community"), pMod = platz("Moderation");
|
|
if (pFuer >= 0 && pCom >= 0) {
|
|
ok(pFuer > pCom, ` das Eigene steht hinter der Community (${pFuer} > ${pCom})`);
|
|
}
|
|
if (pMod >= 0 && pCom >= 0) {
|
|
ok(pMod < pCom, ` die Moderation steht VOR der Community (${pMod} < ${pCom})`);
|
|
}
|
|
if (pMod >= 0) {
|
|
const pArbeit = gruppen.findIndex((g) => /Täglich|Rund ums Live|Rund um das Team/.test(g));
|
|
if (pArbeit >= 0) {
|
|
ok(pArbeit < pMod, ` die eigene Arbeit steht ganz oben (${pArbeit} < ${pMod})`);
|
|
}
|
|
}
|
|
if (rolle === "gast") {
|
|
ok(pMod === -1, " ein Mitglied sieht KEINE Moderationsgruppe");
|
|
}
|
|
|
|
await seite.goto(`http://127.0.0.1:${PORT}/workspace/steckbrief.html`,
|
|
{ waitUntil: "networkidle" });
|
|
await seite.waitForTimeout(900);
|
|
const eigen = await seite.evaluate(() => ({
|
|
name: document.getElementById("sb-name")?.textContent?.trim() || "",
|
|
hatDatei: !!document.getElementById("sb-datei"),
|
|
hatSpeichern: !!document.getElementById("sb-speichern"),
|
|
abschnitte: [...document.querySelectorAll(".team-gruppe__name")].map((x) => x.textContent.trim()),
|
|
}));
|
|
ok(eigen.name === name, ` sein Steckbrief zeigt den Namen (${eigen.name})`);
|
|
ok(eigen.hatDatei && eigen.hatSpeichern,
|
|
" mit Feld fuers Bild und Speichern-Knopf");
|
|
melde(` Abschnitte dort: ${eigen.abschnitte.join(", ") || "(keine)"}`);
|
|
ok(kaputt.length === 0,
|
|
` kein Skriptfehler${kaputt.length ? ": " + kaputt.join(" | ") : ""}`);
|
|
await ctx.close();
|
|
}
|
|
} finally {
|
|
await browser.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
melde("");
|
|
melde(`${geprueft} Pruefungen, ${fehler} Fehler`);
|
|
melde(fehler ? "NICHT IN ORDNUNG" : "ALLES IN ORDNUNG");
|
|
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* Rest */ }
|
|
process.exit(fehler ? 1 : 0);
|