@yasserkhanorg/e2e-agents 0.5.15 → 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.
Files changed (105) hide show
  1. package/dist/agent/pipeline.d.ts +1 -1
  2. package/dist/agent/pipeline.d.ts.map +1 -1
  3. package/dist/agent/plan.d.ts +0 -12
  4. package/dist/agent/plan.d.ts.map +1 -1
  5. package/dist/agent/plan.js +0 -365
  6. package/dist/agent/types.d.ts +42 -0
  7. package/dist/agent/types.d.ts.map +1 -0
  8. package/dist/agent/types.js +4 -0
  9. package/dist/api.d.ts +10 -14
  10. package/dist/api.d.ts.map +1 -1
  11. package/dist/api.js +29 -59
  12. package/dist/cli.js +41 -174
  13. package/dist/engine/impact_engine.d.ts +36 -0
  14. package/dist/engine/impact_engine.d.ts.map +1 -0
  15. package/dist/engine/impact_engine.js +196 -0
  16. package/dist/engine/plan_builder.d.ts +9 -0
  17. package/dist/engine/plan_builder.d.ts.map +1 -0
  18. package/dist/engine/plan_builder.js +329 -0
  19. package/dist/esm/agent/plan.js +1 -360
  20. package/dist/esm/agent/types.js +3 -0
  21. package/dist/esm/api.js +27 -56
  22. package/dist/esm/cli.js +40 -173
  23. package/dist/esm/engine/impact_engine.js +191 -0
  24. package/dist/esm/engine/plan_builder.js +323 -0
  25. package/dist/esm/index.js +6 -3
  26. package/dist/esm/knowledge/route_families.js +57 -0
  27. package/dist/index.d.ts +9 -4
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +14 -5
  30. package/dist/knowledge/route_families.d.ts +19 -0
  31. package/dist/knowledge/route_families.d.ts.map +1 -1
  32. package/dist/knowledge/route_families.js +60 -0
  33. package/package.json +1 -1
  34. package/dist/agent/ai_flow_analysis.d.ts +0 -13
  35. package/dist/agent/ai_flow_analysis.d.ts.map +0 -1
  36. package/dist/agent/ai_flow_analysis.js +0 -334
  37. package/dist/agent/ai_mapping.d.ts +0 -14
  38. package/dist/agent/ai_mapping.d.ts.map +0 -1
  39. package/dist/agent/ai_mapping.js +0 -559
  40. package/dist/agent/analysis.d.ts +0 -64
  41. package/dist/agent/analysis.d.ts.map +0 -1
  42. package/dist/agent/analysis.js +0 -292
  43. package/dist/agent/blast_radius.d.ts +0 -4
  44. package/dist/agent/blast_radius.d.ts.map +0 -1
  45. package/dist/agent/blast_radius.js +0 -37
  46. package/dist/agent/dependency_graph.d.ts +0 -14
  47. package/dist/agent/dependency_graph.d.ts.map +0 -1
  48. package/dist/agent/dependency_graph.js +0 -227
  49. package/dist/agent/flags.d.ts +0 -23
  50. package/dist/agent/flags.d.ts.map +0 -1
  51. package/dist/agent/flags.js +0 -171
  52. package/dist/agent/flow_catalog.d.ts +0 -25
  53. package/dist/agent/flow_catalog.d.ts.map +0 -1
  54. package/dist/agent/flow_catalog.js +0 -115
  55. package/dist/agent/flow_mapping.d.ts +0 -10
  56. package/dist/agent/flow_mapping.d.ts.map +0 -1
  57. package/dist/agent/flow_mapping.js +0 -84
  58. package/dist/agent/framework.d.ts +0 -13
  59. package/dist/agent/framework.d.ts.map +0 -1
  60. package/dist/agent/framework.js +0 -149
  61. package/dist/agent/gap_suggestions.d.ts +0 -14
  62. package/dist/agent/gap_suggestions.d.ts.map +0 -1
  63. package/dist/agent/gap_suggestions.js +0 -101
  64. package/dist/agent/generator.d.ts +0 -10
  65. package/dist/agent/generator.d.ts.map +0 -1
  66. package/dist/agent/generator.js +0 -115
  67. package/dist/agent/operational_insights.d.ts +0 -41
  68. package/dist/agent/operational_insights.d.ts.map +0 -1
  69. package/dist/agent/operational_insights.js +0 -127
  70. package/dist/agent/report.d.ts +0 -97
  71. package/dist/agent/report.d.ts.map +0 -1
  72. package/dist/agent/report.js +0 -159
  73. package/dist/agent/runner.d.ts +0 -7
  74. package/dist/agent/runner.d.ts.map +0 -1
  75. package/dist/agent/runner.js +0 -898
  76. package/dist/agent/selectors.d.ts +0 -10
  77. package/dist/agent/selectors.d.ts.map +0 -1
  78. package/dist/agent/selectors.js +0 -75
  79. package/dist/agent/subsystem_risk.d.ts +0 -23
  80. package/dist/agent/subsystem_risk.d.ts.map +0 -1
  81. package/dist/agent/subsystem_risk.js +0 -207
  82. package/dist/agent/tests.d.ts +0 -19
  83. package/dist/agent/tests.d.ts.map +0 -1
  84. package/dist/agent/tests.js +0 -116
  85. package/dist/agent/traceability.d.ts +0 -22
  86. package/dist/agent/traceability.d.ts.map +0 -1
  87. package/dist/agent/traceability.js +0 -183
  88. package/dist/esm/agent/ai_flow_analysis.js +0 -331
  89. package/dist/esm/agent/ai_mapping.js +0 -556
  90. package/dist/esm/agent/analysis.js +0 -287
  91. package/dist/esm/agent/blast_radius.js +0 -34
  92. package/dist/esm/agent/dependency_graph.js +0 -224
  93. package/dist/esm/agent/flags.js +0 -160
  94. package/dist/esm/agent/flow_catalog.js +0 -112
  95. package/dist/esm/agent/flow_mapping.js +0 -81
  96. package/dist/esm/agent/framework.js +0 -145
  97. package/dist/esm/agent/gap_suggestions.js +0 -98
  98. package/dist/esm/agent/generator.js +0 -112
  99. package/dist/esm/agent/operational_insights.js +0 -124
  100. package/dist/esm/agent/report.js +0 -156
  101. package/dist/esm/agent/runner.js +0 -894
  102. package/dist/esm/agent/selectors.js +0 -71
  103. package/dist/esm/agent/subsystem_risk.js +0 -204
  104. package/dist/esm/agent/tests.js +0 -111
  105. package/dist/esm/agent/traceability.js +0 -180
@@ -1,10 +0,0 @@
1
- export interface DataTestIdSuggestion {
2
- file: string;
3
- line: number;
4
- tag: string;
5
- testId: string;
6
- snippet: string;
7
- }
8
- export declare function findDataTestIdSuggestions(relativePath: string, content: string | null, flowId: string): DataTestIdSuggestion[];
9
- export declare function applyDataTestIdSuggestions(content: string, suggestions: DataTestIdSuggestion[]): string;
10
- //# sourceMappingURL=selectors.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"selectors.d.ts","sourceRoot":"","sources":["../../src/agent/selectors.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,oBAAoB;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACnB;AAKD,wBAAgB,yBAAyB,CACrC,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,MAAM,GAAG,IAAI,EACtB,MAAM,EAAE,MAAM,GACf,oBAAoB,EAAE,CA8CxB;AAED,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,oBAAoB,EAAE,GAAG,MAAM,CA6BvG"}
@@ -1,75 +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.findDataTestIdSuggestions = findDataTestIdSuggestions;
6
- exports.applyDataTestIdSuggestions = applyDataTestIdSuggestions;
7
- const utils_js_1 = require("./utils.js");
8
- const INTERACTIVE_TAGS = ['button', 'input', 'select', 'textarea', 'form', 'a'];
9
- const INTERACTIVE_HINT = /(onClick|onSubmit|onChange|type=['"]submit['"]|role=['"]button['"])/;
10
- function findDataTestIdSuggestions(relativePath, content, flowId) {
11
- if (!content) {
12
- return [];
13
- }
14
- const suggestions = [];
15
- const lines = content.split('\n');
16
- let counter = 1;
17
- for (let i = 0; i < lines.length; i += 1) {
18
- const line = lines[i];
19
- const trimmed = line.trim();
20
- if (!trimmed.startsWith('<')) {
21
- continue;
22
- }
23
- if (trimmed.includes('data-testid')) {
24
- continue;
25
- }
26
- const tagMatch = trimmed.match(/^<([a-z][a-z0-9-]*)\b/);
27
- if (!tagMatch) {
28
- continue;
29
- }
30
- const tag = tagMatch[1];
31
- if (!INTERACTIVE_TAGS.includes(tag)) {
32
- continue;
33
- }
34
- if (!INTERACTIVE_HINT.test(trimmed)) {
35
- continue;
36
- }
37
- const testId = `${flowId}-${tag}-${counter}`;
38
- counter += 1;
39
- suggestions.push({
40
- file: (0, utils_js_1.normalizePath)(relativePath),
41
- line: i + 1,
42
- tag,
43
- testId,
44
- snippet: trimmed.slice(0, 200),
45
- });
46
- }
47
- return suggestions;
48
- }
49
- function applyDataTestIdSuggestions(content, suggestions) {
50
- if (suggestions.length === 0) {
51
- return content;
52
- }
53
- const lines = content.split('\n');
54
- const suggestionsByLine = new Map();
55
- for (const suggestion of suggestions) {
56
- const bucket = suggestionsByLine.get(suggestion.line) || [];
57
- bucket.push(suggestion);
58
- suggestionsByLine.set(suggestion.line, bucket);
59
- }
60
- for (const [lineNumber, lineSuggestions] of suggestionsByLine.entries()) {
61
- const index = lineNumber - 1;
62
- if (index < 0 || index >= lines.length) {
63
- continue;
64
- }
65
- let line = lines[index];
66
- for (const suggestion of lineSuggestions) {
67
- const pattern = new RegExp(`<${suggestion.tag}(\\s|>)`);
68
- if (pattern.test(line) && !line.includes('data-testid')) {
69
- line = line.replace(pattern, `<${suggestion.tag} data-testid="${suggestion.testId}"$1`);
70
- }
71
- }
72
- lines[index] = line;
73
- }
74
- return lines.join('\n');
75
- }
@@ -1,23 +0,0 @@
1
- import type { SubsystemRiskImpactConfig } from './config.js';
2
- export type RiskPriority = 'P0' | 'P1' | 'P2';
3
- export interface SubsystemRiskRuleMatch {
4
- ruleId: string;
5
- scoreDelta: number;
6
- priorityFloor?: RiskPriority;
7
- reasons: string[];
8
- keywords: string[];
9
- }
10
- export interface SubsystemRiskMapInfo {
11
- source: 'map';
12
- enabled: boolean;
13
- mapPath: string;
14
- mapFound: boolean;
15
- rulesLoaded: number;
16
- }
17
- export interface SubsystemRiskResolver {
18
- info: SubsystemRiskMapInfo;
19
- warnings: string[];
20
- matchFile: (relativePath: string) => SubsystemRiskRuleMatch[];
21
- }
22
- export declare function loadSubsystemRiskResolver(config: SubsystemRiskImpactConfig): SubsystemRiskResolver;
23
- //# sourceMappingURL=subsystem_risk.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"subsystem_risk.d.ts","sourceRoot":"","sources":["../../src/agent/subsystem_risk.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,yBAAyB,EAAC,MAAM,aAAa,CAAC;AAG3D,MAAM,MAAM,YAAY,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AA2B9C,MAAM,WAAW,sBAAsB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,YAAY,CAAC;IAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACjC,MAAM,EAAE,KAAK,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,qBAAqB;IAClC,IAAI,EAAE,oBAAoB,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,sBAAsB,EAAE,CAAC;CACjE;AAyGD,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,yBAAyB,GAAG,qBAAqB,CAuHlG"}
@@ -1,207 +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.loadSubsystemRiskResolver = loadSubsystemRiskResolver;
6
- const fs_1 = require("fs");
7
- const utils_js_1 = require("./utils.js");
8
- const PRIORITY_RANK = {
9
- P0: 0,
10
- P1: 1,
11
- P2: 2,
12
- };
13
- function coerceNumber(value) {
14
- if (typeof value === 'number' && Number.isFinite(value)) {
15
- return value;
16
- }
17
- if (typeof value === 'string') {
18
- const parsed = Number(value);
19
- if (Number.isFinite(parsed)) {
20
- return parsed;
21
- }
22
- }
23
- return undefined;
24
- }
25
- function normalizePriority(value) {
26
- if (value === 'P0' || value === 'P1' || value === 'P2') {
27
- return value;
28
- }
29
- return undefined;
30
- }
31
- function parseStringArray(value) {
32
- if (!Array.isArray(value)) {
33
- return [];
34
- }
35
- return value
36
- .filter((item) => typeof item === 'string')
37
- .map((item) => item.trim())
38
- .filter(Boolean);
39
- }
40
- function parsePathArray(value) {
41
- return parseStringArray(value).map((item) => (0, utils_js_1.normalizePath)(item));
42
- }
43
- function parseKeywords(value) {
44
- if (!Array.isArray(value)) {
45
- return [];
46
- }
47
- return Array.from(new Set(value
48
- .filter((item) => typeof item === 'string')
49
- .map((item) => item.trim().toLowerCase())
50
- .filter(Boolean)));
51
- }
52
- function parseRules(rawRules, warnings) {
53
- if (!Array.isArray(rawRules)) {
54
- warnings.push('Subsystem risk map has no "rules" array.');
55
- return [];
56
- }
57
- const parsed = [];
58
- for (let i = 0; i < rawRules.length; i += 1) {
59
- const rawRule = rawRules[i];
60
- if (!rawRule || typeof rawRule !== 'object') {
61
- continue;
62
- }
63
- const patterns = parsePathArray(rawRule.patterns);
64
- if (patterns.length === 0) {
65
- warnings.push(`Subsystem risk rule at index ${i} has no valid patterns and was skipped.`);
66
- continue;
67
- }
68
- const id = typeof rawRule.id === 'string' && rawRule.id.trim()
69
- ? rawRule.id.trim()
70
- : `rule-${i + 1}`;
71
- const description = typeof rawRule.description === 'string' ? rawRule.description.trim() : undefined;
72
- const reasons = parseStringArray(rawRule.reasons);
73
- const keywords = parseKeywords(rawRule.keywords);
74
- const scoreDelta = coerceNumber(rawRule.scoreDelta) ?? 0;
75
- const priorityFloor = normalizePriority(rawRule.priorityFloor);
76
- const normalizedReasons = reasons.length > 0
77
- ? reasons
78
- : (description ? [description] : []);
79
- parsed.push({
80
- id,
81
- description,
82
- patterns,
83
- scoreDelta,
84
- priorityFloor,
85
- reasons: normalizedReasons,
86
- keywords,
87
- });
88
- }
89
- return parsed;
90
- }
91
- function comparePriority(a, b) {
92
- return PRIORITY_RANK[a] <= PRIORITY_RANK[b] ? a : b;
93
- }
94
- function loadSubsystemRiskResolver(config) {
95
- const mapPath = (0, utils_js_1.normalizePath)(config.mapPath);
96
- const warnings = [];
97
- if (!config.enabled) {
98
- return {
99
- info: {
100
- source: 'map',
101
- enabled: false,
102
- mapPath,
103
- mapFound: false,
104
- rulesLoaded: 0,
105
- },
106
- warnings,
107
- matchFile: () => [],
108
- };
109
- }
110
- if (!(0, fs_1.existsSync)(config.mapPath)) {
111
- warnings.push(`Subsystem risk map file not found: ${config.mapPath}`);
112
- return {
113
- info: {
114
- source: 'map',
115
- enabled: true,
116
- mapPath,
117
- mapFound: false,
118
- rulesLoaded: 0,
119
- },
120
- warnings,
121
- matchFile: () => [],
122
- };
123
- }
124
- let raw;
125
- try {
126
- raw = JSON.parse((0, fs_1.readFileSync)(config.mapPath, 'utf-8'));
127
- }
128
- catch {
129
- warnings.push(`Subsystem risk map is invalid JSON: ${config.mapPath}`);
130
- return {
131
- info: {
132
- source: 'map',
133
- enabled: true,
134
- mapPath,
135
- mapFound: true,
136
- rulesLoaded: 0,
137
- },
138
- warnings,
139
- matchFile: () => [],
140
- };
141
- }
142
- const rules = parseRules(raw.rules, warnings);
143
- if (rules.length === 0) {
144
- warnings.push(`Subsystem risk map loaded but no valid rules were found: ${config.mapPath}`);
145
- }
146
- const maxRules = Math.max(1, Math.round(config.maxRulesPerFile));
147
- return {
148
- info: {
149
- source: 'map',
150
- enabled: true,
151
- mapPath,
152
- mapFound: true,
153
- rulesLoaded: rules.length,
154
- },
155
- warnings,
156
- matchFile: (relativePath) => {
157
- const normalizedPath = (0, utils_js_1.normalizePath)(relativePath);
158
- const matches = [];
159
- for (const rule of rules) {
160
- const matched = rule.patterns.some((pattern) => (0, utils_js_1.matchGlob)(normalizedPath, pattern));
161
- if (!matched) {
162
- continue;
163
- }
164
- const reasons = rule.reasons.length > 0
165
- ? rule.reasons
166
- : [`Subsystem risk rule matched: ${rule.id}`];
167
- matches.push({
168
- ruleId: rule.id,
169
- scoreDelta: rule.scoreDelta,
170
- priorityFloor: rule.priorityFloor,
171
- reasons,
172
- keywords: rule.keywords,
173
- priorityRank: rule.priorityFloor ? PRIORITY_RANK[rule.priorityFloor] : 99,
174
- });
175
- }
176
- if (matches.length <= maxRules) {
177
- return matches.map(({ priorityRank, ...rest }) => rest);
178
- }
179
- matches.sort((a, b) => {
180
- const deltaDiff = Math.abs(b.scoreDelta) - Math.abs(a.scoreDelta);
181
- if (deltaDiff !== 0) {
182
- return deltaDiff;
183
- }
184
- const priorityDiff = a.priorityRank - b.priorityRank;
185
- if (priorityDiff !== 0) {
186
- return priorityDiff;
187
- }
188
- return a.ruleId.localeCompare(b.ruleId);
189
- });
190
- const capped = matches.slice(0, maxRules).map(({ priorityRank, ...rest }) => rest);
191
- let floor;
192
- for (const match of capped) {
193
- if (match.priorityFloor) {
194
- floor = floor ? comparePriority(floor, match.priorityFloor) : match.priorityFloor;
195
- }
196
- }
197
- if (floor) {
198
- for (const match of capped) {
199
- if (!match.priorityFloor) {
200
- match.priorityFloor = floor;
201
- }
202
- }
203
- }
204
- return capped;
205
- },
206
- };
207
- }
@@ -1,19 +0,0 @@
1
- import type { FlowImpact } from './analysis.js';
2
- export interface TestFile {
3
- path: string;
4
- content: string | null;
5
- }
6
- export interface FlowCoverage {
7
- flowId: string;
8
- flowName: string;
9
- priority: string;
10
- coveredBy: string[];
11
- score: number;
12
- expectedTests?: string[];
13
- source?: 'catalog' | 'traceability' | 'heuristic' | 'ai';
14
- missingScenarios?: string[];
15
- }
16
- export declare function discoverTests(appRoot: string, patterns: string[]): TestFile[];
17
- export declare function mapTestsToFlows(flows: FlowImpact[], tests: TestFile[]): FlowCoverage[];
18
- export declare function mapCatalogTestsToFlows(flows: FlowImpact[], testsRoot: string, testsByFlow: Map<string, string[]>): FlowCoverage[];
19
- //# sourceMappingURL=tests.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tests.d.ts","sourceRoot":"","sources":["../../src/agent/tests.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAG9C,MAAM,WAAW,QAAQ;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,YAAY;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,MAAM,CAAC,EAAE,SAAS,GAAG,cAAc,GAAG,WAAW,GAAG,IAAI,CAAC;IACzD,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAkB7E;AAUD,wBAAgB,eAAe,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,CAgCtF;AAuBD,wBAAgB,sBAAsB,CAClC,KAAK,EAAE,UAAU,EAAE,EACnB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GACnC,YAAY,EAAE,CA8BhB"}
@@ -1,116 +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.discoverTests = discoverTests;
6
- exports.mapTestsToFlows = mapTestsToFlows;
7
- exports.mapCatalogTestsToFlows = mapCatalogTestsToFlows;
8
- const fs_1 = require("fs");
9
- const glob_1 = require("glob");
10
- const path_1 = require("path");
11
- const utils_js_1 = require("./utils.js");
12
- function discoverTests(appRoot, patterns) {
13
- const files = new Set();
14
- for (const pattern of patterns) {
15
- const matches = (0, glob_1.globSync)(pattern, {
16
- cwd: appRoot,
17
- ignore: ['**/node_modules/**', '**/.git/**'],
18
- nodir: true,
19
- });
20
- for (const match of matches) {
21
- files.add((0, utils_js_1.normalizePath)(match));
22
- }
23
- }
24
- return Array.from(files).map((relativePath) => {
25
- const fullPath = (0, path_1.join)(appRoot, relativePath);
26
- const content = (0, utils_js_1.safeReadTextFile)(fullPath);
27
- return { path: relativePath, content };
28
- });
29
- }
30
- function buildFlowKeywords(flow) {
31
- const tokens = [];
32
- tokens.push(...(0, utils_js_1.tokenize)(flow.id));
33
- tokens.push(...(0, utils_js_1.tokenize)(flow.name));
34
- tokens.push(...flow.keywords);
35
- return (0, utils_js_1.uniqueTokens)(tokens);
36
- }
37
- function mapTestsToFlows(flows, tests) {
38
- const coverage = [];
39
- for (const flow of flows) {
40
- const keywords = buildFlowKeywords(flow);
41
- const matched = [];
42
- let score = 0;
43
- for (const test of tests) {
44
- const haystack = `${test.path} ${test.content || ''}`.toLowerCase();
45
- let localScore = 0;
46
- for (const keyword of keywords) {
47
- if (keyword && haystack.includes(keyword.toLowerCase())) {
48
- localScore += 1;
49
- }
50
- }
51
- if (localScore > 0) {
52
- matched.push(test.path);
53
- score += localScore;
54
- }
55
- }
56
- coverage.push({
57
- flowId: flow.id,
58
- flowName: flow.name,
59
- priority: flow.priority,
60
- coveredBy: matched,
61
- score,
62
- source: 'heuristic',
63
- });
64
- }
65
- return coverage;
66
- }
67
- function resolveExpectedTests(testsRoot, expectedTests) {
68
- const resolved = [];
69
- for (const entry of expectedTests) {
70
- if (!entry) {
71
- continue;
72
- }
73
- const normalized = (0, utils_js_1.normalizePath)(entry).replace(/^\.\//, '');
74
- if (normalized.startsWith('e2e-tests/playwright/')) {
75
- resolved.push(normalized.slice('e2e-tests/playwright/'.length));
76
- continue;
77
- }
78
- const specsIndex = normalized.indexOf('specs/');
79
- if (specsIndex >= 0) {
80
- resolved.push(normalized.slice(specsIndex));
81
- continue;
82
- }
83
- resolved.push(normalized);
84
- }
85
- return Array.from(new Set(resolved));
86
- }
87
- function mapCatalogTestsToFlows(flows, testsRoot, testsByFlow) {
88
- return flows.map((flow) => {
89
- const expectedTests = resolveExpectedTests(testsRoot, testsByFlow.get(flow.id) || []);
90
- const coveredBy = [];
91
- for (const expected of expectedTests) {
92
- const isAbsolute = expected.startsWith('/');
93
- const globTarget = isAbsolute ? expected : expected;
94
- if (expected.includes('*') || expected.includes('?') || expected.includes('{')) {
95
- const matches = (0, glob_1.globSync)(globTarget, { cwd: isAbsolute ? undefined : testsRoot, nodir: true });
96
- if (matches.length > 0) {
97
- coveredBy.push(...matches.map((match) => (0, utils_js_1.normalizePath)(match)));
98
- }
99
- continue;
100
- }
101
- const fullPath = isAbsolute ? expected : (0, path_1.join)(testsRoot, expected);
102
- if ((0, fs_1.existsSync)(fullPath)) {
103
- coveredBy.push(isAbsolute ? (0, utils_js_1.normalizePath)(expected) : expected);
104
- }
105
- }
106
- return {
107
- flowId: flow.id,
108
- flowName: flow.name,
109
- priority: flow.priority,
110
- coveredBy: Array.from(new Set(coveredBy)),
111
- score: coveredBy.length,
112
- expectedTests,
113
- source: 'catalog',
114
- };
115
- });
116
- }
@@ -1,22 +0,0 @@
1
- import type { FlowImpact } from './analysis.js';
2
- import type { TraceabilityImpactConfig } from './config.js';
3
- import type { FlowCoverage } from './tests.js';
4
- export interface TraceabilityStats {
5
- source: 'manifest';
6
- enabled: boolean;
7
- manifestPath: string;
8
- manifestFound: boolean;
9
- manifestTests: number;
10
- manifestEdges: number;
11
- matchedFlows: number;
12
- totalFlows: number;
13
- matchedTests: number;
14
- coverageRatio: number;
15
- }
16
- export interface TraceabilityMappingResult {
17
- coverage: FlowCoverage[];
18
- stats: TraceabilityStats;
19
- warnings: string[];
20
- }
21
- export declare function mapTraceabilityToFlows(appRoot: string, config: TraceabilityImpactConfig, flows: FlowImpact[]): TraceabilityMappingResult;
22
- //# sourceMappingURL=traceability.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"traceability.d.ts","sourceRoot":"","sources":["../../src/agent/traceability.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAC9C,OAAO,KAAK,EAAC,wBAAwB,EAAC,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,YAAY,CAAC;AAoB7C,MAAM,WAAW,iBAAiB;IAC9B,MAAM,EAAE,UAAU,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,OAAO,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,yBAAyB;IACtC,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,KAAK,EAAE,iBAAiB,CAAC;IACzB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACtB;AAsFD,wBAAgB,sBAAsB,CAClC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,wBAAwB,EAChC,KAAK,EAAE,UAAU,EAAE,GACpB,yBAAyB,CAiH3B"}
@@ -1,183 +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.mapTraceabilityToFlows = mapTraceabilityToFlows;
6
- const fs_1 = require("fs");
7
- const path_1 = require("path");
8
- const utils_js_1 = require("./utils.js");
9
- function ratio(numerator, denominator) {
10
- if (denominator <= 0) {
11
- return 0;
12
- }
13
- return Number((numerator / denominator).toFixed(4));
14
- }
15
- function resolveManifestPath(root, configuredPath) {
16
- if ((0, path_1.isAbsolute)(configuredPath)) {
17
- return configuredPath;
18
- }
19
- return (0, path_1.join)(root, configuredPath);
20
- }
21
- function safeReadManifest(path) {
22
- if (!(0, fs_1.existsSync)(path)) {
23
- return null;
24
- }
25
- try {
26
- return JSON.parse((0, fs_1.readFileSync)(path, 'utf-8'));
27
- }
28
- catch {
29
- return null;
30
- }
31
- }
32
- function normalizeFiles(files) {
33
- if (!files) {
34
- return [];
35
- }
36
- return files.map((file) => (0, utils_js_1.normalizePath)(file)).filter(Boolean);
37
- }
38
- function normalizeTests(tests) {
39
- if (!tests) {
40
- return [];
41
- }
42
- return tests.map((test) => (0, utils_js_1.normalizePath)(test)).filter(Boolean);
43
- }
44
- function buildFileToTestsMap(manifest) {
45
- const fileToTests = new Map();
46
- const setMapping = (file, test) => {
47
- if (!file || !test) {
48
- return;
49
- }
50
- const key = (0, utils_js_1.normalizePath)(file);
51
- const value = (0, utils_js_1.normalizePath)(test);
52
- if (!fileToTests.has(key)) {
53
- fileToTests.set(key, new Set());
54
- }
55
- fileToTests.get(key)?.add(value);
56
- };
57
- if (manifest.tests) {
58
- for (const entry of manifest.tests) {
59
- const files = normalizeFiles(entry.touchedFiles);
60
- for (const file of files) {
61
- setMapping(file, entry.test);
62
- }
63
- }
64
- }
65
- if (manifest.fileToTests) {
66
- for (const [file, tests] of Object.entries(manifest.fileToTests)) {
67
- const normalizedTests = normalizeTests(tests);
68
- for (const test of normalizedTests) {
69
- setMapping(file, test);
70
- }
71
- }
72
- }
73
- if (manifest.mappings) {
74
- for (const entry of manifest.mappings) {
75
- const normalizedTests = normalizeTests(entry.tests);
76
- for (const test of normalizedTests) {
77
- setMapping(entry.file, test);
78
- }
79
- }
80
- }
81
- return fileToTests;
82
- }
83
- function mapTraceabilityToFlows(appRoot, config, flows) {
84
- const manifestPath = resolveManifestPath(appRoot, config.manifestPath);
85
- const warnings = [];
86
- const fallbackStats = {
87
- source: 'manifest',
88
- enabled: config.enabled,
89
- manifestPath,
90
- manifestFound: false,
91
- manifestTests: 0,
92
- manifestEdges: 0,
93
- matchedFlows: 0,
94
- totalFlows: flows.length,
95
- matchedTests: 0,
96
- coverageRatio: 0,
97
- };
98
- if (!config.enabled) {
99
- return {
100
- coverage: [],
101
- warnings,
102
- stats: fallbackStats,
103
- };
104
- }
105
- const manifest = safeReadManifest(manifestPath);
106
- if (!manifest) {
107
- warnings.push(`Traceability manifest not found or invalid: ${manifestPath}`);
108
- return {
109
- coverage: [],
110
- warnings,
111
- stats: fallbackStats,
112
- };
113
- }
114
- const fileToTests = buildFileToTestsMap(manifest);
115
- let manifestEdges = 0;
116
- for (const tests of fileToTests.values()) {
117
- manifestEdges += tests.size;
118
- }
119
- const manifestTests = new Set();
120
- const coverage = [];
121
- const matchedTests = new Set();
122
- let matchedFlows = 0;
123
- for (const tests of fileToTests.values()) {
124
- for (const test of tests) {
125
- manifestTests.add(test);
126
- }
127
- }
128
- for (const flow of flows) {
129
- const signalCounts = new Map();
130
- const files = flow.files.map((file) => (0, utils_js_1.normalizePath)(file));
131
- for (const file of files) {
132
- const tests = fileToTests.get(file);
133
- if (!tests) {
134
- continue;
135
- }
136
- for (const test of tests) {
137
- signalCounts.set(test, (signalCounts.get(test) || 0) + 1);
138
- }
139
- }
140
- const coveredBy = Array.from(signalCounts.entries())
141
- .filter(([, count]) => count >= Math.max(1, config.minSignalsPerTest))
142
- .map(([test]) => test)
143
- .sort();
144
- const score = Array.from(signalCounts.values()).reduce((acc, value) => acc + value, 0);
145
- if (coveredBy.length > 0) {
146
- matchedFlows += 1;
147
- for (const test of coveredBy) {
148
- matchedTests.add(test);
149
- }
150
- }
151
- coverage.push({
152
- flowId: flow.id,
153
- flowName: flow.name,
154
- priority: flow.priority,
155
- coveredBy,
156
- score,
157
- source: 'traceability',
158
- });
159
- }
160
- const stats = {
161
- source: 'manifest',
162
- enabled: config.enabled,
163
- manifestPath,
164
- manifestFound: true,
165
- manifestTests: manifestTests.size,
166
- manifestEdges,
167
- matchedFlows,
168
- totalFlows: flows.length,
169
- matchedTests: matchedTests.size,
170
- coverageRatio: ratio(matchedFlows, flows.length),
171
- };
172
- if (manifestEdges === 0) {
173
- warnings.push(`Traceability manifest has no file-to-test mappings: ${manifestPath}`);
174
- }
175
- else if (stats.coverageRatio < 0.4) {
176
- warnings.push(`Traceability coverage is low (${stats.matchedFlows}/${stats.totalFlows} flows mapped). Recommendations may require heuristic fallback.`);
177
- }
178
- return {
179
- coverage,
180
- warnings,
181
- stats,
182
- };
183
- }