# Data Management (Reduced Scope for E2E) Keep E2E data handling minimal and deterministic. ## In scope 1. API-based setup in `beforeEach` using Playwright `request` fixture 2. API-based cleanup in `afterEach` when tests create persistent entities 3. Dedicated test users for stable parallel execution 4. Tenant/community isolation strategy ## Out of scope - complex factory frameworks - large synthetic seed systems - replacing app behavior with heavy mocks ## Recommended approach - create only the minimum data needed for each test - **always clean up the data a run creates** — a run must leave the tenant as it found it (no accumulation). Accumulated entities slow lists, cause confusing "duplicate"-looking rows, and break `filter({ hasText })` with strict-mode violations. - encode ownership/traceability in generated test data names - tests that modify or delete entities MUST create their own data first — never rely on pre-existing rows - **golden fixtures are exempt** — stable, manually-created base entities referenced by name (templates, goal cycles, competencies) must never be deleted Example naming — include the worker index, not just `Date.now()`. Under parallel workers (`--repeat-each`, default local workers > 1) two runs can hit `Date.now()` in the same millisecond and create same-named entities: ```ts const name = `E2E Cycle ${Date.now()}-${test.info().workerIndex}`; ``` ## Self-cleaning suites Track created names and delete them in teardown. The teardown layer depends on the suite: - **`afterEach`** — independent, single-test entities (API or UI delete). - **`afterAll`** — `serial` suites and **cross-app** specs, where one entity is seeded once and consumed by several tests / by the web side. "Used later in this run" justifies **deferring** the delete to `afterAll`, never skipping it. In an `afterAll` you have no test-scoped `page` fixture — open a fresh `browser.newContext({ baseURL, storageState })`, run the page-object `delete*` helper, then close it. Page objects should expose idempotent cleanup helpers (`deleteCycle(name)`, `cleanup*IfExists(name)`) that no-op when the entity is already gone, so teardown is best-effort and order-independent. ## Strict mode prevention When asserting rows in a table, always filter by the unique `Date.now()` name, never by a shared description or generic text. Leftover entities from previous runs cause Playwright strict mode violations when `filter({ hasText })` matches multiple rows. ```ts // Good — unique name guarantees single match const name = `E2E Item ${Date.now()}`; await page.createItem(name); await expect(tableRows.filter({ hasText: name })).toBeVisible(); // Bad — shared text matches multiple leftover rows await expect(tableRows.filter({ hasText: 'E2E test description' })).toBeVisible(); ``` For tests that don't create data (e.g., "cancel delete"), use positional locators like `tableRows.first()` instead of filtering by content. ## Parallel safety - avoid shared mutable entities across tests - use user-per-suite or user-per-worker where feasible - isolate by community/tenant when modules require different context ## Failure hygiene - cleanup should be best-effort and explicit - do not hide primary assertion failures with cleanup errors