@avi770/testteam 1.2.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 +54 -0
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/agents/01-analyst.ts +100 -0
- package/agents/02-seed-architect.ts +59 -0
- package/agents/03-test-generator.ts +191 -0
- package/agents/04-unit-runner.ts +160 -0
- package/agents/05-browser-crawler.ts +790 -0
- package/agents/06-api-exerciser.ts +311 -0
- package/agents/07-security-scout.ts +188 -0
- package/agents/08-a11y-guardian.ts +212 -0
- package/agents/09-healer.ts +228 -0
- package/agents/10-reporter.ts +266 -0
- package/agents/11-fixer.ts +253 -0
- package/agents/12-ux-inspector.ts +444 -0
- package/agents/13-performance-profiler.ts +271 -0
- package/agents/14-data-integrity-auditor.ts +417 -0
- package/agents/15-regression-sentinel.ts +307 -0
- package/agents/16-chaos-agent.ts +228 -0
- package/agents/17-documentation-validator.ts +266 -0
- package/agents/18-integration-watchdog.ts +178 -0
- package/agents/19-tenant-isolation-auditor.ts +199 -0
- package/agents/20-workflow-completion-tester.ts +203 -0
- package/agents/21-state-session-tester.ts +262 -0
- package/agents/22-email-notification-verifier.ts +244 -0
- package/agents/23-migration-tester.ts +80 -0
- package/agents/__tests__/01-analyst.test.ts +188 -0
- package/agents/__tests__/02-seed-architect.test.ts +152 -0
- package/agents/__tests__/03-test-generator-full.test.ts +321 -0
- package/agents/__tests__/03-test-generator.test.ts +318 -0
- package/agents/__tests__/04-unit-runner.test.ts +320 -0
- package/agents/__tests__/05-browser-crawler-beta.test.ts +492 -0
- package/agents/__tests__/05-browser-crawler-release.test.ts +412 -0
- package/agents/__tests__/05-browser-crawler-uat.test.ts +578 -0
- package/agents/__tests__/05-browser-crawler.test.ts +518 -0
- package/agents/__tests__/06-api-exerciser.test.ts +619 -0
- package/agents/__tests__/07-security-scout.test.ts +382 -0
- package/agents/__tests__/08-a11y-guardian.test.ts +530 -0
- package/agents/__tests__/09-healer.test.ts +384 -0
- package/agents/__tests__/10-reporter.test.ts +366 -0
- package/agents/__tests__/11-fixer.test.ts +406 -0
- package/agents/__tests__/12-ux-inspector-extended.test.ts +465 -0
- package/agents/__tests__/12-ux-inspector.test.ts +443 -0
- package/agents/__tests__/13-performance-profiler.test.ts +411 -0
- package/agents/__tests__/14-data-integrity-auditor-extended.test.ts +573 -0
- package/agents/__tests__/14-data-integrity-auditor.test.ts +407 -0
- package/agents/__tests__/15-regression-sentinel.test.ts +657 -0
- package/agents/__tests__/16-chaos-agent.test.ts +427 -0
- package/agents/__tests__/17-documentation-validator.test.ts +402 -0
- package/agents/__tests__/18-integration-watchdog.test.ts +263 -0
- package/agents/__tests__/19-tenant-isolation-auditor.test.ts +400 -0
- package/agents/__tests__/20-workflow-completion-tester.test.ts +586 -0
- package/agents/__tests__/21-state-session-tester.test.ts +374 -0
- package/agents/__tests__/22-email-notification-verifier.test.ts +441 -0
- package/agents/__tests__/23-migration-tester.test.ts +145 -0
- package/agents/__tests__/base-agent.test.ts +188 -0
- package/agents/__tests__/registry.test.ts +218 -0
- package/agents/base-agent.ts +77 -0
- package/agents/registry.ts +136 -0
- package/baselines/api-schemas/.gitkeep +0 -0
- package/baselines/performance/.gitkeep +0 -0
- package/baselines/screenshots/.gitkeep +0 -0
- package/bin/testteam.js +10 -0
- package/core/__tests__/ci-output.test.ts +430 -0
- package/core/__tests__/cli.test.ts +387 -0
- package/core/__tests__/config.test.ts +78 -0
- package/core/__tests__/cost-tracker.test.ts +158 -0
- package/core/__tests__/evidence.test.ts +265 -0
- package/core/__tests__/fix-loop.test.ts +210 -0
- package/core/__tests__/health-check.test.ts +44 -0
- package/core/__tests__/init.test.ts +609 -0
- package/core/__tests__/integration.test.ts +204 -0
- package/core/__tests__/license-gen.test.ts +227 -0
- package/core/__tests__/license.test.ts +326 -0
- package/core/__tests__/multi-browser.test.ts +278 -0
- package/core/__tests__/orchestrator.test.ts +519 -0
- package/core/__tests__/phase-gate.test.ts +43 -0
- package/core/__tests__/report-html.test.ts +398 -0
- package/core/__tests__/report-upload.test.ts +325 -0
- package/core/__tests__/run-counter.test.ts +234 -0
- package/core/ci-output.ts +240 -0
- package/core/cli.ts +232 -0
- package/core/config.ts +178 -0
- package/core/cost-tracker.ts +59 -0
- package/core/evidence.ts +132 -0
- package/core/fix-loop.ts +85 -0
- package/core/health-check.ts +54 -0
- package/core/init.ts +546 -0
- package/core/license-gen.ts +212 -0
- package/core/license.ts +211 -0
- package/core/messages.ts +67 -0
- package/core/multi-browser.ts +136 -0
- package/core/orchestrator.ts +354 -0
- package/core/phase-gate.ts +55 -0
- package/core/report-html.ts +657 -0
- package/core/report-upload.ts +188 -0
- package/core/run-counter.ts +175 -0
- package/core/types.ts +57 -0
- package/dist/agents/01-analyst.d.ts +11 -0
- package/dist/agents/01-analyst.d.ts.map +1 -0
- package/dist/agents/01-analyst.js +75 -0
- package/dist/agents/01-analyst.js.map +1 -0
- package/dist/agents/02-seed-architect.d.ts +11 -0
- package/dist/agents/02-seed-architect.d.ts.map +1 -0
- package/dist/agents/02-seed-architect.js +51 -0
- package/dist/agents/02-seed-architect.js.map +1 -0
- package/dist/agents/03-test-generator.d.ts +9 -0
- package/dist/agents/03-test-generator.d.ts.map +1 -0
- package/dist/agents/03-test-generator.js +167 -0
- package/dist/agents/03-test-generator.js.map +1 -0
- package/dist/agents/04-unit-runner.d.ts +9 -0
- package/dist/agents/04-unit-runner.d.ts.map +1 -0
- package/dist/agents/04-unit-runner.js +113 -0
- package/dist/agents/04-unit-runner.js.map +1 -0
- package/dist/agents/05-browser-crawler.d.ts +30 -0
- package/dist/agents/05-browser-crawler.d.ts.map +1 -0
- package/dist/agents/05-browser-crawler.js +685 -0
- package/dist/agents/05-browser-crawler.js.map +1 -0
- package/dist/agents/06-api-exerciser.d.ts +23 -0
- package/dist/agents/06-api-exerciser.d.ts.map +1 -0
- package/dist/agents/06-api-exerciser.js +253 -0
- package/dist/agents/06-api-exerciser.js.map +1 -0
- package/dist/agents/07-security-scout.d.ts +11 -0
- package/dist/agents/07-security-scout.d.ts.map +1 -0
- package/dist/agents/07-security-scout.js +142 -0
- package/dist/agents/07-security-scout.js.map +1 -0
- package/dist/agents/08-a11y-guardian.d.ts +13 -0
- package/dist/agents/08-a11y-guardian.d.ts.map +1 -0
- package/dist/agents/08-a11y-guardian.js +176 -0
- package/dist/agents/08-a11y-guardian.js.map +1 -0
- package/dist/agents/09-healer.d.ts +33 -0
- package/dist/agents/09-healer.d.ts.map +1 -0
- package/dist/agents/09-healer.js +167 -0
- package/dist/agents/09-healer.js.map +1 -0
- package/dist/agents/10-reporter.d.ts +26 -0
- package/dist/agents/10-reporter.d.ts.map +1 -0
- package/dist/agents/10-reporter.js +215 -0
- package/dist/agents/10-reporter.js.map +1 -0
- package/dist/agents/11-fixer.d.ts +26 -0
- package/dist/agents/11-fixer.d.ts.map +1 -0
- package/dist/agents/11-fixer.js +195 -0
- package/dist/agents/11-fixer.js.map +1 -0
- package/dist/agents/12-ux-inspector.d.ts +15 -0
- package/dist/agents/12-ux-inspector.d.ts.map +1 -0
- package/dist/agents/12-ux-inspector.js +364 -0
- package/dist/agents/12-ux-inspector.js.map +1 -0
- package/dist/agents/13-performance-profiler.d.ts +13 -0
- package/dist/agents/13-performance-profiler.d.ts.map +1 -0
- package/dist/agents/13-performance-profiler.js +216 -0
- package/dist/agents/13-performance-profiler.js.map +1 -0
- package/dist/agents/14-data-integrity-auditor.d.ts +12 -0
- package/dist/agents/14-data-integrity-auditor.d.ts.map +1 -0
- package/dist/agents/14-data-integrity-auditor.js +356 -0
- package/dist/agents/14-data-integrity-auditor.js.map +1 -0
- package/dist/agents/15-regression-sentinel.d.ts +25 -0
- package/dist/agents/15-regression-sentinel.d.ts.map +1 -0
- package/dist/agents/15-regression-sentinel.js +251 -0
- package/dist/agents/15-regression-sentinel.js.map +1 -0
- package/dist/agents/16-chaos-agent.d.ts +9 -0
- package/dist/agents/16-chaos-agent.d.ts.map +1 -0
- package/dist/agents/16-chaos-agent.js +207 -0
- package/dist/agents/16-chaos-agent.js.map +1 -0
- package/dist/agents/17-documentation-validator.d.ts +31 -0
- package/dist/agents/17-documentation-validator.d.ts.map +1 -0
- package/dist/agents/17-documentation-validator.js +246 -0
- package/dist/agents/17-documentation-validator.js.map +1 -0
- package/dist/agents/18-integration-watchdog.d.ts +10 -0
- package/dist/agents/18-integration-watchdog.d.ts.map +1 -0
- package/dist/agents/18-integration-watchdog.js +138 -0
- package/dist/agents/18-integration-watchdog.js.map +1 -0
- package/dist/agents/19-tenant-isolation-auditor.d.ts +9 -0
- package/dist/agents/19-tenant-isolation-auditor.d.ts.map +1 -0
- package/dist/agents/19-tenant-isolation-auditor.js +166 -0
- package/dist/agents/19-tenant-isolation-auditor.js.map +1 -0
- package/dist/agents/20-workflow-completion-tester.d.ts +12 -0
- package/dist/agents/20-workflow-completion-tester.d.ts.map +1 -0
- package/dist/agents/20-workflow-completion-tester.js +159 -0
- package/dist/agents/20-workflow-completion-tester.js.map +1 -0
- package/dist/agents/21-state-session-tester.d.ts +10 -0
- package/dist/agents/21-state-session-tester.d.ts.map +1 -0
- package/dist/agents/21-state-session-tester.js +233 -0
- package/dist/agents/21-state-session-tester.js.map +1 -0
- package/dist/agents/22-email-notification-verifier.d.ts +11 -0
- package/dist/agents/22-email-notification-verifier.d.ts.map +1 -0
- package/dist/agents/22-email-notification-verifier.js +199 -0
- package/dist/agents/22-email-notification-verifier.js.map +1 -0
- package/dist/agents/23-migration-tester.d.ts +10 -0
- package/dist/agents/23-migration-tester.d.ts.map +1 -0
- package/dist/agents/23-migration-tester.js +74 -0
- package/dist/agents/23-migration-tester.js.map +1 -0
- package/dist/agents/base-agent.d.ts +19 -0
- package/dist/agents/base-agent.d.ts.map +1 -0
- package/dist/agents/base-agent.js +67 -0
- package/dist/agents/base-agent.js.map +1 -0
- package/dist/agents/registry.d.ts +29 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/registry.js +117 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/core/ci-output.d.ts +35 -0
- package/dist/core/ci-output.d.ts.map +1 -0
- package/dist/core/ci-output.js +193 -0
- package/dist/core/ci-output.js.map +1 -0
- package/dist/core/cli.d.ts +11 -0
- package/dist/core/cli.d.ts.map +1 -0
- package/dist/core/cli.js +197 -0
- package/dist/core/cli.js.map +1 -0
- package/dist/core/config.d.ts +111 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +42 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/cost-tracker.d.ts +22 -0
- package/dist/core/cost-tracker.d.ts.map +1 -0
- package/dist/core/cost-tracker.js +41 -0
- package/dist/core/cost-tracker.js.map +1 -0
- package/dist/core/evidence.d.ts +28 -0
- package/dist/core/evidence.d.ts.map +1 -0
- package/dist/core/evidence.js +95 -0
- package/dist/core/evidence.js.map +1 -0
- package/dist/core/fix-loop.d.ts +29 -0
- package/dist/core/fix-loop.d.ts.map +1 -0
- package/dist/core/fix-loop.js +70 -0
- package/dist/core/fix-loop.js.map +1 -0
- package/dist/core/health-check.d.ts +21 -0
- package/dist/core/health-check.d.ts.map +1 -0
- package/dist/core/health-check.js +26 -0
- package/dist/core/health-check.js.map +1 -0
- package/dist/core/init.d.ts +2 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +435 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/license-gen.d.ts +12 -0
- package/dist/core/license-gen.d.ts.map +1 -0
- package/dist/core/license-gen.js +169 -0
- package/dist/core/license-gen.js.map +1 -0
- package/dist/core/license.d.ts +33 -0
- package/dist/core/license.d.ts.map +1 -0
- package/dist/core/license.js +170 -0
- package/dist/core/license.js.map +1 -0
- package/dist/core/messages.d.ts +10 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/messages.js +47 -0
- package/dist/core/messages.js.map +1 -0
- package/dist/core/multi-browser.d.ts +36 -0
- package/dist/core/multi-browser.d.ts.map +1 -0
- package/dist/core/multi-browser.js +88 -0
- package/dist/core/multi-browser.js.map +1 -0
- package/dist/core/orchestrator.d.ts +48 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +291 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/phase-gate.d.ts +4 -0
- package/dist/core/phase-gate.d.ts.map +1 -0
- package/dist/core/phase-gate.js +39 -0
- package/dist/core/phase-gate.js.map +1 -0
- package/dist/core/report-html.d.ts +9 -0
- package/dist/core/report-html.d.ts.map +1 -0
- package/dist/core/report-html.js +617 -0
- package/dist/core/report-html.js.map +1 -0
- package/dist/core/report-upload.d.ts +16 -0
- package/dist/core/report-upload.d.ts.map +1 -0
- package/dist/core/report-upload.js +124 -0
- package/dist/core/report-upload.js.map +1 -0
- package/dist/core/run-counter.d.ts +40 -0
- package/dist/core/run-counter.d.ts.map +1 -0
- package/dist/core/run-counter.js +120 -0
- package/dist/core/run-counter.js.map +1 -0
- package/dist/core/types.d.ts +53 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/helpers/api-client.d.ts +30 -0
- package/dist/helpers/api-client.d.ts.map +1 -0
- package/dist/helpers/api-client.js +77 -0
- package/dist/helpers/api-client.js.map +1 -0
- package/dist/helpers/element-discovery.d.ts +18 -0
- package/dist/helpers/element-discovery.d.ts.map +1 -0
- package/dist/helpers/element-discovery.js +82 -0
- package/dist/helpers/element-discovery.js.map +1 -0
- package/dist/helpers/env-resolver.d.ts +29 -0
- package/dist/helpers/env-resolver.d.ts.map +1 -0
- package/dist/helpers/env-resolver.js +51 -0
- package/dist/helpers/env-resolver.js.map +1 -0
- package/dist/helpers/form-filler.d.ts +13 -0
- package/dist/helpers/form-filler.d.ts.map +1 -0
- package/dist/helpers/form-filler.js +98 -0
- package/dist/helpers/form-filler.js.map +1 -0
- package/dist/helpers/modal-handler.d.ts +16 -0
- package/dist/helpers/modal-handler.d.ts.map +1 -0
- package/dist/helpers/modal-handler.js +95 -0
- package/dist/helpers/modal-handler.js.map +1 -0
- package/dist/helpers/navigation.d.ts +37 -0
- package/dist/helpers/navigation.d.ts.map +1 -0
- package/dist/helpers/navigation.js +83 -0
- package/dist/helpers/navigation.js.map +1 -0
- package/dist/helpers/quality-gate.d.ts +17 -0
- package/dist/helpers/quality-gate.d.ts.map +1 -0
- package/dist/helpers/quality-gate.js +144 -0
- package/dist/helpers/quality-gate.js.map +1 -0
- package/dist/helpers/screenshot.d.ts +24 -0
- package/dist/helpers/screenshot.d.ts.map +1 -0
- package/dist/helpers/screenshot.js +76 -0
- package/dist/helpers/screenshot.js.map +1 -0
- package/dist/helpers/seed-validator.d.ts +15 -0
- package/dist/helpers/seed-validator.d.ts.map +1 -0
- package/dist/helpers/seed-validator.js +53 -0
- package/dist/helpers/seed-validator.js.map +1 -0
- package/helpers/__tests__/api-client.test.ts +199 -0
- package/helpers/__tests__/element-discovery.test.ts +202 -0
- package/helpers/__tests__/form-filler-extended.test.ts +212 -0
- package/helpers/__tests__/form-filler.test.ts +99 -0
- package/helpers/__tests__/modal-handler.test.ts +152 -0
- package/helpers/__tests__/navigation.test.ts +214 -0
- package/helpers/__tests__/quality-gate.test.ts +117 -0
- package/helpers/__tests__/screenshot.test.ts +139 -0
- package/helpers/__tests__/seed-validator.test.ts +114 -0
- package/helpers/api-client.ts +111 -0
- package/helpers/element-discovery.ts +105 -0
- package/helpers/env-resolver.ts +69 -0
- package/helpers/form-filler.ts +126 -0
- package/helpers/modal-handler.ts +108 -0
- package/helpers/navigation.ts +100 -0
- package/helpers/quality-gate.ts +180 -0
- package/helpers/screenshot.ts +111 -0
- package/helpers/seed-validator.ts +70 -0
- package/package.json +88 -0
|
@@ -0,0 +1,374 @@
|
|
|
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
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
const stubConfig: ValidatedConfig = {
|
|
10
|
+
schemaVersion: 1,
|
|
11
|
+
name: 'test',
|
|
12
|
+
auth: {
|
|
13
|
+
loginUrl: 'http://localhost/login',
|
|
14
|
+
credentials: { admin: { email: 'admin@test.com', password: 'secret' } },
|
|
15
|
+
},
|
|
16
|
+
modules: [
|
|
17
|
+
{ id: 'dashboard', route: '/dashboard', sidebarIcon: true },
|
|
18
|
+
{ id: 'invoices', route: '/invoices', sidebarIcon: true },
|
|
19
|
+
{ id: 'clients', route: '/clients', sidebarIcon: true },
|
|
20
|
+
],
|
|
21
|
+
environments: {
|
|
22
|
+
beta: { baseUrl: 'http://localhost:3000', seed: false },
|
|
23
|
+
},
|
|
24
|
+
portals: [],
|
|
25
|
+
breakpoints: [],
|
|
26
|
+
integrations: [],
|
|
27
|
+
workers: [],
|
|
28
|
+
workflows: [],
|
|
29
|
+
accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
|
|
30
|
+
tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
|
|
31
|
+
costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
|
|
32
|
+
agentClassification: { blocking: [], advisory: [] },
|
|
33
|
+
thresholds: {
|
|
34
|
+
unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
|
|
35
|
+
lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
|
|
36
|
+
apiResponseTime: 2000,
|
|
37
|
+
bundleSizeLimit: 500000,
|
|
38
|
+
maxConsoleErrors: 0,
|
|
39
|
+
},
|
|
40
|
+
disabledAgents: [],
|
|
41
|
+
disabledAgentSet: new Set<number>(),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// Mock global fetch (preFlight URL reachability)
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
const fetchMock = vi.fn();
|
|
49
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
50
|
+
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Mock Playwright
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
interface MockPage {
|
|
56
|
+
goto: ReturnType<typeof vi.fn>;
|
|
57
|
+
fill: ReturnType<typeof vi.fn>;
|
|
58
|
+
click: ReturnType<typeof vi.fn>;
|
|
59
|
+
waitForNavigation: ReturnType<typeof vi.fn>;
|
|
60
|
+
reload: ReturnType<typeof vi.fn>;
|
|
61
|
+
goBack: ReturnType<typeof vi.fn>;
|
|
62
|
+
url: ReturnType<typeof vi.fn>;
|
|
63
|
+
content: ReturnType<typeof vi.fn>;
|
|
64
|
+
close: ReturnType<typeof vi.fn>;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function makeMockPage(urlAfterGoto = 'http://localhost:3000/login'): MockPage {
|
|
68
|
+
return {
|
|
69
|
+
goto: vi.fn().mockResolvedValue(undefined),
|
|
70
|
+
fill: vi.fn().mockResolvedValue(undefined),
|
|
71
|
+
click: vi.fn().mockResolvedValue(undefined),
|
|
72
|
+
waitForNavigation: vi.fn().mockResolvedValue(undefined),
|
|
73
|
+
reload: vi.fn().mockResolvedValue(undefined),
|
|
74
|
+
goBack: vi.fn().mockResolvedValue(undefined),
|
|
75
|
+
url: vi.fn().mockReturnValue(urlAfterGoto),
|
|
76
|
+
content: vi.fn().mockResolvedValue('<html>dashboard content</html>'),
|
|
77
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let currentMockPage: MockPage;
|
|
82
|
+
let mockContext: { newPage: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
|
|
83
|
+
let mockBrowser: { newContext: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
|
|
84
|
+
|
|
85
|
+
vi.mock('playwright', async () => ({
|
|
86
|
+
chromium: {
|
|
87
|
+
launch: vi.fn().mockImplementation(async () => mockBrowser),
|
|
88
|
+
},
|
|
89
|
+
}));
|
|
90
|
+
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
// Import after mocks
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
const { StateSessionTesterAgent } = await import('../21-state-session-tester');
|
|
96
|
+
|
|
97
|
+
function createAgent(phase: Phase = 'beta'): InstanceType<typeof StateSessionTesterAgent> {
|
|
98
|
+
return new StateSessionTesterAgent(stubConfig, phase, '/tmp/test-run');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Tests
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
describe('StateSessionTesterAgent', () => {
|
|
106
|
+
beforeEach(() => {
|
|
107
|
+
fetchMock.mockReset();
|
|
108
|
+
fetchMock.mockResolvedValue({ ok: true, status: 200 });
|
|
109
|
+
|
|
110
|
+
currentMockPage = makeMockPage('http://localhost:3000/login');
|
|
111
|
+
mockContext = {
|
|
112
|
+
newPage: vi.fn().mockResolvedValue(currentMockPage),
|
|
113
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
114
|
+
};
|
|
115
|
+
mockBrowser = {
|
|
116
|
+
newContext: vi.fn().mockResolvedValue(mockContext),
|
|
117
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('has agentId 21 and agentName "State & Session Tester"', () => {
|
|
122
|
+
const agent = createAgent();
|
|
123
|
+
expect(agent.agentId).toBe(21);
|
|
124
|
+
expect(agent.agentName).toBe('State & Session Tester');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// -------------------------------------------------------------------------
|
|
128
|
+
// preFlight()
|
|
129
|
+
// -------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
describe('preFlight()', () => {
|
|
132
|
+
it('resolves gracefully when base URL is not reachable (fallback)', async () => {
|
|
133
|
+
fetchMock.mockRejectedValue(new Error('ECONNREFUSED'));
|
|
134
|
+
|
|
135
|
+
const agent = createAgent();
|
|
136
|
+
const result = await agent.run();
|
|
137
|
+
|
|
138
|
+
// With resolveEnvironment the agent no longer throws on unreachable URLs.
|
|
139
|
+
expect(result.status).toBe('passed');
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it('passes when base URL is reachable', async () => {
|
|
143
|
+
fetchMock.mockResolvedValue({ ok: true, status: 200 });
|
|
144
|
+
|
|
145
|
+
const agent = createAgent();
|
|
146
|
+
const result = await agent.run();
|
|
147
|
+
|
|
148
|
+
expect(result.findings.some((f) => f.description.includes('not reachable'))).toBe(false);
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// -------------------------------------------------------------------------
|
|
153
|
+
// execute() — deep link scenario
|
|
154
|
+
// -------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
describe('deep link scenario', () => {
|
|
157
|
+
it('creates no finding when deep link redirects to login (correct behaviour)', async () => {
|
|
158
|
+
// url() returns login url — correct redirect
|
|
159
|
+
currentMockPage.url.mockReturnValue('http://localhost:3000/login');
|
|
160
|
+
|
|
161
|
+
const agent = createAgent();
|
|
162
|
+
const result = await agent.run();
|
|
163
|
+
|
|
164
|
+
const deepLinkFinding = result.findings.find((f) => f.id.includes('deep-link'));
|
|
165
|
+
expect(deepLinkFinding).toBeUndefined();
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('creates a medium finding when deep link lands on unexpected page (not login, not module)', async () => {
|
|
169
|
+
// url() returns a non-login, non-module URL
|
|
170
|
+
currentMockPage.url.mockReturnValue('http://localhost:3000/unexpected-error');
|
|
171
|
+
currentMockPage.content.mockResolvedValue('<html>500 Error</html>');
|
|
172
|
+
|
|
173
|
+
const agent = createAgent();
|
|
174
|
+
const result = await agent.run();
|
|
175
|
+
|
|
176
|
+
const deepLinkFinding = result.findings.find((f) => f.id.includes('deep-link'));
|
|
177
|
+
expect(deepLinkFinding).toBeDefined();
|
|
178
|
+
expect(deepLinkFinding!.severity).toBe('medium');
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// -------------------------------------------------------------------------
|
|
183
|
+
// execute() — refresh persistence scenario
|
|
184
|
+
// -------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
describe('refresh persistence scenario', () => {
|
|
187
|
+
it('creates a medium finding when page reload redirects to login', async () => {
|
|
188
|
+
// After reload, url() returns login url — session was lost
|
|
189
|
+
currentMockPage.url
|
|
190
|
+
.mockReturnValueOnce('http://localhost:3000/login') // deep link: login redirect (no finding)
|
|
191
|
+
.mockReturnValueOnce('http://localhost:3000/dashboard') // before reload
|
|
192
|
+
.mockReturnValueOnce('http://localhost:3000/login'); // after reload — session lost
|
|
193
|
+
|
|
194
|
+
const agent = createAgent();
|
|
195
|
+
const result = await agent.run();
|
|
196
|
+
|
|
197
|
+
const refreshFinding = result.findings.find((f) => f.id.includes('refresh'));
|
|
198
|
+
expect(refreshFinding).toBeDefined();
|
|
199
|
+
expect(refreshFinding!.severity).toBe('medium');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('creates no finding when page reload stays on the same module', async () => {
|
|
203
|
+
// After reload, url() stays on the module — session persisted
|
|
204
|
+
currentMockPage.url
|
|
205
|
+
.mockReturnValueOnce('http://localhost:3000/login') // deep link: login redirect (no finding)
|
|
206
|
+
.mockReturnValue('http://localhost:3000/dashboard'); // before and after reload
|
|
207
|
+
|
|
208
|
+
const agent = createAgent();
|
|
209
|
+
const result = await agent.run();
|
|
210
|
+
|
|
211
|
+
const refreshFinding = result.findings.find((f) => f.id.includes('refresh'));
|
|
212
|
+
expect(refreshFinding).toBeUndefined();
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// -------------------------------------------------------------------------
|
|
217
|
+
// execute() — back/forward navigation scenario
|
|
218
|
+
// -------------------------------------------------------------------------
|
|
219
|
+
|
|
220
|
+
describe('back/forward navigation scenario', () => {
|
|
221
|
+
it('creates no finding when goBack lands on the correct module', async () => {
|
|
222
|
+
// Sequence: login, dashboard (deep link no finding), dashboard (refresh persist), dashboard, invoices, clients, back, back → dashboard
|
|
223
|
+
currentMockPage.url
|
|
224
|
+
.mockReturnValueOnce('http://localhost:3000/login') // deep link scenario
|
|
225
|
+
.mockReturnValueOnce('http://localhost:3000/dashboard') // before reload (refresh scenario)
|
|
226
|
+
.mockReturnValue('http://localhost:3000/dashboard'); // back/forward result
|
|
227
|
+
|
|
228
|
+
const agent = createAgent();
|
|
229
|
+
const result = await agent.run();
|
|
230
|
+
|
|
231
|
+
const backFinding = result.findings.find((f) => f.id.includes('back-forward'));
|
|
232
|
+
expect(backFinding).toBeUndefined();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('creates a low finding when goBack does not land on the expected module', async () => {
|
|
236
|
+
// deep link, refresh OK, then back/forward ends at wrong URL
|
|
237
|
+
currentMockPage.url
|
|
238
|
+
.mockReturnValueOnce('http://localhost:3000/login') // deep link
|
|
239
|
+
.mockReturnValueOnce('http://localhost:3000/dashboard') // before reload
|
|
240
|
+
.mockReturnValue('http://localhost:3000/settings'); // back/forward result (wrong)
|
|
241
|
+
|
|
242
|
+
const agent = createAgent();
|
|
243
|
+
const result = await agent.run();
|
|
244
|
+
|
|
245
|
+
const backFinding = result.findings.find((f) => f.id.includes('back-forward'));
|
|
246
|
+
expect(backFinding).toBeDefined();
|
|
247
|
+
expect(backFinding!.severity).toBe('low');
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
// -------------------------------------------------------------------------
|
|
252
|
+
// Missing credentials
|
|
253
|
+
// -------------------------------------------------------------------------
|
|
254
|
+
|
|
255
|
+
describe('no credentials', () => {
|
|
256
|
+
it('creates a critical finding when no credentials are configured', async () => {
|
|
257
|
+
const noCredConfig: ValidatedConfig = {
|
|
258
|
+
...stubConfig,
|
|
259
|
+
auth: { loginUrl: 'http://localhost/login', credentials: {} },
|
|
260
|
+
};
|
|
261
|
+
const agent = new StateSessionTesterAgent(noCredConfig, 'beta', '/tmp/test-run');
|
|
262
|
+
const result = await agent.run();
|
|
263
|
+
|
|
264
|
+
const finding = result.findings.find(f => f.id.includes('no-credentials'));
|
|
265
|
+
expect(finding).toBeDefined();
|
|
266
|
+
expect(finding!.severity).toBe('critical');
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
// -------------------------------------------------------------------------
|
|
271
|
+
// No modules
|
|
272
|
+
// -------------------------------------------------------------------------
|
|
273
|
+
|
|
274
|
+
describe('no modules', () => {
|
|
275
|
+
it('creates a medium finding when no modules are configured', async () => {
|
|
276
|
+
const noModConfig: ValidatedConfig = {
|
|
277
|
+
...stubConfig,
|
|
278
|
+
modules: [],
|
|
279
|
+
};
|
|
280
|
+
const agent = new StateSessionTesterAgent(noModConfig, 'beta', '/tmp/test-run');
|
|
281
|
+
const result = await agent.run();
|
|
282
|
+
|
|
283
|
+
const finding = result.findings.find(f => f.id.includes('no-modules'));
|
|
284
|
+
expect(finding).toBeDefined();
|
|
285
|
+
expect(finding!.severity).toBe('medium');
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
// -------------------------------------------------------------------------
|
|
290
|
+
// Deep link: error scenario
|
|
291
|
+
// -------------------------------------------------------------------------
|
|
292
|
+
|
|
293
|
+
describe('deep link error', () => {
|
|
294
|
+
it('creates an infra-issue finding when deep link navigation throws', async () => {
|
|
295
|
+
currentMockPage.goto.mockRejectedValue(new Error('ERR_CONNECTION_REFUSED'));
|
|
296
|
+
|
|
297
|
+
const agent = createAgent();
|
|
298
|
+
const result = await agent.run();
|
|
299
|
+
|
|
300
|
+
const errorFinding = result.findings.find(f => f.id.includes('deep-link-error'));
|
|
301
|
+
expect(errorFinding).toBeDefined();
|
|
302
|
+
expect(errorFinding!.severity).toBe('medium');
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// -------------------------------------------------------------------------
|
|
307
|
+
// Refresh persistence error
|
|
308
|
+
// -------------------------------------------------------------------------
|
|
309
|
+
|
|
310
|
+
describe('refresh persistence error', () => {
|
|
311
|
+
it('creates an infra-issue finding when refresh scenario throws', async () => {
|
|
312
|
+
// Deep link succeeds (login redirect), but then second browser launch fails
|
|
313
|
+
let launchCount = 0;
|
|
314
|
+
mockBrowser.newContext.mockImplementation(async () => {
|
|
315
|
+
launchCount++;
|
|
316
|
+
if (launchCount === 2) throw new Error('Browser crashed');
|
|
317
|
+
return mockContext;
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const agent = createAgent();
|
|
321
|
+
const result = await agent.run();
|
|
322
|
+
|
|
323
|
+
const errorFinding = result.findings.find(f => f.id.includes('refresh-error'));
|
|
324
|
+
expect(errorFinding).toBeDefined();
|
|
325
|
+
});
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// -------------------------------------------------------------------------
|
|
329
|
+
// Back/forward with fewer than 3 modules — skips scenario
|
|
330
|
+
// -------------------------------------------------------------------------
|
|
331
|
+
|
|
332
|
+
describe('back/forward with fewer than 3 modules', () => {
|
|
333
|
+
it('skips back/forward scenario when fewer than 3 modules configured', async () => {
|
|
334
|
+
const twoModConfig: ValidatedConfig = {
|
|
335
|
+
...stubConfig,
|
|
336
|
+
modules: [
|
|
337
|
+
{ id: 'dashboard', route: '/dashboard', sidebarIcon: true },
|
|
338
|
+
{ id: 'invoices', route: '/invoices', sidebarIcon: true },
|
|
339
|
+
],
|
|
340
|
+
};
|
|
341
|
+
currentMockPage.url.mockReturnValue('http://localhost:3000/login');
|
|
342
|
+
|
|
343
|
+
const agent = new StateSessionTesterAgent(twoModConfig, 'beta', '/tmp/test-run');
|
|
344
|
+
const result = await agent.run();
|
|
345
|
+
|
|
346
|
+
const backFinding = result.findings.find(f => f.id.includes('back-forward'));
|
|
347
|
+
expect(backFinding).toBeUndefined();
|
|
348
|
+
});
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// -------------------------------------------------------------------------
|
|
352
|
+
// Back/forward error scenario
|
|
353
|
+
// -------------------------------------------------------------------------
|
|
354
|
+
|
|
355
|
+
describe('back/forward error', () => {
|
|
356
|
+
it('creates an infra-issue finding when back/forward scenario throws', async () => {
|
|
357
|
+
let launchCount = 0;
|
|
358
|
+
mockBrowser.newContext.mockImplementation(async () => {
|
|
359
|
+
launchCount++;
|
|
360
|
+
if (launchCount === 3) throw new Error('Out of memory');
|
|
361
|
+
return mockContext;
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
currentMockPage.url.mockReturnValue('http://localhost:3000/login');
|
|
365
|
+
|
|
366
|
+
const agent = createAgent();
|
|
367
|
+
const result = await agent.run();
|
|
368
|
+
|
|
369
|
+
const errorFinding = result.findings.find(f => f.id.includes('back-forward-error'));
|
|
370
|
+
expect(errorFinding).toBeDefined();
|
|
371
|
+
expect(errorFinding!.severity).toBe('low');
|
|
372
|
+
});
|
|
373
|
+
});
|
|
374
|
+
});
|