@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.
- package/CHANGELOG.md +53 -1
- package/README.md +72 -7
- package/agents/15-regression-sentinel.ts +3 -2
- package/agents/24-signup-onboarding-tester.ts +429 -0
- package/agents/25-crud-flow-tester.ts +302 -0
- package/agents/26-form-validator.ts +297 -0
- package/agents/27-search-filter-tester.ts +326 -0
- package/agents/28-navigation-routing-tester.ts +425 -0
- package/agents/29-responsive-interaction-tester.ts +350 -0
- package/agents/30-multi-user-scenario-tester.ts +319 -0
- package/agents/31-load-tester.ts +134 -0
- package/agents/32-memory-leak-detector.ts +194 -0
- package/agents/33-bundle-analyzer.ts +132 -0
- package/agents/34-xss-scanner.ts +191 -0
- package/agents/35-csrf-tester.ts +82 -0
- package/agents/36-auth-fuzzer.ts +194 -0
- package/agents/37-dependency-scanner.ts +176 -0
- package/agents/38-secrets-scanner.ts +137 -0
- package/agents/39-api-contract-tester.ts +199 -0
- package/agents/40-rate-limit-tester.ts +94 -0
- package/agents/41-api-pagination-tester.ts +97 -0
- package/agents/42-graphql-tester.ts +222 -0
- package/agents/43-data-consistency-checker.ts +205 -0
- package/agents/44-backup-recovery-tester.ts +152 -0
- package/agents/45-data-privacy-scanner.ts +125 -0
- package/agents/46-seo-auditor.ts +294 -0
- package/agents/47-social-preview-tester.ts +232 -0
- package/agents/48-lighthouse-auditor.ts +213 -0
- package/agents/49-i18n-tester.ts +198 -0
- package/agents/50-timezone-tester.ts +173 -0
- package/agents/51-error-recovery-tester.ts +155 -0
- package/agents/52-offline-mode-tester.ts +180 -0
- package/agents/53-graceful-degradation-tester.ts +156 -0
- package/agents/54-websocket-tester.ts +151 -0
- package/agents/55-realtime-sync-tester.ts +194 -0
- package/agents/56-file-upload-tester.ts +194 -0
- package/agents/57-export-tester.ts +174 -0
- package/agents/58-payment-flow-tester.ts +183 -0
- package/agents/59-ssl-tls-auditor.ts +141 -0
- package/agents/60-dns-cdn-tester.ts +117 -0
- package/agents/61-docker-health-checker.ts +111 -0
- package/agents/62-env-config-validator.ts +152 -0
- package/agents/63-log-quality-auditor.ts +136 -0
- package/agents/64-analytics-tracker-tester.ts +165 -0
- package/agents/65-gdpr-compliance-tester.ts +215 -0
- package/agents/66-soc2-control-validator.ts +210 -0
- package/agents/67-wcag-aaa-tester.ts +241 -0
- package/agents/68-dead-code-detector.ts +135 -0
- package/agents/69-type-safety-auditor.ts +164 -0
- package/agents/70-complexity-analyzer.ts +179 -0
- package/agents/__tests__/24-signup-onboarding-tester.test.ts +274 -0
- package/agents/__tests__/25-crud-flow-tester.test.ts +322 -0
- package/agents/__tests__/26-form-validator.test.ts +345 -0
- package/agents/__tests__/27-search-filter-tester.test.ts +311 -0
- package/agents/__tests__/28-navigation-routing-tester.test.ts +328 -0
- package/agents/__tests__/29-responsive-interaction-tester.test.ts +297 -0
- package/agents/__tests__/30-multi-user-scenario-tester.test.ts +328 -0
- package/agents/__tests__/31-load-tester.test.ts +189 -0
- package/agents/__tests__/32-memory-leak-detector.test.ts +251 -0
- package/agents/__tests__/33-bundle-analyzer.test.ts +237 -0
- package/agents/__tests__/34-xss-scanner.test.ts +258 -0
- package/agents/__tests__/35-csrf-tester.test.ts +200 -0
- package/agents/__tests__/36-auth-fuzzer.test.ts +214 -0
- package/agents/__tests__/37-dependency-scanner.test.ts +266 -0
- package/agents/__tests__/38-secrets-scanner.test.ts +224 -0
- package/agents/__tests__/39-api-contract-tester.test.ts +312 -0
- package/agents/__tests__/40-rate-limit-tester.test.ts +192 -0
- package/agents/__tests__/41-api-pagination-tester.test.ts +198 -0
- package/agents/__tests__/42-graphql-tester.test.ts +252 -0
- package/agents/__tests__/43-data-consistency-checker.test.ts +232 -0
- package/agents/__tests__/44-backup-recovery-tester.test.ts +222 -0
- package/agents/__tests__/45-data-privacy-scanner.test.ts +223 -0
- package/agents/__tests__/46-seo-auditor.test.ts +261 -0
- package/agents/__tests__/47-social-preview-tester.test.ts +245 -0
- package/agents/__tests__/48-lighthouse-auditor.test.ts +276 -0
- package/agents/__tests__/49-i18n-tester.test.ts +201 -0
- package/agents/__tests__/50-timezone-tester.test.ts +172 -0
- package/agents/__tests__/51-error-recovery-tester.test.ts +162 -0
- package/agents/__tests__/52-offline-mode-tester.test.ts +164 -0
- package/agents/__tests__/53-graceful-degradation-tester.test.ts +168 -0
- package/agents/__tests__/54-websocket-tester.test.ts +157 -0
- package/agents/__tests__/55-realtime-sync-tester.test.ts +181 -0
- package/agents/__tests__/56-file-upload-tester.test.ts +172 -0
- package/agents/__tests__/57-export-tester.test.ts +169 -0
- package/agents/__tests__/58-payment-flow-tester.test.ts +182 -0
- package/agents/__tests__/59-ssl-tls-auditor.test.ts +179 -0
- package/agents/__tests__/60-dns-cdn-tester.test.ts +176 -0
- package/agents/__tests__/61-docker-health-checker.test.ts +150 -0
- package/agents/__tests__/62-env-config-validator.test.ts +166 -0
- package/agents/__tests__/63-log-quality-auditor.test.ts +175 -0
- package/agents/__tests__/64-analytics-tracker-tester.test.ts +158 -0
- package/agents/__tests__/65-gdpr-compliance-tester.test.ts +174 -0
- package/agents/__tests__/66-soc2-control-validator.test.ts +183 -0
- package/agents/__tests__/67-wcag-aaa-tester.test.ts +190 -0
- package/agents/__tests__/68-dead-code-detector.test.ts +174 -0
- package/agents/__tests__/69-type-safety-auditor.test.ts +173 -0
- package/agents/__tests__/70-complexity-analyzer.test.ts +177 -0
- package/agents/__tests__/registry.test.ts +13 -13
- package/agents/base-agent.ts +8 -0
- package/agents/registry.ts +171 -28
- package/core/__tests__/integration.test.ts +4 -4
- package/core/__tests__/orchestrator.test.ts +17 -16
- package/core/cli.ts +128 -6
- package/core/license.ts +208 -211
- package/core/orchestrator.ts +78 -5
- package/dist/agents/15-regression-sentinel.d.ts +2 -1
- package/dist/agents/15-regression-sentinel.d.ts.map +1 -1
- package/dist/agents/15-regression-sentinel.js +2 -2
- package/dist/agents/15-regression-sentinel.js.map +1 -1
- package/dist/agents/24-signup-onboarding-tester.d.ts +35 -0
- package/dist/agents/24-signup-onboarding-tester.d.ts.map +1 -0
- package/dist/agents/24-signup-onboarding-tester.js +357 -0
- package/dist/agents/24-signup-onboarding-tester.js.map +1 -0
- package/dist/agents/25-crud-flow-tester.d.ts +11 -0
- package/dist/agents/25-crud-flow-tester.d.ts.map +1 -0
- package/dist/agents/25-crud-flow-tester.js +253 -0
- package/dist/agents/25-crud-flow-tester.js.map +1 -0
- package/dist/agents/26-form-validator.d.ts +12 -0
- package/dist/agents/26-form-validator.d.ts.map +1 -0
- package/dist/agents/26-form-validator.js +257 -0
- package/dist/agents/26-form-validator.js.map +1 -0
- package/dist/agents/27-search-filter-tester.d.ts +20 -0
- package/dist/agents/27-search-filter-tester.d.ts.map +1 -0
- package/dist/agents/27-search-filter-tester.js +267 -0
- package/dist/agents/27-search-filter-tester.js.map +1 -0
- package/dist/agents/28-navigation-routing-tester.d.ts +32 -0
- package/dist/agents/28-navigation-routing-tester.d.ts.map +1 -0
- package/dist/agents/28-navigation-routing-tester.js +363 -0
- package/dist/agents/28-navigation-routing-tester.js.map +1 -0
- package/dist/agents/29-responsive-interaction-tester.d.ts +26 -0
- package/dist/agents/29-responsive-interaction-tester.d.ts.map +1 -0
- package/dist/agents/29-responsive-interaction-tester.js +272 -0
- package/dist/agents/29-responsive-interaction-tester.js.map +1 -0
- package/dist/agents/30-multi-user-scenario-tester.d.ts +24 -0
- package/dist/agents/30-multi-user-scenario-tester.d.ts.map +1 -0
- package/dist/agents/30-multi-user-scenario-tester.js +254 -0
- package/dist/agents/30-multi-user-scenario-tester.js.map +1 -0
- package/dist/agents/31-load-tester.d.ts +12 -0
- package/dist/agents/31-load-tester.d.ts.map +1 -0
- package/dist/agents/31-load-tester.js +110 -0
- package/dist/agents/31-load-tester.js.map +1 -0
- package/dist/agents/32-memory-leak-detector.d.ts +12 -0
- package/dist/agents/32-memory-leak-detector.d.ts.map +1 -0
- package/dist/agents/32-memory-leak-detector.js +167 -0
- package/dist/agents/32-memory-leak-detector.js.map +1 -0
- package/dist/agents/33-bundle-analyzer.d.ts +10 -0
- package/dist/agents/33-bundle-analyzer.d.ts.map +1 -0
- package/dist/agents/33-bundle-analyzer.js +111 -0
- package/dist/agents/33-bundle-analyzer.js.map +1 -0
- package/dist/agents/34-xss-scanner.d.ts +11 -0
- package/dist/agents/34-xss-scanner.d.ts.map +1 -0
- package/dist/agents/34-xss-scanner.js +164 -0
- package/dist/agents/34-xss-scanner.js.map +1 -0
- package/dist/agents/35-csrf-tester.d.ts +11 -0
- package/dist/agents/35-csrf-tester.d.ts.map +1 -0
- package/dist/agents/35-csrf-tester.js +70 -0
- package/dist/agents/35-csrf-tester.js.map +1 -0
- package/dist/agents/36-auth-fuzzer.d.ts +13 -0
- package/dist/agents/36-auth-fuzzer.d.ts.map +1 -0
- package/dist/agents/36-auth-fuzzer.js +163 -0
- package/dist/agents/36-auth-fuzzer.js.map +1 -0
- package/dist/agents/37-dependency-scanner.d.ts +11 -0
- package/dist/agents/37-dependency-scanner.d.ts.map +1 -0
- package/dist/agents/37-dependency-scanner.js +139 -0
- package/dist/agents/37-dependency-scanner.js.map +1 -0
- package/dist/agents/38-secrets-scanner.d.ts +11 -0
- package/dist/agents/38-secrets-scanner.d.ts.map +1 -0
- package/dist/agents/38-secrets-scanner.js +116 -0
- package/dist/agents/38-secrets-scanner.js.map +1 -0
- package/dist/agents/39-api-contract-tester.d.ts +12 -0
- package/dist/agents/39-api-contract-tester.d.ts.map +1 -0
- package/dist/agents/39-api-contract-tester.js +142 -0
- package/dist/agents/39-api-contract-tester.js.map +1 -0
- package/dist/agents/40-rate-limit-tester.d.ts +12 -0
- package/dist/agents/40-rate-limit-tester.d.ts.map +1 -0
- package/dist/agents/40-rate-limit-tester.js +79 -0
- package/dist/agents/40-rate-limit-tester.js.map +1 -0
- package/dist/agents/41-api-pagination-tester.d.ts +12 -0
- package/dist/agents/41-api-pagination-tester.d.ts.map +1 -0
- package/dist/agents/41-api-pagination-tester.js +79 -0
- package/dist/agents/41-api-pagination-tester.js.map +1 -0
- package/dist/agents/42-graphql-tester.d.ts +13 -0
- package/dist/agents/42-graphql-tester.d.ts.map +1 -0
- package/dist/agents/42-graphql-tester.js +187 -0
- package/dist/agents/42-graphql-tester.js.map +1 -0
- package/dist/agents/43-data-consistency-checker.d.ts +11 -0
- package/dist/agents/43-data-consistency-checker.d.ts.map +1 -0
- package/dist/agents/43-data-consistency-checker.js +176 -0
- package/dist/agents/43-data-consistency-checker.js.map +1 -0
- package/dist/agents/44-backup-recovery-tester.d.ts +11 -0
- package/dist/agents/44-backup-recovery-tester.d.ts.map +1 -0
- package/dist/agents/44-backup-recovery-tester.js +128 -0
- package/dist/agents/44-backup-recovery-tester.js.map +1 -0
- package/dist/agents/45-data-privacy-scanner.d.ts +11 -0
- package/dist/agents/45-data-privacy-scanner.d.ts.map +1 -0
- package/dist/agents/45-data-privacy-scanner.js +100 -0
- package/dist/agents/45-data-privacy-scanner.js.map +1 -0
- package/dist/agents/46-seo-auditor.d.ts +12 -0
- package/dist/agents/46-seo-auditor.d.ts.map +1 -0
- package/dist/agents/46-seo-auditor.js +275 -0
- package/dist/agents/46-seo-auditor.js.map +1 -0
- package/dist/agents/47-social-preview-tester.d.ts +11 -0
- package/dist/agents/47-social-preview-tester.d.ts.map +1 -0
- package/dist/agents/47-social-preview-tester.js +219 -0
- package/dist/agents/47-social-preview-tester.js.map +1 -0
- package/dist/agents/48-lighthouse-auditor.d.ts +11 -0
- package/dist/agents/48-lighthouse-auditor.d.ts.map +1 -0
- package/dist/agents/48-lighthouse-auditor.js +192 -0
- package/dist/agents/48-lighthouse-auditor.js.map +1 -0
- package/dist/agents/49-i18n-tester.d.ts +13 -0
- package/dist/agents/49-i18n-tester.d.ts.map +1 -0
- package/dist/agents/49-i18n-tester.js +172 -0
- package/dist/agents/49-i18n-tester.js.map +1 -0
- package/dist/agents/50-timezone-tester.d.ts +11 -0
- package/dist/agents/50-timezone-tester.d.ts.map +1 -0
- package/dist/agents/50-timezone-tester.js +152 -0
- package/dist/agents/50-timezone-tester.js.map +1 -0
- package/dist/agents/51-error-recovery-tester.d.ts +11 -0
- package/dist/agents/51-error-recovery-tester.d.ts.map +1 -0
- package/dist/agents/51-error-recovery-tester.js +134 -0
- package/dist/agents/51-error-recovery-tester.js.map +1 -0
- package/dist/agents/52-offline-mode-tester.d.ts +12 -0
- package/dist/agents/52-offline-mode-tester.d.ts.map +1 -0
- package/dist/agents/52-offline-mode-tester.js +161 -0
- package/dist/agents/52-offline-mode-tester.js.map +1 -0
- package/dist/agents/53-graceful-degradation-tester.d.ts +12 -0
- package/dist/agents/53-graceful-degradation-tester.d.ts.map +1 -0
- package/dist/agents/53-graceful-degradation-tester.js +130 -0
- package/dist/agents/53-graceful-degradation-tester.js.map +1 -0
- package/dist/agents/54-websocket-tester.d.ts +10 -0
- package/dist/agents/54-websocket-tester.d.ts.map +1 -0
- package/dist/agents/54-websocket-tester.js +132 -0
- package/dist/agents/54-websocket-tester.js.map +1 -0
- package/dist/agents/55-realtime-sync-tester.d.ts +11 -0
- package/dist/agents/55-realtime-sync-tester.d.ts.map +1 -0
- package/dist/agents/55-realtime-sync-tester.js +175 -0
- package/dist/agents/55-realtime-sync-tester.js.map +1 -0
- package/dist/agents/56-file-upload-tester.d.ts +12 -0
- package/dist/agents/56-file-upload-tester.d.ts.map +1 -0
- package/dist/agents/56-file-upload-tester.js +166 -0
- package/dist/agents/56-file-upload-tester.js.map +1 -0
- package/dist/agents/57-export-tester.d.ts +11 -0
- package/dist/agents/57-export-tester.d.ts.map +1 -0
- package/dist/agents/57-export-tester.js +155 -0
- package/dist/agents/57-export-tester.js.map +1 -0
- package/dist/agents/58-payment-flow-tester.d.ts +11 -0
- package/dist/agents/58-payment-flow-tester.d.ts.map +1 -0
- package/dist/agents/58-payment-flow-tester.js +159 -0
- package/dist/agents/58-payment-flow-tester.js.map +1 -0
- package/dist/agents/59-ssl-tls-auditor.d.ts +10 -0
- package/dist/agents/59-ssl-tls-auditor.d.ts.map +1 -0
- package/dist/agents/59-ssl-tls-auditor.js +132 -0
- package/dist/agents/59-ssl-tls-auditor.js.map +1 -0
- package/dist/agents/60-dns-cdn-tester.d.ts +10 -0
- package/dist/agents/60-dns-cdn-tester.d.ts.map +1 -0
- package/dist/agents/60-dns-cdn-tester.js +105 -0
- package/dist/agents/60-dns-cdn-tester.js.map +1 -0
- package/dist/agents/61-docker-health-checker.d.ts +10 -0
- package/dist/agents/61-docker-health-checker.d.ts.map +1 -0
- package/dist/agents/61-docker-health-checker.js +95 -0
- package/dist/agents/61-docker-health-checker.js.map +1 -0
- package/dist/agents/62-env-config-validator.d.ts +10 -0
- package/dist/agents/62-env-config-validator.d.ts.map +1 -0
- package/dist/agents/62-env-config-validator.js +132 -0
- package/dist/agents/62-env-config-validator.js.map +1 -0
- package/dist/agents/63-log-quality-auditor.d.ts +9 -0
- package/dist/agents/63-log-quality-auditor.d.ts.map +1 -0
- package/dist/agents/63-log-quality-auditor.js +121 -0
- package/dist/agents/63-log-quality-auditor.js.map +1 -0
- package/dist/agents/64-analytics-tracker-tester.d.ts +10 -0
- package/dist/agents/64-analytics-tracker-tester.d.ts.map +1 -0
- package/dist/agents/64-analytics-tracker-tester.js +146 -0
- package/dist/agents/64-analytics-tracker-tester.js.map +1 -0
- package/dist/agents/65-gdpr-compliance-tester.d.ts +13 -0
- package/dist/agents/65-gdpr-compliance-tester.d.ts.map +1 -0
- package/dist/agents/65-gdpr-compliance-tester.js +186 -0
- package/dist/agents/65-gdpr-compliance-tester.js.map +1 -0
- package/dist/agents/66-soc2-control-validator.d.ts +14 -0
- package/dist/agents/66-soc2-control-validator.d.ts.map +1 -0
- package/dist/agents/66-soc2-control-validator.js +178 -0
- package/dist/agents/66-soc2-control-validator.js.map +1 -0
- package/dist/agents/67-wcag-aaa-tester.d.ts +13 -0
- package/dist/agents/67-wcag-aaa-tester.d.ts.map +1 -0
- package/dist/agents/67-wcag-aaa-tester.js +207 -0
- package/dist/agents/67-wcag-aaa-tester.js.map +1 -0
- package/dist/agents/68-dead-code-detector.d.ts +9 -0
- package/dist/agents/68-dead-code-detector.d.ts.map +1 -0
- package/dist/agents/68-dead-code-detector.js +116 -0
- package/dist/agents/68-dead-code-detector.js.map +1 -0
- package/dist/agents/69-type-safety-auditor.d.ts +9 -0
- package/dist/agents/69-type-safety-auditor.d.ts.map +1 -0
- package/dist/agents/69-type-safety-auditor.js +148 -0
- package/dist/agents/69-type-safety-auditor.js.map +1 -0
- package/dist/agents/70-complexity-analyzer.d.ts +10 -0
- package/dist/agents/70-complexity-analyzer.d.ts.map +1 -0
- package/dist/agents/70-complexity-analyzer.js +154 -0
- package/dist/agents/70-complexity-analyzer.js.map +1 -0
- package/dist/agents/base-agent.d.ts +3 -1
- package/dist/agents/base-agent.d.ts.map +1 -1
- package/dist/agents/base-agent.js +7 -1
- package/dist/agents/base-agent.js.map +1 -1
- package/dist/agents/registry.d.ts +5 -4
- package/dist/agents/registry.d.ts.map +1 -1
- package/dist/agents/registry.js +170 -29
- package/dist/agents/registry.js.map +1 -1
- package/dist/clients/agent-mvp.d.ts +66 -0
- package/dist/clients/agent-mvp.d.ts.map +1 -0
- package/dist/clients/agent-mvp.js +91 -0
- package/dist/clients/agent-mvp.js.map +1 -0
- package/dist/clients/total-recall.d.ts +37 -0
- package/dist/clients/total-recall.d.ts.map +1 -0
- package/dist/clients/total-recall.js +224 -0
- package/dist/clients/total-recall.js.map +1 -0
- package/dist/core/cli.d.ts +3 -0
- package/dist/core/cli.d.ts.map +1 -1
- package/dist/core/cli.js +122 -6
- package/dist/core/cli.js.map +1 -1
- package/dist/core/license.js +2 -5
- package/dist/core/license.js.map +1 -1
- package/dist/core/orchestrator.d.ts +10 -1
- package/dist/core/orchestrator.d.ts.map +1 -1
- package/dist/core/orchestrator.js +66 -5
- package/dist/core/orchestrator.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,311 @@
|
|
|
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
|
+
],
|
|
23
|
+
environments: {
|
|
24
|
+
alpha: { baseUrl: 'http://localhost:3000', seed: false },
|
|
25
|
+
},
|
|
26
|
+
portals: [],
|
|
27
|
+
breakpoints: [],
|
|
28
|
+
integrations: [],
|
|
29
|
+
workers: [],
|
|
30
|
+
workflows: [],
|
|
31
|
+
accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
|
|
32
|
+
tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
|
|
33
|
+
costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
|
|
34
|
+
agentClassification: { blocking: [], advisory: [27] },
|
|
35
|
+
thresholds: {
|
|
36
|
+
unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
|
|
37
|
+
lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
|
|
38
|
+
apiResponseTime: 2000,
|
|
39
|
+
bundleSizeLimit: 500000,
|
|
40
|
+
maxConsoleErrors: 0,
|
|
41
|
+
},
|
|
42
|
+
disabledAgents: [],
|
|
43
|
+
disabledAgentSet: new Set<number>(),
|
|
44
|
+
...overrides,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// Mock fetch
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
const fetchMock = vi.fn();
|
|
53
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Mock helpers
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
const loginMock = vi.fn().mockResolvedValue(undefined);
|
|
60
|
+
|
|
61
|
+
vi.mock('../../helpers/navigation', () => ({
|
|
62
|
+
login: (...args: unknown[]) => loginMock(...args),
|
|
63
|
+
waitForSectionLoad: vi.fn().mockResolvedValue(undefined),
|
|
64
|
+
navigateToSection: vi.fn().mockResolvedValue(undefined),
|
|
65
|
+
switchPortal: vi.fn().mockResolvedValue(undefined),
|
|
66
|
+
}));
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Mock Playwright
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
interface MockLocator {
|
|
73
|
+
count: ReturnType<typeof vi.fn>;
|
|
74
|
+
first: ReturnType<typeof vi.fn>;
|
|
75
|
+
nth: ReturnType<typeof vi.fn>;
|
|
76
|
+
click: ReturnType<typeof vi.fn>;
|
|
77
|
+
fill: ReturnType<typeof vi.fn>;
|
|
78
|
+
evaluate: ReturnType<typeof vi.fn>;
|
|
79
|
+
selectOption: ReturnType<typeof vi.fn>;
|
|
80
|
+
locator: ReturnType<typeof vi.fn>;
|
|
81
|
+
getAttribute: ReturnType<typeof vi.fn>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function makeMockLocator(countVal = 0): MockLocator {
|
|
85
|
+
const locator: MockLocator = {
|
|
86
|
+
count: vi.fn().mockResolvedValue(countVal),
|
|
87
|
+
first: vi.fn(),
|
|
88
|
+
nth: vi.fn(),
|
|
89
|
+
click: vi.fn().mockResolvedValue(undefined),
|
|
90
|
+
fill: vi.fn().mockResolvedValue(undefined),
|
|
91
|
+
evaluate: vi.fn().mockResolvedValue('div'),
|
|
92
|
+
selectOption: vi.fn().mockResolvedValue(undefined),
|
|
93
|
+
locator: vi.fn().mockReturnValue({ count: vi.fn().mockResolvedValue(0), nth: vi.fn(), getAttribute: vi.fn() }),
|
|
94
|
+
getAttribute: vi.fn().mockResolvedValue(null),
|
|
95
|
+
};
|
|
96
|
+
locator.first.mockReturnValue(locator);
|
|
97
|
+
locator.nth.mockReturnValue(locator);
|
|
98
|
+
return locator;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
interface MockPage {
|
|
102
|
+
close: ReturnType<typeof vi.fn>;
|
|
103
|
+
goto: ReturnType<typeof vi.fn>;
|
|
104
|
+
locator: ReturnType<typeof vi.fn>;
|
|
105
|
+
waitForTimeout: ReturnType<typeof vi.fn>;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function makeMockPage(): MockPage {
|
|
109
|
+
const defaultLocator = makeMockLocator(0);
|
|
110
|
+
return {
|
|
111
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
112
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
113
|
+
locator: vi.fn().mockReturnValue(defaultLocator),
|
|
114
|
+
waitForTimeout: vi.fn().mockResolvedValue(undefined),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
let currentMockPage: MockPage;
|
|
119
|
+
let mockBrowser: { newPage: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
|
|
120
|
+
|
|
121
|
+
vi.mock('playwright', async () => ({
|
|
122
|
+
chromium: {
|
|
123
|
+
launch: vi.fn().mockImplementation(async () => mockBrowser),
|
|
124
|
+
},
|
|
125
|
+
}));
|
|
126
|
+
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// Dynamic import AFTER mocks are hoisted
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
const { SearchFilterTesterAgent } = await import('../27-search-filter-tester');
|
|
132
|
+
|
|
133
|
+
function createAgent(
|
|
134
|
+
config: ValidatedConfig = makeConfig(),
|
|
135
|
+
phase: Phase = 'beta',
|
|
136
|
+
): InstanceType<typeof SearchFilterTesterAgent> {
|
|
137
|
+
return new SearchFilterTesterAgent(config, phase, '/tmp/test-run');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
// Tests
|
|
142
|
+
// ---------------------------------------------------------------------------
|
|
143
|
+
|
|
144
|
+
describe('SearchFilterTesterAgent', () => {
|
|
145
|
+
beforeEach(() => {
|
|
146
|
+
fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
|
|
147
|
+
loginMock.mockReset().mockResolvedValue(undefined);
|
|
148
|
+
|
|
149
|
+
currentMockPage = makeMockPage();
|
|
150
|
+
mockBrowser = {
|
|
151
|
+
newPage: vi.fn().mockResolvedValue(currentMockPage),
|
|
152
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
153
|
+
};
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Identity
|
|
157
|
+
it('has agentId 27 and correct agentName', () => {
|
|
158
|
+
const agent = createAgent();
|
|
159
|
+
expect(agent.agentId).toBe(27);
|
|
160
|
+
expect(agent.agentName).toBe('Search & Filter Tester');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// preFlight
|
|
164
|
+
it('throws when modules array is empty', async () => {
|
|
165
|
+
const config = makeConfig({ modules: [] });
|
|
166
|
+
const agent = createAgent(config);
|
|
167
|
+
const result = await agent.run();
|
|
168
|
+
|
|
169
|
+
expect(result.status).toBe('failed');
|
|
170
|
+
expect(result.findings[0].description).toContain('module');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('resolves environment in preFlight', async () => {
|
|
174
|
+
const agent = createAgent();
|
|
175
|
+
const result = await agent.run();
|
|
176
|
+
expect(result.findings.every(f => !f.description.includes('preFlight'))).toBe(true);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
// No search or filter found
|
|
180
|
+
it('produces low finding when no search or filter UI found', async () => {
|
|
181
|
+
const agent = createAgent();
|
|
182
|
+
const result = await agent.run();
|
|
183
|
+
|
|
184
|
+
const noSearchFilter = result.findings.filter(f => f.description.includes('No search or filter'));
|
|
185
|
+
expect(noSearchFilter.length).toBeGreaterThan(0);
|
|
186
|
+
expect(noSearchFilter[0].severity).toBe('low');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Search found and tested
|
|
190
|
+
it('tests search functionality when search input exists', async () => {
|
|
191
|
+
const searchLocator = makeMockLocator(1);
|
|
192
|
+
const noResultsLocator = makeMockLocator(0);
|
|
193
|
+
|
|
194
|
+
currentMockPage.locator.mockImplementation((sel: string) => {
|
|
195
|
+
if (sel.includes('search') || sel.includes('find') || sel.includes('searchbox'))
|
|
196
|
+
return searchLocator;
|
|
197
|
+
if (sel.includes('no results') || sel.includes('empty'))
|
|
198
|
+
return noResultsLocator;
|
|
199
|
+
return makeMockLocator(0);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
const agent = createAgent();
|
|
203
|
+
const result = await agent.run();
|
|
204
|
+
|
|
205
|
+
// Search fill should have been called
|
|
206
|
+
expect(searchLocator.fill).toHaveBeenCalled();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Search not filtering
|
|
210
|
+
it('produces medium finding when gibberish search still shows items', async () => {
|
|
211
|
+
const searchLocator = makeMockLocator(1);
|
|
212
|
+
const listLocator = makeMockLocator(5); // 5 list items still showing
|
|
213
|
+
const noResultsLocator = makeMockLocator(0);
|
|
214
|
+
|
|
215
|
+
currentMockPage.locator.mockImplementation((sel: string) => {
|
|
216
|
+
if (sel.includes('search') || sel.includes('find') || sel.includes('searchbox'))
|
|
217
|
+
return searchLocator;
|
|
218
|
+
if (sel.includes('no results') || sel.includes('not found') || sel.includes('empty'))
|
|
219
|
+
return noResultsLocator;
|
|
220
|
+
if (sel.includes('tr,') || sel.includes('list-item') || sel.includes('card'))
|
|
221
|
+
return listLocator;
|
|
222
|
+
return makeMockLocator(0);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const agent = createAgent();
|
|
226
|
+
const result = await agent.run();
|
|
227
|
+
|
|
228
|
+
const searchFinding = result.findings.find(f => f.description.includes('Search may not be filtering'));
|
|
229
|
+
expect(searchFinding).toBeDefined();
|
|
230
|
+
expect(searchFinding!.severity).toBe('medium');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Filter found
|
|
234
|
+
it('reports filter controls when found', async () => {
|
|
235
|
+
const filterLocator = makeMockLocator(2);
|
|
236
|
+
filterLocator.evaluate.mockResolvedValue('select');
|
|
237
|
+
const optionLocator = makeMockLocator(3);
|
|
238
|
+
optionLocator.nth = vi.fn().mockReturnValue({ getAttribute: vi.fn().mockResolvedValue('val1') });
|
|
239
|
+
filterLocator.locator.mockReturnValue(optionLocator);
|
|
240
|
+
|
|
241
|
+
currentMockPage.locator.mockImplementation((sel: string) => {
|
|
242
|
+
if (sel.includes('select') || sel.includes('combobox') || sel.includes('Filter') || sel.includes('filter'))
|
|
243
|
+
return filterLocator;
|
|
244
|
+
return makeMockLocator(0);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
const agent = createAgent();
|
|
248
|
+
const result = await agent.run();
|
|
249
|
+
|
|
250
|
+
const filterFinding = result.findings.find(f => f.description.includes('filter control'));
|
|
251
|
+
expect(filterFinding).toBeDefined();
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Login failure
|
|
255
|
+
it('produces medium finding and continues when login fails', async () => {
|
|
256
|
+
loginMock.mockRejectedValue(new Error('Invalid credentials'));
|
|
257
|
+
|
|
258
|
+
const agent = createAgent();
|
|
259
|
+
const result = await agent.run();
|
|
260
|
+
|
|
261
|
+
const loginFinding = result.findings.find(f => f.id.includes('login-failed'));
|
|
262
|
+
expect(loginFinding).toBeDefined();
|
|
263
|
+
expect(loginFinding!.severity).toBe('medium');
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
// No credentials
|
|
267
|
+
it('produces medium finding when no credentials configured', async () => {
|
|
268
|
+
const config = makeConfig({
|
|
269
|
+
auth: { loginUrl: 'http://localhost/login', credentials: {} },
|
|
270
|
+
});
|
|
271
|
+
const agent = createAgent(config);
|
|
272
|
+
const result = await agent.run();
|
|
273
|
+
|
|
274
|
+
const noCredsFinding = result.findings.find(f => f.id.includes('no-credentials'));
|
|
275
|
+
expect(noCredsFinding).toBeDefined();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// Playwright crash
|
|
279
|
+
it('produces medium finding when Playwright crashes', async () => {
|
|
280
|
+
mockBrowser.newPage.mockRejectedValue(new Error('Browser crashed'));
|
|
281
|
+
|
|
282
|
+
const agent = createAgent();
|
|
283
|
+
const result = await agent.run();
|
|
284
|
+
|
|
285
|
+
const pwFinding = result.findings.find(f => f.id.includes('playwright-failure'));
|
|
286
|
+
expect(pwFinding).toBeDefined();
|
|
287
|
+
expect(pwFinding!.severity).toBe('medium');
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
// Navigation failure
|
|
291
|
+
it('produces medium finding when module navigation fails', async () => {
|
|
292
|
+
currentMockPage.goto.mockRejectedValue(new Error('Navigation failed'));
|
|
293
|
+
|
|
294
|
+
const agent = createAgent();
|
|
295
|
+
const result = await agent.run();
|
|
296
|
+
|
|
297
|
+
const navFinding = result.findings.find(f => f.id.includes('nav-failed'));
|
|
298
|
+
expect(navFinding).toBeDefined();
|
|
299
|
+
expect(navFinding!.severity).toBe('medium');
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
// Browser closed in finally
|
|
303
|
+
it('closes browser even when an error occurs', async () => {
|
|
304
|
+
mockBrowser.newPage.mockRejectedValue(new Error('Launch error'));
|
|
305
|
+
|
|
306
|
+
const agent = createAgent();
|
|
307
|
+
await agent.run();
|
|
308
|
+
|
|
309
|
+
expect(mockBrowser.close).toHaveBeenCalled();
|
|
310
|
+
});
|
|
311
|
+
});
|
|
@@ -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
|
+
},
|
|
18
|
+
},
|
|
19
|
+
modules: [
|
|
20
|
+
{ id: 'dashboard', route: '/dashboard', sidebarIcon: true },
|
|
21
|
+
{ id: 'tasks', route: '/tasks', sidebarIcon: true },
|
|
22
|
+
{ id: 'settings', route: '/settings', sidebarIcon: false },
|
|
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: [28] },
|
|
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
|
+
innerText: ReturnType<typeof vi.fn>;
|
|
76
|
+
evaluateAll: ReturnType<typeof vi.fn>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function makeMockLocator(countVal = 0, text = 'Content'): MockLocator {
|
|
80
|
+
return {
|
|
81
|
+
count: vi.fn().mockResolvedValue(countVal),
|
|
82
|
+
innerText: vi.fn().mockResolvedValue(text),
|
|
83
|
+
evaluateAll: vi.fn().mockResolvedValue([]),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
interface MockPage {
|
|
88
|
+
close: ReturnType<typeof vi.fn>;
|
|
89
|
+
goto: ReturnType<typeof vi.fn>;
|
|
90
|
+
url: ReturnType<typeof vi.fn>;
|
|
91
|
+
locator: ReturnType<typeof vi.fn>;
|
|
92
|
+
goBack: ReturnType<typeof vi.fn>;
|
|
93
|
+
goForward: ReturnType<typeof vi.fn>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function makeMockPage(urlOverride?: string): MockPage {
|
|
97
|
+
return {
|
|
98
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
99
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
100
|
+
url: vi.fn().mockReturnValue(urlOverride ?? 'http://localhost:3000/dashboard'),
|
|
101
|
+
locator: vi.fn().mockReturnValue(makeMockLocator(0, 'Page Content')),
|
|
102
|
+
goBack: vi.fn().mockResolvedValue(undefined),
|
|
103
|
+
goForward: vi.fn().mockResolvedValue(undefined),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let currentMockPage: MockPage;
|
|
108
|
+
let mockBrowser: {
|
|
109
|
+
newPage: ReturnType<typeof vi.fn>;
|
|
110
|
+
close: ReturnType<typeof vi.fn>;
|
|
111
|
+
newContext: ReturnType<typeof vi.fn>;
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
function makeMockIncognitoPage(url?: string): MockPage {
|
|
115
|
+
return makeMockPage(url ?? 'http://localhost:3000/login');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
vi.mock('playwright', async () => ({
|
|
119
|
+
chromium: {
|
|
120
|
+
launch: vi.fn().mockImplementation(async () => mockBrowser),
|
|
121
|
+
},
|
|
122
|
+
}));
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Dynamic import AFTER mocks are hoisted
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
const { NavigationRoutingTesterAgent } = await import('../28-navigation-routing-tester');
|
|
129
|
+
|
|
130
|
+
function createAgent(
|
|
131
|
+
config: ValidatedConfig = makeConfig(),
|
|
132
|
+
phase: Phase = 'beta',
|
|
133
|
+
): InstanceType<typeof NavigationRoutingTesterAgent> {
|
|
134
|
+
return new NavigationRoutingTesterAgent(config, phase, '/tmp/test-run');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Tests
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
|
|
141
|
+
describe('NavigationRoutingTesterAgent', () => {
|
|
142
|
+
beforeEach(() => {
|
|
143
|
+
fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
|
|
144
|
+
loginMock.mockReset().mockResolvedValue(undefined);
|
|
145
|
+
|
|
146
|
+
currentMockPage = makeMockPage();
|
|
147
|
+
|
|
148
|
+
const incognitoPage = makeMockIncognitoPage();
|
|
149
|
+
const incognitoContext = {
|
|
150
|
+
newPage: vi.fn().mockResolvedValue(incognitoPage),
|
|
151
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
mockBrowser = {
|
|
155
|
+
newPage: vi.fn().mockResolvedValue(currentMockPage),
|
|
156
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
157
|
+
newContext: vi.fn().mockResolvedValue(incognitoContext),
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Identity
|
|
162
|
+
it('has agentId 28 and correct agentName', () => {
|
|
163
|
+
const agent = createAgent();
|
|
164
|
+
expect(agent.agentId).toBe(28);
|
|
165
|
+
expect(agent.agentName).toBe('Navigation & Routing Tester');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// preFlight
|
|
169
|
+
it('throws when modules array is empty', async () => {
|
|
170
|
+
const config = makeConfig({ modules: [] });
|
|
171
|
+
const agent = createAgent(config);
|
|
172
|
+
const result = await agent.run();
|
|
173
|
+
|
|
174
|
+
expect(result.status).toBe('failed');
|
|
175
|
+
expect(result.findings[0].description).toContain('module');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Sidebar navigation — happy path
|
|
179
|
+
it('navigates to each sidebar module without findings on success', async () => {
|
|
180
|
+
currentMockPage.locator.mockReturnValue(makeMockLocator(0, 'Page Content'));
|
|
181
|
+
|
|
182
|
+
const agent = createAgent();
|
|
183
|
+
const result = await agent.run();
|
|
184
|
+
|
|
185
|
+
// Should not produce sidebar blank page findings
|
|
186
|
+
const sidebarBlank = result.findings.filter(f => f.id.includes('sidebar-blank'));
|
|
187
|
+
expect(sidebarBlank.length).toBe(0);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
// Sidebar navigation — blank page
|
|
191
|
+
it('produces medium finding when sidebar navigation results in blank page', async () => {
|
|
192
|
+
currentMockPage.locator.mockReturnValue(makeMockLocator(0, ''));
|
|
193
|
+
|
|
194
|
+
const agent = createAgent();
|
|
195
|
+
const result = await agent.run();
|
|
196
|
+
|
|
197
|
+
const blankFindings = result.findings.filter(f => f.id.includes('sidebar-blank'));
|
|
198
|
+
expect(blankFindings.length).toBeGreaterThan(0);
|
|
199
|
+
expect(blankFindings[0].severity).toBe('medium');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
// 404 handling — app shows 404 page
|
|
203
|
+
it('produces no finding when app properly handles 404 routes', async () => {
|
|
204
|
+
currentMockPage.locator.mockReturnValue(makeMockLocator(0, 'Page not found'));
|
|
205
|
+
|
|
206
|
+
const agent = createAgent();
|
|
207
|
+
const result = await agent.run();
|
|
208
|
+
|
|
209
|
+
const notFoundFindings = result.findings.filter(f => f.id.includes('404'));
|
|
210
|
+
expect(notFoundFindings.length).toBe(0);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// 404 handling — blank page
|
|
214
|
+
it('produces high finding when 404 route shows blank page', async () => {
|
|
215
|
+
// URL stays on fake route, body is empty
|
|
216
|
+
currentMockPage.url.mockReturnValue('http://localhost:3000/this-page-does-not-exist-12345');
|
|
217
|
+
currentMockPage.locator.mockReturnValue(makeMockLocator(0, ''));
|
|
218
|
+
|
|
219
|
+
const agent = createAgent();
|
|
220
|
+
const result = await agent.run();
|
|
221
|
+
|
|
222
|
+
const blankFindings = result.findings.filter(f => f.id === '28-404-blank');
|
|
223
|
+
expect(blankFindings.length).toBe(1);
|
|
224
|
+
expect(blankFindings[0].severity).toBe('high');
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Protected routes — redirects to login (good)
|
|
228
|
+
it('produces no critical finding when protected routes redirect to login', async () => {
|
|
229
|
+
// Incognito page URL returns /login — correct behavior
|
|
230
|
+
const incognitoPage = makeMockIncognitoPage('http://localhost:3000/login');
|
|
231
|
+
const incognitoContext = {
|
|
232
|
+
newPage: vi.fn().mockResolvedValue(incognitoPage),
|
|
233
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
234
|
+
};
|
|
235
|
+
mockBrowser.newContext.mockResolvedValue(incognitoContext);
|
|
236
|
+
|
|
237
|
+
const agent = createAgent();
|
|
238
|
+
const result = await agent.run();
|
|
239
|
+
|
|
240
|
+
const unprotectedFindings = result.findings.filter(f => f.id.includes('unprotected'));
|
|
241
|
+
expect(unprotectedFindings.length).toBe(0);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Protected routes — accessible without auth (critical)
|
|
245
|
+
it('produces critical finding when module is accessible without authentication', async () => {
|
|
246
|
+
// Incognito page stays on original URL with content
|
|
247
|
+
const incognitoPage = makeMockPage('http://localhost:3000/dashboard');
|
|
248
|
+
incognitoPage.locator.mockReturnValue(makeMockLocator(0, 'Sensitive data visible'));
|
|
249
|
+
const incognitoContext = {
|
|
250
|
+
newPage: vi.fn().mockResolvedValue(incognitoPage),
|
|
251
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
252
|
+
};
|
|
253
|
+
mockBrowser.newContext.mockResolvedValue(incognitoContext);
|
|
254
|
+
|
|
255
|
+
const agent = createAgent();
|
|
256
|
+
const result = await agent.run();
|
|
257
|
+
|
|
258
|
+
const unprotectedFindings = result.findings.filter(f => f.id.includes('unprotected'));
|
|
259
|
+
expect(unprotectedFindings.length).toBeGreaterThan(0);
|
|
260
|
+
expect(unprotectedFindings[0].severity).toBe('critical');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
// Link integrity — all links work
|
|
264
|
+
it('produces no findings when all internal links load correctly', async () => {
|
|
265
|
+
currentMockPage.locator.mockReturnValue(makeMockLocator(0, 'Page Content'));
|
|
266
|
+
|
|
267
|
+
const agent = createAgent();
|
|
268
|
+
const result = await agent.run();
|
|
269
|
+
|
|
270
|
+
const brokenLinkFindings = result.findings.filter(f => f.id.includes('broken-link'));
|
|
271
|
+
expect(brokenLinkFindings.length).toBe(0);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
// Back/forward navigation — success
|
|
275
|
+
it('produces no findings when back/forward works correctly', async () => {
|
|
276
|
+
let callCount = 0;
|
|
277
|
+
currentMockPage.url.mockImplementation(() => {
|
|
278
|
+
callCount++;
|
|
279
|
+
// After goBack, return first module URL; after goForward, return second
|
|
280
|
+
if (callCount <= 2) return 'http://localhost:3000/dashboard';
|
|
281
|
+
return 'http://localhost:3000/tasks';
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
currentMockPage.locator.mockReturnValue(makeMockLocator(0, 'Page Content'));
|
|
285
|
+
|
|
286
|
+
const agent = createAgent();
|
|
287
|
+
const result = await agent.run();
|
|
288
|
+
|
|
289
|
+
const historyFindings = result.findings.filter(f =>
|
|
290
|
+
f.id.includes('back-nav') || f.id.includes('forward-nav'),
|
|
291
|
+
);
|
|
292
|
+
expect(historyFindings.length).toBe(0);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
// Login failure — graceful degradation
|
|
296
|
+
it('produces medium finding and continues when login fails', async () => {
|
|
297
|
+
loginMock.mockRejectedValue(new Error('Invalid credentials'));
|
|
298
|
+
|
|
299
|
+
const agent = createAgent();
|
|
300
|
+
const result = await agent.run();
|
|
301
|
+
|
|
302
|
+
const loginFinding = result.findings.find(f => f.id.includes('login-failed'));
|
|
303
|
+
expect(loginFinding).toBeDefined();
|
|
304
|
+
expect(loginFinding!.severity).toBe('medium');
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Playwright crash
|
|
308
|
+
it('produces medium finding when Playwright crashes', async () => {
|
|
309
|
+
mockBrowser.newPage.mockRejectedValue(new Error('Browser crashed'));
|
|
310
|
+
|
|
311
|
+
const agent = createAgent();
|
|
312
|
+
const result = await agent.run();
|
|
313
|
+
|
|
314
|
+
const pwFinding = result.findings.find(f => f.id.includes('playwright-failure'));
|
|
315
|
+
expect(pwFinding).toBeDefined();
|
|
316
|
+
expect(pwFinding!.severity).toBe('medium');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// Browser closed in finally
|
|
320
|
+
it('closes browser even when an error occurs', async () => {
|
|
321
|
+
mockBrowser.newPage.mockRejectedValue(new Error('Launch error'));
|
|
322
|
+
|
|
323
|
+
const agent = createAgent();
|
|
324
|
+
await agent.run();
|
|
325
|
+
|
|
326
|
+
expect(mockBrowser.close).toHaveBeenCalled();
|
|
327
|
+
});
|
|
328
|
+
});
|