--- name: testing description: | Testing workflow and quality standards for writing and running tests. Use when: (1) Writing new tests, (2) Adding a new feature that needs tests, (3) Modifying logic that has existing tests, (4) Before claiming a task is complete. --- # Testing Skill Standards and workflow for writing and running tests. Every feature must be tested. **Announce at start:** "I'm using testing skill to ensure proper test coverage." ## Trigger Conditions - Writing new tests for a feature or bug fix - Adding a new feature (must include tests) - Modifying logic that has existing tests (must update them) - Before claiming work is complete - Before committing code ## Framework **Vitest 4** — configured in `vitest.config.ts`. ## Test Structure ``` tests/ ├── unit/ # Individual functions, utilities, components ├── integration/ # IPC, database, service interactions ├── regression/ # Regression test cases └── e2e/ # End-to-end tests (Playwright, playwright.config.ts) ``` ## Two Test Environments | Environment | When | File naming | | ---------------- | --------------------------------- | --------------- | | `node` (default) | Main process, utilities, services | `*.test.ts` | | `jsdom` | DOM/browser-dependent code | `*.dom.test.ts` | ## Workflow ### Step 1: Identify What to Test Before writing tests, list the **riskiest scenarios** first: - What happens when the dependency returns `undefined` / throws? - What happens at boundaries (empty list, max retries, past timestamp)? - What is most likely to break in production? ### Step 2: Write Tests Follow these quality rules: **1. Describe behavior, not code structure** ```typescript // Wrong — describes implementation it('should call repo.getConversation', ...) // Correct — describes behavior it('should return cached task without hitting repo on second call', ...) it('should reject with error when conversation does not exist', ...) ``` **2. Every describe block must cover at least one failure path** Happy-path-only tests leave the most dangerous code untested. **3. One behavior per test** Keep each `it()` focused. More than 3 `expect()` calls in one test is a signal it is testing too much at once. **4. Self-check** After writing a test, mentally delete the core logic it targets. If the test would still pass, rewrite it — it is not guarding anything. **5. Start from risk, not from coverage gaps** List scenarios most likely to produce bugs. Write those first. Coverage is the outcome, not the starting point. ### Step 3: Run Tests ```bash bun run test # Run all tests (REQUIRED before every commit) bun run test:coverage # Check coverage (before opening a PR) ``` ### Step 4: Verify Coverage **Coverage target**: ≥ 80% for all source files matched by `vitest.config.ts` → `coverage.include` (currently `src/**/*.{ts,tsx}` plus a few scripts). New source files are automatically included in coverage — no manual config changes needed. If a new file is accidentally excluded by a rule in `coverage.exclude`, remove it from the exclude list. ### Step 5: Update Existing Tests When modifying logic, check if existing tests need updating: ```bash bun run test -- --reporter=verbose # See which tests pass/fail with names ``` If a test fails because the behavior changed intentionally, update the test. If it fails unexpectedly, investigate. ## Edge Case Checklist When testing a module, verify: - [ ] `null` / `undefined` inputs handled - [ ] Empty arrays/objects handled - [ ] Error thrown by dependencies handled - [ ] Boundary values (0, -1, max, empty string) - [ ] Async operations (timeout, rejection, cancellation) ## Quick Checklist Before submitting code: - [ ] New features have corresponding test cases - [ ] Modified logic has updated tests - [ ] `bun run test` passes - [ ] Tests describe **behavior**, not implementation - [ ] At least one failure path per describe block - [ ] New source files are not accidentally excluded by `coverage.exclude` - [ ] `bun run test:coverage` meets ≥ 80% target ## Common Mistakes | Mistake | Correct | | --------------------------------- | --------------------------------------------- | | Testing implementation details | Test observable behavior | | Only testing happy path | Must include at least one failure path | | 5+ expects in one `it()` | Split into separate tests | | Skipping tests for "simple" code | Simple code breaks too — test the risky parts | | Writing tests after saying "done" | Tests are part of "done", not an afterthought |