@yasserkhanorg/e2e-agents 0.5.16 → 0.6.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/dist/agent/pipeline.d.ts +1 -1
- package/dist/agent/pipeline.d.ts.map +1 -1
- package/dist/agent/plan.d.ts +0 -12
- package/dist/agent/plan.d.ts.map +1 -1
- package/dist/agent/plan.js +0 -365
- package/dist/agent/types.d.ts +42 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +4 -0
- package/dist/api.d.ts +10 -14
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +29 -59
- package/dist/cli.js +41 -174
- package/dist/engine/impact_engine.d.ts +36 -0
- package/dist/engine/impact_engine.d.ts.map +1 -0
- package/dist/engine/impact_engine.js +196 -0
- package/dist/engine/plan_builder.d.ts +9 -0
- package/dist/engine/plan_builder.d.ts.map +1 -0
- package/dist/engine/plan_builder.js +329 -0
- package/dist/esm/agent/plan.js +1 -360
- package/dist/esm/agent/types.js +3 -0
- package/dist/esm/api.js +27 -56
- package/dist/esm/cli.js +40 -173
- package/dist/esm/engine/impact_engine.js +191 -0
- package/dist/esm/engine/plan_builder.js +323 -0
- package/dist/esm/index.js +6 -3
- package/dist/esm/knowledge/route_families.js +57 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -5
- package/dist/knowledge/route_families.d.ts +19 -0
- package/dist/knowledge/route_families.d.ts.map +1 -1
- package/dist/knowledge/route_families.js +60 -0
- package/package.json +1 -1
- package/dist/agent/ai_flow_analysis.d.ts +0 -13
- package/dist/agent/ai_flow_analysis.d.ts.map +0 -1
- package/dist/agent/ai_flow_analysis.js +0 -334
- package/dist/agent/ai_mapping.d.ts +0 -14
- package/dist/agent/ai_mapping.d.ts.map +0 -1
- package/dist/agent/ai_mapping.js +0 -560
- package/dist/agent/analysis.d.ts +0 -64
- package/dist/agent/analysis.d.ts.map +0 -1
- package/dist/agent/analysis.js +0 -292
- package/dist/agent/blast_radius.d.ts +0 -4
- package/dist/agent/blast_radius.d.ts.map +0 -1
- package/dist/agent/blast_radius.js +0 -37
- package/dist/agent/dependency_graph.d.ts +0 -14
- package/dist/agent/dependency_graph.d.ts.map +0 -1
- package/dist/agent/dependency_graph.js +0 -227
- package/dist/agent/flags.d.ts +0 -23
- package/dist/agent/flags.d.ts.map +0 -1
- package/dist/agent/flags.js +0 -171
- package/dist/agent/flow_catalog.d.ts +0 -25
- package/dist/agent/flow_catalog.d.ts.map +0 -1
- package/dist/agent/flow_catalog.js +0 -115
- package/dist/agent/flow_mapping.d.ts +0 -10
- package/dist/agent/flow_mapping.d.ts.map +0 -1
- package/dist/agent/flow_mapping.js +0 -84
- package/dist/agent/framework.d.ts +0 -13
- package/dist/agent/framework.d.ts.map +0 -1
- package/dist/agent/framework.js +0 -149
- package/dist/agent/gap_suggestions.d.ts +0 -14
- package/dist/agent/gap_suggestions.d.ts.map +0 -1
- package/dist/agent/gap_suggestions.js +0 -101
- package/dist/agent/generator.d.ts +0 -10
- package/dist/agent/generator.d.ts.map +0 -1
- package/dist/agent/generator.js +0 -115
- package/dist/agent/operational_insights.d.ts +0 -41
- package/dist/agent/operational_insights.d.ts.map +0 -1
- package/dist/agent/operational_insights.js +0 -127
- package/dist/agent/report.d.ts +0 -97
- package/dist/agent/report.d.ts.map +0 -1
- package/dist/agent/report.js +0 -159
- package/dist/agent/runner.d.ts +0 -7
- package/dist/agent/runner.d.ts.map +0 -1
- package/dist/agent/runner.js +0 -898
- package/dist/agent/selectors.d.ts +0 -10
- package/dist/agent/selectors.d.ts.map +0 -1
- package/dist/agent/selectors.js +0 -75
- package/dist/agent/subsystem_risk.d.ts +0 -23
- package/dist/agent/subsystem_risk.d.ts.map +0 -1
- package/dist/agent/subsystem_risk.js +0 -207
- package/dist/agent/tests.d.ts +0 -19
- package/dist/agent/tests.d.ts.map +0 -1
- package/dist/agent/tests.js +0 -116
- package/dist/agent/traceability.d.ts +0 -22
- package/dist/agent/traceability.d.ts.map +0 -1
- package/dist/agent/traceability.js +0 -183
- package/dist/esm/agent/ai_flow_analysis.js +0 -331
- package/dist/esm/agent/ai_mapping.js +0 -557
- package/dist/esm/agent/analysis.js +0 -287
- package/dist/esm/agent/blast_radius.js +0 -34
- package/dist/esm/agent/dependency_graph.js +0 -224
- package/dist/esm/agent/flags.js +0 -160
- package/dist/esm/agent/flow_catalog.js +0 -112
- package/dist/esm/agent/flow_mapping.js +0 -81
- package/dist/esm/agent/framework.js +0 -145
- package/dist/esm/agent/gap_suggestions.js +0 -98
- package/dist/esm/agent/generator.js +0 -112
- package/dist/esm/agent/operational_insights.js +0 -124
- package/dist/esm/agent/report.js +0 -156
- package/dist/esm/agent/runner.js +0 -894
- package/dist/esm/agent/selectors.js +0 -71
- package/dist/esm/agent/subsystem_risk.js +0 -204
- package/dist/esm/agent/tests.js +0 -111
- package/dist/esm/agent/traceability.js +0 -180
|
@@ -1,101 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
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
|
|
@@ -1 +0,0 @@
|
|
|
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"}
|
package/dist/agent/generator.js
DELETED
|
@@ -1,115 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { type PlanReport } from './plan.js';
|
|
2
|
-
import type { CalibrationSummary } from './feedback.js';
|
|
3
|
-
export interface FlakyTestRecord {
|
|
4
|
-
test: string;
|
|
5
|
-
flakeRate: number;
|
|
6
|
-
flakeRate7d?: number;
|
|
7
|
-
flakeRate30d?: number;
|
|
8
|
-
trend?: 'up' | 'down' | 'stable';
|
|
9
|
-
subsystem?: string;
|
|
10
|
-
owners?: string[];
|
|
11
|
-
quarantine?: boolean;
|
|
12
|
-
quarantineState?: 'none' | 'active' | 'retire-candidate';
|
|
13
|
-
lastFailureAt?: string;
|
|
14
|
-
}
|
|
15
|
-
export interface FlakyManifest {
|
|
16
|
-
schemaVersion?: string;
|
|
17
|
-
tests: FlakyTestRecord[];
|
|
18
|
-
}
|
|
19
|
-
export interface QualityGateRecord {
|
|
20
|
-
name: string;
|
|
21
|
-
status: 'pass' | 'warn' | 'fail';
|
|
22
|
-
details?: string;
|
|
23
|
-
}
|
|
24
|
-
export interface QualityGateManifest {
|
|
25
|
-
schemaVersion?: string;
|
|
26
|
-
gates: QualityGateRecord[];
|
|
27
|
-
}
|
|
28
|
-
export interface OperationalInsights {
|
|
29
|
-
flaky?: {
|
|
30
|
-
highRiskRecommendedTests: FlakyTestRecord[];
|
|
31
|
-
quarantinedRecommendedTests: string[];
|
|
32
|
-
ownerMentions?: string[];
|
|
33
|
-
};
|
|
34
|
-
qualityGates?: {
|
|
35
|
-
failed: QualityGateRecord[];
|
|
36
|
-
warnings: QualityGateRecord[];
|
|
37
|
-
};
|
|
38
|
-
calibration?: CalibrationSummary['overall'];
|
|
39
|
-
}
|
|
40
|
-
export declare function applyOperationalInsights(plan: PlanReport, appRoot: string): PlanReport;
|
|
41
|
-
//# sourceMappingURL=operational_insights.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"operational_insights.d.ts","sourceRoot":"","sources":["../../src/agent/operational_insights.ts"],"names":[],"mappings":"AAKA,OAAO,EAAyB,KAAK,UAAU,EAAC,MAAM,WAAW,CAAC;AAClE,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,eAAe,CAAC;AAGtD,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,IAAI,GAAG,MAAM,GAAG,QAAQ,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,kBAAkB,CAAC;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,aAAa;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,eAAe,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,iBAAiB,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,mBAAmB;IAChC,KAAK,CAAC,EAAE;QACJ,wBAAwB,EAAE,eAAe,EAAE,CAAC;QAC5C,2BAA2B,EAAE,MAAM,EAAE,CAAC;QACtC,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;KAC5B,CAAC;IACF,YAAY,CAAC,EAAE;QACX,MAAM,EAAE,iBAAiB,EAAE,CAAC;QAC5B,QAAQ,EAAE,iBAAiB,EAAE,CAAC;KACjC,CAAC;IACF,WAAW,CAAC,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;CAC/C;AA2CD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CA4FtF"}
|
|
@@ -1,127 +0,0 @@
|
|
|
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.applyOperationalInsights = applyOperationalInsights;
|
|
6
|
-
const fs_1 = require("fs");
|
|
7
|
-
const path_1 = require("path");
|
|
8
|
-
const plan_js_1 = require("./plan.js");
|
|
9
|
-
const test_path_js_1 = require("./test_path.js");
|
|
10
|
-
function readJson(path) {
|
|
11
|
-
if (!(0, fs_1.existsSync)(path)) {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
try {
|
|
15
|
-
return JSON.parse((0, fs_1.readFileSync)(path, 'utf-8'));
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return null;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
function normalizeTestName(test) {
|
|
22
|
-
return test.replace(/ \(flags:.*\)$/, '').trim();
|
|
23
|
-
}
|
|
24
|
-
function subsystemForTest(test) {
|
|
25
|
-
return (0, test_path_js_1.inferSubsystemFromTestPath)(test);
|
|
26
|
-
}
|
|
27
|
-
function riskyRate(entry) {
|
|
28
|
-
if (entry.flakeRate30d !== undefined) {
|
|
29
|
-
return entry.flakeRate30d;
|
|
30
|
-
}
|
|
31
|
-
return entry.flakeRate;
|
|
32
|
-
}
|
|
33
|
-
function loadFlakyManifest(appRoot) {
|
|
34
|
-
const path = (0, path_1.join)(appRoot, '.e2e-ai-agents', 'flaky-tests.json');
|
|
35
|
-
return readJson(path);
|
|
36
|
-
}
|
|
37
|
-
function loadQualityGates(appRoot) {
|
|
38
|
-
const path = (0, path_1.join)(appRoot, '.e2e-ai-agents', 'quality-gates.json');
|
|
39
|
-
return readJson(path);
|
|
40
|
-
}
|
|
41
|
-
function loadCalibration(appRoot) {
|
|
42
|
-
const path = (0, path_1.join)(appRoot, '.e2e-ai-agents', 'calibration.json');
|
|
43
|
-
return readJson(path);
|
|
44
|
-
}
|
|
45
|
-
function applyOperationalInsights(plan, appRoot) {
|
|
46
|
-
const enhanced = { ...plan };
|
|
47
|
-
const insights = {};
|
|
48
|
-
const flaky = loadFlakyManifest(appRoot);
|
|
49
|
-
if (flaky && Array.isArray(flaky.tests)) {
|
|
50
|
-
const recommended = new Set(plan.recommendedTests.map(normalizeTestName));
|
|
51
|
-
const risky = flaky.tests
|
|
52
|
-
.filter((entry) => recommended.has(normalizeTestName(entry.test)) && riskyRate(entry) >= 0.2)
|
|
53
|
-
.sort((a, b) => riskyRate(b) - riskyRate(a))
|
|
54
|
-
.slice(0, 10);
|
|
55
|
-
const quarantined = risky.filter((entry) => entry.quarantine).map((entry) => entry.test);
|
|
56
|
-
const owners = Array.from(new Set(risky
|
|
57
|
-
.flatMap((entry) => entry.owners || [])
|
|
58
|
-
.filter(Boolean)));
|
|
59
|
-
insights.flaky = {
|
|
60
|
-
highRiskRecommendedTests: risky,
|
|
61
|
-
quarantinedRecommendedTests: quarantined,
|
|
62
|
-
ownerMentions: owners,
|
|
63
|
-
};
|
|
64
|
-
if (quarantined.length > 0) {
|
|
65
|
-
enhanced.reasons = [...enhanced.reasons, `Quarantined flaky tests in recommendation: ${quarantined.join(', ')}`];
|
|
66
|
-
}
|
|
67
|
-
if (owners.length > 0) {
|
|
68
|
-
enhanced.reasons = [...enhanced.reasons, `Subsystem owners to notify for flaky risk: ${owners.join(', ')}`];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
const gates = loadQualityGates(appRoot);
|
|
72
|
-
if (gates && Array.isArray(gates.gates)) {
|
|
73
|
-
const failed = gates.gates.filter((gate) => gate.status === 'fail');
|
|
74
|
-
const warnings = gates.gates.filter((gate) => gate.status === 'warn');
|
|
75
|
-
insights.qualityGates = { failed, warnings };
|
|
76
|
-
if (failed.length > 0 && enhanced.runSet !== 'full') {
|
|
77
|
-
enhanced.runSet = 'full';
|
|
78
|
-
enhanced.reasons = [...enhanced.reasons, `Quality gates failed: ${failed.map((gate) => gate.name).join(', ')}`];
|
|
79
|
-
enhanced.policy.triggeredRules = [...new Set([...enhanced.policy.triggeredRules, 'quality-gate-failed'])];
|
|
80
|
-
enhanced.decision = {
|
|
81
|
-
action: 'run-now',
|
|
82
|
-
title: 'Run now',
|
|
83
|
-
summary: 'Quality gate failures detected. Full suite is required before merge.',
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const calibration = loadCalibration(appRoot);
|
|
88
|
-
if (calibration) {
|
|
89
|
-
insights.calibration = calibration.overall;
|
|
90
|
-
if (calibration.overall.falseNegativeRate >= 0.2 && enhanced.runSet !== 'full') {
|
|
91
|
-
enhanced.runSet = 'full';
|
|
92
|
-
enhanced.reasons = [...enhanced.reasons, 'Historical false-negative rate is high; escalating to full suite.'];
|
|
93
|
-
enhanced.policy.triggeredRules = [...new Set([...enhanced.policy.triggeredRules, 'historical-fnr-high'])];
|
|
94
|
-
}
|
|
95
|
-
const recommendedSubsystems = Array.from(new Set(plan.recommendedTests.map(subsystemForTest)));
|
|
96
|
-
const highRiskSubsystems = recommendedSubsystems
|
|
97
|
-
.map((subsystem) => {
|
|
98
|
-
const metric = calibration.bySubsystem[subsystem];
|
|
99
|
-
if (!metric) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
if (metric.samples < 5) {
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
if (metric.recent30d.falseNegativeRate >= 0.2 || metric.falseNegativeRate >= 0.25) {
|
|
106
|
-
return { subsystem, fnr: metric.recent30d.falseNegativeRate || metric.falseNegativeRate };
|
|
107
|
-
}
|
|
108
|
-
return null;
|
|
109
|
-
})
|
|
110
|
-
.filter(Boolean);
|
|
111
|
-
if (highRiskSubsystems.length > 0 && enhanced.runSet !== 'full') {
|
|
112
|
-
enhanced.runSet = 'full';
|
|
113
|
-
enhanced.reasons = [
|
|
114
|
-
...enhanced.reasons,
|
|
115
|
-
`Historical subsystem false-negative risk is high: ${highRiskSubsystems.map((entry) => `${entry.subsystem}(${entry.fnr})`).join(', ')}`,
|
|
116
|
-
];
|
|
117
|
-
enhanced.policy.triggeredRules = [...new Set([...enhanced.policy.triggeredRules, 'subsystem-fnr-high'])];
|
|
118
|
-
enhanced.decision = {
|
|
119
|
-
action: 'run-now',
|
|
120
|
-
title: 'Run now',
|
|
121
|
-
summary: 'Subsystem calibration risk is high. Full suite is required before merge.',
|
|
122
|
-
};
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
enhanced.insights = insights;
|
|
126
|
-
return (0, plan_js_1.refreshPlanEnforcement)(enhanced);
|
|
127
|
-
}
|
package/dist/agent/report.d.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import type { AgentConfig } from './config.js';
|
|
2
|
-
import type { FlowImpact } from './analysis.js';
|
|
3
|
-
import type { FlowCoverage } from './tests.js';
|
|
4
|
-
import type { DataTestIdSuggestion } from './selectors.js';
|
|
5
|
-
import type { GapTestSuggestion } from './gap_suggestions.js';
|
|
6
|
-
export interface ReportData {
|
|
7
|
-
mode: 'impact' | 'gap';
|
|
8
|
-
runMetadata?: {
|
|
9
|
-
runId: string;
|
|
10
|
-
startedAt: string;
|
|
11
|
-
completedAt: string;
|
|
12
|
-
durationMs: number;
|
|
13
|
-
sinceRef: string;
|
|
14
|
-
appPath: string;
|
|
15
|
-
testsRoot: string;
|
|
16
|
-
};
|
|
17
|
-
changedFiles: string[];
|
|
18
|
-
flows: FlowImpact[];
|
|
19
|
-
coverage: FlowCoverage[];
|
|
20
|
-
gaps: FlowImpact[];
|
|
21
|
-
dataTestIds: DataTestIdSuggestion[];
|
|
22
|
-
testSuggestions?: GapTestSuggestion[];
|
|
23
|
-
suggestedNewTests?: GapTestSuggestion[];
|
|
24
|
-
framework: string;
|
|
25
|
-
testPatterns: string[];
|
|
26
|
-
specPDF?: string;
|
|
27
|
-
warnings: string[];
|
|
28
|
-
flowCatalog?: string;
|
|
29
|
-
recommendedTests?: string[];
|
|
30
|
-
impactModel?: {
|
|
31
|
-
schemaVersion: '1.0.0';
|
|
32
|
-
flowMapping: 'catalog' | 'heuristic' | 'ai';
|
|
33
|
-
testMapping: 'catalog' | 'traceability' | 'heuristic' | 'ai';
|
|
34
|
-
confidenceClass: 'high' | 'medium' | 'low';
|
|
35
|
-
traceability?: {
|
|
36
|
-
source: 'manifest';
|
|
37
|
-
enabled: boolean;
|
|
38
|
-
manifestPath: string;
|
|
39
|
-
manifestFound: boolean;
|
|
40
|
-
manifestTests: number;
|
|
41
|
-
manifestEdges: number;
|
|
42
|
-
matchedFlows: number;
|
|
43
|
-
totalFlows: number;
|
|
44
|
-
matchedTests: number;
|
|
45
|
-
coverageRatio: number;
|
|
46
|
-
};
|
|
47
|
-
dependencyGraph?: {
|
|
48
|
-
source: 'static-dependency-graph';
|
|
49
|
-
enabled: boolean;
|
|
50
|
-
seedFiles: number;
|
|
51
|
-
expandedFiles: number;
|
|
52
|
-
analyzedFiles: number;
|
|
53
|
-
analyzedEdges: number;
|
|
54
|
-
maxDepth: number;
|
|
55
|
-
truncated: boolean;
|
|
56
|
-
};
|
|
57
|
-
subsystemRisk?: {
|
|
58
|
-
source: 'map';
|
|
59
|
-
enabled: boolean;
|
|
60
|
-
mapPath: string;
|
|
61
|
-
mapFound: boolean;
|
|
62
|
-
rulesLoaded: number;
|
|
63
|
-
filesMatched: number;
|
|
64
|
-
ruleMatches: number;
|
|
65
|
-
boostedFlows: number;
|
|
66
|
-
};
|
|
67
|
-
};
|
|
68
|
-
pipeline?: {
|
|
69
|
-
runner: string;
|
|
70
|
-
results: Array<{
|
|
71
|
-
flowId: string;
|
|
72
|
-
flowName: string;
|
|
73
|
-
generatedDir: string;
|
|
74
|
-
generateStatus: string;
|
|
75
|
-
healStatus?: string;
|
|
76
|
-
error?: string;
|
|
77
|
-
failureCategory?: string;
|
|
78
|
-
failureCode?: string;
|
|
79
|
-
}>;
|
|
80
|
-
warnings: string[];
|
|
81
|
-
mcp?: {
|
|
82
|
-
requested: boolean;
|
|
83
|
-
active: boolean;
|
|
84
|
-
backend: string;
|
|
85
|
-
};
|
|
86
|
-
};
|
|
87
|
-
applied?: {
|
|
88
|
-
patchedFiles: string[];
|
|
89
|
-
generatedTests: string[];
|
|
90
|
-
skippedTests: string[];
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
export declare function writeReport(appRoot: string, config: AgentConfig, data: ReportData): {
|
|
94
|
-
markdownPath: string;
|
|
95
|
-
jsonPath: string;
|
|
96
|
-
};
|
|
97
|
-
//# sourceMappingURL=report.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/agent/report.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/report.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
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.writeReport = writeReport;
|
|
6
|
-
const fs_1 = require("fs");
|
|
7
|
-
const path_1 = require("path");
|
|
8
|
-
const flags_js_1 = require("./flags.js");
|
|
9
|
-
function formatFlow(flow) {
|
|
10
|
-
const reasonText = flow.reasons.length > 0 ? flow.reasons.join('; ') : 'No specific reasons';
|
|
11
|
-
const audienceText = flow.audience && flow.audience.length > 0 ? `\n Audience: ${flow.audience.join(', ')}` : '';
|
|
12
|
-
const flagsText = flow.flags && flow.flags.length > 0 ? `\n Flags: ${(0, flags_js_1.formatFlags)(flow.flags)}` : '';
|
|
13
|
-
const blastText = flow.blastRadius ? `\n Blast radius: ${flow.blastRadius.summary}` : '';
|
|
14
|
-
return `- [${flow.priority}] ${flow.name} (${flow.id})\n Score: ${flow.score}\n Reasons: ${reasonText}\n Files: ${flow.files.join(', ')}${audienceText}${flagsText}${blastText}`;
|
|
15
|
-
}
|
|
16
|
-
function formatGap(flow) {
|
|
17
|
-
return `- [${flow.priority}] ${flow.name} (${flow.id})`;
|
|
18
|
-
}
|
|
19
|
-
function formatSuggestion(suggestion) {
|
|
20
|
-
return `- ${suggestion.file}:${suggestion.line} -> ${suggestion.testId}\n ${suggestion.snippet}`;
|
|
21
|
-
}
|
|
22
|
-
function formatTestSuggestion(suggestion) {
|
|
23
|
-
const source = suggestion.sourceFiles.length > 0 ? suggestion.sourceFiles.join(', ') : 'N/A';
|
|
24
|
-
return `- [${suggestion.priority}] ${suggestion.flowName} (${suggestion.flowId})\n Path: ${suggestion.suggestedTestPath}\n Source files: ${source}\n Why: ${suggestion.rationale}`;
|
|
25
|
-
}
|
|
26
|
-
function flowCounts(flows) {
|
|
27
|
-
return flows.reduce((acc, flow) => {
|
|
28
|
-
if (flow.priority === 'P0')
|
|
29
|
-
acc.p0 += 1;
|
|
30
|
-
else if (flow.priority === 'P1')
|
|
31
|
-
acc.p1 += 1;
|
|
32
|
-
else
|
|
33
|
-
acc.p2 += 1;
|
|
34
|
-
return acc;
|
|
35
|
-
}, { p0: 0, p1: 0, p2: 0 });
|
|
36
|
-
}
|
|
37
|
-
function writeReport(appRoot, config, data) {
|
|
38
|
-
const specsDir = (0, path_1.join)(appRoot, config.artifacts.specsDir);
|
|
39
|
-
const baseDir = (0, path_1.join)(appRoot, '.e2e-ai-agents');
|
|
40
|
-
if (config.artifacts.mode !== 'none') {
|
|
41
|
-
(0, fs_1.mkdirSync)(specsDir, { recursive: true });
|
|
42
|
-
}
|
|
43
|
-
(0, fs_1.mkdirSync)(baseDir, { recursive: true });
|
|
44
|
-
const counts = flowCounts(data.flows);
|
|
45
|
-
const markdownLines = [];
|
|
46
|
-
markdownLines.push(`# ${data.mode === 'impact' ? 'Impact Analysis' : 'Gap Analysis'} Report`);
|
|
47
|
-
markdownLines.push('');
|
|
48
|
-
if (data.runMetadata) {
|
|
49
|
-
markdownLines.push(`Run ID: ${data.runMetadata.runId}`);
|
|
50
|
-
markdownLines.push(`Run window: ${data.runMetadata.startedAt} -> ${data.runMetadata.completedAt}`);
|
|
51
|
-
markdownLines.push(`Run duration (ms): ${data.runMetadata.durationMs}`);
|
|
52
|
-
markdownLines.push(`Since ref: ${data.runMetadata.sinceRef}`);
|
|
53
|
-
}
|
|
54
|
-
markdownLines.push(`Framework: ${data.framework}`);
|
|
55
|
-
markdownLines.push(`Test Patterns: ${data.testPatterns.join(', ') || 'None'}`);
|
|
56
|
-
if (data.flowCatalog) {
|
|
57
|
-
markdownLines.push(`Flow Catalog: ${data.flowCatalog}`);
|
|
58
|
-
}
|
|
59
|
-
if (data.impactModel) {
|
|
60
|
-
markdownLines.push(`Impact Model: flow=${data.impactModel.flowMapping} test=${data.impactModel.testMapping} confidence=${data.impactModel.confidenceClass}`);
|
|
61
|
-
if (data.impactModel.traceability) {
|
|
62
|
-
const traceability = data.impactModel.traceability;
|
|
63
|
-
markdownLines.push(`Traceability: enabled=${traceability.enabled} manifestFound=${traceability.manifestFound} matchedFlows=${traceability.matchedFlows}/${traceability.totalFlows} matchedTests=${traceability.matchedTests} coverageRatio=${traceability.coverageRatio}`);
|
|
64
|
-
}
|
|
65
|
-
if (data.impactModel.dependencyGraph) {
|
|
66
|
-
const graph = data.impactModel.dependencyGraph;
|
|
67
|
-
markdownLines.push(`Dependency Graph: enabled=${graph.enabled} seeds=${graph.seedFiles} expanded=${graph.expandedFiles} files=${graph.analyzedFiles} edges=${graph.analyzedEdges} depth=${graph.maxDepth}${graph.truncated ? ' (truncated)' : ''}`);
|
|
68
|
-
}
|
|
69
|
-
if (data.impactModel.subsystemRisk) {
|
|
70
|
-
const subsystemRisk = data.impactModel.subsystemRisk;
|
|
71
|
-
markdownLines.push(`Subsystem Risk: enabled=${subsystemRisk.enabled} mapFound=${subsystemRisk.mapFound} rules=${subsystemRisk.rulesLoaded} filesMatched=${subsystemRisk.filesMatched} ruleMatches=${subsystemRisk.ruleMatches} boostedFlows=${subsystemRisk.boostedFlows}`);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
markdownLines.push(`Changed Files: ${data.changedFiles.length}`);
|
|
75
|
-
markdownLines.push(`Flows: P0=${counts.p0} P1=${counts.p1} P2=${counts.p2}`);
|
|
76
|
-
if (data.specPDF) {
|
|
77
|
-
markdownLines.push(`Spec PDF: ${data.specPDF}`);
|
|
78
|
-
}
|
|
79
|
-
if (data.warnings.length > 0) {
|
|
80
|
-
markdownLines.push('');
|
|
81
|
-
markdownLines.push('Warnings:');
|
|
82
|
-
markdownLines.push(...data.warnings.map((warning) => `- ${warning}`));
|
|
83
|
-
}
|
|
84
|
-
if (data.flows.length > 0) {
|
|
85
|
-
markdownLines.push('');
|
|
86
|
-
markdownLines.push('Impacted Flows:');
|
|
87
|
-
markdownLines.push(...data.flows.map(formatFlow));
|
|
88
|
-
}
|
|
89
|
-
if (data.gaps.length > 0) {
|
|
90
|
-
markdownLines.push('');
|
|
91
|
-
markdownLines.push('Coverage Gaps (P0/P1 without tests):');
|
|
92
|
-
markdownLines.push(...data.gaps.map(formatGap));
|
|
93
|
-
}
|
|
94
|
-
if (data.recommendedTests && data.recommendedTests.length > 0) {
|
|
95
|
-
markdownLines.push('');
|
|
96
|
-
markdownLines.push('Recommended Tests to Run:');
|
|
97
|
-
markdownLines.push(...data.recommendedTests.map((test) => `- ${test}`));
|
|
98
|
-
}
|
|
99
|
-
if (data.pipeline) {
|
|
100
|
-
markdownLines.push('');
|
|
101
|
-
markdownLines.push('Pipeline Results:');
|
|
102
|
-
markdownLines.push(`- Runner: ${data.pipeline.runner}`);
|
|
103
|
-
if (data.pipeline.mcp) {
|
|
104
|
-
markdownLines.push(`- MCP: requested=${data.pipeline.mcp.requested} active=${data.pipeline.mcp.active} backend=${data.pipeline.mcp.backend}`);
|
|
105
|
-
}
|
|
106
|
-
for (const result of data.pipeline.results) {
|
|
107
|
-
const status = result.healStatus ? `${result.generateStatus}/${result.healStatus}` : result.generateStatus;
|
|
108
|
-
markdownLines.push(`- ${result.flowId} (${result.flowName}): ${status} -> ${result.generatedDir}`);
|
|
109
|
-
if (result.error) {
|
|
110
|
-
markdownLines.push(` Error: ${result.error}`);
|
|
111
|
-
}
|
|
112
|
-
if (result.failureCategory || result.failureCode) {
|
|
113
|
-
markdownLines.push(` Failure taxonomy: category=${result.failureCategory || 'unknown'} code=${result.failureCode || 'unknown'}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
if (data.pipeline.warnings.length > 0) {
|
|
117
|
-
markdownLines.push('Pipeline warnings:');
|
|
118
|
-
markdownLines.push(...data.pipeline.warnings.map((warning) => `- ${warning}`));
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
if (data.dataTestIds.length > 0) {
|
|
122
|
-
markdownLines.push('');
|
|
123
|
-
markdownLines.push('data-testid Suggestions:');
|
|
124
|
-
markdownLines.push(...data.dataTestIds.map(formatSuggestion));
|
|
125
|
-
}
|
|
126
|
-
if (data.testSuggestions && data.testSuggestions.length > 0) {
|
|
127
|
-
markdownLines.push('');
|
|
128
|
-
markdownLines.push('Suggested New Tests (Actionable):');
|
|
129
|
-
markdownLines.push(...data.testSuggestions.map(formatTestSuggestion));
|
|
130
|
-
}
|
|
131
|
-
if (data.applied) {
|
|
132
|
-
markdownLines.push('');
|
|
133
|
-
markdownLines.push('Applied Changes:');
|
|
134
|
-
if (data.applied.patchedFiles.length > 0) {
|
|
135
|
-
markdownLines.push(`- Patched files: ${data.applied.patchedFiles.join(', ')}`);
|
|
136
|
-
}
|
|
137
|
-
if (data.applied.generatedTests.length > 0) {
|
|
138
|
-
markdownLines.push(`- Generated tests: ${data.applied.generatedTests.join(', ')}`);
|
|
139
|
-
}
|
|
140
|
-
if (data.applied.skippedTests.length > 0) {
|
|
141
|
-
markdownLines.push(`- Skipped test files: ${data.applied.skippedTests.join(', ')}`);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
const markdownContent = markdownLines.join('\n');
|
|
145
|
-
const reportName = data.mode === 'impact' ? 'impact-plan.md' : 'gap-report.md';
|
|
146
|
-
const markdownPath = (0, path_1.join)(specsDir, reportName);
|
|
147
|
-
if (config.artifacts.mode !== 'none') {
|
|
148
|
-
(0, fs_1.writeFileSync)(markdownPath, markdownContent, 'utf-8');
|
|
149
|
-
}
|
|
150
|
-
const jsonPath = (0, path_1.join)(baseDir, data.mode === 'impact' ? 'impact.json' : 'gap.json');
|
|
151
|
-
const jsonData = data.mode === 'gap'
|
|
152
|
-
? {
|
|
153
|
-
...data,
|
|
154
|
-
suggestedNewTests: data.suggestedNewTests || data.testSuggestions || [],
|
|
155
|
-
}
|
|
156
|
-
: data;
|
|
157
|
-
(0, fs_1.writeFileSync)(jsonPath, JSON.stringify(jsonData, null, 2), 'utf-8');
|
|
158
|
-
return { markdownPath, jsonPath };
|
|
159
|
-
}
|