@avi770/testteam 1.2.0 → 2.0.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 (300) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/README.md +53 -6
  3. package/agents/24-signup-onboarding-tester.ts +429 -0
  4. package/agents/25-crud-flow-tester.ts +302 -0
  5. package/agents/26-form-validator.ts +297 -0
  6. package/agents/27-search-filter-tester.ts +326 -0
  7. package/agents/28-navigation-routing-tester.ts +425 -0
  8. package/agents/29-responsive-interaction-tester.ts +350 -0
  9. package/agents/30-multi-user-scenario-tester.ts +319 -0
  10. package/agents/31-load-tester.ts +134 -0
  11. package/agents/32-memory-leak-detector.ts +194 -0
  12. package/agents/33-bundle-analyzer.ts +132 -0
  13. package/agents/34-xss-scanner.ts +191 -0
  14. package/agents/35-csrf-tester.ts +82 -0
  15. package/agents/36-auth-fuzzer.ts +194 -0
  16. package/agents/37-dependency-scanner.ts +176 -0
  17. package/agents/38-secrets-scanner.ts +137 -0
  18. package/agents/39-api-contract-tester.ts +199 -0
  19. package/agents/40-rate-limit-tester.ts +94 -0
  20. package/agents/41-api-pagination-tester.ts +97 -0
  21. package/agents/42-graphql-tester.ts +222 -0
  22. package/agents/43-data-consistency-checker.ts +205 -0
  23. package/agents/44-backup-recovery-tester.ts +152 -0
  24. package/agents/45-data-privacy-scanner.ts +125 -0
  25. package/agents/46-seo-auditor.ts +294 -0
  26. package/agents/47-social-preview-tester.ts +232 -0
  27. package/agents/48-lighthouse-auditor.ts +213 -0
  28. package/agents/49-i18n-tester.ts +198 -0
  29. package/agents/50-timezone-tester.ts +173 -0
  30. package/agents/51-error-recovery-tester.ts +155 -0
  31. package/agents/52-offline-mode-tester.ts +180 -0
  32. package/agents/53-graceful-degradation-tester.ts +156 -0
  33. package/agents/54-websocket-tester.ts +151 -0
  34. package/agents/55-realtime-sync-tester.ts +194 -0
  35. package/agents/56-file-upload-tester.ts +194 -0
  36. package/agents/57-export-tester.ts +174 -0
  37. package/agents/58-payment-flow-tester.ts +183 -0
  38. package/agents/59-ssl-tls-auditor.ts +141 -0
  39. package/agents/60-dns-cdn-tester.ts +117 -0
  40. package/agents/61-docker-health-checker.ts +111 -0
  41. package/agents/62-env-config-validator.ts +152 -0
  42. package/agents/63-log-quality-auditor.ts +136 -0
  43. package/agents/64-analytics-tracker-tester.ts +165 -0
  44. package/agents/65-gdpr-compliance-tester.ts +215 -0
  45. package/agents/66-soc2-control-validator.ts +210 -0
  46. package/agents/67-wcag-aaa-tester.ts +241 -0
  47. package/agents/68-dead-code-detector.ts +135 -0
  48. package/agents/69-type-safety-auditor.ts +164 -0
  49. package/agents/70-complexity-analyzer.ts +179 -0
  50. package/agents/__tests__/24-signup-onboarding-tester.test.ts +274 -0
  51. package/agents/__tests__/25-crud-flow-tester.test.ts +322 -0
  52. package/agents/__tests__/26-form-validator.test.ts +345 -0
  53. package/agents/__tests__/27-search-filter-tester.test.ts +311 -0
  54. package/agents/__tests__/28-navigation-routing-tester.test.ts +328 -0
  55. package/agents/__tests__/29-responsive-interaction-tester.test.ts +297 -0
  56. package/agents/__tests__/30-multi-user-scenario-tester.test.ts +328 -0
  57. package/agents/__tests__/31-load-tester.test.ts +189 -0
  58. package/agents/__tests__/32-memory-leak-detector.test.ts +251 -0
  59. package/agents/__tests__/33-bundle-analyzer.test.ts +237 -0
  60. package/agents/__tests__/34-xss-scanner.test.ts +258 -0
  61. package/agents/__tests__/35-csrf-tester.test.ts +200 -0
  62. package/agents/__tests__/36-auth-fuzzer.test.ts +214 -0
  63. package/agents/__tests__/37-dependency-scanner.test.ts +266 -0
  64. package/agents/__tests__/38-secrets-scanner.test.ts +224 -0
  65. package/agents/__tests__/39-api-contract-tester.test.ts +312 -0
  66. package/agents/__tests__/40-rate-limit-tester.test.ts +192 -0
  67. package/agents/__tests__/41-api-pagination-tester.test.ts +198 -0
  68. package/agents/__tests__/42-graphql-tester.test.ts +252 -0
  69. package/agents/__tests__/43-data-consistency-checker.test.ts +232 -0
  70. package/agents/__tests__/44-backup-recovery-tester.test.ts +222 -0
  71. package/agents/__tests__/45-data-privacy-scanner.test.ts +223 -0
  72. package/agents/__tests__/46-seo-auditor.test.ts +261 -0
  73. package/agents/__tests__/47-social-preview-tester.test.ts +245 -0
  74. package/agents/__tests__/48-lighthouse-auditor.test.ts +276 -0
  75. package/agents/__tests__/49-i18n-tester.test.ts +201 -0
  76. package/agents/__tests__/50-timezone-tester.test.ts +172 -0
  77. package/agents/__tests__/51-error-recovery-tester.test.ts +162 -0
  78. package/agents/__tests__/52-offline-mode-tester.test.ts +164 -0
  79. package/agents/__tests__/53-graceful-degradation-tester.test.ts +168 -0
  80. package/agents/__tests__/54-websocket-tester.test.ts +157 -0
  81. package/agents/__tests__/55-realtime-sync-tester.test.ts +181 -0
  82. package/agents/__tests__/56-file-upload-tester.test.ts +172 -0
  83. package/agents/__tests__/57-export-tester.test.ts +169 -0
  84. package/agents/__tests__/58-payment-flow-tester.test.ts +182 -0
  85. package/agents/__tests__/59-ssl-tls-auditor.test.ts +179 -0
  86. package/agents/__tests__/60-dns-cdn-tester.test.ts +176 -0
  87. package/agents/__tests__/61-docker-health-checker.test.ts +150 -0
  88. package/agents/__tests__/62-env-config-validator.test.ts +166 -0
  89. package/agents/__tests__/63-log-quality-auditor.test.ts +175 -0
  90. package/agents/__tests__/64-analytics-tracker-tester.test.ts +158 -0
  91. package/agents/__tests__/65-gdpr-compliance-tester.test.ts +174 -0
  92. package/agents/__tests__/66-soc2-control-validator.test.ts +183 -0
  93. package/agents/__tests__/67-wcag-aaa-tester.test.ts +190 -0
  94. package/agents/__tests__/68-dead-code-detector.test.ts +174 -0
  95. package/agents/__tests__/69-type-safety-auditor.test.ts +173 -0
  96. package/agents/__tests__/70-complexity-analyzer.test.ts +177 -0
  97. package/agents/__tests__/registry.test.ts +13 -13
  98. package/agents/registry.ts +146 -5
  99. package/core/__tests__/integration.test.ts +4 -4
  100. package/core/__tests__/orchestrator.test.ts +17 -16
  101. package/core/license.ts +208 -211
  102. package/core/orchestrator.ts +6 -4
  103. package/dist/agents/24-signup-onboarding-tester.d.ts +35 -0
  104. package/dist/agents/24-signup-onboarding-tester.d.ts.map +1 -0
  105. package/dist/agents/24-signup-onboarding-tester.js +357 -0
  106. package/dist/agents/24-signup-onboarding-tester.js.map +1 -0
  107. package/dist/agents/25-crud-flow-tester.d.ts +11 -0
  108. package/dist/agents/25-crud-flow-tester.d.ts.map +1 -0
  109. package/dist/agents/25-crud-flow-tester.js +253 -0
  110. package/dist/agents/25-crud-flow-tester.js.map +1 -0
  111. package/dist/agents/26-form-validator.d.ts +12 -0
  112. package/dist/agents/26-form-validator.d.ts.map +1 -0
  113. package/dist/agents/26-form-validator.js +257 -0
  114. package/dist/agents/26-form-validator.js.map +1 -0
  115. package/dist/agents/27-search-filter-tester.d.ts +20 -0
  116. package/dist/agents/27-search-filter-tester.d.ts.map +1 -0
  117. package/dist/agents/27-search-filter-tester.js +267 -0
  118. package/dist/agents/27-search-filter-tester.js.map +1 -0
  119. package/dist/agents/28-navigation-routing-tester.d.ts +32 -0
  120. package/dist/agents/28-navigation-routing-tester.d.ts.map +1 -0
  121. package/dist/agents/28-navigation-routing-tester.js +363 -0
  122. package/dist/agents/28-navigation-routing-tester.js.map +1 -0
  123. package/dist/agents/29-responsive-interaction-tester.d.ts +26 -0
  124. package/dist/agents/29-responsive-interaction-tester.d.ts.map +1 -0
  125. package/dist/agents/29-responsive-interaction-tester.js +272 -0
  126. package/dist/agents/29-responsive-interaction-tester.js.map +1 -0
  127. package/dist/agents/30-multi-user-scenario-tester.d.ts +24 -0
  128. package/dist/agents/30-multi-user-scenario-tester.d.ts.map +1 -0
  129. package/dist/agents/30-multi-user-scenario-tester.js +254 -0
  130. package/dist/agents/30-multi-user-scenario-tester.js.map +1 -0
  131. package/dist/agents/31-load-tester.d.ts +12 -0
  132. package/dist/agents/31-load-tester.d.ts.map +1 -0
  133. package/dist/agents/31-load-tester.js +110 -0
  134. package/dist/agents/31-load-tester.js.map +1 -0
  135. package/dist/agents/32-memory-leak-detector.d.ts +12 -0
  136. package/dist/agents/32-memory-leak-detector.d.ts.map +1 -0
  137. package/dist/agents/32-memory-leak-detector.js +167 -0
  138. package/dist/agents/32-memory-leak-detector.js.map +1 -0
  139. package/dist/agents/33-bundle-analyzer.d.ts +10 -0
  140. package/dist/agents/33-bundle-analyzer.d.ts.map +1 -0
  141. package/dist/agents/33-bundle-analyzer.js +111 -0
  142. package/dist/agents/33-bundle-analyzer.js.map +1 -0
  143. package/dist/agents/34-xss-scanner.d.ts +11 -0
  144. package/dist/agents/34-xss-scanner.d.ts.map +1 -0
  145. package/dist/agents/34-xss-scanner.js +164 -0
  146. package/dist/agents/34-xss-scanner.js.map +1 -0
  147. package/dist/agents/35-csrf-tester.d.ts +11 -0
  148. package/dist/agents/35-csrf-tester.d.ts.map +1 -0
  149. package/dist/agents/35-csrf-tester.js +70 -0
  150. package/dist/agents/35-csrf-tester.js.map +1 -0
  151. package/dist/agents/36-auth-fuzzer.d.ts +13 -0
  152. package/dist/agents/36-auth-fuzzer.d.ts.map +1 -0
  153. package/dist/agents/36-auth-fuzzer.js +163 -0
  154. package/dist/agents/36-auth-fuzzer.js.map +1 -0
  155. package/dist/agents/37-dependency-scanner.d.ts +11 -0
  156. package/dist/agents/37-dependency-scanner.d.ts.map +1 -0
  157. package/dist/agents/37-dependency-scanner.js +139 -0
  158. package/dist/agents/37-dependency-scanner.js.map +1 -0
  159. package/dist/agents/38-secrets-scanner.d.ts +11 -0
  160. package/dist/agents/38-secrets-scanner.d.ts.map +1 -0
  161. package/dist/agents/38-secrets-scanner.js +116 -0
  162. package/dist/agents/38-secrets-scanner.js.map +1 -0
  163. package/dist/agents/39-api-contract-tester.d.ts +12 -0
  164. package/dist/agents/39-api-contract-tester.d.ts.map +1 -0
  165. package/dist/agents/39-api-contract-tester.js +142 -0
  166. package/dist/agents/39-api-contract-tester.js.map +1 -0
  167. package/dist/agents/40-rate-limit-tester.d.ts +12 -0
  168. package/dist/agents/40-rate-limit-tester.d.ts.map +1 -0
  169. package/dist/agents/40-rate-limit-tester.js +79 -0
  170. package/dist/agents/40-rate-limit-tester.js.map +1 -0
  171. package/dist/agents/41-api-pagination-tester.d.ts +12 -0
  172. package/dist/agents/41-api-pagination-tester.d.ts.map +1 -0
  173. package/dist/agents/41-api-pagination-tester.js +79 -0
  174. package/dist/agents/41-api-pagination-tester.js.map +1 -0
  175. package/dist/agents/42-graphql-tester.d.ts +13 -0
  176. package/dist/agents/42-graphql-tester.d.ts.map +1 -0
  177. package/dist/agents/42-graphql-tester.js +187 -0
  178. package/dist/agents/42-graphql-tester.js.map +1 -0
  179. package/dist/agents/43-data-consistency-checker.d.ts +11 -0
  180. package/dist/agents/43-data-consistency-checker.d.ts.map +1 -0
  181. package/dist/agents/43-data-consistency-checker.js +176 -0
  182. package/dist/agents/43-data-consistency-checker.js.map +1 -0
  183. package/dist/agents/44-backup-recovery-tester.d.ts +11 -0
  184. package/dist/agents/44-backup-recovery-tester.d.ts.map +1 -0
  185. package/dist/agents/44-backup-recovery-tester.js +128 -0
  186. package/dist/agents/44-backup-recovery-tester.js.map +1 -0
  187. package/dist/agents/45-data-privacy-scanner.d.ts +11 -0
  188. package/dist/agents/45-data-privacy-scanner.d.ts.map +1 -0
  189. package/dist/agents/45-data-privacy-scanner.js +100 -0
  190. package/dist/agents/45-data-privacy-scanner.js.map +1 -0
  191. package/dist/agents/46-seo-auditor.d.ts +12 -0
  192. package/dist/agents/46-seo-auditor.d.ts.map +1 -0
  193. package/dist/agents/46-seo-auditor.js +275 -0
  194. package/dist/agents/46-seo-auditor.js.map +1 -0
  195. package/dist/agents/47-social-preview-tester.d.ts +11 -0
  196. package/dist/agents/47-social-preview-tester.d.ts.map +1 -0
  197. package/dist/agents/47-social-preview-tester.js +219 -0
  198. package/dist/agents/47-social-preview-tester.js.map +1 -0
  199. package/dist/agents/48-lighthouse-auditor.d.ts +11 -0
  200. package/dist/agents/48-lighthouse-auditor.d.ts.map +1 -0
  201. package/dist/agents/48-lighthouse-auditor.js +192 -0
  202. package/dist/agents/48-lighthouse-auditor.js.map +1 -0
  203. package/dist/agents/49-i18n-tester.d.ts +13 -0
  204. package/dist/agents/49-i18n-tester.d.ts.map +1 -0
  205. package/dist/agents/49-i18n-tester.js +172 -0
  206. package/dist/agents/49-i18n-tester.js.map +1 -0
  207. package/dist/agents/50-timezone-tester.d.ts +11 -0
  208. package/dist/agents/50-timezone-tester.d.ts.map +1 -0
  209. package/dist/agents/50-timezone-tester.js +152 -0
  210. package/dist/agents/50-timezone-tester.js.map +1 -0
  211. package/dist/agents/51-error-recovery-tester.d.ts +11 -0
  212. package/dist/agents/51-error-recovery-tester.d.ts.map +1 -0
  213. package/dist/agents/51-error-recovery-tester.js +134 -0
  214. package/dist/agents/51-error-recovery-tester.js.map +1 -0
  215. package/dist/agents/52-offline-mode-tester.d.ts +12 -0
  216. package/dist/agents/52-offline-mode-tester.d.ts.map +1 -0
  217. package/dist/agents/52-offline-mode-tester.js +161 -0
  218. package/dist/agents/52-offline-mode-tester.js.map +1 -0
  219. package/dist/agents/53-graceful-degradation-tester.d.ts +12 -0
  220. package/dist/agents/53-graceful-degradation-tester.d.ts.map +1 -0
  221. package/dist/agents/53-graceful-degradation-tester.js +130 -0
  222. package/dist/agents/53-graceful-degradation-tester.js.map +1 -0
  223. package/dist/agents/54-websocket-tester.d.ts +10 -0
  224. package/dist/agents/54-websocket-tester.d.ts.map +1 -0
  225. package/dist/agents/54-websocket-tester.js +132 -0
  226. package/dist/agents/54-websocket-tester.js.map +1 -0
  227. package/dist/agents/55-realtime-sync-tester.d.ts +11 -0
  228. package/dist/agents/55-realtime-sync-tester.d.ts.map +1 -0
  229. package/dist/agents/55-realtime-sync-tester.js +175 -0
  230. package/dist/agents/55-realtime-sync-tester.js.map +1 -0
  231. package/dist/agents/56-file-upload-tester.d.ts +12 -0
  232. package/dist/agents/56-file-upload-tester.d.ts.map +1 -0
  233. package/dist/agents/56-file-upload-tester.js +166 -0
  234. package/dist/agents/56-file-upload-tester.js.map +1 -0
  235. package/dist/agents/57-export-tester.d.ts +11 -0
  236. package/dist/agents/57-export-tester.d.ts.map +1 -0
  237. package/dist/agents/57-export-tester.js +155 -0
  238. package/dist/agents/57-export-tester.js.map +1 -0
  239. package/dist/agents/58-payment-flow-tester.d.ts +11 -0
  240. package/dist/agents/58-payment-flow-tester.d.ts.map +1 -0
  241. package/dist/agents/58-payment-flow-tester.js +159 -0
  242. package/dist/agents/58-payment-flow-tester.js.map +1 -0
  243. package/dist/agents/59-ssl-tls-auditor.d.ts +10 -0
  244. package/dist/agents/59-ssl-tls-auditor.d.ts.map +1 -0
  245. package/dist/agents/59-ssl-tls-auditor.js +132 -0
  246. package/dist/agents/59-ssl-tls-auditor.js.map +1 -0
  247. package/dist/agents/60-dns-cdn-tester.d.ts +10 -0
  248. package/dist/agents/60-dns-cdn-tester.d.ts.map +1 -0
  249. package/dist/agents/60-dns-cdn-tester.js +105 -0
  250. package/dist/agents/60-dns-cdn-tester.js.map +1 -0
  251. package/dist/agents/61-docker-health-checker.d.ts +10 -0
  252. package/dist/agents/61-docker-health-checker.d.ts.map +1 -0
  253. package/dist/agents/61-docker-health-checker.js +95 -0
  254. package/dist/agents/61-docker-health-checker.js.map +1 -0
  255. package/dist/agents/62-env-config-validator.d.ts +10 -0
  256. package/dist/agents/62-env-config-validator.d.ts.map +1 -0
  257. package/dist/agents/62-env-config-validator.js +132 -0
  258. package/dist/agents/62-env-config-validator.js.map +1 -0
  259. package/dist/agents/63-log-quality-auditor.d.ts +9 -0
  260. package/dist/agents/63-log-quality-auditor.d.ts.map +1 -0
  261. package/dist/agents/63-log-quality-auditor.js +121 -0
  262. package/dist/agents/63-log-quality-auditor.js.map +1 -0
  263. package/dist/agents/64-analytics-tracker-tester.d.ts +10 -0
  264. package/dist/agents/64-analytics-tracker-tester.d.ts.map +1 -0
  265. package/dist/agents/64-analytics-tracker-tester.js +146 -0
  266. package/dist/agents/64-analytics-tracker-tester.js.map +1 -0
  267. package/dist/agents/65-gdpr-compliance-tester.d.ts +13 -0
  268. package/dist/agents/65-gdpr-compliance-tester.d.ts.map +1 -0
  269. package/dist/agents/65-gdpr-compliance-tester.js +186 -0
  270. package/dist/agents/65-gdpr-compliance-tester.js.map +1 -0
  271. package/dist/agents/66-soc2-control-validator.d.ts +14 -0
  272. package/dist/agents/66-soc2-control-validator.d.ts.map +1 -0
  273. package/dist/agents/66-soc2-control-validator.js +178 -0
  274. package/dist/agents/66-soc2-control-validator.js.map +1 -0
  275. package/dist/agents/67-wcag-aaa-tester.d.ts +13 -0
  276. package/dist/agents/67-wcag-aaa-tester.d.ts.map +1 -0
  277. package/dist/agents/67-wcag-aaa-tester.js +207 -0
  278. package/dist/agents/67-wcag-aaa-tester.js.map +1 -0
  279. package/dist/agents/68-dead-code-detector.d.ts +9 -0
  280. package/dist/agents/68-dead-code-detector.d.ts.map +1 -0
  281. package/dist/agents/68-dead-code-detector.js +116 -0
  282. package/dist/agents/68-dead-code-detector.js.map +1 -0
  283. package/dist/agents/69-type-safety-auditor.d.ts +9 -0
  284. package/dist/agents/69-type-safety-auditor.d.ts.map +1 -0
  285. package/dist/agents/69-type-safety-auditor.js +148 -0
  286. package/dist/agents/69-type-safety-auditor.js.map +1 -0
  287. package/dist/agents/70-complexity-analyzer.d.ts +10 -0
  288. package/dist/agents/70-complexity-analyzer.d.ts.map +1 -0
  289. package/dist/agents/70-complexity-analyzer.js +154 -0
  290. package/dist/agents/70-complexity-analyzer.js.map +1 -0
  291. package/dist/agents/registry.d.ts +3 -3
  292. package/dist/agents/registry.d.ts.map +1 -1
  293. package/dist/agents/registry.js +146 -5
  294. package/dist/agents/registry.js.map +1 -1
  295. package/dist/core/license.js +2 -5
  296. package/dist/core/license.js.map +1 -1
  297. package/dist/core/orchestrator.d.ts.map +1 -1
  298. package/dist/core/orchestrator.js +6 -4
  299. package/dist/core/orchestrator.js.map +1 -1
  300. package/package.json +2 -2
@@ -0,0 +1,222 @@
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 helpers
7
+ // ---------------------------------------------------------------------------
8
+
9
+ function makeConfig(overrides: Partial<ValidatedConfig> = {}): ValidatedConfig {
10
+ return {
11
+ schemaVersion: 1,
12
+ name: 'test',
13
+ auth: { loginUrl: 'http://localhost/login', credentials: {} },
14
+ modules: [],
15
+ environments: {
16
+ alpha: { baseUrl: 'http://localhost:3000', seed: false },
17
+ },
18
+ portals: [],
19
+ breakpoints: [],
20
+ integrations: [],
21
+ workers: [],
22
+ workflows: [],
23
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
24
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
25
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
26
+ agentClassification: { blocking: [], advisory: [44] },
27
+ thresholds: {
28
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
29
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
30
+ apiResponseTime: 2000,
31
+ bundleSizeLimit: 500000,
32
+ maxConsoleErrors: 0,
33
+ },
34
+ disabledAgents: [],
35
+ disabledAgentSet: new Set<number>(),
36
+ ...overrides,
37
+ };
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Mock fs and child_process
42
+ // ---------------------------------------------------------------------------
43
+
44
+ const mockStat = vi.fn();
45
+ vi.mock('node:fs/promises', () => ({
46
+ stat: (...args: unknown[]) => mockStat(...args),
47
+ }));
48
+
49
+ const execFileMock = vi.fn();
50
+ vi.mock('node:util', async (importOriginal) => {
51
+ const orig = await importOriginal() as Record<string, unknown>;
52
+ return {
53
+ ...orig,
54
+ promisify: () => execFileMock,
55
+ };
56
+ });
57
+
58
+ vi.mock('node:child_process', () => ({
59
+ execFile: vi.fn(),
60
+ }));
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Dynamic import AFTER mocks
64
+ // ---------------------------------------------------------------------------
65
+
66
+ const { BackupRecoveryTesterAgent } = await import('../44-backup-recovery-tester');
67
+
68
+ function createAgent(
69
+ config: ValidatedConfig = makeConfig(),
70
+ phase: Phase = 'beta',
71
+ ): InstanceType<typeof BackupRecoveryTesterAgent> {
72
+ return new BackupRecoveryTesterAgent(config, phase, '/tmp/test-run');
73
+ }
74
+
75
+ // ---------------------------------------------------------------------------
76
+ // Tests
77
+ // ---------------------------------------------------------------------------
78
+
79
+ describe('BackupRecoveryTesterAgent', () => {
80
+ beforeEach(() => {
81
+ mockStat.mockReset();
82
+ execFileMock.mockReset();
83
+ });
84
+
85
+ it('has agentId 44 and correct agentName', () => {
86
+ const agent = createAgent();
87
+ expect(agent.agentId).toBe(44);
88
+ expect(agent.agentName).toBe('Backup Recovery Tester');
89
+ });
90
+
91
+ it('produces low finding when no prisma schema exists', async () => {
92
+ mockStat.mockRejectedValue(new Error('ENOENT'));
93
+
94
+ const agent = createAgent();
95
+ const result = await agent.run();
96
+
97
+ const noSchema = result.findings.find(f => f.id.includes('no-prisma-schema'));
98
+ expect(noSchema).toBeDefined();
99
+ expect(noSchema!.severity).toBe('low');
100
+ });
101
+
102
+ it('detects schema pull errors', async () => {
103
+ mockStat.mockResolvedValue({ isFile: () => true });
104
+ execFileMock
105
+ .mockResolvedValueOnce({ stdout: '', stderr: 'Error: could not connect' })
106
+ .mockResolvedValueOnce({ stdout: 'all good', stderr: '' });
107
+
108
+ const agent = createAgent();
109
+ const result = await agent.run();
110
+
111
+ const pullError = result.findings.find(f => f.id.includes('schema-pull-error'));
112
+ expect(pullError).toBeDefined();
113
+ expect(pullError!.severity).toBe('medium');
114
+ });
115
+
116
+ it('detects pending migrations', async () => {
117
+ mockStat.mockResolvedValue({ isFile: () => true });
118
+ execFileMock
119
+ .mockResolvedValueOnce({ stdout: 'schema output', stderr: '' })
120
+ .mockResolvedValueOnce({ stdout: 'Migration pending and not yet applied', stderr: '' });
121
+
122
+ const agent = createAgent();
123
+ const result = await agent.run();
124
+
125
+ const pending = result.findings.find(f => f.id.includes('pending-migrations'));
126
+ expect(pending).toBeDefined();
127
+ expect(pending!.severity).toBe('high');
128
+ });
129
+
130
+ it('detects schema drift', async () => {
131
+ mockStat.mockResolvedValue({ isFile: () => true });
132
+ execFileMock
133
+ .mockResolvedValueOnce({ stdout: 'schema output', stderr: '' })
134
+ .mockResolvedValueOnce({ stdout: 'database drift detected', stderr: '' });
135
+
136
+ const agent = createAgent();
137
+ const result = await agent.run();
138
+
139
+ const drift = result.findings.find(f => f.id.includes('schema-drift'));
140
+ expect(drift).toBeDefined();
141
+ expect(drift!.severity).toBe('high');
142
+ });
143
+
144
+ it('passes when no issues found', async () => {
145
+ mockStat.mockResolvedValue({ isFile: () => true });
146
+ execFileMock
147
+ .mockResolvedValueOnce({ stdout: 'schema output', stderr: '' })
148
+ .mockResolvedValueOnce({ stdout: 'Database schema is in sync', stderr: '' });
149
+
150
+ const agent = createAgent();
151
+ const result = await agent.run();
152
+
153
+ const findings = result.findings.filter(f =>
154
+ f.id.includes('pending') || f.id.includes('drift') || f.id.includes('pull-error'),
155
+ );
156
+ expect(findings.length).toBe(0);
157
+ });
158
+
159
+ it('handles schema pull failure gracefully', async () => {
160
+ mockStat.mockResolvedValue({ isFile: () => true });
161
+ execFileMock
162
+ .mockRejectedValueOnce(new Error('npx not found'))
163
+ .mockResolvedValueOnce({ stdout: '', stderr: '' });
164
+
165
+ const agent = createAgent();
166
+ const result = await agent.run();
167
+
168
+ const pullFailed = result.findings.find(f => f.id.includes('schema-pull-failed'));
169
+ expect(pullFailed).toBeDefined();
170
+ expect(pullFailed!.severity).toBe('medium');
171
+ });
172
+
173
+ it('handles migration status failure gracefully without crashing', async () => {
174
+ mockStat.mockResolvedValue({ isFile: () => true });
175
+ execFileMock
176
+ .mockResolvedValueOnce({ stdout: 'ok', stderr: '' })
177
+ .mockRejectedValueOnce(new Error('prisma not found'));
178
+
179
+ const agent = createAgent();
180
+ const result = await agent.run();
181
+
182
+ // The inner catch consumes the error and sets stdout/stderr to empty strings
183
+ // so the agent doesn't crash — it gracefully handles the failure
184
+ expect(result.status).toBeDefined();
185
+ expect(result.status).not.toBe('failed');
186
+ });
187
+
188
+ it('records evidence for schema pull', async () => {
189
+ mockStat.mockResolvedValue({ isFile: () => true });
190
+ execFileMock
191
+ .mockResolvedValueOnce({ stdout: 'model User { id Int }', stderr: '' })
192
+ .mockResolvedValueOnce({ stdout: 'all synced', stderr: '' });
193
+
194
+ const agent = createAgent();
195
+ const result = await agent.run();
196
+
197
+ expect(result.evidence.some(e => e.description.includes('prisma db pull'))).toBe(true);
198
+ });
199
+
200
+ it('uses projectRoot from config', async () => {
201
+ const config = makeConfig({ projectRoot: '/custom/root' });
202
+ mockStat.mockRejectedValue(new Error('ENOENT'));
203
+
204
+ const agent = createAgent(config);
205
+ await agent.run();
206
+
207
+ expect(mockStat).toHaveBeenCalledWith(expect.stringContaining('custom'));
208
+ });
209
+
210
+ it('handles migrate status non-zero exit with stdout', async () => {
211
+ mockStat.mockResolvedValue({ isFile: () => true });
212
+ execFileMock
213
+ .mockResolvedValueOnce({ stdout: 'ok', stderr: '' })
214
+ .mockRejectedValueOnce({ stdout: 'pending migrations', stderr: '' });
215
+
216
+ const agent = createAgent();
217
+ const result = await agent.run();
218
+
219
+ // Should handle the non-zero exit (which has stdout)
220
+ expect(result.status).toBeDefined();
221
+ });
222
+ });
@@ -0,0 +1,223 @@
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 helpers
7
+ // ---------------------------------------------------------------------------
8
+
9
+ function makeConfig(overrides: Partial<ValidatedConfig> = {}): ValidatedConfig {
10
+ return {
11
+ schemaVersion: 1,
12
+ name: 'test',
13
+ auth: {
14
+ loginUrl: 'http://localhost/login',
15
+ credentials: {
16
+ admin: { email: 'admin@test.com', password: 'secret' },
17
+ },
18
+ },
19
+ modules: [{ id: 'clients', route: '/clients', sidebarIcon: true }],
20
+ environments: {
21
+ alpha: { baseUrl: 'http://localhost:3000', seed: false },
22
+ },
23
+ portals: [],
24
+ breakpoints: [],
25
+ integrations: [],
26
+ workers: [],
27
+ workflows: [],
28
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
29
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
30
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
31
+ agentClassification: { blocking: [], advisory: [45] },
32
+ thresholds: {
33
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
34
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
35
+ apiResponseTime: 2000,
36
+ bundleSizeLimit: 500000,
37
+ maxConsoleErrors: 0,
38
+ },
39
+ disabledAgents: [],
40
+ disabledAgentSet: new Set<number>(),
41
+ ...overrides,
42
+ };
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Mock fetch
47
+ // ---------------------------------------------------------------------------
48
+
49
+ const fetchMock = vi.fn();
50
+ vi.stubGlobal('fetch', fetchMock);
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Mock ApiClient
54
+ // ---------------------------------------------------------------------------
55
+
56
+ const mockGet = vi.fn();
57
+
58
+ vi.mock('../../helpers/api-client', () => ({
59
+ ApiClient: {
60
+ createAuthenticated: vi.fn().mockImplementation(async () => ({
61
+ get: mockGet,
62
+ post: vi.fn(),
63
+ put: vi.fn(),
64
+ delete: vi.fn(),
65
+ })),
66
+ },
67
+ }));
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Dynamic import AFTER mocks
71
+ // ---------------------------------------------------------------------------
72
+
73
+ const { DataPrivacyScannerAgent } = await import('../45-data-privacy-scanner');
74
+
75
+ function createAgent(
76
+ config: ValidatedConfig = makeConfig(),
77
+ phase: Phase = 'beta',
78
+ ): InstanceType<typeof DataPrivacyScannerAgent> {
79
+ return new DataPrivacyScannerAgent(config, phase, '/tmp/test-run');
80
+ }
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // Tests
84
+ // ---------------------------------------------------------------------------
85
+
86
+ describe('DataPrivacyScannerAgent', () => {
87
+ beforeEach(() => {
88
+ fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
89
+ mockGet.mockReset();
90
+ });
91
+
92
+ it('has agentId 45 and correct agentName', () => {
93
+ const agent = createAgent();
94
+ expect(agent.agentId).toBe(45);
95
+ expect(agent.agentName).toBe('Data Privacy Scanner');
96
+ });
97
+
98
+ it('throws when modules array is empty', async () => {
99
+ const config = makeConfig({ modules: [] });
100
+ const agent = createAgent(config);
101
+ const result = await agent.run();
102
+ expect(result.status).toBe('failed');
103
+ });
104
+
105
+ it('detects unmasked email addresses', async () => {
106
+ mockGet.mockResolvedValue({
107
+ status: 200,
108
+ body: [{ name: 'John', email: 'john@example.com' }],
109
+ });
110
+
111
+ const agent = createAgent();
112
+ const result = await agent.run();
113
+
114
+ const emailFinding = result.findings.find(f => f.description.includes('Email'));
115
+ expect(emailFinding).toBeDefined();
116
+ expect(emailFinding!.severity).toBe('high');
117
+ });
118
+
119
+ it('detects unmasked SSN patterns', async () => {
120
+ mockGet.mockResolvedValue({
121
+ status: 200,
122
+ body: [{ name: 'John', ssn: '123-45-6789' }],
123
+ });
124
+
125
+ const agent = createAgent();
126
+ const result = await agent.run();
127
+
128
+ const ssnFinding = result.findings.find(f => f.description.includes('SSN'));
129
+ expect(ssnFinding).toBeDefined();
130
+ expect(ssnFinding!.severity).toBe('high');
131
+ });
132
+
133
+ it('detects unmasked credit card numbers', async () => {
134
+ mockGet.mockResolvedValue({
135
+ status: 200,
136
+ body: [{ name: 'John', card: '4111-1111-1111-1111' }],
137
+ });
138
+
139
+ const agent = createAgent();
140
+ const result = await agent.run();
141
+
142
+ const ccFinding = result.findings.find(f => f.description.includes('Credit Card'));
143
+ expect(ccFinding).toBeDefined();
144
+ });
145
+
146
+ it('detects unmasked phone numbers', async () => {
147
+ mockGet.mockResolvedValue({
148
+ status: 200,
149
+ body: [{ name: 'John', phone: '(555) 123-4567' }],
150
+ });
151
+
152
+ const agent = createAgent();
153
+ const result = await agent.run();
154
+
155
+ const phoneFinding = result.findings.find(f => f.description.includes('Phone'));
156
+ expect(phoneFinding).toBeDefined();
157
+ });
158
+
159
+ it('does not flag when no PII present', async () => {
160
+ mockGet.mockResolvedValue({
161
+ status: 200,
162
+ body: [{ id: 1, status: 'active', count: 42 }],
163
+ });
164
+
165
+ const agent = createAgent();
166
+ const result = await agent.run();
167
+
168
+ const piiFindings = result.findings.filter(f => f.id.includes('unmasked'));
169
+ expect(piiFindings.length).toBe(0);
170
+ });
171
+
172
+ it('handles non-200 responses', async () => {
173
+ mockGet.mockResolvedValue({ status: 404, body: {} });
174
+
175
+ const agent = createAgent();
176
+ const result = await agent.run();
177
+
178
+ const piiFindings = result.findings.filter(f => f.id.includes('unmasked'));
179
+ expect(piiFindings.length).toBe(0);
180
+ });
181
+
182
+ it('handles request errors gracefully', async () => {
183
+ mockGet.mockRejectedValue(new Error('Connection refused'));
184
+
185
+ const agent = createAgent();
186
+ const result = await agent.run();
187
+
188
+ const scanError = result.findings.find(f => f.id.includes('scan-error'));
189
+ expect(scanError).toBeDefined();
190
+ });
191
+
192
+ it('handles auth failure gracefully', async () => {
193
+ const { ApiClient } = await import('../../helpers/api-client');
194
+ (ApiClient.createAuthenticated as ReturnType<typeof vi.fn>).mockRejectedValueOnce(
195
+ new Error('Auth failed'),
196
+ );
197
+
198
+ const agent = createAgent();
199
+ const result = await agent.run();
200
+
201
+ const authFailed = result.findings.find(f => f.id.includes('auth-failed'));
202
+ expect(authFailed).toBeDefined();
203
+ });
204
+
205
+ it('records evidence with scan summary', async () => {
206
+ mockGet.mockResolvedValue({
207
+ status: 200,
208
+ body: [{ id: 1, name: 'Test' }],
209
+ });
210
+
211
+ const agent = createAgent();
212
+ const result = await agent.run();
213
+
214
+ expect(result.evidence.some(e => e.description.includes('PII scan'))).toBe(true);
215
+ });
216
+
217
+ it('resolves environment in preFlight', async () => {
218
+ mockGet.mockResolvedValue({ status: 200, body: [] });
219
+ const agent = createAgent();
220
+ const result = await agent.run();
221
+ expect(result.findings.every(f => !f.description.includes('preFlight'))).toBe(true);
222
+ });
223
+ });
@@ -0,0 +1,261 @@
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 helpers
7
+ // ---------------------------------------------------------------------------
8
+
9
+ function makeConfig(overrides: Partial<ValidatedConfig> = {}): ValidatedConfig {
10
+ return {
11
+ schemaVersion: 1,
12
+ name: 'test',
13
+ auth: {
14
+ loginUrl: 'http://localhost/login',
15
+ credentials: {
16
+ admin: { email: 'admin@test.com', password: 'secret' },
17
+ },
18
+ },
19
+ modules: [{ id: 'home', route: '/', sidebarIcon: true }],
20
+ environments: {
21
+ alpha: { baseUrl: 'http://localhost:3000', seed: false },
22
+ },
23
+ portals: [],
24
+ breakpoints: [],
25
+ integrations: [],
26
+ workers: [],
27
+ workflows: [],
28
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
29
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
30
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
31
+ agentClassification: { blocking: [], advisory: [46] },
32
+ thresholds: {
33
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
34
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
35
+ apiResponseTime: 2000,
36
+ bundleSizeLimit: 500000,
37
+ maxConsoleErrors: 0,
38
+ },
39
+ disabledAgents: [],
40
+ disabledAgentSet: new Set<number>(),
41
+ ...overrides,
42
+ };
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Mock fetch
47
+ // ---------------------------------------------------------------------------
48
+
49
+ const fetchMock = vi.fn();
50
+ vi.stubGlobal('fetch', fetchMock);
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Mock helpers
54
+ // ---------------------------------------------------------------------------
55
+
56
+ const loginMock = vi.fn().mockResolvedValue(undefined);
57
+
58
+ vi.mock('../../helpers/navigation', () => ({
59
+ login: (...args: unknown[]) => loginMock(...args),
60
+ waitForSectionLoad: vi.fn().mockResolvedValue(undefined),
61
+ navigateToSection: vi.fn().mockResolvedValue(undefined),
62
+ switchPortal: vi.fn().mockResolvedValue(undefined),
63
+ }));
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Mock Playwright
67
+ // ---------------------------------------------------------------------------
68
+
69
+ interface MockLocator {
70
+ count: ReturnType<typeof vi.fn>;
71
+ }
72
+
73
+ function makeMockLocator(countVal = 0): MockLocator {
74
+ return { count: vi.fn().mockResolvedValue(countVal) };
75
+ }
76
+
77
+ interface MockPage {
78
+ close: ReturnType<typeof vi.fn>;
79
+ goto: ReturnType<typeof vi.fn>;
80
+ evaluate: ReturnType<typeof vi.fn>;
81
+ locator: ReturnType<typeof vi.fn>;
82
+ }
83
+
84
+ function makeMockPage(): MockPage {
85
+ return {
86
+ close: vi.fn().mockResolvedValue(undefined),
87
+ goto: vi.fn().mockResolvedValue(undefined),
88
+ evaluate: vi.fn().mockResolvedValue(null),
89
+ locator: vi.fn().mockReturnValue(makeMockLocator(1)),
90
+ };
91
+ }
92
+
93
+ let currentMockPage: MockPage;
94
+ let mockBrowser: { newPage: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
95
+
96
+ vi.mock('playwright', async () => ({
97
+ chromium: {
98
+ launch: vi.fn().mockImplementation(async () => mockBrowser),
99
+ },
100
+ }));
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Dynamic import AFTER mocks
104
+ // ---------------------------------------------------------------------------
105
+
106
+ const { SeoAuditorAgent } = await import('../46-seo-auditor');
107
+
108
+ function createAgent(
109
+ config: ValidatedConfig = makeConfig(),
110
+ phase: Phase = 'beta',
111
+ ): InstanceType<typeof SeoAuditorAgent> {
112
+ return new SeoAuditorAgent(config, phase, '/tmp/test-run');
113
+ }
114
+
115
+ // ---------------------------------------------------------------------------
116
+ // Tests
117
+ // ---------------------------------------------------------------------------
118
+
119
+ describe('SeoAuditorAgent', () => {
120
+ beforeEach(() => {
121
+ fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
122
+ loginMock.mockReset().mockResolvedValue(undefined);
123
+
124
+ currentMockPage = makeMockPage();
125
+ mockBrowser = {
126
+ newPage: vi.fn().mockResolvedValue(currentMockPage),
127
+ close: vi.fn().mockResolvedValue(undefined),
128
+ };
129
+ });
130
+
131
+ it('has agentId 46 and correct agentName', () => {
132
+ const agent = createAgent();
133
+ expect(agent.agentId).toBe(46);
134
+ expect(agent.agentName).toBe('SEO Auditor');
135
+ });
136
+
137
+ it('throws when modules array is empty', async () => {
138
+ const config = makeConfig({ modules: [] });
139
+ const agent = createAgent(config);
140
+ const result = await agent.run();
141
+ expect(result.status).toBe('failed');
142
+ });
143
+
144
+ it('detects missing title', async () => {
145
+ currentMockPage.evaluate.mockImplementation((fn: unknown) => {
146
+ const fnStr = String(fn);
147
+ if (fnStr.includes('document.title')) return Promise.resolve('');
148
+ return Promise.resolve(null);
149
+ });
150
+
151
+ const agent = createAgent();
152
+ const result = await agent.run();
153
+
154
+ const noTitle = result.findings.find(f => f.id.includes('no-title'));
155
+ expect(noTitle).toBeDefined();
156
+ });
157
+
158
+ it('detects missing meta description', async () => {
159
+ currentMockPage.evaluate.mockImplementation((fn: unknown) => {
160
+ const fnStr = String(fn);
161
+ if (fnStr.includes('document.title')) return Promise.resolve('My Page');
162
+ if (fnStr.includes('meta[name="description"]')) return Promise.resolve(null);
163
+ return Promise.resolve(null);
164
+ });
165
+
166
+ const agent = createAgent();
167
+ const result = await agent.run();
168
+
169
+ const noMeta = result.findings.find(f => f.id.includes('no-meta-desc'));
170
+ expect(noMeta).toBeDefined();
171
+ });
172
+
173
+ it('detects missing h1', async () => {
174
+ currentMockPage.evaluate.mockResolvedValue('Page Title');
175
+ currentMockPage.locator.mockReturnValue(makeMockLocator(0));
176
+
177
+ const agent = createAgent();
178
+ const result = await agent.run();
179
+
180
+ const noH1 = result.findings.find(f => f.id.includes('no-h1'));
181
+ expect(noH1).toBeDefined();
182
+ });
183
+
184
+ it('detects multiple h1 tags', async () => {
185
+ currentMockPage.evaluate.mockResolvedValue('Page Title');
186
+ currentMockPage.locator.mockReturnValue(makeMockLocator(3));
187
+
188
+ const agent = createAgent();
189
+ const result = await agent.run();
190
+
191
+ const multiH1 = result.findings.find(f => f.id.includes('multiple-h1'));
192
+ expect(multiH1).toBeDefined();
193
+ });
194
+
195
+ it('checks robots.txt', async () => {
196
+ fetchMock.mockImplementation((url: string) => {
197
+ if (typeof url === 'string' && url.includes('robots.txt')) {
198
+ return Promise.resolve({ ok: false, status: 404 });
199
+ }
200
+ return Promise.resolve({ ok: true, status: 200 });
201
+ });
202
+
203
+ const agent = createAgent();
204
+ const result = await agent.run();
205
+
206
+ const noRobots = result.findings.find(f => f.id.includes('no-robots-txt'));
207
+ expect(noRobots).toBeDefined();
208
+ });
209
+
210
+ it('checks sitemap.xml', async () => {
211
+ fetchMock.mockImplementation((url: string) => {
212
+ if (typeof url === 'string' && url.includes('sitemap.xml')) {
213
+ return Promise.resolve({ ok: false, status: 404 });
214
+ }
215
+ return Promise.resolve({ ok: true, status: 200 });
216
+ });
217
+
218
+ const agent = createAgent();
219
+ const result = await agent.run();
220
+
221
+ const noSitemap = result.findings.find(f => f.id.includes('no-sitemap-xml'));
222
+ expect(noSitemap).toBeDefined();
223
+ });
224
+
225
+ it('produces medium finding when login fails', async () => {
226
+ loginMock.mockRejectedValue(new Error('Auth failed'));
227
+
228
+ const agent = createAgent();
229
+ const result = await agent.run();
230
+
231
+ const loginFinding = result.findings.find(f => f.id.includes('login-failed'));
232
+ expect(loginFinding).toBeDefined();
233
+ });
234
+
235
+ it('produces medium finding when Playwright crashes', async () => {
236
+ mockBrowser.newPage.mockRejectedValue(new Error('Browser crashed'));
237
+
238
+ const agent = createAgent();
239
+ const result = await agent.run();
240
+
241
+ const pwFinding = result.findings.find(f => f.id.includes('playwright-failure'));
242
+ expect(pwFinding).toBeDefined();
243
+ });
244
+
245
+ it('closes browser even when an error occurs', async () => {
246
+ mockBrowser.newPage.mockRejectedValue(new Error('Launch error'));
247
+ const agent = createAgent();
248
+ await agent.run();
249
+ expect(mockBrowser.close).toHaveBeenCalled();
250
+ });
251
+
252
+ it('produces medium finding when navigation fails', async () => {
253
+ currentMockPage.goto.mockRejectedValue(new Error('Navigation failed'));
254
+
255
+ const agent = createAgent();
256
+ const result = await agent.run();
257
+
258
+ const navFinding = result.findings.find(f => f.id.includes('nav-failed'));
259
+ expect(navFinding).toBeDefined();
260
+ });
261
+ });