@yasserkhanorg/e2e-agents 0.6.0 → 0.7.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/plan.d.ts +2 -1
- package/dist/agent/plan.d.ts.map +1 -1
- package/dist/api.d.ts +4 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +38 -0
- package/dist/cli.js +57 -14
- package/dist/engine/ai_enrichment.d.ts +43 -0
- package/dist/engine/ai_enrichment.d.ts.map +1 -0
- package/dist/engine/ai_enrichment.js +235 -0
- package/dist/engine/diff_loader.d.ts +11 -0
- package/dist/engine/diff_loader.d.ts.map +1 -0
- package/dist/engine/diff_loader.js +74 -0
- package/dist/engine/plan_builder.d.ts +2 -1
- package/dist/engine/plan_builder.d.ts.map +1 -1
- package/dist/engine/plan_builder.js +60 -15
- package/dist/esm/api.js +37 -0
- package/dist/esm/cli.js +58 -15
- package/dist/esm/engine/ai_enrichment.js +232 -0
- package/dist/esm/engine/diff_loader.js +70 -0
- package/dist/esm/engine/plan_builder.js +60 -15
- package/dist/esm/knowledge/route_families.js +2 -1
- package/dist/knowledge/route_families.d.ts.map +1 -1
- package/dist/knowledge/route_families.js +2 -1
- package/package.json +1 -1
|
@@ -193,7 +193,7 @@ function buildRecommendedTests(impact) {
|
|
|
193
193
|
}
|
|
194
194
|
return tests;
|
|
195
195
|
}
|
|
196
|
-
export function buildPlanFromImpact(impact, policyOverride) {
|
|
196
|
+
export function buildPlanFromImpact(impact, policyOverride, aiEnrichment) {
|
|
197
197
|
const policy = { ...DEFAULT_POLICY, ...(policyOverride || {}) };
|
|
198
198
|
const confidence = computeConfidence(impact);
|
|
199
199
|
const runSetResult = pickRunSet(impact, confidence, policy);
|
|
@@ -201,23 +201,61 @@ export function buildPlanFromImpact(impact, policyOverride) {
|
|
|
201
201
|
const enforcement = evaluateEnforcement(decision, policy);
|
|
202
202
|
const gaps = getGaps(impact);
|
|
203
203
|
const partialGaps = getPartialGaps(impact);
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
204
|
+
// Build two separate lookup maps from aiEnrichment: one by featureId, one by familyId.
|
|
205
|
+
// The familyId map stores only the FIRST feature encountered to avoid last-write-wins collisions.
|
|
206
|
+
const aiFeatureByFeatureId = new Map();
|
|
207
|
+
const aiFeatureByFamilyId = new Map();
|
|
208
|
+
if (aiEnrichment) {
|
|
209
|
+
for (const ef of aiEnrichment.enrichedFeatures) {
|
|
210
|
+
if (ef.featureId) {
|
|
211
|
+
aiFeatureByFeatureId.set(ef.featureId, ef);
|
|
212
|
+
}
|
|
213
|
+
if (ef.familyId && !aiFeatureByFamilyId.has(ef.familyId)) {
|
|
214
|
+
aiFeatureByFamilyId.set(ef.familyId, ef);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const gapDetails = gaps.map((f) => {
|
|
219
|
+
const label = featureLabel(f);
|
|
220
|
+
const aiFeature = f.featureId
|
|
221
|
+
? (aiFeatureByFeatureId.get(f.featureId) ?? aiFeatureByFamilyId.get(f.familyId))
|
|
222
|
+
: aiFeatureByFamilyId.get(f.familyId);
|
|
223
|
+
const baseReasons = [`No Playwright or Cypress tests found for ${label}`];
|
|
224
|
+
const reasons = aiFeature && aiFeature.aiReasons.length > 0
|
|
225
|
+
? [...baseReasons, ...aiFeature.aiReasons]
|
|
226
|
+
: baseReasons;
|
|
227
|
+
const missingScenarios = aiFeature && aiFeature.aiMissingScenarios.length > 0
|
|
228
|
+
? aiFeature.aiMissingScenarios
|
|
229
|
+
: (f.userFlows.length > 0 ? f.userFlows.slice(0, 5) : undefined);
|
|
230
|
+
return {
|
|
231
|
+
id: label,
|
|
232
|
+
name: label,
|
|
233
|
+
priority: f.priority,
|
|
234
|
+
reasons,
|
|
235
|
+
files: f.changedFiles.slice(0, 6),
|
|
236
|
+
missingScenarios,
|
|
237
|
+
source: aiFeature ? 'ai+deterministic' : 'deterministic',
|
|
238
|
+
};
|
|
239
|
+
});
|
|
212
240
|
// Add partial gaps as advisory info
|
|
213
241
|
for (const f of partialGaps) {
|
|
214
|
-
const
|
|
242
|
+
const coverageType = f.playwrightSpecs.length > 0 ? 'Cypress' : 'Playwright';
|
|
243
|
+
const hasOpposite = f.playwrightSpecs.length > 0 ? 'Playwright' : 'Cypress';
|
|
244
|
+
const label = featureLabel(f);
|
|
245
|
+
const aiFeature = f.featureId
|
|
246
|
+
? (aiFeatureByFeatureId.get(f.featureId) ?? aiFeatureByFamilyId.get(f.familyId))
|
|
247
|
+
: aiFeatureByFamilyId.get(f.familyId);
|
|
248
|
+
const baseReasons = [`Missing ${coverageType} tests for ${label} (has ${hasOpposite} only)`];
|
|
249
|
+
const reasons = aiFeature && aiFeature.aiReasons.length > 0
|
|
250
|
+
? [...baseReasons, ...aiFeature.aiReasons]
|
|
251
|
+
: baseReasons;
|
|
215
252
|
gapDetails.push({
|
|
216
|
-
id:
|
|
217
|
-
name: `${
|
|
253
|
+
id: label,
|
|
254
|
+
name: `${label} (partial)`,
|
|
218
255
|
priority: f.priority,
|
|
219
|
-
reasons
|
|
256
|
+
reasons,
|
|
220
257
|
files: f.changedFiles.slice(0, 6),
|
|
258
|
+
source: aiFeature ? 'ai+deterministic' : 'deterministic',
|
|
221
259
|
});
|
|
222
260
|
}
|
|
223
261
|
const coveredFlows = impact.impactedFeatures
|
|
@@ -237,11 +275,12 @@ export function buildPlanFromImpact(impact, policyOverride) {
|
|
|
237
275
|
const p1 = impact.impactedFeatures.filter((f) => f.priority === 'P1').length;
|
|
238
276
|
const p2 = impact.impactedFeatures.filter((f) => f.priority === 'P2').length;
|
|
239
277
|
const runId = `plan-${Date.now().toString(36)}`;
|
|
278
|
+
const planSource = aiEnrichment ? 'ai+deterministic' : 'impact';
|
|
240
279
|
return {
|
|
241
280
|
schemaVersion: '1.0.0',
|
|
242
281
|
runId,
|
|
243
282
|
generatedAt: new Date().toISOString(),
|
|
244
|
-
source:
|
|
283
|
+
source: planSource,
|
|
245
284
|
runSet: runSetResult.runSet,
|
|
246
285
|
confidence,
|
|
247
286
|
reasons: runSetResult.reasons,
|
|
@@ -292,12 +331,18 @@ export function renderCiSummaryMarkdown(plan) {
|
|
|
292
331
|
lines.push(`The following ${uncoveredP0P1Flows} feature(s) have no test coverage and must be covered before merge:`);
|
|
293
332
|
lines.push('');
|
|
294
333
|
for (const gap of plan.gapDetails.filter((g) => !g.name.includes('(partial)'))) {
|
|
295
|
-
|
|
334
|
+
const aiLabel = gap.source === 'ai+deterministic' ? ' ✦ AI-enriched' : '';
|
|
335
|
+
lines.push(`- **${gap.name}** [${gap.priority}]${aiLabel}`);
|
|
296
336
|
if (gap.missingScenarios && gap.missingScenarios.length > 0) {
|
|
297
337
|
for (const scenario of gap.missingScenarios) {
|
|
298
338
|
lines.push(` - ${scenario}`);
|
|
299
339
|
}
|
|
300
340
|
}
|
|
341
|
+
// Show AI-provided reasons (skip the first deterministic reason which is always included)
|
|
342
|
+
const aiReasons = gap.reasons.slice(1);
|
|
343
|
+
if (aiReasons.length > 0) {
|
|
344
|
+
lines.push(` - *AI insight*: ${aiReasons.join('; ')}`);
|
|
345
|
+
}
|
|
301
346
|
}
|
|
302
347
|
}
|
|
303
348
|
if (plan.coveredFlows.length > 0) {
|
|
@@ -154,7 +154,8 @@ export function loadRouteFamilyManifest(testsRoot, config) {
|
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
156
|
if (config?.strict) {
|
|
157
|
-
|
|
157
|
+
// eslint-disable-next-line no-console
|
|
158
|
+
console.warn('[e2e-agents] Route family manifest not found. The manifest is optional context for AI enrichment — create .e2e-ai-agents/route-families.json to enable family-level routing hints.');
|
|
158
159
|
}
|
|
159
160
|
return null;
|
|
160
161
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"route_families.d.ts","sourceRoot":"","sources":["../../src/knowledge/route_families.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,eAAe,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEjD,MAAM,WAAW,YAAY;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,YAAY,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,mBAAmB;IAChC,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAC,CAAC,CAAC;CACvD;AAED,MAAM,WAAW,iBAAiB;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC;CACpB;AA+HD,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"route_families.d.ts","sourceRoot":"","sources":["../../src/knowledge/route_families.ts"],"names":[],"mappings":"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"}
|
|
@@ -166,7 +166,8 @@ function loadRouteFamilyManifest(testsRoot, config) {
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
if (config?.strict) {
|
|
169
|
-
|
|
169
|
+
// eslint-disable-next-line no-console
|
|
170
|
+
console.warn('[e2e-agents] Route family manifest not found. The manifest is optional context for AI enrichment — create .e2e-ai-agents/route-families.json to enable family-level routing hints.');
|
|
170
171
|
}
|
|
171
172
|
return null;
|
|
172
173
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yasserkhanorg/e2e-agents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Pluggable LLM provider library for AI-powered test automation. Use Claude, Ollama, or your own LLM. Integrate with Playwright, Jest, or any test framework. MCP server for test agents, cost tracking, and hybrid provider mode.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/esm/index.js",
|