@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,398 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
+ import * as fs from 'fs';
3
+ import * as path from 'path';
4
+ import * as os from 'os';
5
+ import { generateHTMLReport } from '../report-html';
6
+ import type { RunState, AgentResult, Finding, PhaseGateResult } from '../types';
7
+
8
+ /** Helper to build a minimal AgentResult. */
9
+ function makeAgentResult(
10
+ overrides: Partial<AgentResult> & { agentId: number; status: AgentResult['status'] },
11
+ ): AgentResult {
12
+ return {
13
+ agentName: `Agent ${overrides.agentId}`,
14
+ phase: 'alpha',
15
+ startedAt: '2026-01-01T00:00:00Z',
16
+ completedAt: '2026-01-01T00:01:00Z',
17
+ durationMs: 60_000,
18
+ findings: [],
19
+ evidence: [],
20
+ summary: 'Test result summary',
21
+ ...overrides,
22
+ };
23
+ }
24
+
25
+ /** Helper to build a minimal PhaseGateResult. */
26
+ function makeGate(overrides: Partial<PhaseGateResult> = {}): PhaseGateResult {
27
+ return {
28
+ phase: 'alpha',
29
+ passed: true,
30
+ blockingResults: [],
31
+ advisoryResults: [],
32
+ failedBlockingAgents: [],
33
+ timestamp: '2026-01-01T00:01:00Z',
34
+ ...overrides,
35
+ };
36
+ }
37
+
38
+ /** Helper to build a minimal Finding. */
39
+ function makeFinding(
40
+ overrides: Partial<Finding> & { id: string; severity: Finding['severity'] },
41
+ ): Finding {
42
+ return {
43
+ type: 'code-bug-logic',
44
+ agentId: 1,
45
+ module: 'test-module',
46
+ description: 'Test finding description',
47
+ ...overrides,
48
+ };
49
+ }
50
+
51
+ /** Helper to build a minimal RunState. */
52
+ function makeRunState(overrides: Partial<RunState> = {}): RunState {
53
+ return {
54
+ runId: 'run-html-test-001',
55
+ startedAt: '2026-01-01T00:00:00Z',
56
+ currentPhase: 'alpha',
57
+ phaseAttempt: 1,
58
+ agentResults: new Map(),
59
+ gateResults: [],
60
+ totalCost: 0,
61
+ ...overrides,
62
+ };
63
+ }
64
+
65
+ let tmpDir: string;
66
+
67
+ beforeEach(() => {
68
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'report-html-test-'));
69
+ });
70
+
71
+ afterEach(() => {
72
+ fs.rmSync(tmpDir, { recursive: true, force: true });
73
+ });
74
+
75
+ describe('generateHTMLReport', () => {
76
+ it('generates valid HTML structure', () => {
77
+ const state = makeRunState({ gateResults: [makeGate()] });
78
+ const outputPath = path.join(tmpDir, 'report.html');
79
+
80
+ generateHTMLReport(state, outputPath);
81
+
82
+ const html = fs.readFileSync(outputPath, 'utf-8');
83
+ expect(html).toContain('<!DOCTYPE html>');
84
+ expect(html).toContain('<html');
85
+ expect(html).toContain('<head>');
86
+ expect(html).toContain('<body>');
87
+ expect(html).toContain('</html>');
88
+ });
89
+
90
+ it('includes all agent results in output', () => {
91
+ const agent1 = makeAgentResult({ agentId: 1, agentName: 'Smoke Tester', status: 'passed', phase: 'alpha' });
92
+ const agent2 = makeAgentResult({ agentId: 5, agentName: 'API Validator', status: 'failed', phase: 'beta' });
93
+ const agent3 = makeAgentResult({ agentId: 12, agentName: 'Regression Runner', status: 'skipped', phase: 'uat' });
94
+
95
+ const agentMap = new Map<number, AgentResult>();
96
+ agentMap.set(1, agent1);
97
+ agentMap.set(5, agent2);
98
+ agentMap.set(12, agent3);
99
+
100
+ const state = makeRunState({
101
+ agentResults: agentMap,
102
+ gateResults: [makeGate()],
103
+ });
104
+ const outputPath = path.join(tmpDir, 'report.html');
105
+
106
+ generateHTMLReport(state, outputPath);
107
+
108
+ const html = fs.readFileSync(outputPath, 'utf-8');
109
+ expect(html).toContain('Smoke Tester');
110
+ expect(html).toContain('API Validator');
111
+ expect(html).toContain('Regression Runner');
112
+ expect(html).toContain('passed');
113
+ expect(html).toContain('failed');
114
+ expect(html).toContain('skipped');
115
+ });
116
+
117
+ it('includes findings grouped by severity', () => {
118
+ const criticalFinding = makeFinding({
119
+ id: 'f-crit-1',
120
+ severity: 'critical',
121
+ module: 'auth',
122
+ description: 'Critical auth bypass',
123
+ });
124
+ const highFinding = makeFinding({
125
+ id: 'f-high-1',
126
+ severity: 'high',
127
+ module: 'api',
128
+ description: 'Missing input validation',
129
+ });
130
+ const lowFinding = makeFinding({
131
+ id: 'f-low-1',
132
+ severity: 'low',
133
+ module: 'ui',
134
+ description: 'Minor styling issue',
135
+ });
136
+ const infoFinding = makeFinding({
137
+ id: 'f-info-1',
138
+ severity: 'info',
139
+ module: 'docs',
140
+ description: 'Documentation improvement',
141
+ });
142
+
143
+ const agent = makeAgentResult({
144
+ agentId: 1,
145
+ status: 'failed',
146
+ findings: [criticalFinding, highFinding, lowFinding, infoFinding],
147
+ });
148
+
149
+ const agentMap = new Map<number, AgentResult>();
150
+ agentMap.set(1, agent);
151
+
152
+ const state = makeRunState({
153
+ agentResults: agentMap,
154
+ gateResults: [makeGate({ passed: false, failedBlockingAgents: [1] })],
155
+ });
156
+ const outputPath = path.join(tmpDir, 'report.html');
157
+
158
+ generateHTMLReport(state, outputPath);
159
+
160
+ const html = fs.readFileSync(outputPath, 'utf-8');
161
+ // Each severity group should appear
162
+ expect(html).toContain('CRITICAL (1)');
163
+ expect(html).toContain('HIGH (1)');
164
+ expect(html).toContain('LOW (1)');
165
+ expect(html).toContain('INFO (1)');
166
+ // Finding descriptions should appear
167
+ expect(html).toContain('Critical auth bypass');
168
+ expect(html).toContain('Missing input validation');
169
+ expect(html).toContain('Minor styling issue');
170
+ expect(html).toContain('Documentation improvement');
171
+
172
+ // Verify that critical appears before low in the HTML
173
+ const critIndex = html.indexOf('CRITICAL (1)');
174
+ const highIndex = html.indexOf('HIGH (1)');
175
+ const lowIndex = html.indexOf('LOW (1)');
176
+ const infoIndex = html.indexOf('INFO (1)');
177
+ expect(critIndex).toBeLessThan(highIndex);
178
+ expect(highIndex).toBeLessThan(lowIndex);
179
+ expect(lowIndex).toBeLessThan(infoIndex);
180
+ });
181
+
182
+ it('shows phase gate visualization for all four phases', () => {
183
+ const state = makeRunState({
184
+ gateResults: [
185
+ makeGate({ phase: 'alpha', passed: true }),
186
+ makeGate({ phase: 'beta', passed: true }),
187
+ makeGate({ phase: 'uat', passed: false, failedBlockingAgents: [5] }),
188
+ makeGate({ phase: 'release', passed: true }),
189
+ ],
190
+ });
191
+ const outputPath = path.join(tmpDir, 'report.html');
192
+
193
+ generateHTMLReport(state, outputPath);
194
+
195
+ const html = fs.readFileSync(outputPath, 'utf-8');
196
+ expect(html).toContain('Phase Gate Summary');
197
+ expect(html).toContain('phase-node');
198
+ expect(html).toContain('alpha');
199
+ expect(html).toContain('beta');
200
+ expect(html).toContain('uat');
201
+ expect(html).toContain('release');
202
+ // Pipeline arrows should exist
203
+ expect(html).toContain('phase-arrow');
204
+ });
205
+
206
+ it('handles empty results gracefully', () => {
207
+ const state = makeRunState();
208
+ const outputPath = path.join(tmpDir, 'empty-report.html');
209
+
210
+ generateHTMLReport(state, outputPath);
211
+
212
+ const html = fs.readFileSync(outputPath, 'utf-8');
213
+ expect(html).toContain('<!DOCTYPE html>');
214
+ expect(html).toContain('No agent results recorded');
215
+ expect(html).toContain('No findings');
216
+ expect(html).toContain('No coverage data available');
217
+ });
218
+
219
+ it('creates the output file at the specified path', () => {
220
+ const state = makeRunState({ gateResults: [makeGate()] });
221
+ const nestedPath = path.join(tmpDir, 'sub', 'dir', 'report.html');
222
+
223
+ generateHTMLReport(state, nestedPath);
224
+
225
+ expect(fs.existsSync(nestedPath)).toBe(true);
226
+ const stat = fs.statSync(nestedPath);
227
+ expect(stat.size).toBeGreaterThan(0);
228
+ });
229
+
230
+ it('is self-contained with no external links', () => {
231
+ const critFinding = makeFinding({
232
+ id: 'f1',
233
+ severity: 'critical',
234
+ module: 'auth',
235
+ description: 'SQL injection vulnerability in login form',
236
+ });
237
+ const agent = makeAgentResult({
238
+ agentId: 1,
239
+ status: 'failed',
240
+ findings: [critFinding],
241
+ });
242
+
243
+ const agentMap = new Map<number, AgentResult>();
244
+ agentMap.set(1, agent);
245
+
246
+ const state = makeRunState({
247
+ agentResults: agentMap,
248
+ gateResults: [makeGate({ passed: false, failedBlockingAgents: [1] })],
249
+ });
250
+ const outputPath = path.join(tmpDir, 'report.html');
251
+
252
+ generateHTMLReport(state, outputPath);
253
+
254
+ const html = fs.readFileSync(outputPath, 'utf-8');
255
+
256
+ // Should not contain any external URLs except possibly in finding descriptions
257
+ // Strip finding descriptions before checking
258
+ const htmlWithoutFindings = html.replace(/SQL injection vulnerability in login form/g, '');
259
+ expect(htmlWithoutFindings).not.toMatch(/https?:\/\//);
260
+ });
261
+
262
+ it('displays correct verdict badge for PASS', () => {
263
+ const state = makeRunState({
264
+ gateResults: [makeGate({ passed: true })],
265
+ });
266
+ const outputPath = path.join(tmpDir, 'report.html');
267
+
268
+ generateHTMLReport(state, outputPath);
269
+
270
+ const html = fs.readFileSync(outputPath, 'utf-8');
271
+ expect(html).toContain('badge-pass');
272
+ expect(html).toContain('>PASS<');
273
+ });
274
+
275
+ it('displays correct verdict badge for FAIL', () => {
276
+ const state = makeRunState({
277
+ gateResults: [makeGate({ passed: false, failedBlockingAgents: [1] })],
278
+ });
279
+ const outputPath = path.join(tmpDir, 'report.html');
280
+
281
+ generateHTMLReport(state, outputPath);
282
+
283
+ const html = fs.readFileSync(outputPath, 'utf-8');
284
+ expect(html).toContain('badge-fail');
285
+ expect(html).toContain('>FAIL<');
286
+ });
287
+
288
+ it('displays correct verdict badge for CONDITIONAL', () => {
289
+ const advisoryResult = makeAgentResult({ agentId: 7, status: 'failed' });
290
+ const state = makeRunState({
291
+ gateResults: [
292
+ makeGate({ passed: true, advisoryResults: [advisoryResult] }),
293
+ ],
294
+ });
295
+ const outputPath = path.join(tmpDir, 'report.html');
296
+
297
+ generateHTMLReport(state, outputPath);
298
+
299
+ const html = fs.readFileSync(outputPath, 'utf-8');
300
+ expect(html).toContain('badge-warn');
301
+ expect(html).toContain('>CONDITIONAL<');
302
+ });
303
+
304
+ it('includes run metadata in header', () => {
305
+ const state = makeRunState({
306
+ runId: 'run-abc-789',
307
+ startedAt: '2026-03-27T10:30:00Z',
308
+ gateResults: [makeGate()],
309
+ });
310
+ const outputPath = path.join(tmpDir, 'report.html');
311
+
312
+ generateHTMLReport(state, outputPath);
313
+
314
+ const html = fs.readFileSync(outputPath, 'utf-8');
315
+ expect(html).toContain('run-abc-789');
316
+ expect(html).toContain('2026-03-27T10:30:00Z');
317
+ expect(html).toContain('Duration');
318
+ expect(html).toContain('Commit');
319
+ });
320
+
321
+ it('includes inline CSS and JS with no external dependencies', () => {
322
+ const state = makeRunState({ gateResults: [makeGate()] });
323
+ const outputPath = path.join(tmpDir, 'report.html');
324
+
325
+ generateHTMLReport(state, outputPath);
326
+
327
+ const html = fs.readFileSync(outputPath, 'utf-8');
328
+ expect(html).toContain('<style>');
329
+ expect(html).toContain('</style>');
330
+ expect(html).toContain('<script>');
331
+ expect(html).toContain('</script>');
332
+ // No link or script tags with src/href to external resources
333
+ expect(html).not.toMatch(/<link[^>]+href\s*=\s*["']http/);
334
+ expect(html).not.toMatch(/<script[^>]+src\s*=\s*["']http/);
335
+ });
336
+
337
+ it('renders coverage section when specialist agents are present', () => {
338
+ const coverageAgent = makeAgentResult({
339
+ agentId: 4,
340
+ agentName: 'Coverage Analyzer',
341
+ status: 'passed',
342
+ summary: '87.3% unit coverage',
343
+ });
344
+ const securityAgent = makeAgentResult({
345
+ agentId: 7,
346
+ agentName: 'Security Scanner',
347
+ status: 'passed',
348
+ summary: 'No vulnerabilities found',
349
+ });
350
+
351
+ const agentMap = new Map<number, AgentResult>();
352
+ agentMap.set(4, coverageAgent);
353
+ agentMap.set(7, securityAgent);
354
+
355
+ const state = makeRunState({
356
+ agentResults: agentMap,
357
+ gateResults: [makeGate()],
358
+ });
359
+ const outputPath = path.join(tmpDir, 'report.html');
360
+
361
+ generateHTMLReport(state, outputPath);
362
+
363
+ const html = fs.readFileSync(outputPath, 'utf-8');
364
+ expect(html).toContain('Coverage Summary');
365
+ expect(html).toContain('87.3% unit coverage');
366
+ expect(html).toContain('No vulnerabilities found');
367
+ });
368
+
369
+ it('escapes HTML special characters in finding descriptions', () => {
370
+ const finding = makeFinding({
371
+ id: 'f-xss-1',
372
+ severity: 'high',
373
+ module: 'input',
374
+ description: 'Unescaped <script>alert("xss")</script> in output',
375
+ });
376
+ const agent = makeAgentResult({
377
+ agentId: 1,
378
+ status: 'failed',
379
+ findings: [finding],
380
+ });
381
+
382
+ const agentMap = new Map<number, AgentResult>();
383
+ agentMap.set(1, agent);
384
+
385
+ const state = makeRunState({
386
+ agentResults: agentMap,
387
+ gateResults: [makeGate({ passed: false, failedBlockingAgents: [1] })],
388
+ });
389
+ const outputPath = path.join(tmpDir, 'report.html');
390
+
391
+ generateHTMLReport(state, outputPath);
392
+
393
+ const html = fs.readFileSync(outputPath, 'utf-8');
394
+ // The raw <script> should be escaped
395
+ expect(html).toContain('&lt;script&gt;');
396
+ expect(html).not.toContain('alert("xss")');
397
+ });
398
+ });