@avi770/testteam 1.2.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 (325) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +167 -0
  4. package/agents/01-analyst.ts +100 -0
  5. package/agents/02-seed-architect.ts +59 -0
  6. package/agents/03-test-generator.ts +191 -0
  7. package/agents/04-unit-runner.ts +160 -0
  8. package/agents/05-browser-crawler.ts +790 -0
  9. package/agents/06-api-exerciser.ts +311 -0
  10. package/agents/07-security-scout.ts +188 -0
  11. package/agents/08-a11y-guardian.ts +212 -0
  12. package/agents/09-healer.ts +228 -0
  13. package/agents/10-reporter.ts +266 -0
  14. package/agents/11-fixer.ts +253 -0
  15. package/agents/12-ux-inspector.ts +444 -0
  16. package/agents/13-performance-profiler.ts +271 -0
  17. package/agents/14-data-integrity-auditor.ts +417 -0
  18. package/agents/15-regression-sentinel.ts +307 -0
  19. package/agents/16-chaos-agent.ts +228 -0
  20. package/agents/17-documentation-validator.ts +266 -0
  21. package/agents/18-integration-watchdog.ts +178 -0
  22. package/agents/19-tenant-isolation-auditor.ts +199 -0
  23. package/agents/20-workflow-completion-tester.ts +203 -0
  24. package/agents/21-state-session-tester.ts +262 -0
  25. package/agents/22-email-notification-verifier.ts +244 -0
  26. package/agents/23-migration-tester.ts +80 -0
  27. package/agents/__tests__/01-analyst.test.ts +188 -0
  28. package/agents/__tests__/02-seed-architect.test.ts +152 -0
  29. package/agents/__tests__/03-test-generator-full.test.ts +321 -0
  30. package/agents/__tests__/03-test-generator.test.ts +318 -0
  31. package/agents/__tests__/04-unit-runner.test.ts +320 -0
  32. package/agents/__tests__/05-browser-crawler-beta.test.ts +492 -0
  33. package/agents/__tests__/05-browser-crawler-release.test.ts +412 -0
  34. package/agents/__tests__/05-browser-crawler-uat.test.ts +578 -0
  35. package/agents/__tests__/05-browser-crawler.test.ts +518 -0
  36. package/agents/__tests__/06-api-exerciser.test.ts +619 -0
  37. package/agents/__tests__/07-security-scout.test.ts +382 -0
  38. package/agents/__tests__/08-a11y-guardian.test.ts +530 -0
  39. package/agents/__tests__/09-healer.test.ts +384 -0
  40. package/agents/__tests__/10-reporter.test.ts +366 -0
  41. package/agents/__tests__/11-fixer.test.ts +406 -0
  42. package/agents/__tests__/12-ux-inspector-extended.test.ts +465 -0
  43. package/agents/__tests__/12-ux-inspector.test.ts +443 -0
  44. package/agents/__tests__/13-performance-profiler.test.ts +411 -0
  45. package/agents/__tests__/14-data-integrity-auditor-extended.test.ts +573 -0
  46. package/agents/__tests__/14-data-integrity-auditor.test.ts +407 -0
  47. package/agents/__tests__/15-regression-sentinel.test.ts +657 -0
  48. package/agents/__tests__/16-chaos-agent.test.ts +427 -0
  49. package/agents/__tests__/17-documentation-validator.test.ts +402 -0
  50. package/agents/__tests__/18-integration-watchdog.test.ts +263 -0
  51. package/agents/__tests__/19-tenant-isolation-auditor.test.ts +400 -0
  52. package/agents/__tests__/20-workflow-completion-tester.test.ts +586 -0
  53. package/agents/__tests__/21-state-session-tester.test.ts +374 -0
  54. package/agents/__tests__/22-email-notification-verifier.test.ts +441 -0
  55. package/agents/__tests__/23-migration-tester.test.ts +145 -0
  56. package/agents/__tests__/base-agent.test.ts +188 -0
  57. package/agents/__tests__/registry.test.ts +218 -0
  58. package/agents/base-agent.ts +77 -0
  59. package/agents/registry.ts +136 -0
  60. package/baselines/api-schemas/.gitkeep +0 -0
  61. package/baselines/performance/.gitkeep +0 -0
  62. package/baselines/screenshots/.gitkeep +0 -0
  63. package/bin/testteam.js +10 -0
  64. package/core/__tests__/ci-output.test.ts +430 -0
  65. package/core/__tests__/cli.test.ts +387 -0
  66. package/core/__tests__/config.test.ts +78 -0
  67. package/core/__tests__/cost-tracker.test.ts +158 -0
  68. package/core/__tests__/evidence.test.ts +265 -0
  69. package/core/__tests__/fix-loop.test.ts +210 -0
  70. package/core/__tests__/health-check.test.ts +44 -0
  71. package/core/__tests__/init.test.ts +609 -0
  72. package/core/__tests__/integration.test.ts +204 -0
  73. package/core/__tests__/license-gen.test.ts +227 -0
  74. package/core/__tests__/license.test.ts +326 -0
  75. package/core/__tests__/multi-browser.test.ts +278 -0
  76. package/core/__tests__/orchestrator.test.ts +519 -0
  77. package/core/__tests__/phase-gate.test.ts +43 -0
  78. package/core/__tests__/report-html.test.ts +398 -0
  79. package/core/__tests__/report-upload.test.ts +325 -0
  80. package/core/__tests__/run-counter.test.ts +234 -0
  81. package/core/ci-output.ts +240 -0
  82. package/core/cli.ts +232 -0
  83. package/core/config.ts +178 -0
  84. package/core/cost-tracker.ts +59 -0
  85. package/core/evidence.ts +132 -0
  86. package/core/fix-loop.ts +85 -0
  87. package/core/health-check.ts +54 -0
  88. package/core/init.ts +546 -0
  89. package/core/license-gen.ts +212 -0
  90. package/core/license.ts +211 -0
  91. package/core/messages.ts +67 -0
  92. package/core/multi-browser.ts +136 -0
  93. package/core/orchestrator.ts +354 -0
  94. package/core/phase-gate.ts +55 -0
  95. package/core/report-html.ts +657 -0
  96. package/core/report-upload.ts +188 -0
  97. package/core/run-counter.ts +175 -0
  98. package/core/types.ts +57 -0
  99. package/dist/agents/01-analyst.d.ts +11 -0
  100. package/dist/agents/01-analyst.d.ts.map +1 -0
  101. package/dist/agents/01-analyst.js +75 -0
  102. package/dist/agents/01-analyst.js.map +1 -0
  103. package/dist/agents/02-seed-architect.d.ts +11 -0
  104. package/dist/agents/02-seed-architect.d.ts.map +1 -0
  105. package/dist/agents/02-seed-architect.js +51 -0
  106. package/dist/agents/02-seed-architect.js.map +1 -0
  107. package/dist/agents/03-test-generator.d.ts +9 -0
  108. package/dist/agents/03-test-generator.d.ts.map +1 -0
  109. package/dist/agents/03-test-generator.js +167 -0
  110. package/dist/agents/03-test-generator.js.map +1 -0
  111. package/dist/agents/04-unit-runner.d.ts +9 -0
  112. package/dist/agents/04-unit-runner.d.ts.map +1 -0
  113. package/dist/agents/04-unit-runner.js +113 -0
  114. package/dist/agents/04-unit-runner.js.map +1 -0
  115. package/dist/agents/05-browser-crawler.d.ts +30 -0
  116. package/dist/agents/05-browser-crawler.d.ts.map +1 -0
  117. package/dist/agents/05-browser-crawler.js +685 -0
  118. package/dist/agents/05-browser-crawler.js.map +1 -0
  119. package/dist/agents/06-api-exerciser.d.ts +23 -0
  120. package/dist/agents/06-api-exerciser.d.ts.map +1 -0
  121. package/dist/agents/06-api-exerciser.js +253 -0
  122. package/dist/agents/06-api-exerciser.js.map +1 -0
  123. package/dist/agents/07-security-scout.d.ts +11 -0
  124. package/dist/agents/07-security-scout.d.ts.map +1 -0
  125. package/dist/agents/07-security-scout.js +142 -0
  126. package/dist/agents/07-security-scout.js.map +1 -0
  127. package/dist/agents/08-a11y-guardian.d.ts +13 -0
  128. package/dist/agents/08-a11y-guardian.d.ts.map +1 -0
  129. package/dist/agents/08-a11y-guardian.js +176 -0
  130. package/dist/agents/08-a11y-guardian.js.map +1 -0
  131. package/dist/agents/09-healer.d.ts +33 -0
  132. package/dist/agents/09-healer.d.ts.map +1 -0
  133. package/dist/agents/09-healer.js +167 -0
  134. package/dist/agents/09-healer.js.map +1 -0
  135. package/dist/agents/10-reporter.d.ts +26 -0
  136. package/dist/agents/10-reporter.d.ts.map +1 -0
  137. package/dist/agents/10-reporter.js +215 -0
  138. package/dist/agents/10-reporter.js.map +1 -0
  139. package/dist/agents/11-fixer.d.ts +26 -0
  140. package/dist/agents/11-fixer.d.ts.map +1 -0
  141. package/dist/agents/11-fixer.js +195 -0
  142. package/dist/agents/11-fixer.js.map +1 -0
  143. package/dist/agents/12-ux-inspector.d.ts +15 -0
  144. package/dist/agents/12-ux-inspector.d.ts.map +1 -0
  145. package/dist/agents/12-ux-inspector.js +364 -0
  146. package/dist/agents/12-ux-inspector.js.map +1 -0
  147. package/dist/agents/13-performance-profiler.d.ts +13 -0
  148. package/dist/agents/13-performance-profiler.d.ts.map +1 -0
  149. package/dist/agents/13-performance-profiler.js +216 -0
  150. package/dist/agents/13-performance-profiler.js.map +1 -0
  151. package/dist/agents/14-data-integrity-auditor.d.ts +12 -0
  152. package/dist/agents/14-data-integrity-auditor.d.ts.map +1 -0
  153. package/dist/agents/14-data-integrity-auditor.js +356 -0
  154. package/dist/agents/14-data-integrity-auditor.js.map +1 -0
  155. package/dist/agents/15-regression-sentinel.d.ts +25 -0
  156. package/dist/agents/15-regression-sentinel.d.ts.map +1 -0
  157. package/dist/agents/15-regression-sentinel.js +251 -0
  158. package/dist/agents/15-regression-sentinel.js.map +1 -0
  159. package/dist/agents/16-chaos-agent.d.ts +9 -0
  160. package/dist/agents/16-chaos-agent.d.ts.map +1 -0
  161. package/dist/agents/16-chaos-agent.js +207 -0
  162. package/dist/agents/16-chaos-agent.js.map +1 -0
  163. package/dist/agents/17-documentation-validator.d.ts +31 -0
  164. package/dist/agents/17-documentation-validator.d.ts.map +1 -0
  165. package/dist/agents/17-documentation-validator.js +246 -0
  166. package/dist/agents/17-documentation-validator.js.map +1 -0
  167. package/dist/agents/18-integration-watchdog.d.ts +10 -0
  168. package/dist/agents/18-integration-watchdog.d.ts.map +1 -0
  169. package/dist/agents/18-integration-watchdog.js +138 -0
  170. package/dist/agents/18-integration-watchdog.js.map +1 -0
  171. package/dist/agents/19-tenant-isolation-auditor.d.ts +9 -0
  172. package/dist/agents/19-tenant-isolation-auditor.d.ts.map +1 -0
  173. package/dist/agents/19-tenant-isolation-auditor.js +166 -0
  174. package/dist/agents/19-tenant-isolation-auditor.js.map +1 -0
  175. package/dist/agents/20-workflow-completion-tester.d.ts +12 -0
  176. package/dist/agents/20-workflow-completion-tester.d.ts.map +1 -0
  177. package/dist/agents/20-workflow-completion-tester.js +159 -0
  178. package/dist/agents/20-workflow-completion-tester.js.map +1 -0
  179. package/dist/agents/21-state-session-tester.d.ts +10 -0
  180. package/dist/agents/21-state-session-tester.d.ts.map +1 -0
  181. package/dist/agents/21-state-session-tester.js +233 -0
  182. package/dist/agents/21-state-session-tester.js.map +1 -0
  183. package/dist/agents/22-email-notification-verifier.d.ts +11 -0
  184. package/dist/agents/22-email-notification-verifier.d.ts.map +1 -0
  185. package/dist/agents/22-email-notification-verifier.js +199 -0
  186. package/dist/agents/22-email-notification-verifier.js.map +1 -0
  187. package/dist/agents/23-migration-tester.d.ts +10 -0
  188. package/dist/agents/23-migration-tester.d.ts.map +1 -0
  189. package/dist/agents/23-migration-tester.js +74 -0
  190. package/dist/agents/23-migration-tester.js.map +1 -0
  191. package/dist/agents/base-agent.d.ts +19 -0
  192. package/dist/agents/base-agent.d.ts.map +1 -0
  193. package/dist/agents/base-agent.js +67 -0
  194. package/dist/agents/base-agent.js.map +1 -0
  195. package/dist/agents/registry.d.ts +29 -0
  196. package/dist/agents/registry.d.ts.map +1 -0
  197. package/dist/agents/registry.js +117 -0
  198. package/dist/agents/registry.js.map +1 -0
  199. package/dist/core/ci-output.d.ts +35 -0
  200. package/dist/core/ci-output.d.ts.map +1 -0
  201. package/dist/core/ci-output.js +193 -0
  202. package/dist/core/ci-output.js.map +1 -0
  203. package/dist/core/cli.d.ts +11 -0
  204. package/dist/core/cli.d.ts.map +1 -0
  205. package/dist/core/cli.js +197 -0
  206. package/dist/core/cli.js.map +1 -0
  207. package/dist/core/config.d.ts +111 -0
  208. package/dist/core/config.d.ts.map +1 -0
  209. package/dist/core/config.js +42 -0
  210. package/dist/core/config.js.map +1 -0
  211. package/dist/core/cost-tracker.d.ts +22 -0
  212. package/dist/core/cost-tracker.d.ts.map +1 -0
  213. package/dist/core/cost-tracker.js +41 -0
  214. package/dist/core/cost-tracker.js.map +1 -0
  215. package/dist/core/evidence.d.ts +28 -0
  216. package/dist/core/evidence.d.ts.map +1 -0
  217. package/dist/core/evidence.js +95 -0
  218. package/dist/core/evidence.js.map +1 -0
  219. package/dist/core/fix-loop.d.ts +29 -0
  220. package/dist/core/fix-loop.d.ts.map +1 -0
  221. package/dist/core/fix-loop.js +70 -0
  222. package/dist/core/fix-loop.js.map +1 -0
  223. package/dist/core/health-check.d.ts +21 -0
  224. package/dist/core/health-check.d.ts.map +1 -0
  225. package/dist/core/health-check.js +26 -0
  226. package/dist/core/health-check.js.map +1 -0
  227. package/dist/core/init.d.ts +2 -0
  228. package/dist/core/init.d.ts.map +1 -0
  229. package/dist/core/init.js +435 -0
  230. package/dist/core/init.js.map +1 -0
  231. package/dist/core/license-gen.d.ts +12 -0
  232. package/dist/core/license-gen.d.ts.map +1 -0
  233. package/dist/core/license-gen.js +169 -0
  234. package/dist/core/license-gen.js.map +1 -0
  235. package/dist/core/license.d.ts +33 -0
  236. package/dist/core/license.d.ts.map +1 -0
  237. package/dist/core/license.js +170 -0
  238. package/dist/core/license.js.map +1 -0
  239. package/dist/core/messages.d.ts +10 -0
  240. package/dist/core/messages.d.ts.map +1 -0
  241. package/dist/core/messages.js +47 -0
  242. package/dist/core/messages.js.map +1 -0
  243. package/dist/core/multi-browser.d.ts +36 -0
  244. package/dist/core/multi-browser.d.ts.map +1 -0
  245. package/dist/core/multi-browser.js +88 -0
  246. package/dist/core/multi-browser.js.map +1 -0
  247. package/dist/core/orchestrator.d.ts +48 -0
  248. package/dist/core/orchestrator.d.ts.map +1 -0
  249. package/dist/core/orchestrator.js +291 -0
  250. package/dist/core/orchestrator.js.map +1 -0
  251. package/dist/core/phase-gate.d.ts +4 -0
  252. package/dist/core/phase-gate.d.ts.map +1 -0
  253. package/dist/core/phase-gate.js +39 -0
  254. package/dist/core/phase-gate.js.map +1 -0
  255. package/dist/core/report-html.d.ts +9 -0
  256. package/dist/core/report-html.d.ts.map +1 -0
  257. package/dist/core/report-html.js +617 -0
  258. package/dist/core/report-html.js.map +1 -0
  259. package/dist/core/report-upload.d.ts +16 -0
  260. package/dist/core/report-upload.d.ts.map +1 -0
  261. package/dist/core/report-upload.js +124 -0
  262. package/dist/core/report-upload.js.map +1 -0
  263. package/dist/core/run-counter.d.ts +40 -0
  264. package/dist/core/run-counter.d.ts.map +1 -0
  265. package/dist/core/run-counter.js +120 -0
  266. package/dist/core/run-counter.js.map +1 -0
  267. package/dist/core/types.d.ts +53 -0
  268. package/dist/core/types.d.ts.map +1 -0
  269. package/dist/core/types.js +2 -0
  270. package/dist/core/types.js.map +1 -0
  271. package/dist/helpers/api-client.d.ts +30 -0
  272. package/dist/helpers/api-client.d.ts.map +1 -0
  273. package/dist/helpers/api-client.js +77 -0
  274. package/dist/helpers/api-client.js.map +1 -0
  275. package/dist/helpers/element-discovery.d.ts +18 -0
  276. package/dist/helpers/element-discovery.d.ts.map +1 -0
  277. package/dist/helpers/element-discovery.js +82 -0
  278. package/dist/helpers/element-discovery.js.map +1 -0
  279. package/dist/helpers/env-resolver.d.ts +29 -0
  280. package/dist/helpers/env-resolver.d.ts.map +1 -0
  281. package/dist/helpers/env-resolver.js +51 -0
  282. package/dist/helpers/env-resolver.js.map +1 -0
  283. package/dist/helpers/form-filler.d.ts +13 -0
  284. package/dist/helpers/form-filler.d.ts.map +1 -0
  285. package/dist/helpers/form-filler.js +98 -0
  286. package/dist/helpers/form-filler.js.map +1 -0
  287. package/dist/helpers/modal-handler.d.ts +16 -0
  288. package/dist/helpers/modal-handler.d.ts.map +1 -0
  289. package/dist/helpers/modal-handler.js +95 -0
  290. package/dist/helpers/modal-handler.js.map +1 -0
  291. package/dist/helpers/navigation.d.ts +37 -0
  292. package/dist/helpers/navigation.d.ts.map +1 -0
  293. package/dist/helpers/navigation.js +83 -0
  294. package/dist/helpers/navigation.js.map +1 -0
  295. package/dist/helpers/quality-gate.d.ts +17 -0
  296. package/dist/helpers/quality-gate.d.ts.map +1 -0
  297. package/dist/helpers/quality-gate.js +144 -0
  298. package/dist/helpers/quality-gate.js.map +1 -0
  299. package/dist/helpers/screenshot.d.ts +24 -0
  300. package/dist/helpers/screenshot.d.ts.map +1 -0
  301. package/dist/helpers/screenshot.js +76 -0
  302. package/dist/helpers/screenshot.js.map +1 -0
  303. package/dist/helpers/seed-validator.d.ts +15 -0
  304. package/dist/helpers/seed-validator.d.ts.map +1 -0
  305. package/dist/helpers/seed-validator.js +53 -0
  306. package/dist/helpers/seed-validator.js.map +1 -0
  307. package/helpers/__tests__/api-client.test.ts +199 -0
  308. package/helpers/__tests__/element-discovery.test.ts +202 -0
  309. package/helpers/__tests__/form-filler-extended.test.ts +212 -0
  310. package/helpers/__tests__/form-filler.test.ts +99 -0
  311. package/helpers/__tests__/modal-handler.test.ts +152 -0
  312. package/helpers/__tests__/navigation.test.ts +214 -0
  313. package/helpers/__tests__/quality-gate.test.ts +117 -0
  314. package/helpers/__tests__/screenshot.test.ts +139 -0
  315. package/helpers/__tests__/seed-validator.test.ts +114 -0
  316. package/helpers/api-client.ts +111 -0
  317. package/helpers/element-discovery.ts +105 -0
  318. package/helpers/env-resolver.ts +69 -0
  319. package/helpers/form-filler.ts +126 -0
  320. package/helpers/modal-handler.ts +108 -0
  321. package/helpers/navigation.ts +100 -0
  322. package/helpers/quality-gate.ts +180 -0
  323. package/helpers/screenshot.ts +111 -0
  324. package/helpers/seed-validator.ts +70 -0
  325. package/package.json +88 -0
@@ -0,0 +1,427 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import type { ValidatedConfig } from '../../core/config';
3
+ import type { Phase } from '../../core/types';
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Stub config
7
+ // ---------------------------------------------------------------------------
8
+
9
+ const stubConfig: ValidatedConfig = {
10
+ schemaVersion: 1,
11
+ name: 'test',
12
+ auth: {
13
+ loginUrl: 'http://localhost/login',
14
+ credentials: { admin: { email: 'admin@test.com', password: 'secret' } },
15
+ },
16
+ modules: [
17
+ { id: 'dashboard', route: '/dashboard', sidebarIcon: true },
18
+ { id: 'invoices', route: '/invoices', sidebarIcon: true },
19
+ { id: 'clients', route: '/clients', sidebarIcon: true },
20
+ ],
21
+ environments: {
22
+ beta: { baseUrl: 'http://localhost:3000', seed: false },
23
+ },
24
+ portals: [],
25
+ breakpoints: [],
26
+ integrations: [],
27
+ workers: [],
28
+ workflows: [],
29
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
30
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
31
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
32
+ agentClassification: { blocking: [], advisory: [] },
33
+ thresholds: {
34
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
35
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
36
+ apiResponseTime: 2000,
37
+ bundleSizeLimit: 500000,
38
+ maxConsoleErrors: 0,
39
+ },
40
+ disabledAgents: [],
41
+ disabledAgentSet: new Set<number>(),
42
+ };
43
+
44
+ // ---------------------------------------------------------------------------
45
+ // Mock global fetch (preFlight URL reachability + large data scenarios)
46
+ // ---------------------------------------------------------------------------
47
+
48
+ const fetchMock = vi.fn();
49
+ vi.stubGlobal('fetch', fetchMock);
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Mock Playwright
53
+ // ---------------------------------------------------------------------------
54
+
55
+ type PageErrorListener = (err: Error) => void;
56
+
57
+ interface MockPage {
58
+ goto: ReturnType<typeof vi.fn>;
59
+ evaluate: ReturnType<typeof vi.fn>;
60
+ route: ReturnType<typeof vi.fn>;
61
+ url: ReturnType<typeof vi.fn>;
62
+ content: ReturnType<typeof vi.fn>;
63
+ close: ReturnType<typeof vi.fn>;
64
+ on: ReturnType<typeof vi.fn>;
65
+ _pageErrorListeners: PageErrorListener[];
66
+ emitPageError(err: Error): void;
67
+ }
68
+
69
+ function makeMockPage(overrides: Partial<MockPage> = {}): MockPage {
70
+ const pageErrorListeners: PageErrorListener[] = [];
71
+ const page: MockPage = {
72
+ goto: vi.fn().mockResolvedValue(undefined),
73
+ evaluate: vi.fn().mockResolvedValue(undefined),
74
+ route: vi.fn().mockResolvedValue(undefined),
75
+ url: vi.fn().mockReturnValue('http://localhost:3000/login'),
76
+ content: vi.fn().mockResolvedValue('<html>login</html>'),
77
+ close: vi.fn().mockResolvedValue(undefined),
78
+ on: vi.fn().mockImplementation((event: string, listener: PageErrorListener) => {
79
+ if (event === 'pageerror') pageErrorListeners.push(listener);
80
+ }),
81
+ _pageErrorListeners: pageErrorListeners,
82
+ emitPageError(err: Error) {
83
+ for (const l of pageErrorListeners) l(err);
84
+ },
85
+ ...overrides,
86
+ };
87
+ return page;
88
+ }
89
+
90
+ let currentMockPage: MockPage;
91
+ let mockContext: { newPage: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
92
+ let mockBrowser: { newContext: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
93
+
94
+ vi.mock('playwright', async () => ({
95
+ chromium: {
96
+ launch: vi.fn().mockImplementation(async () => mockBrowser),
97
+ },
98
+ }));
99
+
100
+ // ---------------------------------------------------------------------------
101
+ // Import after mocks
102
+ // ---------------------------------------------------------------------------
103
+
104
+ const { ChaosAgent } = await import('../16-chaos-agent');
105
+
106
+ function createAgent(phase: Phase = 'beta'): InstanceType<typeof ChaosAgent> {
107
+ return new ChaosAgent(stubConfig, phase, '/tmp/test-run');
108
+ }
109
+
110
+ // ---------------------------------------------------------------------------
111
+ // Tests
112
+ // ---------------------------------------------------------------------------
113
+
114
+ describe('ChaosAgent', () => {
115
+ beforeEach(() => {
116
+ fetchMock.mockReset();
117
+
118
+ currentMockPage = makeMockPage();
119
+ mockContext = {
120
+ newPage: vi.fn().mockResolvedValue(currentMockPage),
121
+ close: vi.fn().mockResolvedValue(undefined),
122
+ };
123
+ mockBrowser = {
124
+ newContext: vi.fn().mockResolvedValue(mockContext),
125
+ close: vi.fn().mockResolvedValue(undefined),
126
+ };
127
+
128
+ // Default: base URL reachable
129
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
130
+ });
131
+
132
+ it('has agentId 16 and agentName "Chaos Agent"', () => {
133
+ const agent = createAgent();
134
+ expect(agent.agentId).toBe(16);
135
+ expect(agent.agentName).toBe('Chaos Agent');
136
+ });
137
+
138
+ // -------------------------------------------------------------------------
139
+ // preFlight()
140
+ // -------------------------------------------------------------------------
141
+
142
+ describe('preFlight()', () => {
143
+ it('throws when phase is not "beta" or "uat"', async () => {
144
+ const agent = createAgent('alpha' as Phase);
145
+ const result = await agent.run();
146
+
147
+ expect(result.status).toBe('failed');
148
+ expect(result.findings[0].description).toContain('beta');
149
+ });
150
+
151
+ it('does not throw when base URL is not reachable (resolveEnvironment handles fallback)', async () => {
152
+ fetchMock.mockRejectedValue(new Error('ECONNREFUSED'));
153
+
154
+ const agent = createAgent('beta');
155
+ const result = await agent.run();
156
+
157
+ // resolveEnvironment falls back gracefully — preFlight no longer throws
158
+ const preflightError = result.findings.find(
159
+ (f) => f.description.includes('not reachable'),
160
+ );
161
+ expect(preflightError).toBeUndefined();
162
+ });
163
+
164
+ it('passes for phase "beta" when base URL is reachable', async () => {
165
+ const agent = createAgent('beta');
166
+ const result = await agent.run();
167
+
168
+ expect(result.findings.some((f) => f.description.includes('requires phase'))).toBe(false);
169
+ });
170
+
171
+ it('passes for phase "uat" when base URL is reachable', async () => {
172
+ const uatConfig: ValidatedConfig = {
173
+ ...stubConfig,
174
+ environments: {
175
+ uat: { baseUrl: 'http://localhost:3000', seed: false },
176
+ },
177
+ };
178
+ const agent = new ChaosAgent(uatConfig, 'uat', '/tmp/test-run');
179
+ const result = await agent.run();
180
+
181
+ expect(result.findings.some((f) => f.description.includes('requires phase'))).toBe(false);
182
+ });
183
+ });
184
+
185
+ // -------------------------------------------------------------------------
186
+ // execute() — token expiry scenario
187
+ // -------------------------------------------------------------------------
188
+
189
+ describe('token expiry scenario', () => {
190
+ it('creates a critical security finding when app does not redirect to login after expired token', async () => {
191
+ // url() returns a non-login URL after goto with expired JWT
192
+ currentMockPage.url.mockReturnValue('http://localhost:3000/dashboard');
193
+
194
+ const agent = createAgent('beta');
195
+ const result = await agent.run();
196
+
197
+ const tokenFinding = result.findings.find((f) => f.id.includes('token-expiry'));
198
+ expect(tokenFinding).toBeDefined();
199
+ expect(tokenFinding!.severity).toBe('critical');
200
+ expect(tokenFinding!.type).toBe('code-bug-security');
201
+ });
202
+
203
+ it('creates no finding when app redirects to login after expired token', async () => {
204
+ // url() returns a login URL — correct behaviour
205
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
206
+
207
+ const agent = createAgent('beta');
208
+ const result = await agent.run();
209
+
210
+ const tokenFinding = result.findings.find((f) => f.id.includes('token-expiry'));
211
+ expect(tokenFinding).toBeUndefined();
212
+ });
213
+ });
214
+
215
+ // -------------------------------------------------------------------------
216
+ // execute() — network throttle scenario
217
+ // -------------------------------------------------------------------------
218
+
219
+ describe('network throttle scenario', () => {
220
+ it('creates a high finding when uncaught JS errors occur during network abort', async () => {
221
+ // Simulate the page emitting a pageerror after route.abort
222
+ currentMockPage.goto.mockImplementation(async () => {
223
+ currentMockPage.emitPageError(new Error('TypeError: Failed to fetch'));
224
+ return undefined;
225
+ });
226
+ // Make first goto (token scenario) return login url to avoid token-expiry finding
227
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
228
+
229
+ const agent = createAgent('beta');
230
+ const result = await agent.run();
231
+
232
+ const throttleFinding = result.findings.find((f) => f.id.includes('network-throttle'));
233
+ expect(throttleFinding).toBeDefined();
234
+ expect(throttleFinding!.severity).toBe('high');
235
+ });
236
+
237
+ it('creates no finding when app handles network abort gracefully', async () => {
238
+ // No pageerror events emitted
239
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
240
+
241
+ const agent = createAgent('beta');
242
+ const result = await agent.run();
243
+
244
+ const throttleFinding = result.findings.find((f) => f.id.includes('network-throttle'));
245
+ expect(throttleFinding).toBeUndefined();
246
+ });
247
+ });
248
+
249
+ // -------------------------------------------------------------------------
250
+ // execute() — large data scenario
251
+ // -------------------------------------------------------------------------
252
+
253
+ describe('large data scenario', () => {
254
+ it('creates a high finding when a list endpoint returns 500 for large requests', async () => {
255
+ // First call: preFlight HEAD (ok), subsequent calls: large-data endpoint returns 500
256
+ fetchMock
257
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // preFlight
258
+ .mockResolvedValue({ ok: false, status: 500 }); // list endpoints
259
+
260
+ // token scenario: url = /login (no finding)
261
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
262
+
263
+ const agent = createAgent('beta');
264
+ const result = await agent.run();
265
+
266
+ const largeDataFinding = result.findings.find((f) => f.id.includes('large-data'));
267
+ expect(largeDataFinding).toBeDefined();
268
+ expect(largeDataFinding!.severity).toBe('high');
269
+ });
270
+
271
+ it('creates no finding when list endpoints respond normally to large requests', async () => {
272
+ // All fetches succeed
273
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
274
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
275
+
276
+ const agent = createAgent('beta');
277
+ const result = await agent.run();
278
+
279
+ const largeDataFindings = result.findings.filter((f) => f.id.includes('large-data'));
280
+ expect(largeDataFindings).toHaveLength(0);
281
+ });
282
+ });
283
+
284
+ // -------------------------------------------------------------------------
285
+ // UAT mode — only non-destructive scenarios
286
+ // -------------------------------------------------------------------------
287
+
288
+ describe('UAT mode', () => {
289
+ it('skips large data scenario in UAT mode', async () => {
290
+ const uatConfig: ValidatedConfig = {
291
+ ...stubConfig,
292
+ environments: {
293
+ uat: { baseUrl: 'http://localhost:3000', seed: false },
294
+ },
295
+ };
296
+
297
+ // First call: preFlight HEAD (ok), subsequent calls: large-data endpoint returns 500
298
+ fetchMock
299
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // preFlight
300
+ .mockResolvedValue({ ok: false, status: 500 }); // list endpoints
301
+
302
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
303
+
304
+ const agent = new ChaosAgent(uatConfig, 'uat', '/tmp/test-run');
305
+ const result = await agent.run();
306
+
307
+ const largeDataFindings = result.findings.filter((f) => f.id.includes('large-data'));
308
+ expect(largeDataFindings).toHaveLength(0);
309
+ });
310
+ });
311
+
312
+ // -------------------------------------------------------------------------
313
+ // No environment configured
314
+ // -------------------------------------------------------------------------
315
+
316
+ describe('no environment', () => {
317
+ it('fails or produces finding when no environment is configured', async () => {
318
+ const noEnvConfig: ValidatedConfig = {
319
+ ...stubConfig,
320
+ environments: {},
321
+ };
322
+ fetchMock.mockRejectedValue(new Error('no env'));
323
+ const agent = new ChaosAgent(noEnvConfig, 'beta', '/tmp/test-run');
324
+ const result = await agent.run();
325
+
326
+ // Either preFlight throws (failed status) or execute finds no env
327
+ expect(
328
+ result.status === 'failed' ||
329
+ result.findings.some(f => f.severity === 'critical' || f.description.includes('No environment')),
330
+ ).toBe(true);
331
+ });
332
+ });
333
+
334
+ // -------------------------------------------------------------------------
335
+ // Token expiry scenario: outer try/catch (chromium.launch fails)
336
+ // -------------------------------------------------------------------------
337
+
338
+ describe('token expiry scenario — browser failure', () => {
339
+ it('creates a skipped finding when token scenario browser launch fails', async () => {
340
+ mockBrowser.newContext.mockRejectedValue(new Error('Browser not available'));
341
+
342
+ const agent = createAgent('beta');
343
+ const result = await agent.run();
344
+
345
+ const skippedFinding = result.findings.find(f => f.id.includes('token-expiry-skipped'));
346
+ expect(skippedFinding).toBeDefined();
347
+ expect(skippedFinding!.severity).toBe('medium');
348
+ });
349
+ });
350
+
351
+ // -------------------------------------------------------------------------
352
+ // Large data: 502 and 503 status codes
353
+ // -------------------------------------------------------------------------
354
+
355
+ describe('large data scenario — 502/503', () => {
356
+ it('creates a finding for 502 status on large data request', async () => {
357
+ fetchMock
358
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // preFlight
359
+ .mockResolvedValue({ ok: false, status: 502 });
360
+
361
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
362
+
363
+ const agent = createAgent('beta');
364
+ const result = await agent.run();
365
+
366
+ const largeFinding = result.findings.find(f => f.id.includes('large-data'));
367
+ expect(largeFinding).toBeDefined();
368
+ expect(largeFinding!.severity).toBe('high');
369
+ });
370
+
371
+ it('creates a finding for 503 status on large data request', async () => {
372
+ fetchMock
373
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // preFlight
374
+ .mockResolvedValue({ ok: false, status: 503 });
375
+
376
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
377
+
378
+ const agent = createAgent('beta');
379
+ const result = await agent.run();
380
+
381
+ const largeFinding = result.findings.find(f => f.id.includes('large-data'));
382
+ expect(largeFinding).toBeDefined();
383
+ });
384
+ });
385
+
386
+ // -------------------------------------------------------------------------
387
+ // Large data: fetch throws (endpoint does not exist)
388
+ // -------------------------------------------------------------------------
389
+
390
+ describe('large data scenario — fetch throws', () => {
391
+ it('skips endpoints where fetch throws (no finding)', async () => {
392
+ fetchMock
393
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // preFlight
394
+ .mockRejectedValue(new Error('ECONNREFUSED'));
395
+
396
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
397
+
398
+ const agent = createAgent('beta');
399
+ const result = await agent.run();
400
+
401
+ const largeFindings = result.findings.filter(f =>
402
+ f.id.includes('large-data') && !f.id.includes('error'),
403
+ );
404
+ expect(largeFindings).toHaveLength(0);
405
+ });
406
+ });
407
+
408
+ // -------------------------------------------------------------------------
409
+ // Large data: outer catch (unexpected error)
410
+ // -------------------------------------------------------------------------
411
+
412
+ describe('large data scenario — 200 OK responses', () => {
413
+ it('creates no finding when endpoints return 200 OK for large requests', async () => {
414
+ fetchMock
415
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // preFlight
416
+ .mockResolvedValue({ ok: true, status: 200 }); // all endpoints succeed
417
+
418
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
419
+
420
+ const agent = createAgent('beta');
421
+ const result = await agent.run();
422
+
423
+ const largeFindings = result.findings.filter(f => f.id.includes('large-data'));
424
+ expect(largeFindings).toHaveLength(0);
425
+ });
426
+ });
427
+ });