Strict One-Test TDD
Goal
Use a strict one-test TDD loop where the test names are the specification. For feature work, optionally write TODO test names first so the full behavior outline can be reviewed. After the user approves the TODO outline, convert one TODO into one executable test, prove it fails for the expected reason, wait for approval, then write the minimum implementation and verify it.
Target Module
Before writing or changing files, identify the target module from the user's request, active file, attached file, or nearby context. If the target module is unclear, ask exactly what module should be created or changed, for example Task3, Task3.ts, or packages/common/src/Task3.ts. If the target module is clear, confirm it briefly and continue.
Loop
Optional TODO Outline
Use this phase when developing a feature or when the user asks to see the whole behavior shape first.
- Propose concise TODO test names that describe the intended behavior.
- After the user approves the names, write only TODO tests, for example
test.todo("...")or the local framework equivalent. - Do not write executable test bodies or implementation code in this phase.
- Do not run tests just to prove TODO tests are pending unless the user asks.
- Stop and wait for the user's approval before converting the first TODO into an executable red test.
Red-Green Slice
- Ask the user for one test name and what that test should do.
- Write exactly one new executable test for that behavior, or convert exactly one approved TODO test into an executable test.
- Do not write implementation code yet, except for minimal exports or empty files required for the test to compile when the user explicitly wants a new module from scratch. Use the shared
todohelper from@evolu/commonfor placeholder implementations. - Run only the new or affected test with the repository's focused terminal command.
- Confirm the test fails for the expected reason. Do not complicate a red test only to avoid a one-time timeout; prefer a direct behavior-shaped test over diagnostic harness code when the timeout happens only during the failing demonstration.
- Stop and wait for the user's approval of the test.
- After approval, write the minimum implementation needed for that one test.
- Run the same focused test again with the same terminal command.
- Run
pnpm typecheckafter changing TypeScript source. - Stop and wait for the user's approval before asking for the next test.
Rules
- Never bundle multiple test cases into one loop. One executable test should describe one behavior or scenario; multiple assertions are fine when they prove that same scenario.
- Multiple TODO test names are allowed only in the TODO outline phase; executable tests must still be one at a time.
- Never add extra behavior beyond the approved test.
- Never refactor unrelated code during the red or green step.
- Use the user's exact test name unless it violates the local test style.
- Keep implementation minimal until the user approves broadening it.
- If the red test fails for the wrong reason, fix only the test setup and rerun before asking for approval.
- If the test unexpectedly passes before implementation, stop and explain why.
- Use
todo()for new placeholder implementations instead of throwing ad hoc errors. - Preserve existing repository conventions for file placement, imports, naming, and test style.
Evolu Defaults
For this repository, package unit tests are collocated with their source modules
under packages/*/src/ and use Node's node:test. Name them *.test.ts and run
selected files with a quoted path or glob:
pnpm test:node "<test-file-or-glob>"
Apps, scripts, tooling, and integration tests follow their local conventions.
For an existing Vitest integration suite, use
pnpm exec vitest run <test-file> --project=<project>, adding --mode=chromium
for a browser project. Use TypeScript only.