3.8 KiB
Your sole purpose is to make the tests pass in packages/app. You fix one failing test per iteration, then report done when the entire suite is green.
Before you start
Read these docs — they are the law:
docs/CODING_STANDARDS.mddocs/TESTING.md
Your packages
packages/app— Expo mobile + web client
Test commands
Always use fail-fast. Do not run the whole suite. Find the first failure fast.
# Unit tests (vitest)
npm run test -w packages/app -- --bail 1
# E2E tests (Playwright)
npm run test:e2e -w packages/app -- --max-failures 1
Skip *.real.e2e.test.ts and *.local.e2e.test.ts — those are local-only manual tests.
What to do each iteration
- Run the unit tests with
--bail 1. If they pass, run the Playwright e2e tests with--max-failures 1. - Read the failure output carefully. Understand what the test is actually trying to verify.
- Fix it. See the rules below for how.
- Run typecheck:
npm run typecheck - Run the failing test again to confirm it passes.
- If all tests pass (both unit and e2e), report
done: true. Otherwise reportdone: falsewith what failed and what you did.
Rules — read every one
Fix strategy
When a test fails:
- Outdated (tests removed/renamed APIs, stale selectors) — update the test to match reality. If the test no longer tests anything meaningful, delete it.
- Flaky (races, timing, non-deterministic) — find the variance source and make it deterministic. Never add retries or
waitForTimeoutas a fix. - Too slow — make it fast or delete it.
- Tests unimplemented behavior — delete it. You are here to fix tests, not build features.
No shoehorning
Do not shoehorn tests into passing. If code isn't testable, refactor the code to be testable. Signs you're shoehorning:
- Adding
vi.mock()to stub out a dependency - Adding weird vitest config overrides
- Wrapping the test in try/catch to swallow errors
- Adding conditional assertions or
ifbranches in test bodies
Instead: make the dependency injectable, split the function, extract the pure logic.
No mocks
We use real dependencies on purpose. Do not introduce vi.mock(), jest.mock(), or any mocking library. If you need test isolation, use swappable adapters or in-memory implementations (see docs/TESTING.md).
Boy Scout Rule
Leave every file you touch cleaner than you found it:
- Extract duplicated setup into shared helpers
- Simplify complex assertions into readable helpers
- If you see three tests doing the same setup, extract it
- Build a vocabulary of test helpers so specs read like plain English
Playwright e2e specifics
The Playwright tests are outdated. When fixing them:
- Update selectors and test IDs to match current UI
- Build shared helpers in
e2e/helpers/— page objects, common flows, assertions - Specs should read like a DSL:
await createAgent(page, { provider: 'claude' })not 20 lines of clicks - Each spec file shares a single daemon via the fixture system. Do not spawn extra daemons per test.
- Clean up after yourself — if you start a process, kill it by PID when done
Resource hygiene
- NEVER kill the daemon running on port 6767 — that is the live development daemon. Killing it will break your own environment.
- When tests spawn ephemeral daemons, ensure cleanup runs even on test failure (use
afterAll/afterEachor Playwright fixtures with teardown). - Kill processes by PID, never by broad port or name patterns.
What NOT to do
- Do not add auth checks, environment variable gates, or conditional skips
- Do not introduce mocks
- Do not add new vitest plugins or config changes
- Do not implement new features to make a test pass
- Do not add
// @ts-ignoreor// @ts-expect-errorto silence type errors - Do not weaken assertions (e.g., changing
toEqualtotoBeTruthy)