tribunal-kit 2.4.6 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (142) hide show
  1. package/.agent/agents/accessibility-reviewer.md +220 -134
  2. package/.agent/agents/ai-code-reviewer.md +233 -129
  3. package/.agent/agents/backend-specialist.md +238 -178
  4. package/.agent/agents/code-archaeologist.md +181 -119
  5. package/.agent/agents/database-architect.md +207 -164
  6. package/.agent/agents/debugger.md +218 -151
  7. package/.agent/agents/dependency-reviewer.md +136 -55
  8. package/.agent/agents/devops-engineer.md +238 -175
  9. package/.agent/agents/documentation-writer.md +221 -137
  10. package/.agent/agents/explorer-agent.md +180 -142
  11. package/.agent/agents/frontend-reviewer.md +194 -80
  12. package/.agent/agents/frontend-specialist.md +237 -188
  13. package/.agent/agents/game-developer.md +52 -184
  14. package/.agent/agents/logic-reviewer.md +149 -78
  15. package/.agent/agents/mobile-developer.md +223 -152
  16. package/.agent/agents/mobile-reviewer.md +195 -79
  17. package/.agent/agents/orchestrator.md +211 -170
  18. package/.agent/agents/penetration-tester.md +174 -131
  19. package/.agent/agents/performance-optimizer.md +203 -139
  20. package/.agent/agents/performance-reviewer.md +211 -108
  21. package/.agent/agents/product-manager.md +162 -108
  22. package/.agent/agents/project-planner.md +162 -142
  23. package/.agent/agents/qa-automation-engineer.md +242 -138
  24. package/.agent/agents/security-auditor.md +194 -170
  25. package/.agent/agents/seo-specialist.md +213 -132
  26. package/.agent/agents/sql-reviewer.md +194 -73
  27. package/.agent/agents/supervisor-agent.md +203 -156
  28. package/.agent/agents/test-coverage-reviewer.md +193 -81
  29. package/.agent/agents/type-safety-reviewer.md +208 -65
  30. package/.agent/scripts/__pycache__/auto_preview.cpython-311.pyc +0 -0
  31. package/.agent/scripts/__pycache__/bundle_analyzer.cpython-311.pyc +0 -0
  32. package/.agent/scripts/__pycache__/checklist.cpython-311.pyc +0 -0
  33. package/.agent/scripts/__pycache__/dependency_analyzer.cpython-311.pyc +0 -0
  34. package/.agent/scripts/__pycache__/security_scan.cpython-311.pyc +0 -0
  35. package/.agent/scripts/__pycache__/session_manager.cpython-311.pyc +0 -0
  36. package/.agent/scripts/__pycache__/skill_integrator.cpython-311.pyc +0 -0
  37. package/.agent/scripts/__pycache__/swarm_dispatcher.cpython-311.pyc +0 -0
  38. package/.agent/scripts/__pycache__/test_runner.cpython-311.pyc +0 -0
  39. package/.agent/scripts/__pycache__/verify_all.cpython-311.pyc +0 -0
  40. package/.agent/skills/agent-organizer/SKILL.md +126 -132
  41. package/.agent/skills/ai-prompt-injection-defense/SKILL.md +155 -66
  42. package/.agent/skills/api-patterns/SKILL.md +289 -257
  43. package/.agent/skills/api-security-auditor/SKILL.md +172 -70
  44. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +1 -1
  45. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +1 -1
  46. package/.agent/skills/appflow-wireframe/SKILL.md +107 -100
  47. package/.agent/skills/architecture/SKILL.md +331 -200
  48. package/.agent/skills/authentication-best-practices/SKILL.md +168 -67
  49. package/.agent/skills/bash-linux/SKILL.md +154 -215
  50. package/.agent/skills/brainstorming/SKILL.md +104 -210
  51. package/.agent/skills/building-native-ui/SKILL.md +169 -70
  52. package/.agent/skills/clean-code/SKILL.md +360 -206
  53. package/.agent/skills/config-validator/SKILL.md +141 -165
  54. package/.agent/skills/csharp-developer/SKILL.md +528 -107
  55. package/.agent/skills/database-design/SKILL.md +455 -275
  56. package/.agent/skills/deployment-procedures/SKILL.md +145 -188
  57. package/.agent/skills/devops-engineer/SKILL.md +332 -134
  58. package/.agent/skills/devops-incident-responder/SKILL.md +113 -98
  59. package/.agent/skills/edge-computing/SKILL.md +157 -213
  60. package/.agent/skills/extract-design-system/SKILL.md +129 -69
  61. package/.agent/skills/framer-motion-expert/SKILL.md +939 -0
  62. package/.agent/skills/game-design-expert/SKILL.md +105 -0
  63. package/.agent/skills/game-engineering-expert/SKILL.md +122 -0
  64. package/.agent/skills/geo-fundamentals/SKILL.md +124 -215
  65. package/.agent/skills/github-operations/SKILL.md +314 -354
  66. package/.agent/skills/gsap-expert/SKILL.md +901 -0
  67. package/.agent/skills/i18n-localization/SKILL.md +138 -216
  68. package/.agent/skills/intelligent-routing/SKILL.md +127 -139
  69. package/.agent/skills/llm-engineering/SKILL.md +357 -258
  70. package/.agent/skills/local-first/SKILL.md +154 -203
  71. package/.agent/skills/mcp-builder/SKILL.md +118 -224
  72. package/.agent/skills/nextjs-react-expert/SKILL.md +783 -203
  73. package/.agent/skills/nodejs-best-practices/SKILL.md +559 -280
  74. package/.agent/skills/observability/SKILL.md +330 -285
  75. package/.agent/skills/parallel-agents/SKILL.md +122 -181
  76. package/.agent/skills/performance-profiling/SKILL.md +254 -197
  77. package/.agent/skills/plan-writing/SKILL.md +118 -188
  78. package/.agent/skills/platform-engineer/SKILL.md +123 -135
  79. package/.agent/skills/playwright-best-practices/SKILL.md +157 -76
  80. package/.agent/skills/powershell-windows/SKILL.md +146 -230
  81. package/.agent/skills/python-pro/SKILL.md +879 -114
  82. package/.agent/skills/react-specialist/SKILL.md +931 -108
  83. package/.agent/skills/realtime-patterns/SKILL.md +304 -296
  84. package/.agent/skills/rust-pro/SKILL.md +701 -240
  85. package/.agent/skills/seo-fundamentals/SKILL.md +154 -181
  86. package/.agent/skills/server-management/SKILL.md +190 -212
  87. package/.agent/skills/shadcn-ui-expert/SKILL.md +201 -68
  88. package/.agent/skills/sql-pro/SKILL.md +633 -104
  89. package/.agent/skills/swiftui-expert/SKILL.md +171 -70
  90. package/.agent/skills/systematic-debugging/SKILL.md +118 -186
  91. package/.agent/skills/tailwind-patterns/SKILL.md +576 -232
  92. package/.agent/skills/tdd-workflow/SKILL.md +137 -209
  93. package/.agent/skills/testing-patterns/SKILL.md +573 -205
  94. package/.agent/skills/vue-expert/SKILL.md +964 -119
  95. package/.agent/skills/vulnerability-scanner/SKILL.md +269 -316
  96. package/.agent/skills/web-accessibility-auditor/SKILL.md +188 -71
  97. package/.agent/skills/webapp-testing/SKILL.md +145 -236
  98. package/.agent/workflows/api-tester.md +151 -279
  99. package/.agent/workflows/audit.md +138 -168
  100. package/.agent/workflows/brainstorm.md +110 -146
  101. package/.agent/workflows/changelog.md +112 -144
  102. package/.agent/workflows/create.md +124 -139
  103. package/.agent/workflows/debug.md +189 -196
  104. package/.agent/workflows/deploy.md +189 -153
  105. package/.agent/workflows/enhance.md +151 -139
  106. package/.agent/workflows/fix.md +135 -143
  107. package/.agent/workflows/generate.md +157 -164
  108. package/.agent/workflows/migrate.md +160 -163
  109. package/.agent/workflows/orchestrate.md +168 -151
  110. package/.agent/workflows/performance-benchmarker.md +123 -305
  111. package/.agent/workflows/plan.md +173 -151
  112. package/.agent/workflows/preview.md +80 -137
  113. package/.agent/workflows/refactor.md +183 -153
  114. package/.agent/workflows/review-ai.md +129 -140
  115. package/.agent/workflows/review.md +116 -155
  116. package/.agent/workflows/session.md +94 -154
  117. package/.agent/workflows/status.md +79 -125
  118. package/.agent/workflows/strengthen-skills.md +139 -99
  119. package/.agent/workflows/swarm.md +179 -194
  120. package/.agent/workflows/test.md +211 -166
  121. package/.agent/workflows/tribunal-backend.md +113 -111
  122. package/.agent/workflows/tribunal-database.md +115 -132
  123. package/.agent/workflows/tribunal-frontend.md +118 -115
  124. package/.agent/workflows/tribunal-full.md +133 -136
  125. package/.agent/workflows/tribunal-mobile.md +119 -123
  126. package/.agent/workflows/tribunal-performance.md +133 -152
  127. package/.agent/workflows/ui-ux-pro-max.md +143 -171
  128. package/README.md +11 -15
  129. package/package.json +1 -1
  130. package/.agent/skills/dotnet-core-expert/SKILL.md +0 -103
  131. package/.agent/skills/framer-motion-animations/SKILL.md +0 -74
  132. package/.agent/skills/game-development/2d-games/SKILL.md +0 -119
  133. package/.agent/skills/game-development/3d-games/SKILL.md +0 -135
  134. package/.agent/skills/game-development/SKILL.md +0 -236
  135. package/.agent/skills/game-development/game-art/SKILL.md +0 -185
  136. package/.agent/skills/game-development/game-audio/SKILL.md +0 -190
  137. package/.agent/skills/game-development/game-design/SKILL.md +0 -129
  138. package/.agent/skills/game-development/mobile-games/SKILL.md +0 -108
  139. package/.agent/skills/game-development/multiplayer/SKILL.md +0 -132
  140. package/.agent/skills/game-development/pc-games/SKILL.md +0 -144
  141. package/.agent/skills/game-development/vr-ar/SKILL.md +0 -123
  142. package/.agent/skills/game-development/web-games/SKILL.md +0 -150
@@ -1,205 +1,573 @@
1
- ---
2
- name: testing-patterns
3
- description: Testing patterns and principles. Unit, integration, mocking strategies.
4
- allowed-tools: Read, Write, Edit, Glob, Grep
5
- version: 1.0.0
6
- last-updated: 2026-03-12
7
- applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
8
- ---
9
-
10
- # Testing Patterns
11
-
12
- > Tests don't prove code works. They make it safe to change.
13
- > A codebase without tests is a codebase you're afraid to touch.
14
-
15
- ---
16
-
17
- ## Test Pyramid
18
-
19
- Write tests at the right level. Most tests should be unit tests. Fewer integration tests. Fewest E2E tests.
20
-
21
- ```
22
- /\
23
- /E2E\ Fewest — expensive to write and run
24
- /------\
25
- /Integr. \ Some — verify component interactions
26
- /----------\
27
- / Unit Tests \ Most — fast, isolated, focused
28
- /--------------\
29
- ```
30
-
31
- **Why this shape:**
32
- - Unit tests run in milliseconds — you can have thousands
33
- - E2E tests take seconds — you want dozens, not hundreds
34
- - Inverting the pyramid = slow CI, fragile test suite, low confidence
35
-
36
- ---
37
-
38
- ## AAA Pattern
39
-
40
- Every test follows this structure:
41
-
42
- ```ts
43
- it('should return 401 when token is expired', async () => {
44
- // Arrange set up the scenario
45
- const expiredToken = generateToken({ expiresIn: '-1s' });
46
- const request = buildRequest({ authorization: `Bearer ${expiredToken}` });
47
-
48
- // Act do the thing being tested
49
- const response = await authMiddleware(request);
50
-
51
- // Assert — verify the outcome
52
- expect(response.status).toBe(401);
53
- expect(response.body.error).toBe('Token expired');
54
- });
55
- ```
56
-
57
- Never combine Arrange and Assert. Never skip Arrange by relying on test state from a previous test.
58
-
59
- ---
60
-
61
- ## Unit Tests
62
-
63
- Unit tests test one unit of logic in isolation. Everything external is replaced with a controlled substitute.
64
-
65
- ```ts
66
- // Test the function's logic — not the database, not the network
67
- it('should hash the password before saving', async () => {
68
- const mockSave = vi.fn().mockResolvedValue({ id: '1' });
69
- const userRepo = { save: mockSave };
70
-
71
- await createUser({ email: 'a@b.com', password: 'secret' }, userRepo);
72
-
73
- const savedUser = mockSave.mock.calls[0][0];
74
- expect(savedUser.password).not.toBe('secret'); // was hashed
75
- expect(savedUser.password).toMatch(/^\$2b\$/); // bcrypt format
76
- });
77
- ```
78
-
79
- **Rules for unit tests:**
80
- - One assertion per concept (multiple `expect` calls are fine if they verify the same behavior)
81
- - No network calls, no file system, no real database
82
- - Tests are order-independent they don't rely on state from other tests
83
- - Test names describe behavior: `should X when Y` or `returns X given Y`
84
-
85
- ---
86
-
87
- ## Integration Tests
88
-
89
- Integration tests verify that two or more components work together correctly.
90
-
91
- ```ts
92
- // Tests the real database interaction
93
- it('should save and retrieve a user', async () => {
94
- const user = await UserService.create({ email: 'test@test.com', name: 'Test' });
95
- const found = await UserService.findById(user.id);
96
-
97
- expect(found.email).toBe('test@test.com');
98
- });
99
- ```
100
-
101
- **Integration test rules:**
102
- - Use a real test database — not a mock
103
- - Clean up data before or after each test (use transactions that rollback, or seed scripts)
104
- - Slower than unit tests — run on CI but not on every file save
105
-
106
- ---
107
-
108
- ## Mocking Principles
109
-
110
- Mocks replace external dependencies. Use them accurately.
111
-
112
- ```ts
113
- // ❌ Mock that returns nothing useful
114
- vi.mock('./mailer', () => ({ send: vi.fn() }));
115
-
116
- // Mock that reflects real behavior
117
- vi.mock('./mailer', () => ({
118
- send: vi.fn().mockResolvedValue({ messageId: 'mock-id-123' }),
119
- }));
120
- ```
121
-
122
- **What to mock:**
123
- - External HTTP calls (payment gateways, third-party APIs)
124
- - Time (`Date.now()`, `new Date()`) when time-dependent
125
- - File system in unit tests
126
- - Email/SMS sending
127
-
128
- **What not to mock:**
129
- - Your own business logic
130
- - The function being tested
131
- - Simple pure utility functions
132
-
133
- ---
134
-
135
- ## Test Coverage
136
-
137
- Coverage measures which lines are executed during tests. 100% coverage does not mean the code is correct.
138
-
139
- **Useful coverage:**
140
- - `> 80%` for business logic modules
141
- - Focus on statement + branch coverage (not just line coverage)
142
- - Low coverage on infrastructure/config files is acceptable
143
-
144
- **Coverage anti-patterns:**
145
- - Tests written solely to increase coverage numbers
146
- - Asserting that mocks were called instead of asserting real outcomes
147
- - Tautology tests: `expect(result).toBe(result)`
148
-
149
- ---
150
-
151
- ## Scripts
152
-
153
- | Script | Purpose | Run With |
154
- |---|---|---|
155
- | `scripts/test_runner.py` | Runs test suite and reports results | `python scripts/test_runner.py <project_path>` |
156
-
157
- ---
158
-
159
- ## Output Format
160
-
161
- When this skill produces or reviews code, structure your output as follows:
162
-
163
- ```
164
- ━━━ Testing Patterns Report ━━━━━━━━━━━━━━━━━━━━━━━━
165
- Skill: Testing Patterns
166
- Language: [detected language / framework]
167
- Scope: [N files · N functions]
168
- ─────────────────────────────────────────────────
169
- ✅ Passed: [checks that passed, or "All clean"]
170
- ⚠️ Warnings: [non-blocking issues, or "None"]
171
- Blocked: [blocking issues requiring fix, or "None"]
172
- ─────────────────────────────────────────────────
173
- VBC status: PENDING → VERIFIED
174
- Evidence: [test output / lint pass / compile success]
175
- ```
176
-
177
- **VBC (Verification-Before-Completion) is mandatory.**
178
- Do not mark status as VERIFIED until concrete terminal evidence is provided.
179
-
180
-
181
- ---
182
-
183
- ## 🏛️ Tribunal Integration (Anti-Hallucination)
184
-
185
- **Slash command: `/test`**
186
- **Active reviewers: `logic` · `test-coverage-reviewer`**
187
-
188
- ### ❌ Forbidden AI Tropes in Testing
189
-
190
- 1. **Tautological Tests** — writing `expect(x).toBe(x)` or testing that a mock returns what it was mocked to return, ignoring actual behavior.
191
- 2. **Missing the "Arrange" Step** — relying on test state leftover from a previous `it()` block.
192
- 3. **Over-mocking** mocking the system under test or mocking out so much that the test is completely useless.
193
- 4. **Assertions in loops** — writing loops with dynamic assertions instead of explicit, readable test cases.
194
- 5. **Ignoring Async Failures** — forgetting to `await` the assertions or the function under test, leading to false positives.
195
-
196
- ### ✅ Pre-Flight Self-Audit
197
-
198
- Review these questions before generating test code:
199
- ```
200
- Does each test explicitly follow the Arrange-Act-Assert (AAA) pattern?
201
- Are external dependencies (DBs, Networks) isolated efficiently using proper mocks?
202
- Is the test asserting verifying behavioral OUTCOMES rather than internal implementation details?
203
- ✅ Did I properly `await` asynchronous operations to prevent false positives?
204
- ✅ Are test names readable and descriptive of the tested behavior?
205
- ```
1
+ ---
2
+ name: testing-patterns
3
+ description: Testing mastery across stacks. Unit testing with Jest/Vitest/pytest, integration testing, E2E with Playwright, mocking strategies, test architecture (AAA, Given-When-Then), code coverage, snapshot testing, API testing, component testing with Testing Library, and TDD workflow. Use when writing tests, designing test architecture, or improving test coverage.
4
+ allowed-tools: Read, Write, Edit, Glob, Grep
5
+ version: 2.0.0
6
+ last-updated: 2026-04-01
7
+ applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
8
+ ---
9
+
10
+ # Testing Patterns — Cross-Stack Testing Mastery
11
+
12
+ > A test that doesn't fail when the code is wrong is worse than no test at all.
13
+ > Every test must answer: "What behavior am I proving? What would break if I deleted this test?"
14
+
15
+ ---
16
+
17
+ ## Test Architecture
18
+
19
+ ### The Testing Pyramid
20
+
21
+ ```
22
+ / E2E \ ← Few: critical user flows (Playwright/Cypress)
23
+ /──────────\
24
+ / Integration \ ← Moderate: API routes, DB queries, component integration
25
+ /──────────────\
26
+ / Unit Tests \ ← Many: pure functions, hooks, utilities, business logic
27
+ /──────────────────\
28
+
29
+ Rules:
30
+ - 70% unit, 20% integration, 10% E2E
31
+ - Unit tests: < 50ms each
32
+ - Integration tests: < 2s each
33
+ - E2E tests: < 30s each
34
+ - If a test takes > 5s, it's a design problem
35
+ ```
36
+
37
+ ### AAA Pattern (Arrange-Act-Assert)
38
+
39
+ ```typescript
40
+ // Every test follows the same structure
41
+ it("calculates total with tax", () => {
42
+ // Arrange — set up the scenario
43
+ const cart = new Cart();
44
+ cart.addItem({ name: "Widget", price: 100 });
45
+ cart.setTaxRate(0.08);
46
+
47
+ // Act — perform the action being tested
48
+ const total = cart.calculateTotal();
49
+
50
+ // Assert — verify the result
51
+ expect(total).toBe(108);
52
+ });
53
+
54
+ // ❌ BAD: Multiple acts in one test
55
+ it("does too many things", () => {
56
+ cart.addItem({ name: "A", price: 10 });
57
+ expect(cart.total).toBe(10); // assert
58
+ cart.addItem({ name: "B", price: 20 });
59
+ expect(cart.total).toBe(30); // another assert after another act
60
+ cart.removeItem("A");
61
+ expect(cart.total).toBe(20); // yet another — split into 3 tests
62
+ });
63
+ ```
64
+
65
+ ### Test Naming Convention
66
+
67
+ ```typescript
68
+ // Format: [unit] + [scenario] + [expected result]
69
+
70
+ // ✅ GOOD: Descriptive, reads like a specification
71
+ describe("calculateDiscount", () => {
72
+ it("returns 0% when cart total is under $50", () => {});
73
+ it("returns 10% when cart total is $50-$99", () => {});
74
+ it("returns 20% when cart total is $100+", () => {});
75
+ it("throws when cart is empty", () => {});
76
+ });
77
+
78
+ // ❌ BAD: Vague, implementation-focused
79
+ describe("calculateDiscount", () => {
80
+ it("works", () => {});
81
+ it("test1", () => {});
82
+ it("should return correct value", () => {});
83
+ });
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Unit Testing (Vitest / Jest)
89
+
90
+ ### Pure Function Testing
91
+
92
+ ```typescript
93
+ // utils/math.ts
94
+ export function clamp(value: number, min: number, max: number): number {
95
+ return Math.min(Math.max(value, min), max);
96
+ }
97
+
98
+ // utils/math.test.ts
99
+ import { describe, it, expect } from "vitest";
100
+ import { clamp } from "./math";
101
+
102
+ describe("clamp", () => {
103
+ it("returns the value when within range", () => {
104
+ expect(clamp(5, 0, 10)).toBe(5);
105
+ });
106
+
107
+ it("clamps to min when value is below range", () => {
108
+ expect(clamp(-5, 0, 10)).toBe(0);
109
+ });
110
+
111
+ it("clamps to max when value is above range", () => {
112
+ expect(clamp(15, 0, 10)).toBe(10);
113
+ });
114
+
115
+ it("handles equal min and max", () => {
116
+ expect(clamp(5, 3, 3)).toBe(3);
117
+ });
118
+
119
+ it("handles floating point values", () => {
120
+ expect(clamp(0.5, 0, 1)).toBeCloseTo(0.5);
121
+ });
122
+ });
123
+ ```
124
+
125
+ ### Async Testing
126
+
127
+ ```typescript
128
+ import { describe, it, expect, vi } from "vitest";
129
+
130
+ // Async function under test
131
+ async function fetchUser(id: string): Promise<User> {
132
+ const response = await fetch(`/api/users/${id}`);
133
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
134
+ return response.json();
135
+ }
136
+
137
+ describe("fetchUser", () => {
138
+ it("returns user data on success", async () => {
139
+ const mockUser = { id: "1", name: "Alice" };
140
+ global.fetch = vi.fn().mockResolvedValue({
141
+ ok: true,
142
+ json: () => Promise.resolve(mockUser),
143
+ });
144
+
145
+ const user = await fetchUser("1");
146
+ expect(user).toEqual(mockUser);
147
+ expect(fetch).toHaveBeenCalledWith("/api/users/1");
148
+ });
149
+
150
+ it("throws on HTTP error", async () => {
151
+ global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 404 });
152
+
153
+ await expect(fetchUser("999")).rejects.toThrow("HTTP 404");
154
+ });
155
+ });
156
+ ```
157
+
158
+ ### Timer & Date Mocking
159
+
160
+ ```typescript
161
+ describe("debounce", () => {
162
+ beforeEach(() => {
163
+ vi.useFakeTimers();
164
+ });
165
+
166
+ afterEach(() => {
167
+ vi.useRealTimers();
168
+ });
169
+
170
+ it("delays execution by specified ms", () => {
171
+ const fn = vi.fn();
172
+ const debounced = debounce(fn, 300);
173
+
174
+ debounced();
175
+ expect(fn).not.toHaveBeenCalled(); // not yet
176
+
177
+ vi.advanceTimersByTime(200);
178
+ expect(fn).not.toHaveBeenCalled(); // still not
179
+
180
+ vi.advanceTimersByTime(100);
181
+ expect(fn).toHaveBeenCalledOnce(); // now
182
+ });
183
+
184
+ it("resets timer on subsequent calls", () => {
185
+ const fn = vi.fn();
186
+ const debounced = debounce(fn, 300);
187
+
188
+ debounced();
189
+ vi.advanceTimersByTime(200);
190
+ debounced(); // reset timer
191
+ vi.advanceTimersByTime(200);
192
+ expect(fn).not.toHaveBeenCalled(); // timer was reset
193
+
194
+ vi.advanceTimersByTime(100);
195
+ expect(fn).toHaveBeenCalledOnce();
196
+ });
197
+ });
198
+
199
+ // Date mocking
200
+ it("formats today's date", () => {
201
+ vi.setSystemTime(new Date("2024-06-15T12:00:00Z"));
202
+ expect(getFormattedDate()).toBe("June 15, 2024");
203
+ vi.useRealTimers();
204
+ });
205
+ ```
206
+
207
+ ---
208
+
209
+ ## Mocking Strategies
210
+
211
+ ### Module Mocks
212
+
213
+ ```typescript
214
+ import { vi, describe, it, expect, beforeEach } from "vitest";
215
+ import { sendEmail } from "./email-service";
216
+ import { createUser } from "./user-service";
217
+
218
+ // Mock an entire module
219
+ vi.mock("./email-service", () => ({
220
+ sendEmail: vi.fn().mockResolvedValue({ sent: true }),
221
+ }));
222
+
223
+ describe("createUser", () => {
224
+ beforeEach(() => {
225
+ vi.clearAllMocks(); // reset call counts between tests
226
+ });
227
+
228
+ it("sends welcome email after creating user", async () => {
229
+ await createUser({ name: "Alice", email: "alice@test.com" });
230
+
231
+ expect(sendEmail).toHaveBeenCalledWith({
232
+ to: "alice@test.com",
233
+ subject: "Welcome!",
234
+ body: expect.stringContaining("Alice"),
235
+ });
236
+ });
237
+
238
+ it("does not send email on validation failure", async () => {
239
+ await expect(createUser({ name: "", email: "" })).rejects.toThrow();
240
+ expect(sendEmail).not.toHaveBeenCalled();
241
+ });
242
+ });
243
+ ```
244
+
245
+ ### Spy Pattern
246
+
247
+ ```typescript
248
+ // Spy on an existing method (don't replace it — observe it)
249
+ const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
250
+
251
+ await riskyOperation();
252
+
253
+ expect(consoleSpy).toHaveBeenCalledWith(
254
+ expect.stringContaining("failed"),
255
+ expect.any(Error)
256
+ );
257
+
258
+ consoleSpy.mockRestore(); // restore original
259
+ ```
260
+
261
+ ### Dependency Injection Pattern (Testable by Design)
262
+
263
+ ```typescript
264
+ // ❌ BAD: Hard-coded dependency — untestable without module mocking
265
+ class UserService {
266
+ async getUser(id: string) {
267
+ return await fetch(`/api/users/${id}`).then((r) => r.json());
268
+ }
269
+ }
270
+
271
+ // ✅ GOOD: Injected dependency — naturally testable
272
+ interface HttpClient {
273
+ get<T>(url: string): Promise<T>;
274
+ }
275
+
276
+ class UserService {
277
+ constructor(private http: HttpClient) {}
278
+
279
+ async getUser(id: string): Promise<User> {
280
+ return this.http.get<User>(`/api/users/${id}`);
281
+ }
282
+ }
283
+
284
+ // In test:
285
+ const mockHttp: HttpClient = {
286
+ get: vi.fn().mockResolvedValue({ id: "1", name: "Alice" }),
287
+ };
288
+ const service = new UserService(mockHttp);
289
+
290
+ // ❌ HALLUCINATION TRAP: Prefer dependency injection over vi.mock()
291
+ // vi.mock() is global and can leak between tests
292
+ // DI makes tests isolated and explicit
293
+ ```
294
+
295
+ ---
296
+
297
+ ## React Component Testing (Testing Library)
298
+
299
+ ```tsx
300
+ import { render, screen, waitFor } from "@testing-library/react";
301
+ import userEvent from "@testing-library/user-event";
302
+ import { describe, it, expect, vi } from "vitest";
303
+ import { LoginForm } from "./LoginForm";
304
+
305
+ describe("LoginForm", () => {
306
+ it("renders email and password fields", () => {
307
+ render(<LoginForm onSubmit={vi.fn()} />);
308
+
309
+ expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
310
+ expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
311
+ expect(screen.getByRole("button", { name: /sign in/i })).toBeInTheDocument();
312
+ });
313
+
314
+ it("calls onSubmit with credentials", async () => {
315
+ const user = userEvent.setup();
316
+ const onSubmit = vi.fn();
317
+ render(<LoginForm onSubmit={onSubmit} />);
318
+
319
+ await user.type(screen.getByLabelText(/email/i), "alice@test.com");
320
+ await user.type(screen.getByLabelText(/password/i), "secret123");
321
+ await user.click(screen.getByRole("button", { name: /sign in/i }));
322
+
323
+ expect(onSubmit).toHaveBeenCalledWith({
324
+ email: "alice@test.com",
325
+ password: "secret123",
326
+ });
327
+ });
328
+
329
+ it("shows validation error for invalid email", async () => {
330
+ const user = userEvent.setup();
331
+ render(<LoginForm onSubmit={vi.fn()} />);
332
+
333
+ await user.type(screen.getByLabelText(/email/i), "not-an-email");
334
+ await user.click(screen.getByRole("button", { name: /sign in/i }));
335
+
336
+ expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
337
+ });
338
+
339
+ it("disables submit button while loading", async () => {
340
+ render(<LoginForm onSubmit={vi.fn()} isLoading={true} />);
341
+
342
+ expect(screen.getByRole("button", { name: /sign in/i })).toBeDisabled();
343
+ });
344
+ });
345
+
346
+ // ❌ HALLUCINATION TRAP: Query priorities (use in this order):
347
+ // 1. getByRole — accessible role ("button", "textbox", etc.)
348
+ // 2. getByLabelText — form inputs with labels
349
+ // 3. getByPlaceholderText — when no label exists
350
+ // 4. getByText — non-interactive elements
351
+ // 5. getByTestId — LAST RESORT only
352
+ // ❌ Never default to getByTestId — it tests implementation, not behavior
353
+ ```
354
+
355
+ ---
356
+
357
+ ## E2E Testing (Playwright)
358
+
359
+ ```typescript
360
+ import { test, expect } from "@playwright/test";
361
+
362
+ test.describe("Login Flow", () => {
363
+ test("successful login redirects to dashboard", async ({ page }) => {
364
+ await page.goto("/login");
365
+
366
+ await page.getByLabel("Email").fill("admin@test.com");
367
+ await page.getByLabel("Password").fill("password123");
368
+ await page.getByRole("button", { name: "Sign In" }).click();
369
+
370
+ // Wait for navigation
371
+ await expect(page).toHaveURL("/dashboard");
372
+ await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
373
+ });
374
+
375
+ test("shows error for invalid credentials", async ({ page }) => {
376
+ await page.goto("/login");
377
+
378
+ await page.getByLabel("Email").fill("wrong@test.com");
379
+ await page.getByLabel("Password").fill("wrongpassword");
380
+ await page.getByRole("button", { name: "Sign In" }).click();
381
+
382
+ await expect(page.getByText("Invalid credentials")).toBeVisible();
383
+ await expect(page).toHaveURL("/login"); // no redirect
384
+ });
385
+
386
+ test("responsive: mobile menu toggles", async ({ page, isMobile }) => {
387
+ test.skip(!isMobile, "Mobile only");
388
+
389
+ await page.goto("/");
390
+ await page.getByRole("button", { name: "Menu" }).click();
391
+ await expect(page.getByRole("navigation")).toBeVisible();
392
+ });
393
+ });
394
+
395
+ // API testing with Playwright
396
+ test("API: create user returns 201", async ({ request }) => {
397
+ const response = await request.post("/api/users", {
398
+ data: { name: "Alice", email: "alice@test.com" },
399
+ });
400
+
401
+ expect(response.status()).toBe(201);
402
+ const body = await response.json();
403
+ expect(body).toMatchObject({ name: "Alice", email: "alice@test.com" });
404
+ });
405
+ ```
406
+
407
+ ### Playwright Config
408
+
409
+ ```typescript
410
+ // playwright.config.ts
411
+ import { defineConfig } from "@playwright/test";
412
+
413
+ export default defineConfig({
414
+ testDir: "./e2e",
415
+ timeout: 30000,
416
+ retries: process.env.CI ? 2 : 0, // retry in CI only
417
+ use: {
418
+ baseURL: "http://localhost:3000",
419
+ trace: "on-first-retry", // save trace on failures
420
+ screenshot: "only-on-failure",
421
+ },
422
+ webServer: {
423
+ command: "npm run dev",
424
+ port: 3000,
425
+ reuseExistingServer: !process.env.CI,
426
+ },
427
+ projects: [
428
+ { name: "chrome", use: { browserName: "chromium" } },
429
+ { name: "firefox", use: { browserName: "firefox" } },
430
+ { name: "mobile", use: { ...devices["iPhone 14"] } },
431
+ ],
432
+ });
433
+ ```
434
+
435
+ ---
436
+
437
+ ## API Testing
438
+
439
+ ```typescript
440
+ // Testing REST APIs with supertest (Express/Fastify)
441
+ import request from "supertest";
442
+ import { app } from "./app";
443
+
444
+ describe("POST /api/users", () => {
445
+ it("creates a user and returns 201", async () => {
446
+ const response = await request(app)
447
+ .post("/api/users")
448
+ .send({ name: "Alice", email: "alice@test.com" })
449
+ .expect(201)
450
+ .expect("Content-Type", /json/);
451
+
452
+ expect(response.body).toMatchObject({
453
+ id: expect.any(Number),
454
+ name: "Alice",
455
+ email: "alice@test.com",
456
+ });
457
+ });
458
+
459
+ it("returns 400 for missing required fields", async () => {
460
+ await request(app)
461
+ .post("/api/users")
462
+ .send({ name: "" })
463
+ .expect(400);
464
+ });
465
+
466
+ it("returns 409 for duplicate email", async () => {
467
+ await request(app)
468
+ .post("/api/users")
469
+ .send({ name: "Alice", email: "existing@test.com" })
470
+ .expect(409);
471
+ });
472
+ });
473
+ ```
474
+
475
+ ---
476
+
477
+ ## Code Coverage
478
+
479
+ ```jsonc
480
+ // vitest.config.ts
481
+ export default defineConfig({
482
+ test: {
483
+ coverage: {
484
+ provider: "v8",
485
+ reporter: ["text", "lcov", "html"],
486
+ thresholds: {
487
+ lines: 80,
488
+ functions: 80,
489
+ branches: 75,
490
+ statements: 80,
491
+ },
492
+ exclude: [
493
+ "**/*.test.ts",
494
+ "**/*.spec.ts",
495
+ "**/types/**",
496
+ "**/mocks/**",
497
+ ],
498
+ },
499
+ },
500
+ });
501
+
502
+ // Run: npx vitest --coverage
503
+ ```
504
+
505
+ ```
506
+ Coverage rules:
507
+ - 80% is the practical threshold (not 100%)
508
+ - 100% coverage ≠ 100% confidence
509
+ - Cover edge cases and error paths, not just happy paths
510
+ - Avoid testing implementation details (private methods, internal state)
511
+ - Focus coverage on: business logic, data transformations, auth/security
512
+ - Skip coverage on: config files, types-only files, generated code
513
+ ```
514
+
515
+ ---
516
+
517
+ ## Output Format
518
+
519
+ ```
520
+ ━━━ Testing Report ━━━━━━━━━━━━━━━━━━━━━━━━
521
+ Skill: Testing Patterns
522
+ Framework: [Vitest/Jest/pytest/Playwright]
523
+ Scope: [N test files · N test cases]
524
+ ─────────────────────────────────────────────────
525
+ ✅ Passed: [N/N tests passing]
526
+ ⚠️ Warnings: [flaky tests, slow tests]
527
+ ❌ Blocked: [failing tests]
528
+ ─────────────────────────────────────────────────
529
+ Coverage: [lines/branches/functions %]
530
+ VBC status: PENDING → VERIFIED
531
+ Evidence: [test runner output]
532
+ ```
533
+
534
+ ---
535
+
536
+ ## 🤖 LLM-Specific Traps
537
+
538
+ 1. **`getByTestId` as Default:** Testing Library queries should use roles and labels first. `getByTestId` is a last resort.
539
+ 2. **Testing Implementation, Not Behavior:** Don't test internal state, private methods, or CSS classes. Test what the user sees and does.
540
+ 3. **Missing `userEvent.setup()`:** Always call `userEvent.setup()` before interactions. Direct `userEvent.click()` is deprecated.
541
+ 4. **`vi.mock()` Without `vi.clearAllMocks()`:** Mock state leaks between tests without clearing. Always call in `beforeEach`.
542
+ 5. **Async Tests Without `await`:** Forgetting `await` in async test assertions silently passes. Always `await expect(fn()).rejects.toThrow()`.
543
+ 6. **Snapshot Test Overuse:** Snapshot tests are brittle and low-value for UI. Use them for serialized data structures, not rendered components.
544
+ 7. **Missing Error Path Tests:** Only testing the happy path. Every function that can fail needs at least one error test.
545
+ 8. **Hardcoded IDs/Dates:** Tests relying on specific database IDs or timestamps are flaky. Use factories and relative assertions.
546
+ 9. **`--coverage` Without Thresholds:** Coverage reports without enforced thresholds are useless. Set minimum thresholds in config.
547
+ 10. **E2E Tests That Test Implementation:** E2E tests should simulate real user flows, not click through internal implementation details.
548
+
549
+ ---
550
+
551
+ ## 🏛️ Tribunal Integration
552
+
553
+ **Slash command: `/test`**
554
+
555
+ ### ✅ Pre-Flight Self-Audit
556
+
557
+ ```
558
+ ✅ Does every test follow AAA (Arrange-Act-Assert)?
559
+ ✅ Are test names descriptive (unit + scenario + expected)?
560
+ ✅ Did I test both happy path AND error cases?
561
+ ✅ Am I using Testing Library query priorities correctly?
562
+ ✅ Did I use userEvent.setup() for interactions?
563
+ ✅ Did I clear mocks in beforeEach?
564
+ ✅ Are async assertions properly awaited?
565
+ ✅ Is coverage threshold enforced in config?
566
+ ✅ Are E2E tests testing user behavior (not implementation)?
567
+ ✅ Do all tests pass independently (no shared state)?
568
+ ```
569
+
570
+ ### 🛑 VBC Protocol
571
+
572
+ - ❌ **Forbidden:** Writing tests that aren't run.
573
+ - ✅ **Required:** Provide test runner output showing pass/fail counts.