E2E-Test Contract — Renderer ↔ Generator
Zusammenhang: Der E2E-Generator (
@kumiko/framework/testing → generateE2ESpec
generateZodFixture) leitet JSON-serialisierbare Test-Specs aus der Registry ab; ein externer Playwright-Worker liest sie und führt sie aus. Damit die generierten Tests tatsächlich grün werden können, muss der UI-Renderer (app/, ui-core) eine kleine Menge Marker setzen. Dieses Dokument ist der Vertrag.
Warum ein Vertrag
Der Generator weiß nichts über die konkrete UI-Implementierung — er sieht nur die Registry. Die Tests interagieren mit der UI über stabile, semantische Anker. Wenn Renderer und Generator sich über diese Anker nicht einig sind, fallen sämtliche generierten Tests still auf die Nase. Das Vertragsdokument ist die einzige Stelle an der sich beide Seiten treffen.
Die vier Marker
Der Renderer MUSS auf jedem relevanten Element diese Attribute/Rollen setzen. Der Generator emittiert Locators, die exakt danach greifen.
| Marker | Locator | Verwendung |
|---|---|---|
data-testid="field-<name>" | page.getByTestId("field-<name>") | Pro Input/Select/Checkbox im entityEdit-Formular. <name> = Feldname aus der EntityDefinition (z.B. title, price). |
data-testid="field-error-<name>" | page.getByTestId("field-error-<name>") | Pro Validation-Hint. Sichtbar werden, sobald ein required-Feld leer abgespeichert wurde. |
| Save-Button Label `/save | speichern/i` | page.getByRole("button", { name: /save|speichern/i }) |
role="table" auf der List-Tabelle | page.getByRole("table") | Native <table> reicht; wenn der Renderer Card-Views baut, fällt list-renders still kaputt — dann muss der Generator ein Kind dafür bekommen. |
Die Fixture-Row (list-has-fixture-row, edit-save-persists) wird per getByText(<value>)
gesucht. Das heißt der Wert der Identifying-Column muss im DOM sichtbar sein, nicht
nur in einem Tooltip oder Input-Value.
Form-Interaktionen pro Field-Typ
Der Generator differenziert nach FieldDefinition.type und emittiert unterschiedliche
Playwright-Calls. Der Renderer muss das passende Markup bereitstellen:
| Feldtyp | Interaktion | Renderer-Vorgabe |
|---|---|---|
text, number, date, timestamp, tz | locator.fill(string) | <input> oder contenteditable-Wrapper, den Playwrights fill akzeptiert. |
boolean | locator.setChecked(bool) | Echte Checkbox (native oder role="checkbox" mit aria-checked). Switches müssen das Role erfüllen. |
select | locator.selectOption(string) | Native <select> bevorzugt. Custom-Dropdowns müssen Playwrights selectOption emulieren oder einen Override-Hook bekommen. |
money, embedded, locatedTimestamp, file/image/files/images | — | Noch nicht generisch. Generator skippt; Tests müssen hand-geschrieben ergänzt werden. |
Test-Kinds
Der Generator emittiert pro Screen bis zu zwei Kinds:
- entityList →
list-renders: navigiert auf die Screen-URL, erwartet die Tabelle. - entityList →
list-has-fixture-row: seedet eine Row viaPOST /api/writemit dem Create-Handler (<feature>:write:<entity>:create), lädt die Liste, erwartet den identifying value. - entityEdit →
edit-validates-required: öffnet leeres Formular, klickt Save, erwartet für jedes required Feld denfield-error-<name>Hint. - entityEdit →
edit-save-persists: füllt alle editierbaren Felder, klickt Save, navigiert ggf. zur Liste und erwartet den identifying value.
custom Screens werden übersprungen (keine generische Annahme möglich). Der
Test-Autor ergänzt Custom-Tests hand-geschrieben als *.e2e.ts-Datei neben dem Feature.
Dateikonvention
- Generiert:
*.e2e.tsneben dem Feature oder untere2e/(M4 entscheidet final). - Hand-geschrieben:
*.e2e.ts— gleicher Suffix, Vitest excluded, Biome-Override entspricht dem der anderen Test-Suffixe (noConsole/noNonNullAssertionaus). - Playwright: matcht
**/*.e2e.ts(Config kommt in M4).
Änderungen an diesem Vertrag
Jede Änderung an einem Marker ODER an der Test-Kinds-Liste MUSS in EINEM Commit passieren:
- Generator-Output (
generateE2ESpec) und ggf.E2ETestSpec-Shape - Renderer (app/, ui-core)
- Dieses Dokument
- Snapshot-Test in
packages/framework/src/testing/__tests__/e2e-generator.test.ts
Sonst sind die generierten Specs still kaputt, bis jemand sie zufällig ausführt.
Cross-Links
- UI-Architecture (Screens, Layouts, Forms):
ui-architecture.md - UI-Renderer (Package-Struktur, Primitives):
ui-renderer.md - Testing-Conventions (Integration vs. E2E):
CLAUDE.md→ “Vorgehen bei neuen Features”