Im Code stand die Begruendung selbst: "«Review» allein sagt einem Neuen nichts." Das Wort ist englisch, ein Hauptwort ohne Handlung, und es verraet nicht, WER jetzt dran ist. "Zur Freigabe" sagt beides: fertig von mir, wartet auf jemanden. Geaendert wurde nur, was ein Mensch LIEST -- der Zustandsschluessel bleibt `review`, der gehoert der Datenbank. Betroffen: Spalte und Weiterknopf im Aufgabenbrett, Dateien-Filter, Startseiten-Zaehler, Kachel-Unterzeile, Hinweis "Datei wartet auf Freigabe", Report. NICHT geaendert: der Kalender. Dort ist `review` eine TERMINART (ein Gespraech, in dem man zurueckschaut), kein Zustand. Ich hatte das beim Umbenennen selbst verwechselt und wieder zurueckgenommen -- ein Kommentar an der Stelle haelt die zwei Bedeutungen jetzt auseinander. pruef-sprung hing an der Wortwahl (`/review/i` auf der Beschriftung) und wurde rot, obwohl der Filter richtig stand. Sie prueft jetzt `data-status` -- den Schluessel, der sich nicht mit der Sprache aendert. DAZU, unabhaengig gefunden: Datumsfelder waren 48 px hoch, alle anderen Felder 44. Gemessen auf drei Seiten bei 390 px. Alle liegen auf dem 44-px-Beruehrziel -- nur das Datumsfeld drueckte sich darueber, weil Chromium in `::-webkit-datetime-edit` eine eigene Innenpolsterung setzt, die sogar ein gesetztes `height: 44px` ueberstimmt. Zwei Anteile, einzeln nachgemessen (jeder allein 48->46, erst beide zusammen 48->44): 1 px Polsterung oben und unten im Feldkasten, und eine Zeilenhoehe von 24 statt 22. Keine feste Hoehe gesetzt -- die Zeilenhoehe wird aus Beruehrziel und Polsterung gerechnet, damit sie mitwandert, wenn sich eines davon aendert. Geprueft: pruef-formulare 19/0 (war 16 mit 3 Fehlern), pruef-sprung 43/0, pruef-start-ansicht 151/0, pruef-aufgabenbrett 49/0, pruef-uebersicht 35/0, pruef-uebersicht-browser 20/0, pruef-deutsche-texte, pruef-css-klassen. Ausserdem: vorlagen.js geloescht (8,2 KB). `vorlagenBlock(` wurde inca104799eingebaut und in9267797dwieder ausgebaut -- seither laedt die Datei auf zwei Seiten, ohne dass jemand sie aufruft. Co-Authored-By: Claude Opus 5 <[email protected]>
369 lines
18 KiB
JavaScript
369 lines
18 KiB
JavaScript
/* Prueft den Sprung vom Bericht zu der Stelle, die eine Zahl zaehlt.
|
||
|
||
Wunsch vom 01.09.2026: "auf dieser Seite will ich, dass wenn man auf
|
||
die Kisten drueckt, sofort zu der Seite -- diesem Punkt --
|
||
weitergeschickt wird, wo man draufgedrueckt hat."
|
||
|
||
WAS HIER WIRKLICH SCHIEFGEHEN KANN, und warum diese Pruefung so
|
||
ausfuehrlich ist:
|
||
|
||
1. Ein Verweis, der irgendwohin fuehrt. "aufgaben.html" statt
|
||
"aufgaben.html?zeigen=ueberfaellig" faellt niemandem auf -- man
|
||
landet ja auf der richtigen Seite. Nur eben nicht an der Stelle.
|
||
Geprueft wird deshalb die ADRESSE jeder einzelnen Kiste.
|
||
|
||
2. Ein Ausschnitt, aus dem man nicht mehr herauskommt. Bleibt die
|
||
Zielseite gefiltert, sieht man beim naechsten Besuch drei von
|
||
zwanzig Aufgaben und haelt das fuer den ganzen Bestand. Geprueft
|
||
wird, dass "Alles zeigen" wirklich alles zurueckbringt UND den
|
||
Parameter aus der Adresse nimmt.
|
||
|
||
3. Eine Hervorhebung, die das Falsche trifft. Deshalb wird gezaehlt:
|
||
Die Zahl der hervorgehobenen Karten muss zu dem passen, was
|
||
angelegt wurde -- nicht "mehr als null".
|
||
|
||
4. Eine Pruefung, die immer bestaetigt. Zu jedem Sprung gehoert
|
||
deshalb eine GEGENPROBE: dieselbe Seite OHNE Parameter darf
|
||
nichts hervorheben und keine Leiste zeigen. */
|
||
|
||
import { mkdtempSync, rmSync } from "node:fs";
|
||
import { tmpdir } from "node:os";
|
||
import { join } from "node:path";
|
||
|
||
const ordner = mkdtempSync(join(tmpdir(), "ws-sprung-"));
|
||
process.env.WORKSPACE_DB = join(ordner, "workspace.db");
|
||
/* DER PORT MUSS MEINER SEIN (nachgeruestet 19.09.2026).
|
||
Ohne diese Zeile startet der eigene Server bei belegtem Port
|
||
STILL nicht -- und alles Folgende misst gegen einen fremden
|
||
Stand. Das ist der dritte Ausgang: belegt ist etwas anderes
|
||
als kaputt. */
|
||
await portMussFreiSein(4212, "pruef-sprung");
|
||
process.env.PORT = "4212";
|
||
process.env.SITE_ACCESS_SECRET = "lokaler-test";
|
||
process.env.SITE_ACCESS_CODE_DOGI = "DOGI-TEST-1";
|
||
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";
|
||
import { portMussFreiSein } from "./helfer-port.mjs";
|
||
await import("./index.js");
|
||
/* DIE NOTBREMSE. Muss NACH index.js stehen: Der Server haengt dort
|
||
zwei Auffangnetze ein, die einen Absturz nur protokollieren --
|
||
richtig fuer den Betrieb, fatal fuer eine Pruefung. Ohne diese
|
||
Zeile bleibt der Prozess nach einem Fehler ewig stehen, weil der
|
||
Server ihn am Leben haelt (siehe helfer-notbremse.mjs). */
|
||
notbremse(480_000, "pruef-sprung");
|
||
await new Promise((r) => setTimeout(r, 700));
|
||
const BASIS = "http://127.0.0.1:4212";
|
||
setTimeout(() => { console.log("ABBRUCH"); process.exit(1); }, 300_000).unref?.();
|
||
|
||
/* ORTSZEIT, nicht UTC. Die Anwendung rechnet "heute" nach der Uhr des
|
||
Benutzers; toISOString() liefert dagegen UTC und damit zwischen
|
||
Mitternacht und 2 Uhr den VORTAG. In diesem Fenster schlug diese
|
||
Pruefung fehl, ohne dass etwas kaputt war. */
|
||
const tagText = (d = new Date()) =>
|
||
`${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`
|
||
+ `-${String(d.getDate()).padStart(2, "0")}`;
|
||
|
||
let fehler = 0;
|
||
const ok = (b, t) => { console.log((b ? " ok " : " FEHL ") + t); if (!b) fehler++; };
|
||
|
||
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");
|
||
for (let i = 0; i < 60; i++) {
|
||
try {
|
||
const p = new DatabaseSync(process.env.WORKSPACE_DB);
|
||
try { p.prepare("SELECT 1 FROM personen LIMIT 1").get(); break; } finally { p.close(); }
|
||
} catch { await new Promise((r) => setTimeout(r, 250)); }
|
||
}
|
||
const d = new DatabaseSync(process.env.WORKSPACE_DB);
|
||
const jetzt = new Date().toISOString();
|
||
const heute = tagText();
|
||
const gestern = tagText(new Date(Date.now() - 86400_000));
|
||
const morgen = tagText(new Date(Date.now() + 86400_000));
|
||
|
||
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, aktiv, erstellt) VALUES (?,?,?,?,?,1,?)")
|
||
.run(name, rolle, hash, salt, 32768, jetzt);
|
||
return d.prepare("SELECT last_insert_rowid() AS id").get().id;
|
||
}
|
||
const idDogi = anlegen("Filipe", "admin", "CODE-DOGI-0001");
|
||
const idTili = anlegen("Tili", "creator", "CODE-ANNA-0001");
|
||
|
||
/* ---- Ein Bestand mit BEKANNTEN Zahlen. Nur so kann die Pruefung
|
||
sagen "genau drei" statt "mehr als null". ---- */
|
||
const AUFG = { offen: 3, arbeit: 1, review: 2, erledigt: 4 };
|
||
let nr = 0;
|
||
for (const [status, anzahl] of Object.entries(AUFG)) {
|
||
for (let i = 0; i < anzahl; i++) {
|
||
d.prepare(`INSERT INTO aufgaben
|
||
(titel, beschreibung, status, prioritaet, creator_id, verantwortlich_id, erstellt, erstellt_von)
|
||
VALUES (?,?,?,?,?,?,?,?)`)
|
||
.run(`Aufgabe ${++nr} (${status})`, "", status, "mittel", idTili, idDogi, jetzt, idDogi);
|
||
}
|
||
}
|
||
/* Zwei ueberfaellige -- eine offene und eine in Arbeit, damit die
|
||
Hervorhebung SPALTENUEBERGREIFEND geprueft wird. Genau da bricht eine
|
||
Umsetzung, die nur in einer Spalte sucht. */
|
||
for (const status of ["offen", "arbeit"]) {
|
||
d.prepare(`INSERT INTO aufgaben
|
||
(titel, beschreibung, status, prioritaet, frist, creator_id, verantwortlich_id, erstellt, erstellt_von)
|
||
VALUES (?,?,?,?,?,?,?,?,?)`)
|
||
.run(`Laengst faellig (${status})`, "", status, "hoch", gestern, idTili, idDogi, jetzt, idDogi);
|
||
}
|
||
const UEBERFAELLIG = 2;
|
||
/* Eine Frist in der Zukunft und eine erledigte mit alter Frist: Beide
|
||
duerfen NICHT als ueberfaellig gelten. Ohne sie wuerde eine Umsetzung
|
||
durchgehen, die schlicht jede Karte mit Frist hervorhebt. */
|
||
d.prepare(`INSERT INTO aufgaben (titel, beschreibung, status, prioritaet, frist,
|
||
creator_id, verantwortlich_id, erstellt, erstellt_von) VALUES (?,?,?,?,?,?,?,?,?)`)
|
||
.run("Frist erst morgen", "", "offen", "mittel", morgen, idTili, idDogi, jetzt, idDogi);
|
||
d.prepare(`INSERT INTO aufgaben (titel, beschreibung, status, prioritaet, frist,
|
||
creator_id, verantwortlich_id, erstellt, erstellt_von) VALUES (?,?,?,?,?,?,?,?,?)`)
|
||
.run("Alte Frist, aber fertig", "", "erledigt", "mittel", gestern, idTili, idDogi, jetzt, idDogi);
|
||
AUFG.offen += 2; /* die beiden neuen offenen */
|
||
AUFG.arbeit += 1;
|
||
AUFG.erledigt += 1;
|
||
|
||
/* Bereichseintraege: zwei dringende, einer harmlos. */
|
||
for (const [titel, dringlich, status] of [
|
||
["Ton bricht ab", "hoch", "offen"],
|
||
["Licht zu dunkel", "hoch", "offen"],
|
||
["Kleinigkeit", "niedrig", "offen"],
|
||
["Schon erledigt", "hoch", "erledigt"],
|
||
]) {
|
||
d.prepare(`INSERT INTO eintraege
|
||
(bereich, art, titel, text, status, dringlichkeit, datum, creator_id, erstellt, erstellt_von)
|
||
VALUES ('live','vorbereitung',?,?,?,?,?,?,?,?)`)
|
||
.run(titel, "", status, dringlich, heute, idTili, jetzt, idDogi);
|
||
}
|
||
const DRINGEND_LIVE = 2;
|
||
const OFFEN_LIVE = 3;
|
||
d.close();
|
||
|
||
/* ---------------------------------------------------------------- */
|
||
|
||
const { chromium } = await import(
|
||
"file:///C:/Users/qciga/Documents/Obelix/Analyse/node_modules/playwright/index.mjs");
|
||
const browser = await chromium.launch();
|
||
const kontext = await browser.newContext({ viewport: { width: 1280, height: 1000 } });
|
||
const konsole = [];
|
||
const seite = await kontext.newPage();
|
||
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 });
|
||
|
||
/* ================================================================
|
||
1. Die Kisten im Bericht sind Wege -- mit dem richtigen Ziel
|
||
================================================================ */
|
||
console.log("\n=== Die Kisten im Bericht fuehren irgendwohin");
|
||
await seite.goto(BASIS + "/workspace/report.html", { waitUntil: "networkidle" });
|
||
await seite.waitForTimeout(1400);
|
||
|
||
const kisten = await seite.evaluate(() => [...document.querySelectorAll(".kacheln .kachel")]
|
||
.map((k) => ({
|
||
name: k.querySelector(".kachel__name")?.textContent?.trim(),
|
||
zahl: k.querySelector(".kachel__zahl")?.textContent?.trim(),
|
||
ziel: k.tagName === "A" ? k.getAttribute("href") : null,
|
||
beschriftung: k.getAttribute("aria-label") || "",
|
||
})));
|
||
|
||
ok(kisten.length >= 10, `${kisten.length} Kennzahlen auf der Seite`);
|
||
const mitWeg = kisten.filter((k) => k.ziel);
|
||
ok(mitWeg.length >= 8, ` davon ${mitWeg.length} mit einem Weg dahinter`);
|
||
|
||
/* Die wichtigsten einzeln -- ein Verweis auf die richtige SEITE, aber
|
||
ohne Stelle, waere sonst nicht zu unterscheiden. */
|
||
const soll = [
|
||
["Aufgaben erledigt", "aufgaben.html?zeigen=erledigt"],
|
||
["Aufgaben offen", "aufgaben.html?zeigen=offen"],
|
||
["überfällig", "aufgaben.html?zeigen=ueberfaellig"],
|
||
/* Heisst seit dem 20.09.2026 so -- der Schluessel bleibt `review`,
|
||
das Wort auf dem Bildschirm nicht. */
|
||
["Dateien warten auf Freigabe", "dateien.html?zeigen=review"],
|
||
["Dateien freigegeben", "dateien.html?zeigen=frei"],
|
||
["Termine gewesen", "kalender.html?zeigen=vergangen"],
|
||
["Termine stehen an", "kalender.html?zeigen=kommend"],
|
||
["dringende Punkte", "bereich.html?b=live&zeigen=dringend"],
|
||
];
|
||
for (const [name, ziel] of soll) {
|
||
const k = kisten.find((x) => x.name === name);
|
||
ok(k?.ziel === ziel, ` "${name}" fuehrt auf ${k?.ziel || "NIRGENDWOHIN"}`);
|
||
}
|
||
ok(kisten.every((k) => !k.ziel || /ansehen/.test(k.beschriftung)),
|
||
" jeder Weg hat einen Satz fuer Vorleseprogramme");
|
||
|
||
/* GEGENPROBE: Eine Kiste ohne Inhalt darf NICHT verlinkt sein. Ein Weg
|
||
zu null Dingen ist eine Enttaeuschung, kein Angebot. Geprueft an
|
||
"Content: neue Eintraege" -- dort wurde nichts angelegt. */
|
||
const leer = kisten.find((k) => k.name === "LIVE-Schnitt aus 0 Bewertung(en)");
|
||
ok(leer && !leer.ziel, ` Gegenprobe: "${leer?.name}" fuehrt bewusst nirgendwohin`);
|
||
|
||
/* ================================================================
|
||
2. Aufgabenbrett: hervorheben, nicht filtern
|
||
================================================================ */
|
||
console.log("\n=== Der Sprung auf das Aufgabenbrett");
|
||
for (const [was, erwartet, wort] of [
|
||
["offen", AUFG.offen, "offen"],
|
||
["review", AUFG.review, "Freigabe"],
|
||
["erledigt", AUFG.erledigt, "erledigt"],
|
||
["ueberfaellig", UEBERFAELLIG, "Frist"],
|
||
]) {
|
||
await seite.goto(`${BASIS}/workspace/aufgaben.html?zeigen=${was}`, { waitUntil: "networkidle" });
|
||
await seite.waitForTimeout(900);
|
||
const r = await seite.evaluate(() => ({
|
||
treffer: document.querySelectorAll('.karte[data-treffer="ja"]').length,
|
||
/* ALLE Karten muessen weiterhin da sein -- das Brett lebt davon,
|
||
dass man die vier Spalten nebeneinander sieht. */
|
||
karten: document.querySelectorAll(".karte").length,
|
||
leiste: !!document.querySelector(".ziel-leiste"),
|
||
text: document.querySelector(".ziel-leiste__text")?.textContent || "",
|
||
marke: document.querySelector(".ziel-leiste__marke")?.textContent || "",
|
||
}));
|
||
ok(r.treffer === erwartet,
|
||
`?zeigen=${was}: ${r.treffer} hervorgehoben (erwartet ${erwartet}), ${r.karten} Karten insgesamt`);
|
||
ok(r.leiste && r.marke === String(erwartet), ` die Leiste sagt "${r.marke}"`);
|
||
ok(new RegExp(wort, "i").test(r.text), ` und erklaert es ("${r.text}")`);
|
||
ok(r.karten > erwartet, " es wird hervorgehoben, NICHT gefiltert");
|
||
}
|
||
|
||
/* Der Weg zurueck. Ohne ihn bliebe die Seite fuer immer markiert. */
|
||
const zurueck = await seite.evaluate(async () => {
|
||
document.querySelector(".ziel-leiste__weg").click();
|
||
await new Promise((r) => setTimeout(r, 250));
|
||
return {
|
||
treffer: document.querySelectorAll('[data-treffer="ja"]').length,
|
||
leiste: !!document.querySelector(".ziel-leiste"),
|
||
adresse: location.search,
|
||
};
|
||
});
|
||
ok(zurueck.treffer === 0 && !zurueck.leiste, '"Alles zeigen" nimmt die Hervorhebung zurueck');
|
||
ok(!zurueck.adresse.includes("zeigen"),
|
||
` und raeumt die Adresse mit auf ("${zurueck.adresse}") – sonst kaeme sie beim Neuladen wieder`);
|
||
|
||
/* GEGENPROBE: ohne Parameter darf nichts markiert sein. */
|
||
await seite.goto(BASIS + "/workspace/aufgaben.html", { waitUntil: "networkidle" });
|
||
await seite.waitForTimeout(900);
|
||
const ohne = await seite.evaluate(() => ({
|
||
treffer: document.querySelectorAll('[data-treffer="ja"]').length,
|
||
leiste: !!document.querySelector(".ziel-leiste"),
|
||
karten: document.querySelectorAll(".karte").length,
|
||
}));
|
||
ok(ohne.treffer === 0 && !ohne.leiste && ohne.karten > 0,
|
||
`Gegenprobe: ohne Parameter nichts hervorgehoben (${ohne.karten} Karten, ${ohne.treffer} markiert)`);
|
||
|
||
/* Und ein unbekannter Wert darf ebenfalls nichts tun -- nicht abstuerzen
|
||
und nicht eine leere Leiste zeigen. */
|
||
await seite.goto(BASIS + "/workspace/aufgaben.html?zeigen=quatsch", { waitUntil: "networkidle" });
|
||
await seite.waitForTimeout(900);
|
||
const quatsch = await seite.evaluate(() => ({
|
||
leiste: !!document.querySelector(".ziel-leiste"),
|
||
karten: document.querySelectorAll(".karte").length,
|
||
}));
|
||
ok(!quatsch.leiste && quatsch.karten > 0,
|
||
"Gegenprobe: ein unbekannter Wert wird still ignoriert");
|
||
|
||
/* ================================================================
|
||
3. Bereichsseite: dringend hervorheben, offen filtern
|
||
================================================================ */
|
||
console.log("\n=== Der Sprung in die LIVE-Analyse");
|
||
await seite.goto(BASIS + "/workspace/bereich.html?b=live&zeigen=dringend", { waitUntil: "networkidle" });
|
||
await seite.waitForTimeout(1200);
|
||
const dr = await seite.evaluate(() => ({
|
||
treffer: document.querySelectorAll('.eintrag-karte[data-treffer="ja"]').length,
|
||
alle: document.querySelectorAll(".eintrag-karte").length,
|
||
marke: document.querySelector(".ziel-leiste__marke")?.textContent || "",
|
||
}));
|
||
ok(dr.treffer === DRINGEND_LIVE,
|
||
`dringende Eintraege hervorgehoben: ${dr.treffer} von ${dr.alle} (erwartet ${DRINGEND_LIVE})`);
|
||
ok(dr.alle > dr.treffer, " die harmlosen stehen weiterhin da");
|
||
|
||
await seite.goto(BASIS + "/workspace/bereich.html?b=live&zeigen=offen", { waitUntil: "networkidle" });
|
||
await seite.waitForTimeout(1200);
|
||
const of = await seite.evaluate(() => ({
|
||
alle: document.querySelectorAll(".eintrag-karte").length,
|
||
treffer: document.querySelectorAll('.eintrag-karte[data-treffer="ja"]').length,
|
||
gedrueckt: [...document.querySelectorAll("#filter .schritt")]
|
||
.find((b) => b.getAttribute("aria-pressed") === "true")?.textContent,
|
||
}));
|
||
ok(of.alle === OFFEN_LIVE && of.treffer === OFFEN_LIVE,
|
||
`nur die offenen: ${of.alle} gezeigt (erwartet ${OFFEN_LIVE})`);
|
||
ok(of.gedrueckt === "Nur offene",
|
||
` und der Filterknopf zeigt es an ("${of.gedrueckt}") – ein unsichtbarer Ausschnitt waere eine Falle`);
|
||
|
||
/* ================================================================
|
||
4. Dateien: hier wird wirklich gefiltert
|
||
================================================================ */
|
||
console.log("\n=== Der Sprung in die Dateien");
|
||
await seite.goto(BASIS + "/workspace/dateien.html?zeigen=review", { waitUntil: "networkidle" });
|
||
await seite.waitForTimeout(1200);
|
||
const da = await seite.evaluate(() => ({
|
||
leiste: !!document.querySelector(".ziel-leiste"),
|
||
text: document.querySelector(".ziel-leiste__text")?.textContent || "",
|
||
gedrueckt: (() => {
|
||
const b = [...document.querySelectorAll("#filter .schritt")]
|
||
.find((x) => x.getAttribute("aria-pressed") === "true");
|
||
return b ? { schluessel: b.dataset.status || "", wort: b.textContent.trim() } : null;
|
||
})(),
|
||
}));
|
||
/* Es liegt keine Datei da -- und genau dieser Fall ist der wichtige:
|
||
Wer auf eine Zahl klickt und auf einer leeren Seite landet, glaubt,
|
||
er sei falsch abgebogen. Die Leiste muss ihm sagen, dass er richtig
|
||
ist und die Sache erledigt. */
|
||
ok(da.leiste, "auch ohne Treffer erscheint die Leiste");
|
||
ok(/nichts davon da/.test(da.text), ` und sagt, dass hier nichts liegt ("${da.text}")`);
|
||
/* AM SCHLUESSEL PRUEFEN, NICHT AM WORT (20.09.2026). Hier stand
|
||
`/review/i` auf der Beschriftung. Als aus "Review" das deutsche
|
||
"Zur Freigabe" wurde, wurde diese Pruefung rot -- obwohl der Filter
|
||
genau richtig stand. Eine Pruefung, die an der Wortwahl haengt,
|
||
bestraft jede Textverbesserung. `data-status` ist der Schluessel,
|
||
den der Server kennt; der aendert sich nicht mit der Sprache. */
|
||
ok(da.gedrueckt?.schluessel === "review" && (da.gedrueckt?.wort || "").length > 2,
|
||
` der Filter steht auf "${da.gedrueckt?.wort}" (Schluessel: ${da.gedrueckt?.schluessel})`);
|
||
|
||
/* ================================================================
|
||
5. Kalender: schaltet auf die Liste um
|
||
================================================================ */
|
||
console.log("\n=== Der Sprung in den Kalender");
|
||
await seite.goto(BASIS + "/workspace/kalender.html?zeigen=kommend", { waitUntil: "networkidle" });
|
||
await seite.waitForTimeout(1400);
|
||
const ka = await seite.evaluate(() => ({
|
||
ansicht: document.getElementById("kalender")?.dataset.ansicht,
|
||
gedrueckt: [...document.querySelectorAll(".k-ansicht")]
|
||
.find((b) => b.getAttribute("aria-pressed") === "true")?.dataset.ansicht,
|
||
leiste: !!document.querySelector(".ziel-leiste"),
|
||
}));
|
||
ok(ka.ansicht === "liste",
|
||
`der Kalender schaltet auf die Liste um (${ka.ansicht}) – in der Monatsansicht liesse sich `
|
||
+ "vergangen/kommend gar nicht hervorheben");
|
||
ok(ka.gedrueckt === "liste",
|
||
` und der Ansichtsknopf zeigt dasselbe (${ka.gedrueckt}) – sonst behauptet die Leiste "Monat"`);
|
||
ok(ka.leiste, " die Leiste ist da");
|
||
|
||
/* ================================================================ */
|
||
ok(konsole.length === 0,
|
||
`keine Fehler in der Konsole${konsole.length ? " -> " + konsole.join(" | ") : ""}`);
|
||
|
||
await seite.goto(BASIS + "/workspace/report.html", { waitUntil: "networkidle" });
|
||
await seite.waitForTimeout(1200);
|
||
await seite.screenshot({ path: "pruef-sprung.png", fullPage: false });
|
||
|
||
await kontext.close();
|
||
await browser.close();
|
||
try { rmSync(ordner, { recursive: true, force: true }); } catch { /* egal */ }
|
||
console.log(fehler ? `\n${fehler} Prüfung(en) fehlgeschlagen.` : "\nAlles in Ordnung.");
|
||
process.exit(fehler ? 1 : 0);
|