@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,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
|
+
});
|