Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,31 @@ Keep reviews convergent: consolidate actionable findings and clear exit criteria
Block on concrete correctness, security, or agreed-contract defects; unrelated
hardening is follow-up. Reopen scope only when new evidence warrants it.

## Choose tests by behavior

Default to colocated Vitest tests. Mount React with React Testing Library for
component behavior; do not mock React hooks or implement a substitute lifecycle.
Use Node for logic/services and opt into jsdom only when a DOM is needed.

Before adding a browser journey, name the browser behavior or integration boundary
it proves that lower-layer tests cannot. Keep scenario matrices in the lowest
layer that preserves that contract; retain representative app wiring coverage.
Do not infer layout, native editing or cross-window correctness from a DOM emulator.
When moving coverage, map removed assertions to replacements and demonstrate that
the replacement catches the regression before deleting the browser case.

Generate only the fixture data the test needs. Share immutable builds and stateless
servers, never mutable test state, identities or browser contexts. Preserve large
datasets and isolated runners when scale or performance is the behavior under test.
Record browser cases added/removed, their browser-only justification, replacement
coverage and fail-then-pass evidence in the PR description. For test infrastructure
changes, report before/after setup and execution timings with the command, engine,
environment and checked snapshots; distinguish local measurements from hosted CI.
List deferred checks. Do not meet time budgets by skipping engines, dropping
failure paths or weakening assertions. Enforce these rules during agent review;
do not rely on contributors filling in a PR template. Follow the
[test-layer review rules](docs/contributing.md#choosing-a-test-layer).

## Deterministic tests

Tests must control the ordering they assert, not depend on runner speed.
Expand Down
24 changes: 19 additions & 5 deletions docs/browser-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,11 +62,25 @@ isolation and one invocation to preserve both engines' evidence.

Compiled frontend assets are worker-scoped, split by `developmentReact` and
`pluginFixtures`, and removed when that worker ends. They are never reused across
invocations. Every test still gets a fresh preview server/port, ephemeral signing
keys, signed histories, relay state and browser context/storage. Evidence records
the worker and its build time; worker restarts rebuild rather than reuse stale assets.

Results go to ignored `test-results/browser/`: each test writes `evidence.json`
invocations. Every built-app test still gets a fresh preview server/port, ephemeral
signing keys, signed histories, relay state and browser context/storage. Evidence records
the worker and its build time, plus history counts and signing time; worker
restarts rebuild rather than reuse stale assets.

Declare `historyCounts` with `test.use` for built-app tests that do not need large
histories, for example `{ alpha: 1, beta: 0 }`. Counts apply per community. Keep
pagination, anchor and measurement datasets unchanged unless their behavior is
revalidated at the new size. The legacy large default remains for unaudited cases;
new tests should explicitly choose their data rather than inherit it accidentally.

Source-only diagnostic pages can import `test` and `expect` from
`source-fixture.mjs` and navigate to `/tests/fixtures/example.html`. That fixture
shares a stateless Vite server and its isolated optimizer cache per worker, with
fresh browser contexts/storage for every test. Do not use it for custom mutable
server middleware or a different Vite configuration. The existing `vite-server.mjs`
helper keeps independently configured servers' caches isolated.

Results go to ignored `test-results/browser/`: each built-app test writes `evidence.json`
with runtime versions, HEAD/dirty status, request ledger, runtime errors and
measurements. Failure screenshots and traces are retained too. The next invocation
replaces that output; copy artifacts before a rerun if you need to compare them.
Expand Down
52 changes: 52 additions & 0 deletions docs/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,58 @@ then Playwright. Updating a test's location must also update discovery, imports,
fixture URLs and root-path calculations; moving a file must not silently drop it
from the gate.

### Choosing a test layer

Choose the cheapest layer that can observe the failure, not the tool used by the
last test in the feature. Regression coverage is about behavior, not test counts
or a coverage percentage. These rules apply to human and AI contributions alike.

| Contract | Default layer |
| --- | --- |
| Parsing, policy, state machines, protocol handling, service coordination | Vitest in Node; use real collaborating services where the boundary matters |
| Component state, effects, subscriptions, forms, semantic DOM and stale async results | React Testing Library in Vitest with jsdom |
| Layout, virtualization, scrolling, native editing/focus interactions, real browser storage coordination | Playwright in both engines |
| App composition across routing, plugins, transport and persistence | Representative Playwright journeys, with permutations in lower layers |

Run JS tests with `bin/pnpm exec vitest run`, optionally followed by a test path.
For mounted component tests, add `// @vitest-environment jsdom` at the top of the
colocated test and import `@testing-library/jest-dom/vitest` for DOM assertions.
Use real React (including StrictMode), role/label queries and `userEvent` for
interactions. Use `fireEvent` for deliberately low-level events or bulk input
whose keystrokes are not the contract. Unmount with RTL `cleanup` in `afterEach`;
clear owned storage and restore spies. Fake external services, not React hooks.
Keep snapshots stable until a service actually changes, and assert cleanup and
late-result rejection through real mounting, rerendering and unmounting.
See the [composer tests](../src/features/messages/MessageComposer.test.tsx).

jsdom is the default DOM emulator, not a second browser gate. Its
[standards-oriented implementation](https://github.com/jsdom/jsdom#readme) and
compatibility with Testing Library favor behavioral fidelity over emulator-only
speed claims. [Vitest supports Happy DOM too](https://vitest.dev/guide/environment),
but introducing another emulator requires a demonstrated benefit on our actual
component tests without per-environment workarounds. Neither proves rendering,
native IME behavior or browser performance. Keep layout shims local and explicit;
do not treat synthetic dimensions as acceptance evidence.

Before accepting test changes, reviewers should verify:

- Each added browser case identifies a browser-specific behavior or integration
boundary that a lower layer cannot establish. Keep failure/recovery coverage,
but avoid repeating the same state matrix through full app startup.
- A moved assertion has a named replacement and evidence that a plausible defect
makes it fail. Similar test titles do not establish equivalent coverage.
- Fixture data matches the test's needs. Share stateless servers/compiled assets,
not browser contexts or mutable state; keep scale tests representative.
- Timing claims distinguish setup, execution, runner/engine and the checked
snapshot. Report added/removed cases and deferred checks. Do not impose a
flaky wall-clock threshold on ordinary correctness tests.

Follow `AGENTS.md` to record those decisions in the PR description and enforce
them during agent review. Request a lower-layer test when the browser justification
is missing, rather than accept unbounded journey growth. Existing broad fixtures and hook-mocked tests are
migration work, not patterns for new tests; convert them by owner without
bundling unrelated product changes.

### Manual browser fixtures

With `just web` running **without a `BUZZ_DEV_VIEWER` pin**, these separate diagnostic pages
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,15 @@
"@playwright/test": "1.60.0",
"@tailwindcss/postcss": "4.3.3",
"@tauri-apps/cli": "2.11.4",
"@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "7.0.1",
"@testing-library/react": "16.3.3",
"@testing-library/user-event": "14.6.7",
"@types/node": "24.13.3",
"@types/react": "19.2.18",
"@types/react-dom": "19.2.7",
"@vitejs/plugin-react": "6.1.1",
"jsdom": "30.0.1",
"postcss": "8.5.28",
"tailwindcss": "4.3.3",
"typescript": "7.0.2",
Expand Down
Loading
Loading