@yasserkhanorg/e2e-agents 1.8.3 → 1.8.4
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan_crew.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/plan_crew.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAC,gBAAgB,EAAE,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAUtE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAgC5C,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAkD5H;
|
|
1
|
+
{"version":3,"file":"plan_crew.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/plan_crew.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAC,gBAAgB,EAAE,UAAU,EAAC,MAAM,qBAAqB,CAAC;AAUtE,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,aAAa,CAAC;AAgC5C,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAkD5H;AAWD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CAwEnF;AAED,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CAE3G;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG,MAAM,CA0InF;AAED,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,CAAC,EAAE,UAAU,GAAG;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAA;CAAC,CAa/K"}
|
|
@@ -85,25 +85,37 @@ async function runPlanCrewAnalysis(plan, config, args) {
|
|
|
85
85
|
timings: result.timings,
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* Match a strategy/design entry against a set of gap family IDs.
|
|
90
|
+
*/
|
|
91
|
+
function matchesGapFamily(flowId, flowName, gapFamilies) {
|
|
92
|
+
return Array.from(gapFamilies).some((fam) => flowId.startsWith(fam) || flowName.toLowerCase().includes(fam.replace(/_/g, ' ')));
|
|
93
|
+
}
|
|
88
94
|
function buildCrewMarkdown(crew, plan) {
|
|
89
95
|
const totalCases = crew.testDesigns.reduce((n, td) => n + td.testCases.length, 0);
|
|
90
96
|
const gapFamilies = new Set((plan?.gapDetails ?? []).map((g) => g.id));
|
|
91
|
-
const gapDesigns = gapFamilies.size > 0
|
|
92
|
-
? crew.testDesigns.filter((td) => Array.from(gapFamilies).some((fam) => td.flowId.startsWith(fam) || td.flowName.toLowerCase().includes(fam.replace(/_/g, ' '))))
|
|
93
|
-
: [];
|
|
94
|
-
const gapCases = gapDesigns.reduce((n, td) => n + td.testCases.length, 0);
|
|
95
|
-
const gapP0Cases = gapDesigns.reduce((n, td) => n + td.testCases.filter((tc) => tc.priority === 'P0').length, 0);
|
|
96
97
|
const lines = [
|
|
97
98
|
'### Crew Insights',
|
|
98
99
|
'',
|
|
99
100
|
`Workflow: \`${crew.workflow}\``,
|
|
100
101
|
`Impacted flows: **${crew.summary.impactedFlows}**`,
|
|
101
|
-
`
|
|
102
|
+
`Strategy entries: **${crew.summary.strategyEntries}**`,
|
|
102
103
|
];
|
|
103
|
-
if (
|
|
104
|
-
|
|
104
|
+
if (totalCases > 0) {
|
|
105
|
+
const gapDesigns = gapFamilies.size > 0
|
|
106
|
+
? crew.testDesigns.filter((td) => matchesGapFamily(td.flowId, td.flowName, gapFamilies))
|
|
107
|
+
: [];
|
|
108
|
+
const gapCases = gapDesigns.reduce((n, td) => n + td.testCases.length, 0);
|
|
109
|
+
const gapP0Cases = gapDesigns.reduce((n, td) => n + td.testCases.filter((tc) => tc.priority === 'P0').length, 0);
|
|
110
|
+
lines.push(`Structured test designs: **${crew.summary.testDesigns}** flows, **${totalCases}** test cases`);
|
|
111
|
+
if (gapDesigns.length > 0) {
|
|
112
|
+
lines.push(`Gap-focused: **${gapDesigns.length}** flows, **${gapCases}** test cases (**${gapP0Cases}** P0)`);
|
|
113
|
+
}
|
|
105
114
|
}
|
|
106
|
-
|
|
115
|
+
if (crew.summary.crossImpacts > 0) {
|
|
116
|
+
lines.push(`Cross-impacts: **${crew.summary.crossImpacts}** (${crew.summary.highRiskCrossImpacts} high risk)`);
|
|
117
|
+
}
|
|
118
|
+
lines.push(`Estimated AI cost: **$${crew.summary.totalCostUSD.toFixed(4)}**`);
|
|
107
119
|
if (crew.strategyEntries.length > 0) {
|
|
108
120
|
lines.push('');
|
|
109
121
|
lines.push('Top Strategy Recommendations:');
|
|
@@ -151,14 +163,18 @@ function appendCrewToSummary(baseMarkdown, crew, plan) {
|
|
|
151
163
|
*/
|
|
152
164
|
function buildCrewTestPlan(crew, plan) {
|
|
153
165
|
const gapFamilies = new Set((plan?.gapDetails ?? []).map((g) => g.id));
|
|
166
|
+
const hasTestDesigns = crew.testDesigns.length > 0;
|
|
154
167
|
const totalCases = crew.testDesigns.reduce((n, td) => n + td.testCases.length, 0);
|
|
155
|
-
// Split
|
|
168
|
+
// Split strategy entries into gap-related and covered
|
|
169
|
+
const gapStrategies = gapFamilies.size > 0
|
|
170
|
+
? crew.strategyEntries.filter((s) => matchesGapFamily(s.flowId, s.flowName, gapFamilies))
|
|
171
|
+
: [];
|
|
172
|
+
const coveredStrategies = crew.strategyEntries.filter((s) => !gapStrategies.includes(s));
|
|
173
|
+
// Split test designs (if present) into gap-related and covered
|
|
156
174
|
const gapDesigns = [];
|
|
157
175
|
const coveredDesigns = [];
|
|
158
176
|
for (const td of crew.testDesigns) {
|
|
159
|
-
|
|
160
|
-
const isGap = Array.from(gapFamilies).some((fam) => td.flowId.startsWith(fam) || td.flowName.toLowerCase().includes(fam.replace(/_/g, ' ')));
|
|
161
|
-
if (isGap) {
|
|
177
|
+
if (matchesGapFamily(td.flowId, td.flowName, gapFamilies)) {
|
|
162
178
|
gapDesigns.push(td);
|
|
163
179
|
}
|
|
164
180
|
else {
|
|
@@ -176,77 +192,92 @@ function buildCrewTestPlan(crew, plan) {
|
|
|
176
192
|
'',
|
|
177
193
|
`| Metric | Count |`,
|
|
178
194
|
`|--------|-------|`,
|
|
179
|
-
`| Gap flows (missing tests) | ${
|
|
180
|
-
`| Covered flows (expansion) | ${
|
|
181
|
-
`| Total | ${crew.
|
|
195
|
+
`| Gap flows (missing tests) | ${gapStrategies.length} flows${hasTestDesigns ? `, **${gapCases} test cases**` : ''} |`,
|
|
196
|
+
`| Covered flows (expansion) | ${coveredStrategies.length} flows${hasTestDesigns ? `, ${coveredCases} test cases` : ''} |`,
|
|
197
|
+
`| Total strategy entries | ${crew.strategyEntries.length} flows${hasTestDesigns ? `, ${totalCases} test cases` : ''} |`,
|
|
182
198
|
`| High-risk cross-impacts | ${crew.summary.highRiskCrossImpacts} |`,
|
|
183
199
|
`| AI cost | $${crew.summary.totalCostUSD.toFixed(4)} |`,
|
|
184
200
|
'',
|
|
185
201
|
];
|
|
186
|
-
//
|
|
187
|
-
if (
|
|
202
|
+
// ── Gap flows ──
|
|
203
|
+
if (gapStrategies.length > 0) {
|
|
188
204
|
lines.push('## Priority: Gap Flows (Missing Tests)');
|
|
189
205
|
lines.push('');
|
|
190
206
|
lines.push('These flows have **no existing E2E coverage** and should be addressed first.');
|
|
191
207
|
lines.push('');
|
|
192
|
-
for (const
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
const risk = strategy?.crossImpactRisk ?? 'unknown';
|
|
196
|
-
const p0Cases = td.testCases.filter((tc) => tc.priority === 'P0');
|
|
197
|
-
const p1Cases = td.testCases.filter((tc) => tc.priority === 'P1');
|
|
198
|
-
lines.push(`### ${td.flowName}`);
|
|
208
|
+
for (const strategy of gapStrategies) {
|
|
209
|
+
const td = crew.testDesigns.find((d) => d.flowId === strategy.flowId);
|
|
210
|
+
lines.push(`### ${strategy.flowName}`);
|
|
199
211
|
lines.push('');
|
|
200
|
-
lines.push(`Strategy: **${approach}** |
|
|
212
|
+
lines.push(`Strategy: **${strategy.approach}** | Priority: **${strategy.priority}** | Cross-impact risk: **${strategy.crossImpactRisk}**`);
|
|
213
|
+
if (strategy.rationale) {
|
|
214
|
+
lines.push(`> ${strategy.rationale}`);
|
|
215
|
+
}
|
|
216
|
+
if (strategy.testCategories.length > 0) {
|
|
217
|
+
lines.push(`Test categories: ${strategy.testCategories.join(', ')}`);
|
|
218
|
+
}
|
|
201
219
|
lines.push('');
|
|
202
|
-
//
|
|
203
|
-
if (
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
lines.push(
|
|
208
|
-
|
|
209
|
-
|
|
220
|
+
// If test designs exist, show P0/P1 cases
|
|
221
|
+
if (td && td.testCases.length > 0) {
|
|
222
|
+
const p0Cases = td.testCases.filter((tc) => tc.priority === 'P0');
|
|
223
|
+
const p1Cases = td.testCases.filter((tc) => tc.priority === 'P1');
|
|
224
|
+
if (p0Cases.length > 0) {
|
|
225
|
+
lines.push('**P0 — Must test:**');
|
|
226
|
+
lines.push('');
|
|
227
|
+
for (const tc of p0Cases) {
|
|
228
|
+
lines.push(`- [ ] **${tc.name}** (${tc.type})`);
|
|
229
|
+
if (tc.preconditions.length > 0) {
|
|
230
|
+
lines.push(` - Preconditions: ${tc.preconditions.join('; ')}`);
|
|
231
|
+
}
|
|
232
|
+
lines.push(` - Steps: ${tc.steps.join(' → ')}`);
|
|
233
|
+
lines.push(` - Expected: ${tc.expectedOutcome}`);
|
|
210
234
|
}
|
|
211
|
-
lines.push(
|
|
212
|
-
lines.push(` - Expected: ${tc.expectedOutcome}`);
|
|
235
|
+
lines.push('');
|
|
213
236
|
}
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
lines.push(
|
|
237
|
+
if (p1Cases.length > 0) {
|
|
238
|
+
lines.push(`<details><summary>P1 — Should test (${p1Cases.length})</summary>`);
|
|
239
|
+
lines.push('');
|
|
240
|
+
for (const tc of p1Cases) {
|
|
241
|
+
lines.push(`- [ ] **${tc.name}** (${tc.type}) — ${tc.expectedOutcome}`);
|
|
242
|
+
}
|
|
243
|
+
lines.push('');
|
|
244
|
+
lines.push('</details>');
|
|
245
|
+
lines.push('');
|
|
222
246
|
}
|
|
223
|
-
lines.push('');
|
|
224
|
-
lines.push('</details>');
|
|
225
|
-
lines.push('');
|
|
226
247
|
}
|
|
227
248
|
}
|
|
228
249
|
}
|
|
229
|
-
// Covered
|
|
230
|
-
if (
|
|
250
|
+
// ── Covered flows ──
|
|
251
|
+
if (coveredStrategies.length > 0) {
|
|
231
252
|
lines.push('## Covered Flows (Regression / Expansion)');
|
|
232
253
|
lines.push('');
|
|
233
|
-
lines.push('These flows already have specs.
|
|
254
|
+
lines.push('These flows already have specs. Verify changes haven\'t introduced regressions.');
|
|
234
255
|
lines.push('');
|
|
235
|
-
for (const
|
|
236
|
-
const
|
|
237
|
-
const
|
|
238
|
-
const
|
|
239
|
-
lines.push(`<details><summary><strong>${
|
|
256
|
+
for (const strategy of coveredStrategies) {
|
|
257
|
+
const td = crew.testDesigns.find((d) => d.flowId === strategy.flowId);
|
|
258
|
+
const caseCount = td ? td.testCases.length : 0;
|
|
259
|
+
const detail = caseCount > 0 ? ` | ${caseCount} cases` : '';
|
|
260
|
+
lines.push(`<details><summary><strong>${strategy.flowName}</strong> — ${strategy.approach}${detail} (${strategy.priority})</summary>`);
|
|
240
261
|
lines.push('');
|
|
241
|
-
|
|
242
|
-
|
|
262
|
+
lines.push(`Cross-impact risk: ${strategy.crossImpactRisk}`);
|
|
263
|
+
if (strategy.rationale) {
|
|
264
|
+
lines.push(`> ${strategy.rationale}`);
|
|
265
|
+
}
|
|
266
|
+
if (strategy.testCategories.length > 0) {
|
|
267
|
+
lines.push(`Test categories: ${strategy.testCategories.join(', ')}`);
|
|
268
|
+
}
|
|
269
|
+
if (td && td.testCases.length > 0) {
|
|
270
|
+
lines.push('');
|
|
271
|
+
for (const tc of td.testCases) {
|
|
272
|
+
lines.push(`- [ ] **${tc.name}** (${tc.priority}, ${tc.type}) — ${tc.expectedOutcome}`);
|
|
273
|
+
}
|
|
243
274
|
}
|
|
244
275
|
lines.push('');
|
|
245
276
|
lines.push('</details>');
|
|
246
277
|
lines.push('');
|
|
247
278
|
}
|
|
248
279
|
}
|
|
249
|
-
// Cross-impacts
|
|
280
|
+
// ── Cross-impacts ──
|
|
250
281
|
const highRisk = crew.crossImpacts.filter((ci) => ci.riskLevel === 'high');
|
|
251
282
|
if (highRisk.length > 0) {
|
|
252
283
|
lines.push('## High-Risk Cross-Impacts');
|
|
@@ -78,25 +78,37 @@ export async function runPlanCrewAnalysis(plan, config, args) {
|
|
|
78
78
|
timings: result.timings,
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Match a strategy/design entry against a set of gap family IDs.
|
|
83
|
+
*/
|
|
84
|
+
function matchesGapFamily(flowId, flowName, gapFamilies) {
|
|
85
|
+
return Array.from(gapFamilies).some((fam) => flowId.startsWith(fam) || flowName.toLowerCase().includes(fam.replace(/_/g, ' ')));
|
|
86
|
+
}
|
|
81
87
|
export function buildCrewMarkdown(crew, plan) {
|
|
82
88
|
const totalCases = crew.testDesigns.reduce((n, td) => n + td.testCases.length, 0);
|
|
83
89
|
const gapFamilies = new Set((plan?.gapDetails ?? []).map((g) => g.id));
|
|
84
|
-
const gapDesigns = gapFamilies.size > 0
|
|
85
|
-
? crew.testDesigns.filter((td) => Array.from(gapFamilies).some((fam) => td.flowId.startsWith(fam) || td.flowName.toLowerCase().includes(fam.replace(/_/g, ' '))))
|
|
86
|
-
: [];
|
|
87
|
-
const gapCases = gapDesigns.reduce((n, td) => n + td.testCases.length, 0);
|
|
88
|
-
const gapP0Cases = gapDesigns.reduce((n, td) => n + td.testCases.filter((tc) => tc.priority === 'P0').length, 0);
|
|
89
90
|
const lines = [
|
|
90
91
|
'### Crew Insights',
|
|
91
92
|
'',
|
|
92
93
|
`Workflow: \`${crew.workflow}\``,
|
|
93
94
|
`Impacted flows: **${crew.summary.impactedFlows}**`,
|
|
94
|
-
`
|
|
95
|
+
`Strategy entries: **${crew.summary.strategyEntries}**`,
|
|
95
96
|
];
|
|
96
|
-
if (
|
|
97
|
-
|
|
97
|
+
if (totalCases > 0) {
|
|
98
|
+
const gapDesigns = gapFamilies.size > 0
|
|
99
|
+
? crew.testDesigns.filter((td) => matchesGapFamily(td.flowId, td.flowName, gapFamilies))
|
|
100
|
+
: [];
|
|
101
|
+
const gapCases = gapDesigns.reduce((n, td) => n + td.testCases.length, 0);
|
|
102
|
+
const gapP0Cases = gapDesigns.reduce((n, td) => n + td.testCases.filter((tc) => tc.priority === 'P0').length, 0);
|
|
103
|
+
lines.push(`Structured test designs: **${crew.summary.testDesigns}** flows, **${totalCases}** test cases`);
|
|
104
|
+
if (gapDesigns.length > 0) {
|
|
105
|
+
lines.push(`Gap-focused: **${gapDesigns.length}** flows, **${gapCases}** test cases (**${gapP0Cases}** P0)`);
|
|
106
|
+
}
|
|
98
107
|
}
|
|
99
|
-
|
|
108
|
+
if (crew.summary.crossImpacts > 0) {
|
|
109
|
+
lines.push(`Cross-impacts: **${crew.summary.crossImpacts}** (${crew.summary.highRiskCrossImpacts} high risk)`);
|
|
110
|
+
}
|
|
111
|
+
lines.push(`Estimated AI cost: **$${crew.summary.totalCostUSD.toFixed(4)}**`);
|
|
100
112
|
if (crew.strategyEntries.length > 0) {
|
|
101
113
|
lines.push('');
|
|
102
114
|
lines.push('Top Strategy Recommendations:');
|
|
@@ -144,14 +156,18 @@ export function appendCrewToSummary(baseMarkdown, crew, plan) {
|
|
|
144
156
|
*/
|
|
145
157
|
export function buildCrewTestPlan(crew, plan) {
|
|
146
158
|
const gapFamilies = new Set((plan?.gapDetails ?? []).map((g) => g.id));
|
|
159
|
+
const hasTestDesigns = crew.testDesigns.length > 0;
|
|
147
160
|
const totalCases = crew.testDesigns.reduce((n, td) => n + td.testCases.length, 0);
|
|
148
|
-
// Split
|
|
161
|
+
// Split strategy entries into gap-related and covered
|
|
162
|
+
const gapStrategies = gapFamilies.size > 0
|
|
163
|
+
? crew.strategyEntries.filter((s) => matchesGapFamily(s.flowId, s.flowName, gapFamilies))
|
|
164
|
+
: [];
|
|
165
|
+
const coveredStrategies = crew.strategyEntries.filter((s) => !gapStrategies.includes(s));
|
|
166
|
+
// Split test designs (if present) into gap-related and covered
|
|
149
167
|
const gapDesigns = [];
|
|
150
168
|
const coveredDesigns = [];
|
|
151
169
|
for (const td of crew.testDesigns) {
|
|
152
|
-
|
|
153
|
-
const isGap = Array.from(gapFamilies).some((fam) => td.flowId.startsWith(fam) || td.flowName.toLowerCase().includes(fam.replace(/_/g, ' ')));
|
|
154
|
-
if (isGap) {
|
|
170
|
+
if (matchesGapFamily(td.flowId, td.flowName, gapFamilies)) {
|
|
155
171
|
gapDesigns.push(td);
|
|
156
172
|
}
|
|
157
173
|
else {
|
|
@@ -169,77 +185,92 @@ export function buildCrewTestPlan(crew, plan) {
|
|
|
169
185
|
'',
|
|
170
186
|
`| Metric | Count |`,
|
|
171
187
|
`|--------|-------|`,
|
|
172
|
-
`| Gap flows (missing tests) | ${
|
|
173
|
-
`| Covered flows (expansion) | ${
|
|
174
|
-
`| Total | ${crew.
|
|
188
|
+
`| Gap flows (missing tests) | ${gapStrategies.length} flows${hasTestDesigns ? `, **${gapCases} test cases**` : ''} |`,
|
|
189
|
+
`| Covered flows (expansion) | ${coveredStrategies.length} flows${hasTestDesigns ? `, ${coveredCases} test cases` : ''} |`,
|
|
190
|
+
`| Total strategy entries | ${crew.strategyEntries.length} flows${hasTestDesigns ? `, ${totalCases} test cases` : ''} |`,
|
|
175
191
|
`| High-risk cross-impacts | ${crew.summary.highRiskCrossImpacts} |`,
|
|
176
192
|
`| AI cost | $${crew.summary.totalCostUSD.toFixed(4)} |`,
|
|
177
193
|
'',
|
|
178
194
|
];
|
|
179
|
-
//
|
|
180
|
-
if (
|
|
195
|
+
// ── Gap flows ──
|
|
196
|
+
if (gapStrategies.length > 0) {
|
|
181
197
|
lines.push('## Priority: Gap Flows (Missing Tests)');
|
|
182
198
|
lines.push('');
|
|
183
199
|
lines.push('These flows have **no existing E2E coverage** and should be addressed first.');
|
|
184
200
|
lines.push('');
|
|
185
|
-
for (const
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
const risk = strategy?.crossImpactRisk ?? 'unknown';
|
|
189
|
-
const p0Cases = td.testCases.filter((tc) => tc.priority === 'P0');
|
|
190
|
-
const p1Cases = td.testCases.filter((tc) => tc.priority === 'P1');
|
|
191
|
-
lines.push(`### ${td.flowName}`);
|
|
201
|
+
for (const strategy of gapStrategies) {
|
|
202
|
+
const td = crew.testDesigns.find((d) => d.flowId === strategy.flowId);
|
|
203
|
+
lines.push(`### ${strategy.flowName}`);
|
|
192
204
|
lines.push('');
|
|
193
|
-
lines.push(`Strategy: **${approach}** |
|
|
205
|
+
lines.push(`Strategy: **${strategy.approach}** | Priority: **${strategy.priority}** | Cross-impact risk: **${strategy.crossImpactRisk}**`);
|
|
206
|
+
if (strategy.rationale) {
|
|
207
|
+
lines.push(`> ${strategy.rationale}`);
|
|
208
|
+
}
|
|
209
|
+
if (strategy.testCategories.length > 0) {
|
|
210
|
+
lines.push(`Test categories: ${strategy.testCategories.join(', ')}`);
|
|
211
|
+
}
|
|
194
212
|
lines.push('');
|
|
195
|
-
//
|
|
196
|
-
if (
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
lines.push(
|
|
201
|
-
|
|
202
|
-
|
|
213
|
+
// If test designs exist, show P0/P1 cases
|
|
214
|
+
if (td && td.testCases.length > 0) {
|
|
215
|
+
const p0Cases = td.testCases.filter((tc) => tc.priority === 'P0');
|
|
216
|
+
const p1Cases = td.testCases.filter((tc) => tc.priority === 'P1');
|
|
217
|
+
if (p0Cases.length > 0) {
|
|
218
|
+
lines.push('**P0 — Must test:**');
|
|
219
|
+
lines.push('');
|
|
220
|
+
for (const tc of p0Cases) {
|
|
221
|
+
lines.push(`- [ ] **${tc.name}** (${tc.type})`);
|
|
222
|
+
if (tc.preconditions.length > 0) {
|
|
223
|
+
lines.push(` - Preconditions: ${tc.preconditions.join('; ')}`);
|
|
224
|
+
}
|
|
225
|
+
lines.push(` - Steps: ${tc.steps.join(' → ')}`);
|
|
226
|
+
lines.push(` - Expected: ${tc.expectedOutcome}`);
|
|
203
227
|
}
|
|
204
|
-
lines.push(
|
|
205
|
-
lines.push(` - Expected: ${tc.expectedOutcome}`);
|
|
228
|
+
lines.push('');
|
|
206
229
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
lines.push(
|
|
230
|
+
if (p1Cases.length > 0) {
|
|
231
|
+
lines.push(`<details><summary>P1 — Should test (${p1Cases.length})</summary>`);
|
|
232
|
+
lines.push('');
|
|
233
|
+
for (const tc of p1Cases) {
|
|
234
|
+
lines.push(`- [ ] **${tc.name}** (${tc.type}) — ${tc.expectedOutcome}`);
|
|
235
|
+
}
|
|
236
|
+
lines.push('');
|
|
237
|
+
lines.push('</details>');
|
|
238
|
+
lines.push('');
|
|
215
239
|
}
|
|
216
|
-
lines.push('');
|
|
217
|
-
lines.push('</details>');
|
|
218
|
-
lines.push('');
|
|
219
240
|
}
|
|
220
241
|
}
|
|
221
242
|
}
|
|
222
|
-
// Covered
|
|
223
|
-
if (
|
|
243
|
+
// ── Covered flows ──
|
|
244
|
+
if (coveredStrategies.length > 0) {
|
|
224
245
|
lines.push('## Covered Flows (Regression / Expansion)');
|
|
225
246
|
lines.push('');
|
|
226
|
-
lines.push('These flows already have specs.
|
|
247
|
+
lines.push('These flows already have specs. Verify changes haven\'t introduced regressions.');
|
|
227
248
|
lines.push('');
|
|
228
|
-
for (const
|
|
229
|
-
const
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
lines.push(`<details><summary><strong>${
|
|
249
|
+
for (const strategy of coveredStrategies) {
|
|
250
|
+
const td = crew.testDesigns.find((d) => d.flowId === strategy.flowId);
|
|
251
|
+
const caseCount = td ? td.testCases.length : 0;
|
|
252
|
+
const detail = caseCount > 0 ? ` | ${caseCount} cases` : '';
|
|
253
|
+
lines.push(`<details><summary><strong>${strategy.flowName}</strong> — ${strategy.approach}${detail} (${strategy.priority})</summary>`);
|
|
233
254
|
lines.push('');
|
|
234
|
-
|
|
235
|
-
|
|
255
|
+
lines.push(`Cross-impact risk: ${strategy.crossImpactRisk}`);
|
|
256
|
+
if (strategy.rationale) {
|
|
257
|
+
lines.push(`> ${strategy.rationale}`);
|
|
258
|
+
}
|
|
259
|
+
if (strategy.testCategories.length > 0) {
|
|
260
|
+
lines.push(`Test categories: ${strategy.testCategories.join(', ')}`);
|
|
261
|
+
}
|
|
262
|
+
if (td && td.testCases.length > 0) {
|
|
263
|
+
lines.push('');
|
|
264
|
+
for (const tc of td.testCases) {
|
|
265
|
+
lines.push(`- [ ] **${tc.name}** (${tc.priority}, ${tc.type}) — ${tc.expectedOutcome}`);
|
|
266
|
+
}
|
|
236
267
|
}
|
|
237
268
|
lines.push('');
|
|
238
269
|
lines.push('</details>');
|
|
239
270
|
lines.push('');
|
|
240
271
|
}
|
|
241
272
|
}
|
|
242
|
-
// Cross-impacts
|
|
273
|
+
// ── Cross-impacts ──
|
|
243
274
|
const highRisk = crew.crossImpacts.filter((ci) => ci.riskLevel === 'high');
|
|
244
275
|
if (highRisk.length > 0) {
|
|
245
276
|
lines.push('## High-Risk Cross-Impacts');
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yasserkhanorg/e2e-agents",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.4",
|
|
4
4
|
"description": "AI-powered E2E test impact analysis, generation, and healing. Analyzes code changes to identify affected Playwright tests, detects coverage gaps, and generates or repairs specs using pluggable LLM providers (Claude, OpenAI, Ollama). Includes MCP server, traceability, and CI/CD integration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|