Initial Commit
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
## 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(<Button />)` 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 `<feature>.test.ts` next to `<feature>.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/`
|
||||
Reference in New Issue
Block a user