## ADDED Requirements
### Requirement: Layer 0 dep-graph isolation
The `@suaveplan/testing-dom` `dependencies` and `peerDependencies` MUST NOT contain any `@suaveplan/*` workspace package. `devDependencies` MAY include `@suaveplan/testing` (utilities) for the package's own tests, but the production runtime closure MUST NOT contain it.
#### Scenario: Runtime dep-graph guard
- **WHEN** `bunx turbo ls --filter=@suaveplan/testing-dom...` is run (forward closure of runtime deps)
- **THEN** no `@suaveplan/*` package appears
#### Scenario: devDep allowance
- **WHEN** `package.json` is parsed
- **THEN** `dependencies` and `peerDependencies` are free of `@suaveplan/*` AND `devDependencies` may contain `@suaveplan/testing` (this dep is not visible to consumers)
### Requirement: Dual-runner conditional exports preserved
The package `exports["."]` SHALL route the `vitest` condition and the `default` condition to the vitest barrel and the `bun` condition to the bun-test barrel. The exports MUST expose `describe`, `it`, `test`, `expect`, `vi`, `beforeAll`, `beforeEach`, `afterAll`, `afterEach`, `fn`, `spy`, `stub`, `mock`, `fakeClock`, `restoreMocks`, `restoreTimers` under both conditions.
#### Scenario: Vitest consumer
- **WHEN** a consumer running under vitest imports `{ describe, it, expect, vi }` from `@suaveplan/testing-dom`
- **THEN** the symbols come from `vitest`
#### Scenario: bun-test consumer
- **WHEN** a consumer running under `bun test` imports the same names
- **THEN** the symbols come from `bun:test`
### Requirement: `createTestConfig()` produces WSL2-safe configs
The factory SHALL emit a vitest config with `pool: "forks"`, `setupFiles: ["@suaveplan/testing-dom/polyfills"]`, `fileParallelism: false` when `environment === "happy-dom"`, and dynamically-loaded `@vitejs/plugin-react` if that optional peer is installed. When the caller's runner is `bun:test`, the factory SHALL return a `BunTestConfig` instead.
#### Scenario: Happy-dom config
- **WHEN** `createTestConfig({ environment: "happy-dom" })` is awaited and `@vitejs/plugin-react` is present
- **THEN** the returned config includes the React plugin, has `pool === "forks"`, `fileParallelism === false`, and `setupFiles` includes the polyfills path
#### Scenario: Missing React peer doesn't break non-React consumers
- **WHEN** `createTestConfig({ environment: "happy-dom" })` is awaited and the React peer is NOT installed
- **THEN** the factory returns the vitest config without the plugin, no error thrown
### Requirement: Polyfills are side-effect-only
The `@suaveplan/testing-dom/polyfills` subpath MUST resolve to a side-effecting module that installs `globalThis.crypto` (when missing), `globalThis.TextEncoder`, and `globalThis.TextDecoder`. The module MUST export nothing.
#### Scenario: Idempotent polyfill install
- **WHEN** `import "@suaveplan/testing-dom/polyfills"` runs twice in the same process
- **THEN** the second import is a no-op (no errors, no overwrites)
### Requirement: `renderComponent` wraps React render with cleanup and userEvent
`renderComponent(ui, options?)` SHALL render the React tree using `@testing-library/react`'s `render`, auto-register cleanup on `afterEach`, and return the standard `RenderResult` extended with a `userEvent` instance.
#### Scenario: Render and event dispatch
- **WHEN** `renderComponent()` is called and the returned `userEvent.click(getByRole('button'))` is awaited
- **THEN** the click is dispatched and observable side-effects run
#### Scenario: Render failure produces RenderError
- **WHEN** the React tree throws synchronously during render
- **THEN** `renderComponent` throws `RenderError` with `code === "TESTING_DOM.RENDER.SETUP_FAILED"` and `cause` set to the original error
### Requirement: jest-dom matchers extension
Importing `@suaveplan/testing-dom/matchers` SHALL extend vitest's `expect` with the `@testing-library/jest-dom` matcher set. Common matchers MUST be available afterward: `toBeInTheDocument`, `toHaveTextContent`, `toBeDisabled`, `toBeEnabled`, `toHaveAttribute`, `toBeVisible`, `toHaveClass`, `toHaveValue`.
#### Scenario: Matcher availability after side-effect import
- **WHEN** a test file does `import "@suaveplan/testing-dom/matchers"` and then asserts `expect(node).toBeInTheDocument()`
- **THEN** the assertion runs without "matcher does not exist" errors
### Requirement: Structured errors with stable codes
All errors thrown from `@suaveplan/testing-dom` MUST be subclasses of `TestingDomError extends Error` carrying `{ code, cause?, context? }`. Codes match `^TESTING_DOM\.[A-Z]+\.[A-Z_]+$`.
#### Scenario: Coded error
- **WHEN** any helper throws
- **THEN** the thrown value `instanceof TestingDomError` is true AND `err.code` matches the regex
### Requirement: Coverage and docs gates
Istanbul statement + branch coverage across `packages/universal/testing-dom/src/` (excluding `*.bench.ts`, `*.schemas.ts`, `runner/bun-test.ts`) MUST be ≥ 95% on every dimension. Every implementation file (`*.ts` other than test/bench/schemas/types-in-feature-dirs/index barrels) MUST have a sibling `.md` of ≥ 200 words with simple + advanced examples.
#### Scenario: CI coverage gate
- **WHEN** `bunx turbo test:coverage --filter=@suaveplan/testing-dom` runs
- **THEN** statement, branch, function, and line coverage all ≥ 95%
### Requirement: Test placement follows the genesis hard rule
Per-feature unit tests SHALL be co-located in `src/` (named `.test.ts` next to `.ts`). Cross-cutting tests (dep-graph guard, tree-shake guard, no-disallowed grep guard, property tests) MUST live in `tests/` sibling to `src/`.
#### Scenario: Per-feature unit tests live next to source
- **WHEN** a directory under `src/` contains a feature module like `render/render-component.ts`
- **THEN** its unit tests are at `src/render/render-component.test.ts` (sibling) AND not in `tests/`
#### Scenario: Cross-cutting tests live in tests/
- **WHEN** a test exercises package-level concerns (dep-graph, tree-shake, no-disallowed grep, property invariants over multiple modules)
- **THEN** it lives under `tests/` AND not in `src/`