| name | testing |
| description | Use whenever running tests in this project — verifying a fix, validating a PR, running the suite after changes, or checking a single file. Covers which command to use, how the `better-sqlite3` ABI rebuild fits in, and how to target individual files. |
Running tests
Always run tests via npm test. Don't invoke vitest directly with npx vitest run — that skips the pretest hook and the src/main/memory/memory-store-* tests will fail with a NODE_MODULE_VERSION mismatch when the native better-sqlite3 binary was last rebuilt for Electron.
Commands
| Goal | Command |
|---|
| Run the whole suite | npm test |
| Watch mode | npm run test:watch |
| One file | npm test -- path/to/file.test.ts |
| Pattern in test name | npm test -- -t "pattern" |
npm test -- forwards args to vitest. npm test -- path/to/file.test.ts works because the pretest hook still fires.
Why npm test and not npx vitest run
package.json wires up:
"pretest": "npm run rebuild:node",
"test": "vitest run",
"rebuild:node": "node scripts/rebuild-better-sqlite3-node.mjs"
better-sqlite3 is a native module. The app rebuilds it for Electron's Node ABI via npm run rebuild:electron (run before dev, start, dist). Tests run under the system Node, which has a different ABI, so the pretest hook rebuilds it for the test environment first. Skipping pretest leaves the binary in whichever state was last set up — often Electron's — and any test that touches src/main/memory/memory-store.ts:31 (new Database(...)) blows up before its first assertion.
CI (.github/workflows/release-dmg.yml) uses npm test for the same reason.
The Node ↔ Electron ABI flip
better-sqlite3's compiled binary is valid for exactly one runtime's ABI at a time, and the two entry points need opposite ABIs:
| You ran | better-sqlite3 gets rebuilt for | via |
|---|
npm test / test:watch | Node (vitest runs under system Node) | pretest → rebuild:node |
npm run dev / start / dist | Electron (the app loads SQLite in Electron) | predev/prestart/predist → rebuild:electron |
So running the tests leaves the binary built for Node, and the dev app won't load it until it's rebuilt for Electron — and vice-versa. This is expected: the pre* hooks flip it back automatically, so just run the command you want and the ABI re-fixes itself.
- Tests broke the app?
npm run dev (or start/dist) rebuilds for Electron first. To rebuild without launching, run npm run rebuild:electron.
- The app broke the tests?
npm test rebuilds for Node first (rebuild:node self-checks, so it's a near-no-op when already correct).
Exception — an already-running dev app. If npm run dev is already running (hot reload) and you run npm test in another terminal, the next main-process reload loads the now-Node-ABI binary and fails. The predev hook won't re-fire because dev never restarted. Fix: run npm run rebuild:electron, then restart npm run dev.
npm run doctor reports which ABI the binary is currently built for.
When tests fail with NODE_MODULE_VERSION
You ran npx vitest instead of npm test. Run npm test and the pretest hook fixes it. No need to manually npm rebuild better-sqlite3 — the script does the right thing.
The mirror image — the app failing to load better-sqlite3 with NODE_MODULE_VERSION right after a test run — is the same flip in reverse: run npm run rebuild:electron (or just npm run dev, whose predev hook does it).
Before claiming a change is done
npm test — full suite passes
npm run typecheck — no TypeScript errors
- For UI changes, see it — tests verify code correctness, not feature correctness. Don't ask the user for a screenshot to find bugs you can find yourself:
npm run screenshot:component <Component> --theme <id> renders one component under a real theme (no Electron) → a PNG under screenshots/. Add --emit-html to open it in any browser instead.
npm run drive:app launches the built app (npm run build first) under Playwright for flow-level checks. On headless Linux run it under xvfb-run.
- See renderer verification for the fixture convention and details.