Standard: Testing
Applies to
Every code change that enters the repository. This covers the backend (Rust), the page (JS, CSS, HTML), scripts, and workflows.
Not covered: changes to docs only (docs/, README, CHANGELOG). Tests do check the section format of CHANGELOG, so changing that format is covered.
Rules
- When you change a behavior rule, first change the test that checks the rule, watch the changed test fail, and then change the code.
npm test(Rust unit tests and the page's Node tests) andnpm run lint(rustfmt, clippy including warnings, Biome) pass. CI runs the same commands.- When you change a flow people see (buttons, windows, saving, agent integration),
npm run test:e2epasses. When you add a new flow, add a check line to the scenario and the expected list intests/e2e.mjs. - Pure rules (calculation, conversion, parsing) are split into functions that run without the page or the app and checked with unit tests (
src/core/,src/yard/,scripts/*-lib.mjs,mod testsin each Rust module). In the yard these include the lake's shape (lakeShape,tests/lake.test.mjs), closeness (bondProgress,tests/bond.test.mjs), the hint line (hintKey,tests/hint.test.mjs), seasonal events (tests/events.test.mjs), the catalogue (tests/catalog.test.mjs), strolling (tests/wander.test.mjs) and the sound mix (tests/soundmix.test.mjs). - Rust test-only code goes only inside
mod testsat the end of the file. Watch tests scan only up to that point.
What goes where.
| What it checks | Where | How to run |
|---|---|---|
| Rust logic | mod tests at the end of each .rs file | npm test |
| Pure page logic, style contracts, module rules, strings tables | tests/*.test.mjs | npm test |
| The yard's content loaded the way the page loads it, for tests of rules that read it | tests/support/content.mjs (import it before the module under test) | npm test |
Seasonal event dates, shared with the backend's content.rs test so the page and the backend agree | tests/fixtures/event-dates.json | npm test |
| The page's limits and the backend's values kept equal (it reads the Rust source) | tests/limits.test.mjs | npm test |
| Rules for generating release and distribution files | tests/release.test.mjs, tests/distribute.test.mjs | npm test |
| The real app end to end | tests/e2e.mjs, test pet in tests/support/fixture-pet.mjs | npm run test:e2e |
| Real responses from community sites | Rust live_import (skipped by default) | npm run test:live-import |
E2E launches the release executable with an isolated data folder and runs the scenario script inside the app. Results come out as [e2e] name=value lines, and the harness compares them with the expected list and prints ✓/✖.
Recommendations
Follow these when you can. When you cannot, say why in the PR.
- When you change Windows-only code (
#[cfg(windows)]), confirm it compiles withnpm run dist:windows. Compiling is not running: if you cannot run the app on Windows yourself, list anything that needs a run check in the PR description. On a Mac, you can check the installers in a Windows virtual machine (for example UTM on Apple silicon, with Windows 11 on Arm). - Check changes that touch Linux (
cfg(unix), tray, overlay, file permissions) withnpm run dist:linux. It builds in Docker and then runs E2E on Xvfb, so you can see Linux runs from a macOS machine. - For changes that affect performance, attach the measurements from the performance standard.
Exceptions
| When | How |
|---|---|
| A flow that cannot have an E2E check line (OS dialogs, a real AI subscription, another OS) | Write how you checked it by hand and the result in the PR description |
| E2E while the window is covered and WebKit has paused animations | The harness skips checks that depend on walking and prints that it did. This is not a failure |
| An urgent fix with tests added later | A maintainer approves it, and an issue is left for adding the tests |
Rationale
The page has no build step, and the app runs on three operating systems. Unit tests check the rules, guard tests check the strings and module structure, and E2E inside the app checks the real windows and backend. macOS has no WebDriver, so E2E runs inside the app. Based on: decision “Real-app E2E runs a scenario script inside the app”
Automated checks
.github/workflows/build.yml runs lint, unit tests, build, and E2E on every push to main and every PR, on macOS, Windows x64 and arm64, and Linux x64 and arm64 runners. Linux E2E runs on Xvfb. For the site, its site job runs npm run site:build, which catches dead links, and tests/website.test.mjs checks that every version in the changelog has a release page and that the screenshots a page points to exist.
Change history
- 2026-10-01 First version — organized "The order for changing and checking" from CONTRIBUTING into rules
- 2026-10-03 Added the site checks (
tests/website.test.mjs, dead links) to the automated checks - 2026-10-10 Pure functions in
src/yard/are unit-tested too; the shared content loader, the event dates shared with Rust, and the limits test