Die Reiter standen neben dem Titel. Das las sich wie eine einzige lange
Zeile, in der der Titel nur der erste von sechs Knöpfen zu sein schien --
man sah nicht auf einen Blick, in welchem Bereich man war. Jetzt zwei
Zeilen: oben WO man ist, darunter WOHIN man kann.
Die Verwaltung öffnete bisher mit der Anfragenliste. Eine Liste ist eine
Ablage: Sie zeigt, WAS es gibt, nicht was zu TUN ist. Neu ist ein
Cockpit, das in drei Stufen antwortet -- was auf mich wartet, was beim
Kunden liegt, wie es ums Geld steht -- und dann laufende Projekte mit
Fortschritt sowie den Verlauf.
Zwei Grundsätze machen die Zahlen brauchbar: Getrennt nach 'wartet auf
mich' und 'wartet auf den Kunden' (zwölf offene Punkte sind entspannt,
wenn elf beim Kunden liegen). Und das ALTER färbt, nicht die Menge --
vier neue Anfragen sind kein Problem, eine seit sechs Tagen liegende
schon. Ein offener Widerruf ist immer rot, weil eine gesetzliche Frist
läuft.
Jede Kachel ist ein echter <button> und führt in den passenden Reiter.
Farbe ist nie der einzige Träger: Neben jedem farbigen Zustand steht der
Text ('älteste seit 8 Tagen').
Drei Fehler dabei gefunden und behoben:
- Der Titel wurde nur beim Klicken gesetzt. Frisch geladen zeigte die
Seite das Cockpit, während darüber noch 'Projektanfragen' stand. Die
Zuordnung Reiter->Titel liegt jetzt ausserhalb des Klick-Zuhörers.
- '.wd h2' überstimmte '.vw-ub-h': 44px Überschrift über 33px Zahl, die
Seite las sich wie ein Plakat. Dieselbe Spezifitätsfalle wie früher
bei '.wd a'.
- Eine Antwort mit ok:true aber ohne Inhalt riss die ganze Verwaltung
mit. Wird jetzt abgefangen -- ein halber Server ist ein realistischer
Fall.
Geprüft: 22 Serverprüfungen gegen eine echte Datenbank (inkl. vier
Fällen, die belegen, dass die Rechteprüfung wirklich greift), 56 im
Browser auf Computer und Handy, 69 im bestehenden Verwaltungstest,
0 Fundstellen im Design-/WCAG-Test, 5 im Farbsystemtest.
Co-Authored-By: Claude Opus 5 <[email protected]>
384 lines
20 KiB
JavaScript
384 lines
20 KiB
JavaScript
/* Prüft die neuen Bereiche der Verwaltung: Projekte mit Aufgabenliste
|
|
und das Postfach. Wieder mit abgefangenen Netzanfragen, aber mit
|
|
Daten, die den heiklen Faellen entsprechen:
|
|
|
|
- ein Punkt, der auf den KUNDEN wartet (muss auffallen)
|
|
- ein Punkt "nicht enthalten" (muss als solcher erkennbar sein)
|
|
- eine INTERNE Notiz im Postfach (darf nicht wie eine gesendete
|
|
Nachricht aussehen)
|
|
*/
|
|
import { chromium } from "playwright";
|
|
import { createServer } from "node:http";
|
|
import { readFileSync, existsSync } from "node:fs";
|
|
import { join, extname } from "node:path";
|
|
|
|
const W = process.cwd();
|
|
const T = { ".html": "text/html", ".css": "text/css", ".js": "text/javascript",
|
|
".png": "image/png", ".jpg": "image/jpeg", ".svg": "image/svg+xml",
|
|
".webp": "image/webp", ".ico": "image/x-icon", ".json": "application/json" };
|
|
const srv = createServer((q, r) => {
|
|
const p = join(W, decodeURIComponent(q.url.split("?")[0]));
|
|
if (existsSync(p) && !p.endsWith("/")) {
|
|
r.writeHead(200, { "content-type": T[extname(p)] || "text/plain" });
|
|
return r.end(readFileSync(p));
|
|
}
|
|
r.writeHead(404).end("");
|
|
});
|
|
await new Promise((r) => srv.listen(4325, r));
|
|
|
|
const PROJEKT = {
|
|
id: "p1", nummer: "P-0001", titel: "Website für Musterbau", paket: "website",
|
|
status: "design", kunde_id: "k1", kunde_name: "Anna Muster", kunde_firma: "Musterbau",
|
|
preis_cent: 180000, waehrung: "EUR", richttermin: null, archiviert: 0,
|
|
anzahlung_bezahlt: 1, rest_bezahlt: 0, erstellt_am: new Date().toISOString(),
|
|
aufg_gesamt: 6, aufg_erledigt: 2, aufg_kunde: 2, ungelesen: 1,
|
|
naechster_schritt: "Deine Rueckmeldung zum Entwurf.", wartet_auf: "kunde",
|
|
kunde_email: "[email protected]", portfolio_freigabe: 0,
|
|
};
|
|
|
|
const AENDERUNGEN = [
|
|
{ id: "ae1", titel: "Noch eine Unterseite", beschreibung: "Eine Seite fuer Referenzen waere schoen.",
|
|
status: "eingereicht", zusatzpreis_cent: null, zusatzdauer_text: null,
|
|
erstellt_am: new Date(Date.now() - 86400000).toISOString() },
|
|
{ id: "ae2", titel: "Anderes Logo", beschreibung: "Wir haben ein neues.",
|
|
status: "angenommen", zusatzpreis_cent: 12000, zusatzdauer_text: "2 Tage",
|
|
erstellt_am: new Date(Date.now() - 172800000).toISOString() },
|
|
];
|
|
|
|
const PNACHRICHTEN = [
|
|
{ id: "pn1", autor: "kunde", text: "Wann kommt der naechste Entwurf?",
|
|
erstellt_am: new Date(Date.now() - 7200000).toISOString() },
|
|
{ id: "pn2", autor: "dogfather", text: "Morgen Abend.",
|
|
erstellt_am: new Date(Date.now() - 3600000).toISOString() },
|
|
];
|
|
|
|
const AUFGABEN = [
|
|
{ id: "a1", titel: "Briefing-Gespräch führen", beschreibung: "Ziel und Umfang klären.",
|
|
kategorie: "start", status: "erledigt", wer_dran: "dogfather", nicht_enthalten: 0, hinweis: null, reihenfolge: 10 },
|
|
{ id: "a2", titel: "Texte und Bilder zusammenstellen", beschreibung: "Lieber früh als vollständig.",
|
|
kategorie: "start", status: "offen", wer_dran: "kunde", nicht_enthalten: 0, hinweis: null, reihenfolge: 20 },
|
|
{ id: "a3", titel: "Entwurf der Startseite", beschreibung: null,
|
|
kategorie: "gestaltung", status: "erledigt", wer_dran: "dogfather", nicht_enthalten: 0, hinweis: null, reihenfolge: 110 },
|
|
{ id: "a4", titel: "Entwurf ansehen und Rückmeldung geben", beschreibung: null,
|
|
kategorie: "gestaltung", status: "offen", wer_dran: "kunde", nicht_enthalten: 0, hinweis: null, reihenfolge: 130 },
|
|
{ id: "a5", titel: "Alle Unterseiten bauen", beschreibung: null,
|
|
kategorie: "umsetzung", status: "in_arbeit", wer_dran: "dogfather", nicht_enthalten: 0, hinweis: null, reihenfolge: 210 },
|
|
{ id: "a6", titel: "Mehrsprachigkeit", beschreibung: null,
|
|
kategorie: "umsetzung", status: "offen", wer_dran: "dogfather", nicht_enthalten: 1,
|
|
hinweis: "Nicht im Paket enthalten — auf Wunsch als Zusatz.", reihenfolge: 260 },
|
|
{ id: "a7", titel: "Seite live schalten", beschreibung: null,
|
|
kategorie: "abschluss", status: "offen", wer_dran: "dogfather", nicht_enthalten: 0, hinweis: null, reihenfolge: 860 },
|
|
];
|
|
|
|
const FORTSCHRITT = {
|
|
gesamt: 6, erledigt: 2, inArbeit: 1, beimKunden: 2, prozent: 33,
|
|
jeKategorie: {
|
|
start: { gesamt: 2, erledigt: 1, inArbeit: 0 },
|
|
gestaltung: { gesamt: 2, erledigt: 1, inArbeit: 0 },
|
|
umsetzung: { gesamt: 1, erledigt: 0, inArbeit: 1 },
|
|
abschluss: { gesamt: 1, erledigt: 0, inArbeit: 0 },
|
|
},
|
|
};
|
|
|
|
const VERLAUF = [
|
|
{ id: "n1", autor: "kunde", text: "Hallo! Können wir das Logo noch etwas grösser machen?",
|
|
intern: 0, projekt_titel: null, erstellt_am: new Date(Date.now() - 7200000).toISOString() },
|
|
{ id: "n2", autor: "dogfather", text: "Klar, mache ich heute noch.",
|
|
intern: 0, projekt_titel: "Website für Musterbau", erstellt_am: new Date(Date.now() - 3600000).toISOString() },
|
|
{ id: "n3", autor: "dogfather", text: "Nicht vergessen: Rechnung erst nach Abnahme.",
|
|
intern: 1, projekt_titel: null, erstellt_am: new Date(Date.now() - 1800000).toISOString() },
|
|
];
|
|
|
|
const ergebnisse = [];
|
|
function pruefe(name, ok, zusatz) {
|
|
ergebnisse.push({ name, ok });
|
|
console.log((ok ? " ok " : " FEHL ") + name + (zusatz ? " -> " + zusatz : ""));
|
|
}
|
|
|
|
const browser = await chromium.launch();
|
|
|
|
for (const [wo, breite, hoehe] of [["Computer", 1440, 950], ["Handy", 390, 844]]) {
|
|
const seite = await browser.newPage({ viewport: { width: breite, height: hoehe } });
|
|
const fehlerImProtokoll = [];
|
|
seite.on("pageerror", (e) => fehlerImProtokoll.push(e.message));
|
|
|
|
/* Mitschrift dessen, was die Seite tatsächlich an den Server schickt.
|
|
Nur so lässt sich prüfen, ob ein Klick auch WIRKLICH das Richtige
|
|
speichert — dass sich ein Knopf einfärbt, beweist gar nichts. */
|
|
const gesendet = [];
|
|
let beziffert = null;
|
|
let projektNachricht = null;
|
|
|
|
// NUR die API abfangen. Ein Muster, das den ganzen Ordner /webdesign
|
|
// umfasst, trifft auch die Seite SELBST -- dann bekommt der Browser
|
|
// statt der HTML-Datei ein JSON serviert. Genau das ist beim ersten
|
|
// Lauf passiert: Die Seite blieb leer, und der Test meldete
|
|
// "Abzeichen fehlt" statt "Seite gar nicht geladen".
|
|
// (Und der Hinweis steht in Zeilenkommentaren, weil das Glob-Muster
|
|
// ein Sternchen-Schrägstrich enthält und einen Blockkommentar
|
|
// vorzeitig beenden würde -- auch das ist hier schon passiert.)
|
|
await seite.route("**/webdesign/admin/**", (route) => {
|
|
const u = route.request().url();
|
|
if (u.includes("/admin/projekte/p1/alles")) {
|
|
return route.fulfill({ json: { ok: true, projekt: PROJEKT, aufgaben: AUFGABEN,
|
|
fortschritt: FORTSCHRITT, aenderungen: AENDERUNGEN, nachrichten: PNACHRICHTEN } });
|
|
}
|
|
if (u.includes("/admin/projekte/p1/aufgaben")) {
|
|
return route.fulfill({ json: { ok: true, projekt: PROJEKT, aufgaben: AUFGABEN, fortschritt: FORTSCHRITT } });
|
|
}
|
|
if (u.match(/\/admin\/projekte\/p1$/)) {
|
|
gesendet.push(route.request().postDataJSON());
|
|
return route.fulfill({ json: { ok: true } });
|
|
}
|
|
if (u.includes("/admin/aenderungen/")) {
|
|
beziffert = route.request().postDataJSON();
|
|
return route.fulfill({ json: { ok: true, preisText: "250,00 €" } });
|
|
}
|
|
if (u.includes("/admin/projekt-nachricht")) {
|
|
projektNachricht = route.request().postDataJSON();
|
|
return route.fulfill({ json: { ok: true } });
|
|
}
|
|
if (u.includes("/admin/projekte")) return route.fulfill({ json: { ok: true, projekte: [PROJEKT] } });
|
|
if (u.includes("/admin/postfach-zaehler")) return route.fulfill({ json: { ok: true, ungelesen: 1 } });
|
|
if (u.includes("/admin/postfach/k1")) {
|
|
return route.fulfill({ json: { ok: true,
|
|
kunde: { id: "k1", name: "Anna Muster", email: "[email protected]", sprache: "de" },
|
|
nachrichten: VERLAUF, projekte: [{ id: "p1", titel: "Website für Musterbau" }] } });
|
|
}
|
|
if (u.includes("/admin/postfach")) {
|
|
return route.fulfill({ json: { ok: true, ungelesen: 1, verlaeufe: [{
|
|
kunde_id: "k1", name: "Anna Muster", firma: "Musterbau", email: "[email protected]",
|
|
anzahl: 3, ungelesen: 1, letzte: new Date(Date.now() - 1800000).toISOString(),
|
|
letzter_text: "Nicht vergessen: Rechnung erst nach Abnahme.", letzter_autor: "dogfather" }] } });
|
|
}
|
|
/* Die Uebersicht ist seit dem 23.08.2026 die Startansicht und wird
|
|
deshalb bei JEDEM Laden abgerufen. Ohne eigene Ersatzantwort fiel
|
|
sie in den Auffang unten und bekam ein blankes {ok:true} -- was
|
|
einen Fehler ausloeste, der als "keine Fehler im Protokoll"
|
|
fehlschlug, ohne zu verraten, woher er kam. */
|
|
if (u.includes("/admin/uebersicht")) return route.fulfill({ json: { ok: true,
|
|
beiMir: { anfragen: { n: 0 }, nachrichten: { n: 0 }, wuensche: { n: 0 },
|
|
widerrufe: { n: 0 }, projekte: 0 },
|
|
beiKunde: { projekte: 0, aufgaben: 0, zahlungen: { n: 0, summe: 0 }, zahlungenText: "0,00 €" },
|
|
geld: { monat: { n: 0, brutto: 0, netto: 0, bruttoText: "0,00 €", nettoText: "0,00 €" },
|
|
gesamt: { n: 0, bruttoText: "0,00 €" }, gebuehrenText: "0,00 €" },
|
|
projekte: [], verlauf: [], bestand: {} } });
|
|
if (u.includes("/admin/anfragen")) return route.fulfill({ json: { ok: true, anfragen: [] } });
|
|
if (u.includes("/admin/kunden")) return route.fulfill({ json: { ok: true, kunden: [] } });
|
|
return route.fulfill({ json: { ok: true } });
|
|
});
|
|
await seite.route("**/auth/**", (r) => r.fulfill({ json: { ok: true, user: { name: "Test" } } }));
|
|
await seite.addInitScript(() => {
|
|
sessionStorage.setItem("wd-vw-token", "t");
|
|
sessionStorage.setItem("wd-sitzung-aktiv", "1");
|
|
});
|
|
|
|
await seite.goto("http://127.0.0.1:4325/webdesign/verwaltung.html");
|
|
await seite.waitForTimeout(900);
|
|
console.log("\n=== " + wo + " ===");
|
|
|
|
// Abzeichen am Reiter
|
|
const abz = await seite.locator("#vw-pf-zaehler").innerText().catch(() => "");
|
|
const abzSichtbar = await seite.locator("#vw-pf-zaehler").isVisible().catch(() => false);
|
|
pruefe(wo + ": Postfach-Abzeichen zeigt die ungelesene Nachricht", abzSichtbar && abz === "1", abz);
|
|
|
|
// ---- Projekte ----
|
|
await seite.click('[data-tab="projekte"]');
|
|
await seite.waitForTimeout(500);
|
|
pruefe(wo + ": Projektliste zeigt das Projekt", await seite.locator("[data-projekt]").count() === 1);
|
|
pruefe(wo + ": „wartet auf Kunde“ ist sichtbar", await seite.locator(".vw-wartet").count() === 1);
|
|
|
|
await seite.click("[data-projekt]");
|
|
await seite.waitForTimeout(600);
|
|
|
|
const m = await seite.evaluate(() => {
|
|
const gruppen = Array.from(document.querySelectorAll(".vw-aufg-gruppe")).map((g) => g.dataset.k);
|
|
const farben = Array.from(document.querySelectorAll(".vw-aufg-gruppe")).map((g) =>
|
|
getComputedStyle(g.querySelector(".vw-aufg-kopf")).color);
|
|
const balken = Array.from(document.querySelectorAll(".vw-fs-teil > i")).map((i) =>
|
|
getComputedStyle(i).backgroundColor);
|
|
const zeile = document.querySelector('[data-aufgabe="a2"]');
|
|
const normal = document.querySelector('[data-aufgabe="a5"]');
|
|
return {
|
|
gruppen, farben, balkenFarben: [...new Set(balken)].length,
|
|
kundenZeileAnders: zeile && normal
|
|
? getComputedStyle(zeile).backgroundColor !== getComputedStyle(normal).backgroundColor : false,
|
|
nichtEnthalten: document.querySelectorAll(".vw-aufg-nicht").length,
|
|
haken: document.querySelectorAll(".vw-aufg-haken").length,
|
|
erledigtDurchgestrichen: getComputedStyle(
|
|
document.querySelector('[data-aufgabe="a1"] .vw-aufg-titel')).textDecorationLine,
|
|
blickFelder: document.querySelectorAll(".vw-blick-feld").length,
|
|
|
|
/* Die REIHENFOLGE im Bild, nicht im Quelltext.
|
|
|
|
Diese Pruefung fehlte im ersten Anlauf, und deshalb meldete der
|
|
Test 37 von 37 gruen, waehrend jede Zeile sichtbar falsch herum
|
|
gebaut war: Kaestchen, dann die kleinen Knoepfe, und der Titel
|
|
ganz rechts. Alle inhaltlichen Pruefungen (Farben, Anzahl,
|
|
Durchgestrichenes) trafen zu -- keine einzige schaute darauf, WO
|
|
die Dinge liegen. Aufgefallen ist es erst am Screenshot.
|
|
|
|
Lehre: Wer eine Anordnung baut, muss die Anordnung pruefen, nicht
|
|
nur ihre Bestandteile. */
|
|
anordnung: (() => {
|
|
const z = document.querySelector('[data-aufgabe="a2"]');
|
|
if (!z) return null;
|
|
const k = z.querySelector(".vw-aufg-haken").getBoundingClientRect();
|
|
const t = z.querySelector(".vw-aufg-titel").getBoundingClientRect();
|
|
const r = z.querySelector(".vw-aufg-rechts").getBoundingClientRect();
|
|
const b = z.querySelector(".vw-aufg-text");
|
|
return {
|
|
reihenfolgeStimmt: k.right <= t.left + 1 && t.right <= r.left + 1,
|
|
textUnterTitel: b ? b.getBoundingClientRect().top >= t.bottom - 2 : true,
|
|
textNichtUnterKaestchen: b ? b.getBoundingClientRect().left >= k.right - 1 : true,
|
|
};
|
|
})(),
|
|
};
|
|
});
|
|
|
|
pruefe(wo + ": alle vier Abschnitte erscheinen", m.gruppen.join(",") === "start,gestaltung,umsetzung,abschluss", m.gruppen.join(","));
|
|
pruefe(wo + ": jeder Abschnitt hat eine EIGENE Farbe", new Set(m.farben).size === 4, m.farben.join(" | "));
|
|
pruefe(wo + ": Punkt für den Kunden hebt sich ab", m.kundenZeileAnders);
|
|
pruefe(wo + ": „nicht enthalten“ ist gekennzeichnet", m.nichtEnthalten === 1);
|
|
pruefe(wo + ": jeder Punkt hat ein Kästchen zum Abhaken", m.haken === 7, "n=" + m.haken);
|
|
pruefe(wo + ": Erledigtes ist durchgestrichen", m.erledigtDurchgestrichen.includes("line-through"), m.erledigtDurchgestrichen);
|
|
pruefe(wo + ": Zusammenfassung mit vier Zahlen", m.blickFelder === 4);
|
|
pruefe(wo + ": Kästchen — Titel — Knöpfe stehen in dieser Reihenfolge", m.anordnung?.reihenfolgeStimmt);
|
|
pruefe(wo + ": die Erklärung steht UNTER dem Titel", m.anordnung?.textUnterTitel);
|
|
pruefe(wo + ": die Erklärung ist am Titel ausgerichtet, nicht am Rand", m.anordnung?.textNichtUnterKaestchen);
|
|
|
|
/* ---- Stand ändern ----
|
|
Dafür gab es bis zu dieser Erweiterung KEINE Oberfläche. Der Kunde
|
|
sah Phase und "nächster Schritt" ganz oben in seinem Portal, und
|
|
beides liess sich nirgends ändern — ein Projekt blieb für immer im
|
|
Briefing stehen, egal wie weit es wirklich war. */
|
|
pruefe(wo + ": alle Phasen sind wählbar",
|
|
await seite.locator("[data-phase]").count() === 9, "7 Phasen + 2 Sonderfälle");
|
|
pruefe(wo + ": die aktuelle Phase ist hervorgehoben",
|
|
await seite.locator('[data-phase="design"].vw-an').count() === 1);
|
|
pruefe(wo + ": „wer ist am Zug“ zeigt den Kunden",
|
|
await seite.locator('[data-wartet="kunde"].vw-an').count() === 1);
|
|
|
|
gesendet.length = 0;
|
|
await seite.click('[data-phase="entwicklung"]');
|
|
await seite.waitForTimeout(400);
|
|
pruefe(wo + ": Phasenklick speichert sofort",
|
|
gesendet.some((g) => g && g.status === "entwicklung"), JSON.stringify(gesendet));
|
|
|
|
await seite.click('[data-wartet="dogfather"]');
|
|
await seite.waitForTimeout(400);
|
|
pruefe(wo + ": „wer ist am Zug“ speichert sofort",
|
|
gesendet.some((g) => g && g.wartetAuf === "dogfather"));
|
|
|
|
await seite.fill("#vw-p-schritt", "Entwurf ansehen und Rückmeldung geben");
|
|
await seite.fill("#vw-p-termin", "Mitte Oktober");
|
|
gesendet.length = 0;
|
|
await seite.click("#vw-p-speichern");
|
|
await seite.waitForTimeout(500);
|
|
pruefe(wo + ": „nächster Schritt“ und Termin werden gespeichert",
|
|
gesendet.some((g) => g && g.naechsterSchritt === "Entwurf ansehen und Rückmeldung geben"
|
|
&& g.richttermin === "Mitte Oktober"), JSON.stringify(gesendet));
|
|
|
|
gesendet.length = 0;
|
|
await seite.check("#vw-p-rest");
|
|
await seite.waitForTimeout(400);
|
|
pruefe(wo + ": Zahlungsstand wird sofort gespeichert",
|
|
gesendet.some((g) => g && g.restBezahlt === true), JSON.stringify(gesendet));
|
|
|
|
/* ---- Änderungswunsch beziffern ----
|
|
Der Kunde konnte seit jeher Ideen einreichen — beziffern ging nur
|
|
serverseitig. Der Kreis war offen: Er schickt etwas los und hört nie
|
|
wieder davon. */
|
|
pruefe(wo + ": beide Änderungswünsche erscheinen", await seite.locator("[data-ae]").count() === 2);
|
|
pruefe(wo + ": nur der OFFENE hat ein Bezifferungsfeld",
|
|
await seite.locator("[data-beziffern]").count() === 1);
|
|
|
|
// Ohne Preis darf nichts rausgehen — der Kunde könnte sonst nicht entscheiden.
|
|
beziffert = null;
|
|
await seite.click("[data-beziffern]");
|
|
await seite.waitForTimeout(400);
|
|
pruefe(wo + ": ohne Preis wird NICHT gesendet", beziffert === null, JSON.stringify(beziffert));
|
|
|
|
await seite.fill(".vw-ae-preis", "250");
|
|
await seite.fill(".vw-ae-dauer", "3 Tage");
|
|
await seite.click("[data-beziffern]");
|
|
await seite.waitForTimeout(600);
|
|
pruefe(wo + ": mit Preis geht das Angebot raus",
|
|
beziffert && beziffert.preisEuro === 250 && beziffert.dauer === "3 Tage", JSON.stringify(beziffert));
|
|
|
|
/* ---- Nachricht zum Projekt ----
|
|
Ohne sie hiess es: Der Kunde schreibt im Projekt, und ich kann ihm
|
|
nur woanders antworten. */
|
|
pruefe(wo + ": Projektnachrichten erscheinen",
|
|
await seite.locator("#vw-p-blasen .vw-blase").count() === 2);
|
|
await seite.fill("#vw-p-nachricht", "Der Entwurf kommt morgen.");
|
|
await seite.click("#vw-p-senden");
|
|
await seite.waitForTimeout(500);
|
|
pruefe(wo + ": Antwort zum Projekt geht raus",
|
|
projektNachricht && projektNachricht.text === "Der Entwurf kommt morgen."
|
|
&& projektNachricht.projektId === "p1", JSON.stringify(projektNachricht));
|
|
|
|
// Touch-Ziele auf dem Handy
|
|
if (breite < 900) {
|
|
const zuKlein = await seite.evaluate(() =>
|
|
Array.from(document.querySelectorAll(".vw-aufg-haken, .vw-mini"))
|
|
.filter((b) => { const r = b.getBoundingClientRect(); return r.width < 24 || r.height < 24; }).length);
|
|
pruefe(wo + ": alle Knöpfe sind gross genug zum Antippen", zuKlein === 0, "zu klein: " + zuKlein);
|
|
}
|
|
|
|
await seite.screenshot({ path: `pruef-aufgaben-${wo.toLowerCase()}.png` });
|
|
await seite.keyboard.press("Escape");
|
|
await seite.waitForTimeout(300);
|
|
|
|
// ---- Postfach ----
|
|
await seite.click('[data-tab="postfach"]');
|
|
await seite.waitForTimeout(500);
|
|
pruefe(wo + ": Postfach zeigt den Verlauf", await seite.locator("[data-verlauf]").count() === 1);
|
|
|
|
await seite.click("[data-verlauf]");
|
|
await seite.waitForTimeout(600);
|
|
|
|
const pf = await seite.evaluate(() => {
|
|
const intern = document.querySelector(".vw-blase--intern");
|
|
const wir = document.querySelector(".vw-blase--wir");
|
|
const kunde = document.querySelector(".vw-blase--kunde");
|
|
const lauf = document.getElementById("vw-blasen");
|
|
return {
|
|
/* Auf den Postfach-Verlauf eingegrenzt. Ein document-weites
|
|
".vw-blase" zaehlt auch die Nachrichten der Projektansicht mit --
|
|
die ist zwar geschlossen, steht aber weiter im Dokument. */
|
|
anzahl: document.querySelectorAll("#vw-blasen .vw-blase").length,
|
|
internDa: !!intern,
|
|
internAnders: intern && wir
|
|
? getComputedStyle(intern).borderStyle !== getComputedStyle(wir).borderStyle : false,
|
|
kundeLinks: kunde && wir
|
|
? kunde.getBoundingClientRect().left < wir.getBoundingClientRect().left : false,
|
|
amEnde: lauf ? lauf.scrollTop + lauf.clientHeight >= lauf.scrollHeight - 5 : false,
|
|
schalterDa: !!document.getElementById("vw-pf-intern"),
|
|
};
|
|
});
|
|
pruefe(wo + ": alle drei Nachrichten erscheinen", pf.anzahl === 3, "n=" + pf.anzahl);
|
|
pruefe(wo + ": die interne Notiz ist da", pf.internDa);
|
|
pruefe(wo + ": interne Notiz sieht ANDERS aus als eine gesendete", pf.internAnders);
|
|
pruefe(wo + ": Kunde links, eigene Nachrichten rechts", pf.kundeLinks);
|
|
pruefe(wo + ": Verlauf springt ans Ende", pf.amEnde);
|
|
|
|
// Der Schalter muss das Schreibfeld sichtbar umfaerben
|
|
const vorher = await seite.evaluate(() => getComputedStyle(document.getElementById("vw-schreibfeld")).borderColor);
|
|
await seite.check("#vw-pf-intern");
|
|
await seite.waitForTimeout(250);
|
|
const nachher = await seite.evaluate(() => getComputedStyle(document.getElementById("vw-schreibfeld")).borderColor);
|
|
pruefe(wo + ": „interne Notiz“ färbt das Schreibfeld sichtbar um", vorher !== nachher, vorher + " -> " + nachher);
|
|
|
|
pruefe(wo + ": keine Fehler im Protokoll", fehlerImProtokoll.length === 0, fehlerImProtokoll.join(" | "));
|
|
|
|
await seite.screenshot({ path: `pruef-postfach-${wo.toLowerCase()}.png` });
|
|
await seite.close();
|
|
}
|
|
|
|
await browser.close();
|
|
srv.close();
|
|
const fehler = ergebnisse.filter((e) => !e.ok).length;
|
|
console.log(`\n===== ${ergebnisse.length - fehler} bestanden, ${fehler} fehlgeschlagen =====`);
|
|
process.exitCode = fehler ? 1 : 0;
|