# 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
