Skip to content

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.

MarkerLocatorVerwendung
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 `/savespeichern/i`page.getByRole("button", { name: /save|speichern/i })
role="table" auf der List-Tabellepage.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:

FeldtypInteraktionRenderer-Vorgabe
text, number, date, timestamp, tzlocator.fill(string)<input> oder contenteditable-Wrapper, den Playwrights fill akzeptiert.
booleanlocator.setChecked(bool)Echte Checkbox (native oder role="checkbox" mit aria-checked). Switches müssen das Role erfüllen.
selectlocator.selectOption(string)Native <select> bevorzugt. Custom-Dropdowns müssen Playwrights selectOption emulieren oder einen Override-Hook bekommen.
money, embedded, locatedTimestamp, file/image/files/imagesNoch 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 via POST /api/write mit 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 den field-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.ts neben dem Feature oder unter e2e/ (M4 entscheidet final).
  • Hand-geschrieben: *.e2e.ts — gleicher Suffix, Vitest excluded, Biome-Override entspricht dem der anderen Test-Suffixe (noConsole/noNonNullAssertion aus).
  • 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:

  1. Generator-Output (generateE2ESpec) und ggf. E2ETestSpec-Shape
  2. Renderer (app/, ui-core)
  3. Dieses Dokument
  4. 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.

  • 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”