@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,374 @@
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)
46
+ // ---------------------------------------------------------------------------
47
+
48
+ const fetchMock = vi.fn();
49
+ vi.stubGlobal('fetch', fetchMock);
50
+
51
+ // ---------------------------------------------------------------------------
52
+ // Mock Playwright
53
+ // ---------------------------------------------------------------------------
54
+
55
+ interface MockPage {
56
+ goto: ReturnType<typeof vi.fn>;
57
+ fill: ReturnType<typeof vi.fn>;
58
+ click: ReturnType<typeof vi.fn>;
59
+ waitForNavigation: ReturnType<typeof vi.fn>;
60
+ reload: ReturnType<typeof vi.fn>;
61
+ goBack: ReturnType<typeof vi.fn>;
62
+ url: ReturnType<typeof vi.fn>;
63
+ content: ReturnType<typeof vi.fn>;
64
+ close: ReturnType<typeof vi.fn>;
65
+ }
66
+
67
+ function makeMockPage(urlAfterGoto = 'http://localhost:3000/login'): MockPage {
68
+ return {
69
+ goto: vi.fn().mockResolvedValue(undefined),
70
+ fill: vi.fn().mockResolvedValue(undefined),
71
+ click: vi.fn().mockResolvedValue(undefined),
72
+ waitForNavigation: vi.fn().mockResolvedValue(undefined),
73
+ reload: vi.fn().mockResolvedValue(undefined),
74
+ goBack: vi.fn().mockResolvedValue(undefined),
75
+ url: vi.fn().mockReturnValue(urlAfterGoto),
76
+ content: vi.fn().mockResolvedValue('<html>dashboard content</html>'),
77
+ close: vi.fn().mockResolvedValue(undefined),
78
+ };
79
+ }
80
+
81
+ let currentMockPage: MockPage;
82
+ let mockContext: { newPage: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
83
+ let mockBrowser: { newContext: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
84
+
85
+ vi.mock('playwright', async () => ({
86
+ chromium: {
87
+ launch: vi.fn().mockImplementation(async () => mockBrowser),
88
+ },
89
+ }));
90
+
91
+ // ---------------------------------------------------------------------------
92
+ // Import after mocks
93
+ // ---------------------------------------------------------------------------
94
+
95
+ const { StateSessionTesterAgent } = await import('../21-state-session-tester');
96
+
97
+ function createAgent(phase: Phase = 'beta'): InstanceType<typeof StateSessionTesterAgent> {
98
+ return new StateSessionTesterAgent(stubConfig, phase, '/tmp/test-run');
99
+ }
100
+
101
+ // ---------------------------------------------------------------------------
102
+ // Tests
103
+ // ---------------------------------------------------------------------------
104
+
105
+ describe('StateSessionTesterAgent', () => {
106
+ beforeEach(() => {
107
+ fetchMock.mockReset();
108
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
109
+
110
+ currentMockPage = makeMockPage('http://localhost:3000/login');
111
+ mockContext = {
112
+ newPage: vi.fn().mockResolvedValue(currentMockPage),
113
+ close: vi.fn().mockResolvedValue(undefined),
114
+ };
115
+ mockBrowser = {
116
+ newContext: vi.fn().mockResolvedValue(mockContext),
117
+ close: vi.fn().mockResolvedValue(undefined),
118
+ };
119
+ });
120
+
121
+ it('has agentId 21 and agentName "State & Session Tester"', () => {
122
+ const agent = createAgent();
123
+ expect(agent.agentId).toBe(21);
124
+ expect(agent.agentName).toBe('State & Session Tester');
125
+ });
126
+
127
+ // -------------------------------------------------------------------------
128
+ // preFlight()
129
+ // -------------------------------------------------------------------------
130
+
131
+ describe('preFlight()', () => {
132
+ it('resolves gracefully when base URL is not reachable (fallback)', async () => {
133
+ fetchMock.mockRejectedValue(new Error('ECONNREFUSED'));
134
+
135
+ const agent = createAgent();
136
+ const result = await agent.run();
137
+
138
+ // With resolveEnvironment the agent no longer throws on unreachable URLs.
139
+ expect(result.status).toBe('passed');
140
+ });
141
+
142
+ it('passes when base URL is reachable', async () => {
143
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
144
+
145
+ const agent = createAgent();
146
+ const result = await agent.run();
147
+
148
+ expect(result.findings.some((f) => f.description.includes('not reachable'))).toBe(false);
149
+ });
150
+ });
151
+
152
+ // -------------------------------------------------------------------------
153
+ // execute() — deep link scenario
154
+ // -------------------------------------------------------------------------
155
+
156
+ describe('deep link scenario', () => {
157
+ it('creates no finding when deep link redirects to login (correct behaviour)', async () => {
158
+ // url() returns login url — correct redirect
159
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
160
+
161
+ const agent = createAgent();
162
+ const result = await agent.run();
163
+
164
+ const deepLinkFinding = result.findings.find((f) => f.id.includes('deep-link'));
165
+ expect(deepLinkFinding).toBeUndefined();
166
+ });
167
+
168
+ it('creates a medium finding when deep link lands on unexpected page (not login, not module)', async () => {
169
+ // url() returns a non-login, non-module URL
170
+ currentMockPage.url.mockReturnValue('http://localhost:3000/unexpected-error');
171
+ currentMockPage.content.mockResolvedValue('<html>500 Error</html>');
172
+
173
+ const agent = createAgent();
174
+ const result = await agent.run();
175
+
176
+ const deepLinkFinding = result.findings.find((f) => f.id.includes('deep-link'));
177
+ expect(deepLinkFinding).toBeDefined();
178
+ expect(deepLinkFinding!.severity).toBe('medium');
179
+ });
180
+ });
181
+
182
+ // -------------------------------------------------------------------------
183
+ // execute() — refresh persistence scenario
184
+ // -------------------------------------------------------------------------
185
+
186
+ describe('refresh persistence scenario', () => {
187
+ it('creates a medium finding when page reload redirects to login', async () => {
188
+ // After reload, url() returns login url — session was lost
189
+ currentMockPage.url
190
+ .mockReturnValueOnce('http://localhost:3000/login') // deep link: login redirect (no finding)
191
+ .mockReturnValueOnce('http://localhost:3000/dashboard') // before reload
192
+ .mockReturnValueOnce('http://localhost:3000/login'); // after reload — session lost
193
+
194
+ const agent = createAgent();
195
+ const result = await agent.run();
196
+
197
+ const refreshFinding = result.findings.find((f) => f.id.includes('refresh'));
198
+ expect(refreshFinding).toBeDefined();
199
+ expect(refreshFinding!.severity).toBe('medium');
200
+ });
201
+
202
+ it('creates no finding when page reload stays on the same module', async () => {
203
+ // After reload, url() stays on the module — session persisted
204
+ currentMockPage.url
205
+ .mockReturnValueOnce('http://localhost:3000/login') // deep link: login redirect (no finding)
206
+ .mockReturnValue('http://localhost:3000/dashboard'); // before and after reload
207
+
208
+ const agent = createAgent();
209
+ const result = await agent.run();
210
+
211
+ const refreshFinding = result.findings.find((f) => f.id.includes('refresh'));
212
+ expect(refreshFinding).toBeUndefined();
213
+ });
214
+ });
215
+
216
+ // -------------------------------------------------------------------------
217
+ // execute() — back/forward navigation scenario
218
+ // -------------------------------------------------------------------------
219
+
220
+ describe('back/forward navigation scenario', () => {
221
+ it('creates no finding when goBack lands on the correct module', async () => {
222
+ // Sequence: login, dashboard (deep link no finding), dashboard (refresh persist), dashboard, invoices, clients, back, back → dashboard
223
+ currentMockPage.url
224
+ .mockReturnValueOnce('http://localhost:3000/login') // deep link scenario
225
+ .mockReturnValueOnce('http://localhost:3000/dashboard') // before reload (refresh scenario)
226
+ .mockReturnValue('http://localhost:3000/dashboard'); // back/forward result
227
+
228
+ const agent = createAgent();
229
+ const result = await agent.run();
230
+
231
+ const backFinding = result.findings.find((f) => f.id.includes('back-forward'));
232
+ expect(backFinding).toBeUndefined();
233
+ });
234
+
235
+ it('creates a low finding when goBack does not land on the expected module', async () => {
236
+ // deep link, refresh OK, then back/forward ends at wrong URL
237
+ currentMockPage.url
238
+ .mockReturnValueOnce('http://localhost:3000/login') // deep link
239
+ .mockReturnValueOnce('http://localhost:3000/dashboard') // before reload
240
+ .mockReturnValue('http://localhost:3000/settings'); // back/forward result (wrong)
241
+
242
+ const agent = createAgent();
243
+ const result = await agent.run();
244
+
245
+ const backFinding = result.findings.find((f) => f.id.includes('back-forward'));
246
+ expect(backFinding).toBeDefined();
247
+ expect(backFinding!.severity).toBe('low');
248
+ });
249
+ });
250
+
251
+ // -------------------------------------------------------------------------
252
+ // Missing credentials
253
+ // -------------------------------------------------------------------------
254
+
255
+ describe('no credentials', () => {
256
+ it('creates a critical finding when no credentials are configured', async () => {
257
+ const noCredConfig: ValidatedConfig = {
258
+ ...stubConfig,
259
+ auth: { loginUrl: 'http://localhost/login', credentials: {} },
260
+ };
261
+ const agent = new StateSessionTesterAgent(noCredConfig, 'beta', '/tmp/test-run');
262
+ const result = await agent.run();
263
+
264
+ const finding = result.findings.find(f => f.id.includes('no-credentials'));
265
+ expect(finding).toBeDefined();
266
+ expect(finding!.severity).toBe('critical');
267
+ });
268
+ });
269
+
270
+ // -------------------------------------------------------------------------
271
+ // No modules
272
+ // -------------------------------------------------------------------------
273
+
274
+ describe('no modules', () => {
275
+ it('creates a medium finding when no modules are configured', async () => {
276
+ const noModConfig: ValidatedConfig = {
277
+ ...stubConfig,
278
+ modules: [],
279
+ };
280
+ const agent = new StateSessionTesterAgent(noModConfig, 'beta', '/tmp/test-run');
281
+ const result = await agent.run();
282
+
283
+ const finding = result.findings.find(f => f.id.includes('no-modules'));
284
+ expect(finding).toBeDefined();
285
+ expect(finding!.severity).toBe('medium');
286
+ });
287
+ });
288
+
289
+ // -------------------------------------------------------------------------
290
+ // Deep link: error scenario
291
+ // -------------------------------------------------------------------------
292
+
293
+ describe('deep link error', () => {
294
+ it('creates an infra-issue finding when deep link navigation throws', async () => {
295
+ currentMockPage.goto.mockRejectedValue(new Error('ERR_CONNECTION_REFUSED'));
296
+
297
+ const agent = createAgent();
298
+ const result = await agent.run();
299
+
300
+ const errorFinding = result.findings.find(f => f.id.includes('deep-link-error'));
301
+ expect(errorFinding).toBeDefined();
302
+ expect(errorFinding!.severity).toBe('medium');
303
+ });
304
+ });
305
+
306
+ // -------------------------------------------------------------------------
307
+ // Refresh persistence error
308
+ // -------------------------------------------------------------------------
309
+
310
+ describe('refresh persistence error', () => {
311
+ it('creates an infra-issue finding when refresh scenario throws', async () => {
312
+ // Deep link succeeds (login redirect), but then second browser launch fails
313
+ let launchCount = 0;
314
+ mockBrowser.newContext.mockImplementation(async () => {
315
+ launchCount++;
316
+ if (launchCount === 2) throw new Error('Browser crashed');
317
+ return mockContext;
318
+ });
319
+
320
+ const agent = createAgent();
321
+ const result = await agent.run();
322
+
323
+ const errorFinding = result.findings.find(f => f.id.includes('refresh-error'));
324
+ expect(errorFinding).toBeDefined();
325
+ });
326
+ });
327
+
328
+ // -------------------------------------------------------------------------
329
+ // Back/forward with fewer than 3 modules — skips scenario
330
+ // -------------------------------------------------------------------------
331
+
332
+ describe('back/forward with fewer than 3 modules', () => {
333
+ it('skips back/forward scenario when fewer than 3 modules configured', async () => {
334
+ const twoModConfig: ValidatedConfig = {
335
+ ...stubConfig,
336
+ modules: [
337
+ { id: 'dashboard', route: '/dashboard', sidebarIcon: true },
338
+ { id: 'invoices', route: '/invoices', sidebarIcon: true },
339
+ ],
340
+ };
341
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
342
+
343
+ const agent = new StateSessionTesterAgent(twoModConfig, 'beta', '/tmp/test-run');
344
+ const result = await agent.run();
345
+
346
+ const backFinding = result.findings.find(f => f.id.includes('back-forward'));
347
+ expect(backFinding).toBeUndefined();
348
+ });
349
+ });
350
+
351
+ // -------------------------------------------------------------------------
352
+ // Back/forward error scenario
353
+ // -------------------------------------------------------------------------
354
+
355
+ describe('back/forward error', () => {
356
+ it('creates an infra-issue finding when back/forward scenario throws', async () => {
357
+ let launchCount = 0;
358
+ mockBrowser.newContext.mockImplementation(async () => {
359
+ launchCount++;
360
+ if (launchCount === 3) throw new Error('Out of memory');
361
+ return mockContext;
362
+ });
363
+
364
+ currentMockPage.url.mockReturnValue('http://localhost:3000/login');
365
+
366
+ const agent = createAgent();
367
+ const result = await agent.run();
368
+
369
+ const errorFinding = result.findings.find(f => f.id.includes('back-forward-error'));
370
+ expect(errorFinding).toBeDefined();
371
+ expect(errorFinding!.severity).toBe('low');
372
+ });
373
+ });
374
+ });