6.0 KiB
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.jsonis parsed - THEN
dependenciesandpeerDependenciesare free of@suaveplan/*ANDdevDependenciesmay 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 testimports 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-reactis present - THEN the returned config includes the React plugin, has
pool === "forks",fileParallelism === false, andsetupFilesincludes 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 returneduserEvent.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
renderComponentthrowsRenderErrorwithcode === "TESTING_DOM.RENDER.SETUP_FAILED"andcauseset 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 assertsexpect(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 TestingDomErroris true ANDerr.codematches 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-domruns - 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 likerender/render-component.ts - THEN its unit tests are at
src/render/render-component.test.ts(sibling) AND not intests/
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 insrc/