@yasserkhanorg/e2e-agents 0.3.2
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/LICENSE +168 -0
- package/README.md +620 -0
- package/dist/agent/analysis.d.ts +62 -0
- package/dist/agent/analysis.d.ts.map +1 -0
- package/dist/agent/analysis.js +292 -0
- package/dist/agent/blast_radius.d.ts +4 -0
- package/dist/agent/blast_radius.d.ts.map +1 -0
- package/dist/agent/blast_radius.js +37 -0
- package/dist/agent/cache_utils.d.ts +38 -0
- package/dist/agent/cache_utils.d.ts.map +1 -0
- package/dist/agent/cache_utils.js +67 -0
- package/dist/agent/config.d.ts +148 -0
- package/dist/agent/config.d.ts.map +1 -0
- package/dist/agent/config.js +640 -0
- package/dist/agent/dependency_graph.d.ts +14 -0
- package/dist/agent/dependency_graph.d.ts.map +1 -0
- package/dist/agent/dependency_graph.js +227 -0
- package/dist/agent/feedback.d.ts +55 -0
- package/dist/agent/feedback.d.ts.map +1 -0
- package/dist/agent/feedback.js +257 -0
- package/dist/agent/flags.d.ts +23 -0
- package/dist/agent/flags.d.ts.map +1 -0
- package/dist/agent/flags.js +171 -0
- package/dist/agent/flow_catalog.d.ts +25 -0
- package/dist/agent/flow_catalog.d.ts.map +1 -0
- package/dist/agent/flow_catalog.js +106 -0
- package/dist/agent/flow_mapping.d.ts +10 -0
- package/dist/agent/flow_mapping.d.ts.map +1 -0
- package/dist/agent/flow_mapping.js +84 -0
- package/dist/agent/framework.d.ts +13 -0
- package/dist/agent/framework.d.ts.map +1 -0
- package/dist/agent/framework.js +149 -0
- package/dist/agent/gap_suggestions.d.ts +14 -0
- package/dist/agent/gap_suggestions.d.ts.map +1 -0
- package/dist/agent/gap_suggestions.js +101 -0
- package/dist/agent/generator.d.ts +10 -0
- package/dist/agent/generator.d.ts.map +1 -0
- package/dist/agent/generator.js +115 -0
- package/dist/agent/git.d.ts +11 -0
- package/dist/agent/git.d.ts.map +1 -0
- package/dist/agent/git.js +90 -0
- package/dist/agent/handoff.d.ts +22 -0
- package/dist/agent/handoff.d.ts.map +1 -0
- package/dist/agent/handoff.js +180 -0
- package/dist/agent/impact-analyzer.d.ts +114 -0
- package/dist/agent/impact-analyzer.d.ts.map +1 -0
- package/dist/agent/impact-analyzer.js +557 -0
- package/dist/agent/index.d.ts +21 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +38 -0
- package/dist/agent/model-router.d.ts +57 -0
- package/dist/agent/model-router.d.ts.map +1 -0
- package/dist/agent/model-router.js +154 -0
- package/dist/agent/operational_insights.d.ts +41 -0
- package/dist/agent/operational_insights.d.ts.map +1 -0
- package/dist/agent/operational_insights.js +126 -0
- package/dist/agent/pipeline.d.ts +23 -0
- package/dist/agent/pipeline.d.ts.map +1 -0
- package/dist/agent/pipeline.js +609 -0
- package/dist/agent/plan.d.ts +91 -0
- package/dist/agent/plan.d.ts.map +1 -0
- package/dist/agent/plan.js +331 -0
- package/dist/agent/playwright_report.d.ts +8 -0
- package/dist/agent/playwright_report.d.ts.map +1 -0
- package/dist/agent/playwright_report.js +126 -0
- package/dist/agent/report-generator.d.ts +24 -0
- package/dist/agent/report-generator.d.ts.map +1 -0
- package/dist/agent/report-generator.js +250 -0
- package/dist/agent/report.d.ts +81 -0
- package/dist/agent/report.d.ts.map +1 -0
- package/dist/agent/report.js +147 -0
- package/dist/agent/runner.d.ts +7 -0
- package/dist/agent/runner.d.ts.map +1 -0
- package/dist/agent/runner.js +576 -0
- package/dist/agent/selectors.d.ts +10 -0
- package/dist/agent/selectors.d.ts.map +1 -0
- package/dist/agent/selectors.js +75 -0
- package/dist/agent/spec-bridge.d.ts +101 -0
- package/dist/agent/spec-bridge.d.ts.map +1 -0
- package/dist/agent/spec-bridge.js +273 -0
- package/dist/agent/spec-builder.d.ts +102 -0
- package/dist/agent/spec-builder.d.ts.map +1 -0
- package/dist/agent/spec-builder.js +273 -0
- package/dist/agent/subsystem_risk.d.ts +23 -0
- package/dist/agent/subsystem_risk.d.ts.map +1 -0
- package/dist/agent/subsystem_risk.js +207 -0
- package/dist/agent/telemetry.d.ts +84 -0
- package/dist/agent/telemetry.d.ts.map +1 -0
- package/dist/agent/telemetry.js +220 -0
- package/dist/agent/test_path.d.ts +2 -0
- package/dist/agent/test_path.d.ts.map +1 -0
- package/dist/agent/test_path.js +23 -0
- package/dist/agent/tests.d.ts +18 -0
- package/dist/agent/tests.d.ts.map +1 -0
- package/dist/agent/tests.js +106 -0
- package/dist/agent/traceability.d.ts +22 -0
- package/dist/agent/traceability.d.ts.map +1 -0
- package/dist/agent/traceability.js +183 -0
- package/dist/agent/traceability_capture.d.ts +18 -0
- package/dist/agent/traceability_capture.d.ts.map +1 -0
- package/dist/agent/traceability_capture.js +313 -0
- package/dist/agent/traceability_ingest.d.ts +21 -0
- package/dist/agent/traceability_ingest.d.ts.map +1 -0
- package/dist/agent/traceability_ingest.js +237 -0
- package/dist/agent/utils.d.ts +13 -0
- package/dist/agent/utils.d.ts.map +1 -0
- package/dist/agent/utils.js +152 -0
- package/dist/agent/validators/selector-validator.d.ts +74 -0
- package/dist/agent/validators/selector-validator.d.ts.map +1 -0
- package/dist/agent/validators/selector-validator.js +165 -0
- package/dist/anthropic_provider.d.ts +65 -0
- package/dist/anthropic_provider.d.ts.map +1 -0
- package/dist/anthropic_provider.js +332 -0
- package/dist/api.d.ts +48 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +113 -0
- package/dist/base_provider.d.ts +53 -0
- package/dist/base_provider.d.ts.map +1 -0
- package/dist/base_provider.js +81 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +843 -0
- package/dist/custom_provider.d.ts +20 -0
- package/dist/custom_provider.d.ts.map +1 -0
- package/dist/custom_provider.js +276 -0
- package/dist/e2e-test-gen/index.d.ts +51 -0
- package/dist/e2e-test-gen/index.d.ts.map +1 -0
- package/dist/e2e-test-gen/index.js +57 -0
- package/dist/e2e-test-gen/spec_parser.d.ts +142 -0
- package/dist/e2e-test-gen/spec_parser.d.ts.map +1 -0
- package/dist/e2e-test-gen/spec_parser.js +786 -0
- package/dist/e2e-test-gen/types.d.ts +185 -0
- package/dist/e2e-test-gen/types.d.ts.map +1 -0
- package/dist/e2e-test-gen/types.js +4 -0
- package/dist/esm/agent/analysis.js +287 -0
- package/dist/esm/agent/blast_radius.js +34 -0
- package/dist/esm/agent/cache_utils.js +63 -0
- package/dist/esm/agent/config.js +637 -0
- package/dist/esm/agent/dependency_graph.js +224 -0
- package/dist/esm/agent/feedback.js +253 -0
- package/dist/esm/agent/flags.js +160 -0
- package/dist/esm/agent/flow_catalog.js +103 -0
- package/dist/esm/agent/flow_mapping.js +81 -0
- package/dist/esm/agent/framework.js +145 -0
- package/dist/esm/agent/gap_suggestions.js +98 -0
- package/dist/esm/agent/generator.js +112 -0
- package/dist/esm/agent/git.js +87 -0
- package/dist/esm/agent/handoff.js +177 -0
- package/dist/esm/agent/impact-analyzer.js +548 -0
- package/dist/esm/agent/index.js +22 -0
- package/dist/esm/agent/model-router.js +150 -0
- package/dist/esm/agent/operational_insights.js +123 -0
- package/dist/esm/agent/pipeline.js +605 -0
- package/dist/esm/agent/plan.js +324 -0
- package/dist/esm/agent/playwright_report.js +123 -0
- package/dist/esm/agent/report-generator.js +247 -0
- package/dist/esm/agent/report.js +144 -0
- package/dist/esm/agent/runner.js +572 -0
- package/dist/esm/agent/selectors.js +71 -0
- package/dist/esm/agent/spec-bridge.js +267 -0
- package/dist/esm/agent/spec-builder.js +267 -0
- package/dist/esm/agent/subsystem_risk.js +204 -0
- package/dist/esm/agent/telemetry.js +216 -0
- package/dist/esm/agent/test_path.js +20 -0
- package/dist/esm/agent/tests.js +101 -0
- package/dist/esm/agent/traceability.js +180 -0
- package/dist/esm/agent/traceability_capture.js +310 -0
- package/dist/esm/agent/traceability_ingest.js +234 -0
- package/dist/esm/agent/utils.js +138 -0
- package/dist/esm/agent/validators/selector-validator.js +160 -0
- package/dist/esm/anthropic_provider.js +324 -0
- package/dist/esm/api.js +105 -0
- package/dist/esm/base_provider.js +77 -0
- package/dist/esm/cli.js +841 -0
- package/dist/esm/custom_provider.js +272 -0
- package/dist/esm/e2e-test-gen/index.js +50 -0
- package/dist/esm/e2e-test-gen/spec_parser.js +782 -0
- package/dist/esm/e2e-test-gen/types.js +3 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/logger.js +89 -0
- package/dist/esm/mcp-server.js +465 -0
- package/dist/esm/ollama_provider.js +300 -0
- package/dist/esm/openai_provider.js +242 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/plan-and-test-constants.js +126 -0
- package/dist/esm/provider_factory.js +336 -0
- package/dist/esm/provider_interface.js +23 -0
- package/dist/esm/provider_utils.js +96 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +41 -0
- package/dist/logger.d.ts +23 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +93 -0
- package/dist/mcp-server.d.ts +35 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +469 -0
- package/dist/ollama_provider.d.ts +65 -0
- package/dist/ollama_provider.d.ts.map +1 -0
- package/dist/ollama_provider.js +308 -0
- package/dist/openai_provider.d.ts +23 -0
- package/dist/openai_provider.d.ts.map +1 -0
- package/dist/openai_provider.js +250 -0
- package/dist/plan-and-test-constants.d.ts +110 -0
- package/dist/plan-and-test-constants.d.ts.map +1 -0
- package/dist/plan-and-test-constants.js +132 -0
- package/dist/provider_factory.d.ts +99 -0
- package/dist/provider_factory.d.ts.map +1 -0
- package/dist/provider_factory.js +341 -0
- package/dist/provider_interface.d.ts +358 -0
- package/dist/provider_interface.d.ts.map +1 -0
- package/dist/provider_interface.js +28 -0
- package/dist/provider_utils.d.ts +39 -0
- package/dist/provider_utils.d.ts.map +1 -0
- package/dist/provider_utils.js +103 -0
- package/package.json +101 -0
- package/schemas/gap.schema.json +18 -0
- package/schemas/impact.schema.json +418 -0
- package/schemas/plan.schema.json +285 -0
- package/schemas/subsystem-risk-map.schema.json +62 -0
- package/schemas/traceability-input.schema.json +122 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.buildGapTestSuggestions = buildGapTestSuggestions;
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const utils_js_1 = require("./utils.js");
|
|
8
|
+
function inferTestDir(patterns) {
|
|
9
|
+
if (patterns.length === 0) {
|
|
10
|
+
return 'tests';
|
|
11
|
+
}
|
|
12
|
+
const pattern = patterns[0];
|
|
13
|
+
const wildcardIndex = pattern.search(/[*{]/);
|
|
14
|
+
const base = wildcardIndex === -1 ? pattern : pattern.slice(0, wildcardIndex);
|
|
15
|
+
const trimmed = base.replace(/\/+$/, '');
|
|
16
|
+
return trimmed || 'tests';
|
|
17
|
+
}
|
|
18
|
+
function inferExtension(patterns) {
|
|
19
|
+
const joined = patterns.join(' ');
|
|
20
|
+
if (joined.includes('.ts') || joined.includes('.tsx')) {
|
|
21
|
+
return 'ts';
|
|
22
|
+
}
|
|
23
|
+
return 'js';
|
|
24
|
+
}
|
|
25
|
+
function normalizeFramework(framework) {
|
|
26
|
+
if (framework === 'cypress' || framework === 'selenium') {
|
|
27
|
+
return framework;
|
|
28
|
+
}
|
|
29
|
+
return 'playwright';
|
|
30
|
+
}
|
|
31
|
+
function buildSkeleton(flow, sourceFiles, framework) {
|
|
32
|
+
const linkedFiles = sourceFiles.length > 0 ? sourceFiles.join(', ') : 'N/A';
|
|
33
|
+
if (framework === 'cypress') {
|
|
34
|
+
return [
|
|
35
|
+
`describe('Flow: ${flow.name}', () => {`,
|
|
36
|
+
` it('${flow.priority}: critical coverage for ${flow.id}', () => {`,
|
|
37
|
+
" cy.visit('/');",
|
|
38
|
+
` // Linked code areas: ${linkedFiles}`,
|
|
39
|
+
' // TODO: implement critical user path assertions',
|
|
40
|
+
' });',
|
|
41
|
+
'});',
|
|
42
|
+
'',
|
|
43
|
+
].join('\n');
|
|
44
|
+
}
|
|
45
|
+
if (framework === 'selenium') {
|
|
46
|
+
return [
|
|
47
|
+
"const {Builder} = require('selenium-webdriver');",
|
|
48
|
+
'',
|
|
49
|
+
'(async () => {',
|
|
50
|
+
" const driver = await new Builder().forBrowser('chrome').build();",
|
|
51
|
+
' try {',
|
|
52
|
+
" await driver.get('http://localhost:3000');",
|
|
53
|
+
` // Linked code areas: ${linkedFiles}`,
|
|
54
|
+
' // TODO: implement critical user path assertions',
|
|
55
|
+
' } finally {',
|
|
56
|
+
' await driver.quit();',
|
|
57
|
+
' }',
|
|
58
|
+
'})();',
|
|
59
|
+
'',
|
|
60
|
+
].join('\n');
|
|
61
|
+
}
|
|
62
|
+
return [
|
|
63
|
+
"import {test, expect} from '@mattermost/playwright-lib';",
|
|
64
|
+
'',
|
|
65
|
+
`test('${flow.priority}: ${flow.name} critical path', {tag: '@ai-assisted'}, async ({pw}) => {`,
|
|
66
|
+
' const {user, team} = await pw.initSetup();',
|
|
67
|
+
' const {channelsPage} = await pw.testBrowser.login(user);',
|
|
68
|
+
" await channelsPage.goto(team.name);",
|
|
69
|
+
` // Linked code areas: ${linkedFiles}`,
|
|
70
|
+
' // TODO: implement critical user path assertions',
|
|
71
|
+
' await expect(channelsPage.page).toHaveURL(/.*/);',
|
|
72
|
+
'});',
|
|
73
|
+
'',
|
|
74
|
+
].join('\n');
|
|
75
|
+
}
|
|
76
|
+
function buildGapTestSuggestions(testsRoot, flowsWithGaps, framework, testPatterns) {
|
|
77
|
+
const testDir = inferTestDir(testPatterns);
|
|
78
|
+
const ext = inferExtension(testPatterns);
|
|
79
|
+
const resolvedFramework = normalizeFramework(framework);
|
|
80
|
+
return flowsWithGaps
|
|
81
|
+
.filter((flow) => flow.priority === 'P0' || flow.priority === 'P1')
|
|
82
|
+
.map((flow) => {
|
|
83
|
+
const fileName = resolvedFramework === 'cypress' ? `${flow.id}.cy.${ext}` : `${flow.id}.spec.${ext}`;
|
|
84
|
+
const candidatePath = (0, path_1.resolve)(testsRoot, testDir, fileName);
|
|
85
|
+
const suggestionPath = (0, utils_js_1.isPathWithinRoot)(testsRoot, candidatePath)
|
|
86
|
+
? candidatePath
|
|
87
|
+
: (0, path_1.resolve)(testsRoot, 'tests', fileName);
|
|
88
|
+
const sourceFiles = (flow.files || []).slice(0, 6);
|
|
89
|
+
const rationale = flow.reasons.length > 0 ? flow.reasons.join('; ') : 'High priority flow is currently uncovered';
|
|
90
|
+
return {
|
|
91
|
+
flowId: flow.id,
|
|
92
|
+
flowName: flow.name,
|
|
93
|
+
priority: flow.priority,
|
|
94
|
+
rationale,
|
|
95
|
+
sourceFiles,
|
|
96
|
+
suggestedTestPath: suggestionPath,
|
|
97
|
+
framework: resolvedFramework,
|
|
98
|
+
skeleton: buildSkeleton(flow, sourceFiles, resolvedFramework),
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { FrameworkType } from './config.js';
|
|
2
|
+
import type { FlowImpact } from './analysis.js';
|
|
3
|
+
export interface GeneratedTest {
|
|
4
|
+
path: string;
|
|
5
|
+
flowId: string;
|
|
6
|
+
created: boolean;
|
|
7
|
+
reason?: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function generateTests(appRoot: string, flows: FlowImpact[], framework: FrameworkType, testPatterns: string[], testIdsByFlow: Map<string, string[]>): GeneratedTest[];
|
|
10
|
+
//# sourceMappingURL=generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/agent/generator.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,aAAa,CAAC;AAC/C,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAG9C,MAAM,WAAW,aAAa;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AA6ED,wBAAgB,aAAa,CACzB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,UAAU,EAAE,EACnB,SAAS,EAAE,aAAa,EACxB,YAAY,EAAE,MAAM,EAAE,EACtB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACrC,aAAa,EAAE,CAyCjB"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.generateTests = generateTests;
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = require("path");
|
|
8
|
+
const utils_js_1 = require("./utils.js");
|
|
9
|
+
function inferTestDir(patterns) {
|
|
10
|
+
if (patterns.length === 0) {
|
|
11
|
+
return 'tests';
|
|
12
|
+
}
|
|
13
|
+
const pattern = patterns[0];
|
|
14
|
+
const wildcardIndex = pattern.search(/[*{]/);
|
|
15
|
+
const base = wildcardIndex === -1 ? pattern : pattern.slice(0, wildcardIndex);
|
|
16
|
+
const trimmed = base.replace(/\/+$/, '');
|
|
17
|
+
return trimmed || 'tests';
|
|
18
|
+
}
|
|
19
|
+
function inferTestExtension(patterns, framework) {
|
|
20
|
+
const joined = patterns.join(' ');
|
|
21
|
+
if (joined.includes('.ts') || joined.includes('.tsx')) {
|
|
22
|
+
return 'ts';
|
|
23
|
+
}
|
|
24
|
+
return 'js';
|
|
25
|
+
}
|
|
26
|
+
function createPlaywrightTest(flow, testIds) {
|
|
27
|
+
const idsComment = testIds.length > 0 ? `// Suggested data-testid: ${testIds.join(', ')}` : '// TODO: add data-testid selectors';
|
|
28
|
+
return [
|
|
29
|
+
"import {test, expect} from '@mattermost/playwright-lib';",
|
|
30
|
+
'',
|
|
31
|
+
'/**',
|
|
32
|
+
` * @objective Validate ${flow.name} flow`,
|
|
33
|
+
' */',
|
|
34
|
+
`test('${flow.priority}: ${flow.name} basic flow', {tag: '@ai-assisted'}, async ({pw}) => {`,
|
|
35
|
+
' const {user, team} = await pw.initSetup();',
|
|
36
|
+
' const {channelsPage} = await pw.testBrowser.login(user);',
|
|
37
|
+
" await channelsPage.goto(team.name);",
|
|
38
|
+
` ${idsComment}`,
|
|
39
|
+
' // # TODO: implement steps',
|
|
40
|
+
' // * TODO: implement assertions',
|
|
41
|
+
' await expect(channelsPage.page).toHaveURL(/.*/);',
|
|
42
|
+
'});',
|
|
43
|
+
'',
|
|
44
|
+
].join('\n');
|
|
45
|
+
}
|
|
46
|
+
function createCypressTest(flow, testIds) {
|
|
47
|
+
const idsComment = testIds.length > 0 ? `// Suggested data-testid: ${testIds.join(', ')}` : '// TODO: add data-testid selectors';
|
|
48
|
+
return [
|
|
49
|
+
`describe('Flow: ${flow.name}', () => {`,
|
|
50
|
+
` it('${flow.priority}: ${flow.name} basic flow', () => {`,
|
|
51
|
+
" cy.visit('/');",
|
|
52
|
+
` ${idsComment}`,
|
|
53
|
+
' // TODO: implement steps',
|
|
54
|
+
' cy.url().should(\'match\', /.*/);',
|
|
55
|
+
' });',
|
|
56
|
+
'});',
|
|
57
|
+
'',
|
|
58
|
+
].join('\n');
|
|
59
|
+
}
|
|
60
|
+
function createSeleniumTest(flow, testIds) {
|
|
61
|
+
const idsComment = testIds.length > 0 ? `// Suggested data-testid: ${testIds.join(', ')}` : '// TODO: add data-testid selectors';
|
|
62
|
+
return [
|
|
63
|
+
"const {Builder, By, until} = require('selenium-webdriver');",
|
|
64
|
+
'',
|
|
65
|
+
`(async () => {`,
|
|
66
|
+
" const driver = await new Builder().forBrowser('chrome').build();",
|
|
67
|
+
' try {',
|
|
68
|
+
" await driver.get('http://localhost:3000');",
|
|
69
|
+
` ${idsComment}`,
|
|
70
|
+
' // TODO: implement steps',
|
|
71
|
+
' await driver.wait(until.titleIs(\'\'), 5000);',
|
|
72
|
+
' } finally {',
|
|
73
|
+
' await driver.quit();',
|
|
74
|
+
' }',
|
|
75
|
+
'})();',
|
|
76
|
+
'',
|
|
77
|
+
].join('\n');
|
|
78
|
+
}
|
|
79
|
+
function generateTests(appRoot, flows, framework, testPatterns, testIdsByFlow) {
|
|
80
|
+
const inferredTestDir = inferTestDir(testPatterns);
|
|
81
|
+
const safeTestDir = (0, utils_js_1.isPathWithinRoot)(appRoot, (0, path_1.resolve)(appRoot, inferredTestDir)) ? inferredTestDir : 'tests';
|
|
82
|
+
const testDir = safeTestDir;
|
|
83
|
+
const extension = inferTestExtension(testPatterns, framework);
|
|
84
|
+
const generated = [];
|
|
85
|
+
for (const flow of flows) {
|
|
86
|
+
if (flow.priority !== 'P0' && flow.priority !== 'P1') {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const testIds = testIdsByFlow.get(flow.id) || [];
|
|
90
|
+
const fileName = framework === 'cypress' ? `${flow.id}.cy.${extension}` : `${flow.id}.spec.${extension}`;
|
|
91
|
+
const fullPath = (0, path_1.resolve)(appRoot, testDir, fileName);
|
|
92
|
+
if (!(0, utils_js_1.isPathWithinRoot)(appRoot, fullPath)) {
|
|
93
|
+
generated.push({ path: fullPath, flowId: flow.id, created: false, reason: 'outside-root' });
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
if ((0, fs_1.existsSync)(fullPath)) {
|
|
97
|
+
generated.push({ path: fullPath, flowId: flow.id, created: false, reason: 'exists' });
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
(0, fs_1.mkdirSync)((0, path_1.join)(appRoot, testDir), { recursive: true });
|
|
101
|
+
let content = '';
|
|
102
|
+
if (framework === 'cypress') {
|
|
103
|
+
content = createCypressTest(flow, testIds);
|
|
104
|
+
}
|
|
105
|
+
else if (framework === 'selenium') {
|
|
106
|
+
content = createSeleniumTest(flow, testIds);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
content = createPlaywrightTest(flow, testIds);
|
|
110
|
+
}
|
|
111
|
+
(0, fs_1.writeFileSync)(fullPath, content, 'utf-8');
|
|
112
|
+
generated.push({ path: fullPath, flowId: flow.id, created: true });
|
|
113
|
+
}
|
|
114
|
+
return generated;
|
|
115
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export interface GitChangeResult {
|
|
2
|
+
files: string[];
|
|
3
|
+
error?: string;
|
|
4
|
+
baseRef?: string;
|
|
5
|
+
baseStrategy?: 'merge-base' | 'direct';
|
|
6
|
+
}
|
|
7
|
+
export interface GitChangeOptions {
|
|
8
|
+
includeUncommitted?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function getChangedFiles(appRoot: string, since: string, options?: GitChangeOptions): GitChangeResult;
|
|
11
|
+
//# sourceMappingURL=git.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../src/agent/git.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,eAAe;IAC5B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC7B,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAChC;AA8CD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,eAAe,CAuC3G"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.getChangedFiles = getChangedFiles;
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const utils_js_1 = require("./utils.js");
|
|
8
|
+
function runGitRaw(args, cwd) {
|
|
9
|
+
const result = (0, child_process_1.spawnSync)('git', args, {
|
|
10
|
+
cwd,
|
|
11
|
+
encoding: 'utf-8',
|
|
12
|
+
timeout: 30000,
|
|
13
|
+
});
|
|
14
|
+
if (result.error || result.status !== 0) {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
return result.stdout;
|
|
18
|
+
}
|
|
19
|
+
function runGit(args, cwd) {
|
|
20
|
+
const output = runGitRaw(args, cwd);
|
|
21
|
+
if (output === null) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return output
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map((file) => file.trim())
|
|
27
|
+
.filter(Boolean)
|
|
28
|
+
.map((file) => (0, utils_js_1.normalizePath)(file));
|
|
29
|
+
}
|
|
30
|
+
function parseStatusLines(lines) {
|
|
31
|
+
const files = [];
|
|
32
|
+
for (const line of lines) {
|
|
33
|
+
if (!line)
|
|
34
|
+
continue;
|
|
35
|
+
if (line.length < 4)
|
|
36
|
+
continue;
|
|
37
|
+
const pathPart = line.slice(3).trim();
|
|
38
|
+
if (!pathPart)
|
|
39
|
+
continue;
|
|
40
|
+
if (pathPart.includes('->')) {
|
|
41
|
+
const parts = pathPart.split('->').map((part) => part.trim());
|
|
42
|
+
const target = parts[parts.length - 1];
|
|
43
|
+
if (target) {
|
|
44
|
+
files.push((0, utils_js_1.normalizePath)(target));
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
else {
|
|
48
|
+
files.push((0, utils_js_1.normalizePath)(pathPart));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return files;
|
|
52
|
+
}
|
|
53
|
+
function getChangedFiles(appRoot, since, options) {
|
|
54
|
+
try {
|
|
55
|
+
const files = new Set();
|
|
56
|
+
let baseRef = since;
|
|
57
|
+
let baseStrategy = 'direct';
|
|
58
|
+
const mergeBase = runGitRaw(['merge-base', since, 'HEAD'], appRoot);
|
|
59
|
+
if (mergeBase) {
|
|
60
|
+
const candidate = mergeBase
|
|
61
|
+
.split('\n')
|
|
62
|
+
.map((line) => line.trim())
|
|
63
|
+
.find(Boolean);
|
|
64
|
+
if (candidate) {
|
|
65
|
+
baseRef = candidate;
|
|
66
|
+
baseStrategy = 'merge-base';
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const diffFiles = runGit(['diff', '--name-only', `${baseRef}..HEAD`, '--', '.'], appRoot);
|
|
70
|
+
if (!diffFiles) {
|
|
71
|
+
return { files: [], error: 'git diff failed' };
|
|
72
|
+
}
|
|
73
|
+
diffFiles.forEach((file) => files.add(file));
|
|
74
|
+
if (options?.includeUncommitted) {
|
|
75
|
+
const staged = runGit(['diff', '--name-only', '--cached', '--', '.'], appRoot) || [];
|
|
76
|
+
staged.forEach((file) => files.add(file));
|
|
77
|
+
const unstaged = runGit(['diff', '--name-only', '--', '.'], appRoot) || [];
|
|
78
|
+
unstaged.forEach((file) => files.add(file));
|
|
79
|
+
const statusOutput = runGitRaw(['status', '--porcelain', '--', '.'], appRoot);
|
|
80
|
+
if (statusOutput) {
|
|
81
|
+
const statusLines = statusOutput.split('\n').filter(Boolean);
|
|
82
|
+
parseStatusLines(statusLines).forEach((file) => files.add(file));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return { files: Array.from(files), baseRef, baseStrategy };
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return { files: [], error: 'git diff failed' };
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export interface FinalizeGeneratedTestsOptions {
|
|
2
|
+
appPath: string;
|
|
3
|
+
testsRoot?: string;
|
|
4
|
+
gapReportPath?: string;
|
|
5
|
+
branch?: string;
|
|
6
|
+
commitMessage?: string;
|
|
7
|
+
createPr?: boolean;
|
|
8
|
+
prTitle?: string;
|
|
9
|
+
prBody?: string;
|
|
10
|
+
baseBranch?: string;
|
|
11
|
+
dryRun?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface FinalizeGeneratedTestsResult {
|
|
14
|
+
repoRoot: string;
|
|
15
|
+
branch: string;
|
|
16
|
+
stagedPaths: string[];
|
|
17
|
+
committed: boolean;
|
|
18
|
+
commitSha?: string;
|
|
19
|
+
prUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare function finalizeGeneratedTests(options: FinalizeGeneratedTestsOptions): FinalizeGeneratedTestsResult;
|
|
22
|
+
//# sourceMappingURL=handoff.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handoff.d.ts","sourceRoot":"","sources":["../../src/agent/handoff.ts"],"names":[],"mappings":"AAwBA,MAAM,WAAW,6BAA6B;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,4BAA4B;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AA+HD,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,4BAA4B,CAmE3G"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
// See LICENSE.txt for license information.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.finalizeGeneratedTests = finalizeGeneratedTests;
|
|
6
|
+
const child_process_1 = require("child_process");
|
|
7
|
+
const fs_1 = require("fs");
|
|
8
|
+
const path_1 = require("path");
|
|
9
|
+
function runCommand(bin, args, cwd, opts = {}) {
|
|
10
|
+
try {
|
|
11
|
+
const stdout = (0, child_process_1.execFileSync)(bin, args, { cwd, encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
12
|
+
return { ok: true, stdout: stdout.trim(), stderr: '' };
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
if (opts.allowFailure) {
|
|
16
|
+
const cause = error;
|
|
17
|
+
const stdout = typeof cause.stdout === 'string' ? cause.stdout : cause.stdout?.toString('utf-8') || '';
|
|
18
|
+
const stderr = typeof cause.stderr === 'string' ? cause.stderr : cause.stderr?.toString('utf-8') || cause.message || '';
|
|
19
|
+
return { ok: false, stdout: stdout.trim(), stderr: stderr.trim() };
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function normalizeBranchName(branch) {
|
|
25
|
+
if (!branch || !branch.trim()) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
const trimmed = branch.trim();
|
|
29
|
+
if (trimmed.startsWith('codex/')) {
|
|
30
|
+
return trimmed;
|
|
31
|
+
}
|
|
32
|
+
return `codex/${trimmed.replace(/^\/+/, '')}`;
|
|
33
|
+
}
|
|
34
|
+
function resolveRepoRoot(appPath) {
|
|
35
|
+
const abs = (0, path_1.resolve)(appPath);
|
|
36
|
+
const result = runCommand('git', ['-C', abs, 'rev-parse', '--show-toplevel'], abs, { allowFailure: true });
|
|
37
|
+
if (!result.ok || !result.stdout) {
|
|
38
|
+
throw new Error(`Unable to find git repository root from ${abs}: ${result.stderr || 'git rev-parse failed'}`);
|
|
39
|
+
}
|
|
40
|
+
return result.stdout;
|
|
41
|
+
}
|
|
42
|
+
function readGapReport(path) {
|
|
43
|
+
if (!(0, fs_1.existsSync)(path)) {
|
|
44
|
+
throw new Error(`Gap report not found: ${path}`);
|
|
45
|
+
}
|
|
46
|
+
try {
|
|
47
|
+
return JSON.parse((0, fs_1.readFileSync)(path, 'utf-8'));
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
+
throw new Error(`Unable to parse gap report at ${path}: ${message}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function uniq(values) {
|
|
55
|
+
return Array.from(new Set(values.filter(Boolean)));
|
|
56
|
+
}
|
|
57
|
+
function toRepoRelative(repoRoot, absoluteOrRelative) {
|
|
58
|
+
const absolute = (0, path_1.resolve)(absoluteOrRelative);
|
|
59
|
+
return (0, path_1.relative)(repoRoot, absolute) || '.';
|
|
60
|
+
}
|
|
61
|
+
function isRepoRelativePathSafe(path) {
|
|
62
|
+
if (!path || path === '.') {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (path.startsWith('..') || path.includes('/../') || path.includes('\\..\\')) {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
function collectStageTargets(repoRoot, appPath, testsRoot, gap) {
|
|
71
|
+
const targets = [];
|
|
72
|
+
const applied = gap.applied || {};
|
|
73
|
+
for (const patched of applied.patchedFiles || []) {
|
|
74
|
+
targets.push(toRepoRelative(repoRoot, (0, path_1.resolve)(appPath, patched)));
|
|
75
|
+
}
|
|
76
|
+
for (const generated of applied.generatedTests || []) {
|
|
77
|
+
targets.push(toRepoRelative(repoRoot, generated));
|
|
78
|
+
}
|
|
79
|
+
for (const result of gap.pipeline?.results || []) {
|
|
80
|
+
if (result.generatedDir) {
|
|
81
|
+
targets.push(toRepoRelative(repoRoot, result.generatedDir));
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const artifacts = ['gap.json', 'impact.json', 'plan.json', 'ci-summary.md', 'pr-comment.md'];
|
|
85
|
+
for (const file of artifacts) {
|
|
86
|
+
targets.push(toRepoRelative(repoRoot, (0, path_1.resolve)(testsRoot, '.e2e-ai-agents', file)));
|
|
87
|
+
}
|
|
88
|
+
return uniq(targets).filter((path) => isRepoRelativePathSafe(path) && (0, fs_1.existsSync)((0, path_1.resolve)(repoRoot, path)));
|
|
89
|
+
}
|
|
90
|
+
function ensureBranch(repoRoot, requestedBranch, dryRun = false) {
|
|
91
|
+
const current = runCommand('git', ['branch', '--show-current'], repoRoot, { allowFailure: true });
|
|
92
|
+
const currentBranch = current.stdout || 'HEAD';
|
|
93
|
+
const normalized = normalizeBranchName(requestedBranch);
|
|
94
|
+
if (!normalized) {
|
|
95
|
+
return currentBranch;
|
|
96
|
+
}
|
|
97
|
+
if (currentBranch === normalized) {
|
|
98
|
+
return currentBranch;
|
|
99
|
+
}
|
|
100
|
+
const exists = runCommand('git', ['rev-parse', '--verify', normalized], repoRoot, { allowFailure: true }).ok;
|
|
101
|
+
if (!dryRun) {
|
|
102
|
+
if (exists) {
|
|
103
|
+
runCommand('git', ['checkout', normalized], repoRoot);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
runCommand('git', ['checkout', '-b', normalized], repoRoot);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return normalized;
|
|
110
|
+
}
|
|
111
|
+
function hasStagedChanges(repoRoot) {
|
|
112
|
+
const result = runCommand('git', ['diff', '--cached', '--name-only'], repoRoot, { allowFailure: true });
|
|
113
|
+
if (!result.ok) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
return result.stdout.trim().length > 0;
|
|
117
|
+
}
|
|
118
|
+
function finalizeGeneratedTests(options) {
|
|
119
|
+
const appPath = (0, path_1.resolve)(options.appPath);
|
|
120
|
+
const testsRoot = (0, path_1.resolve)(options.testsRoot || options.appPath);
|
|
121
|
+
const repoRoot = resolveRepoRoot(appPath);
|
|
122
|
+
const gapReportPath = options.gapReportPath || (0, path_1.resolve)(testsRoot, '.e2e-ai-agents', 'gap.json');
|
|
123
|
+
const gap = readGapReport(gapReportPath);
|
|
124
|
+
const stageTargets = collectStageTargets(repoRoot, appPath, testsRoot, gap);
|
|
125
|
+
const commitMessage = options.commitMessage || 'test(e2e): add generated coverage and healed specs';
|
|
126
|
+
const dryRun = Boolean(options.dryRun);
|
|
127
|
+
const branch = ensureBranch(repoRoot, options.branch, dryRun);
|
|
128
|
+
if (stageTargets.length === 0) {
|
|
129
|
+
return {
|
|
130
|
+
repoRoot,
|
|
131
|
+
branch,
|
|
132
|
+
stagedPaths: [],
|
|
133
|
+
committed: false,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
for (const path of stageTargets) {
|
|
137
|
+
if (!dryRun) {
|
|
138
|
+
runCommand('git', ['add', '--', path], repoRoot, { allowFailure: true });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
let committed = false;
|
|
142
|
+
let commitSha;
|
|
143
|
+
if (!dryRun && hasStagedChanges(repoRoot)) {
|
|
144
|
+
runCommand('git', ['commit', '-m', commitMessage], repoRoot);
|
|
145
|
+
committed = true;
|
|
146
|
+
const head = runCommand('git', ['rev-parse', 'HEAD'], repoRoot, { allowFailure: true });
|
|
147
|
+
if (head.ok) {
|
|
148
|
+
commitSha = head.stdout;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
let prUrl;
|
|
152
|
+
if (!dryRun && options.createPr) {
|
|
153
|
+
const args = ['pr', 'create'];
|
|
154
|
+
if (options.prTitle) {
|
|
155
|
+
args.push('--title', options.prTitle);
|
|
156
|
+
}
|
|
157
|
+
if (options.prBody) {
|
|
158
|
+
args.push('--body', options.prBody);
|
|
159
|
+
}
|
|
160
|
+
if (options.baseBranch) {
|
|
161
|
+
args.push('--base', options.baseBranch);
|
|
162
|
+
}
|
|
163
|
+
if (!options.prTitle && !options.prBody) {
|
|
164
|
+
args.push('--fill');
|
|
165
|
+
}
|
|
166
|
+
const result = runCommand('gh', args, repoRoot, { allowFailure: true });
|
|
167
|
+
if (!result.ok) {
|
|
168
|
+
throw new Error(`Failed to create PR via gh: ${result.stderr || 'unknown error'}`);
|
|
169
|
+
}
|
|
170
|
+
prUrl = result.stdout.split('\n').find((line) => line.startsWith('http')) || result.stdout;
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
repoRoot,
|
|
174
|
+
branch,
|
|
175
|
+
stagedPaths: stageTargets,
|
|
176
|
+
committed,
|
|
177
|
+
commitSha,
|
|
178
|
+
prUrl,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
3
|
+
* See LICENSE.txt for license information.
|
|
4
|
+
*
|
|
5
|
+
* Impact Analysis Engine
|
|
6
|
+
*
|
|
7
|
+
* Analyzes code changes and identifies which user flows are affected,
|
|
8
|
+
* then maps those flows to test coverage gaps.
|
|
9
|
+
*/
|
|
10
|
+
export interface GitChange {
|
|
11
|
+
path: string;
|
|
12
|
+
status: 'added' | 'modified' | 'deleted';
|
|
13
|
+
ref?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ChangeAnalysis {
|
|
16
|
+
file: string;
|
|
17
|
+
status: 'added' | 'modified' | 'deleted';
|
|
18
|
+
linesAdded: number;
|
|
19
|
+
linesRemoved: number;
|
|
20
|
+
functions: string[];
|
|
21
|
+
classes: string[];
|
|
22
|
+
imports: Array<{
|
|
23
|
+
from: string;
|
|
24
|
+
names: string[];
|
|
25
|
+
}>;
|
|
26
|
+
}
|
|
27
|
+
export interface Flow {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
priority: 'P0' | 'P1' | 'P2';
|
|
31
|
+
keywords: string[];
|
|
32
|
+
paths: string[];
|
|
33
|
+
tests: string[];
|
|
34
|
+
audience: string[];
|
|
35
|
+
flags: Array<{
|
|
36
|
+
name: string;
|
|
37
|
+
source: string;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
export interface FlowImpact {
|
|
41
|
+
flow: Flow;
|
|
42
|
+
matchType: 'path' | 'keyword' | 'import' | 'combined';
|
|
43
|
+
confidence: number;
|
|
44
|
+
affectedFiles: string[];
|
|
45
|
+
existingTests: string[];
|
|
46
|
+
testGaps: string[];
|
|
47
|
+
reasons: string[];
|
|
48
|
+
}
|
|
49
|
+
export interface FlowGroup {
|
|
50
|
+
id: string;
|
|
51
|
+
name: string;
|
|
52
|
+
description: string;
|
|
53
|
+
flows: string[];
|
|
54
|
+
testStrategy: 'sequential' | 'parallel' | 'mixed';
|
|
55
|
+
priority: string;
|
|
56
|
+
affectedFlows: FlowImpact[];
|
|
57
|
+
}
|
|
58
|
+
export interface ImpactReport {
|
|
59
|
+
timestamp: string;
|
|
60
|
+
gitRef: string;
|
|
61
|
+
totalChanges: number;
|
|
62
|
+
affectedFlows: FlowImpact[];
|
|
63
|
+
flowGroups: FlowGroup[];
|
|
64
|
+
ungroupedFlows: FlowImpact[];
|
|
65
|
+
priorityBreakdown: {
|
|
66
|
+
p0: number;
|
|
67
|
+
p1: number;
|
|
68
|
+
p2: number;
|
|
69
|
+
};
|
|
70
|
+
testCoverage: {
|
|
71
|
+
total: number;
|
|
72
|
+
covered: number;
|
|
73
|
+
gaps: number;
|
|
74
|
+
};
|
|
75
|
+
recommendations: string[];
|
|
76
|
+
hasP0Impact: boolean;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Intelligently detect the best git reference for comparison:
|
|
80
|
+
* - If on feature branch: use origin/master, origin/main, or master
|
|
81
|
+
* - If on main branch: use HEAD~1
|
|
82
|
+
* Returns the best available reference to compare against
|
|
83
|
+
*/
|
|
84
|
+
export declare function detectComparisonBase(): string;
|
|
85
|
+
/**
|
|
86
|
+
* Get git changes since a given reference
|
|
87
|
+
* Filters to only include frontend files (webapp and e2e-tests)
|
|
88
|
+
*/
|
|
89
|
+
export declare function getGitChanges(since?: string): GitChange[];
|
|
90
|
+
/**
|
|
91
|
+
* Load flow catalog from flows.json
|
|
92
|
+
*/
|
|
93
|
+
export declare function loadFlowCatalog(catalogPath?: string): Flow[];
|
|
94
|
+
/**
|
|
95
|
+
* Match a flow to changed files
|
|
96
|
+
*/
|
|
97
|
+
export declare function matchFlowToChanges(flow: Flow, changes: ChangeAnalysis[]): FlowImpact | null;
|
|
98
|
+
/**
|
|
99
|
+
* Find existing tests for a flow
|
|
100
|
+
*/
|
|
101
|
+
export declare function findExistingTests(flow: Flow, repoRoot?: string): string[];
|
|
102
|
+
/**
|
|
103
|
+
* Identify test coverage gaps for a flow
|
|
104
|
+
*/
|
|
105
|
+
export declare function identifyTestGaps(flow: Flow, existingTests: string[]): string[];
|
|
106
|
+
/**
|
|
107
|
+
* Analyze code impact and return comprehensive report
|
|
108
|
+
*/
|
|
109
|
+
export declare function analyzeImpact(changes: GitChange[], flows: Flow[], options?: {
|
|
110
|
+
verbose?: boolean;
|
|
111
|
+
includeTests?: boolean;
|
|
112
|
+
repoRoot?: string;
|
|
113
|
+
}): Promise<ImpactReport>;
|
|
114
|
+
//# sourceMappingURL=impact-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"impact-analyzer.d.ts","sourceRoot":"","sources":["../../src/agent/impact-analyzer.ts"],"names":[],"mappings":"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"}
|