@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,164 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import type { Finding } from '../core/types';
|
|
4
|
+
import { BaseAgent } from './base-agent';
|
|
5
|
+
|
|
6
|
+
/** Find all .ts files under a directory (excluding tests and node_modules). */
|
|
7
|
+
function findTsFiles(dir: string, results: string[] = []): string[] {
|
|
8
|
+
if (!fs.existsSync(dir)) return results;
|
|
9
|
+
|
|
10
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
const fullPath = path.join(dir, entry.name);
|
|
13
|
+
if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.git' && entry.name !== 'dist') {
|
|
14
|
+
findTsFiles(fullPath, results);
|
|
15
|
+
} else if (entry.isFile() && entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {
|
|
16
|
+
results.push(fullPath);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return results;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class TypeSafetyAuditorAgent extends BaseAgent {
|
|
23
|
+
readonly agentId = 69;
|
|
24
|
+
readonly agentName = 'Type Safety Auditor';
|
|
25
|
+
|
|
26
|
+
protected async preFlight(): Promise<void> {
|
|
27
|
+
// File-based agent
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected async execute(): Promise<Finding[]> {
|
|
31
|
+
const findings: Finding[] = [];
|
|
32
|
+
|
|
33
|
+
const projectRoot = this.config.projectRoot ?? process.cwd();
|
|
34
|
+
const srcDir = path.join(projectRoot, 'src');
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync(srcDir)) {
|
|
37
|
+
findings.push({
|
|
38
|
+
id: `${this.agentId}-no-src-dir`,
|
|
39
|
+
type: 'infra-issue',
|
|
40
|
+
severity: 'low',
|
|
41
|
+
agentId: this.agentId,
|
|
42
|
+
module: 'type-safety-auditor',
|
|
43
|
+
description: 'No src/ directory found in project root',
|
|
44
|
+
});
|
|
45
|
+
return findings;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const tsFiles = findTsFiles(srcDir);
|
|
49
|
+
let anyCount = 0;
|
|
50
|
+
let tsIgnoreCount = 0;
|
|
51
|
+
let asAnyCount = 0;
|
|
52
|
+
let missingReturnTypeCount = 0;
|
|
53
|
+
|
|
54
|
+
for (const filePath of tsFiles) {
|
|
55
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
56
|
+
const lines = content.split('\n');
|
|
57
|
+
const relPath = path.relative(projectRoot, filePath);
|
|
58
|
+
|
|
59
|
+
for (let lineNum = 0; lineNum < lines.length; lineNum++) {
|
|
60
|
+
const line = lines[lineNum];
|
|
61
|
+
|
|
62
|
+
// Check for : any (type annotation)
|
|
63
|
+
if (/:\s*any[^a-zA-Z]/.test(line) || /:\s*any\s*$/.test(line)) {
|
|
64
|
+
anyCount++;
|
|
65
|
+
if (anyCount <= 5) {
|
|
66
|
+
findings.push({
|
|
67
|
+
id: `${this.agentId}-any-type-${anyCount}`,
|
|
68
|
+
type: 'test-bug',
|
|
69
|
+
severity: 'low',
|
|
70
|
+
agentId: this.agentId,
|
|
71
|
+
module: 'type-safety-auditor',
|
|
72
|
+
file: filePath,
|
|
73
|
+
line: lineNum + 1,
|
|
74
|
+
description: `"any" type annotation in ${relPath}:${lineNum + 1}`,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Check for @ts-ignore
|
|
80
|
+
if (/@ts-ignore/.test(line)) {
|
|
81
|
+
tsIgnoreCount++;
|
|
82
|
+
if (tsIgnoreCount <= 3) {
|
|
83
|
+
findings.push({
|
|
84
|
+
id: `${this.agentId}-ts-ignore-${tsIgnoreCount}`,
|
|
85
|
+
type: 'test-bug',
|
|
86
|
+
severity: 'low',
|
|
87
|
+
agentId: this.agentId,
|
|
88
|
+
module: 'type-safety-auditor',
|
|
89
|
+
file: filePath,
|
|
90
|
+
line: lineNum + 1,
|
|
91
|
+
description: `@ts-ignore comment in ${relPath}:${lineNum + 1}`,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Check for "as any"
|
|
97
|
+
if (/as\s+any\b/.test(line)) {
|
|
98
|
+
asAnyCount++;
|
|
99
|
+
if (asAnyCount <= 3) {
|
|
100
|
+
findings.push({
|
|
101
|
+
id: `${this.agentId}-as-any-${asAnyCount}`,
|
|
102
|
+
type: 'test-bug',
|
|
103
|
+
severity: 'low',
|
|
104
|
+
agentId: this.agentId,
|
|
105
|
+
module: 'type-safety-auditor',
|
|
106
|
+
file: filePath,
|
|
107
|
+
line: lineNum + 1,
|
|
108
|
+
description: `"as any" assertion in ${relPath}:${lineNum + 1}`,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check for missing return types on exported functions
|
|
114
|
+
if (/^export\s+(async\s+)?function\s+\w+\s*\([^)]*\)\s*\{/.test(line.trim())) {
|
|
115
|
+
// No return type annotation before {
|
|
116
|
+
missingReturnTypeCount++;
|
|
117
|
+
if (missingReturnTypeCount <= 3) {
|
|
118
|
+
findings.push({
|
|
119
|
+
id: `${this.agentId}-missing-return-type-${missingReturnTypeCount}`,
|
|
120
|
+
type: 'test-bug',
|
|
121
|
+
severity: 'low',
|
|
122
|
+
agentId: this.agentId,
|
|
123
|
+
module: 'type-safety-auditor',
|
|
124
|
+
file: filePath,
|
|
125
|
+
line: lineNum + 1,
|
|
126
|
+
description: `Exported function missing return type in ${relPath}:${lineNum + 1}`,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Summary findings
|
|
134
|
+
if (anyCount > 5) {
|
|
135
|
+
findings.push({
|
|
136
|
+
id: `${this.agentId}-any-summary`,
|
|
137
|
+
type: 'test-bug',
|
|
138
|
+
severity: 'low',
|
|
139
|
+
agentId: this.agentId,
|
|
140
|
+
module: 'type-safety-auditor',
|
|
141
|
+
description: `${anyCount} total "any" type annotations found (showing first 5)`,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (tsIgnoreCount > 3) {
|
|
146
|
+
findings.push({
|
|
147
|
+
id: `${this.agentId}-ts-ignore-summary`,
|
|
148
|
+
type: 'test-bug',
|
|
149
|
+
severity: 'low',
|
|
150
|
+
agentId: this.agentId,
|
|
151
|
+
module: 'type-safety-auditor',
|
|
152
|
+
description: `${tsIgnoreCount} total @ts-ignore comments found (showing first 3)`,
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
this.addEvidence({
|
|
157
|
+
type: 'log',
|
|
158
|
+
path: '',
|
|
159
|
+
description: `Scanned ${tsFiles.length} files: ${anyCount} any, ${tsIgnoreCount} @ts-ignore, ${asAnyCount} as any, ${missingReturnTypeCount} missing return types`,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
return findings;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import type { Finding } from '../core/types';
|
|
4
|
+
import { BaseAgent } from './base-agent';
|
|
5
|
+
|
|
6
|
+
/** Find all .ts files under a directory (excluding tests and node_modules). */
|
|
7
|
+
function findTsFiles(dir: string, results: string[] = []): string[] {
|
|
8
|
+
if (!fs.existsSync(dir)) return results;
|
|
9
|
+
|
|
10
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
const fullPath = path.join(dir, entry.name);
|
|
13
|
+
if (entry.isDirectory() && entry.name !== 'node_modules' && entry.name !== '.git' && entry.name !== 'dist') {
|
|
14
|
+
findTsFiles(fullPath, results);
|
|
15
|
+
} else if (entry.isFile() && entry.name.endsWith('.ts') && !entry.name.endsWith('.test.ts') && !entry.name.endsWith('.spec.ts') && !entry.name.endsWith('.d.ts')) {
|
|
16
|
+
results.push(fullPath);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
return results;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Branch keywords for rough cyclomatic complexity. */
|
|
23
|
+
const BRANCH_KEYWORDS = /\b(if|else\s+if|else|switch|case|for|while|do|catch|&&|\|\||\?\?)\b/g;
|
|
24
|
+
|
|
25
|
+
interface FunctionInfo {
|
|
26
|
+
name: string;
|
|
27
|
+
startLine: number;
|
|
28
|
+
branchCount: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export class ComplexityAnalyzerAgent extends BaseAgent {
|
|
32
|
+
readonly agentId = 70;
|
|
33
|
+
readonly agentName = 'Complexity Analyzer';
|
|
34
|
+
|
|
35
|
+
protected async preFlight(): Promise<void> {
|
|
36
|
+
// File-based agent
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
protected async execute(): Promise<Finding[]> {
|
|
40
|
+
const findings: Finding[] = [];
|
|
41
|
+
|
|
42
|
+
const projectRoot = this.config.projectRoot ?? process.cwd();
|
|
43
|
+
const srcDir = path.join(projectRoot, 'src');
|
|
44
|
+
|
|
45
|
+
if (!fs.existsSync(srcDir)) {
|
|
46
|
+
findings.push({
|
|
47
|
+
id: `${this.agentId}-no-src-dir`,
|
|
48
|
+
type: 'infra-issue',
|
|
49
|
+
severity: 'low',
|
|
50
|
+
agentId: this.agentId,
|
|
51
|
+
module: 'complexity-analyzer',
|
|
52
|
+
description: 'No src/ directory found in project root',
|
|
53
|
+
});
|
|
54
|
+
return findings;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const tsFiles = findTsFiles(srcDir);
|
|
58
|
+
let complexFunctionCount = 0;
|
|
59
|
+
let longFileCount = 0;
|
|
60
|
+
|
|
61
|
+
for (const filePath of tsFiles) {
|
|
62
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
63
|
+
const lines = content.split('\n');
|
|
64
|
+
const relPath = path.relative(projectRoot, filePath);
|
|
65
|
+
|
|
66
|
+
// Check file length
|
|
67
|
+
if (lines.length > 1000) {
|
|
68
|
+
findings.push({
|
|
69
|
+
id: `${this.agentId}-very-long-file-${longFileCount}`,
|
|
70
|
+
type: 'test-bug',
|
|
71
|
+
severity: 'medium',
|
|
72
|
+
agentId: this.agentId,
|
|
73
|
+
module: 'complexity-analyzer',
|
|
74
|
+
file: filePath,
|
|
75
|
+
description: `File ${relPath} has ${lines.length} lines (>1000) — consider splitting`,
|
|
76
|
+
});
|
|
77
|
+
longFileCount++;
|
|
78
|
+
} else if (lines.length > 500) {
|
|
79
|
+
findings.push({
|
|
80
|
+
id: `${this.agentId}-long-file-${longFileCount}`,
|
|
81
|
+
type: 'test-bug',
|
|
82
|
+
severity: 'low',
|
|
83
|
+
agentId: this.agentId,
|
|
84
|
+
module: 'complexity-analyzer',
|
|
85
|
+
file: filePath,
|
|
86
|
+
description: `File ${relPath} has ${lines.length} lines (>500) — consider refactoring`,
|
|
87
|
+
});
|
|
88
|
+
longFileCount++;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Analyze function complexity
|
|
92
|
+
const functions = this.extractFunctions(lines);
|
|
93
|
+
for (const fn of functions) {
|
|
94
|
+
if (fn.branchCount > 15) {
|
|
95
|
+
complexFunctionCount++;
|
|
96
|
+
if (complexFunctionCount <= 10) {
|
|
97
|
+
findings.push({
|
|
98
|
+
id: `${this.agentId}-complex-function-${complexFunctionCount}`,
|
|
99
|
+
type: 'test-bug',
|
|
100
|
+
severity: 'medium',
|
|
101
|
+
agentId: this.agentId,
|
|
102
|
+
module: 'complexity-analyzer',
|
|
103
|
+
file: filePath,
|
|
104
|
+
line: fn.startLine,
|
|
105
|
+
description: `Function "${fn.name}" in ${relPath}:${fn.startLine} has ${fn.branchCount} branches (>15) — high cyclomatic complexity`,
|
|
106
|
+
suggestedFix: 'Refactor into smaller functions with single responsibilities',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (complexFunctionCount > 10) {
|
|
114
|
+
findings.push({
|
|
115
|
+
id: `${this.agentId}-complex-functions-summary`,
|
|
116
|
+
type: 'test-bug',
|
|
117
|
+
severity: 'medium',
|
|
118
|
+
agentId: this.agentId,
|
|
119
|
+
module: 'complexity-analyzer',
|
|
120
|
+
description: `${complexFunctionCount} functions with high complexity found (showing first 10)`,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.addEvidence({
|
|
125
|
+
type: 'log',
|
|
126
|
+
path: '',
|
|
127
|
+
description: `Scanned ${tsFiles.length} files: ${complexFunctionCount} complex functions, ${longFileCount} long files`,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
return findings;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private extractFunctions(lines: string[]): FunctionInfo[] {
|
|
134
|
+
const functions: FunctionInfo[] = [];
|
|
135
|
+
let currentFunction: FunctionInfo | null = null;
|
|
136
|
+
let braceDepth = 0;
|
|
137
|
+
let functionBraceStart = 0;
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < lines.length; i++) {
|
|
140
|
+
const line = lines[i];
|
|
141
|
+
const trimmed = line.trim();
|
|
142
|
+
|
|
143
|
+
// Detect function start
|
|
144
|
+
const funcMatch = trimmed.match(/(?:async\s+)?(?:function\s+(\w+)|(\w+)\s*(?:=|:)\s*(?:async\s+)?(?:\([^)]*\)|[^=]*)=>|(\w+)\s*\([^)]*\)\s*(?::\s*[^{]+)?\s*\{)/);
|
|
145
|
+
|
|
146
|
+
if (funcMatch && !currentFunction) {
|
|
147
|
+
const name = funcMatch[1] ?? funcMatch[2] ?? funcMatch[3] ?? 'anonymous';
|
|
148
|
+
currentFunction = { name, startLine: i + 1, branchCount: 0 };
|
|
149
|
+
functionBraceStart = braceDepth;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Count braces
|
|
153
|
+
for (const ch of line) {
|
|
154
|
+
if (ch === '{') braceDepth++;
|
|
155
|
+
if (ch === '}') braceDepth--;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Count branches in current function
|
|
159
|
+
if (currentFunction) {
|
|
160
|
+
const matches = trimmed.match(BRANCH_KEYWORDS);
|
|
161
|
+
if (matches) {
|
|
162
|
+
currentFunction.branchCount += matches.length;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Function ended
|
|
167
|
+
if (currentFunction && braceDepth <= functionBraceStart) {
|
|
168
|
+
functions.push(currentFunction);
|
|
169
|
+
currentFunction = null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (currentFunction) {
|
|
174
|
+
functions.push(currentFunction);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return functions;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,274 @@
|
|
|
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
|
+
],
|
|
22
|
+
environments: {
|
|
23
|
+
alpha: { baseUrl: 'http://localhost:3000', seed: false },
|
|
24
|
+
},
|
|
25
|
+
portals: [],
|
|
26
|
+
breakpoints: [],
|
|
27
|
+
integrations: [],
|
|
28
|
+
workers: [],
|
|
29
|
+
workflows: [],
|
|
30
|
+
accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
|
|
31
|
+
tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
|
|
32
|
+
costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
|
|
33
|
+
agentClassification: { blocking: [], advisory: [24] },
|
|
34
|
+
thresholds: {
|
|
35
|
+
unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
|
|
36
|
+
lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
|
|
37
|
+
apiResponseTime: 2000,
|
|
38
|
+
bundleSizeLimit: 500000,
|
|
39
|
+
maxConsoleErrors: 0,
|
|
40
|
+
},
|
|
41
|
+
disabledAgents: [],
|
|
42
|
+
disabledAgentSet: new Set<number>(),
|
|
43
|
+
...overrides,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
// Mock fetch
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
const fetchMock = vi.fn();
|
|
52
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
53
|
+
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
// Mock helpers
|
|
56
|
+
// ---------------------------------------------------------------------------
|
|
57
|
+
|
|
58
|
+
const loginMock = vi.fn().mockResolvedValue(undefined);
|
|
59
|
+
|
|
60
|
+
vi.mock('../../helpers/navigation', () => ({
|
|
61
|
+
login: (...args: unknown[]) => loginMock(...args),
|
|
62
|
+
waitForSectionLoad: vi.fn().mockResolvedValue(undefined),
|
|
63
|
+
navigateToSection: vi.fn().mockResolvedValue(undefined),
|
|
64
|
+
switchPortal: vi.fn().mockResolvedValue(undefined),
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Mock Playwright
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
interface MockLocator {
|
|
72
|
+
count: ReturnType<typeof vi.fn>;
|
|
73
|
+
first: ReturnType<typeof vi.fn>;
|
|
74
|
+
nth: ReturnType<typeof vi.fn>;
|
|
75
|
+
click: ReturnType<typeof vi.fn>;
|
|
76
|
+
fill: 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
|
+
click: vi.fn().mockResolvedValue(undefined),
|
|
86
|
+
fill: vi.fn().mockResolvedValue(undefined),
|
|
87
|
+
innerText: vi.fn().mockResolvedValue('some text'),
|
|
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
|
+
url: ReturnType<typeof vi.fn>;
|
|
98
|
+
locator: ReturnType<typeof vi.fn>;
|
|
99
|
+
waitForTimeout: ReturnType<typeof vi.fn>;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function makeMockPage(): MockPage {
|
|
103
|
+
const defaultLocator = makeMockLocator(0);
|
|
104
|
+
return {
|
|
105
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
106
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
107
|
+
url: vi.fn().mockReturnValue('http://localhost:3000/dashboard'),
|
|
108
|
+
locator: vi.fn().mockReturnValue(defaultLocator),
|
|
109
|
+
waitForTimeout: vi.fn().mockResolvedValue(undefined),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let currentMockPage: MockPage;
|
|
114
|
+
let mockBrowser: { newPage: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
|
|
115
|
+
|
|
116
|
+
vi.mock('playwright', async () => ({
|
|
117
|
+
chromium: {
|
|
118
|
+
launch: vi.fn().mockImplementation(async () => mockBrowser),
|
|
119
|
+
},
|
|
120
|
+
}));
|
|
121
|
+
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
// Dynamic import AFTER mocks are hoisted
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
const { SignupOnboardingTesterAgent } = await import('../24-signup-onboarding-tester');
|
|
127
|
+
|
|
128
|
+
function createAgent(
|
|
129
|
+
config: ValidatedConfig = makeConfig(),
|
|
130
|
+
phase: Phase = 'alpha',
|
|
131
|
+
): InstanceType<typeof SignupOnboardingTesterAgent> {
|
|
132
|
+
return new SignupOnboardingTesterAgent(config, phase, '/tmp/test-run');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
// Tests
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
|
|
139
|
+
describe('SignupOnboardingTesterAgent', () => {
|
|
140
|
+
beforeEach(() => {
|
|
141
|
+
fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
|
|
142
|
+
loginMock.mockReset().mockResolvedValue(undefined);
|
|
143
|
+
|
|
144
|
+
currentMockPage = makeMockPage();
|
|
145
|
+
mockBrowser = {
|
|
146
|
+
newPage: vi.fn().mockResolvedValue(currentMockPage),
|
|
147
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Identity
|
|
152
|
+
it('has agentId 24 and correct agentName', () => {
|
|
153
|
+
const agent = createAgent();
|
|
154
|
+
expect(agent.agentId).toBe(24);
|
|
155
|
+
expect(agent.agentName).toBe('Signup & Onboarding Tester');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// preFlight succeeds
|
|
159
|
+
it('resolves environment in preFlight without error', async () => {
|
|
160
|
+
const agent = createAgent();
|
|
161
|
+
const result = await agent.run();
|
|
162
|
+
expect(result.findings.every(f => !f.description.includes('preFlight'))).toBe(true);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// No credentials
|
|
166
|
+
it('produces medium finding when no credentials configured', async () => {
|
|
167
|
+
const config = makeConfig({
|
|
168
|
+
auth: { loginUrl: 'http://localhost/login', credentials: {} },
|
|
169
|
+
});
|
|
170
|
+
const agent = createAgent(config);
|
|
171
|
+
const result = await agent.run();
|
|
172
|
+
|
|
173
|
+
const noCredsFinding = result.findings.find(f => f.id.includes('no-credentials'));
|
|
174
|
+
expect(noCredsFinding).toBeDefined();
|
|
175
|
+
expect(noCredsFinding!.severity).toBe('medium');
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Registration page not found
|
|
179
|
+
it('produces medium finding when no registration page found', async () => {
|
|
180
|
+
// goto resolves but no registration form fields (count=0)
|
|
181
|
+
const agent = createAgent();
|
|
182
|
+
const result = await agent.run();
|
|
183
|
+
|
|
184
|
+
const regFinding = result.findings.find(f => f.id.includes('no-register-page'));
|
|
185
|
+
expect(regFinding).toBeDefined();
|
|
186
|
+
expect(regFinding!.severity).toBe('medium');
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Registration page found with fields
|
|
190
|
+
it('detects registration form when email and password fields exist', async () => {
|
|
191
|
+
currentMockPage.locator.mockImplementation((sel: string) => {
|
|
192
|
+
if (sel.includes('email')) return makeMockLocator(1);
|
|
193
|
+
if (sel.includes('password')) return makeMockLocator(1);
|
|
194
|
+
if (sel.includes('name')) return makeMockLocator(1);
|
|
195
|
+
if (sel.includes('submit') || sel.includes('Register') || sel.includes('Sign up')) return makeMockLocator(1);
|
|
196
|
+
return makeMockLocator(0);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const agent = createAgent();
|
|
200
|
+
const result = await agent.run();
|
|
201
|
+
|
|
202
|
+
// Should NOT have a "no register page" finding
|
|
203
|
+
const regFinding = result.findings.find(f => f.id.includes('no-register-page'));
|
|
204
|
+
expect(regFinding).toBeUndefined();
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
// Login page missing inputs
|
|
208
|
+
it('produces high finding when login page missing email/password', async () => {
|
|
209
|
+
// All locators return 0 by default — means login page has no inputs
|
|
210
|
+
const agent = createAgent();
|
|
211
|
+
const result = await agent.run();
|
|
212
|
+
|
|
213
|
+
const loginMissing = result.findings.find(f => f.id.includes('login-missing-inputs'));
|
|
214
|
+
expect(loginMissing).toBeDefined();
|
|
215
|
+
expect(loginMissing!.severity).toBe('high');
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// Login flow stuck
|
|
219
|
+
it('produces medium finding when login stays on login page', async () => {
|
|
220
|
+
currentMockPage.url.mockReturnValue('http://localhost:3000/login');
|
|
221
|
+
|
|
222
|
+
const agent = createAgent();
|
|
223
|
+
const result = await agent.run();
|
|
224
|
+
|
|
225
|
+
const stuckFinding = result.findings.find(f => f.id.includes('login-flow-stuck'));
|
|
226
|
+
expect(stuckFinding).toBeDefined();
|
|
227
|
+
expect(stuckFinding!.severity).toBe('medium');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Onboarding detection
|
|
231
|
+
it('reports info finding about onboarding detection', async () => {
|
|
232
|
+
const agent = createAgent();
|
|
233
|
+
const result = await agent.run();
|
|
234
|
+
|
|
235
|
+
const onboardFinding = result.findings.find(
|
|
236
|
+
f => f.id.includes('onboarding-detected') || f.id.includes('no-onboarding'),
|
|
237
|
+
);
|
|
238
|
+
expect(onboardFinding).toBeDefined();
|
|
239
|
+
expect(onboardFinding!.severity).toBe('info');
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
// Playwright crash
|
|
243
|
+
it('produces medium finding when Playwright crashes', async () => {
|
|
244
|
+
mockBrowser.newPage.mockRejectedValue(new Error('Browser crashed'));
|
|
245
|
+
|
|
246
|
+
const agent = createAgent();
|
|
247
|
+
const result = await agent.run();
|
|
248
|
+
|
|
249
|
+
const pwFinding = result.findings.find(f => f.id.includes('playwright-failure'));
|
|
250
|
+
expect(pwFinding).toBeDefined();
|
|
251
|
+
expect(pwFinding!.severity).toBe('medium');
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// Browser closed in finally
|
|
255
|
+
it('closes browser even when an error occurs', async () => {
|
|
256
|
+
mockBrowser.newPage.mockRejectedValue(new Error('Launch error'));
|
|
257
|
+
|
|
258
|
+
const agent = createAgent();
|
|
259
|
+
await agent.run();
|
|
260
|
+
|
|
261
|
+
expect(mockBrowser.close).toHaveBeenCalled();
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
// Login flow error
|
|
265
|
+
it('handles login flow navigation errors gracefully', async () => {
|
|
266
|
+
currentMockPage.goto.mockRejectedValue(new Error('Navigation error'));
|
|
267
|
+
|
|
268
|
+
const agent = createAgent();
|
|
269
|
+
const result = await agent.run();
|
|
270
|
+
|
|
271
|
+
// Should have a navigation-related finding but not crash
|
|
272
|
+
expect(result.status).toBeDefined();
|
|
273
|
+
});
|
|
274
|
+
});
|