Spenden: Stufen gestaltbar, Bild hochladen, Größe je Stufe
Filipes Wunsch: „mach paar fertige und so dass ich auch hochladen
kann. auch so dass ich das anders gestalten kann oder die größe
verändern kann. ... auch spezielle sachen bei speziellen spenden."
WAS ES SCHON GAB, WAS FEHLTE
Drei Stufen ab Werk, fünf gezeichnete Zeichen, Farbe und Dauer je
Stufe — und sogar schon ein Feld für ein eigenes Bild. Es fehlte der
Weg, das alles zu ÄNDERN: Um eine Stufe umzubenennen, hätte jemand in
die Datenbank greifen müssen.
DIE GRÖSSE IST DAS „SPEZIELLE BEI SPEZIELLEN SPENDEN"
Je Stufe, nicht einmal für alle: Eine Rudel-Legende darf größer
dastehen als ein Danke. Eine einzige Größe für alle wäre wieder eine
Preisliste. Umgesetzt als EINE Schriftgröße, alles darin in `em` —
nicht `transform: scale()`, denn die Karte kommt schon mit
`translateX()` herein, und zwei `transform` an derselben Stelle
schließen einander aus; außerdem wird Text beim Skalieren matschig.
Nur nach oben (1 bis 2,5), und das ist eine ehrliche Grenze: Das
kleinste Wort auf der Karte steht bei 11,52 px, die Hausgrenze ist
11,5. Ein Faktor von 0,8 machte daraus 9,2 px. Kleiner geht an der
richtigen Stelle — die OBS-Tafel hat ihren eigenen Regler in der
Adresse, dort ist es eine Videoeinblendung und kein Text zum Lesen.
AUSPROBIEREN, OHNE EINE SPENDE ANZULEGEN
Der naheliegende Weg wäre gewesen: eine Spende eintragen und danach
löschen. Das ist verboten — in ein laufendes System kommen keine
Testdaten, und „gelöscht" heißt bei Geld nicht „war nie da". Die
Probe schreibt deshalb NICHTS und geht nur an den, der drückt; eine
Probe im ganzen Saal wäre eine Spende, die es nicht gab.
DREI FEHLER, DIE DIE PRÜFUNG GEFUNDEN HAT
1. `protokolliere()` wurde an 17 Stellen falsch herum gerufen —
`(personId, aktion, detail, ip)` statt `(aktion, {…})`. JavaScript
beschwert sich nicht: Das zweite Argument war ein Text, und einen
Text zu zerlegen ergibt lauter `undefined`. Auf dem echten Server
nachgemessen: 39 Protokollzeilen mit Aktionen wie „16.0", alle
ohne Person, ohne Detail, ohne IP. Betroffen waren Material,
Hilfe, Bühne, Reaction und Spenden — also jede Änderung an
Dogi-Media und jede Maßnahme im Live-Chat, ausgerechnet das,
wofür es ein Protokoll gibt. Alle 17 berichtigt, und
pruef-struktur wacht jetzt darüber (mit Gegenprobe).
2. Beim Speichern der Leiter bekam jede Stufe eine NEUE Kennung
(DELETE + INSERT). Ein Bild-Hochladen gegen die eben noch gültige
Kennung antwortete mit 404 — im Alltag trifft das jeden, der einen
zweiten Bildschirm offen hat. Jetzt werden vorhandene Zeilen
geändert statt ersetzt; das Bild bleibt von selbst daran hängen.
3. `ab_cent` ist eindeutig. Zwei Stufen ihre Beträge tauschen zu
lassen scheiterte mit „UNIQUE constraint failed", obwohl das
Ergebnis in Ordnung gewesen wäre: Beim Umschreiben stößt die
Leiter auf sich selbst. Jetzt in drei Schritten — löschen,
geparkte Zwischenwerte, endgültige Werte —, und das ist nach
außen nie sichtbar.
UND DREI, DIE IN MEINER MESSUNG STECKTEN
Die Messung hat eine noch laufende Karte aus dem vorigen Abschnitt
erwischt und daraus drei Fehler gemeldet, die keine waren —
darunter „die Probe läuft im ganzen Saal". Sie zählte außerdem die
versteckten Dateifelder als zu kleine Tippziele. Jetzt räumt sie
vorher auf, wartet auf die Karte MIT DER ERWARTETEN GRÖSSE (die
Karten laufen in einer Schlange — einen Knoten zu entfernen beendet
sie nicht) und lässt die Einblendung zur Ruhe kommen, bevor sie misst.
Ein Bildschirmfoto aus der Einblendphase sah aus, als stünde die
Karte links heraus; nachgemessen: links 18 px, ganz im Bild.
GEMESSEN, NICHT ANGENOMMEN
Karte bei Größe 1: Schrift 16 px, Betrag 25,92 px. Bei Größe 2:
32 px und 51,84 px — Faktor exakt 2,00. Hätte eine einzige Regel noch
in `rem` gestanden, wäre die Karte ungleichmäßig gewachsen, und auf
einem Bild sieht beides nur „größer" aus.
AUCH DAS BILD IST GEPRÜFT
Es liegt am Bühnen-Router und nicht am Spenden-Router: Die
Spendentafel in OBS hat keine Anmeldung, und ein 401 als JSON in
einem `<img>` ergibt ein kaputtes Bild ohne jeden Hinweis. Ohne
Schlüssel, aber mit 128 Bit zufälligem Dateinamen — dieselbe
Größenordnung wie der Bühnenschlüssel, und es ist ein Zierbild, das
ohnehin im Stream steht. Kein Ausbruch aus dem Ordner (vier Wege
geprüft, gemessen wird die Wirkung und nicht der Statuscode).
NACHGETRAGEN AUS BLOCK 4
`reaktion_meldungen` fehlte im Löschkonzept — eine bestehende Prüfung
hat es gefunden. 30 Tage nach dem Erledigen; meistens sind sie
ohnehin früher weg, weil der Live-Chat beim Beenden gelöscht wird und
die Meldungen daran hängen. Wer meldet, muss sich darauf verlassen
können, dass daraus keine dauerhafte Liste wird.
GEPRUEFT
pruef-spenden: 95 Prüfungen (vorher 46), 0 Fehler.
pruef-reaktion 260, pruef-buehne 36, pruef-aufbewahrung 45,
pruef-struktur, pruef-meldungen, pruef-css-klassen, pruef-tippziele,
pruef-deutsche-texte, pruef-auskunft alle grün.
mess-reaktion und mess-buehne: Rückgabewert 0, kein ACHTUNG.
Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -685,6 +685,229 @@ if (zuschauer.konsole.length) console.log(" KONSOLE:", zuschauer.konsole.slice
|
||||
Abzeichen UND ein zweiter Knopf dazugekommen. Genau so ist am
|
||||
28.09. die Regie 197 px uebergelaufen: Die Rechnung ging auf, die
|
||||
Wirklichkeit nicht. */
|
||||
/* ---- Die Gestaltung der Spendenkarten ---------------------------
|
||||
|
||||
Filipe: „mach paar fertige und so dass ich auch hochladen kann.
|
||||
auch so dass ich das anders gestalten kann oder die größe
|
||||
verändern kann."
|
||||
|
||||
GEMESSEN WIRD DIE KARTE, NICHT DIE EINSTELLUNG. Dass in der
|
||||
Datenbank 2.0 steht, sagt nichts darüber, ob die Karte doppelt so
|
||||
groß erscheint -- dazwischen liegen eine Zahl im Stilblatt, eine
|
||||
Umrechnung in `em` und ein Dutzend Regeln, die noch in `rem`
|
||||
stehen könnten. Also: Karte laufen lassen und nachmessen. */
|
||||
console.log("");
|
||||
console.log(" --- Die Gestaltung der Spendenkarten ---");
|
||||
await host.seite.click('#reiter .reiter__knopf[data-tafel="gestaltung"]').catch(() => {});
|
||||
await host.seite.waitForTimeout(700);
|
||||
|
||||
const leiter = await host.seite.evaluate(() => {
|
||||
const zeilen = [...document.querySelectorAll("#stufen .stufe-zeile")];
|
||||
return {
|
||||
zeilen: zeilen.length,
|
||||
ersteName: zeilen[0]?.querySelector(".stufe-name")?.value || "",
|
||||
felder: zeilen[0] ? [...zeilen[0].querySelectorAll("input, select")].length : 0,
|
||||
/* Jedes Bedienelement muss mit dem Finger zu treffen sein --
|
||||
hier wird eingestellt, was vor Publikum im Bild steht. */
|
||||
/* VERSTECKTES ZAEHLT NICHT. Hinter jedem „Bild hochladen" liegt
|
||||
ein `<input type="file" hidden>` -- es ist 0 px hoch, und das
|
||||
ist richtig so: Angefasst wird das Schild davor. Die erste
|
||||
Fassung hat es mitgezaehlt und drei Fehler gemeldet, die
|
||||
keine waren. Eine Messung, die Unsichtbares misst, misst das
|
||||
Falsche. */
|
||||
zuKlein: zeilen.flatMap((z) => [...z.querySelectorAll("input, select, .stufe-tun")])
|
||||
.filter((e) => !e.hidden && e.offsetParent !== null)
|
||||
.filter((e) => e.getBoundingClientRect().height < 40).length,
|
||||
ueber: Math.max(0, document.documentElement.scrollWidth
|
||||
- document.documentElement.clientWidth),
|
||||
};
|
||||
});
|
||||
console.log(` ${leiter.zeilen} Stufen, erste „${leiter.ersteName}", `
|
||||
+ `${leiter.felder} Felder je Zeile, ${leiter.zuKlein} zu kleine Ziele, `
|
||||
+ `seitlich ${leiter.ueber} px`);
|
||||
if (leiter.zeilen < 3 || leiter.felder < 6 || leiter.zuKlein > 0 || leiter.ueber > 0) {
|
||||
console.log(" ACHTUNG: Die Stufenleiter ist unvollstaendig oder laeuft ueber.");
|
||||
ausgang = 1;
|
||||
}
|
||||
await bild(host.seite, "17-stufenleiter");
|
||||
|
||||
/* ---- Eine Stufe umgestalten und nachsehen, ob es ankommt ---- */
|
||||
const vorherKarte = await host.seite.evaluate(async () => {
|
||||
/* Erst einmal bei Größe 1 messen -- ohne Bezugswert sagt die zweite
|
||||
Messung nichts. */
|
||||
const k2 = [...document.querySelectorAll("#stufen .stufe-zeile")][2];
|
||||
k2.querySelector(".stufe-groesse").value = "1";
|
||||
k2.querySelector(".stufe-groesse").dispatchEvent(new Event("input", { bubbles: true }));
|
||||
document.getElementById("stufen-sichern").click();
|
||||
return true;
|
||||
});
|
||||
await host.seite.waitForTimeout(1200);
|
||||
|
||||
/* ==== ERST AUFRAEUMEN, DANN MESSEN ================================
|
||||
|
||||
Die erste Fassung hat einfach `document.querySelector(".spenden-
|
||||
karte")` genommen -- und damit die Karte der echten 10-Euro-Spende
|
||||
erwischt, die aus dem vorigen Messabschnitt noch im Bild stand.
|
||||
Ergebnis: drei gemeldete Fehler, die keine waren, darunter „die
|
||||
Probe laeuft im ganzen Saal". Eine Messung, die nicht weiss, WELCHE
|
||||
Karte sie vor sich hat, misst irgendetwas.
|
||||
|
||||
Also: beide Tafeln leeren, dann druecken, dann auf eine Karte
|
||||
warten, die sich als Probe ausweist. */
|
||||
const tafelnLeeren = async () => {
|
||||
for (const s2 of [host.seite, zuschauer.seite]) {
|
||||
await s2.evaluate(() => {
|
||||
for (const k2 of document.querySelectorAll(".spendenkarte")) k2.remove();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const messeKarte = async (erwartet) => {
|
||||
await tafelnLeeren();
|
||||
await host.seite.evaluate(() => {
|
||||
const k2 = [...document.querySelectorAll("#stufen .stufe-zeile")][2];
|
||||
[...k2.querySelectorAll(".stufe-tun")].find((x) => x.textContent === "Ausprobieren")?.click();
|
||||
});
|
||||
/* ==== AUF DIE RICHTIGE KARTE WARTEN, NICHT AUF DIE UHR ==========
|
||||
|
||||
Zwei Fallen auf einmal, und die zweite hat die erste Fassung
|
||||
gekostet:
|
||||
|
||||
(1) Die Karten laufen in einer SCHLANGE. Ein Knoten aus dem
|
||||
Dokument zu nehmen beendet nicht, was die Seite gerade
|
||||
zeigt -- die naechste Karte kommt erst, wenn die Dauer der
|
||||
vorigen abgelaufen ist. Bei elf Sekunden Dauer und acht
|
||||
Sekunden Wartezeit kam sie nie, und die Messung meldete
|
||||
einen Fehler, den es nicht gab.
|
||||
|
||||
(2) Gewartet wird auf die Karte MIT DER ERWARTETEN GROESSE.
|
||||
Sonst koennte die vorige Probe die Antwort geben, und die
|
||||
Messung bestaetigte eine Aenderung, die gar nicht
|
||||
angekommen ist. */
|
||||
await host.seite.waitForFunction(
|
||||
(soll) => [...document.querySelectorAll(".spendenkarte")].some((k2) =>
|
||||
k2.querySelector(".spendenkarte__wer")?.textContent === "Probe"
|
||||
&& Math.abs(parseFloat(getComputedStyle(k2).fontSize) - soll) < 0.5),
|
||||
erwartet, { timeout: 16000 },
|
||||
).catch(() => {});
|
||||
/* DIE KARTE KOMMT VON LINKS HEREIN (450 ms). Wer sofort misst,
|
||||
misst einen Zwischenschritt: Waehrend des Hereinkommens steht
|
||||
sie 28 px weiter links und ist halb durchsichtig. Ein
|
||||
Bildschirmfoto aus dieser Phase sieht aus, als waere sie
|
||||
abgeschnitten -- und genau das hat mich beim ersten Ansehen in
|
||||
die Irre gefuehrt. */
|
||||
await host.seite.waitForTimeout(700);
|
||||
return host.seite.evaluate(() => {
|
||||
const k2 = [...document.querySelectorAll(".spendenkarte")]
|
||||
.find((x) => x.querySelector(".spendenkarte__wer")?.textContent === "Probe");
|
||||
if (!k2) return null;
|
||||
const r = k2.getBoundingClientRect();
|
||||
const c = getComputedStyle(k2);
|
||||
const betrag = k2.querySelector(".spendenkarte__betrag");
|
||||
return {
|
||||
schrift: Math.round(parseFloat(c.fontSize) * 100) / 100,
|
||||
breit: Math.round(r.width), hoch: Math.round(r.height),
|
||||
betragText: betrag?.textContent || "",
|
||||
betragSchrift: Math.round(parseFloat(getComputedStyle(betrag).fontSize) * 100) / 100,
|
||||
stufe: k2.querySelector(".spendenkarte__stufe")?.textContent || "",
|
||||
wer: k2.querySelector(".spendenkarte__wer")?.textContent || "",
|
||||
links: Math.round(r.left), oben: Math.round(r.top),
|
||||
/* ALLE VIER SEITEN. Die erste Fassung hat nur rechts und unten
|
||||
geprueft -- eine Karte, die links heraussteht, waere
|
||||
durchgegangen. Im Stream ist links genauso abgeschnitten wie
|
||||
rechts. */
|
||||
imBild: r.left >= -1 && r.top >= -1
|
||||
&& r.right <= window.innerWidth + 1 && r.bottom <= window.innerHeight + 1,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
const kKlein = await messeKarte(16);
|
||||
console.log(` Größe 1: Karte ${kKlein?.breit}x${kKlein?.hoch} px, Schrift ${kKlein?.schrift} px, `
|
||||
+ `Betrag „${kKlein?.betragText}" bei ${kKlein?.betragSchrift} px, Stufe „${kKlein?.stufe}"`);
|
||||
if (!kKlein || kKlein.wer !== "Probe") {
|
||||
console.log(" ACHTUNG: Die Probe zeigt keine Karte -- oder sie ist nicht als Probe erkennbar.");
|
||||
ausgang = 1;
|
||||
}
|
||||
await bild(host.seite, "18-karte-normal");
|
||||
|
||||
/* DIE PROBE DARF NUR BEIM DRUECKENDEN ERSCHEINEN. Eine Probe, die im
|
||||
ganzen Saal laeuft, ist eine Spende, die es nicht gab. */
|
||||
const beimZuschauer = await zuschauer.seite.evaluate(() =>
|
||||
document.querySelectorAll(".spendenkarte").length);
|
||||
console.log(` Karten bei der Zuschauerin waehrend der Probe: ${beimZuschauer} (soll: 0)`);
|
||||
if (beimZuschauer !== 0) {
|
||||
console.log(" ACHTUNG: Die Probe laeuft im ganzen Saal.");
|
||||
ausgang = 1;
|
||||
}
|
||||
|
||||
/* Jetzt auf doppelte Größe -- und nachmessen. */
|
||||
await host.seite.evaluate(() => {
|
||||
const k2 = [...document.querySelectorAll("#stufen .stufe-zeile")][2];
|
||||
k2.querySelector(".stufe-groesse").value = "2";
|
||||
k2.querySelector(".stufe-groesse").dispatchEvent(new Event("input", { bubbles: true }));
|
||||
k2.querySelector(".stufe-farbe").value = "#55d6a0";
|
||||
k2.querySelector(".stufe-farbe").dispatchEvent(new Event("input", { bubbles: true }));
|
||||
document.getElementById("stufen-sichern").click();
|
||||
});
|
||||
await host.seite.waitForTimeout(1400);
|
||||
const kGross = await messeKarte(32);
|
||||
console.log(` Größe 2: Karte ${kGross?.breit}x${kGross?.hoch} px, Schrift ${kGross?.schrift} px, `
|
||||
+ `Betrag bei ${kGross?.betragSchrift} px, links ${kGross?.links} / oben ${kGross?.oben}, `
|
||||
+ `ganz im Bild=${kGross?.imBild}`);
|
||||
await bild(host.seite, "19-karte-doppelt");
|
||||
|
||||
if (!kGross || !kKlein) {
|
||||
console.log(" ACHTUNG: Die Karte liess sich nicht zweimal messen.");
|
||||
ausgang = 1;
|
||||
} else {
|
||||
/* ZWEIFACH HEISST ZWEIFACH. Wenn eine einzige Regel noch in `rem`
|
||||
stünde statt in `em`, wüchse die Karte ungleichmäßig -- und das
|
||||
sieht man auf einem Bildschirmfoto nicht, weil beides „größer"
|
||||
aussieht. */
|
||||
const faktor = kGross.betragSchrift / kKlein.betragSchrift;
|
||||
console.log(` Der Betrag wuchs um das ${faktor.toFixed(2)}-fache (soll: 2,00)`);
|
||||
if (Math.abs(faktor - 2) > 0.05) {
|
||||
console.log(" ACHTUNG: Die Größe wirkt nicht oder nicht gleichmaessig.");
|
||||
ausgang = 1;
|
||||
}
|
||||
if (kGross.hoch <= kKlein.hoch) {
|
||||
console.log(" ACHTUNG: Die Karte wird nicht hoeher.");
|
||||
ausgang = 1;
|
||||
}
|
||||
/* UND SIE MUSS INS BILD PASSEN. Die Obergrenze 2,5 ist daran
|
||||
ausgerichtet -- eine Karte, die rechts heraussteht, ist im
|
||||
Stream abgeschnitten. */
|
||||
if (!kGross.imBild) {
|
||||
console.log(" ACHTUNG: Die doppelt grosse Karte steht aus dem Bild heraus.");
|
||||
ausgang = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Und die Farbe? ---- */
|
||||
const farbe = await host.seite.evaluate(() => {
|
||||
const k2 = [...document.querySelectorAll(".spendenkarte")]
|
||||
.find((x) => x.querySelector(".spendenkarte__wer")?.textContent === "Probe");
|
||||
return k2 ? getComputedStyle(k2).getPropertyValue("--sp-farbe").trim() : "";
|
||||
});
|
||||
console.log(` Farbe der Karte: „${farbe}" (gesetzt war #55d6a0)`);
|
||||
if (farbe.toLowerCase() !== "#55d6a0") {
|
||||
console.log(" ACHTUNG: Die Farbe kommt nicht an.");
|
||||
ausgang = 1;
|
||||
}
|
||||
|
||||
/* Wieder auf einen vernuenftigen Wert -- die naechste Messung soll
|
||||
nicht mit einer Karte in doppelter Groesse anfangen. */
|
||||
await host.seite.evaluate(() => {
|
||||
const k2 = [...document.querySelectorAll("#stufen .stufe-zeile")][2];
|
||||
k2.querySelector(".stufe-groesse").value = "1.45";
|
||||
k2.querySelector(".stufe-groesse").dispatchEvent(new Event("input", { bubbles: true }));
|
||||
k2.querySelector(".stufe-farbe").value = "#e0a13a";
|
||||
k2.querySelector(".stufe-farbe").dispatchEvent(new Event("input", { bubbles: true }));
|
||||
document.getElementById("stufen-sichern").click();
|
||||
});
|
||||
await host.seite.waitForTimeout(1000);
|
||||
|
||||
/* ---- Das Video: Tempo, Anordnung, Umschalter --------------------
|
||||
|
||||
Filipe: „auch bei den videos sachen wie pause geschwindigkeit ...
|
||||
|
||||
Reference in New Issue
Block a user