tribunal-kit 4.5.1 → 4.6.1

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 (214) hide show
  1. package/.agent/.shared/ui-ux-pro-max/README.md +4 -4
  2. package/.agent/ARCHITECTURE.md +282 -277
  3. package/.agent/agents/accessibility-reviewer.md +187 -187
  4. package/.agent/agents/ai-code-reviewer.md +199 -199
  5. package/.agent/agents/api-architect.md +71 -66
  6. package/.agent/agents/backend-specialist.md +219 -215
  7. package/.agent/agents/cloud-engineer.md +98 -0
  8. package/.agent/agents/code-archaeologist.md +168 -161
  9. package/.agent/agents/database-architect.md +184 -184
  10. package/.agent/agents/db-latency-auditor.md +213 -216
  11. package/.agent/agents/debugger.md +198 -191
  12. package/.agent/agents/dependency-reviewer.md +106 -103
  13. package/.agent/agents/devops-engineer.md +218 -218
  14. package/.agent/agents/documentation-writer.md +209 -201
  15. package/.agent/agents/explorer-agent.md +167 -160
  16. package/.agent/agents/frontend-reviewer.md +162 -160
  17. package/.agent/agents/frontend-specialist.md +257 -248
  18. package/.agent/agents/game-developer.md +48 -48
  19. package/.agent/agents/logic-reviewer.md +118 -116
  20. package/.agent/agents/mobile-developer.md +197 -200
  21. package/.agent/agents/mobile-reviewer.md +159 -162
  22. package/.agent/agents/orchestrator.md +187 -181
  23. package/.agent/agents/penetration-tester.md +160 -157
  24. package/.agent/agents/performance-optimizer.md +183 -183
  25. package/.agent/agents/performance-reviewer.md +178 -178
  26. package/.agent/agents/precedence-reviewer.md +251 -250
  27. package/.agent/agents/product-manager.md +149 -142
  28. package/.agent/agents/product-owner.md +81 -80
  29. package/.agent/agents/project-planner.md +152 -142
  30. package/.agent/agents/qa-automation-engineer.md +216 -225
  31. package/.agent/agents/resilience-reviewer.md +88 -88
  32. package/.agent/agents/schema-reviewer.md +67 -67
  33. package/.agent/agents/security-auditor.md +180 -174
  34. package/.agent/agents/seo-specialist.md +188 -193
  35. package/.agent/agents/sql-reviewer.md +159 -161
  36. package/.agent/agents/supervisor-agent.md +173 -184
  37. package/.agent/agents/swarm-worker-contracts.md +170 -166
  38. package/.agent/agents/swarm-worker-registry.md +92 -92
  39. package/.agent/agents/system-architect.md +85 -0
  40. package/.agent/agents/test-coverage-reviewer.md +158 -160
  41. package/.agent/agents/test-engineer.md +118 -118
  42. package/.agent/agents/throughput-optimizer.md +291 -299
  43. package/.agent/agents/type-safety-reviewer.md +182 -175
  44. package/.agent/agents/ui-ux-auditor.md +300 -292
  45. package/.agent/agents/vitals-reviewer.md +223 -223
  46. package/.agent/mcp_config.json +37 -40
  47. package/.agent/patterns/generator.md +11 -9
  48. package/.agent/patterns/inversion.md +14 -12
  49. package/.agent/patterns/pipeline.md +11 -9
  50. package/.agent/patterns/reviewer.md +15 -13
  51. package/.agent/patterns/tool-wrapper.md +11 -9
  52. package/.agent/routing_index.json +714 -0
  53. package/.agent/rules/GEMINI.md +359 -352
  54. package/.agent/scripts/compile_router.py +112 -0
  55. package/.agent/scripts/migrate_skills_frontmatter.py +64 -0
  56. package/.agent/scripts/strengthen_skills.js +1 -1
  57. package/.agent/skills/advanced-rag-pipelines/SKILL.md +56 -0
  58. package/.agent/skills/agent-organizer/SKILL.md +156 -150
  59. package/.agent/skills/agentic-patterns/SKILL.md +313 -315
  60. package/.agent/skills/ai-prompt-injection-defense/SKILL.md +190 -184
  61. package/.agent/skills/api-patterns/SKILL.md +253 -247
  62. package/.agent/skills/api-security-auditor/SKILL.md +195 -193
  63. package/.agent/skills/app-builder/SKILL.md +573 -572
  64. package/.agent/skills/app-builder/templates/SKILL.md +108 -115
  65. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -76
  66. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -92
  67. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -88
  68. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -88
  69. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -83
  70. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -90
  71. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -90
  72. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +126 -122
  73. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +127 -122
  74. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +172 -169
  75. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +139 -134
  76. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -83
  77. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +122 -119
  78. package/.agent/skills/appflow-wireframe/SKILL.md +146 -145
  79. package/.agent/skills/architecture/SKILL.md +226 -219
  80. package/.agent/skills/authentication-best-practices/SKILL.md +197 -189
  81. package/.agent/skills/backend-security-expert/SKILL.md +16 -2
  82. package/.agent/skills/bash-linux/SKILL.md +179 -179
  83. package/.agent/skills/behavioral-modes/SKILL.md +239 -223
  84. package/.agent/skills/brainstorming/SKILL.md +498 -486
  85. package/.agent/skills/browser-native-ai/SKILL.md +57 -4
  86. package/.agent/skills/building-native-ui/SKILL.md +202 -202
  87. package/.agent/skills/cicd-pro/SKILL.md +442 -0
  88. package/.agent/skills/clean-code/SKILL.md +400 -381
  89. package/.agent/skills/cloud-architect/SKILL.md +439 -0
  90. package/.agent/skills/code-review-checklist/SKILL.md +203 -194
  91. package/.agent/skills/config-validator/SKILL.md +165 -165
  92. package/.agent/skills/containerization-pro/SKILL.md +452 -0
  93. package/.agent/skills/csharp-developer/SKILL.md +518 -518
  94. package/.agent/skills/data-validation-schemas/SKILL.md +333 -328
  95. package/.agent/skills/database-design/SKILL.md +247 -240
  96. package/.agent/skills/deployment-procedures/SKILL.md +172 -169
  97. package/.agent/skills/devops-engineer/SKILL.md +345 -345
  98. package/.agent/skills/devops-incident-responder/SKILL.md +143 -137
  99. package/.agent/skills/documentation-templates/SKILL.md +291 -279
  100. package/.agent/skills/edge-computing/SKILL.md +183 -181
  101. package/.agent/skills/emil-design-eng/SKILL.md +147 -0
  102. package/.agent/skills/error-resilience/SKILL.md +411 -428
  103. package/.agent/skills/extract-design-system/SKILL.md +160 -158
  104. package/.agent/skills/framer-motion-expert/SKILL.md +253 -244
  105. package/.agent/skills/frontend-design/SKILL.md +208 -201
  106. package/.agent/skills/frontend-security-expert/SKILL.md +16 -3
  107. package/.agent/skills/game-design-expert/SKILL.md +132 -129
  108. package/.agent/skills/game-engineering-expert/SKILL.md +148 -146
  109. package/.agent/skills/generative-ui-expert/SKILL.md +57 -1
  110. package/.agent/skills/geo-fundamentals/SKILL.md +148 -147
  111. package/.agent/skills/git-pro/SKILL.md +435 -0
  112. package/.agent/skills/github-operations/SKILL.md +335 -329
  113. package/.agent/skills/gsap-core/SKILL.md +319 -308
  114. package/.agent/skills/gsap-frameworks/SKILL.md +213 -207
  115. package/.agent/skills/gsap-performance/SKILL.md +139 -133
  116. package/.agent/skills/gsap-plugins/SKILL.md +486 -480
  117. package/.agent/skills/gsap-react/SKILL.md +202 -189
  118. package/.agent/skills/gsap-scrolltrigger/SKILL.md +357 -350
  119. package/.agent/skills/gsap-timeline/SKILL.md +165 -161
  120. package/.agent/skills/gsap-utils/SKILL.md +344 -338
  121. package/.agent/skills/harness-protocol/SKILL.md +48 -0
  122. package/.agent/skills/i18n-localization/SKILL.md +174 -163
  123. package/.agent/skills/intelligent-routing/SKILL.md +202 -246
  124. package/.agent/skills/knowledge-graph/SKILL.md +60 -52
  125. package/.agent/skills/lint-and-validate/SKILL.md +261 -261
  126. package/.agent/skills/llm-engineering/SKILL.md +400 -394
  127. package/.agent/skills/local-first/SKILL.md +178 -178
  128. package/.agent/skills/mcp-builder/SKILL.md +143 -142
  129. package/.agent/skills/mobile-design/SKILL.md +272 -263
  130. package/.agent/skills/monorepo-management/SKILL.md +335 -334
  131. package/.agent/skills/motion-engineering/SKILL.md +266 -234
  132. package/.agent/skills/nextjs-react-expert/SKILL.md +236 -234
  133. package/.agent/skills/nodejs-best-practices/SKILL.md +547 -548
  134. package/.agent/skills/observability/SKILL.md +343 -343
  135. package/.agent/skills/parallel-agents/SKILL.md +143 -146
  136. package/.agent/skills/performance-profiling/SKILL.md +259 -267
  137. package/.agent/skills/plan-writing/SKILL.md +150 -142
  138. package/.agent/skills/platform-engineer/SKILL.md +148 -147
  139. package/.agent/skills/playwright-best-practices/SKILL.md +188 -187
  140. package/.agent/skills/powershell-windows/SKILL.md +162 -162
  141. package/.agent/skills/project-idioms/SKILL.md +137 -137
  142. package/.agent/skills/python-patterns/SKILL.md +260 -259
  143. package/.agent/skills/python-pro/SKILL.md +324 -323
  144. package/.agent/skills/react-specialist/SKILL.md +305 -277
  145. package/.agent/skills/readme-builder/SKILL.md +310 -300
  146. package/.agent/skills/realtime-patterns/SKILL.md +323 -319
  147. package/.agent/skills/red-team-tactics/SKILL.md +231 -218
  148. package/.agent/skills/review-animations/SKILL.md +72 -0
  149. package/.agent/skills/review-animations/STANDARDS.md +73 -0
  150. package/.agent/skills/rust-pro/SKILL.md +671 -673
  151. package/.agent/skills/seo-fundamentals/SKILL.md +179 -179
  152. package/.agent/skills/server-management/SKILL.md +218 -214
  153. package/.agent/skills/shadcn-ui-expert/SKILL.md +231 -231
  154. package/.agent/skills/skill-creator/SKILL.md +87 -86
  155. package/.agent/skills/sql-pro/SKILL.md +629 -629
  156. package/.agent/skills/supabase-postgres-best-practices/SKILL.md +97 -97
  157. package/.agent/skills/swiftui-expert/SKILL.md +204 -201
  158. package/.agent/skills/system-design-pro/SKILL.md +345 -0
  159. package/.agent/skills/systematic-debugging/SKILL.md +153 -142
  160. package/.agent/skills/tailwind-patterns/SKILL.md +610 -566
  161. package/.agent/skills/tdd-workflow/SKILL.md +169 -161
  162. package/.agent/skills/test-result-analyzer/SKILL.md +313 -309
  163. package/.agent/skills/testing-patterns/SKILL.md +566 -579
  164. package/.agent/skills/trend-researcher/SKILL.md +243 -237
  165. package/.agent/skills/typescript-advanced/SKILL.md +336 -335
  166. package/.agent/skills/ui-ux-pro-max/SKILL.md +590 -562
  167. package/.agent/skills/ui-ux-researcher/SKILL.md +244 -244
  168. package/.agent/skills/vue-expert/SKILL.md +294 -275
  169. package/.agent/skills/vulnerability-scanner/SKILL.md +416 -404
  170. package/.agent/skills/web-accessibility-auditor/SKILL.md +219 -218
  171. package/.agent/skills/web-design-guidelines/SKILL.md +192 -186
  172. package/.agent/skills/webapp-testing/SKILL.md +167 -169
  173. package/.agent/skills/webgpu-performance/SKILL.md +56 -2
  174. package/.agent/skills/whimsy-injector/SKILL.md +346 -325
  175. package/.agent/skills/workflow-optimizer/SKILL.md +231 -229
  176. package/.agent/workflows/acf.md +141 -0
  177. package/.agent/workflows/api-tester.md +176 -151
  178. package/.agent/workflows/audit.md +150 -127
  179. package/.agent/workflows/brainstorm.md +134 -110
  180. package/.agent/workflows/changelog.md +140 -112
  181. package/.agent/workflows/create.md +168 -124
  182. package/.agent/workflows/debug.md +190 -165
  183. package/.agent/workflows/deploy.md +201 -180
  184. package/.agent/workflows/enhance.md +154 -128
  185. package/.agent/workflows/fix.md +136 -114
  186. package/.agent/workflows/generate.md +198 -183
  187. package/.agent/workflows/marathon.md +37 -11
  188. package/.agent/workflows/migrate.md +184 -160
  189. package/.agent/workflows/orchestrate.md +192 -168
  190. package/.agent/workflows/performance-benchmarker.md +135 -114
  191. package/.agent/workflows/plan.md +196 -173
  192. package/.agent/workflows/preview.md +103 -80
  193. package/.agent/workflows/refactor.md +192 -161
  194. package/.agent/workflows/review-ai.md +125 -101
  195. package/.agent/workflows/review.md +141 -116
  196. package/.agent/workflows/session.md +122 -94
  197. package/.agent/workflows/status.md +101 -79
  198. package/.agent/workflows/strengthen-skills.md +164 -138
  199. package/.agent/workflows/super-prompt.md +24 -0
  200. package/.agent/workflows/swarm.md +193 -179
  201. package/.agent/workflows/test.md +211 -189
  202. package/.agent/workflows/tribunal-backend.md +136 -105
  203. package/.agent/workflows/tribunal-database.md +129 -95
  204. package/.agent/workflows/tribunal-frontend.md +140 -96
  205. package/.agent/workflows/tribunal-full.md +131 -100
  206. package/.agent/workflows/tribunal-mobile.md +129 -95
  207. package/.agent/workflows/tribunal-performance.md +136 -110
  208. package/.agent/workflows/tribunal-speed.md +209 -183
  209. package/.agent/workflows/ui-ux-pro-max.md +155 -122
  210. package/README.md +107 -55
  211. package/mcp_config.json +1 -3
  212. package/package.json +94 -94
  213. package/.agent/GEMINI.md +0 -121
  214. package/.agent/skills/doc.md +0 -177
@@ -1,583 +1,568 @@
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.1.0
6
- last-updated: 2026-04-26
7
- applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
8
- ---
9
-
10
- # Testing Patterns — Cross-Stack Testing Mastery
11
-
12
- ---
13
-
14
- ## Test Architecture
15
-
16
- ### The Testing Pyramid
17
-
18
- ```
19
- / E2E \ ← Few: critical user flows (Playwright/Cypress)
20
- /──────────\
21
- / Integration \ ← Moderate: API routes, DB queries, component integration
22
- /──────────────\
23
- / Unit Tests \ ← Many: pure functions, hooks, utilities, business logic
24
- /──────────────────\
25
-
26
- Rules:
27
- - 70% unit, 20% integration, 10% E2E
28
- - Unit tests: < 50ms each
29
- - Integration tests: < 2s each
30
- - E2E tests: < 30s each
31
- - If a test takes > 5s, it's a design problem
32
- ```
33
-
34
- ### AAA Pattern (Arrange-Act-Assert)
35
-
36
- ```typescript
37
- // Every test follows the same structure
38
- it("calculates total with tax", () => {
39
- // Arrange — set up the scenario
40
- const cart = new Cart();
41
- cart.addItem({ name: "Widget", price: 100 });
42
- cart.setTaxRate(0.08);
43
-
44
- // Act perform the action being tested
45
- const total = cart.calculateTotal();
46
-
47
- // Assertverify the result
48
- expect(total).toBe(108);
49
- });
50
-
51
- // ❌ BAD: Multiple acts in one test
52
- it("does too many things", () => {
53
- cart.addItem({ name: "A", price: 10 });
54
- expect(cart.total).toBe(10); // assert
55
- cart.addItem({ name: "B", price: 20 });
56
- expect(cart.total).toBe(30); // another assert after another act
57
- cart.removeItem("A");
58
- expect(cart.total).toBe(20); // yet another split into 3 tests
59
- });
60
- ```
61
-
62
- ### Test Naming Convention
63
-
64
- ```typescript
65
- // Format: [unit] + [scenario] + [expected result]
66
-
67
- // ✅ GOOD: Descriptive, reads like a specification
68
- describe("calculateDiscount", () => {
69
- it("returns 0% when cart total is under $50", () => {});
70
- it("returns 10% when cart total is $50-$99", () => {});
71
- it("returns 20% when cart total is $100+", () => {});
72
- it("throws when cart is empty", () => {});
73
- });
74
-
75
- // BAD: Vague, implementation-focused
76
- describe("calculateDiscount", () => {
77
- it("works", () => {});
78
- it("test1", () => {});
79
- it("should return correct value", () => {});
80
- });
81
- ```
82
-
83
- ---
84
-
85
- ## Unit Testing (Vitest / Jest)
86
-
87
- ### Pure Function Testing
88
-
89
- ```typescript
90
- // utils/math.ts
91
- export function clamp(value: number, min: number, max: number): number {
92
- return Math.min(Math.max(value, min), max);
93
- }
94
-
95
- // utils/math.test.ts
96
- import { describe, it, expect } from "vitest";
97
- import { clamp } from "./math";
98
-
99
- describe("clamp", () => {
100
- it("returns the value when within range", () => {
101
- expect(clamp(5, 0, 10)).toBe(5);
102
- });
103
-
104
- it("clamps to min when value is below range", () => {
105
- expect(clamp(-5, 0, 10)).toBe(0);
106
- });
107
-
108
- it("clamps to max when value is above range", () => {
109
- expect(clamp(15, 0, 10)).toBe(10);
110
- });
111
-
112
- it("handles equal min and max", () => {
113
- expect(clamp(5, 3, 3)).toBe(3);
114
- });
115
-
116
- it("handles floating point values", () => {
117
- expect(clamp(0.5, 0, 1)).toBeCloseTo(0.5);
118
- });
119
- });
120
- ```
121
-
122
- ### Async Testing
123
-
124
- ```typescript
125
- import { describe, it, expect, vi } from "vitest";
126
-
127
- // Async function under test
128
- async function fetchUser(id: string): Promise<User> {
129
- const response = await fetch(`/api/users/${id}`);
130
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
131
- return response.json();
132
- }
133
-
134
- describe("fetchUser", () => {
135
- it("returns user data on success", async () => {
136
- const mockUser = { id: "1", name: "Alice" };
137
- global.fetch = vi.fn().mockResolvedValue({
138
- ok: true,
139
- json: () => Promise.resolve(mockUser),
140
- });
141
-
142
- const user = await fetchUser("1");
143
- expect(user).toEqual(mockUser);
144
- expect(fetch).toHaveBeenCalledWith("/api/users/1");
145
- });
146
-
147
- it("throws on HTTP error", async () => {
148
- global.fetch = vi.fn().mockResolvedValue({ ok: false, status: 404 });
149
-
150
- await expect(fetchUser("999")).rejects.toThrow("HTTP 404");
151
- });
152
- });
153
- ```
154
-
155
- ### Timer & Date Mocking
156
-
157
- ```typescript
158
- describe("debounce", () => {
159
- beforeEach(() => {
160
- vi.useFakeTimers();
161
- });
162
-
163
- afterEach(() => {
164
- vi.useRealTimers();
165
- });
166
-
167
- it("delays execution by specified ms", () => {
168
- const fn = vi.fn();
169
- const debounced = debounce(fn, 300);
170
-
171
- debounced();
172
- expect(fn).not.toHaveBeenCalled(); // not yet
173
-
174
- vi.advanceTimersByTime(200);
175
- expect(fn).not.toHaveBeenCalled(); // still not
176
-
177
- vi.advanceTimersByTime(100);
178
- expect(fn).toHaveBeenCalledOnce(); // now
179
- });
180
-
181
- it("resets timer on subsequent calls", () => {
182
- const fn = vi.fn();
183
- const debounced = debounce(fn, 300);
184
-
185
- debounced();
186
- vi.advanceTimersByTime(200);
187
- debounced(); // reset timer
188
- vi.advanceTimersByTime(200);
189
- expect(fn).not.toHaveBeenCalled(); // timer was reset
190
-
191
- vi.advanceTimersByTime(100);
192
- expect(fn).toHaveBeenCalledOnce();
193
- });
194
- });
195
-
196
- // Date mocking
197
- it("formats today's date", () => {
198
- vi.setSystemTime(new Date("2024-06-15T12:00:00Z"));
199
- expect(getFormattedDate()).toBe("June 15, 2024");
200
- vi.useRealTimers();
201
- });
202
- ```
203
-
204
- ---
205
-
206
- ## Mocking Strategies
207
-
208
- ### Module Mocks
209
-
210
- ```typescript
211
- import { vi, describe, it, expect, beforeEach } from "vitest";
212
- import { sendEmail } from "./email-service";
213
- import { createUser } from "./user-service";
214
-
215
- // Mock an entire module
216
- vi.mock("./email-service", () => ({
217
- sendEmail: vi.fn().mockResolvedValue({ sent: true }),
218
- }));
219
-
220
- describe("createUser", () => {
221
- beforeEach(() => {
222
- vi.clearAllMocks(); // reset call counts between tests
223
- });
224
-
225
- it("sends welcome email after creating user", async () => {
226
- await createUser({ name: "Alice", email: "alice@test.com" });
227
-
228
- expect(sendEmail).toHaveBeenCalledWith({
229
- to: "alice@test.com",
230
- subject: "Welcome!",
231
- body: expect.stringContaining("Alice"),
232
- });
233
- });
234
-
235
- it("does not send email on validation failure", async () => {
236
- await expect(createUser({ name: "", email: "" })).rejects.toThrow();
237
- expect(sendEmail).not.toHaveBeenCalled();
238
- });
239
- });
240
- ```
241
-
242
- ### Spy Pattern
243
-
244
- ```typescript
245
- // Spy on an existing method (don't replace it — observe it)
246
- const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
247
-
248
- await riskyOperation();
249
-
250
- expect(consoleSpy).toHaveBeenCalledWith(
251
- expect.stringContaining("failed"),
252
- expect.any(Error)
253
- );
254
-
255
- consoleSpy.mockRestore(); // restore original
256
- ```
257
-
258
- ### Dependency Injection Pattern (Testable by Design)
259
-
260
- ```typescript
261
- // ❌ BAD: Hard-coded dependency — untestable without module mocking
262
- class UserService {
263
- async getUser(id: string) {
264
- return await fetch(`/api/users/${id}`).then((r) => r.json());
265
- }
266
- }
267
-
268
- // ✅ GOOD: Injected dependency — naturally testable
269
- interface HttpClient {
270
- get<T>(url: string): Promise<T>;
271
- }
272
-
273
- class UserService {
274
- constructor(private http: HttpClient) {}
275
-
276
- async getUser(id: string): Promise<User> {
277
- return this.http.get<User>(`/api/users/${id}`);
278
- }
279
- }
280
-
281
- // In test:
282
- const mockHttp: HttpClient = {
283
- get: vi.fn().mockResolvedValue({ id: "1", name: "Alice" }),
284
- };
285
- const service = new UserService(mockHttp);
286
-
287
- // ❌ HALLUCINATION TRAP: Prefer dependency injection over vi.mock()
288
- // vi.mock() is global and can leak between tests
289
- // DI makes tests isolated and explicit
290
- ```
291
-
292
- ---
293
-
294
- ## React Component Testing (Testing Library)
295
-
296
- ```tsx
297
- import { render, screen, waitFor } from "@testing-library/react";
298
- import userEvent from "@testing-library/user-event";
299
- import { describe, it, expect, vi } from "vitest";
300
- import { LoginForm } from "./LoginForm";
301
-
302
- describe("LoginForm", () => {
303
- it("renders email and password fields", () => {
304
- render(<LoginForm onSubmit={vi.fn()} />);
305
-
306
- expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
307
- expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
308
- expect(screen.getByRole("button", { name: /sign in/i })).toBeInTheDocument();
309
- });
310
-
311
- it("calls onSubmit with credentials", async () => {
312
- const user = userEvent.setup();
313
- const onSubmit = vi.fn();
314
- render(<LoginForm onSubmit={onSubmit} />);
315
-
316
- await user.type(screen.getByLabelText(/email/i), "alice@test.com");
317
- await user.type(screen.getByLabelText(/password/i), "secret123");
318
- await user.click(screen.getByRole("button", { name: /sign in/i }));
319
-
320
- expect(onSubmit).toHaveBeenCalledWith({
321
- email: "alice@test.com",
322
- password: "secret123",
323
- });
324
- });
325
-
326
- it("shows validation error for invalid email", async () => {
327
- const user = userEvent.setup();
328
- render(<LoginForm onSubmit={vi.fn()} />);
329
-
330
- await user.type(screen.getByLabelText(/email/i), "not-an-email");
331
- await user.click(screen.getByRole("button", { name: /sign in/i }));
332
-
333
- expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
334
- });
335
-
336
- it("disables submit button while loading", async () => {
337
- render(<LoginForm onSubmit={vi.fn()} isLoading={true} />);
338
-
339
- expect(screen.getByRole("button", { name: /sign in/i })).toBeDisabled();
340
- });
341
- });
342
-
343
- // ❌ HALLUCINATION TRAP: Query priorities (use in this order):
344
- // 1. getByRole — accessible role ("button", "textbox", etc.)
345
- // 2. getByLabelText — form inputs with labels
346
- // 3. getByPlaceholderText — when no label exists
347
- // 4. getByText — non-interactive elements
348
- // 5. getByTestId — LAST RESORT only
349
- // ❌ Never default to getByTestId — it tests implementation, not behavior
350
- ```
351
-
352
- ---
353
-
354
- ## E2E Testing (Playwright)
355
-
356
- ```typescript
357
- import { test, expect } from "@playwright/test";
358
-
359
- test.describe("Login Flow", () => {
360
- test("successful login redirects to dashboard", async ({ page }) => {
361
- await page.goto("/login");
362
-
363
- await page.getByLabel("Email").fill("admin@test.com");
364
- await page.getByLabel("Password").fill("password123");
365
- await page.getByRole("button", { name: "Sign In" }).click();
366
-
367
- // Wait for navigation
368
- await expect(page).toHaveURL("/dashboard");
369
- await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
370
- });
371
-
372
- test("shows error for invalid credentials", async ({ page }) => {
373
- await page.goto("/login");
374
-
375
- await page.getByLabel("Email").fill("wrong@test.com");
376
- await page.getByLabel("Password").fill("wrongpassword");
377
- await page.getByRole("button", { name: "Sign In" }).click();
378
-
379
- await expect(page.getByText("Invalid credentials")).toBeVisible();
380
- await expect(page).toHaveURL("/login"); // no redirect
381
- });
382
-
383
- test("responsive: mobile menu toggles", async ({ page, isMobile }) => {
384
- test.skip(!isMobile, "Mobile only");
385
-
386
- await page.goto("/");
387
- await page.getByRole("button", { name: "Menu" }).click();
388
- await expect(page.getByRole("navigation")).toBeVisible();
389
- });
390
- });
391
-
392
- // API testing with Playwright
393
- test("API: create user returns 201", async ({ request }) => {
394
- const response = await request.post("/api/users", {
395
- data: { name: "Alice", email: "alice@test.com" },
396
- });
397
-
398
- expect(response.status()).toBe(201);
399
- const body = await response.json();
400
- expect(body).toMatchObject({ name: "Alice", email: "alice@test.com" });
401
- });
402
- ```
403
-
404
- ### Playwright Config
405
-
406
- ```typescript
407
- // playwright.config.ts
408
- import { defineConfig } from "@playwright/test";
409
-
410
- export default defineConfig({
411
- testDir: "./e2e",
412
- timeout: 30000,
413
- retries: process.env.CI ? 2 : 0, // retry in CI only
414
- use: {
415
- baseURL: "http://localhost:3000",
416
- trace: "on-first-retry", // save trace on failures
417
- screenshot: "only-on-failure",
418
- },
419
- webServer: {
420
- command: "npm run dev",
421
- port: 3000,
422
- reuseExistingServer: !process.env.CI,
423
- },
424
- projects: [
425
- { name: "chrome", use: { browserName: "chromium" } },
426
- { name: "firefox", use: { browserName: "firefox" } },
427
- { name: "mobile", use: { ...devices["iPhone 14"] } },
428
- ],
429
- });
430
- ```
431
-
432
- ---
433
-
434
- ## API Testing
435
-
436
- ```typescript
437
- // Testing REST APIs with supertest (Express/Fastify)
438
- import request from "supertest";
439
- import { app } from "./app";
440
-
441
- describe("POST /api/users", () => {
442
- it("creates a user and returns 201", async () => {
443
- const response = await request(app)
444
- .post("/api/users")
445
- .send({ name: "Alice", email: "alice@test.com" })
446
- .expect(201)
447
- .expect("Content-Type", /json/);
448
-
449
- expect(response.body).toMatchObject({
450
- id: expect.any(Number),
451
- name: "Alice",
452
- email: "alice@test.com",
453
- });
454
- });
455
-
456
- it("returns 400 for missing required fields", async () => {
457
- await request(app)
458
- .post("/api/users")
459
- .send({ name: "" })
460
- .expect(400);
461
- });
462
-
463
- it("returns 409 for duplicate email", async () => {
464
- await request(app)
465
- .post("/api/users")
466
- .send({ name: "Alice", email: "existing@test.com" })
467
- .expect(409);
468
- });
469
- });
470
- ```
471
-
472
- ---
473
-
474
- ## Mutation Testing (Tribunal Engine)
475
-
476
- ```bash
477
- # Run the Tribunal Mutation Engine
478
- npx tribunal-kit mutate src/math.js "npx jest src/math.test.js"
479
- ```
480
-
481
- ```
482
- Mutation Engine rules:
483
- - Code coverage only proves code was EXECUTED, not that it was TESTED.
484
- - The Mutation Engine swaps operators (=== to !==) and verifies the test suite FAILS.
485
- - If the test passes despite the mutation, the mutant "survives" (false positive test).
486
- - Use this engine on critical business logic to eradicate LLM "tautological" tests.
487
- ```
488
-
489
- ---
490
-
491
- ## Code Coverage
492
-
493
- ```jsonc
494
- // vitest.config.ts
495
- export default defineConfig({
496
- test: {
497
- coverage: {
498
- provider: "v8",
499
- reporter: ["text", "lcov", "html"],
500
- thresholds: {
501
- lines: 80,
502
- functions: 80,
503
- branches: 75,
504
- statements: 80,
505
- },
506
- exclude: [
507
- "**/*.test.ts",
508
- "**/*.spec.ts",
509
- "**/types/**",
510
- "**/mocks/**",
511
- ],
512
- },
513
- },
514
- });
515
-
516
- // Run: npx vitest --coverage
517
- ```
518
-
519
- ```
520
- Coverage rules:
521
- - 80% is the practical threshold (not 100%)
522
- - 100% coverage ≠ 100% confidence
523
- - Cover edge cases and error paths, not just happy paths
524
- - Avoid testing implementation details (private methods, internal state)
525
- - Focus coverage on: business logic, data transformations, auth/security
526
- - Skip coverage on: config files, types-only files, generated code
527
- ```
528
-
529
- ---
530
-
531
-
532
- ---
533
-
534
-
535
-
536
- AI coding assistants often fall into specific bad habits when dealing with this domain. These are strictly forbidden:
537
-
538
- 1. **Over-engineering:** Proposing complex abstractions or distributed systems when a simpler approach suffices.
539
- 2. **Hallucinated Libraries/Methods:** Using non-existent methods or packages. Always `// VERIFY` or check `package.json` / `requirements.txt`.
540
- 3. **Skipping Edge Cases:** Writing the "happy path" and ignoring error handling, timeouts, or data validation.
541
- 4. **Context Amnesia:** Forgetting the user's constraints and offering generic advice instead of tailored solutions.
542
- 5. **Silent Degradation:** Catching and suppressing errors without logging or re-raising.
543
-
544
- ---
545
-
546
-
547
-
548
- **Slash command: `/review` or `/tribunal-full`**
549
- **Active reviewers: `logic-reviewer` · `security-auditor`**
550
-
551
- ### ❌ Forbidden AI Tropes
552
-
553
- 1. **Blind Assumptions:** Never make an assumption without documenting it clearly with `// VERIFY: [reason]`.
554
- 2. **Silent Degradation:** Catching and suppressing errors without logging or handling.
555
- 3. **Context Amnesia:** Forgetting the user's constraints and offering generic advice instead of tailored solutions.
556
-
557
-
558
-
559
- Review these questions before confirming output:
560
- ```
561
- ✅ Did I rely ONLY on real, verified tools and methods?
562
- ✅ Is this solution appropriately scoped to the user's constraints?
563
- ✅ Did I handle potential failure modes and edge cases?
564
- ✅ Have I avoided generic boilerplate that doesn't add value?
565
- ```
566
-
567
- ### 🛑 Verification-Before-Completion (VBC) Protocol
568
-
569
- **CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
570
- - ❌ **Forbidden:** Declaring a task complete because the output "looks correct."
571
- - ✅ **Required:** You are explicitly forbidden from finalizing any task without providing **concrete evidence** (terminal output, passing tests, compile success, or equivalent proof) that your output works as intended.
572
-
573
-
574
- ## Pre-Flight Checklist
575
- - [ ] Have I reviewed the user's specific constraints and requests?
576
- - [ ] Have I checked the environment for relevant existing implementations?
577
-
578
- ## VBC Protocol (Verification-Before-Completion)
579
- You MUST verify existing code signatures and variables before attempting to modify or call them. No hallucination is permitted.
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.1.0
6
+ last-updated: 2026-04-26
7
+ applies-to-model: gemini-2.5-pro, claude-3-7-sonnet
8
+ routing:
9
+ domain: general
10
+ tier: basic
11
+ ---
12
+
13
+ # Testing Patterns — Cross-Stack Testing Mastery
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
+ // Actperform 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(expect.stringContaining("failed"), expect.any(Error));
254
+
255
+ consoleSpy.mockRestore(); // restore original
256
+ ```
257
+
258
+ ### Dependency Injection Pattern (Testable by Design)
259
+
260
+ ```typescript
261
+ // ❌ BAD: Hard-coded dependency — untestable without module mocking
262
+ class UserService {
263
+ async getUser(id: string) {
264
+ return await fetch(`/api/users/${id}`).then((r) => r.json());
265
+ }
266
+ }
267
+
268
+ // ✅ GOOD: Injected dependency — naturally testable
269
+ interface HttpClient {
270
+ get<T>(url: string): Promise<T>;
271
+ }
272
+
273
+ class UserService {
274
+ constructor(private http: HttpClient) {}
275
+
276
+ async getUser(id: string): Promise<User> {
277
+ return this.http.get<User>(`/api/users/${id}`);
278
+ }
279
+ }
280
+
281
+ // In test:
282
+ const mockHttp: HttpClient = {
283
+ get: vi.fn().mockResolvedValue({ id: "1", name: "Alice" }),
284
+ };
285
+ const service = new UserService(mockHttp);
286
+
287
+ // ❌ HALLUCINATION TRAP: Prefer dependency injection over vi.mock()
288
+ // vi.mock() is global and can leak between tests
289
+ // DI makes tests isolated and explicit
290
+ ```
291
+
292
+ ---
293
+
294
+ ## React Component Testing (Testing Library)
295
+
296
+ ```tsx
297
+ import { render, screen, waitFor } from "@testing-library/react";
298
+ import userEvent from "@testing-library/user-event";
299
+ import { describe, it, expect, vi } from "vitest";
300
+ import { LoginForm } from "./LoginForm";
301
+
302
+ describe("LoginForm", () => {
303
+ it("renders email and password fields", () => {
304
+ render(<LoginForm onSubmit={vi.fn()} />);
305
+
306
+ expect(screen.getByLabelText(/email/i)).toBeInTheDocument();
307
+ expect(screen.getByLabelText(/password/i)).toBeInTheDocument();
308
+ expect(screen.getByRole("button", { name: /sign in/i })).toBeInTheDocument();
309
+ });
310
+
311
+ it("calls onSubmit with credentials", async () => {
312
+ const user = userEvent.setup();
313
+ const onSubmit = vi.fn();
314
+ render(<LoginForm onSubmit={onSubmit} />);
315
+
316
+ await user.type(screen.getByLabelText(/email/i), "alice@test.com");
317
+ await user.type(screen.getByLabelText(/password/i), "secret123");
318
+ await user.click(screen.getByRole("button", { name: /sign in/i }));
319
+
320
+ expect(onSubmit).toHaveBeenCalledWith({
321
+ email: "alice@test.com",
322
+ password: "secret123",
323
+ });
324
+ });
325
+
326
+ it("shows validation error for invalid email", async () => {
327
+ const user = userEvent.setup();
328
+ render(<LoginForm onSubmit={vi.fn()} />);
329
+
330
+ await user.type(screen.getByLabelText(/email/i), "not-an-email");
331
+ await user.click(screen.getByRole("button", { name: /sign in/i }));
332
+
333
+ expect(screen.getByText(/invalid email/i)).toBeInTheDocument();
334
+ });
335
+
336
+ it("disables submit button while loading", async () => {
337
+ render(<LoginForm onSubmit={vi.fn()} isLoading={true} />);
580
338
 
339
+ expect(screen.getByRole("button", { name: /sign in/i })).toBeDisabled();
340
+ });
341
+ });
342
+
343
+ // ❌ HALLUCINATION TRAP: Query priorities (use in this order):
344
+ // 1. getByRole — accessible role ("button", "textbox", etc.)
345
+ // 2. getByLabelText — form inputs with labels
346
+ // 3. getByPlaceholderText — when no label exists
347
+ // 4. getByText — non-interactive elements
348
+ // 5. getByTestId — LAST RESORT only
349
+ // ❌ Never default to getByTestId — it tests implementation, not behavior
350
+ ```
351
+
352
+ ---
353
+
354
+ ## E2E Testing (Playwright)
355
+
356
+ ```typescript
357
+ import { test, expect } from "@playwright/test";
358
+
359
+ test.describe("Login Flow", () => {
360
+ test("successful login redirects to dashboard", async ({ page }) => {
361
+ await page.goto("/login");
362
+
363
+ await page.getByLabel("Email").fill("admin@test.com");
364
+ await page.getByLabel("Password").fill("password123");
365
+ await page.getByRole("button", { name: "Sign In" }).click();
366
+
367
+ // Wait for navigation
368
+ await expect(page).toHaveURL("/dashboard");
369
+ await expect(page.getByRole("heading", { name: "Dashboard" })).toBeVisible();
370
+ });
371
+
372
+ test("shows error for invalid credentials", async ({ page }) => {
373
+ await page.goto("/login");
374
+
375
+ await page.getByLabel("Email").fill("wrong@test.com");
376
+ await page.getByLabel("Password").fill("wrongpassword");
377
+ await page.getByRole("button", { name: "Sign In" }).click();
378
+
379
+ await expect(page.getByText("Invalid credentials")).toBeVisible();
380
+ await expect(page).toHaveURL("/login"); // no redirect
381
+ });
382
+
383
+ test("responsive: mobile menu toggles", async ({ page, isMobile }) => {
384
+ test.skip(!isMobile, "Mobile only");
385
+
386
+ await page.goto("/");
387
+ await page.getByRole("button", { name: "Menu" }).click();
388
+ await expect(page.getByRole("navigation")).toBeVisible();
389
+ });
390
+ });
391
+
392
+ // API testing with Playwright
393
+ test("API: create user returns 201", async ({ request }) => {
394
+ const response = await request.post("/api/users", {
395
+ data: { name: "Alice", email: "alice@test.com" },
396
+ });
397
+
398
+ expect(response.status()).toBe(201);
399
+ const body = await response.json();
400
+ expect(body).toMatchObject({ name: "Alice", email: "alice@test.com" });
401
+ });
402
+ ```
403
+
404
+ ### Playwright Config
405
+
406
+ ```typescript
407
+ // playwright.config.ts
408
+ import { defineConfig } from "@playwright/test";
409
+
410
+ export default defineConfig({
411
+ testDir: "./e2e",
412
+ timeout: 30000,
413
+ retries: process.env.CI ? 2 : 0, // retry in CI only
414
+ use: {
415
+ baseURL: "http://localhost:3000",
416
+ trace: "on-first-retry", // save trace on failures
417
+ screenshot: "only-on-failure",
418
+ },
419
+ webServer: {
420
+ command: "npm run dev",
421
+ port: 3000,
422
+ reuseExistingServer: !process.env.CI,
423
+ },
424
+ projects: [
425
+ { name: "chrome", use: { browserName: "chromium" } },
426
+ { name: "firefox", use: { browserName: "firefox" } },
427
+ { name: "mobile", use: { ...devices["iPhone 14"] } },
428
+ ],
429
+ });
430
+ ```
431
+
432
+ ---
433
+
434
+ ## API Testing
435
+
436
+ ```typescript
437
+ // Testing REST APIs with supertest (Express/Fastify)
438
+ import request from "supertest";
439
+ import { app } from "./app";
440
+
441
+ describe("POST /api/users", () => {
442
+ it("creates a user and returns 201", async () => {
443
+ const response = await request(app).post("/api/users").send({ name: "Alice", email: "alice@test.com" }).expect(201).expect("Content-Type", /json/);
444
+
445
+ expect(response.body).toMatchObject({
446
+ id: expect.any(Number),
447
+ name: "Alice",
448
+ email: "alice@test.com",
449
+ });
450
+ });
451
+
452
+ it("returns 400 for missing required fields", async () => {
453
+ await request(app).post("/api/users").send({ name: "" }).expect(400);
454
+ });
455
+
456
+ it("returns 409 for duplicate email", async () => {
457
+ await request(app).post("/api/users").send({ name: "Alice", email: "existing@test.com" }).expect(409);
458
+ });
459
+ });
460
+ ```
461
+
462
+ ---
463
+
464
+ ## Mutation Testing (Tribunal Engine)
465
+
466
+ ```bash
467
+ # Run the Tribunal Mutation Engine
468
+ npx tribunal-kit mutate src/math.js "npx jest src/math.test.js"
469
+ ```
470
+
471
+ ```
472
+ Mutation Engine rules:
473
+ - Code coverage only proves code was EXECUTED, not that it was TESTED.
474
+ - The Mutation Engine swaps operators (=== to !==) and verifies the test suite FAILS.
475
+ - If the test passes despite the mutation, the mutant "survives" (false positive test).
476
+ - Use this engine on critical business logic to eradicate LLM "tautological" tests.
477
+ ```
478
+
479
+ ---
480
+
481
+ ## Code Coverage
482
+
483
+ ```jsonc
484
+ // vitest.config.ts
485
+ export default defineConfig({
486
+ test: {
487
+ coverage: {
488
+ provider: "v8",
489
+ reporter: ["text", "lcov", "html"],
490
+ thresholds: {
491
+ lines: 80,
492
+ functions: 80,
493
+ branches: 75,
494
+ statements: 80,
495
+ },
496
+ exclude: [
497
+ "**/*.test.ts",
498
+ "**/*.spec.ts",
499
+ "**/types/**",
500
+ "**/mocks/**",
501
+ ],
502
+ },
503
+ },
504
+ });
505
+
506
+ // Run: npx vitest --coverage
507
+ ```
508
+
509
+ ```
510
+ Coverage rules:
511
+ - 80% is the practical threshold (not 100%)
512
+ - 100% coverage ≠ 100% confidence
513
+ - Cover edge cases and error paths, not just happy paths
514
+ - Avoid testing implementation details (private methods, internal state)
515
+ - Focus coverage on: business logic, data transformations, auth/security
516
+ - Skip coverage on: config files, types-only files, generated code
517
+ ```
518
+
519
+ ---
520
+
521
+ ---
522
+
523
+ AI coding assistants often fall into specific bad habits when dealing with this domain. These are strictly forbidden:
524
+
525
+ 1. **Over-engineering:** Proposing complex abstractions or distributed systems when a simpler approach suffices.
526
+ 2. **Hallucinated Libraries/Methods:** Using non-existent methods or packages. Always `// VERIFY` or check `package.json` / `requirements.txt`.
527
+ 3. **Skipping Edge Cases:** Writing the "happy path" and ignoring error handling, timeouts, or data validation.
528
+ 4. **Context Amnesia:** Forgetting the user's constraints and offering generic advice instead of tailored solutions.
529
+ 5. **Silent Degradation:** Catching and suppressing errors without logging or re-raising.
530
+
531
+ ---
532
+
533
+ **Slash command: `/review` or `/tribunal-full`**
534
+ **Active reviewers: `logic-reviewer` · `security-auditor`**
535
+
536
+ ### ❌ Forbidden AI Tropes
537
+
538
+ 1. **Blind Assumptions:** Never make an assumption without documenting it clearly with `// VERIFY: [reason]`.
539
+ 2. **Silent Degradation:** Catching and suppressing errors without logging or handling.
540
+ 3. **Context Amnesia:** Forgetting the user's constraints and offering generic advice instead of tailored solutions.
541
+
542
+ Review these questions before confirming output:
543
+
544
+ ```
545
+ ✅ Did I rely ONLY on real, verified tools and methods?
546
+ ✅ Is this solution appropriately scoped to the user's constraints?
547
+ ✅ Did I handle potential failure modes and edge cases?
548
+ ✅ Have I avoided generic boilerplate that doesn't add value?
549
+ ```
550
+
551
+ ### 🛑 Verification-Before-Completion (VBC) Protocol
552
+
553
+ **CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
554
+
555
+ - ❌ **Forbidden:** Declaring a task complete because the output "looks correct."
556
+ - ✅ **Required:** You are explicitly forbidden from finalizing any task without providing **concrete evidence** (terminal output, passing tests, compile success, or equivalent proof) that your output works as intended.
557
+
558
+ ## Pre-Flight Checklist
559
+
560
+ - [ ] Have I reviewed the user's specific constraints and requests?
561
+ - [ ] Have I checked the environment for relevant existing implementations?
562
+
563
+ ## VBC Protocol (Verification-Before-Completion)
564
+
565
+ You MUST verify existing code signatures and variables before attempting to modify or call them. No hallucination is permitted.
581
566
 
582
567
  ---
583
568
 
@@ -607,6 +592,7 @@ AI coding assistants often fall into specific bad habits when dealing with this
607
592
  ### ✅ Pre-Flight Self-Audit
608
593
 
609
594
  Review these questions before confirming output:
595
+
610
596
  ```
611
597
  ✅ Did I rely ONLY on real, verified tools and methods?
612
598
  ✅ Is this solution appropriately scoped to the user's constraints?
@@ -617,5 +603,6 @@ Review these questions before confirming output:
617
603
  ### 🛑 Verification-Before-Completion (VBC) Protocol
618
604
 
619
605
  **CRITICAL:** You must follow a strict "evidence-based closeout" state machine.
606
+
620
607
  - ❌ **Forbidden:** Declaring a task complete because the output "looks correct."
621
608
  - ✅ **Required:** You are explicitly forbidden from finalizing any task without providing **concrete evidence** (terminal output, passing tests, compile success, or equivalent proof) that your output works as intended.