donobu 5.60.1 → 5.60.3
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/cli/donobu-cli.js +158 -52
- package/dist/envVars.d.ts +4 -0
- package/dist/envVars.js +12 -0
- package/dist/esm/cli/donobu-cli.js +158 -52
- package/dist/esm/envVars.d.ts +4 -0
- package/dist/esm/envVars.js +12 -0
- package/dist/esm/lib/page/extendPage.d.ts +6 -0
- package/dist/esm/lib/page/extendPage.js +24 -1
- package/dist/esm/lib/test/healRerunGate.d.ts +102 -0
- package/dist/esm/lib/test/healRerunGate.js +228 -0
- package/dist/esm/lib/test/testExtension.d.ts +1 -0
- package/dist/esm/lib/test/testExtension.js +20 -10
- package/dist/esm/managers/DonobuStack.d.ts +19 -19
- package/dist/esm/reporter/buildReport.js +54 -1
- package/dist/esm/reporter/merge.d.ts +1 -6
- package/dist/esm/reporter/merge.js +57 -35
- package/dist/esm/reporter/model.d.ts +16 -0
- package/dist/esm/reporter/model.js +10 -1
- package/dist/esm/reporter/render.js +34 -12
- package/dist/esm/reporter/renderMarkdown.js +148 -93
- package/dist/esm/reporter/renderSlack.js +39 -28
- package/dist/esm/reporter/reportWalk.d.ts +16 -6
- package/dist/esm/reporter/reportWalk.js +63 -13
- package/dist/esm/utils/BrowserUtils.d.ts +4 -4
- package/dist/lib/page/extendPage.d.ts +6 -0
- package/dist/lib/page/extendPage.js +24 -1
- package/dist/lib/test/healRerunGate.d.ts +102 -0
- package/dist/lib/test/healRerunGate.js +228 -0
- package/dist/lib/test/testExtension.d.ts +1 -0
- package/dist/lib/test/testExtension.js +20 -10
- package/dist/managers/DonobuStack.d.ts +19 -19
- package/dist/reporter/buildReport.js +54 -1
- package/dist/reporter/merge.d.ts +1 -6
- package/dist/reporter/merge.js +57 -35
- package/dist/reporter/model.d.ts +16 -0
- package/dist/reporter/model.js +10 -1
- package/dist/reporter/render.js +34 -12
- package/dist/reporter/renderMarkdown.js +148 -93
- package/dist/reporter/renderSlack.js +39 -28
- package/dist/reporter/reportWalk.d.ts +16 -6
- package/dist/reporter/reportWalk.js +63 -13
- package/dist/utils/BrowserUtils.d.ts +4 -4
- package/package.json +4 -4
package/dist/cli/donobu-cli.js
CHANGED
|
@@ -62,6 +62,7 @@ const path = __importStar(require("path"));
|
|
|
62
62
|
const v4_1 = require("zod/v4");
|
|
63
63
|
const envVars_1 = require("../envVars");
|
|
64
64
|
const gptClients_1 = require("../lib/test/fixtures/gptClients");
|
|
65
|
+
const healRerunGate_1 = require("../lib/test/healRerunGate");
|
|
65
66
|
const donobuTestStack_1 = require("../lib/test/utils/donobuTestStack");
|
|
66
67
|
const triageTestFailure_1 = require("../lib/test/utils/triageTestFailure");
|
|
67
68
|
const fileUploadWorkerRegistry_1 = require("../persistence/files/fileUploadWorkerRegistry");
|
|
@@ -73,6 +74,7 @@ const render_1 = require("../reporter/render");
|
|
|
73
74
|
const renderMarkdown_1 = require("../reporter/renderMarkdown");
|
|
74
75
|
const renderPullRequestBody_1 = require("../reporter/renderPullRequestBody");
|
|
75
76
|
const renderSlack_1 = require("../reporter/renderSlack");
|
|
77
|
+
const reportWalk_1 = require("../reporter/reportWalk");
|
|
76
78
|
const slack_1 = require("../reporter/slack");
|
|
77
79
|
const Logger_1 = require("../utils/Logger");
|
|
78
80
|
const FAILURE_EVIDENCE_PREFIX = 'failure-evidence-';
|
|
@@ -988,12 +990,21 @@ function evaluateAutoHealEligibility(plans) {
|
|
|
988
990
|
/**
|
|
989
991
|
* Coalesce directives from one or more treatment plans into a single Playwright
|
|
990
992
|
* invocation. Multiple failed tests can be healed in a single rerun, so we
|
|
991
|
-
* gather all relevant
|
|
993
|
+
* gather all relevant projects (or files, as a fallback) here.
|
|
994
|
+
*
|
|
995
|
+
* The rerun deliberately targets whole Playwright *projects*, not individual
|
|
996
|
+
* failed tests. Suites commonly chain ordered tests — checkpoint guards,
|
|
997
|
+
* serial files, cross-file prerequisites within a project — and a rerun that
|
|
998
|
+
* filters down to just the failed titles can never heal such a test: its
|
|
999
|
+
* prerequisite never runs, the test skips itself, and no fix is exercised.
|
|
1000
|
+
* The project is the unit where Playwright encodes that ordering (testMatch
|
|
1001
|
+
* order, workers, dependencies), so it is the narrowest scope that is always
|
|
1002
|
+
* safe to re-run. File narrowing is kept only as a fallback when no project
|
|
1003
|
+
* name could be resolved.
|
|
992
1004
|
*/
|
|
993
1005
|
function derivePlaywrightDirectiveArgs(descriptors) {
|
|
994
1006
|
const targetFiles = new Set();
|
|
995
1007
|
const targetProjects = new Set();
|
|
996
|
-
const targetTitles = new Set();
|
|
997
1008
|
const additionalArgs = [];
|
|
998
1009
|
for (const descriptor of descriptors) {
|
|
999
1010
|
const directives = descriptor.plan.automationDirectives;
|
|
@@ -1008,9 +1019,6 @@ function derivePlaywrightDirectiveArgs(descriptors) {
|
|
|
1008
1019
|
if (projectCandidate && !looksLikePath(projectCandidate)) {
|
|
1009
1020
|
targetProjects.add(projectCandidate);
|
|
1010
1021
|
}
|
|
1011
|
-
if (descriptor.testCase.title) {
|
|
1012
|
-
targetTitles.add(descriptor.testCase.title);
|
|
1013
|
-
}
|
|
1014
1022
|
if (directives.additionalPlaywrightArgs) {
|
|
1015
1023
|
directives.additionalPlaywrightArgs.forEach((arg) => {
|
|
1016
1024
|
additionalArgs.push(arg);
|
|
@@ -1018,20 +1026,11 @@ function derivePlaywrightDirectiveArgs(descriptors) {
|
|
|
1018
1026
|
}
|
|
1019
1027
|
}
|
|
1020
1028
|
return {
|
|
1021
|
-
files: Array.from(targetFiles),
|
|
1029
|
+
files: targetProjects.size > 0 ? [] : Array.from(targetFiles),
|
|
1022
1030
|
projects: Array.from(targetProjects),
|
|
1023
|
-
grepPattern: targetTitles.size > 0
|
|
1024
|
-
? Array.from(targetTitles)
|
|
1025
|
-
.map((title) => escapeRegex(title))
|
|
1026
|
-
.join('|')
|
|
1027
|
-
: undefined,
|
|
1028
1031
|
extras: additionalArgs,
|
|
1029
1032
|
};
|
|
1030
1033
|
}
|
|
1031
|
-
// We match test titles via `--grep`, so ensure literal characters are escaped.
|
|
1032
|
-
function escapeRegex(value) {
|
|
1033
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
1034
|
-
}
|
|
1035
1034
|
// Some teams name projects after directories (e.g. `projects/mobile`); treat those as file paths.
|
|
1036
1035
|
function looksLikePath(value) {
|
|
1037
1036
|
return value.includes('/') || value.includes('\\');
|
|
@@ -1057,21 +1056,34 @@ function extractOriginalFiles(args) {
|
|
|
1057
1056
|
}
|
|
1058
1057
|
/**
|
|
1059
1058
|
* Preserve most user-provided Playwright flags (e.g. `--config`, `--workers`).
|
|
1060
|
-
* We only strip flags we know we're going to replace (projects,
|
|
1059
|
+
* We only strip flags we know we're going to replace (projects, reporter).
|
|
1060
|
+
* The user's own `--grep` IS preserved: it scoped the initial run, so the heal
|
|
1061
|
+
* rerun must stay inside it (the rerun no longer adds a grep of its own).
|
|
1061
1062
|
*/
|
|
1062
1063
|
function extractPreservedOptions(args) {
|
|
1063
|
-
|
|
1064
|
+
const preserved = [];
|
|
1065
|
+
const rest = args.slice(1);
|
|
1066
|
+
for (let i = 0; i < rest.length; i += 1) {
|
|
1067
|
+
const arg = rest[i];
|
|
1064
1068
|
if (!arg.startsWith('--')) {
|
|
1065
|
-
|
|
1069
|
+
continue;
|
|
1066
1070
|
}
|
|
1067
|
-
const optionName = arg.
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1071
|
+
const optionName = arg.split('=')[0];
|
|
1072
|
+
if (optionName === '--project' || optionName === '--reporter') {
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
preserved.push(arg);
|
|
1076
|
+
// Space-separated flag values (`--grep pattern`) would otherwise be
|
|
1077
|
+
// dropped by the `--`-prefix filter above; carry the grep value along.
|
|
1078
|
+
if ((optionName === '--grep' || optionName === '--grep-invert') &&
|
|
1079
|
+
arg === optionName &&
|
|
1080
|
+
i + 1 < rest.length &&
|
|
1081
|
+
!rest[i + 1].startsWith('--')) {
|
|
1082
|
+
preserved.push(rest[i + 1]);
|
|
1083
|
+
i += 1;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
return preserved;
|
|
1075
1087
|
}
|
|
1076
1088
|
/**
|
|
1077
1089
|
* Merge the user's original Playwright command with the automation directives
|
|
@@ -1084,14 +1096,14 @@ function buildPlaywrightArgsWithDirectives(originalArgs, directives) {
|
|
|
1084
1096
|
: extractOriginalFiles(originalArgs);
|
|
1085
1097
|
const preservedOptions = extractPreservedOptions(originalArgs);
|
|
1086
1098
|
const projectArgs = directives.projects.map((project) => `--project=${project}`);
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1099
|
+
// When the rerun targets whole projects, file filters from the original
|
|
1100
|
+
// invocation would shrink the scope below the project's testMatch and can
|
|
1101
|
+
// exclude prerequisite tests — drop them; `--project` already narrows.
|
|
1102
|
+
const fileArgs = directives.projects.length > 0 ? [] : files;
|
|
1090
1103
|
const finalArgs = [
|
|
1091
1104
|
'test',
|
|
1092
|
-
...
|
|
1105
|
+
...fileArgs,
|
|
1093
1106
|
...projectArgs,
|
|
1094
|
-
...grepArgs,
|
|
1095
1107
|
...directives.extras,
|
|
1096
1108
|
...preservedOptions,
|
|
1097
1109
|
];
|
|
@@ -1317,12 +1329,28 @@ async function attemptAutoHealRun(params) {
|
|
|
1317
1329
|
let healExitCode = params.currentExitCode;
|
|
1318
1330
|
const healOptions = {
|
|
1319
1331
|
...params.options,
|
|
1320
|
-
clearAiCache: params.options.clearAiCache || evaluation.clearPageAiCache === true,
|
|
1321
1332
|
autoHeal: false,
|
|
1322
1333
|
triageOutputDir: staging.triageBaseDir,
|
|
1323
1334
|
};
|
|
1324
|
-
|
|
1325
|
-
|
|
1335
|
+
// Heal targets, expanded with their declared `describe.serial` siblings so
|
|
1336
|
+
// the rerun executes the whole declared group (Playwright's own retry
|
|
1337
|
+
// semantics for serial chains). The serial flags come from the initial
|
|
1338
|
+
// run's Donobu report; without it, targets run unexpanded.
|
|
1339
|
+
const healTargets = (0, healRerunGate_1.expandTargetsWithSerialCompanions)(evaluation.eligiblePlans.map((record) => ({
|
|
1340
|
+
file: record.evidence.failureContext.testCase.file ?? '',
|
|
1341
|
+
title: record.evidence.failureContext.testCase.title,
|
|
1342
|
+
projectName: record.evidence.failureContext.testCase.projectName,
|
|
1343
|
+
})), params.initialReport ?? null);
|
|
1344
|
+
// Cache invalidation is scoped to the heal targets' spec files: serial
|
|
1345
|
+
// prerequisites and any other re-running test keep their fast cache replay.
|
|
1346
|
+
// The user's own `--clear-ai-cache` flag stays run-wide.
|
|
1347
|
+
const clearCacheFiles = evaluation.clearPageAiCache && !params.options.clearAiCache
|
|
1348
|
+
? Array.from(new Set(healTargets
|
|
1349
|
+
.map((target) => target.file)
|
|
1350
|
+
.filter((file) => Boolean(file))))
|
|
1351
|
+
: [];
|
|
1352
|
+
if (clearCacheFiles.length > 0) {
|
|
1353
|
+
Logger_1.appLogger.info(`Auto-heal: clearing Page.AI cache for ${clearCacheFiles.length} target spec file(s) as recommended by the treatment plan.`);
|
|
1326
1354
|
}
|
|
1327
1355
|
const healArgsWithDirectives = buildPlaywrightArgsWithDirectives(params.playwrightArgs, evaluation.directives);
|
|
1328
1356
|
const healArgsForRun = overrideOutputDir(healArgsWithDirectives, staging.playwrightOutputDir);
|
|
@@ -1346,7 +1374,22 @@ async function attemptAutoHealRun(params) {
|
|
|
1346
1374
|
applyJsonReportEnv(envOverrides, staging.playwrightOutputDir);
|
|
1347
1375
|
// Flag downstream systems so they know this invocation came from auto-heal.
|
|
1348
1376
|
envOverrides.DONOBU_AUTO_HEAL_ACTIVE = '1';
|
|
1349
|
-
|
|
1377
|
+
if (clearCacheFiles.length > 0) {
|
|
1378
|
+
envOverrides.DONOBU_PAGE_AI_CLEAR_CACHE_FILES =
|
|
1379
|
+
JSON.stringify(clearCacheFiles);
|
|
1380
|
+
}
|
|
1381
|
+
// The rerun plan drives the runtime gate in the test fixture: within the
|
|
1382
|
+
// gated projects, only the heal targets (and their declared
|
|
1383
|
+
// `describe.serial` siblings) execute. Projects outside the gate —
|
|
1384
|
+
// declared dependencies of target projects, teardown projects — run in
|
|
1385
|
+
// full, exactly as Playwright schedules them.
|
|
1386
|
+
const healPlanPath = path.join(staging.rootDir, 'heal-rerun-plan.json');
|
|
1387
|
+
await fs_1.promises.writeFile(healPlanPath, JSON.stringify({
|
|
1388
|
+
targets: healTargets,
|
|
1389
|
+
gatedProjects: (0, healRerunGate_1.computeGatedProjects)(healTargets.map((target) => target.projectName), params.initialReport?.metadata?.projectDependencies),
|
|
1390
|
+
}), 'utf8');
|
|
1391
|
+
envOverrides.DONOBU_AUTO_HEAL_PLAN_PATH = healPlanPath;
|
|
1392
|
+
Logger_1.appLogger.info(`Auto-heal: re-running ${healTargets.length} targeted test(s) from ${evaluation.eligiblePlans.length} treatment plan(s)...`);
|
|
1350
1393
|
const healJsonReportPath = path.join(staging.playwrightOutputDir, PLAYWRIGHT_JSON_REPORT_FILENAME);
|
|
1351
1394
|
const reporterSetup = await ensureJsonReporter(healArgsForRun, {
|
|
1352
1395
|
jsonOutputFile: healJsonReportPath,
|
|
@@ -1396,6 +1439,30 @@ async function attemptAutoHealRun(params) {
|
|
|
1396
1439
|
// Prefer the heal reporter's state file; fall back to Playwright's JSON
|
|
1397
1440
|
// reporter output for configs that don't wire up the Donobu reporter.
|
|
1398
1441
|
const healReport = await loadDonobuReportForMerge(staging.playwrightOutputDir, healReportCopy?.destinationPath ?? undefined);
|
|
1442
|
+
// A heal rerun "succeeds" only when every test it set out to heal
|
|
1443
|
+
// actually passed in the rerun. The rerun's exit code alone can't tell:
|
|
1444
|
+
// a target that skipped itself (e.g. a precondition guard fired) exits
|
|
1445
|
+
// zero, and the project-scope rerun includes unrelated tests. Matched on
|
|
1446
|
+
// title + project — file paths differ in shape between treatment plans
|
|
1447
|
+
// (absolute) and reports (rootDir-relative).
|
|
1448
|
+
if (healExitCode === 0) {
|
|
1449
|
+
const unhealedTargets = evaluation.eligiblePlans.filter((record) => {
|
|
1450
|
+
const testCase = record.evidence.failureContext.testCase;
|
|
1451
|
+
const healTest = findTestInReport(healReport, testCase.title, testCase.projectName);
|
|
1452
|
+
if (!healTest) {
|
|
1453
|
+
return true;
|
|
1454
|
+
}
|
|
1455
|
+
const status = (0, reportWalk_1.statusOf)(healTest);
|
|
1456
|
+
return status !== 'passed' && status !== 'flaky' && status !== 'healed';
|
|
1457
|
+
});
|
|
1458
|
+
if (unhealedTargets.length > 0) {
|
|
1459
|
+
const titles = unhealedTargets
|
|
1460
|
+
.map((record) => record.evidence.failureContext.testCase.title)
|
|
1461
|
+
.join('", "');
|
|
1462
|
+
Logger_1.appLogger.warn(`Auto-heal rerun did not fix ${unhealedTargets.length} targeted test(s): "${titles}" (failed again, or never re-attempted because a precondition was missing). Keeping failing status.`);
|
|
1463
|
+
healExitCode = 1;
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1399
1466
|
if (params.initialReport || healReport) {
|
|
1400
1467
|
// Write the merged report directly to the user's JSON reporter target
|
|
1401
1468
|
// when one exists. When the user did not configure a JSON reporter,
|
|
@@ -1409,20 +1476,15 @@ async function attemptAutoHealRun(params) {
|
|
|
1409
1476
|
healReport,
|
|
1410
1477
|
healReportSourcePath: healReportCopy?.destinationPath ?? undefined,
|
|
1411
1478
|
mergedReportPath: params.userJsonOutputFile ?? undefined,
|
|
1412
|
-
healedTests: evaluation.eligiblePlans.map((record) => ({
|
|
1413
|
-
plan: record.plan,
|
|
1414
|
-
testCase: record.evidence.failureContext.testCase,
|
|
1415
|
-
})),
|
|
1416
|
-
healSucceeded: healExitCode === 0,
|
|
1417
1479
|
outputDir: params.playwrightOutputDir,
|
|
1418
1480
|
triageRunDir: params.triageRunDir,
|
|
1419
1481
|
});
|
|
1420
1482
|
if (mergedReport) {
|
|
1421
1483
|
await regenerateDonobuReports(mergedReport);
|
|
1422
1484
|
await writeAutoHealPullRequestBody(mergedReport, params.playwrightOutputDir);
|
|
1423
|
-
// The heal rerun only re-runs the
|
|
1424
|
-
//
|
|
1425
|
-
//
|
|
1485
|
+
// The heal rerun only re-runs the projects containing healable tests,
|
|
1486
|
+
// so `healExitCode` is blind to failures triage declined to retry
|
|
1487
|
+
// (e.g. real product bugs). Re-derive the status from the merged
|
|
1426
1488
|
// report so those remaining failures still fail CI. We only escalate
|
|
1427
1489
|
// (never downgrade a non-zero heal exit) to preserve infra/crash codes.
|
|
1428
1490
|
const remainingFailures = countUnexpectedTests(mergedReport);
|
|
@@ -1450,6 +1512,43 @@ function countUnexpectedTests(report) {
|
|
|
1450
1512
|
const unexpected = stats?.unexpected;
|
|
1451
1513
|
return typeof unexpected === 'number' ? unexpected : undefined;
|
|
1452
1514
|
}
|
|
1515
|
+
/**
|
|
1516
|
+
* Find a test entry in a report by title + project name. File paths are
|
|
1517
|
+
* deliberately not part of the lookup: treatment plans carry absolute paths
|
|
1518
|
+
* while reports carry rootDir-relative ones, and title + project is already
|
|
1519
|
+
* how Playwright disambiguates tests within a run.
|
|
1520
|
+
*/
|
|
1521
|
+
function findTestInReport(report, title, projectName) {
|
|
1522
|
+
if (!report?.suites || !title) {
|
|
1523
|
+
return null;
|
|
1524
|
+
}
|
|
1525
|
+
const visitSuite = (suite) => {
|
|
1526
|
+
for (const spec of suite.specs ?? []) {
|
|
1527
|
+
if (spec.title !== title) {
|
|
1528
|
+
continue;
|
|
1529
|
+
}
|
|
1530
|
+
for (const test of spec.tests ?? []) {
|
|
1531
|
+
if (!projectName || (test.projectName ?? '') === projectName) {
|
|
1532
|
+
return test;
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
for (const child of suite.suites ?? []) {
|
|
1537
|
+
const found = visitSuite(child);
|
|
1538
|
+
if (found) {
|
|
1539
|
+
return found;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return null;
|
|
1543
|
+
};
|
|
1544
|
+
for (const suite of report.suites) {
|
|
1545
|
+
const found = visitSuite(suite);
|
|
1546
|
+
if (found) {
|
|
1547
|
+
return found;
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1453
1552
|
/**
|
|
1454
1553
|
* Filename of the auto-heal PR body artifact. Lands in the Playwright output
|
|
1455
1554
|
* directory alongside the merged JSON / HTML / Markdown reports so the
|
|
@@ -1667,8 +1766,6 @@ async function mergePlaywrightJsonReports(params) {
|
|
|
1667
1766
|
const merged = (0, merge_1.mergeReports)({
|
|
1668
1767
|
initialReport: params.initialReport,
|
|
1669
1768
|
healReport: params.healReport,
|
|
1670
|
-
healedTests: params.healedTests,
|
|
1671
|
-
healSucceeded: params.healSucceeded,
|
|
1672
1769
|
triageRunDir: params.triageRunDir,
|
|
1673
1770
|
initialReportSourcePath: params.initialReportSourcePath,
|
|
1674
1771
|
healReportSourcePath: params.healReportSourcePath,
|
|
@@ -2085,6 +2182,22 @@ async function runHealCommand(cliArgs) {
|
|
|
2085
2182
|
applyJsonReportEnv(envOverrides, playwrightOutputDir);
|
|
2086
2183
|
// Downstream hooks check this flag to avoid recursive auto-heal loops.
|
|
2087
2184
|
envOverrides.DONOBU_AUTO_HEAL_ACTIVE = '1';
|
|
2185
|
+
// Same runtime gating as the automatic rerun: only the plan's test (plus
|
|
2186
|
+
// declared `describe.serial` siblings) executes within its own project;
|
|
2187
|
+
// dependency projects run in full. Without an initial report there is no
|
|
2188
|
+
// dependency graph, so the gate scopes to just the target's project.
|
|
2189
|
+
const healPlanPath = path.join(os.tmpdir(), `donobu-heal-rerun-plan-${Date.now()}.json`);
|
|
2190
|
+
await fs_1.promises.writeFile(healPlanPath, JSON.stringify({
|
|
2191
|
+
targets: [
|
|
2192
|
+
{
|
|
2193
|
+
file: persisted.failure.testCase.file,
|
|
2194
|
+
title: persisted.failure.testCase.title,
|
|
2195
|
+
projectName: persisted.failure.testCase.projectName,
|
|
2196
|
+
},
|
|
2197
|
+
],
|
|
2198
|
+
gatedProjects: (0, healRerunGate_1.computeGatedProjects)([persisted.failure.testCase.projectName], undefined),
|
|
2199
|
+
}), 'utf8');
|
|
2200
|
+
envOverrides.DONOBU_AUTO_HEAL_PLAN_PATH = healPlanPath;
|
|
2088
2201
|
Logger_1.appLogger.info(`Re-running Playwright using treatment plan at ${parsed.planPath}...`);
|
|
2089
2202
|
const healJsonReportPath = path.join(playwrightOutputDir, PLAYWRIGHT_JSON_REPORT_FILENAME);
|
|
2090
2203
|
const reporterSetup = await ensureJsonReporter(healArgsWithDirectives, {
|
|
@@ -2123,13 +2236,6 @@ async function runHealCommand(cliArgs) {
|
|
|
2123
2236
|
healReport,
|
|
2124
2237
|
healReportSourcePath: healReportCopy?.destinationPath ?? undefined,
|
|
2125
2238
|
mergedReportPath,
|
|
2126
|
-
healedTests: [
|
|
2127
|
-
{
|
|
2128
|
-
plan: persisted.plan,
|
|
2129
|
-
testCase: persisted.failure.testCase,
|
|
2130
|
-
},
|
|
2131
|
-
],
|
|
2132
|
-
healSucceeded: exitCode === 0,
|
|
2133
2239
|
outputDir: path.dirname(mergedReportPath),
|
|
2134
2240
|
});
|
|
2135
2241
|
if (mergedReport) {
|
package/dist/envVars.d.ts
CHANGED
|
@@ -59,6 +59,8 @@ export declare const env: Env<{
|
|
|
59
59
|
true: "true";
|
|
60
60
|
false: "false";
|
|
61
61
|
}>>;
|
|
62
|
+
DONOBU_PAGE_AI_CLEAR_CACHE_FILES: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
63
|
+
DONOBU_AUTO_HEAL_PLAN_PATH: z.ZodOptional<z.ZodString>;
|
|
62
64
|
GOOGLE_GENERATIVE_AI_API_KEY: z.ZodOptional<z.ZodString>;
|
|
63
65
|
GOOGLE_GENERATIVE_AI_MODEL_NAME: z.ZodOptional<z.ZodString>;
|
|
64
66
|
OLLAMA_MODEL_NAME: z.ZodOptional<z.ZodString>;
|
|
@@ -116,6 +118,8 @@ export declare const env: Env<{
|
|
|
116
118
|
CI_COMMIT_REF_NAME?: string | undefined;
|
|
117
119
|
PLAYWRIGHT_JSON_OUTPUT_DIR?: string | undefined;
|
|
118
120
|
DONOBU_PAGE_AI_CLEAR_CACHE?: "0" | "1" | "true" | "false" | undefined;
|
|
121
|
+
DONOBU_PAGE_AI_CLEAR_CACHE_FILES?: string[] | undefined;
|
|
122
|
+
DONOBU_AUTO_HEAL_PLAN_PATH?: string | undefined;
|
|
119
123
|
GOOGLE_GENERATIVE_AI_API_KEY?: string | undefined;
|
|
120
124
|
GOOGLE_GENERATIVE_AI_MODEL_NAME?: string | undefined;
|
|
121
125
|
OLLAMA_MODEL_NAME?: string | undefined;
|
package/dist/envVars.js
CHANGED
|
@@ -128,6 +128,18 @@ re-render reports from merged data).`),
|
|
|
128
128
|
bypass and invalidate Page.AI cache entries for the current run. The Donobu
|
|
129
129
|
CLI sets this automatically when invoked with \`--clear-ai-cache\` so a retry
|
|
130
130
|
always regenerates selectors from scratch.`),
|
|
131
|
+
DONOBU_PAGE_AI_CLEAR_CACHE_FILES: v4_1.z.array(v4_1.z.string()).optional()
|
|
132
|
+
.describe(`Spec file paths whose Page.AI cache entries should be bypassed and
|
|
133
|
+
invalidated for the current run (JSON-encoded on the wire). Set by the
|
|
134
|
+
auto-heal orchestrator so the rerun regenerates selectors only for the spec
|
|
135
|
+
files that contain heal targets, while every other re-running test keeps its
|
|
136
|
+
fast deterministic cache replay.`),
|
|
137
|
+
DONOBU_AUTO_HEAL_PLAN_PATH: v4_1.z.string().optional()
|
|
138
|
+
.describe(`Path to the auto-heal rerun plan (JSON) written by the Donobu CLI before the
|
|
139
|
+
heal rerun. When set, a Donobu auto fixture skips every test that is not part
|
|
140
|
+
of the plan (heal targets and, for targets inside a \`describe.serial\` group,
|
|
141
|
+
their serial siblings) before any browser fixture initializes, so the rerun
|
|
142
|
+
only pays for what the heal actually needs.`),
|
|
131
143
|
GOOGLE_GENERATIVE_AI_API_KEY: v4_1.z.string().optional()
|
|
132
144
|
.describe(`Automatically create GPT configurations for Google Gemini using this API key.
|
|
133
145
|
For convenience, the created configuration names will reflect the
|