E2E-Spec-Wahrheitstabelle
Das Blockworx Engineering Handbook hat seine Real-Flow-Anforderung für E2E-Tests am
2026-09-04 verbindlich gemacht (docs/70-standards/testing/e2e.md). Regel 8 dieses
Standards verlangt von jedem Repository eine Spec-Wahrheitstabelle: eine Zeile je
Spec, ihre Oberfläche und die Klasse der Abkürzung, die sie noch nimmt — mit
Zeilenbezug und Zählung —, damit die verbleibenden Abkürzungen sichtbar sind statt
angenommen. Diese Seite ist diese Tabelle für SupaCloud. Sie wird von Hand gepflegt und
bei jeder Spec-Änderung neu gezählt; die Zahlen unten stammen vom 2026-09-04 aus
web/tests/e2e und web/tests/e2e-real.
Klassen
Abschnitt betitelt „Klassen“Die Klassen des Handbuchs, plus eine, die dieses Repository braucht:
| Klasse | Bedeutung |
|---|---|
| A | Echter Ablauf durch die echte Oberfläche: gebootetes Backend, gebautes Web-Bundle, UI-Klicks oder öffentliche API-Aufrufe, kein direkter Datenbankschreibzugriff im Ablauf. |
| B | Ein Datenbankschritt im Ablauf: etwas, das der Ablauf erzeugen sollte, wird direkt geschrieben (Seed, Stempel, UPDATE). |
| C | Das Ergebnis wird aus der Datenbank gelesen statt vom Artefakt. |
| D | Übersprungene Zellen (test.skip / fixme). |
| E | Zeitstempel-Manipulation in Zeilen statt einer E2E-gesteuerten Uhr. |
| F | Eine Route, die kein Nutzer erreicht. |
| M | Gemockte API: der Browser fährt das gebaute Web-Bundle, aber jeder /api/**-Aufruf wird von page.route-Fixtures beantwortet (web/tests/e2e/vite-mock-api.ts, fixtures.ts). Nach der Definition des Handbuchs („trifft die echte laufende App”) ist das kein E2E-Test; es ist das Contract-Konformitäts-Geschirr über den ADR-0047-Fixtures (fixtures.contract.test.ts prüft die Fixtures gegen das OpenAPI-Schema). Die Mechanik-Regeln (kein Sleep, kein Skip, Retries, Video) gelten trotzdem; die Real-Flow-Regeln 1–4 kann es nicht erfüllen und beansprucht sie nicht. |
| Lane | Konfiguration | Spec-Verzeichnis | Zellen | Läuft in | Browser |
|---|---|---|---|---|---|
| Mock (Smoke) | web/playwright.config.ts |
tests/e2e |
208 (113 chromium-desktop, 95 mobile-chrome) |
pr-checks.yml (Core-Loop-Netz, @pr-core-loop), e2e-nightly.yml mock-smoke + mock-full |
Chromium |
| Echt (Nightly) | web/playwright.nightly.config.ts (lokal: playwright.real.config.ts) |
tests/e2e-real |
41 (1 auth-setup, 20 chromium-desktop, 20 mobile-chrome) |
e2e-nightly.yml Real-Backend-Job, 06:00 UTC |
Chromium |
Layout-Steuerung ist seit 2026-09-04 eine Eigenschaft des Projekts: ein Test mit
@desktop-only im Titel betritt das Projekt mobile-chrome nie, @mobile-only nie
chromium-desktop (grepInvert am Projekt). Die 19 test.skip-Zellen, die das vorher
ausdrückten, sind weg — ein gesteuerter Test wird nicht gelistet, ein übersprungener
wurde gelistet und schwieg.
Die Tabelle
Abschnitt betitelt „Die Tabelle“Die Selektor-Spalten zählen Aufrufstellen in der Spec-Datei: testid = getByTestId /
data-testid; role = getByRole; text = getByText / getByLabel /
getByPlaceholder; locator = .locator(; css = ein .locator(, das an einer Klasse
oder Id hängt. Regel 6 des Standards lässt nur data-testid zu, also ist jede Zahl
ungleich null unter role, text und css offene Schuld — hier aufgeführt, damit sie
sich nicht wegdenken lässt.
Mock-Lane (web/tests/e2e)
Abschnitt betitelt „Mock-Lane (web/tests/e2e)“Mock-Lane, umgestellt am 05.09.2026. Jede Mock-Spec selektiert jetzt per data-testid;
die Zahlen unten sind der Stand nach dieser Umstellung. Zwei .locator(-Aufrufe bleiben
absichtlich, es ist Bibliotheks-DOM UNTER einem Element, das wir markieren:
.cm-content in memory-editor-body (CodeMirror) und .xterm-screen in log-terminal
(xterm.js). visual.spec.ts behält zwei Dokument-Sonden (html[data-theme] und die
Screenshot-Maske über time-Elemente) - das Dokument ist kein Produktelement.
Attribut-Selektoren, die bei einer data-testid BEGINNEN ([data-testid="backlog-column"] [data-state=queued], [data-testid][data-variant=nav]), zählen als locator, nicht als
Schuld. marketplace-subscription.spec.ts war die letzte Spec mit Rollen-Mischung und ist
seit demselben Tag auf Ids.
| Spec | Zellen (Desktop / Mobil) | Klasse | Abkürzung, mit Bezug | testid / role / text / locator / css | Gefahrene Routen |
|---|---|---|---|---|---|
accessibility.spec.ts |
8 / 8 | M | gemockte API | 1 / 0 / 0 / 0 / 0 | ein axe-Durchlauf je gelistetem Pfad |
app-flows.spec.ts |
8 / 14 | M | gemockte API; 6 Tests @mobile-only |
64 / 0 / 0 / 0 / 0 | /, /inbox, /inbox?lang=de, /inbox?modal=…, /login?lang=de, … |
badge-coordinator.spec.ts |
2 / 2 | M | gemockte API; prüft die Fetch-Zählung des Leader-Tabs über page.route |
1 / 0 / 0 / 0 / 0 | / in drei Tabs |
core-loop.spec.ts |
5 / 0 | M | gemockte API; describe @desktop-only |
17 / 0 / 0 / 0 / 0 | /backlog, /cli, /runs/{id}, /tasks/{id} |
crud-matrix.spec.ts |
9 / 0 | M | gemockte API; describe @desktop-only |
82 / 0 / 0 / 1 / 1 | /projects, /projects/new, /resources, /reports/schedules, /intelligence/memory, … |
delivery-debt-surfaces.spec.ts |
14 / 5 | M | gemockte API; 9 Tests @desktop-only |
72 / 0 / 0 / 1 / 1 | /inbox?source=governor, /reports/usage, /settings?tab=…, /admin?tab=payouts…, /cli |
marketplace-subscription.spec.ts |
7 / 7 | M | gemockte API | 25 / 0 / 0 / 3 / 3 | /marketplace/{item}, /admin/marketplace/payouts?tab=fees |
memory.spec.ts |
1 / 1 | M | gemockte API | 15 / 0 / 0 / 0 / 0 | /intelligence/memory |
mobile-capture.spec.ts |
19 / 19 | M | gemockte API; Screenshot-Geschirr, keine DOM-Prüfung über die Aufnahme hinaus | 0 / 0 / 0 / 0 / 0 | die 19 Capture-Routen |
mobile-feed-scroll.spec.ts |
0 / 1 | M | gemockte API; @mobile-only |
3 / 0 / 0 / 0 / 0 | /runs/{id} |
mock-capture-desktop.spec.ts |
9 / 9 | M | gemockte API; Screenshot-Geschirr | 0 / 0 / 0 / 0 / 0 | die 9 Desktop-Capture-Routen |
mock-conformance.spec.ts |
9 / 9 | M | gemockte API; Konformität der Screens zu den Fixtures | 4 / 0 / 0 / 2 / 0 | die 9 Konformitäts-Routen |
optimistic-conflicts.spec.ts |
2 / 0 | M | gemockte API; describe @desktop-only |
11 / 0 / 0 / 2 / 0 | /runs, /projects/{id}/backlog |
visual.spec.ts |
20 / 20 | M | gemockte API; toHaveScreenshot |
6 / 0 / 0 / 2 / 2 | die 20 Visual-Routen |
Echte Lane (web/tests/e2e-real)
Abschnitt betitelt „Echte Lane (web/tests/e2e-real)“| Spec | Zellen (Desktop / Mobil) | Klasse | Abkürzung, mit Bezug | testid / role / text / locator / css | Gefahrene Routen |
|---|---|---|---|---|---|
auth.setup.ts |
1 (Projekt auth-setup) |
A | registriert und meldet sich über die öffentliche API an (/api/auth/register Zeile 13, /api/auth/login Zeile 19) und speichert die Sitzung — Stammdaten, nach Regel 1 erlaubt |
– | – |
app-real-flows.spec.ts |
7 / 7 | D im CI (geparkt), A ×6 + B ×1 wenn sie läuft | Selektoren seit dem Abend-Durchgang Test-Ids (übrig: fünf locator("body")-Inhaltsprüfungen und eine a[href]-Aufzählung, keine Steuerelement-Selektoren); der Run-Detail-Test (Zeile 373, S4_NIGHTLY_SEEDED) betrachtet einen Run, den scripts/ui-capture/seed.sql per psql im Workflow-Schritt „Seed nightly data” geschrieben hat (.forgejo/workflows/e2e-nightly.yml Zeilen 115–137). Der Run ist das geprüfte Artefakt und wird geseedet, nicht von einem Ablauf erzeugt — Klasse B. Die anderen sechs fahren Login, Workspace- und Projektanlage, jede erreichbare Routenoberfläche, die Legacy-Umleitungen und die mobile Bottom-Bar durch die UI. |
0 / 18 / 6 / 13 / 2 | /build, /projects, /settings?tab=workspace, /runs/{id}, jede nutzererreichbare Route, die Legacy-Stubs |
app-negative-flows.spec.ts |
13 / 13 | D im CI (geparkt), A ×13 wenn sie läuft | Selektoren seit dem zweiten Abend-Durchgang Test-Ids (übrig: vier locator("body")-Inhaltsprüfungen und eine Statustext-Prüfung); API-Lesezugriffe über page.request.get (Zeilen 84–110, 488, 580) sind zusätzliche Konsistenzprüfungen über die öffentliche API (Regel 3 erlaubt sie neben einer DOM-Prüfung); der Admin-Request-Kontext (Zeile 135) und request.delete (Zeile 455) sind Aufrufe, die ein angemeldeter Admin machen kann (Regel 1). Kein Datenbankzugriff. |
12 / 26 / 9 / 38 / 29 | /login, /, /admin, /build/apps/new, Projekt- / Run- / Deploy- / Marketplace- / Einladungs-Abläufe |
Hilfstreiber (scripts/e2e, nicht Playwright)
Abschnitt betitelt „Hilfstreiber (scripts/e2e, nicht Playwright)“Die #1161-Messtreiber laufen von der Kommandozeile gegen einen gebooteten Stack. Sie stehen hier, weil Regel 1 auch für sie gilt.
| Treiber | Klasse | Abkürzung, mit Bezug | Aufrufer |
|---|---|---|---|
raise-gate.mjs |
A | prägt die Aufgabensitzung über das Produkt (POST /api/workspaces/{id}/tasks/{task_id}/e2e-session, Owner/Admin, nur mit SUPACLOUD_E2E_TASK_SESSION_MINT auf dem Bench-Server; MODE=prod lehnt das Flag beim Start ab). Bis 2026-09-04 war das der eine Klasse-B-Stempel im Treiberbestand (UPDATE tasks SET session_id per psql); alles danach (der MCP-Handler, der Insert in task_tool_approvals, das Hub-Ereignis, der Telegram-Push) war schon der echte Weg, jetzt ist es auch der Eintritt. |
telegram-surface-check.mjs, form-surface-check.mjs |
| alle anderen Treiber | A | nur öffentliche API und die Chat-Oberflächen; telegram-surface-check.mjs liest eine Zählung per psql (Zeile 468) als zusätzliche Prüfung neben der Oberflächenprüfung |
– |
Die frühere B-Zeile wurde durch eine Produktfähigkeit ersetzt, nicht durch eine
Treiberänderung: die E2E-gesteuerte Sitzungsprägung (dieselbe Form wie die E2E-Uhr des
Handbuchs, eine Fähigkeit, die nur der E2E-Modus freigibt). Der Bench-Server setzt das
Flag in boot-nightly-stack.sh.
Korrektur vom 2026-09-04, abends
Abschnitt betitelt „Korrektur vom 2026-09-04, abends“Die erste Fassung dieser Seite zählte die Mock-Lane und schrieb „A” über die echte Lane. Das war
falsch: beide Spec-Dateien der echten Lane begannen mit test.skip(!process.env.E2E_REAL_QUALIFICATION, …),
und nichts setzt diese Variable — nicht der Nightly-Workflow, nicht package.json, keine Konfiguration.
Der letzte grüne Nachtlauf (2026-08-29, Task 206965) lief „41 tests: 40 skipped, 1 passed” — die eine
bestandene Zelle ist auth.setup. Die Lane war seit 2026-08-06 ein leeres Grün (Commit 34d95957a:
die #791-Qualifikationsklassen C2/C3/E1 ritten in jedem Nachtlauf rot, #840, und wurden geparkt; #791 hält
fest, dass die Reaktivierung je Lane und vom Owner entschieden wird). Die Parkung bleibt, bis der Owner
entscheidet; der MECHANISMUS änderte sich auf feat/e2e-real-lane-testids: jede geparkte Zelle trägt
@qualification, die Konfigurationen schließen das Tag auf Projektebene aus, solange
E2E_REAL_QUALIFICATION=1 nicht gesetzt ist, und die Auflistung zeigt 1 Test (geparkt) oder 39
(Qualifikation an) statt 40 stiller Skips. Derselbe Durchgang machte die Seed-Vorbedingung zu
@nightly-seeded, die zwei Layout-Skips zu Projekt-Tags und entfernte die eigenen Sleeps der echten Lane
(drei 100-ms-Pacing-Sleeps und zwei Retry-Backoffs), die die erste Fassung ebenfalls nicht gezählt hatte.
Zählung
Abschnitt betitelt „Zählung“| Klasse | Zellen / Stellen | Wo |
|---|---|---|
| A | 40 Zellen der echten Lane + auth.setup — nur mit E2E_REAL_QUALIFICATION=1; im heutigen Nachtlauf sind sie D |
tests/e2e-real |
| B | 1 | app-real-flows.spec.ts:373 (geseedeter Run) |
| C | 0 | – |
| D | 40 im CI (die geparkte echte Lane, siehe Korrektur oben); 0 test.skip-Aufrufe verbleiben in beiden Lanes |
19 Mock-Lane-Skips wurden am 2026-09-04 zu Projekt-Tags (app-flows 6 @mobile-only, delivery-debt-surfaces 9 @desktop-only, core-loop / crud-matrix / optimistic-conflicts describe-weit @desktop-only, mobile-feed-scroll @mobile-only) |
| E | 0 | kein created_at- / updated_at-Schreibzugriff in scripts/e2e oder den Specs |
| F | 0 | jede gefahrene Route ist die Route eines angemeldeten Nutzers oder Admins; die Legacy-Stubs werden absichtlich gefahren, um die Umleitung zu prüfen |
| M | 208 Zellen | tests/e2e |
| Regel-6-Schuld | echte Lane: am 2026-09-04 getilgt (Inhaltsprüfungen ausgenommen); Mock-Lane: role 178, text 70, .locator( 46 (27 an Klasse oder Id verankert) noch offen — die Mock-Lane ist nach dem Standard kein E2E, die Migration dort ist Hygiene |
je Spec oben |
Mechanik — wo jede Regel steht
Abschnitt betitelt „Mechanik — wo jede Regel steht“| Handbuch-Regel | Stand 2026-09-04 |
|---|---|
| Playwright, Chromium primär | Ja. Firefox- und WebKit-Smoke-Projekte fehlen: das CI-Image bringt seine Browser vorinstalliert mit (PLAYWRIGHT_BROWSERS_PATH, kein npx playwright install in den Workflows), also kommen die beiden weiteren Browser mit dem Image (bw-infra scripts/ci-images), und die Konfigurationen ergänzen dann die zwei Projekte. |
trace: on-first-retry, video: retain-on-failure |
Ja, alle drei Konfigurationen. |
| Auto-Retry höchstens einmal | Ja: retries: process.env.CI ? 1 : 0 in allen drei Konfigurationen (vorher 2). |
Kein waitForTimeout / Sleep |
Ja: 0 Aufrufe in tests/e2e und tests/e2e-real. Sieben Stellen wurden am 2026-09-04 umgeschrieben: badge-coordinator.spec.ts (drei Sleeps → expect.poll auf den Fetch-Zähler je Tab), core-loop.spec.ts (ein Sleep → page.waitForResponse auf das Snapshot-GET, das der Route-Handler des Tests selbst vorbereitet), mobile-capture / mock-capture-desktop / mock-conformance (je ein Sleep → settleForCapture in route-helpers.ts: Netzwerk ruhig, Schriften geladen, kein aria-busy, zwei gemalte Frames). |
Kein test.skip / fixme; gepinnte Defekte über test.fail() + Ticket |
Ja: 0 Skips; heute ist kein Defekt gepinnt, also 0 test.fail(). |
Nur data-testid |
Nein — siehe die Zeile zur Regel-6-Schuld; die Migration sind additive Commits am Produkt (eine Test-Id kommt dorthin, wo der Test hinschaut), Spec für Spec. |
| Falsifikationsnachweis für jede umgeschriebene Spec | Unten festgehalten für die Umschreibung vom 2026-09-04. |
| Spec-Wahrheitstabelle | Diese Seite. |
| ≤ 10 Minuten Wanduhr | Mock-Lane: lokal 3,5 min (208 Zellen, 8 Worker); 9,8 min im nächtlichen mock-e2e-full-Job auf dem 4-CPU-Runner, Build eingeschlossen — am Rand des Budgets, also gehört der Build in einen eigenen Schritt, bevor Playwrights Uhr läuft. Echte Lane: der Playwright-Schritt des Nightly-Jobs; seine Dauer wird aus dem Workflow-Lauf gelesen, sobald die Lane wieder startet (PR #1265). |
| Nur lokal, nie Staging | Ja: die echte Lane bootet ihren eigenen Stack (scripts/e2e/boot-nightly-stack.sh), die Mock-Lane braucht kein Backend. Keine Konfiguration zeigt auf einen Stage- oder Produktions-Origin. |
| Jeder Produktionsvorfall nennt die Spec, die ihn gefangen hätte | 2026-09-01: die nächtliche Real-Backend-Lane startete nicht mehr (act prüfte das --memory=12g des Workflows gegen die 6-GiB-Kappe der Runner-Lane und lehnte den Job vor seinem ersten Schritt ab). Keine Spec kann eine Lane fangen, die nicht startet; die Sicherung ist die Workflow-Deklaration (--memory=6g, PR #1265) plus die konsumentenseitige Speicher-Sicherung in bw-infra. |
Falsifikationsprotokoll — Mechanik-Umschreibung 2026-09-04
Abschnitt betitelt „Falsifikationsprotokoll — Mechanik-Umschreibung 2026-09-04“Regel 7 verlangt drei Läufe je umgeschriebener Spec: (a) die alte Spec bleibt grün mit absichtlich gebrochenem Produktpfad, (b) die neue Spec wird auf demselben Bruch rot, (c) die neue Spec ist auf dem intakten Pfad grün. Die Umschreibung vom 2026-09-04 hat Wartebedingungen geändert, nicht Zusicherungen — die alten Specs prüften bereits die Fetch-Zählung und den Follow-Snapshot, sie schliefen nur eine feste Zeit, bevor sie es taten. Lauf (a) kann darum keine Lücke zeigen, die die alte Spec hatte; was die Läufe belegen, ist, dass die neuen Wartebedingungen echte Bedingungen sind (rot, wenn der Pfad gebrochen ist) und dass der intakte Pfad grün ist.
| Lauf | Was | Ergebnis |
|---|---|---|
| (c) intakt | volle Mock-Lane, beide Projekte, vorgebautes Web (PLAYWRIGHT_PREBUILT_WEB=1), --ignore-snapshots wie im CI-Job |
siehe Lane-Historie unten: der Lauf vor dem Fixture-Fix reproduzierte den CI-Fehler exakt (204 grün, 4 rot, 2,1 min); der letzte Lauf nach dem Fix steht dort |
| (b) gebrochen, Badge | der Badge-Fetch des Produkts (inbox-actionable.ts, beide Fetch-Stellen) passiert nie; neu gebaut; badge-coordinator.spec.ts |
rot in beiden Tests: expect.poll auf den Fetch-Zähler je Tab läuft nach 10 s aus („no tab fetched … hits=[0,0,0]“) |
| (a) alt, Badge | die Spec vor der Umschreibung auf demselben gebrochenen Build | ebenfalls rot: nach ihrem festen 2,5-s-Sleep scheitert dieselbe Zähler-Zusicherung — die Umschreibung hat das Warten geändert, nicht die Zusicherung, also gibt es keine Lücke, die (a) zeigen könnte |
| (b) gebrochen, Follow | das kalte Snapshot-GET wird nie beantwortet (das Mock-Backend hält es); Follow-Test in core-loop.spec.ts |
rot: page.waitForResponse auf den Snapshot löst nie auf (Test-Timeout 30 s) — das Warten ist eine echte Bedingung, kein No-op |
| (a) alt, Follow | die Spec vor der Umschreibung auf demselben Hänger | auch rot, später und unschärfer: ihr expect.poll auf den Request-Zähler besteht (der Request ging raus), der 400-ms-Sleep verstreicht, und die Backfill-Zusicherung nach dem Resync scheitert |
Lane-Historie, am 2026-09-04 aus der Forgejo-Actions-Datenbank gelesen
Abschnitt betitelt „Lane-Historie, am 2026-09-04 aus der Forgejo-Actions-Datenbank gelesen“| Lane | Zustand | Seit | Ursache | Behebung |
|---|---|---|---|---|
Mock voll (mock-e2e-full) |
rot | 2026-09-02 (grün am 31.08. und 01.09.) | marketplace-subscription.spec.ts trug ein festes Bezahlt-bis-Datum (2026-09-01); ab 02.09. sagte die Karte „access has ended” — 4 Zellen rot in beiden Projekten, 204 grün, 9,8 min auf dem 4-CPU-Runner inklusive Build |
Fixture-Daten und die zwei Datums-Erwartungen folgen der Lauf-Uhr (daysFromNow, calendarDay) — lokal 14/14 |
| Mock voll, lokal | rot auf jedem Entwicklerrechner mit einem Backend auf :8080 |
— | das SSR-Auth-Tor der Preview löste das Cookie gegen ein fremdes lokales Backend auf und leitete nach /login um, bevor die In-Page-Mocks erreicht wurden (≈60 Zellen) |
webServer.env.BACKEND_URL in playwright.config.ts auf einen toten Port gepinnt |
Echt nächtlich (e2e-nightly) |
rot | 2026-08-30 | 30./31.08.: ein Kommentar in der fortgesetzten docker run-Kette von boot-nightly-stack.sh beendete das Kommando vor dem Image-Namen (auf main bereits behoben, das Skript dokumentiert es); ab 01.09.: act lehnte den Job vor dem ersten Schritt ab (--memory=12g gegen die 6-GiB-Kappe der Lane) |
PR #1265 (--memory=6g) plus die konsumentenseitige Sicherung in bw-infra |
Smoke Core-Loop (smoke-e2e, PR-Netz) |
grün | — | — | — |
| Mock voll, lokal final | grün | 2026-09-04 | der letzte intakte Lauf nach den Korrekturen, beide Projekte, --ignore-snapshots wie im CI |
208 von 208 Zellen bestanden in 3,5 min (Lauf vor dem Fix: 204 / 4 in 2,1 min) |