@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,294 @@
1
+ import type { Browser, Page } from 'playwright';
2
+ import type { Finding } from '../core/types';
3
+ import { BaseAgent } from './base-agent';
4
+ import { resolveEnvironment } from '../helpers/env-resolver';
5
+ import { login } from '../helpers/navigation';
6
+
7
+ const NAV_TIMEOUT_MS = 5_000;
8
+
9
+ export class SeoAuditorAgent extends BaseAgent {
10
+ readonly agentId = 46;
11
+ readonly agentName = 'SEO Auditor';
12
+ private baseUrl = '';
13
+
14
+ protected async preFlight(): Promise<void> {
15
+ if (!this.config.modules || this.config.modules.length === 0) {
16
+ throw new Error('SeoAuditorAgent requires at least one module in config.modules');
17
+ }
18
+ const { env } = await resolveEnvironment(this.config, this.phase);
19
+ this.baseUrl = env.baseUrl;
20
+ }
21
+
22
+ protected async execute(): Promise<Finding[]> {
23
+ const findings: Finding[] = [];
24
+ const rawLoginUrl = this.config.auth.loginUrl ?? '/login';
25
+ const loginUrl = rawLoginUrl.startsWith('http') ? rawLoginUrl : `${this.baseUrl}${rawLoginUrl}`;
26
+ const credentials =
27
+ this.config.auth.credentials?.['admin'] ??
28
+ Object.values(this.config.auth.credentials ?? {})[0];
29
+
30
+ const { chromium } = await import('playwright');
31
+ let browser: Browser | null = null;
32
+
33
+ try {
34
+ browser = await chromium.launch({ headless: true });
35
+ const page = await browser.newPage();
36
+
37
+ if (credentials) {
38
+ try {
39
+ await login(page, credentials, loginUrl);
40
+ } catch (loginError) {
41
+ findings.push({
42
+ id: `${this.agentId}-login-failed`,
43
+ type: 'infra-issue',
44
+ severity: 'medium',
45
+ agentId: this.agentId,
46
+ module: 'seo-auditor',
47
+ description: `Login failed — continuing anonymously: ${loginError instanceof Error ? loginError.message.split('\n')[0] : String(loginError)}`,
48
+ });
49
+ }
50
+ }
51
+
52
+ for (const mod of this.config.modules) {
53
+ const moduleFindings = await this.auditModule(page, mod.id, mod.route);
54
+ findings.push(...moduleFindings);
55
+ }
56
+
57
+ // Check robots.txt and sitemap.xml
58
+ const robotsFindings = await this.checkRobotsAndSitemap();
59
+ findings.push(...robotsFindings);
60
+
61
+ await page.close().catch(() => undefined);
62
+ } catch (playwrightError) {
63
+ findings.push({
64
+ id: `${this.agentId}-playwright-failure`,
65
+ type: 'infra-issue',
66
+ severity: 'medium',
67
+ agentId: this.agentId,
68
+ module: 'seo-auditor',
69
+ description: `Playwright execution failed: ${playwrightError instanceof Error ? playwrightError.message.split('\n')[0] : String(playwrightError)}`,
70
+ });
71
+ } finally {
72
+ if (browser) await browser.close().catch(() => undefined);
73
+ }
74
+
75
+ return findings;
76
+ }
77
+
78
+ private async auditModule(
79
+ page: Page,
80
+ moduleId: string,
81
+ moduleRoute: string,
82
+ ): Promise<Finding[]> {
83
+ const findings: Finding[] = [];
84
+ const fullUrl = moduleRoute.startsWith('http')
85
+ ? moduleRoute
86
+ : `${this.baseUrl}${moduleRoute}`;
87
+
88
+ try {
89
+ await Promise.race([
90
+ page.goto(fullUrl, { waitUntil: 'domcontentloaded' }),
91
+ new Promise((_, reject) =>
92
+ setTimeout(() => reject(new Error('Navigation timeout')), NAV_TIMEOUT_MS),
93
+ ),
94
+ ]);
95
+ } catch {
96
+ findings.push({
97
+ id: `${this.agentId}-nav-failed-${moduleId}`,
98
+ type: 'test-bug',
99
+ severity: 'medium',
100
+ agentId: this.agentId,
101
+ module: moduleId,
102
+ description: `Failed to navigate to module ${moduleId} at ${moduleRoute}`,
103
+ });
104
+ return findings;
105
+ }
106
+
107
+ // Check <title>
108
+ try {
109
+ const title = await page.evaluate(() => document.title);
110
+ if (!title || title.trim() === '') {
111
+ findings.push({
112
+ id: `${this.agentId}-no-title-${moduleId}`,
113
+ type: 'test-bug',
114
+ severity: 'medium',
115
+ agentId: this.agentId,
116
+ module: moduleId,
117
+ description: `Module ${moduleId} has no <title> tag`,
118
+ });
119
+ } else if (title.length > 60) {
120
+ findings.push({
121
+ id: `${this.agentId}-long-title-${moduleId}`,
122
+ type: 'test-bug',
123
+ severity: 'low',
124
+ agentId: this.agentId,
125
+ module: moduleId,
126
+ description: `Module ${moduleId} title is ${title.length} chars (recommended <60)`,
127
+ });
128
+ }
129
+ } catch {
130
+ // Could not evaluate title
131
+ }
132
+
133
+ // Check <meta name="description">
134
+ try {
135
+ const metaDesc = await page.evaluate(() => {
136
+ const el = document.querySelector('meta[name="description"]');
137
+ return el?.getAttribute('content') ?? null;
138
+ });
139
+ if (!metaDesc) {
140
+ findings.push({
141
+ id: `${this.agentId}-no-meta-desc-${moduleId}`,
142
+ type: 'test-bug',
143
+ severity: 'medium',
144
+ agentId: this.agentId,
145
+ module: moduleId,
146
+ description: `Module ${moduleId} has no <meta name="description">`,
147
+ });
148
+ } else if (metaDesc.length > 160) {
149
+ findings.push({
150
+ id: `${this.agentId}-long-meta-desc-${moduleId}`,
151
+ type: 'test-bug',
152
+ severity: 'low',
153
+ agentId: this.agentId,
154
+ module: moduleId,
155
+ description: `Module ${moduleId} meta description is ${metaDesc.length} chars (recommended <160)`,
156
+ });
157
+ }
158
+ } catch {
159
+ // Could not evaluate meta description
160
+ }
161
+
162
+ // Check <link rel="canonical">
163
+ try {
164
+ const canonical = await page.evaluate(() => {
165
+ const el = document.querySelector('link[rel="canonical"]');
166
+ return el?.getAttribute('href') ?? null;
167
+ });
168
+ if (!canonical) {
169
+ findings.push({
170
+ id: `${this.agentId}-no-canonical-${moduleId}`,
171
+ type: 'test-bug',
172
+ severity: 'low',
173
+ agentId: this.agentId,
174
+ module: moduleId,
175
+ description: `Module ${moduleId} has no <link rel="canonical">`,
176
+ });
177
+ }
178
+ } catch {
179
+ // Could not evaluate canonical
180
+ }
181
+
182
+ // Check <h1> — exactly one
183
+ try {
184
+ const h1Count = await Promise.race([
185
+ page.locator('h1').count(),
186
+ new Promise<number>((_, reject) =>
187
+ setTimeout(() => reject(new Error('Timeout')), NAV_TIMEOUT_MS),
188
+ ),
189
+ ]) as number;
190
+
191
+ if (h1Count === 0) {
192
+ findings.push({
193
+ id: `${this.agentId}-no-h1-${moduleId}`,
194
+ type: 'test-bug',
195
+ severity: 'medium',
196
+ agentId: this.agentId,
197
+ module: moduleId,
198
+ description: `Module ${moduleId} has no <h1> tag`,
199
+ });
200
+ } else if (h1Count > 1) {
201
+ findings.push({
202
+ id: `${this.agentId}-multiple-h1-${moduleId}`,
203
+ type: 'test-bug',
204
+ severity: 'medium',
205
+ agentId: this.agentId,
206
+ module: moduleId,
207
+ description: `Module ${moduleId} has ${h1Count} <h1> tags (should have exactly 1)`,
208
+ });
209
+ }
210
+ } catch {
211
+ // Could not count h1 tags
212
+ }
213
+
214
+ // Check og: tags
215
+ try {
216
+ const ogTitle = await page.evaluate(() => {
217
+ const el = document.querySelector('meta[property="og:title"]');
218
+ return el?.getAttribute('content') ?? null;
219
+ });
220
+ if (!ogTitle) {
221
+ findings.push({
222
+ id: `${this.agentId}-no-og-title-${moduleId}`,
223
+ type: 'test-bug',
224
+ severity: 'low',
225
+ agentId: this.agentId,
226
+ module: moduleId,
227
+ description: `Module ${moduleId} has no og:title meta tag`,
228
+ });
229
+ }
230
+ } catch {
231
+ // Could not evaluate og tags
232
+ }
233
+
234
+ return findings;
235
+ }
236
+
237
+ private async checkRobotsAndSitemap(): Promise<Finding[]> {
238
+ const findings: Finding[] = [];
239
+
240
+ // Check robots.txt
241
+ try {
242
+ const response = await fetch(`${this.baseUrl}/robots.txt`, {
243
+ signal: AbortSignal.timeout(NAV_TIMEOUT_MS),
244
+ });
245
+ if (response.status !== 200) {
246
+ findings.push({
247
+ id: `${this.agentId}-no-robots-txt`,
248
+ type: 'test-bug',
249
+ severity: 'medium',
250
+ agentId: this.agentId,
251
+ module: 'seo-auditor',
252
+ description: `robots.txt not found (status ${response.status})`,
253
+ });
254
+ }
255
+ } catch {
256
+ findings.push({
257
+ id: `${this.agentId}-robots-txt-error`,
258
+ type: 'infra-issue',
259
+ severity: 'low',
260
+ agentId: this.agentId,
261
+ module: 'seo-auditor',
262
+ description: 'Failed to fetch robots.txt',
263
+ });
264
+ }
265
+
266
+ // Check sitemap.xml
267
+ try {
268
+ const response = await fetch(`${this.baseUrl}/sitemap.xml`, {
269
+ signal: AbortSignal.timeout(NAV_TIMEOUT_MS),
270
+ });
271
+ if (response.status !== 200) {
272
+ findings.push({
273
+ id: `${this.agentId}-no-sitemap-xml`,
274
+ type: 'test-bug',
275
+ severity: 'medium',
276
+ agentId: this.agentId,
277
+ module: 'seo-auditor',
278
+ description: `sitemap.xml not found (status ${response.status})`,
279
+ });
280
+ }
281
+ } catch {
282
+ findings.push({
283
+ id: `${this.agentId}-sitemap-xml-error`,
284
+ type: 'infra-issue',
285
+ severity: 'low',
286
+ agentId: this.agentId,
287
+ module: 'seo-auditor',
288
+ description: 'Failed to fetch sitemap.xml',
289
+ });
290
+ }
291
+
292
+ return findings;
293
+ }
294
+ }
@@ -0,0 +1,232 @@
1
+ import type { Browser, Page } from 'playwright';
2
+ import type { Finding } from '../core/types';
3
+ import { BaseAgent } from './base-agent';
4
+ import { resolveEnvironment } from '../helpers/env-resolver';
5
+ import { login } from '../helpers/navigation';
6
+
7
+ const NAV_TIMEOUT_MS = 5_000;
8
+
9
+ export class SocialPreviewTesterAgent extends BaseAgent {
10
+ readonly agentId = 47;
11
+ readonly agentName = 'Social Preview Tester';
12
+ private baseUrl = '';
13
+
14
+ protected async preFlight(): Promise<void> {
15
+ if (!this.config.modules || this.config.modules.length === 0) {
16
+ throw new Error('SocialPreviewTesterAgent requires at least one module in config.modules');
17
+ }
18
+ const { env } = await resolveEnvironment(this.config, this.phase);
19
+ this.baseUrl = env.baseUrl;
20
+ }
21
+
22
+ protected async execute(): Promise<Finding[]> {
23
+ const findings: Finding[] = [];
24
+ const rawLoginUrl = this.config.auth.loginUrl ?? '/login';
25
+ const loginUrl = rawLoginUrl.startsWith('http') ? rawLoginUrl : `${this.baseUrl}${rawLoginUrl}`;
26
+ const credentials =
27
+ this.config.auth.credentials?.['admin'] ??
28
+ Object.values(this.config.auth.credentials ?? {})[0];
29
+
30
+ const { chromium } = await import('playwright');
31
+ let browser: Browser | null = null;
32
+
33
+ try {
34
+ browser = await chromium.launch({ headless: true });
35
+ const page = await browser.newPage();
36
+
37
+ if (credentials) {
38
+ try {
39
+ await login(page, credentials, loginUrl);
40
+ } catch (loginError) {
41
+ findings.push({
42
+ id: `${this.agentId}-login-failed`,
43
+ type: 'infra-issue',
44
+ severity: 'medium',
45
+ agentId: this.agentId,
46
+ module: 'social-preview',
47
+ description: `Login failed — continuing anonymously: ${loginError instanceof Error ? loginError.message.split('\n')[0] : String(loginError)}`,
48
+ });
49
+ }
50
+ }
51
+
52
+ for (const mod of this.config.modules) {
53
+ const moduleFindings = await this.testModulePreview(page, mod.id, mod.route);
54
+ findings.push(...moduleFindings);
55
+ }
56
+
57
+ await page.close().catch(() => undefined);
58
+ } catch (playwrightError) {
59
+ findings.push({
60
+ id: `${this.agentId}-playwright-failure`,
61
+ type: 'infra-issue',
62
+ severity: 'medium',
63
+ agentId: this.agentId,
64
+ module: 'social-preview',
65
+ description: `Playwright execution failed: ${playwrightError instanceof Error ? playwrightError.message.split('\n')[0] : String(playwrightError)}`,
66
+ });
67
+ } finally {
68
+ if (browser) await browser.close().catch(() => undefined);
69
+ }
70
+
71
+ return findings;
72
+ }
73
+
74
+ private async testModulePreview(
75
+ page: Page,
76
+ moduleId: string,
77
+ moduleRoute: string,
78
+ ): Promise<Finding[]> {
79
+ const findings: Finding[] = [];
80
+ const fullUrl = moduleRoute.startsWith('http')
81
+ ? moduleRoute
82
+ : `${this.baseUrl}${moduleRoute}`;
83
+
84
+ try {
85
+ await Promise.race([
86
+ page.goto(fullUrl, { waitUntil: 'domcontentloaded' }),
87
+ new Promise((_, reject) =>
88
+ setTimeout(() => reject(new Error('Navigation timeout')), NAV_TIMEOUT_MS),
89
+ ),
90
+ ]);
91
+ } catch {
92
+ findings.push({
93
+ id: `${this.agentId}-nav-failed-${moduleId}`,
94
+ type: 'test-bug',
95
+ severity: 'medium',
96
+ agentId: this.agentId,
97
+ module: moduleId,
98
+ description: `Failed to navigate to module ${moduleId} at ${moduleRoute}`,
99
+ });
100
+ return findings;
101
+ }
102
+
103
+ // Check og:image
104
+ try {
105
+ const ogImage = await page.evaluate(() => {
106
+ const el = document.querySelector('meta[property="og:image"]');
107
+ return el?.getAttribute('content') ?? null;
108
+ });
109
+
110
+ if (!ogImage) {
111
+ findings.push({
112
+ id: `${this.agentId}-no-og-image-${moduleId}`,
113
+ type: 'test-bug',
114
+ severity: 'medium',
115
+ agentId: this.agentId,
116
+ module: moduleId,
117
+ description: `Module ${moduleId} has no og:image meta tag`,
118
+ });
119
+ } else {
120
+ // Verify image URL returns 200
121
+ try {
122
+ const imageUrl = ogImage.startsWith('http') ? ogImage : `${this.baseUrl}${ogImage}`;
123
+ const response = await fetch(imageUrl, {
124
+ method: 'HEAD',
125
+ signal: AbortSignal.timeout(NAV_TIMEOUT_MS),
126
+ });
127
+ if (response.status !== 200) {
128
+ findings.push({
129
+ id: `${this.agentId}-og-image-broken-${moduleId}`,
130
+ type: 'test-bug',
131
+ severity: 'medium',
132
+ agentId: this.agentId,
133
+ module: moduleId,
134
+ description: `og:image URL returns ${response.status} for module ${moduleId}`,
135
+ });
136
+ }
137
+ } catch {
138
+ findings.push({
139
+ id: `${this.agentId}-og-image-unreachable-${moduleId}`,
140
+ type: 'test-bug',
141
+ severity: 'medium',
142
+ agentId: this.agentId,
143
+ module: moduleId,
144
+ description: `og:image URL is unreachable for module ${moduleId}`,
145
+ });
146
+ }
147
+ }
148
+ } catch {
149
+ // Could not evaluate og:image
150
+ }
151
+
152
+ // Check og:title
153
+ try {
154
+ const ogTitle = await page.evaluate(() => {
155
+ const el = document.querySelector('meta[property="og:title"]');
156
+ return el?.getAttribute('content') ?? null;
157
+ });
158
+ if (!ogTitle) {
159
+ findings.push({
160
+ id: `${this.agentId}-no-og-title-${moduleId}`,
161
+ type: 'test-bug',
162
+ severity: 'medium',
163
+ agentId: this.agentId,
164
+ module: moduleId,
165
+ description: `Module ${moduleId} has no og:title meta tag`,
166
+ });
167
+ } else if (ogTitle.length > 60) {
168
+ findings.push({
169
+ id: `${this.agentId}-long-og-title-${moduleId}`,
170
+ type: 'test-bug',
171
+ severity: 'low',
172
+ agentId: this.agentId,
173
+ module: moduleId,
174
+ description: `Module ${moduleId} og:title is ${ogTitle.length} chars (recommended <60)`,
175
+ });
176
+ }
177
+ } catch {
178
+ // Could not evaluate og:title
179
+ }
180
+
181
+ // Check og:description
182
+ try {
183
+ const ogDesc = await page.evaluate(() => {
184
+ const el = document.querySelector('meta[property="og:description"]');
185
+ return el?.getAttribute('content') ?? null;
186
+ });
187
+ if (!ogDesc) {
188
+ findings.push({
189
+ id: `${this.agentId}-no-og-desc-${moduleId}`,
190
+ type: 'test-bug',
191
+ severity: 'low',
192
+ agentId: this.agentId,
193
+ module: moduleId,
194
+ description: `Module ${moduleId} has no og:description meta tag`,
195
+ });
196
+ } else if (ogDesc.length > 160) {
197
+ findings.push({
198
+ id: `${this.agentId}-long-og-desc-${moduleId}`,
199
+ type: 'test-bug',
200
+ severity: 'low',
201
+ agentId: this.agentId,
202
+ module: moduleId,
203
+ description: `Module ${moduleId} og:description is ${ogDesc.length} chars (recommended <160)`,
204
+ });
205
+ }
206
+ } catch {
207
+ // Could not evaluate og:description
208
+ }
209
+
210
+ // Check twitter:card
211
+ try {
212
+ const twitterCard = await page.evaluate(() => {
213
+ const el = document.querySelector('meta[name="twitter:card"]');
214
+ return el?.getAttribute('content') ?? null;
215
+ });
216
+ if (!twitterCard) {
217
+ findings.push({
218
+ id: `${this.agentId}-no-twitter-card-${moduleId}`,
219
+ type: 'test-bug',
220
+ severity: 'low',
221
+ agentId: this.agentId,
222
+ module: moduleId,
223
+ description: `Module ${moduleId} has no twitter:card meta tag`,
224
+ });
225
+ }
226
+ } catch {
227
+ // Could not evaluate twitter:card
228
+ }
229
+
230
+ return findings;
231
+ }
232
+ }