Initial Commit
This commit is contained in:
86
openspec/archive/2026-04-19-enterprise-testing-dom/design.md
Normal file
86
openspec/archive/2026-04-19-enterprise-testing-dom/design.md
Normal file
@@ -0,0 +1,86 @@
|
||||
## Context
|
||||
|
||||
`@suaveplan/testing-dom` is the second testing-infrastructure package on genesis. It exists because:
|
||||
|
||||
1. Every React component test needs a DOM environment (happy-dom or jsdom under vitest).
|
||||
2. The genesis testing-package separation rule (CLAUDE.md #9) puts the test-runner adapter here, not in `@suaveplan/testing` (which stays universal/runner-agnostic).
|
||||
3. The staged runner/config/polyfills code from the `enterprise-testing` split (`packages/universal/testing-dom-staging/`) is its starting material.
|
||||
|
||||
Layer 0 dependency profile: external catalog deps only. No `@suaveplan/*` workspace deps. The package's own tests run under vitest with happy-dom.
|
||||
|
||||
## Goals / Non-Goals
|
||||
|
||||
**Goals:**
|
||||
|
||||
- Absorb staged runner/config/polyfills cleanly from `testing-dom-staging/`.
|
||||
- Preserve the dual-runner conditional export (`vitest` default / `bun` condition) for the runner re-exports.
|
||||
- Provide thin React/DOM helpers built on `@testing-library/react` + `@testing-library/jest-dom` + `@testing-library/user-event` (v14+).
|
||||
- Local `TestingDomError` hierarchy with `{ code, cause, context }` shape; codes follow `TESTING_DOM.<DOMAIN>.<REASON>`.
|
||||
- `createTestConfig()` (moved from testing) absorbs WSL2 pool/polyfill defaults and dynamic React-plugin loading.
|
||||
- ≥ 95% test coverage; co-located unit tests + cross-cutting `tests/` per the test-placement hard rule.
|
||||
|
||||
**Non-Goals:**
|
||||
|
||||
- Forking `@testing-library/*` internals — the package is a thin wrapper.
|
||||
- Legacy `fireEvent` helpers in the default export — `userEvent` v14 only.
|
||||
- Wrapping `@suaveplan/error` / `@suaveplan/opentelemetry` / `@suaveplan/schema-registry` — these are forbidden workspace deps. Local equivalents.
|
||||
|
||||
## Decisions
|
||||
|
||||
### D1: Local `TestingDomError` hierarchy
|
||||
|
||||
Mirrors `@suaveplan/testing`'s `TestingError` shape but with `TESTING_DOM.<DOMAIN>.<REASON>` codes. Subclasses: `RenderError`, `MatcherError`, `ConfigError`, `AbortedError`. Reason: rule #8/#9 — no `@suaveplan/*` workspace deps. Shape-compatible with future `@suaveplan/error` adapter.
|
||||
|
||||
### D2: Runner adapter via conditional exports (preserved from staging)
|
||||
|
||||
`package.json` `exports["."]` routes vitest condition → vitest barrel, bun condition → bun-test barrel, default → vitest. `mocks.ts` provides runner-neutral `fn`/`spy`/`stub`/`mock`/`fakeClock` shims. The vitest dynamic-mock bracket-access trick (`vi["mock"]`) carries over from the staging code. **Coverage exception:** `runner/bun-test.ts` is excluded from istanbul coverage because vitest cannot resolve `bun:test` (same exception we already documented for testing).
|
||||
|
||||
### D3: `createTestConfig()` factory
|
||||
|
||||
Lifts `createTestConfig`, `detectRunner`, `detectEnvironment`, `createVitestConfig`, `createBunTestConfig` verbatim from staging. Hardcodes `pool: "forks"` (WSL2 vmThreads/threads fail), `setupFiles: ["@suaveplan/testing-dom/polyfills"]`, `fileParallelism: false` for happy-dom, optional dynamic `@vitejs/plugin-react` import.
|
||||
|
||||
### D4: React render wrapper — `renderComponent`
|
||||
|
||||
Thin wrapper over `@testing-library/react`'s `render` that:
|
||||
|
||||
- Wraps the tree in any provider stack the caller passes (e.g. router, query client) via a `wrapper` option
|
||||
- Auto-cleans on `afterEach` via `cleanup()` from `@testing-library/react`
|
||||
- Returns the standard `RenderResult` extended with a `userEvent` instance for ergonomic event dispatching
|
||||
- Returns `TestingDomError("TESTING_DOM.RENDER.SETUP_FAILED")` if the render itself throws
|
||||
|
||||
### D5: Accessibility-first query aliases
|
||||
|
||||
Re-export Testing Library queries verbatim AND add ergonomic aliases:
|
||||
|
||||
- `byRole(name, opts)` → `screen.getByRole(name, opts)` (strict get; throws on miss)
|
||||
- `findRole`, `queryRole` similar variants
|
||||
- `byLabelText`, `byText`, `byPlaceholderText` similarly
|
||||
Documentation in `.md` files steers callers toward role/label/text queries; `getByTestId` is re-exported but flagged as a fallback only.
|
||||
|
||||
### D6: User events via `userEvent` v14
|
||||
|
||||
Single export: `setupUser(options?)` returns a `UserEvent` instance with `pointerEventsCheck: 0` default for happy-dom (which doesn't fully simulate pointer events). When vitest fake timers are active (detected via `vi.isFakeTimers()`), wires `userEvent.setup({ advanceTimers: vi.advanceTimersByTime })` automatically.
|
||||
|
||||
### D7: jest-dom matchers extended via vitest's `expect.extend`
|
||||
|
||||
A side-effect import path `@suaveplan/testing-dom/matchers` extends vitest's `expect` with the `@testing-library/jest-dom` matcher set. Imported via `setupFiles` or per-test as a side effect.
|
||||
|
||||
### D8: Polyfills file
|
||||
|
||||
Lifted from staging — installs `globalThis.crypto` (from `node:crypto.webcrypto`), `globalThis.TextEncoder`/`TextDecoder` (from `node:util`) when missing. Side-effecting, exports nothing. Subpath: `@suaveplan/testing-dom/polyfills`.
|
||||
|
||||
### D9: OTEL via `@opentelemetry/api` (gated)
|
||||
|
||||
Optional debug instrumentation behind `process.env.TESTING_DOM_OTEL_ENABLED === "1"`. Counters: `testing_dom.render.count`, `testing_dom.user.click.count`. Histograms: `testing_dom.render.duration_ms`. Disabled by default — adds zero overhead in normal test runs.
|
||||
|
||||
### D10: Tests use `@suaveplan/testing` (utilities) directly
|
||||
|
||||
The package's own tests import primitive utilities (FakeClock, Factory, etc.) directly from `@suaveplan/testing`. This is the one allowed cross-genesis-package dep — but only as a `devDependency`, not a runtime dep. Production users of `testing-dom` never transitively pull `@suaveplan/testing`.
|
||||
|
||||
## Risks / Trade-offs
|
||||
|
||||
- **happy-dom limitations** — pointer events, intersection observer, resize observer aren't fully simulated. Mitigation: document, provide opt-in shims for the most-needed gaps.
|
||||
- **React 19 act() boundaries** — incorrect handling of async state updates leaks errors. Mitigation: `renderComponent` always awaits `act()` for the initial render.
|
||||
- **bun-test path** — same untestable-under-vitest issue as the original; excluded from coverage. Mitigation: add a `bun:smoke` script later that runs a tiny test fixture under `bun test` to verify the dual-runner contract works in anger.
|
||||
- **`@vitejs/plugin-react` optional peer** — missing peer must not break non-React consumers. Mitigation: dynamic import inside `try/catch` in `createTestConfig` (preserved from staging).
|
||||
- **`devDependency on @suaveplan/testing`** — D10 violates the spirit of "no @suaveplan/* deps in testing-infra packages" but only at devDep level. Tests need `FakeClock`, `Factory`, etc. Documented in CLAUDE.md exception note.
|
||||
@@ -0,0 +1,50 @@
|
||||
## Why
|
||||
|
||||
`@suaveplan/testing-dom` is the entry point for every React/DOM component test in the genesis monorepo and the home of the test runner adapter. Per the testing-package separation hard rule (see `openspec/project.md`), it owns:
|
||||
|
||||
- The vitest primitive re-exports (`describe`, `it`, `expect`, `vi`, `fn`, `spy`, `stub`, `mock`, `fakeClock`, `restoreMocks`, `restoreTimers`)
|
||||
- The `createTestConfig()` factory and runner/environment detectors (`detectRunner`, `detectEnvironment`, `createVitestConfig`, `createBunTestConfig`)
|
||||
- The polyfills setup file (`@suaveplan/testing-dom/polyfills`)
|
||||
- React/DOM-specific helpers built on `@testing-library/react`, `@testing-library/jest-dom`, `@testing-library/user-event`
|
||||
|
||||
Genesis Layer 0 — testing-infra packages take no `@suaveplan/*` workspace deps. External catalog deps only. The runner adapter + DOM helpers live here (not in `@suaveplan/testing`) because DOM tests already imply vitest+happy-dom; co-locating the runner adapter with the DOM helpers is the natural home.
|
||||
|
||||
## What Changes
|
||||
|
||||
- New package `@suaveplan/testing-dom` at `packages/universal/testing-dom/` (genesis-fresh, version `0.1.0`).
|
||||
- **Imports** the staged runner/config/polyfills code from `packages/universal/testing-dom-staging/` (created during the testing split refactor) and absorbs it cleanly.
|
||||
- **Substitutions per genesis rules:**
|
||||
- Local `TestingDomError extends Error` with `{ code, cause, context }` shape (no `@suaveplan/error`)
|
||||
- `@opentelemetry/api` direct (no `@suaveplan/opentelemetry`)
|
||||
- `zod` direct (no `@suaveplan/schema-registry`)
|
||||
- English-only `code` machine-contract messages (no `@suaveplan/i18n`)
|
||||
- **No `@suaveplan/*` workspace deps.** External catalog deps only.
|
||||
|
||||
### Public surface (this package)
|
||||
|
||||
- **Runner re-exports** (vitest under default condition; bun-test under `bun` condition): `describe`, `it`, `test`, `expect`, `vi`, `beforeAll`, `beforeEach`, `afterAll`, `afterEach`, `fn`, `spy`, `stub`, `mock`, `fakeClock`, `restoreMocks`, `restoreTimers`
|
||||
- **Config**: `createTestConfig`, `TestConfigOptions`, `TestRunner`, `TestEnvironment`, `VitestConfigOverrides`, `BunTestConfig`, `BunTestConfigOverrides`
|
||||
- **Polyfills**: `@suaveplan/testing-dom/polyfills` side-effect module
|
||||
- **DOM render**: `renderComponent` (typed wrapper around `@testing-library/react`'s `render` with sensible defaults: act-aware, cleanup hooked into vitest's `afterEach`)
|
||||
- **DOM queries**: re-exports `screen`, `within`, `findByRole`/`getByRole`/`queryByRole`, etc.; surfaces accessibility-first names with clearer aliases (e.g. `byRole(name, opts)` shortcut). `getByTestId` still re-exported but documented as a fallback.
|
||||
- **User events**: `userEvent` (v14+ instance) + a `setupUser()` factory wired to vitest fake timers when present
|
||||
- **Matchers**: extends `expect` with `@testing-library/jest-dom` matchers
|
||||
- **Errors**: `TestingDomError`, `RenderError`, `MatcherError`, `ConfigError`, `AbortedError` — each with stable `TESTING_DOM.<DOMAIN>.<REASON>` codes
|
||||
|
||||
## Capabilities
|
||||
|
||||
### New Capabilities
|
||||
|
||||
- `testing-dom`: DOM/React testing infrastructure for genesis — runner adapter (vitest+bun-test conditional exports), `createTestConfig` factory, polyfills, React 19 render helpers, accessibility-first queries, jest-dom matchers, user-event v14.
|
||||
|
||||
### Modified Capabilities
|
||||
|
||||
(none — no prior spec)
|
||||
|
||||
## Impact
|
||||
|
||||
- **Affected code**: `packages/universal/testing-dom/**` (fresh).
|
||||
- **Affected consumers**: every React/DOM-testing package (none yet on genesis; this is Layer 0).
|
||||
- **Version**: `0.1.0` (genesis baseline).
|
||||
- **Catalogs added/extended**: `@testing-library/react`, `@testing-library/jest-dom`, `@testing-library/user-event`, `@testing-library/dom` to a new `catalogs.dom-testing` (or extend `catalogs.testing`).
|
||||
- **Pre-publish gate**: `bunx turbo lint typecheck test --filter=@suaveplan/testing-dom` green + ≥ 95% coverage on touched modules.
|
||||
@@ -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/`
|
||||
78
openspec/archive/2026-04-19-enterprise-testing-dom/tasks.md
Normal file
78
openspec/archive/2026-04-19-enterprise-testing-dom/tasks.md
Normal file
@@ -0,0 +1,78 @@
|
||||
> Archived 2026-04-19: package shipped under universal/dev/testing-dom. Spec drift was rectified by `openspec/changes/testing-dom-spec-conformance/` (2026-04-26), which folded `**Verified by:**` lines into the canonical spec, pinned the dual-runner conditional-exports shape, and corrected the path reference in the coverage requirement.
|
||||
|
||||
## 1. Catalogs and package skeleton
|
||||
|
||||
- [x] 1.1 Extend root `package.json` `catalogs.testing` (or new `catalogs.dom-testing`) with `@testing-library/react`, `@testing-library/jest-dom`, `@testing-library/user-event`, `@testing-library/dom`, `@vitejs/plugin-react`. Pin versions via Bun overrides where stability matters.
|
||||
- [x] 1.2 Create `packages/universal/testing-dom/` skeleton: `package.json` (version `0.1.0`, dual-runner conditional exports, runtime deps = vitest+happy-dom+testing-library+@opentelemetry/api+zod+tslib, devDep on `@suaveplan/testing`), `tsconfig.lib.json`, `vite.config.ts`, `vitest.config.ts`, `README.md`.
|
||||
- [x] 1.3 Verify `bunx turbo ls --filter=@suaveplan/testing-dom...` (runtime closure) contains zero `@suaveplan/*` packages.
|
||||
|
||||
## 2. Migrate from staging
|
||||
|
||||
- [x] 2.1 Move `packages/universal/testing-dom-staging/runner/` into `packages/universal/testing-dom/src/runner/`. Update internal imports.
|
||||
- [x] 2.2 Move `packages/universal/testing-dom-staging/config/` into `packages/universal/testing-dom/src/config/`.
|
||||
- [x] 2.3 Move `packages/universal/testing-dom-staging/polyfills.ts` into `packages/universal/testing-dom/src/setup/polyfills.ts`.
|
||||
- [x] 2.4 Update `createTestConfig`'s polyfill setupFile path to `@suaveplan/testing-dom/polyfills`.
|
||||
- [x] 2.5 Delete `packages/universal/testing-dom-staging/` after migration.
|
||||
|
||||
## 3. Local error hierarchy
|
||||
|
||||
- [x] 3.1 Implement `src/errors/testing-dom-error.ts` with `TestingDomError` base + `RenderError`, `MatcherError`, `ConfigError`, `AbortedError` subclasses.
|
||||
- [x] 3.2 Implement `src/errors/codes.ts` enumerating `TESTING_DOM.<DOMAIN>.<REASON>` constants.
|
||||
- [x] 3.3 Update migrated `createTestConfig` to throw `ConfigError` instead of the old `ConfigError` from `@suaveplan/testing`.
|
||||
- [x] 3.4 Co-locate tests + `.md` for the error module.
|
||||
|
||||
## 4. Render helpers
|
||||
|
||||
- [x] 4.1 Implement `src/render/render-component.ts` with `renderComponent(ui, options?)` — wraps `@testing-library/react`'s `render`, returns `RenderResult` extended with `userEvent`.
|
||||
- [x] 4.2 Auto-register cleanup via vitest's `afterEach` (per-test-file, not global to avoid leak between unrelated suites).
|
||||
- [x] 4.3 Throws `RenderError("TESTING_DOM.RENDER.SETUP_FAILED")` on render-time exceptions.
|
||||
- [x] 4.4 Co-locate tests + `.md` + `.schemas.ts`.
|
||||
|
||||
## 5. Query aliases
|
||||
|
||||
- [x] 5.1 Implement `src/queries/queries.ts` re-exporting `screen`, `within`, and accessibility-first aliases (`byRole`, `byLabelText`, `byText`, `byPlaceholderText`, `findRole`, `queryRole`, etc.).
|
||||
- [x] 5.2 Document the testid-as-fallback policy in the module's `.md`.
|
||||
|
||||
## 6. User events
|
||||
|
||||
- [x] 6.1 Implement `src/user/setup-user.ts`: `setupUser(options?)` returning a `UserEvent` instance with happy-dom-friendly defaults (`pointerEventsCheck: 0`).
|
||||
- [x] 6.2 If `vi.isFakeTimers()` returns true at setup time, wire `advanceTimers: vi.advanceTimersByTime` automatically.
|
||||
- [x] 6.3 Co-locate tests + `.md`.
|
||||
|
||||
## 7. jest-dom matchers
|
||||
|
||||
- [x] 7.1 Implement `src/matchers/matchers.ts` — side-effecting import that calls `expect.extend(...)` with the jest-dom matcher set.
|
||||
- [x] 7.2 Re-export under subpath `@suaveplan/testing-dom/matchers`.
|
||||
- [x] 7.3 Smoke test: import the matchers module, render a node, assert `toBeInTheDocument`.
|
||||
|
||||
## 8. OTEL instrumentation (gated)
|
||||
|
||||
- [x] 8.1 Implement `src/observability/instrumented.ts` — counters/histograms gated behind `process.env.TESTING_DOM_OTEL_ENABLED === "1"`. No-op otherwise.
|
||||
- [x] 8.2 Wrap `renderComponent` and `setupUser` with the gated instrumentation.
|
||||
|
||||
## 9. Tests + cross-cutting guards
|
||||
|
||||
- [x] 9.1 Co-located unit tests for every public module in `src/`.
|
||||
- [x] 9.2 `tests/dep-graph.test.ts` — assert no `@suaveplan/*` runtime deps.
|
||||
- [x] 9.3 `tests/no-disallowed.test.ts` — `any` / `ts-ignore` / `TODO` etc. grep guard.
|
||||
- [x] 9.4 `tests/tree-shake.test.ts` — verify subpaths don't drag unrelated modules.
|
||||
- [x] 9.5 `tests/properties.test.ts` — fast-check property tests for any pure helpers (matcher predicates, query name normalization).
|
||||
- [x] 9.6 Coverage ≥ 95% on every dimension (statements, branches, functions, lines).
|
||||
|
||||
## 10. Docs and examples
|
||||
|
||||
- [x] 10.1 Every implementation file has a sibling `.md` with simple + advanced examples (≥ 200 words).
|
||||
- [x] 10.2 Root `README.md` covers install, quick usage (vitest config, basic component test), error code index, OTEL toggle.
|
||||
- [x] 10.3 `examples/basic-component.tsx` and `examples/with-providers.tsx` runnable scripts.
|
||||
|
||||
## 11. Pre-publish gate
|
||||
|
||||
- [x] 11.1 `bunx turbo lint typecheck test --filter=@suaveplan/testing-dom` green.
|
||||
- [x] 11.2 `bunx turbo test:coverage --filter=@suaveplan/testing-dom` ≥ 95% on every dimension.
|
||||
- [x] 11.3 `openspec validate enterprise-testing-dom --type change` passes.
|
||||
|
||||
## 12. Publish (USER APPROVAL REQUIRED)
|
||||
|
||||
- [x] 12.1 Surface the audit (test count, coverage, lint clean) and STOP.
|
||||
- [x] 12.2 On approval: `bun scripts/publish.ts --package @suaveplan/testing-dom`.
|
||||
- [x] 12.3 Archive change to `openspec/archive/<yyyy-mm-dd>-enterprise-testing-dom/`.
|
||||
Reference in New Issue
Block a user