@avi770/testteam 1.2.0 → 3.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 (324) hide show
  1. package/CHANGELOG.md +53 -1
  2. package/README.md +72 -7
  3. package/agents/15-regression-sentinel.ts +3 -2
  4. package/agents/24-signup-onboarding-tester.ts +429 -0
  5. package/agents/25-crud-flow-tester.ts +302 -0
  6. package/agents/26-form-validator.ts +297 -0
  7. package/agents/27-search-filter-tester.ts +326 -0
  8. package/agents/28-navigation-routing-tester.ts +425 -0
  9. package/agents/29-responsive-interaction-tester.ts +350 -0
  10. package/agents/30-multi-user-scenario-tester.ts +319 -0
  11. package/agents/31-load-tester.ts +134 -0
  12. package/agents/32-memory-leak-detector.ts +194 -0
  13. package/agents/33-bundle-analyzer.ts +132 -0
  14. package/agents/34-xss-scanner.ts +191 -0
  15. package/agents/35-csrf-tester.ts +82 -0
  16. package/agents/36-auth-fuzzer.ts +194 -0
  17. package/agents/37-dependency-scanner.ts +176 -0
  18. package/agents/38-secrets-scanner.ts +137 -0
  19. package/agents/39-api-contract-tester.ts +199 -0
  20. package/agents/40-rate-limit-tester.ts +94 -0
  21. package/agents/41-api-pagination-tester.ts +97 -0
  22. package/agents/42-graphql-tester.ts +222 -0
  23. package/agents/43-data-consistency-checker.ts +205 -0
  24. package/agents/44-backup-recovery-tester.ts +152 -0
  25. package/agents/45-data-privacy-scanner.ts +125 -0
  26. package/agents/46-seo-auditor.ts +294 -0
  27. package/agents/47-social-preview-tester.ts +232 -0
  28. package/agents/48-lighthouse-auditor.ts +213 -0
  29. package/agents/49-i18n-tester.ts +198 -0
  30. package/agents/50-timezone-tester.ts +173 -0
  31. package/agents/51-error-recovery-tester.ts +155 -0
  32. package/agents/52-offline-mode-tester.ts +180 -0
  33. package/agents/53-graceful-degradation-tester.ts +156 -0
  34. package/agents/54-websocket-tester.ts +151 -0
  35. package/agents/55-realtime-sync-tester.ts +194 -0
  36. package/agents/56-file-upload-tester.ts +194 -0
  37. package/agents/57-export-tester.ts +174 -0
  38. package/agents/58-payment-flow-tester.ts +183 -0
  39. package/agents/59-ssl-tls-auditor.ts +141 -0
  40. package/agents/60-dns-cdn-tester.ts +117 -0
  41. package/agents/61-docker-health-checker.ts +111 -0
  42. package/agents/62-env-config-validator.ts +152 -0
  43. package/agents/63-log-quality-auditor.ts +136 -0
  44. package/agents/64-analytics-tracker-tester.ts +165 -0
  45. package/agents/65-gdpr-compliance-tester.ts +215 -0
  46. package/agents/66-soc2-control-validator.ts +210 -0
  47. package/agents/67-wcag-aaa-tester.ts +241 -0
  48. package/agents/68-dead-code-detector.ts +135 -0
  49. package/agents/69-type-safety-auditor.ts +164 -0
  50. package/agents/70-complexity-analyzer.ts +179 -0
  51. package/agents/__tests__/24-signup-onboarding-tester.test.ts +274 -0
  52. package/agents/__tests__/25-crud-flow-tester.test.ts +322 -0
  53. package/agents/__tests__/26-form-validator.test.ts +345 -0
  54. package/agents/__tests__/27-search-filter-tester.test.ts +311 -0
  55. package/agents/__tests__/28-navigation-routing-tester.test.ts +328 -0
  56. package/agents/__tests__/29-responsive-interaction-tester.test.ts +297 -0
  57. package/agents/__tests__/30-multi-user-scenario-tester.test.ts +328 -0
  58. package/agents/__tests__/31-load-tester.test.ts +189 -0
  59. package/agents/__tests__/32-memory-leak-detector.test.ts +251 -0
  60. package/agents/__tests__/33-bundle-analyzer.test.ts +237 -0
  61. package/agents/__tests__/34-xss-scanner.test.ts +258 -0
  62. package/agents/__tests__/35-csrf-tester.test.ts +200 -0
  63. package/agents/__tests__/36-auth-fuzzer.test.ts +214 -0
  64. package/agents/__tests__/37-dependency-scanner.test.ts +266 -0
  65. package/agents/__tests__/38-secrets-scanner.test.ts +224 -0
  66. package/agents/__tests__/39-api-contract-tester.test.ts +312 -0
  67. package/agents/__tests__/40-rate-limit-tester.test.ts +192 -0
  68. package/agents/__tests__/41-api-pagination-tester.test.ts +198 -0
  69. package/agents/__tests__/42-graphql-tester.test.ts +252 -0
  70. package/agents/__tests__/43-data-consistency-checker.test.ts +232 -0
  71. package/agents/__tests__/44-backup-recovery-tester.test.ts +222 -0
  72. package/agents/__tests__/45-data-privacy-scanner.test.ts +223 -0
  73. package/agents/__tests__/46-seo-auditor.test.ts +261 -0
  74. package/agents/__tests__/47-social-preview-tester.test.ts +245 -0
  75. package/agents/__tests__/48-lighthouse-auditor.test.ts +276 -0
  76. package/agents/__tests__/49-i18n-tester.test.ts +201 -0
  77. package/agents/__tests__/50-timezone-tester.test.ts +172 -0
  78. package/agents/__tests__/51-error-recovery-tester.test.ts +162 -0
  79. package/agents/__tests__/52-offline-mode-tester.test.ts +164 -0
  80. package/agents/__tests__/53-graceful-degradation-tester.test.ts +168 -0
  81. package/agents/__tests__/54-websocket-tester.test.ts +157 -0
  82. package/agents/__tests__/55-realtime-sync-tester.test.ts +181 -0
  83. package/agents/__tests__/56-file-upload-tester.test.ts +172 -0
  84. package/agents/__tests__/57-export-tester.test.ts +169 -0
  85. package/agents/__tests__/58-payment-flow-tester.test.ts +182 -0
  86. package/agents/__tests__/59-ssl-tls-auditor.test.ts +179 -0
  87. package/agents/__tests__/60-dns-cdn-tester.test.ts +176 -0
  88. package/agents/__tests__/61-docker-health-checker.test.ts +150 -0
  89. package/agents/__tests__/62-env-config-validator.test.ts +166 -0
  90. package/agents/__tests__/63-log-quality-auditor.test.ts +175 -0
  91. package/agents/__tests__/64-analytics-tracker-tester.test.ts +158 -0
  92. package/agents/__tests__/65-gdpr-compliance-tester.test.ts +174 -0
  93. package/agents/__tests__/66-soc2-control-validator.test.ts +183 -0
  94. package/agents/__tests__/67-wcag-aaa-tester.test.ts +190 -0
  95. package/agents/__tests__/68-dead-code-detector.test.ts +174 -0
  96. package/agents/__tests__/69-type-safety-auditor.test.ts +173 -0
  97. package/agents/__tests__/70-complexity-analyzer.test.ts +177 -0
  98. package/agents/__tests__/registry.test.ts +13 -13
  99. package/agents/base-agent.ts +8 -0
  100. package/agents/registry.ts +171 -28
  101. package/core/__tests__/integration.test.ts +4 -4
  102. package/core/__tests__/orchestrator.test.ts +17 -16
  103. package/core/cli.ts +128 -6
  104. package/core/license.ts +208 -211
  105. package/core/orchestrator.ts +78 -5
  106. package/dist/agents/15-regression-sentinel.d.ts +2 -1
  107. package/dist/agents/15-regression-sentinel.d.ts.map +1 -1
  108. package/dist/agents/15-regression-sentinel.js +2 -2
  109. package/dist/agents/15-regression-sentinel.js.map +1 -1
  110. package/dist/agents/24-signup-onboarding-tester.d.ts +35 -0
  111. package/dist/agents/24-signup-onboarding-tester.d.ts.map +1 -0
  112. package/dist/agents/24-signup-onboarding-tester.js +357 -0
  113. package/dist/agents/24-signup-onboarding-tester.js.map +1 -0
  114. package/dist/agents/25-crud-flow-tester.d.ts +11 -0
  115. package/dist/agents/25-crud-flow-tester.d.ts.map +1 -0
  116. package/dist/agents/25-crud-flow-tester.js +253 -0
  117. package/dist/agents/25-crud-flow-tester.js.map +1 -0
  118. package/dist/agents/26-form-validator.d.ts +12 -0
  119. package/dist/agents/26-form-validator.d.ts.map +1 -0
  120. package/dist/agents/26-form-validator.js +257 -0
  121. package/dist/agents/26-form-validator.js.map +1 -0
  122. package/dist/agents/27-search-filter-tester.d.ts +20 -0
  123. package/dist/agents/27-search-filter-tester.d.ts.map +1 -0
  124. package/dist/agents/27-search-filter-tester.js +267 -0
  125. package/dist/agents/27-search-filter-tester.js.map +1 -0
  126. package/dist/agents/28-navigation-routing-tester.d.ts +32 -0
  127. package/dist/agents/28-navigation-routing-tester.d.ts.map +1 -0
  128. package/dist/agents/28-navigation-routing-tester.js +363 -0
  129. package/dist/agents/28-navigation-routing-tester.js.map +1 -0
  130. package/dist/agents/29-responsive-interaction-tester.d.ts +26 -0
  131. package/dist/agents/29-responsive-interaction-tester.d.ts.map +1 -0
  132. package/dist/agents/29-responsive-interaction-tester.js +272 -0
  133. package/dist/agents/29-responsive-interaction-tester.js.map +1 -0
  134. package/dist/agents/30-multi-user-scenario-tester.d.ts +24 -0
  135. package/dist/agents/30-multi-user-scenario-tester.d.ts.map +1 -0
  136. package/dist/agents/30-multi-user-scenario-tester.js +254 -0
  137. package/dist/agents/30-multi-user-scenario-tester.js.map +1 -0
  138. package/dist/agents/31-load-tester.d.ts +12 -0
  139. package/dist/agents/31-load-tester.d.ts.map +1 -0
  140. package/dist/agents/31-load-tester.js +110 -0
  141. package/dist/agents/31-load-tester.js.map +1 -0
  142. package/dist/agents/32-memory-leak-detector.d.ts +12 -0
  143. package/dist/agents/32-memory-leak-detector.d.ts.map +1 -0
  144. package/dist/agents/32-memory-leak-detector.js +167 -0
  145. package/dist/agents/32-memory-leak-detector.js.map +1 -0
  146. package/dist/agents/33-bundle-analyzer.d.ts +10 -0
  147. package/dist/agents/33-bundle-analyzer.d.ts.map +1 -0
  148. package/dist/agents/33-bundle-analyzer.js +111 -0
  149. package/dist/agents/33-bundle-analyzer.js.map +1 -0
  150. package/dist/agents/34-xss-scanner.d.ts +11 -0
  151. package/dist/agents/34-xss-scanner.d.ts.map +1 -0
  152. package/dist/agents/34-xss-scanner.js +164 -0
  153. package/dist/agents/34-xss-scanner.js.map +1 -0
  154. package/dist/agents/35-csrf-tester.d.ts +11 -0
  155. package/dist/agents/35-csrf-tester.d.ts.map +1 -0
  156. package/dist/agents/35-csrf-tester.js +70 -0
  157. package/dist/agents/35-csrf-tester.js.map +1 -0
  158. package/dist/agents/36-auth-fuzzer.d.ts +13 -0
  159. package/dist/agents/36-auth-fuzzer.d.ts.map +1 -0
  160. package/dist/agents/36-auth-fuzzer.js +163 -0
  161. package/dist/agents/36-auth-fuzzer.js.map +1 -0
  162. package/dist/agents/37-dependency-scanner.d.ts +11 -0
  163. package/dist/agents/37-dependency-scanner.d.ts.map +1 -0
  164. package/dist/agents/37-dependency-scanner.js +139 -0
  165. package/dist/agents/37-dependency-scanner.js.map +1 -0
  166. package/dist/agents/38-secrets-scanner.d.ts +11 -0
  167. package/dist/agents/38-secrets-scanner.d.ts.map +1 -0
  168. package/dist/agents/38-secrets-scanner.js +116 -0
  169. package/dist/agents/38-secrets-scanner.js.map +1 -0
  170. package/dist/agents/39-api-contract-tester.d.ts +12 -0
  171. package/dist/agents/39-api-contract-tester.d.ts.map +1 -0
  172. package/dist/agents/39-api-contract-tester.js +142 -0
  173. package/dist/agents/39-api-contract-tester.js.map +1 -0
  174. package/dist/agents/40-rate-limit-tester.d.ts +12 -0
  175. package/dist/agents/40-rate-limit-tester.d.ts.map +1 -0
  176. package/dist/agents/40-rate-limit-tester.js +79 -0
  177. package/dist/agents/40-rate-limit-tester.js.map +1 -0
  178. package/dist/agents/41-api-pagination-tester.d.ts +12 -0
  179. package/dist/agents/41-api-pagination-tester.d.ts.map +1 -0
  180. package/dist/agents/41-api-pagination-tester.js +79 -0
  181. package/dist/agents/41-api-pagination-tester.js.map +1 -0
  182. package/dist/agents/42-graphql-tester.d.ts +13 -0
  183. package/dist/agents/42-graphql-tester.d.ts.map +1 -0
  184. package/dist/agents/42-graphql-tester.js +187 -0
  185. package/dist/agents/42-graphql-tester.js.map +1 -0
  186. package/dist/agents/43-data-consistency-checker.d.ts +11 -0
  187. package/dist/agents/43-data-consistency-checker.d.ts.map +1 -0
  188. package/dist/agents/43-data-consistency-checker.js +176 -0
  189. package/dist/agents/43-data-consistency-checker.js.map +1 -0
  190. package/dist/agents/44-backup-recovery-tester.d.ts +11 -0
  191. package/dist/agents/44-backup-recovery-tester.d.ts.map +1 -0
  192. package/dist/agents/44-backup-recovery-tester.js +128 -0
  193. package/dist/agents/44-backup-recovery-tester.js.map +1 -0
  194. package/dist/agents/45-data-privacy-scanner.d.ts +11 -0
  195. package/dist/agents/45-data-privacy-scanner.d.ts.map +1 -0
  196. package/dist/agents/45-data-privacy-scanner.js +100 -0
  197. package/dist/agents/45-data-privacy-scanner.js.map +1 -0
  198. package/dist/agents/46-seo-auditor.d.ts +12 -0
  199. package/dist/agents/46-seo-auditor.d.ts.map +1 -0
  200. package/dist/agents/46-seo-auditor.js +275 -0
  201. package/dist/agents/46-seo-auditor.js.map +1 -0
  202. package/dist/agents/47-social-preview-tester.d.ts +11 -0
  203. package/dist/agents/47-social-preview-tester.d.ts.map +1 -0
  204. package/dist/agents/47-social-preview-tester.js +219 -0
  205. package/dist/agents/47-social-preview-tester.js.map +1 -0
  206. package/dist/agents/48-lighthouse-auditor.d.ts +11 -0
  207. package/dist/agents/48-lighthouse-auditor.d.ts.map +1 -0
  208. package/dist/agents/48-lighthouse-auditor.js +192 -0
  209. package/dist/agents/48-lighthouse-auditor.js.map +1 -0
  210. package/dist/agents/49-i18n-tester.d.ts +13 -0
  211. package/dist/agents/49-i18n-tester.d.ts.map +1 -0
  212. package/dist/agents/49-i18n-tester.js +172 -0
  213. package/dist/agents/49-i18n-tester.js.map +1 -0
  214. package/dist/agents/50-timezone-tester.d.ts +11 -0
  215. package/dist/agents/50-timezone-tester.d.ts.map +1 -0
  216. package/dist/agents/50-timezone-tester.js +152 -0
  217. package/dist/agents/50-timezone-tester.js.map +1 -0
  218. package/dist/agents/51-error-recovery-tester.d.ts +11 -0
  219. package/dist/agents/51-error-recovery-tester.d.ts.map +1 -0
  220. package/dist/agents/51-error-recovery-tester.js +134 -0
  221. package/dist/agents/51-error-recovery-tester.js.map +1 -0
  222. package/dist/agents/52-offline-mode-tester.d.ts +12 -0
  223. package/dist/agents/52-offline-mode-tester.d.ts.map +1 -0
  224. package/dist/agents/52-offline-mode-tester.js +161 -0
  225. package/dist/agents/52-offline-mode-tester.js.map +1 -0
  226. package/dist/agents/53-graceful-degradation-tester.d.ts +12 -0
  227. package/dist/agents/53-graceful-degradation-tester.d.ts.map +1 -0
  228. package/dist/agents/53-graceful-degradation-tester.js +130 -0
  229. package/dist/agents/53-graceful-degradation-tester.js.map +1 -0
  230. package/dist/agents/54-websocket-tester.d.ts +10 -0
  231. package/dist/agents/54-websocket-tester.d.ts.map +1 -0
  232. package/dist/agents/54-websocket-tester.js +132 -0
  233. package/dist/agents/54-websocket-tester.js.map +1 -0
  234. package/dist/agents/55-realtime-sync-tester.d.ts +11 -0
  235. package/dist/agents/55-realtime-sync-tester.d.ts.map +1 -0
  236. package/dist/agents/55-realtime-sync-tester.js +175 -0
  237. package/dist/agents/55-realtime-sync-tester.js.map +1 -0
  238. package/dist/agents/56-file-upload-tester.d.ts +12 -0
  239. package/dist/agents/56-file-upload-tester.d.ts.map +1 -0
  240. package/dist/agents/56-file-upload-tester.js +166 -0
  241. package/dist/agents/56-file-upload-tester.js.map +1 -0
  242. package/dist/agents/57-export-tester.d.ts +11 -0
  243. package/dist/agents/57-export-tester.d.ts.map +1 -0
  244. package/dist/agents/57-export-tester.js +155 -0
  245. package/dist/agents/57-export-tester.js.map +1 -0
  246. package/dist/agents/58-payment-flow-tester.d.ts +11 -0
  247. package/dist/agents/58-payment-flow-tester.d.ts.map +1 -0
  248. package/dist/agents/58-payment-flow-tester.js +159 -0
  249. package/dist/agents/58-payment-flow-tester.js.map +1 -0
  250. package/dist/agents/59-ssl-tls-auditor.d.ts +10 -0
  251. package/dist/agents/59-ssl-tls-auditor.d.ts.map +1 -0
  252. package/dist/agents/59-ssl-tls-auditor.js +132 -0
  253. package/dist/agents/59-ssl-tls-auditor.js.map +1 -0
  254. package/dist/agents/60-dns-cdn-tester.d.ts +10 -0
  255. package/dist/agents/60-dns-cdn-tester.d.ts.map +1 -0
  256. package/dist/agents/60-dns-cdn-tester.js +105 -0
  257. package/dist/agents/60-dns-cdn-tester.js.map +1 -0
  258. package/dist/agents/61-docker-health-checker.d.ts +10 -0
  259. package/dist/agents/61-docker-health-checker.d.ts.map +1 -0
  260. package/dist/agents/61-docker-health-checker.js +95 -0
  261. package/dist/agents/61-docker-health-checker.js.map +1 -0
  262. package/dist/agents/62-env-config-validator.d.ts +10 -0
  263. package/dist/agents/62-env-config-validator.d.ts.map +1 -0
  264. package/dist/agents/62-env-config-validator.js +132 -0
  265. package/dist/agents/62-env-config-validator.js.map +1 -0
  266. package/dist/agents/63-log-quality-auditor.d.ts +9 -0
  267. package/dist/agents/63-log-quality-auditor.d.ts.map +1 -0
  268. package/dist/agents/63-log-quality-auditor.js +121 -0
  269. package/dist/agents/63-log-quality-auditor.js.map +1 -0
  270. package/dist/agents/64-analytics-tracker-tester.d.ts +10 -0
  271. package/dist/agents/64-analytics-tracker-tester.d.ts.map +1 -0
  272. package/dist/agents/64-analytics-tracker-tester.js +146 -0
  273. package/dist/agents/64-analytics-tracker-tester.js.map +1 -0
  274. package/dist/agents/65-gdpr-compliance-tester.d.ts +13 -0
  275. package/dist/agents/65-gdpr-compliance-tester.d.ts.map +1 -0
  276. package/dist/agents/65-gdpr-compliance-tester.js +186 -0
  277. package/dist/agents/65-gdpr-compliance-tester.js.map +1 -0
  278. package/dist/agents/66-soc2-control-validator.d.ts +14 -0
  279. package/dist/agents/66-soc2-control-validator.d.ts.map +1 -0
  280. package/dist/agents/66-soc2-control-validator.js +178 -0
  281. package/dist/agents/66-soc2-control-validator.js.map +1 -0
  282. package/dist/agents/67-wcag-aaa-tester.d.ts +13 -0
  283. package/dist/agents/67-wcag-aaa-tester.d.ts.map +1 -0
  284. package/dist/agents/67-wcag-aaa-tester.js +207 -0
  285. package/dist/agents/67-wcag-aaa-tester.js.map +1 -0
  286. package/dist/agents/68-dead-code-detector.d.ts +9 -0
  287. package/dist/agents/68-dead-code-detector.d.ts.map +1 -0
  288. package/dist/agents/68-dead-code-detector.js +116 -0
  289. package/dist/agents/68-dead-code-detector.js.map +1 -0
  290. package/dist/agents/69-type-safety-auditor.d.ts +9 -0
  291. package/dist/agents/69-type-safety-auditor.d.ts.map +1 -0
  292. package/dist/agents/69-type-safety-auditor.js +148 -0
  293. package/dist/agents/69-type-safety-auditor.js.map +1 -0
  294. package/dist/agents/70-complexity-analyzer.d.ts +10 -0
  295. package/dist/agents/70-complexity-analyzer.d.ts.map +1 -0
  296. package/dist/agents/70-complexity-analyzer.js +154 -0
  297. package/dist/agents/70-complexity-analyzer.js.map +1 -0
  298. package/dist/agents/base-agent.d.ts +3 -1
  299. package/dist/agents/base-agent.d.ts.map +1 -1
  300. package/dist/agents/base-agent.js +7 -1
  301. package/dist/agents/base-agent.js.map +1 -1
  302. package/dist/agents/registry.d.ts +5 -4
  303. package/dist/agents/registry.d.ts.map +1 -1
  304. package/dist/agents/registry.js +170 -29
  305. package/dist/agents/registry.js.map +1 -1
  306. package/dist/clients/agent-mvp.d.ts +66 -0
  307. package/dist/clients/agent-mvp.d.ts.map +1 -0
  308. package/dist/clients/agent-mvp.js +91 -0
  309. package/dist/clients/agent-mvp.js.map +1 -0
  310. package/dist/clients/total-recall.d.ts +37 -0
  311. package/dist/clients/total-recall.d.ts.map +1 -0
  312. package/dist/clients/total-recall.js +224 -0
  313. package/dist/clients/total-recall.js.map +1 -0
  314. package/dist/core/cli.d.ts +3 -0
  315. package/dist/core/cli.d.ts.map +1 -1
  316. package/dist/core/cli.js +122 -6
  317. package/dist/core/cli.js.map +1 -1
  318. package/dist/core/license.js +2 -5
  319. package/dist/core/license.js.map +1 -1
  320. package/dist/core/orchestrator.d.ts +10 -1
  321. package/dist/core/orchestrator.d.ts.map +1 -1
  322. package/dist/core/orchestrator.js +66 -5
  323. package/dist/core/orchestrator.js.map +1 -1
  324. package/package.json +2 -2
@@ -0,0 +1,297 @@
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: [
20
+ { id: 'dashboard', route: '/dashboard', sidebarIcon: true },
21
+ { id: 'tasks', route: '/tasks', sidebarIcon: true },
22
+ { id: 'settings', route: '/settings', sidebarIcon: true },
23
+ ],
24
+ environments: {
25
+ alpha: { baseUrl: 'http://localhost:3000', seed: false },
26
+ },
27
+ portals: [],
28
+ breakpoints: [],
29
+ integrations: [],
30
+ workers: [],
31
+ workflows: [],
32
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
33
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
34
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
35
+ agentClassification: { blocking: [], advisory: [29] },
36
+ thresholds: {
37
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
38
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
39
+ apiResponseTime: 2000,
40
+ bundleSizeLimit: 500000,
41
+ maxConsoleErrors: 0,
42
+ },
43
+ disabledAgents: [],
44
+ disabledAgentSet: new Set<number>(),
45
+ ...overrides,
46
+ };
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Mock fetch
51
+ // ---------------------------------------------------------------------------
52
+
53
+ const fetchMock = vi.fn();
54
+ vi.stubGlobal('fetch', fetchMock);
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // Mock helpers
58
+ // ---------------------------------------------------------------------------
59
+
60
+ const loginMock = vi.fn().mockResolvedValue(undefined);
61
+
62
+ vi.mock('../../helpers/navigation', () => ({
63
+ login: (...args: unknown[]) => loginMock(...args),
64
+ waitForSectionLoad: vi.fn().mockResolvedValue(undefined),
65
+ navigateToSection: vi.fn().mockResolvedValue(undefined),
66
+ switchPortal: vi.fn().mockResolvedValue(undefined),
67
+ }));
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Mock Playwright
71
+ // ---------------------------------------------------------------------------
72
+
73
+ interface MockLocator {
74
+ count: ReturnType<typeof vi.fn>;
75
+ first: ReturnType<typeof vi.fn>;
76
+ nth: ReturnType<typeof vi.fn>;
77
+ click: ReturnType<typeof vi.fn>;
78
+ fill: ReturnType<typeof vi.fn>;
79
+ }
80
+
81
+ function makeMockLocator(countVal = 0): MockLocator {
82
+ const locator: MockLocator = {
83
+ count: vi.fn().mockResolvedValue(countVal),
84
+ first: vi.fn(),
85
+ nth: vi.fn(),
86
+ click: vi.fn().mockResolvedValue(undefined),
87
+ fill: vi.fn().mockResolvedValue(undefined),
88
+ };
89
+ locator.first.mockReturnValue(locator);
90
+ locator.nth.mockReturnValue(locator);
91
+ return locator;
92
+ }
93
+
94
+ interface MockPage {
95
+ close: ReturnType<typeof vi.fn>;
96
+ goto: ReturnType<typeof vi.fn>;
97
+ locator: ReturnType<typeof vi.fn>;
98
+ setViewportSize: ReturnType<typeof vi.fn>;
99
+ evaluate: ReturnType<typeof vi.fn>;
100
+ waitForTimeout: ReturnType<typeof vi.fn>;
101
+ keyboard: { press: ReturnType<typeof vi.fn> };
102
+ }
103
+
104
+ function makeMockPage(): MockPage {
105
+ const defaultLocator = makeMockLocator(0);
106
+ return {
107
+ close: vi.fn().mockResolvedValue(undefined),
108
+ goto: vi.fn().mockResolvedValue(undefined),
109
+ locator: vi.fn().mockReturnValue(defaultLocator),
110
+ setViewportSize: vi.fn().mockResolvedValue(undefined),
111
+ evaluate: vi.fn().mockResolvedValue(false),
112
+ waitForTimeout: vi.fn().mockResolvedValue(undefined),
113
+ keyboard: { press: vi.fn().mockResolvedValue(undefined) },
114
+ };
115
+ }
116
+
117
+ let currentMockPage: MockPage;
118
+ let mockBrowser: { newPage: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
119
+
120
+ vi.mock('playwright', async () => ({
121
+ chromium: {
122
+ launch: vi.fn().mockImplementation(async () => mockBrowser),
123
+ },
124
+ }));
125
+
126
+ // ---------------------------------------------------------------------------
127
+ // Dynamic import AFTER mocks are hoisted
128
+ // ---------------------------------------------------------------------------
129
+
130
+ const { ResponsiveInteractionTesterAgent } = await import('../29-responsive-interaction-tester');
131
+
132
+ function createAgent(
133
+ config: ValidatedConfig = makeConfig(),
134
+ phase: Phase = 'beta',
135
+ ): InstanceType<typeof ResponsiveInteractionTesterAgent> {
136
+ return new ResponsiveInteractionTesterAgent(config, phase, '/tmp/test-run');
137
+ }
138
+
139
+ // ---------------------------------------------------------------------------
140
+ // Tests
141
+ // ---------------------------------------------------------------------------
142
+
143
+ describe('ResponsiveInteractionTesterAgent', () => {
144
+ beforeEach(() => {
145
+ fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
146
+ loginMock.mockReset().mockResolvedValue(undefined);
147
+
148
+ currentMockPage = makeMockPage();
149
+ mockBrowser = {
150
+ newPage: vi.fn().mockResolvedValue(currentMockPage),
151
+ close: vi.fn().mockResolvedValue(undefined),
152
+ };
153
+ });
154
+
155
+ // Identity
156
+ it('has agentId 29 and correct agentName', () => {
157
+ const agent = createAgent();
158
+ expect(agent.agentId).toBe(29);
159
+ expect(agent.agentName).toBe('Responsive Interaction Tester');
160
+ });
161
+
162
+ // preFlight
163
+ it('throws when modules array is empty', async () => {
164
+ const config = makeConfig({ modules: [] });
165
+ const agent = createAgent(config);
166
+ const result = await agent.run();
167
+
168
+ expect(result.status).toBe('failed');
169
+ expect(result.findings[0].description).toContain('module');
170
+ });
171
+
172
+ it('resolves environment in preFlight', async () => {
173
+ const agent = createAgent();
174
+ const result = await agent.run();
175
+ expect(result.findings.every(f => !f.description.includes('preFlight'))).toBe(true);
176
+ });
177
+
178
+ // Viewport is set for each breakpoint
179
+ it('sets viewport size for mobile, tablet, and desktop', async () => {
180
+ const agent = createAgent();
181
+ await agent.run();
182
+
183
+ expect(currentMockPage.setViewportSize).toHaveBeenCalledWith({ width: 375, height: 812 });
184
+ expect(currentMockPage.setViewportSize).toHaveBeenCalledWith({ width: 768, height: 1024 });
185
+ expect(currentMockPage.setViewportSize).toHaveBeenCalledWith({ width: 1280, height: 800 });
186
+ });
187
+
188
+ // Touch target detection
189
+ it('produces medium finding when touch targets are too small', async () => {
190
+ currentMockPage.evaluate.mockImplementation((fn: unknown) => {
191
+ // Return small touch targets for the touch target test
192
+ if (typeof fn === 'function') {
193
+ return Promise.resolve(['button.small (20x20)', 'a.link (30x15)']);
194
+ }
195
+ return Promise.resolve(false);
196
+ });
197
+
198
+ const agent = createAgent();
199
+ const result = await agent.run();
200
+
201
+ const touchFinding = result.findings.find(f => f.description.includes('Touch target too small'));
202
+ expect(touchFinding).toBeDefined();
203
+ expect(touchFinding!.severity).toBe('medium');
204
+ });
205
+
206
+ // Horizontal scroll detection
207
+ it('produces high finding when horizontal scroll detected', async () => {
208
+ // Return true for horizontal scroll check
209
+ currentMockPage.evaluate.mockResolvedValue(true);
210
+
211
+ const agent = createAgent();
212
+ const result = await agent.run();
213
+
214
+ const scrollFinding = result.findings.find(f => f.description.includes('Horizontal scroll'));
215
+ expect(scrollFinding).toBeDefined();
216
+ expect(scrollFinding!.severity).toBe('high');
217
+ });
218
+
219
+ // No horizontal scroll
220
+ it('produces no scroll finding when no horizontal overflow', async () => {
221
+ currentMockPage.evaluate.mockResolvedValue(false);
222
+
223
+ const agent = createAgent();
224
+ const result = await agent.run();
225
+
226
+ const scrollFinding = result.findings.find(f => f.description.includes('Horizontal scroll'));
227
+ expect(scrollFinding).toBeUndefined();
228
+ });
229
+
230
+ // Login failure
231
+ it('produces medium finding and continues when login fails', async () => {
232
+ loginMock.mockRejectedValue(new Error('Invalid credentials'));
233
+
234
+ const agent = createAgent();
235
+ const result = await agent.run();
236
+
237
+ const loginFinding = result.findings.find(f => f.id.includes('login-failed'));
238
+ expect(loginFinding).toBeDefined();
239
+ expect(loginFinding!.severity).toBe('medium');
240
+ });
241
+
242
+ // No credentials
243
+ it('produces medium finding when no credentials configured', async () => {
244
+ const config = makeConfig({
245
+ auth: { loginUrl: 'http://localhost/login', credentials: {} },
246
+ });
247
+ const agent = createAgent(config);
248
+ const result = await agent.run();
249
+
250
+ const noCredsFinding = result.findings.find(f => f.id.includes('no-credentials'));
251
+ expect(noCredsFinding).toBeDefined();
252
+ });
253
+
254
+ // Playwright crash
255
+ it('produces medium finding when Playwright crashes', async () => {
256
+ mockBrowser.newPage.mockRejectedValue(new Error('Browser crashed'));
257
+
258
+ const agent = createAgent();
259
+ const result = await agent.run();
260
+
261
+ const pwFinding = result.findings.find(f => f.id.includes('playwright-failure'));
262
+ expect(pwFinding).toBeDefined();
263
+ expect(pwFinding!.severity).toBe('medium');
264
+ });
265
+
266
+ // Browser closed in finally
267
+ it('closes browser even when an error occurs', async () => {
268
+ mockBrowser.newPage.mockRejectedValue(new Error('Launch error'));
269
+
270
+ const agent = createAgent();
271
+ await agent.run();
272
+
273
+ expect(mockBrowser.close).toHaveBeenCalled();
274
+ });
275
+
276
+ // Tests only first 3 modules
277
+ it('limits testing to first 3 modules', async () => {
278
+ const config = makeConfig({
279
+ modules: [
280
+ { id: 'mod1', route: '/mod1', sidebarIcon: true },
281
+ { id: 'mod2', route: '/mod2', sidebarIcon: true },
282
+ { id: 'mod3', route: '/mod3', sidebarIcon: true },
283
+ { id: 'mod4', route: '/mod4', sidebarIcon: true },
284
+ { id: 'mod5', route: '/mod5', sidebarIcon: true },
285
+ ],
286
+ });
287
+
288
+ const agent = createAgent(config);
289
+ await agent.run();
290
+
291
+ // 3 breakpoints * 3 modules = 9 module navigations
292
+ // Should NOT navigate to mod4 or mod5
293
+ const gotoArgs = currentMockPage.goto.mock.calls.map((c: unknown[]) => c[0] as string);
294
+ expect(gotoArgs.filter((u: string) => u.includes('mod4'))).toHaveLength(0);
295
+ expect(gotoArgs.filter((u: string) => u.includes('mod5'))).toHaveLength(0);
296
+ });
297
+ });
@@ -0,0 +1,328 @@
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
+ staff: { email: 'staff@test.com', password: 'secret2' },
18
+ },
19
+ },
20
+ modules: [
21
+ { id: 'dashboard', route: '/dashboard', sidebarIcon: true },
22
+ { id: 'tasks', route: '/tasks', sidebarIcon: true },
23
+ ],
24
+ environments: {
25
+ alpha: { baseUrl: 'http://localhost:3000', seed: false },
26
+ },
27
+ portals: [],
28
+ breakpoints: [],
29
+ integrations: [],
30
+ workers: [],
31
+ workflows: [],
32
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
33
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
34
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
35
+ agentClassification: { blocking: [], advisory: [30] },
36
+ thresholds: {
37
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
38
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
39
+ apiResponseTime: 2000,
40
+ bundleSizeLimit: 500000,
41
+ maxConsoleErrors: 0,
42
+ },
43
+ disabledAgents: [],
44
+ disabledAgentSet: new Set<number>(),
45
+ ...overrides,
46
+ };
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // Mock fetch
51
+ // ---------------------------------------------------------------------------
52
+
53
+ const fetchMock = vi.fn();
54
+ vi.stubGlobal('fetch', fetchMock);
55
+
56
+ // ---------------------------------------------------------------------------
57
+ // Mock helpers
58
+ // ---------------------------------------------------------------------------
59
+
60
+ const loginMock = vi.fn().mockResolvedValue(undefined);
61
+
62
+ vi.mock('../../helpers/navigation', () => ({
63
+ login: (...args: unknown[]) => loginMock(...args),
64
+ waitForSectionLoad: vi.fn().mockResolvedValue(undefined),
65
+ navigateToSection: vi.fn().mockResolvedValue(undefined),
66
+ switchPortal: vi.fn().mockResolvedValue(undefined),
67
+ }));
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Mock Playwright
71
+ // ---------------------------------------------------------------------------
72
+
73
+ interface MockLocator {
74
+ count: ReturnType<typeof vi.fn>;
75
+ first: ReturnType<typeof vi.fn>;
76
+ nth: ReturnType<typeof vi.fn>;
77
+ innerText: ReturnType<typeof vi.fn>;
78
+ }
79
+
80
+ function makeMockLocator(countVal = 0): MockLocator {
81
+ const locator: MockLocator = {
82
+ count: vi.fn().mockResolvedValue(countVal),
83
+ first: vi.fn(),
84
+ nth: vi.fn(),
85
+ innerText: vi.fn().mockResolvedValue('some content'),
86
+ };
87
+ locator.first.mockReturnValue(locator);
88
+ locator.nth.mockReturnValue(locator);
89
+ return locator;
90
+ }
91
+
92
+ interface MockPage {
93
+ close: ReturnType<typeof vi.fn>;
94
+ goto: ReturnType<typeof vi.fn>;
95
+ url: ReturnType<typeof vi.fn>;
96
+ locator: ReturnType<typeof vi.fn>;
97
+ }
98
+
99
+ function makeMockPage(urlValue = 'http://localhost:3000/dashboard'): MockPage {
100
+ const defaultLocator = makeMockLocator(0);
101
+ return {
102
+ close: vi.fn().mockResolvedValue(undefined),
103
+ goto: vi.fn().mockResolvedValue(undefined),
104
+ url: vi.fn().mockReturnValue(urlValue),
105
+ locator: vi.fn().mockReturnValue(defaultLocator),
106
+ };
107
+ }
108
+
109
+ let mockPage1: MockPage;
110
+ let mockPage2: MockPage;
111
+ let mockContext1: {
112
+ newPage: ReturnType<typeof vi.fn>;
113
+ close: ReturnType<typeof vi.fn>;
114
+ };
115
+ let mockContext2: {
116
+ newPage: ReturnType<typeof vi.fn>;
117
+ close: ReturnType<typeof vi.fn>;
118
+ };
119
+ let mockBrowser: {
120
+ newContext: ReturnType<typeof vi.fn>;
121
+ close: ReturnType<typeof vi.fn>;
122
+ };
123
+
124
+ vi.mock('playwright', async () => ({
125
+ chromium: {
126
+ launch: vi.fn().mockImplementation(async () => mockBrowser),
127
+ },
128
+ }));
129
+
130
+ // ---------------------------------------------------------------------------
131
+ // Dynamic import AFTER mocks are hoisted
132
+ // ---------------------------------------------------------------------------
133
+
134
+ const { MultiUserScenarioTesterAgent } = await import('../30-multi-user-scenario-tester');
135
+
136
+ function createAgent(
137
+ config: ValidatedConfig = makeConfig(),
138
+ phase: Phase = 'beta',
139
+ ): InstanceType<typeof MultiUserScenarioTesterAgent> {
140
+ return new MultiUserScenarioTesterAgent(config, phase, '/tmp/test-run');
141
+ }
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Tests
145
+ // ---------------------------------------------------------------------------
146
+
147
+ describe('MultiUserScenarioTesterAgent', () => {
148
+ beforeEach(() => {
149
+ fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
150
+ loginMock.mockReset().mockResolvedValue(undefined);
151
+
152
+ mockPage1 = makeMockPage('http://localhost:3000/dashboard');
153
+ mockPage2 = makeMockPage('http://localhost:3000/dashboard');
154
+ mockPage1.locator.mockReturnValue(makeMockLocator(0));
155
+ mockPage1.locator.mockImplementation(() => {
156
+ const loc = makeMockLocator(0);
157
+ loc.innerText.mockResolvedValue('dashboard content');
158
+ return loc;
159
+ });
160
+ mockPage2.locator.mockImplementation(() => {
161
+ const loc = makeMockLocator(0);
162
+ loc.innerText.mockResolvedValue('dashboard content');
163
+ return loc;
164
+ });
165
+
166
+ mockContext1 = {
167
+ newPage: vi.fn().mockResolvedValue(mockPage1),
168
+ close: vi.fn().mockResolvedValue(undefined),
169
+ };
170
+ mockContext2 = {
171
+ newPage: vi.fn().mockResolvedValue(mockPage2),
172
+ close: vi.fn().mockResolvedValue(undefined),
173
+ };
174
+
175
+ let contextCallCount = 0;
176
+ mockBrowser = {
177
+ newContext: vi.fn().mockImplementation(async () => {
178
+ contextCallCount++;
179
+ return contextCallCount === 1 ? mockContext1 : mockContext2;
180
+ }),
181
+ close: vi.fn().mockResolvedValue(undefined),
182
+ };
183
+ });
184
+
185
+ // Identity
186
+ it('has agentId 30 and correct agentName', () => {
187
+ const agent = createAgent();
188
+ expect(agent.agentId).toBe(30);
189
+ expect(agent.agentName).toBe('Multi-User Scenario Tester');
190
+ });
191
+
192
+ // preFlight
193
+ it('throws when no credentials configured', async () => {
194
+ const config = makeConfig({
195
+ auth: { loginUrl: 'http://localhost/login', credentials: {} },
196
+ });
197
+ const agent = createAgent(config);
198
+ const result = await agent.run();
199
+
200
+ expect(result.status).toBe('failed');
201
+ expect(result.findings[0].description).toContain('credential');
202
+ });
203
+
204
+ it('resolves environment in preFlight', async () => {
205
+ const agent = createAgent();
206
+ const result = await agent.run();
207
+ expect(result.findings.every(f => !f.description.includes('preFlight'))).toBe(true);
208
+ });
209
+
210
+ // Two browser contexts created
211
+ it('creates two browser contexts for multi-user testing', async () => {
212
+ const agent = createAgent();
213
+ await agent.run();
214
+
215
+ expect(mockBrowser.newContext).toHaveBeenCalledTimes(2);
216
+ });
217
+
218
+ // Login failure for user 1
219
+ it('produces medium finding when user 1 login fails', async () => {
220
+ loginMock.mockRejectedValueOnce(new Error('Auth failed'));
221
+
222
+ const agent = createAgent();
223
+ const result = await agent.run();
224
+
225
+ const loginFinding = result.findings.find(f => f.id.includes('user1-login-failed'));
226
+ expect(loginFinding).toBeDefined();
227
+ expect(loginFinding!.severity).toBe('medium');
228
+ });
229
+
230
+ // Login failure for user 2
231
+ it('produces medium finding when user 2 login fails', async () => {
232
+ loginMock
233
+ .mockResolvedValueOnce(undefined) // user1 succeeds
234
+ .mockRejectedValueOnce(new Error('Auth failed')); // user2 fails
235
+
236
+ const agent = createAgent();
237
+ const result = await agent.run();
238
+
239
+ const loginFinding = result.findings.find(f => f.id.includes('user2-login-failed'));
240
+ expect(loginFinding).toBeDefined();
241
+ });
242
+
243
+ // Returns early when login fails
244
+ it('returns early with finding when login is incomplete', async () => {
245
+ loginMock.mockRejectedValue(new Error('Auth failed'));
246
+
247
+ const agent = createAgent();
248
+ const result = await agent.run();
249
+
250
+ const incompleteFinding = result.findings.find(f => f.id.includes('login-incomplete'));
251
+ expect(incompleteFinding).toBeDefined();
252
+ });
253
+
254
+ // Concurrent sessions — both stay logged in
255
+ it('passes when both users remain logged in concurrently', async () => {
256
+ mockPage1.url.mockReturnValue('http://localhost:3000/dashboard');
257
+ mockPage2.url.mockReturnValue('http://localhost:3000/dashboard');
258
+
259
+ const agent = createAgent();
260
+ const result = await agent.run();
261
+
262
+ const conflictFinding = result.findings.find(f => f.id.includes('concurrent-session-conflict'));
263
+ expect(conflictFinding).toBeUndefined();
264
+ });
265
+
266
+ // Concurrent sessions — one gets redirected
267
+ it('produces high finding when a user gets redirected to login', async () => {
268
+ mockPage1.url.mockReturnValue('http://localhost:3000/login');
269
+ mockPage2.url.mockReturnValue('http://localhost:3000/dashboard');
270
+
271
+ const agent = createAgent();
272
+ const result = await agent.run();
273
+
274
+ const conflictFinding = result.findings.find(f => f.id.includes('concurrent-session-conflict'));
275
+ expect(conflictFinding).toBeDefined();
276
+ expect(conflictFinding!.severity).toBe('high');
277
+ });
278
+
279
+ // Playwright crash
280
+ it('produces medium finding when Playwright crashes', async () => {
281
+ mockBrowser.newContext.mockRejectedValue(new Error('Browser crashed'));
282
+
283
+ const agent = createAgent();
284
+ const result = await agent.run();
285
+
286
+ const pwFinding = result.findings.find(f => f.id.includes('playwright-failure'));
287
+ expect(pwFinding).toBeDefined();
288
+ expect(pwFinding!.severity).toBe('medium');
289
+ });
290
+
291
+ // Browser and contexts closed in finally
292
+ it('closes contexts and browser even when an error occurs', async () => {
293
+ // Let contexts be created but fail on page creation for context2
294
+ const failContext = {
295
+ newPage: vi.fn().mockRejectedValue(new Error('Page error')),
296
+ close: vi.fn().mockResolvedValue(undefined),
297
+ };
298
+
299
+ let ctxCount = 0;
300
+ mockBrowser.newContext.mockImplementation(async () => {
301
+ ctxCount++;
302
+ return ctxCount === 1 ? mockContext1 : failContext;
303
+ });
304
+
305
+ const agent = createAgent();
306
+ await agent.run();
307
+
308
+ expect(mockBrowser.close).toHaveBeenCalled();
309
+ });
310
+
311
+ // Works with single credential set
312
+ it('works with single credential set using same creds for both users', async () => {
313
+ const config = makeConfig({
314
+ auth: {
315
+ loginUrl: 'http://localhost/login',
316
+ credentials: {
317
+ admin: { email: 'admin@test.com', password: 'secret' },
318
+ },
319
+ },
320
+ });
321
+
322
+ const agent = createAgent(config);
323
+ const result = await agent.run();
324
+
325
+ // login should be called twice (once per user)
326
+ expect(loginMock).toHaveBeenCalledTimes(2);
327
+ });
328
+ });