Test layer selection
Skill kamiazya/whiteboard/.claude/skills/test-layer-selection
How to pick the nearest test layer in the whiteboard repo (mcp-node / canvas-viewer-node / canvas-viewer-jsdom / canvas-viewer-browser / web-jsdom / web-browser / E2E), when to reach for a property or model-based test instead of an example, and the commands to run each. Use when writing the red test for a change, or deciding where a regression belongs.From its SKILL.md
npx -y skills add kamiazya/whiteboard --skill test-layer-selectionAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 5 stars5 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
4.9 KB, as published. Nobody here has run it
Test Layer Selection
Start with the smallest failing test at the nearest layer. Do not jump to broad E2E if a smaller failing test can isolate the bug.
| Layer | Use for | Command |
|---|---|---|
mcp-node | pure functions, stores, routes, server behavior, persistence logic in packages/mcp-server | pnpm test --project mcp-node |
canvas-viewer-node | pure parsing/serialization logic in packages/canvas-viewer with no DOM dependency | pnpm test --project canvas-viewer-node |
canvas-viewer-jsdom | packages/canvas-viewer components/hooks when browser layout & pointer behavior are NOT the core risk | pnpm test --project canvas-viewer-jsdom |
canvas-viewer-browser | packages/canvas-viewer popovers, dialogs, scroll, focus, keyboard, pointer, restore flows (real browser) | pnpm test --project canvas-viewer-browser |
web-jsdom | React components/hooks in apps/web when browser layout & pointer behavior are NOT the core risk | pnpm test:web-jsdom (jsdom project only) or pnpm --filter @kamiazya/whiteboard-web test (what CI runs: jsdom + web-node) |
web-browser | apps/web tests needing real browser APIs jsdom lacks: IndexedDB, OPFS, window.showOpenFilePicker. File suffix .browser.test.tsx | part of pnpm test:browser |
| E2E | real routes, server composition, websocket timing, persistence order, multi-step page flows | promote only when needed |
Notes:
- Browser suites together:
pnpm run test:browser(canvas-viewer-browser+web-browser);pnpm run test:browser:tracefor trace artifacts on failure (under<package>/tmp/vitest-traces). - After the targeted test passes, run the broader suite covering the touched area, then
pnpm test. - Runtime is the source of truth: if behavior disagrees with a test, fix the test or implementation to match real behavior.
- Passing tests alone are not sufficient — manually verify the real behavior (Playwright/Chrome MCP) before locking the scenario into regression coverage.
Property-Based / Model-Based Testing (PBT)
fast-check is already wired in via shared wrappers — packages/mcp-server/src/shared/test-utils/fast-check.ts,
apps/web/src/test-utils/fast-check.ts, and packages/canvas-viewer/src/test-utils/fast-check.ts. Reach for
a property (or model-based) test instead of,
or in addition to, an example test when the change is one of:
- Parser / serializer: assert a round-trip (
parse(serialize(x))equalsx, or an explicitly computed normalization ofxwhen the serializer is not injective) plus a totality property (parse either throws or returns a schema-valid value — no silent coercion). - State machine with time/TTL/revocation (auth/session/challenge stores, sliding-window caches):
model the entity as
fc.commands-style random sequences of operations (mint/enroll/verify/revoke/ advance-clock) driven by an injectednow(), and assert invariants hold after every step — this catches ordering bugs an example test's fixed script cannot. - Concurrent store: N concurrent writers converge to one agreed result (e.g. two tabs racing to create the same keypair land on the same key).
- CRDT / mergeable structure: idempotence (merge twice = merge once), commutativity/convergence (independent edits merged either order reach the same state).
- Rounding / normalization: the transform's algebraic invariant (e.g.
normalize(normalize(x)) === normalize(x)).
Prefer example/browser tests for UI wiring and one-off integrations with no clean invariant to state.
Runtime budget by layer: node-layer properties (mcp-node, canvas-viewer-node) can afford the
library's default numRuns; jsdom-layer properties should stay modest; web-browser/canvas-viewer-browser
properties are the most expensive per run (real browser + IndexedDB/etc.) — keep numRuns small
(single digits to ~20) and prefer the shared wrapper's withDefaults()-style override over inlining
{ numRuns } ad hoc.
Discipline (matches the fast-check wrappers already in the repo):
- Never pin a fast-check seed to force a flaky property green — a flake under load is a resource
signal (lower
numRuns), not an RNG problem to hide. - When a property finds a real bug: pin the shrunk counterexample as an example test before fixing production code, fix the implementation, then mutation-check (revert the fix, confirm the pinned example — and ideally the property — goes red, then restore).
- Shared arbitraries belong in the owning package's
test-utils, not copy-pasted per test file.