tribunal-kit 4.5.0 → 4.6.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 (217) hide show
  1. package/.agent/.shared/ui-ux-pro-max/README.md +4 -4
  2. package/.agent/ARCHITECTURE.md +279 -277
  3. package/.agent/GEMINI.md +127 -121
  4. package/.agent/agents/accessibility-reviewer.md +187 -187
  5. package/.agent/agents/ai-code-reviewer.md +199 -199
  6. package/.agent/agents/api-architect.md +71 -66
  7. package/.agent/agents/backend-specialist.md +219 -215
  8. package/.agent/agents/cloud-engineer.md +98 -0
  9. package/.agent/agents/code-archaeologist.md +168 -161
  10. package/.agent/agents/database-architect.md +184 -184
  11. package/.agent/agents/db-latency-auditor.md +213 -216
  12. package/.agent/agents/debugger.md +198 -191
  13. package/.agent/agents/dependency-reviewer.md +106 -103
  14. package/.agent/agents/devops-engineer.md +218 -218
  15. package/.agent/agents/documentation-writer.md +209 -201
  16. package/.agent/agents/explorer-agent.md +167 -160
  17. package/.agent/agents/frontend-reviewer.md +162 -160
  18. package/.agent/agents/frontend-specialist.md +257 -248
  19. package/.agent/agents/game-developer.md +48 -48
  20. package/.agent/agents/logic-reviewer.md +118 -116
  21. package/.agent/agents/mobile-developer.md +197 -200
  22. package/.agent/agents/mobile-reviewer.md +159 -162
  23. package/.agent/agents/orchestrator.md +187 -181
  24. package/.agent/agents/penetration-tester.md +160 -157
  25. package/.agent/agents/performance-optimizer.md +183 -183
  26. package/.agent/agents/performance-reviewer.md +178 -178
  27. package/.agent/agents/precedence-reviewer.md +251 -250
  28. package/.agent/agents/product-manager.md +149 -142
  29. package/.agent/agents/product-owner.md +81 -80
  30. package/.agent/agents/project-planner.md +152 -142
  31. package/.agent/agents/qa-automation-engineer.md +216 -225
  32. package/.agent/agents/resilience-reviewer.md +88 -88
  33. package/.agent/agents/schema-reviewer.md +67 -67
  34. package/.agent/agents/security-auditor.md +180 -174
  35. package/.agent/agents/seo-specialist.md +188 -193
  36. package/.agent/agents/sql-reviewer.md +159 -161
  37. package/.agent/agents/supervisor-agent.md +173 -184
  38. package/.agent/agents/swarm-worker-contracts.md +170 -166
  39. package/.agent/agents/swarm-worker-registry.md +92 -92
  40. package/.agent/agents/system-architect.md +85 -0
  41. package/.agent/agents/test-coverage-reviewer.md +158 -160
  42. package/.agent/agents/test-engineer.md +118 -118
  43. package/.agent/agents/throughput-optimizer.md +291 -299
  44. package/.agent/agents/type-safety-reviewer.md +182 -175
  45. package/.agent/agents/ui-ux-auditor.md +300 -292
  46. package/.agent/agents/vitals-reviewer.md +223 -223
  47. package/.agent/mcp_config.json +37 -40
  48. package/.agent/patterns/generator.md +11 -9
  49. package/.agent/patterns/inversion.md +14 -12
  50. package/.agent/patterns/pipeline.md +11 -9
  51. package/.agent/patterns/reviewer.md +15 -13
  52. package/.agent/patterns/tool-wrapper.md +11 -9
  53. package/.agent/routing_index.json +654 -0
  54. package/.agent/rules/GEMINI.md +358 -352
  55. package/.agent/scripts/compile_router.py +112 -0
  56. package/.agent/scripts/migrate_skills_frontmatter.py +64 -0
  57. package/.agent/scripts/strengthen_skills.js +1 -1
  58. package/.agent/skills/advanced-rag-pipelines/SKILL.md +56 -0
  59. package/.agent/skills/agent-organizer/SKILL.md +156 -150
  60. package/.agent/skills/agentic-patterns/SKILL.md +313 -315
  61. package/.agent/skills/ai-prompt-injection-defense/SKILL.md +190 -184
  62. package/.agent/skills/api-patterns/SKILL.md +253 -247
  63. package/.agent/skills/api-security-auditor/SKILL.md +195 -193
  64. package/.agent/skills/app-builder/SKILL.md +573 -572
  65. package/.agent/skills/app-builder/templates/SKILL.md +108 -115
  66. package/.agent/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -76
  67. package/.agent/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -92
  68. package/.agent/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -88
  69. package/.agent/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -88
  70. package/.agent/skills/app-builder/templates/express-api/TEMPLATE.md +83 -83
  71. package/.agent/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -90
  72. package/.agent/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -90
  73. package/.agent/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +126 -122
  74. package/.agent/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +127 -122
  75. package/.agent/skills/app-builder/templates/nextjs-static/TEMPLATE.md +172 -169
  76. package/.agent/skills/app-builder/templates/nuxt-app/TEMPLATE.md +139 -134
  77. package/.agent/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -83
  78. package/.agent/skills/app-builder/templates/react-native-app/TEMPLATE.md +122 -119
  79. package/.agent/skills/appflow-wireframe/SKILL.md +146 -145
  80. package/.agent/skills/architecture/SKILL.md +226 -219
  81. package/.agent/skills/authentication-best-practices/SKILL.md +197 -189
  82. package/.agent/skills/backend-security-expert/SKILL.md +16 -2
  83. package/.agent/skills/bash-linux/SKILL.md +179 -179
  84. package/.agent/skills/behavioral-modes/SKILL.md +239 -223
  85. package/.agent/skills/brainstorming/SKILL.md +498 -486
  86. package/.agent/skills/browser-native-ai/SKILL.md +57 -4
  87. package/.agent/skills/building-native-ui/SKILL.md +202 -202
  88. package/.agent/skills/cicd-pro/SKILL.md +442 -0
  89. package/.agent/skills/clean-code/SKILL.md +400 -381
  90. package/.agent/skills/cloud-architect/SKILL.md +439 -0
  91. package/.agent/skills/code-review-checklist/SKILL.md +203 -194
  92. package/.agent/skills/config-validator/SKILL.md +165 -165
  93. package/.agent/skills/containerization-pro/SKILL.md +452 -0
  94. package/.agent/skills/csharp-developer/SKILL.md +518 -518
  95. package/.agent/skills/data-validation-schemas/SKILL.md +333 -328
  96. package/.agent/skills/database-design/SKILL.md +247 -240
  97. package/.agent/skills/deployment-procedures/SKILL.md +172 -169
  98. package/.agent/skills/devops-engineer/SKILL.md +345 -345
  99. package/.agent/skills/devops-incident-responder/SKILL.md +143 -137
  100. package/.agent/skills/doc.md +209 -177
  101. package/.agent/skills/documentation-templates/SKILL.md +291 -279
  102. package/.agent/skills/edge-computing/SKILL.md +183 -181
  103. package/.agent/skills/error-resilience/SKILL.md +411 -428
  104. package/.agent/skills/extract-design-system/SKILL.md +160 -158
  105. package/.agent/skills/framer-motion-expert/SKILL.md +253 -244
  106. package/.agent/skills/frontend-design/SKILL.md +208 -201
  107. package/.agent/skills/frontend-security-expert/SKILL.md +16 -3
  108. package/.agent/skills/game-design-expert/SKILL.md +132 -129
  109. package/.agent/skills/game-engineering-expert/SKILL.md +148 -146
  110. package/.agent/skills/generative-ui-expert/SKILL.md +57 -1
  111. package/.agent/skills/geo-fundamentals/SKILL.md +148 -147
  112. package/.agent/skills/git-pro/SKILL.md +435 -0
  113. package/.agent/skills/github-operations/SKILL.md +335 -329
  114. package/.agent/skills/gsap-core/SKILL.md +319 -308
  115. package/.agent/skills/gsap-frameworks/SKILL.md +213 -207
  116. package/.agent/skills/gsap-performance/SKILL.md +139 -133
  117. package/.agent/skills/gsap-plugins/SKILL.md +486 -480
  118. package/.agent/skills/gsap-react/SKILL.md +202 -189
  119. package/.agent/skills/gsap-scrolltrigger/SKILL.md +357 -350
  120. package/.agent/skills/gsap-timeline/SKILL.md +165 -161
  121. package/.agent/skills/gsap-utils/SKILL.md +344 -338
  122. package/.agent/skills/harness-protocol/SKILL.md +48 -0
  123. package/.agent/skills/i18n-localization/SKILL.md +174 -163
  124. package/.agent/skills/intelligent-routing/SKILL.md +202 -246
  125. package/.agent/skills/knowledge-graph/SKILL.md +60 -52
  126. package/.agent/skills/lint-and-validate/SKILL.md +261 -261
  127. package/.agent/skills/llm-engineering/SKILL.md +400 -394
  128. package/.agent/skills/local-first/SKILL.md +178 -178
  129. package/.agent/skills/mcp-builder/SKILL.md +143 -142
  130. package/.agent/skills/mobile-design/SKILL.md +272 -263
  131. package/.agent/skills/monorepo-management/SKILL.md +335 -334
  132. package/.agent/skills/motion-engineering/SKILL.md +266 -234
  133. package/.agent/skills/nextjs-react-expert/SKILL.md +236 -234
  134. package/.agent/skills/nodejs-best-practices/SKILL.md +547 -548
  135. package/.agent/skills/observability/SKILL.md +343 -343
  136. package/.agent/skills/parallel-agents/SKILL.md +143 -146
  137. package/.agent/skills/performance-profiling/SKILL.md +259 -267
  138. package/.agent/skills/plan-writing/SKILL.md +150 -142
  139. package/.agent/skills/platform-engineer/SKILL.md +148 -147
  140. package/.agent/skills/playwright-best-practices/SKILL.md +188 -187
  141. package/.agent/skills/powershell-windows/SKILL.md +162 -162
  142. package/.agent/skills/project-idioms/SKILL.md +137 -137
  143. package/.agent/skills/python-patterns/SKILL.md +260 -259
  144. package/.agent/skills/python-pro/SKILL.md +324 -323
  145. package/.agent/skills/react-specialist/SKILL.md +305 -277
  146. package/.agent/skills/readme-builder/SKILL.md +310 -300
  147. package/.agent/skills/realtime-patterns/SKILL.md +323 -319
  148. package/.agent/skills/red-team-tactics/SKILL.md +231 -218
  149. package/.agent/skills/rust-pro/SKILL.md +671 -673
  150. package/.agent/skills/seo-fundamentals/SKILL.md +179 -179
  151. package/.agent/skills/server-management/SKILL.md +218 -214
  152. package/.agent/skills/shadcn-ui-expert/SKILL.md +231 -231
  153. package/.agent/skills/skill-creator/SKILL.md +87 -86
  154. package/.agent/skills/sql-pro/SKILL.md +629 -629
  155. package/.agent/skills/supabase-postgres-best-practices/SKILL.md +97 -97
  156. package/.agent/skills/swiftui-expert/SKILL.md +204 -201
  157. package/.agent/skills/system-design-pro/SKILL.md +345 -0
  158. package/.agent/skills/systematic-debugging/SKILL.md +153 -142
  159. package/.agent/skills/tailwind-patterns/SKILL.md +610 -566
  160. package/.agent/skills/tdd-workflow/SKILL.md +169 -161
  161. package/.agent/skills/test-result-analyzer/SKILL.md +313 -309
  162. package/.agent/skills/testing-patterns/SKILL.md +566 -579
  163. package/.agent/skills/trend-researcher/SKILL.md +243 -237
  164. package/.agent/skills/typescript-advanced/SKILL.md +336 -335
  165. package/.agent/skills/ui-ux-pro-max/SKILL.md +590 -562
  166. package/.agent/skills/ui-ux-researcher/SKILL.md +244 -244
  167. package/.agent/skills/vue-expert/SKILL.md +294 -275
  168. package/.agent/skills/vulnerability-scanner/SKILL.md +416 -404
  169. package/.agent/skills/web-accessibility-auditor/SKILL.md +219 -218
  170. package/.agent/skills/web-design-guidelines/SKILL.md +192 -186
  171. package/.agent/skills/webapp-testing/SKILL.md +167 -169
  172. package/.agent/skills/webgpu-performance/SKILL.md +56 -2
  173. package/.agent/skills/whimsy-injector/SKILL.md +346 -325
  174. package/.agent/skills/workflow-optimizer/SKILL.md +231 -229
  175. package/.agent/workflows/acf.md +141 -0
  176. package/.agent/workflows/api-tester.md +176 -151
  177. package/.agent/workflows/audit.md +150 -127
  178. package/.agent/workflows/brainstorm.md +134 -110
  179. package/.agent/workflows/changelog.md +140 -112
  180. package/.agent/workflows/create.md +168 -124
  181. package/.agent/workflows/debug.md +190 -165
  182. package/.agent/workflows/deploy.md +201 -180
  183. package/.agent/workflows/enhance.md +154 -128
  184. package/.agent/workflows/fix.md +136 -114
  185. package/.agent/workflows/generate.md +198 -183
  186. package/.agent/workflows/marathon.md +37 -11
  187. package/.agent/workflows/migrate.md +184 -160
  188. package/.agent/workflows/orchestrate.md +192 -168
  189. package/.agent/workflows/performance-benchmarker.md +135 -114
  190. package/.agent/workflows/plan.md +196 -173
  191. package/.agent/workflows/preview.md +103 -80
  192. package/.agent/workflows/refactor.md +192 -161
  193. package/.agent/workflows/review-ai.md +125 -101
  194. package/.agent/workflows/review.md +141 -116
  195. package/.agent/workflows/session.md +122 -94
  196. package/.agent/workflows/status.md +101 -79
  197. package/.agent/workflows/strengthen-skills.md +164 -138
  198. package/.agent/workflows/super-prompt.md +24 -0
  199. package/.agent/workflows/swarm.md +193 -179
  200. package/.agent/workflows/test.md +211 -189
  201. package/.agent/workflows/tribunal-backend.md +136 -105
  202. package/.agent/workflows/tribunal-database.md +122 -95
  203. package/.agent/workflows/tribunal-frontend.md +221 -96
  204. package/.agent/workflows/tribunal-full.md +129 -100
  205. package/.agent/workflows/tribunal-mobile.md +122 -95
  206. package/.agent/workflows/tribunal-performance.md +136 -110
  207. package/.agent/workflows/tribunal-speed.md +209 -183
  208. package/.agent/workflows/ui-ux-pro-max.md +145 -122
  209. package/README.md +107 -55
  210. package/bin/mcp-server.js +159 -0
  211. package/bin/tribunal-kit.js +105 -29
  212. package/bin/wrapper.js +16 -7
  213. package/mcp_config.json +9 -0
  214. package/package.json +94 -86
  215. package/scripts/changelog.js +4 -3
  216. package/scripts/validate-payload.js +6 -1
  217. package/scripts/postinstall.js +0 -127
@@ -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.