@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,411 @@
|
|
|
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 = {
|
|
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: 'reports', route: '/reports', sidebarIcon: true },
|
|
19
|
+
],
|
|
20
|
+
environments: {
|
|
21
|
+
beta: { baseUrl: 'http://localhost:3000', seed: false },
|
|
22
|
+
},
|
|
23
|
+
portals: [],
|
|
24
|
+
breakpoints: [],
|
|
25
|
+
integrations: [],
|
|
26
|
+
workers: [],
|
|
27
|
+
workflows: [],
|
|
28
|
+
accuracy: { enabled: true, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
|
|
29
|
+
tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
|
|
30
|
+
costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' as const },
|
|
31
|
+
agentClassification: { blocking: [], advisory: [] },
|
|
32
|
+
thresholds: {
|
|
33
|
+
unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
|
|
34
|
+
lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
|
|
35
|
+
apiResponseTime: 2000,
|
|
36
|
+
bundleSizeLimit: 500000,
|
|
37
|
+
maxConsoleErrors: 0,
|
|
38
|
+
},
|
|
39
|
+
disabledAgents: [],
|
|
40
|
+
disabledAgentSet: new Set<number>(),
|
|
41
|
+
} satisfies ValidatedConfig;
|
|
42
|
+
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
// Mock fetch (used in preFlight isUrlReachable)
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
|
|
47
|
+
const fetchMock = vi.fn();
|
|
48
|
+
vi.stubGlobal('fetch', fetchMock);
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Mock navigation helpers
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
const loginMock = vi.fn().mockResolvedValue(undefined);
|
|
55
|
+
const navigateToSectionMock = vi.fn().mockResolvedValue(undefined);
|
|
56
|
+
|
|
57
|
+
vi.mock('../../helpers/navigation', () => ({
|
|
58
|
+
login: (...args: unknown[]) => loginMock(...args),
|
|
59
|
+
navigateToSection: (...args: unknown[]) => navigateToSectionMock(...args),
|
|
60
|
+
}));
|
|
61
|
+
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
// Mock Playwright page
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
interface MockPage {
|
|
67
|
+
close: ReturnType<typeof vi.fn>;
|
|
68
|
+
evaluate: ReturnType<typeof vi.fn>;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Default evaluate returns: [navEntries=[], paintEntries=[], lcpMs=0, heapSize=0] */
|
|
72
|
+
function makeMockPage(evaluateResponses: unknown[] = []): MockPage {
|
|
73
|
+
let callIndex = 0;
|
|
74
|
+
return {
|
|
75
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
76
|
+
evaluate: vi.fn().mockImplementation(async () => {
|
|
77
|
+
const val = evaluateResponses[callIndex] ?? 0;
|
|
78
|
+
callIndex++;
|
|
79
|
+
return val;
|
|
80
|
+
}),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
let currentMockPage: MockPage;
|
|
85
|
+
let mockBrowser: { newPage: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> };
|
|
86
|
+
|
|
87
|
+
vi.mock('playwright', () => ({
|
|
88
|
+
chromium: {
|
|
89
|
+
launch: vi.fn().mockImplementation(async () => mockBrowser),
|
|
90
|
+
},
|
|
91
|
+
}));
|
|
92
|
+
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// Mock lighthouse (optional peer dep) — disabled by default
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
vi.mock('lighthouse', () => {
|
|
98
|
+
throw new Error('lighthouse not installed');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
// Dynamic import AFTER mocks
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
const { PerformanceProfilerAgent } = await import('../13-performance-profiler');
|
|
106
|
+
|
|
107
|
+
function createAgent(phase: Phase = 'beta'): InstanceType<typeof PerformanceProfilerAgent> {
|
|
108
|
+
return new PerformanceProfilerAgent(stubConfig, phase, '/tmp/test-run');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Tests
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
describe('PerformanceProfilerAgent', () => {
|
|
116
|
+
beforeEach(() => {
|
|
117
|
+
fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
|
|
118
|
+
loginMock.mockReset().mockResolvedValue(undefined);
|
|
119
|
+
navigateToSectionMock.mockReset().mockResolvedValue(undefined);
|
|
120
|
+
|
|
121
|
+
// Default page: evaluate returns correct types per call order.
|
|
122
|
+
// Per module (2 modules): loadTime:number, paintEntries:array, lcpMs:number
|
|
123
|
+
// After all modules: heap:number
|
|
124
|
+
currentMockPage = makeMockPage([
|
|
125
|
+
// dashboard
|
|
126
|
+
0, [], 0,
|
|
127
|
+
// reports
|
|
128
|
+
0, [], 0,
|
|
129
|
+
// heap (memory check)
|
|
130
|
+
0,
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
mockBrowser = {
|
|
134
|
+
newPage: vi.fn().mockResolvedValue(currentMockPage),
|
|
135
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('has agentId 13 and agentName "Performance Profiler"', () => {
|
|
140
|
+
const agent = createAgent();
|
|
141
|
+
expect(agent.agentId).toBe(13);
|
|
142
|
+
expect(agent.agentName).toBe('Performance Profiler');
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// -------------------------------------------------------------------------
|
|
146
|
+
// preFlight()
|
|
147
|
+
// -------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
describe('preFlight()', () => {
|
|
150
|
+
it('resolves gracefully when base URL is not reachable (fallback)', async () => {
|
|
151
|
+
fetchMock.mockRejectedValue(new Error('ECONNREFUSED'));
|
|
152
|
+
|
|
153
|
+
const agent = createAgent();
|
|
154
|
+
const result = await agent.run();
|
|
155
|
+
|
|
156
|
+
// With resolveEnvironment the agent no longer throws on unreachable URLs.
|
|
157
|
+
expect(result.status).toBe('passed');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('passes when base URL is reachable', async () => {
|
|
161
|
+
const agent = createAgent();
|
|
162
|
+
const result = await agent.run();
|
|
163
|
+
|
|
164
|
+
expect(result.findings.some((f) => f.description.includes('not reachable'))).toBe(false);
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// -------------------------------------------------------------------------
|
|
169
|
+
// execute() — LCP threshold
|
|
170
|
+
// -------------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
describe('LCP > 2500ms creates a finding', () => {
|
|
173
|
+
it('creates a high-severity finding when LCP exceeds 2500ms', async () => {
|
|
174
|
+
let callIndex = 0;
|
|
175
|
+
currentMockPage.evaluate = vi.fn().mockImplementation(async () => {
|
|
176
|
+
// For each module: loadTime=0, paintEntries=[], lcpMs=3000
|
|
177
|
+
const responses = [
|
|
178
|
+
0, // dashboard loadTime
|
|
179
|
+
[], // dashboard paintEntries
|
|
180
|
+
3000, // dashboard LCP — exceeds threshold
|
|
181
|
+
0, // reports loadTime
|
|
182
|
+
[], // reports paintEntries
|
|
183
|
+
3000, // reports LCP — exceeds threshold
|
|
184
|
+
0, // heap
|
|
185
|
+
];
|
|
186
|
+
return responses[callIndex++] ?? 0;
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const agent = createAgent();
|
|
190
|
+
const result = await agent.run();
|
|
191
|
+
|
|
192
|
+
const lcpFindings = result.findings.filter((f) => f.id.includes('lcp'));
|
|
193
|
+
expect(lcpFindings.length).toBeGreaterThan(0);
|
|
194
|
+
expect(lcpFindings[0].severity).toBe('high');
|
|
195
|
+
expect(lcpFindings[0].description).toContain('3000');
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('does NOT create a finding when LCP is within threshold', async () => {
|
|
199
|
+
// All evaluate calls return 0 (lcpMs=0, well under 2500)
|
|
200
|
+
currentMockPage.evaluate = vi.fn().mockResolvedValue(0);
|
|
201
|
+
|
|
202
|
+
const agent = createAgent();
|
|
203
|
+
const result = await agent.run();
|
|
204
|
+
|
|
205
|
+
const lcpFindings = result.findings.filter((f) => f.id.includes('lcp'));
|
|
206
|
+
expect(lcpFindings).toHaveLength(0);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// -------------------------------------------------------------------------
|
|
211
|
+
// FCP threshold
|
|
212
|
+
// -------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
describe('FCP threshold', () => {
|
|
215
|
+
it('creates a medium-severity finding when FCP exceeds 1800ms', async () => {
|
|
216
|
+
let callIndex = 0;
|
|
217
|
+
currentMockPage.evaluate = vi.fn().mockImplementation(async () => {
|
|
218
|
+
const responses: unknown[] = [
|
|
219
|
+
0, // dashboard loadTime
|
|
220
|
+
[{ name: 'first-contentful-paint', startTime: 2500 }], // FCP > 1800
|
|
221
|
+
0, // dashboard LCP
|
|
222
|
+
0, // reports loadTime
|
|
223
|
+
[{ name: 'first-contentful-paint', startTime: 2500 }],
|
|
224
|
+
0, // reports LCP
|
|
225
|
+
0, // heap
|
|
226
|
+
];
|
|
227
|
+
return responses[callIndex++] ?? 0;
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
const agent = createAgent();
|
|
231
|
+
const result = await agent.run();
|
|
232
|
+
|
|
233
|
+
const fcpFindings = result.findings.filter((f) => f.id.includes('fcp'));
|
|
234
|
+
expect(fcpFindings.length).toBeGreaterThan(0);
|
|
235
|
+
expect(fcpFindings[0].severity).toBe('medium');
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
// -------------------------------------------------------------------------
|
|
240
|
+
// Memory leak detection
|
|
241
|
+
// -------------------------------------------------------------------------
|
|
242
|
+
|
|
243
|
+
describe('memory leak detection', () => {
|
|
244
|
+
it('creates a high-severity finding when heap exceeds 100MB', async () => {
|
|
245
|
+
const OVER_100_MB = 110 * 1024 * 1024; // 110 MB
|
|
246
|
+
let callIndex = 0;
|
|
247
|
+
currentMockPage.evaluate = vi.fn().mockImplementation(async () => {
|
|
248
|
+
const responses = [
|
|
249
|
+
// dashboard
|
|
250
|
+
0, [], 0,
|
|
251
|
+
// reports
|
|
252
|
+
0, [], 0,
|
|
253
|
+
// heap check — over 100 MB
|
|
254
|
+
OVER_100_MB,
|
|
255
|
+
];
|
|
256
|
+
return responses[callIndex++] ?? 0;
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
const agent = createAgent();
|
|
260
|
+
const result = await agent.run();
|
|
261
|
+
|
|
262
|
+
const heapFinding = result.findings.find((f) => f.id === '13-memory-heap');
|
|
263
|
+
expect(heapFinding).toBeDefined();
|
|
264
|
+
expect(heapFinding!.severity).toBe('high');
|
|
265
|
+
expect(heapFinding!.description).toContain('memory leak');
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('does NOT flag memory when heap is within threshold', async () => {
|
|
269
|
+
const UNDER_100_MB = 50 * 1024 * 1024; // 50 MB
|
|
270
|
+
let callIndex = 0;
|
|
271
|
+
currentMockPage.evaluate = vi.fn().mockImplementation(async () => {
|
|
272
|
+
const responses = [
|
|
273
|
+
0, [], 0,
|
|
274
|
+
0, [], 0,
|
|
275
|
+
UNDER_100_MB,
|
|
276
|
+
];
|
|
277
|
+
return responses[callIndex++] ?? 0;
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
const agent = createAgent();
|
|
281
|
+
const result = await agent.run();
|
|
282
|
+
|
|
283
|
+
const heapFinding = result.findings.find((f) => f.id === '13-memory-heap');
|
|
284
|
+
expect(heapFinding).toBeUndefined();
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
// -------------------------------------------------------------------------
|
|
289
|
+
// Navigation failure
|
|
290
|
+
// -------------------------------------------------------------------------
|
|
291
|
+
|
|
292
|
+
describe('navigation failure', () => {
|
|
293
|
+
it('creates an infra-issue finding when module navigation throws', async () => {
|
|
294
|
+
navigateToSectionMock.mockRejectedValueOnce(new Error('Navigation timeout'));
|
|
295
|
+
|
|
296
|
+
const agent = createAgent();
|
|
297
|
+
const result = await agent.run();
|
|
298
|
+
|
|
299
|
+
const navFinding = result.findings.find((f) => f.id.includes('nav-dashboard'));
|
|
300
|
+
expect(navFinding).toBeDefined();
|
|
301
|
+
expect(navFinding!.type).toBe('infra-issue');
|
|
302
|
+
});
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// -------------------------------------------------------------------------
|
|
306
|
+
// Passed with no violations
|
|
307
|
+
// -------------------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
it('returns passed status when all metrics are within thresholds', async () => {
|
|
310
|
+
// Each evaluate call must return the correct type:
|
|
311
|
+
// Per module: [loadTime: number, paintEntries: array, lcpMs: number]
|
|
312
|
+
// After all modules: [heap: number]
|
|
313
|
+
let callIdx = 0;
|
|
314
|
+
const responses: unknown[] = [
|
|
315
|
+
0, // dashboard loadTime
|
|
316
|
+
[], // dashboard paintEntries (array — fcpEntry will be undefined → fcpMs = 0)
|
|
317
|
+
0, // dashboard lcpMs
|
|
318
|
+
0, // reports loadTime
|
|
319
|
+
[], // reports paintEntries
|
|
320
|
+
0, // reports lcpMs
|
|
321
|
+
0, // heap
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
const freshPage: MockPage = {
|
|
325
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
326
|
+
evaluate: vi.fn().mockImplementation(async () => responses[callIdx++] ?? 0),
|
|
327
|
+
};
|
|
328
|
+
mockBrowser = {
|
|
329
|
+
newPage: vi.fn().mockResolvedValue(freshPage),
|
|
330
|
+
close: vi.fn().mockResolvedValue(undefined),
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const agent = createAgent();
|
|
334
|
+
const result = await agent.run();
|
|
335
|
+
|
|
336
|
+
expect(result.status).toBe('passed');
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
// -------------------------------------------------------------------------
|
|
340
|
+
// No credentials
|
|
341
|
+
// -------------------------------------------------------------------------
|
|
342
|
+
|
|
343
|
+
it('creates a critical finding when no credentials are configured', async () => {
|
|
344
|
+
const noCreds: ValidatedConfig = {
|
|
345
|
+
...stubConfig,
|
|
346
|
+
auth: { loginUrl: 'http://localhost/login', credentials: {} },
|
|
347
|
+
};
|
|
348
|
+
const agent = new PerformanceProfilerAgent(noCreds, 'beta', '/tmp/test-run');
|
|
349
|
+
const result = await agent.run();
|
|
350
|
+
|
|
351
|
+
const finding = result.findings.find(f => f.id === '13-no-credentials');
|
|
352
|
+
expect(finding).toBeDefined();
|
|
353
|
+
expect(finding!.severity).toBe('critical');
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// -------------------------------------------------------------------------
|
|
357
|
+
// Load time threshold exceeded
|
|
358
|
+
// -------------------------------------------------------------------------
|
|
359
|
+
|
|
360
|
+
describe('page load time threshold', () => {
|
|
361
|
+
it('creates a medium finding when loadTime exceeds 2x apiResponseTime', async () => {
|
|
362
|
+
let callIndex = 0;
|
|
363
|
+
currentMockPage.evaluate = vi.fn().mockImplementation(async () => {
|
|
364
|
+
// apiResponseTime is 2000, threshold is 4000 (2x)
|
|
365
|
+
const responses: unknown[] = [
|
|
366
|
+
5000, // dashboard loadTime — exceeds 4000
|
|
367
|
+
[], // dashboard paintEntries
|
|
368
|
+
0, // dashboard LCP
|
|
369
|
+
5000, // reports loadTime — exceeds 4000
|
|
370
|
+
[], // reports paintEntries
|
|
371
|
+
0, // reports LCP
|
|
372
|
+
0, // heap
|
|
373
|
+
];
|
|
374
|
+
return responses[callIndex++] ?? 0;
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
const agent = createAgent();
|
|
378
|
+
const result = await agent.run();
|
|
379
|
+
|
|
380
|
+
const loadFindings = result.findings.filter(f => f.id.includes('load-time'));
|
|
381
|
+
expect(loadFindings.length).toBeGreaterThan(0);
|
|
382
|
+
expect(loadFindings[0].severity).toBe('medium');
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// -------------------------------------------------------------------------
|
|
387
|
+
// Evidence logging
|
|
388
|
+
// -------------------------------------------------------------------------
|
|
389
|
+
|
|
390
|
+
it('adds log evidence for each module profiled', async () => {
|
|
391
|
+
const agent = createAgent();
|
|
392
|
+
const result = await agent.run();
|
|
393
|
+
|
|
394
|
+
const logEvidence = result.evidence.filter(e => e.type === 'log');
|
|
395
|
+
expect(logEvidence.length).toBeGreaterThanOrEqual(stubConfig.modules.length);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// -------------------------------------------------------------------------
|
|
399
|
+
// Evaluate catch paths (page.evaluate rejection)
|
|
400
|
+
// -------------------------------------------------------------------------
|
|
401
|
+
|
|
402
|
+
it('handles page.evaluate rejections gracefully', async () => {
|
|
403
|
+
currentMockPage.evaluate = vi.fn().mockRejectedValue(new Error('Page crashed'));
|
|
404
|
+
|
|
405
|
+
const agent = createAgent();
|
|
406
|
+
const result = await agent.run();
|
|
407
|
+
|
|
408
|
+
// Should still complete without throwing
|
|
409
|
+
expect(result.status).toBe('passed');
|
|
410
|
+
});
|
|
411
|
+
});
|