@yasserkhanorg/e2e-agents 0.3.3 → 0.3.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.
- package/README.md +7 -2
- package/dist/agent/config.d.ts +2 -0
- package/dist/agent/config.d.ts.map +1 -1
- package/dist/agent/config.js +28 -0
- package/dist/agent/operational_insights.d.ts +1 -1
- package/dist/agent/operational_insights.d.ts.map +1 -1
- package/dist/agent/operational_insights.js +2 -1
- package/dist/agent/pipeline.d.ts +2 -0
- package/dist/agent/pipeline.d.ts.map +1 -1
- package/dist/agent/pipeline.js +240 -29
- package/dist/agent/plan.d.ts +39 -0
- package/dist/agent/plan.d.ts.map +1 -1
- package/dist/agent/plan.js +146 -0
- package/dist/agent/report.d.ts +11 -0
- package/dist/agent/report.d.ts.map +1 -1
- package/dist/agent/report.js +9 -0
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +41 -5
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +1 -0
- package/dist/cli.js +38 -2
- package/dist/esm/agent/config.js +28 -0
- package/dist/esm/agent/operational_insights.js +2 -1
- package/dist/esm/agent/pipeline.js +240 -29
- package/dist/esm/agent/plan.js +145 -1
- package/dist/esm/agent/report.js +9 -0
- package/dist/esm/agent/runner.js +41 -5
- package/dist/esm/api.js +2 -1
- package/dist/esm/cli.js +39 -3
- package/package.json +1 -1
- package/schemas/impact.schema.json +37 -0
- package/schemas/plan.schema.json +48 -0
package/README.md
CHANGED
|
@@ -212,7 +212,9 @@ Optional config file `e2e-ai-agents.config.json` (JSON):
|
|
|
212
212
|
"forceFullOnWarningsAtOrAbove": 2,
|
|
213
213
|
"forceFullOnP0WithGaps": true,
|
|
214
214
|
"forceFullOnRiskyFiles": true,
|
|
215
|
-
"riskyFilePatterns": ["**/auth/**", "**/permissions/**", "**/security/**", "**/*.sql"]
|
|
215
|
+
"riskyFilePatterns": ["**/auth/**", "**/permissions/**", "**/security/**", "**/*.sql"],
|
|
216
|
+
"enforcementMode": "advisory",
|
|
217
|
+
"blockOnActions": ["must-add-tests"]
|
|
216
218
|
},
|
|
217
219
|
"flags": { "defaultState": "on" },
|
|
218
220
|
"audience": { "defaultRoles": ["member"] },
|
|
@@ -257,12 +259,15 @@ Notes:
|
|
|
257
259
|
- `impact/gap` pipeline output now includes `pipeline.mcp` (`requested`, `active`, `backend`) so MCP activation is explicit.
|
|
258
260
|
- `suggest` writes `.e2e-ai-agents/plan.json` with `runSet` (`smoke|targeted|full`) and confidence.
|
|
259
261
|
- `suggest` also writes `.e2e-ai-agents/ci-summary.md` with CI status: `run-now`, `must-add-tests`, or `safe-to-merge`.
|
|
260
|
-
- CLI policy overrides: `--policy-min-confidence`, `--policy-safe-merge-confidence`, `--policy-force-full-on-warnings`, `--policy-risky-patterns`.
|
|
262
|
+
- CLI policy overrides: `--policy-min-confidence`, `--policy-safe-merge-confidence`, `--policy-force-full-on-warnings`, `--policy-risky-patterns`, `--policy-enforcement-mode`, `--policy-block-actions`.
|
|
261
263
|
- GitHub Actions output wiring: `--github-output $GITHUB_OUTPUT`.
|
|
262
264
|
- Optional merge gating: `--fail-on-must-add-tests` exits non-zero when uncovered P0/P1 gaps are detected. Leave this flag unset for advisory-only mode.
|
|
265
|
+
- `suggest` now appends run metrics to `.e2e-ai-agents/metrics.jsonl` and writes aggregated `.e2e-ai-agents/metrics-summary.json`.
|
|
263
266
|
- `impact/gap` now include actionable `testSuggestions` with linked source files and skeleton test code.
|
|
264
267
|
- `impact/gap` now include `impactModel` metadata (`flowMapping`, `testMapping`, `confidenceClass`, traceability stats, dependency graph stats).
|
|
268
|
+
- `impact/gap` now include `runMetadata` (run id/timestamps/duration/since ref) for auditability.
|
|
265
269
|
- `impact/gap` now include optional `impactModel.subsystemRisk` stats (map status, matched files/rules, boosted flows).
|
|
270
|
+
- `impact/gap` pipeline result rows now include failure taxonomy (`failureCategory`, `failureCode`) when generation/heal fails.
|
|
266
271
|
- `feedback` appends outcomes to `.e2e-ai-agents/feedback.json` and recomputes `.e2e-ai-agents/calibration.json`.
|
|
267
272
|
- `feedback` also computes intelligent flaky scores into `.e2e-ai-agents/flaky-tests.json`.
|
|
268
273
|
- `traceability-capture` converts Playwright JSON execution report + optional coverage map into `.e2e-ai-agents/traceability-input.json`.
|
package/dist/agent/config.d.ts
CHANGED
|
@@ -71,6 +71,8 @@ export interface PolicyConfig {
|
|
|
71
71
|
forceFullOnP0WithGaps: boolean;
|
|
72
72
|
forceFullOnRiskyFiles: boolean;
|
|
73
73
|
riskyFilePatterns: string[];
|
|
74
|
+
enforcementMode: 'advisory' | 'warn' | 'block';
|
|
75
|
+
blockOnActions: Array<'run-now' | 'must-add-tests' | 'safe-to-merge'>;
|
|
74
76
|
}
|
|
75
77
|
export interface DependencyGraphImpactConfig {
|
|
76
78
|
enabled: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/agent/config.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,KAAK,CAAC;AAC5C,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,YAAY,GAAG,SAAS,GAAG,UAAU,GAAG,SAAS,CAAC;AACvF,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,YAAY,GAAG,MAAM,CAAC;AAE5D,MAAM,WAAW,YAAY;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC3B,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC3B,YAAY,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,mBAAmB;IAChC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACjC,cAAc,EAAE;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;KACd,CAAC;IACF,eAAe,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,MAAM,SAAS,GAAG,IAAI,GAAG,KAAK,GAAG,SAAS,CAAC;AAEjD,MAAM,MAAM,YAAY,GAClB,cAAc,GACd,YAAY,GACZ,eAAe,GACf,QAAQ,GACR,OAAO,GACP,aAAa,CAAC;AAEpB,MAAM,WAAW,UAAU;IACvB,YAAY,EAAE,SAAS,CAAC;CAC3B;AAED,MAAM,WAAW,cAAc;IAC3B,YAAY,EAAE,YAAY,EAAE,CAAC;CAChC;AAED,MAAM,WAAW,iBAAiB;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,cAAc;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,SAAS,GAAG,QAAQ,CAAC;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,WAAW,SAAS;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,UAAU;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,YAAY;IACzB,wBAAwB,EAAE,MAAM,CAAC;IACjC,sBAAsB,EAAE,MAAM,CAAC;IAC/B,4BAA4B,EAAE,MAAM,CAAC;IACrC,qBAAqB,EAAE,OAAO,CAAC;IAC/B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,iBAAiB,EAAE,MAAM,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/agent/config.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/config.js
CHANGED
|
@@ -75,6 +75,7 @@ const DEFAULT_CONFIG = {
|
|
|
75
75
|
scenarios: 3,
|
|
76
76
|
outputDir: 'specs/functional/ai-assisted',
|
|
77
77
|
heal: true,
|
|
78
|
+
project: 'chrome',
|
|
78
79
|
mcp: false,
|
|
79
80
|
mcpAllowFallback: false,
|
|
80
81
|
},
|
|
@@ -123,6 +124,8 @@ const DEFAULT_CONFIG = {
|
|
|
123
124
|
'**/*.sql',
|
|
124
125
|
'**/webhook/**',
|
|
125
126
|
],
|
|
127
|
+
enforcementMode: 'advisory',
|
|
128
|
+
blockOnActions: ['must-add-tests'],
|
|
126
129
|
},
|
|
127
130
|
flags: {
|
|
128
131
|
defaultState: 'on',
|
|
@@ -269,6 +272,18 @@ function normalizeFlagState(value) {
|
|
|
269
272
|
}
|
|
270
273
|
return undefined;
|
|
271
274
|
}
|
|
275
|
+
function normalizePolicyEnforcementMode(value) {
|
|
276
|
+
if (value === 'advisory' || value === 'warn' || value === 'block') {
|
|
277
|
+
return value;
|
|
278
|
+
}
|
|
279
|
+
return undefined;
|
|
280
|
+
}
|
|
281
|
+
function normalizePolicyBlockAction(value) {
|
|
282
|
+
if (value === 'run-now' || value === 'must-add-tests' || value === 'safe-to-merge') {
|
|
283
|
+
return value;
|
|
284
|
+
}
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
272
287
|
function extractConfigPatch(raw) {
|
|
273
288
|
const patch = {};
|
|
274
289
|
if (typeof raw.path === 'string') {
|
|
@@ -465,6 +480,11 @@ function extractConfigPatch(raw) {
|
|
|
465
480
|
}
|
|
466
481
|
if (raw.policy && typeof raw.policy === 'object') {
|
|
467
482
|
const policy = raw.policy;
|
|
483
|
+
const blockOnActions = Array.isArray(policy.blockOnActions)
|
|
484
|
+
? policy.blockOnActions
|
|
485
|
+
.map((value) => normalizePolicyBlockAction(value))
|
|
486
|
+
.filter((value) => Boolean(value))
|
|
487
|
+
: DEFAULT_CONFIG.policy.blockOnActions;
|
|
468
488
|
patch.policy = {
|
|
469
489
|
minConfidenceForTargeted: coerceNumber(policy.minConfidenceForTargeted) ?? DEFAULT_CONFIG.policy.minConfidenceForTargeted,
|
|
470
490
|
safeMergeMinConfidence: coerceNumber(policy.safeMergeMinConfidence) ?? DEFAULT_CONFIG.policy.safeMergeMinConfidence,
|
|
@@ -478,6 +498,8 @@ function extractConfigPatch(raw) {
|
|
|
478
498
|
riskyFilePatterns: Array.isArray(policy.riskyFilePatterns)
|
|
479
499
|
? policy.riskyFilePatterns.filter((pattern) => typeof pattern === 'string')
|
|
480
500
|
: DEFAULT_CONFIG.policy.riskyFilePatterns,
|
|
501
|
+
enforcementMode: normalizePolicyEnforcementMode(policy.enforcementMode) ?? DEFAULT_CONFIG.policy.enforcementMode,
|
|
502
|
+
blockOnActions: blockOnActions.length > 0 ? blockOnActions : DEFAULT_CONFIG.policy.blockOnActions,
|
|
481
503
|
};
|
|
482
504
|
}
|
|
483
505
|
if (raw.flags && typeof raw.flags === 'object') {
|
|
@@ -613,6 +635,12 @@ function resolveConfig(cwd, configPath, overrides) {
|
|
|
613
635
|
if (overrides.policy.riskyFilePatterns && overrides.policy.riskyFilePatterns.length > 0) {
|
|
614
636
|
policyPatch.riskyFilePatterns = overrides.policy.riskyFilePatterns;
|
|
615
637
|
}
|
|
638
|
+
if (overrides.policy.enforcementMode !== undefined) {
|
|
639
|
+
policyPatch.enforcementMode = overrides.policy.enforcementMode;
|
|
640
|
+
}
|
|
641
|
+
if (overrides.policy.blockOnActions && overrides.policy.blockOnActions.length > 0) {
|
|
642
|
+
policyPatch.blockOnActions = overrides.policy.blockOnActions;
|
|
643
|
+
}
|
|
616
644
|
config.policy = { ...config.policy, ...policyPatch };
|
|
617
645
|
}
|
|
618
646
|
if (overrides?.specPDF) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operational_insights.d.ts","sourceRoot":"","sources":["../../src/agent/operational_insights.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,
|
|
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"}
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
5
5
|
exports.applyOperationalInsights = applyOperationalInsights;
|
|
6
6
|
const fs_1 = require("fs");
|
|
7
7
|
const path_1 = require("path");
|
|
8
|
+
const plan_js_1 = require("./plan.js");
|
|
8
9
|
const test_path_js_1 = require("./test_path.js");
|
|
9
10
|
function readJson(path) {
|
|
10
11
|
if (!(0, fs_1.existsSync)(path)) {
|
|
@@ -122,5 +123,5 @@ function applyOperationalInsights(plan, appRoot) {
|
|
|
122
123
|
}
|
|
123
124
|
}
|
|
124
125
|
enhanced.insights = insights;
|
|
125
|
-
return enhanced;
|
|
126
|
+
return (0, plan_js_1.refreshPlanEnforcement)(enhanced);
|
|
126
127
|
}
|
package/dist/agent/pipeline.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface PipelineResult {
|
|
|
7
7
|
generateStatus: 'success' | 'skipped' | 'failed';
|
|
8
8
|
healStatus?: 'success' | 'skipped' | 'failed';
|
|
9
9
|
error?: string;
|
|
10
|
+
failureCategory?: 'config' | 'environment' | 'generation' | 'validation' | 'runtime' | 'quality' | 'path-safety' | 'unknown';
|
|
11
|
+
failureCode?: string;
|
|
10
12
|
}
|
|
11
13
|
export interface PipelineSummary {
|
|
12
14
|
runner: 'playwright-agents' | 'e2e-test-gen' | 'package-native' | 'unknown';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/agent/pipeline.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAG9C,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACjD,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/agent/pipeline.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAG9C,MAAM,WAAW,cAAc;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IACjD,UAAU,CAAC,EAAE,SAAS,GAAG,SAAS,GAAG,QAAQ,CAAC;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,QAAQ,GAAG,aAAa,GAAG,YAAY,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAC;IAC7H,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC5B,MAAM,EAAE,mBAAmB,GAAG,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAAC;IAC5E,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,CAAC,EAAE;QACF,SAAS,EAAE,OAAO,CAAC;QACnB,MAAM,EAAE,OAAO,CAAC;QAChB,OAAO,EAAE,mBAAmB,GAAG,cAAc,GAAG,gBAAgB,GAAG,SAAS,CAAC;KAChF,CAAC;CACL;AAED,MAAM,WAAW,cAAc;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB;AAk9BD,wBAAgB,mBAAmB,CAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,cAAc,EAAE,EACzB,QAAQ,EAAE,cAAc,GACzB,eAAe,CA4FjB;AA4bD,wBAAgB,qBAAqB,CACjC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,EAAE,EACnB,QAAQ,EAAE,cAAc,GACzB,eAAe,CAmJjB"}
|
package/dist/agent/pipeline.js
CHANGED
|
@@ -15,6 +15,40 @@ function createMcpStatus(backend, requested) {
|
|
|
15
15
|
backend,
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
+
function classifyPipelineFailure(result) {
|
|
19
|
+
if (result.failureCategory || result.failureCode) {
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
if (!result.error) {
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
const errorText = result.error.toLowerCase();
|
|
26
|
+
if (errorText.includes('outside testsroot')) {
|
|
27
|
+
return { ...result, failureCategory: 'path-safety', failureCode: 'path_outside_tests_root' };
|
|
28
|
+
}
|
|
29
|
+
if (errorText.includes('playwright binary') || errorText.includes('not found')) {
|
|
30
|
+
return { ...result, failureCategory: 'environment', failureCode: 'dependency_missing' };
|
|
31
|
+
}
|
|
32
|
+
if (errorText.includes('compile validation')) {
|
|
33
|
+
return { ...result, failureCategory: 'validation', failureCode: 'compile_validation_failed' };
|
|
34
|
+
}
|
|
35
|
+
if (errorText.includes('runtime validation') || errorText.includes('playwright test failed')) {
|
|
36
|
+
return { ...result, failureCategory: 'runtime', failureCode: 'runtime_validation_failed' };
|
|
37
|
+
}
|
|
38
|
+
if (errorText.includes('quality checks failed') || errorText.includes('invalid test content')) {
|
|
39
|
+
return { ...result, failureCategory: 'quality', failureCode: 'quality_guard_failed' };
|
|
40
|
+
}
|
|
41
|
+
if (errorText.includes('generate failed') || errorText.includes('did not produce expected test file')) {
|
|
42
|
+
return { ...result, failureCategory: 'generation', failureCode: 'generation_failed' };
|
|
43
|
+
}
|
|
44
|
+
return { ...result, failureCategory: 'unknown', failureCode: 'unknown' };
|
|
45
|
+
}
|
|
46
|
+
function finalizePipelineSummary(summary) {
|
|
47
|
+
return {
|
|
48
|
+
...summary,
|
|
49
|
+
results: summary.results.map(classifyPipelineFailure),
|
|
50
|
+
};
|
|
51
|
+
}
|
|
18
52
|
function hasE2eTestGenCLI(testsRoot) {
|
|
19
53
|
const cliPath = (0, path_1.join)(testsRoot, 'e2e-test-gen-cli.ts');
|
|
20
54
|
return (0, fs_1.existsSync)(cliPath) ? cliPath : null;
|
|
@@ -118,6 +152,15 @@ function buildNativeStrategyOrder(flow) {
|
|
|
118
152
|
return Array.from(new Set(strategies));
|
|
119
153
|
}
|
|
120
154
|
function createDefaultApiSurfaceCatalog() {
|
|
155
|
+
const pwNestedMethods = new Map();
|
|
156
|
+
pwNestedMethods.set('apiClient', new Set([
|
|
157
|
+
'createPost',
|
|
158
|
+
'createDirectChannel',
|
|
159
|
+
'createChannel',
|
|
160
|
+
'getChannels',
|
|
161
|
+
'getChannelByName',
|
|
162
|
+
'getPostsSince',
|
|
163
|
+
]));
|
|
121
164
|
return {
|
|
122
165
|
pwProps: new Set([
|
|
123
166
|
'initSetup',
|
|
@@ -127,7 +170,20 @@ function createDefaultApiSurfaceCatalog() {
|
|
|
127
170
|
'apiCreateChannel',
|
|
128
171
|
'apiCreateUser',
|
|
129
172
|
'apiLogin',
|
|
173
|
+
'apiClient',
|
|
174
|
+
]),
|
|
175
|
+
pwNestedMethods,
|
|
176
|
+
initSetupKeys: new Set([
|
|
177
|
+
'user',
|
|
178
|
+
'team',
|
|
179
|
+
'adminClient',
|
|
180
|
+
'adminUser',
|
|
181
|
+
'adminConfig',
|
|
182
|
+
'userClient',
|
|
183
|
+
'offTopicUrl',
|
|
184
|
+
'townSquareUrl',
|
|
130
185
|
]),
|
|
186
|
+
initSetupVariableMethods: new Map(),
|
|
131
187
|
testBrowserMethods: new Set([
|
|
132
188
|
'login',
|
|
133
189
|
'openNewBrowserContext',
|
|
@@ -161,10 +217,57 @@ function collectMatches(content, pattern) {
|
|
|
161
217
|
}
|
|
162
218
|
return out;
|
|
163
219
|
}
|
|
220
|
+
function addNestedMethod(catalog, objectName, methodName) {
|
|
221
|
+
const methods = catalog.pwNestedMethods.get(objectName) || new Set();
|
|
222
|
+
methods.add(methodName);
|
|
223
|
+
catalog.pwNestedMethods.set(objectName, methods);
|
|
224
|
+
}
|
|
225
|
+
function escapeRegExp(value) {
|
|
226
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
227
|
+
}
|
|
228
|
+
function parseInitSetupBindings(content) {
|
|
229
|
+
const bindings = [];
|
|
230
|
+
for (const match of content.matchAll(/(?:const|let|var)\s*\{\s*([^}]+)\s*\}\s*=\s*await\s+pw\.initSetup\s*\(/g)) {
|
|
231
|
+
const raw = match[1];
|
|
232
|
+
if (!raw) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
for (const part of raw.split(',')) {
|
|
236
|
+
const cleaned = part.trim();
|
|
237
|
+
if (!cleaned) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const [leftRaw, rightRaw] = cleaned.split(':');
|
|
241
|
+
const key = (leftRaw || '').trim();
|
|
242
|
+
const variableCandidate = (rightRaw || leftRaw || '').trim().split('=')[0]?.trim();
|
|
243
|
+
if (!key || !variableCandidate) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
bindings.push({ key, variable: variableCandidate });
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return bindings;
|
|
250
|
+
}
|
|
251
|
+
function collectDestructuredInitSetupKeys(content) {
|
|
252
|
+
return new Set(parseInitSetupBindings(content).map((binding) => binding.key));
|
|
253
|
+
}
|
|
254
|
+
function addInitSetupVariableMethod(catalog, variable, methodName) {
|
|
255
|
+
const methods = catalog.initSetupVariableMethods.get(variable) || new Set();
|
|
256
|
+
methods.add(methodName);
|
|
257
|
+
catalog.initSetupVariableMethods.set(variable, methods);
|
|
258
|
+
}
|
|
164
259
|
function collectApiSurfaceFromContent(content, catalog) {
|
|
165
260
|
for (const prop of collectMatches(content, /\bpw\.([A-Za-z_][A-Za-z0-9_]*)\b/g)) {
|
|
166
261
|
catalog.pwProps.add(prop);
|
|
167
262
|
}
|
|
263
|
+
for (const match of content.matchAll(/\bpw\.([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)\b/g)) {
|
|
264
|
+
const objectName = match[1];
|
|
265
|
+
const methodName = match[2];
|
|
266
|
+
if (!objectName || !methodName) {
|
|
267
|
+
continue;
|
|
268
|
+
}
|
|
269
|
+
addNestedMethod(catalog, objectName, methodName);
|
|
270
|
+
}
|
|
168
271
|
for (const method of collectMatches(content, /\bpw\.testBrowser\.([A-Za-z_][A-Za-z0-9_]*)\b/g)) {
|
|
169
272
|
catalog.testBrowserMethods.add(method);
|
|
170
273
|
}
|
|
@@ -174,6 +277,13 @@ function collectApiSurfaceFromContent(content, catalog) {
|
|
|
174
277
|
for (const member of collectMatches(content, /\bchannelsPage\.sidebarRight\.([A-Za-z_][A-Za-z0-9_]*)\b/g)) {
|
|
175
278
|
catalog.sidebarRightMembers.add(member);
|
|
176
279
|
}
|
|
280
|
+
for (const binding of parseInitSetupBindings(content)) {
|
|
281
|
+
catalog.initSetupKeys.add(binding.key);
|
|
282
|
+
const methodPattern = new RegExp(`\\b${escapeRegExp(binding.variable)}\\.([A-Za-z_][A-Za-z0-9_]*)\\b`, 'g');
|
|
283
|
+
for (const method of collectMatches(content, methodPattern)) {
|
|
284
|
+
addInitSetupVariableMethod(catalog, binding.variable, method);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
177
287
|
}
|
|
178
288
|
function buildApiSurfaceCatalog(testsRoot, seedFile) {
|
|
179
289
|
const catalog = createDefaultApiSurfaceCatalog();
|
|
@@ -273,13 +383,45 @@ function validateGeneratedSpecContent(content, apiSurface) {
|
|
|
273
383
|
if (apiSurface) {
|
|
274
384
|
const unknownPwProps = Array.from(collectMatches(content, /\bpw\.([A-Za-z_][A-Za-z0-9_]*)\b/g)).filter((prop) => !apiSurface.pwProps.has(prop));
|
|
275
385
|
const unknownBrowserMethods = Array.from(collectMatches(content, /\bpw\.testBrowser\.([A-Za-z_][A-Za-z0-9_]*)\b/g)).filter((method) => !apiSurface.testBrowserMethods.has(method));
|
|
386
|
+
const unknownNestedPwMembers = [];
|
|
387
|
+
for (const match of content.matchAll(/\bpw\.([A-Za-z_][A-Za-z0-9_]*)\.([A-Za-z_][A-Za-z0-9_]*)\b/g)) {
|
|
388
|
+
const objectName = match[1];
|
|
389
|
+
const methodName = match[2];
|
|
390
|
+
if (!objectName || !methodName || objectName === 'testBrowser') {
|
|
391
|
+
continue;
|
|
392
|
+
}
|
|
393
|
+
const knownMethods = apiSurface.pwNestedMethods.get(objectName);
|
|
394
|
+
if (!knownMethods || !knownMethods.has(methodName)) {
|
|
395
|
+
unknownNestedPwMembers.push(`pw.${objectName}.${methodName}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
276
398
|
const unknownChannelMembers = Array.from(collectMatches(content, /\bchannelsPage\.([A-Za-z_][A-Za-z0-9_]*)\b/g)).filter((member) => !apiSurface.channelsPageMembers.has(member));
|
|
277
399
|
const unknownSidebarMembers = Array.from(collectMatches(content, /\bchannelsPage\.sidebarRight\.([A-Za-z_][A-Za-z0-9_]*)\b/g)).filter((member) => !apiSurface.sidebarRightMembers.has(member));
|
|
400
|
+
const initSetupBindings = parseInitSetupBindings(content);
|
|
401
|
+
const unknownInitSetupKeys = initSetupBindings
|
|
402
|
+
.map((binding) => binding.key)
|
|
403
|
+
.filter((key) => !apiSurface.initSetupKeys.has(key));
|
|
404
|
+
const unknownInitSetupVariableMethods = [];
|
|
405
|
+
for (const binding of initSetupBindings) {
|
|
406
|
+
const knownMethods = apiSurface.initSetupVariableMethods.get(binding.variable);
|
|
407
|
+
if (!knownMethods || knownMethods.size === 0) {
|
|
408
|
+
continue;
|
|
409
|
+
}
|
|
410
|
+
const methodPattern = new RegExp(`\\b${escapeRegExp(binding.variable)}\\.([A-Za-z_][A-Za-z0-9_]*)\\b`, 'g');
|
|
411
|
+
for (const method of collectMatches(content, methodPattern)) {
|
|
412
|
+
if (!knownMethods.has(method)) {
|
|
413
|
+
unknownInitSetupVariableMethods.push(`${binding.variable}.${method}`);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
278
417
|
const unknown = [
|
|
279
418
|
...unknownPwProps.map((value) => `pw.${value}`),
|
|
280
419
|
...unknownBrowserMethods.map((value) => `pw.testBrowser.${value}`),
|
|
420
|
+
...unknownNestedPwMembers,
|
|
281
421
|
...unknownChannelMembers.map((value) => `channelsPage.${value}`),
|
|
282
422
|
...unknownSidebarMembers.map((value) => `channelsPage.sidebarRight.${value}`),
|
|
423
|
+
...unknownInitSetupKeys.map((value) => `pw.initSetup.{${value}}`),
|
|
424
|
+
...unknownInitSetupVariableMethods,
|
|
283
425
|
];
|
|
284
426
|
if (unknown.length > 0) {
|
|
285
427
|
issues.push({
|
|
@@ -450,11 +592,11 @@ function summarizeCommandOutput(stdout, stderr) {
|
|
|
450
592
|
const lines = combined.split('\n').slice(-20);
|
|
451
593
|
return lines.join('\n').slice(0, 2000);
|
|
452
594
|
}
|
|
453
|
-
function runCommand(command, args, cwd) {
|
|
595
|
+
function runCommand(command, args, cwd, timeoutMs = 60 * 60 * 1000) {
|
|
454
596
|
const result = (0, child_process_1.spawnSync)(command, args, {
|
|
455
597
|
cwd,
|
|
456
598
|
encoding: 'utf-8',
|
|
457
|
-
timeout:
|
|
599
|
+
timeout: timeoutMs,
|
|
458
600
|
stdio: 'pipe',
|
|
459
601
|
});
|
|
460
602
|
return {
|
|
@@ -464,6 +606,37 @@ function runCommand(command, args, cwd) {
|
|
|
464
606
|
error: result.error ? result.error.message : undefined,
|
|
465
607
|
};
|
|
466
608
|
}
|
|
609
|
+
function runPlaywrightRuntimeValidation(testsRoot, testFile, pipeline, playwrightBinary) {
|
|
610
|
+
if (!playwrightBinary) {
|
|
611
|
+
return {
|
|
612
|
+
status: 'failed',
|
|
613
|
+
detail: 'Playwright binary not found; cannot execute runtime validation.',
|
|
614
|
+
};
|
|
615
|
+
}
|
|
616
|
+
const relativeSpecPath = (0, utils_js_1.normalizePath)((0, path_1.relative)(testsRoot, testFile));
|
|
617
|
+
if (relativeSpecPath.startsWith('../') || relativeSpecPath.startsWith('..\\')) {
|
|
618
|
+
return {
|
|
619
|
+
status: 'failed',
|
|
620
|
+
detail: 'Generated spec path resolved outside testsRoot during runtime validation.',
|
|
621
|
+
};
|
|
622
|
+
}
|
|
623
|
+
const args = ['test', relativeSpecPath, '--workers', '1', '--retries', '0', '--max-failures', '1', '--reporter', 'line'];
|
|
624
|
+
if (pipeline.headless === false) {
|
|
625
|
+
args.push('--headed');
|
|
626
|
+
}
|
|
627
|
+
if (pipeline.project) {
|
|
628
|
+
args.push('--project', pipeline.project);
|
|
629
|
+
}
|
|
630
|
+
const commandResult = runCommand(playwrightBinary, args, testsRoot, 10 * 60 * 1000);
|
|
631
|
+
if (commandResult.status === 0) {
|
|
632
|
+
return { status: 'passed' };
|
|
633
|
+
}
|
|
634
|
+
const summary = summarizeCommandOutput(commandResult.stdout, commandResult.stderr);
|
|
635
|
+
return {
|
|
636
|
+
status: 'failed',
|
|
637
|
+
detail: summary || commandResult.error || `playwright test failed with status ${commandResult.status}`,
|
|
638
|
+
};
|
|
639
|
+
}
|
|
467
640
|
function runPlaywrightListValidation(testsRoot, testFile, pipeline, playwrightBinary) {
|
|
468
641
|
if (!playwrightBinary) {
|
|
469
642
|
return {
|
|
@@ -479,6 +652,9 @@ function runPlaywrightListValidation(testsRoot, testFile, pipeline, playwrightBi
|
|
|
479
652
|
};
|
|
480
653
|
}
|
|
481
654
|
const args = ['test', '--list', relativeSpecPath];
|
|
655
|
+
if (pipeline.headless === false) {
|
|
656
|
+
args.push('--headed');
|
|
657
|
+
}
|
|
482
658
|
if (pipeline.project) {
|
|
483
659
|
args.push('--project', pipeline.project);
|
|
484
660
|
}
|
|
@@ -668,12 +844,12 @@ function runTargetedSpecHeal(testsRoot, targets, pipeline) {
|
|
|
668
844
|
const mcp = createMcpStatus('package-native', Boolean(pipeline.mcp));
|
|
669
845
|
if (targets.length === 0) {
|
|
670
846
|
warnings.add('No targeted specs provided for heal.');
|
|
671
|
-
return {
|
|
847
|
+
return finalizePipelineSummary({
|
|
672
848
|
runner: 'package-native',
|
|
673
849
|
results,
|
|
674
850
|
warnings: Array.from(warnings),
|
|
675
851
|
mcp,
|
|
676
|
-
};
|
|
852
|
+
});
|
|
677
853
|
}
|
|
678
854
|
const playwrightBinary = pipeline.heal ? resolvePlaywrightBinary(testsRoot) : null;
|
|
679
855
|
const seedFile = resolveAgentSeedSpec(testsRoot) || 'specs/seed.spec.ts';
|
|
@@ -731,12 +907,12 @@ function runTargetedSpecHeal(testsRoot, targets, pipeline) {
|
|
|
731
907
|
const syntheticFlow = buildSyntheticFlowFromSpecTarget(relativeSpecPath, target);
|
|
732
908
|
results.push(runPackageNativeFlow(testsRoot, syntheticFlow, pipeline, (0, utils_js_1.normalizePath)((0, path_1.dirname)(absoluteSpecPath)), absoluteSpecPath, playwrightBinary, apiSurface));
|
|
733
909
|
}
|
|
734
|
-
return {
|
|
910
|
+
return finalizePipelineSummary({
|
|
735
911
|
runner: 'package-native',
|
|
736
912
|
results,
|
|
737
913
|
warnings: Array.from(warnings),
|
|
738
914
|
mcp,
|
|
739
|
-
};
|
|
915
|
+
});
|
|
740
916
|
}
|
|
741
917
|
function findSpecFiles(root) {
|
|
742
918
|
if (!(0, fs_1.existsSync)(root)) {
|
|
@@ -882,7 +1058,7 @@ function buildPlaywrightHealerPrompt(testFile, extra) {
|
|
|
882
1058
|
}
|
|
883
1059
|
return lines.join('\n');
|
|
884
1060
|
}
|
|
885
|
-
function runPlaywrightAgentsFlow(testsRoot, flow, pipeline, outputDir, preferredTestFile, seedFile, apiSurface, playwrightBinary
|
|
1061
|
+
function runPlaywrightAgentsFlow(testsRoot, flow, pipeline, outputDir, preferredTestFile, seedFile, apiSurface, playwrightBinary) {
|
|
886
1062
|
(0, fs_1.mkdirSync)(outputDir, { recursive: true });
|
|
887
1063
|
const slug = toSafeSlug(flow.id);
|
|
888
1064
|
const planFile = (0, utils_js_1.normalizePath)((0, path_1.relative)(testsRoot, (0, path_1.join)(outputDir, `${slug}.plan.md`)));
|
|
@@ -896,7 +1072,7 @@ function runPlaywrightAgentsFlow(testsRoot, flow, pipeline, outputDir, preferred
|
|
|
896
1072
|
healStatus: pipeline.heal ? 'skipped' : undefined,
|
|
897
1073
|
};
|
|
898
1074
|
}
|
|
899
|
-
const prompt = buildPlaywrightAgentsPrompt(flow, seedFile, planFile, targetTestFile,
|
|
1075
|
+
const prompt = buildPlaywrightAgentsPrompt(flow, seedFile, planFile, targetTestFile, Boolean(pipeline.heal));
|
|
900
1076
|
const runArgs = [
|
|
901
1077
|
'-p',
|
|
902
1078
|
'--permission-mode',
|
|
@@ -938,7 +1114,7 @@ function runPlaywrightAgentsFlow(testsRoot, flow, pipeline, outputDir, preferred
|
|
|
938
1114
|
}
|
|
939
1115
|
const relativeActualTestFile = (0, utils_js_1.normalizePath)((0, path_1.relative)(testsRoot, actualTestFile));
|
|
940
1116
|
let qualityIssues = validateGeneratedSpecContent((0, fs_1.readFileSync)(actualTestFile, 'utf-8'), apiSurface);
|
|
941
|
-
if (qualityIssues.length > 0 &&
|
|
1117
|
+
if (qualityIssues.length > 0 && pipeline.heal) {
|
|
942
1118
|
const healResult = runCommand('claude', [
|
|
943
1119
|
'-p',
|
|
944
1120
|
'--permission-mode',
|
|
@@ -966,9 +1142,9 @@ function runPlaywrightAgentsFlow(testsRoot, flow, pipeline, outputDir, preferred
|
|
|
966
1142
|
error: `Playwright agents produced invalid test content: ${qualityIssues.map((issue) => issue.message).join(' | ')}`,
|
|
967
1143
|
};
|
|
968
1144
|
}
|
|
969
|
-
if (
|
|
970
|
-
let
|
|
971
|
-
if (
|
|
1145
|
+
if (pipeline.heal) {
|
|
1146
|
+
let compileValidation = runPlaywrightListValidation(testsRoot, actualTestFile, pipeline, playwrightBinary);
|
|
1147
|
+
if (compileValidation.status === 'failed') {
|
|
972
1148
|
const healResult = runCommand('claude', [
|
|
973
1149
|
'-p',
|
|
974
1150
|
'--permission-mode',
|
|
@@ -980,19 +1156,48 @@ function runPlaywrightAgentsFlow(testsRoot, flow, pipeline, outputDir, preferred
|
|
|
980
1156
|
'--add-dir',
|
|
981
1157
|
testsRoot,
|
|
982
1158
|
'--',
|
|
983
|
-
buildPlaywrightHealerPrompt(relativeActualTestFile,
|
|
1159
|
+
buildPlaywrightHealerPrompt(relativeActualTestFile, compileValidation.detail || 'playwright --list failed'),
|
|
984
1160
|
], testsRoot);
|
|
985
1161
|
if (healResult.status === 0 && (0, fs_1.existsSync)(actualTestFile)) {
|
|
986
|
-
|
|
1162
|
+
compileValidation = runPlaywrightListValidation(testsRoot, actualTestFile, pipeline, playwrightBinary);
|
|
987
1163
|
}
|
|
988
|
-
if (
|
|
1164
|
+
if (compileValidation.status === 'failed') {
|
|
1165
|
+
return {
|
|
1166
|
+
flowId: flow.id,
|
|
1167
|
+
flowName: flow.name,
|
|
1168
|
+
generatedDir: outputDir,
|
|
1169
|
+
generateStatus: 'failed',
|
|
1170
|
+
healStatus: 'failed',
|
|
1171
|
+
error: `Playwright agents compile validation failed: ${compileValidation.detail || 'playwright --list failed'}`,
|
|
1172
|
+
};
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
let runtimeValidation = runPlaywrightRuntimeValidation(testsRoot, actualTestFile, pipeline, playwrightBinary);
|
|
1176
|
+
if (runtimeValidation.status === 'failed') {
|
|
1177
|
+
const healResult = runCommand('claude', [
|
|
1178
|
+
'-p',
|
|
1179
|
+
'--permission-mode',
|
|
1180
|
+
'bypassPermissions',
|
|
1181
|
+
'--agent',
|
|
1182
|
+
'playwright-test-healer',
|
|
1183
|
+
'--mcp-config',
|
|
1184
|
+
'.mcp.json',
|
|
1185
|
+
'--add-dir',
|
|
1186
|
+
testsRoot,
|
|
1187
|
+
'--',
|
|
1188
|
+
buildPlaywrightHealerPrompt(relativeActualTestFile, runtimeValidation.detail || 'playwright runtime failed'),
|
|
1189
|
+
], testsRoot);
|
|
1190
|
+
if (healResult.status === 0 && (0, fs_1.existsSync)(actualTestFile)) {
|
|
1191
|
+
runtimeValidation = runPlaywrightRuntimeValidation(testsRoot, actualTestFile, pipeline, playwrightBinary);
|
|
1192
|
+
}
|
|
1193
|
+
if (runtimeValidation.status === 'failed') {
|
|
989
1194
|
return {
|
|
990
1195
|
flowId: flow.id,
|
|
991
1196
|
flowName: flow.name,
|
|
992
1197
|
generatedDir: outputDir,
|
|
993
1198
|
generateStatus: 'failed',
|
|
994
1199
|
healStatus: 'failed',
|
|
995
|
-
error: `Playwright agents
|
|
1200
|
+
error: `Playwright agents runtime validation failed: ${runtimeValidation.detail || 'playwright test failed'}`,
|
|
996
1201
|
};
|
|
997
1202
|
}
|
|
998
1203
|
}
|
|
@@ -1034,15 +1239,11 @@ function runPlaywrightAgentsPipeline(testsRoot, flows, pipeline) {
|
|
|
1034
1239
|
warnings.push('No seed spec file found under specs/. Playwright planner cannot be initialized.');
|
|
1035
1240
|
return { runner: 'unknown', results, warnings, mcp: createMcpStatus('unknown', true) };
|
|
1036
1241
|
}
|
|
1037
|
-
const
|
|
1038
|
-
const playwrightBinary = allowRuntimeHeal ? resolvePlaywrightBinary(testsRoot) : null;
|
|
1242
|
+
const playwrightBinary = pipeline.heal ? resolvePlaywrightBinary(testsRoot) : null;
|
|
1039
1243
|
const apiSurface = buildApiSurfaceCatalog(testsRoot, seedFile);
|
|
1040
|
-
if (
|
|
1244
|
+
if (pipeline.heal && !playwrightBinary) {
|
|
1041
1245
|
warnings.push('Playwright binary was not found. Healer runtime validation may be limited.');
|
|
1042
1246
|
}
|
|
1043
|
-
if (pipeline.heal && !allowRuntimeHeal) {
|
|
1044
|
-
warnings.push('Skipping runtime healer in official MCP mode because no --pipeline-base-url was provided.');
|
|
1045
|
-
}
|
|
1046
1247
|
const outputBase = (0, path_1.resolve)(testsRoot, pipeline.outputDir || 'specs/functional/ai-assisted');
|
|
1047
1248
|
if (!(0, utils_js_1.isPathWithinRoot)(testsRoot, outputBase)) {
|
|
1048
1249
|
warnings.push(`Pipeline outputDir resolves outside testsRoot and was blocked: ${pipeline.outputDir}`);
|
|
@@ -1075,7 +1276,7 @@ function runPlaywrightAgentsPipeline(testsRoot, flows, pipeline) {
|
|
|
1075
1276
|
});
|
|
1076
1277
|
continue;
|
|
1077
1278
|
}
|
|
1078
|
-
results.push(runPlaywrightAgentsFlow(testsRoot, flow, pipeline, outputDir, testFile, seedFile, apiSurface, playwrightBinary
|
|
1279
|
+
results.push(runPlaywrightAgentsFlow(testsRoot, flow, pipeline, outputDir, testFile, seedFile, apiSurface, playwrightBinary));
|
|
1079
1280
|
}
|
|
1080
1281
|
return { runner: 'playwright-agents', results, warnings, mcp: createMcpStatus('playwright-agents', true) };
|
|
1081
1282
|
}
|
|
@@ -1084,32 +1285,37 @@ function runPlaywrightPipeline(testsRoot, flows, pipeline) {
|
|
|
1084
1285
|
if (pipeline.mcp) {
|
|
1085
1286
|
const agentsSummary = runPlaywrightAgentsPipeline(testsRoot, flows, pipeline);
|
|
1086
1287
|
if (agentsSummary.runner !== 'unknown' || agentsSummary.results.length > 0) {
|
|
1087
|
-
return agentsSummary;
|
|
1288
|
+
return finalizePipelineSummary(agentsSummary);
|
|
1088
1289
|
}
|
|
1089
1290
|
if (!pipeline.mcpAllowFallback) {
|
|
1090
1291
|
const warnings = [
|
|
1091
1292
|
...agentsSummary.warnings,
|
|
1092
1293
|
'Official Playwright MCP mode is strict; fallback generation is disabled unless pipeline.mcpAllowFallback=true.',
|
|
1093
1294
|
];
|
|
1094
|
-
return {
|
|
1295
|
+
return finalizePipelineSummary({
|
|
1095
1296
|
runner: 'unknown',
|
|
1096
1297
|
results: agentsSummary.results,
|
|
1097
1298
|
warnings,
|
|
1098
1299
|
mcp: createMcpStatus('unknown', true),
|
|
1099
|
-
};
|
|
1300
|
+
});
|
|
1100
1301
|
}
|
|
1101
1302
|
mcpFallbackWarnings.push(...agentsSummary.warnings);
|
|
1102
1303
|
}
|
|
1103
1304
|
const cliPath = hasE2eTestGenCLI(testsRoot);
|
|
1104
1305
|
if (!cliPath) {
|
|
1105
|
-
return runPackageNativePipeline(testsRoot, flows, pipeline, mcpFallbackWarnings);
|
|
1306
|
+
return finalizePipelineSummary(runPackageNativePipeline(testsRoot, flows, pipeline, mcpFallbackWarnings));
|
|
1106
1307
|
}
|
|
1107
1308
|
const warnings = [...mcpFallbackWarnings];
|
|
1108
1309
|
const results = [];
|
|
1109
1310
|
const outputBase = (0, path_1.resolve)(testsRoot, pipeline.outputDir || 'specs/functional/ai-assisted');
|
|
1110
1311
|
if (!(0, utils_js_1.isPathWithinRoot)(testsRoot, outputBase)) {
|
|
1111
1312
|
warnings.push(`Pipeline outputDir resolves outside testsRoot and was blocked: ${pipeline.outputDir}`);
|
|
1112
|
-
return {
|
|
1313
|
+
return finalizePipelineSummary({
|
|
1314
|
+
runner: 'unknown',
|
|
1315
|
+
results,
|
|
1316
|
+
warnings,
|
|
1317
|
+
mcp: createMcpStatus('unknown', Boolean(pipeline.mcp)),
|
|
1318
|
+
});
|
|
1113
1319
|
}
|
|
1114
1320
|
for (const flow of flows) {
|
|
1115
1321
|
if (flow.priority !== 'P0' && flow.priority !== 'P1') {
|
|
@@ -1205,5 +1411,10 @@ function runPlaywrightPipeline(testsRoot, flows, pipeline) {
|
|
|
1205
1411
|
healStatus,
|
|
1206
1412
|
});
|
|
1207
1413
|
}
|
|
1208
|
-
return {
|
|
1414
|
+
return finalizePipelineSummary({
|
|
1415
|
+
runner: 'e2e-test-gen',
|
|
1416
|
+
results,
|
|
1417
|
+
warnings,
|
|
1418
|
+
mcp: createMcpStatus('e2e-test-gen', Boolean(pipeline.mcp)),
|
|
1419
|
+
});
|
|
1209
1420
|
}
|