@decocms/parity 0.17.0 → 0.17.2
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.js +83 -30
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -39020,6 +39020,21 @@ async function capturePage(page, opts) {
|
|
|
39020
39020
|
let xRobotsTag = null;
|
|
39021
39021
|
let vitals = null;
|
|
39022
39022
|
let html3 = "";
|
|
39023
|
+
let _resolveDisconnect;
|
|
39024
|
+
const disconnectPromise = new Promise((r) => {
|
|
39025
|
+
_resolveDisconnect = r;
|
|
39026
|
+
});
|
|
39027
|
+
const browser = page.context().browser();
|
|
39028
|
+
const handleBrowserDisconnect = () => {
|
|
39029
|
+
dlog(opts.side, opts.viewport, " capturePage: browser disconnected — aborting capture");
|
|
39030
|
+
_resolveDisconnect();
|
|
39031
|
+
};
|
|
39032
|
+
const handlePageCrash = () => {
|
|
39033
|
+
dlog(opts.side, opts.viewport, " capturePage: page crashed — aborting capture");
|
|
39034
|
+
_resolveDisconnect();
|
|
39035
|
+
};
|
|
39036
|
+
browser?.on("disconnected", handleBrowserDisconnect);
|
|
39037
|
+
page.on("crash", handlePageCrash);
|
|
39023
39038
|
const inner = async () => {
|
|
39024
39039
|
try {
|
|
39025
39040
|
dlog(opts.side, opts.viewport, ` capturePage: goto(${opts.url}) start`);
|
|
@@ -39130,7 +39145,7 @@ async function capturePage(page, opts) {
|
|
|
39130
39145
|
};
|
|
39131
39146
|
const SAFETY_MARGIN_MS = 1e4;
|
|
39132
39147
|
const outerDeadlineMs = overallBudgetMs + SAFETY_MARGIN_MS;
|
|
39133
|
-
|
|
39148
|
+
const result = await Promise.race([
|
|
39134
39149
|
inner(),
|
|
39135
39150
|
new Promise((resolve) => setTimeout(() => {
|
|
39136
39151
|
state.console.push({
|
|
@@ -39138,8 +39153,18 @@ async function capturePage(page, opts) {
|
|
|
39138
39153
|
text: `[capture-timeout] capturePage exceeded ${outerDeadlineMs}ms outer deadline — returning partial capture`
|
|
39139
39154
|
});
|
|
39140
39155
|
resolve(buildPartial());
|
|
39141
|
-
}, outerDeadlineMs))
|
|
39156
|
+
}, outerDeadlineMs)),
|
|
39157
|
+
disconnectPromise.then(() => {
|
|
39158
|
+
state.console.push({
|
|
39159
|
+
type: "error",
|
|
39160
|
+
text: "[browser-disconnected] browser/page crashed — returning partial capture"
|
|
39161
|
+
});
|
|
39162
|
+
return buildPartial();
|
|
39163
|
+
})
|
|
39142
39164
|
]);
|
|
39165
|
+
browser?.off("disconnected", handleBrowserDisconnect);
|
|
39166
|
+
page.off("crash", handlePageCrash);
|
|
39167
|
+
return result;
|
|
39143
39168
|
}
|
|
39144
39169
|
|
|
39145
39170
|
// src/report/html-template.ts
|
|
@@ -49348,6 +49373,7 @@ async function cssTraceCommand(opts) {
|
|
|
49348
49373
|
|
|
49349
49374
|
// src/commands/e2e.ts
|
|
49350
49375
|
import { writeFileSync as writeFileSync10 } from "node:fs";
|
|
49376
|
+
import { spawn } from "node:child_process";
|
|
49351
49377
|
import chalk8 from "chalk";
|
|
49352
49378
|
import ora4 from "ora";
|
|
49353
49379
|
|
|
@@ -51232,6 +51258,16 @@ async function validateCartContainsTitle(page, expectedTitle, ctx) {
|
|
|
51232
51258
|
await page.waitForTimeout(2000);
|
|
51233
51259
|
observed = await sweepTitles();
|
|
51234
51260
|
}
|
|
51261
|
+
if (observed.length === 0) {
|
|
51262
|
+
const panelSelectors = selFor(ctx, "minicartPanel");
|
|
51263
|
+
if (panelSelectors.length > 0) {
|
|
51264
|
+
dlog2(ctx, " validateCartContainsTitle: panel empty — waiting up to 5s for hydration");
|
|
51265
|
+
await Promise.race(panelSelectors.map((p) => page.waitForSelector(`${p} *`, { timeout: 5000 }).catch(() => {
|
|
51266
|
+
return;
|
|
51267
|
+
})));
|
|
51268
|
+
observed = await sweepTitles();
|
|
51269
|
+
}
|
|
51270
|
+
}
|
|
51235
51271
|
dlog2(ctx, ` validateCartContainsTitle: observed ${observed.length} titles`);
|
|
51236
51272
|
if (observed.length === 0) {
|
|
51237
51273
|
return { found: false, observedTitles: [], method: "none" };
|
|
@@ -52010,44 +52046,45 @@ async function flowPurchaseJourney(ctx) {
|
|
|
52010
52046
|
const beforeUrl7 = page.url();
|
|
52011
52047
|
const spBefore7 = screenshotPath(ctx, "pj-7-minicart-before");
|
|
52012
52048
|
await screenshotStable(page, { path: spBefore7, fullPage: false });
|
|
52013
|
-
let miniHit = await firstVisibleLocator(page, selFor(ctx, "minicartTrigger"));
|
|
52014
|
-
let miniRecovered = false;
|
|
52015
|
-
if (!miniHit && budget.remaining > 0) {
|
|
52016
|
-
const recovery = await attemptRecovery(page, ctx, "open-minicart", "Abrir o minicart/drawer do carrinho", selFor(ctx, "minicartTrigger"));
|
|
52017
|
-
if (recovery) {
|
|
52018
|
-
miniHit = recovery;
|
|
52019
|
-
miniRecovered = true;
|
|
52020
|
-
budget.remaining--;
|
|
52021
|
-
}
|
|
52022
|
-
}
|
|
52023
52049
|
let cartOpenMethod = "failed";
|
|
52024
52050
|
let miniText = "";
|
|
52025
52051
|
let cartRevealMode = "unknown";
|
|
52026
52052
|
let revealDiagnostics;
|
|
52027
52053
|
const drawerAlreadyOpen = await isCartRevealed(page, expectedProductTitle, ctx) !== null;
|
|
52028
|
-
|
|
52029
|
-
|
|
52030
|
-
|
|
52031
|
-
|
|
52032
|
-
|
|
52033
|
-
|
|
52034
|
-
|
|
52035
|
-
|
|
52036
|
-
|
|
52054
|
+
let miniHit = null;
|
|
52055
|
+
let miniRecovered = false;
|
|
52056
|
+
if (drawerAlreadyOpen) {
|
|
52057
|
+
cartOpenMethod = "already-open";
|
|
52058
|
+
cartRevealMode = "inline-notification";
|
|
52059
|
+
dlog2(ctx, "step 7 open-minicart: drawer already visible — skipping trigger lookup");
|
|
52060
|
+
} else {
|
|
52061
|
+
miniHit = await firstVisibleLocator(page, selFor(ctx, "minicartTrigger"));
|
|
52062
|
+
if (!miniHit && budget.remaining > 0) {
|
|
52063
|
+
const recovery = await attemptRecovery(page, ctx, "open-minicart", "Abrir o minicart/drawer do carrinho", selFor(ctx, "minicartTrigger"));
|
|
52037
52064
|
if (recovery) {
|
|
52038
|
-
budget.remaining--;
|
|
52039
52065
|
miniHit = recovery;
|
|
52040
52066
|
miniRecovered = true;
|
|
52041
|
-
|
|
52042
|
-
cartOpenMethod = openResult.method;
|
|
52043
|
-
revealDiagnostics = openResult.diagnostics;
|
|
52067
|
+
budget.remaining--;
|
|
52044
52068
|
}
|
|
52045
52069
|
}
|
|
52046
|
-
|
|
52047
|
-
|
|
52048
|
-
|
|
52049
|
-
|
|
52050
|
-
|
|
52070
|
+
if (miniHit) {
|
|
52071
|
+
miniText = await miniHit.locator.innerText().catch(() => "");
|
|
52072
|
+
cartRevealMode = await detectCartRevealMode(page, miniHit.locator, false, ctx.viewport).catch(() => "unknown");
|
|
52073
|
+
dlog2(ctx, `step 7 open-minicart: cartRevealMode=${cartRevealMode}`);
|
|
52074
|
+
let openResult = await openMinicart(page, miniHit, ctx, expectedProductTitle);
|
|
52075
|
+
cartOpenMethod = openResult.method;
|
|
52076
|
+
revealDiagnostics = openResult.diagnostics;
|
|
52077
|
+
if (cartOpenMethod === "failed" && revealDiagnostics && budget.remaining > 0) {
|
|
52078
|
+
const recovery = await attemptRecovery(page, ctx, "open-minicart", "Abrir o minicart/drawer do carrinho — o trigger foi clicado mas o drawer não revelou", [miniHit.selector], summarizeCartRevealDiagnostics(revealDiagnostics));
|
|
52079
|
+
if (recovery) {
|
|
52080
|
+
budget.remaining--;
|
|
52081
|
+
miniHit = recovery;
|
|
52082
|
+
miniRecovered = true;
|
|
52083
|
+
openResult = await openMinicart(page, miniHit, ctx, expectedProductTitle);
|
|
52084
|
+
cartOpenMethod = openResult.method;
|
|
52085
|
+
revealDiagnostics = openResult.diagnostics;
|
|
52086
|
+
}
|
|
52087
|
+
}
|
|
52051
52088
|
}
|
|
52052
52089
|
}
|
|
52053
52090
|
let step7Validation;
|
|
@@ -55147,6 +55184,19 @@ async function e2eCommand(opts) {
|
|
|
55147
55184
|
quiet: opts.json === true
|
|
55148
55185
|
});
|
|
55149
55186
|
}
|
|
55187
|
+
const E2E_TIMEOUT_MS = 3 * 60 * 1000;
|
|
55188
|
+
const watchdogScript = `setTimeout(()=>{try{process.kill(${process.pid},9)}catch(_){}},${E2E_TIMEOUT_MS})`;
|
|
55189
|
+
const watchdog = spawn(process.execPath, ["-e", watchdogScript], {
|
|
55190
|
+
stdio: "ignore",
|
|
55191
|
+
detached: false
|
|
55192
|
+
});
|
|
55193
|
+
watchdog.unref();
|
|
55194
|
+
const onSignal = () => {
|
|
55195
|
+
watchdog.kill();
|
|
55196
|
+
process.exit(1);
|
|
55197
|
+
};
|
|
55198
|
+
process.once("SIGINT", onSignal);
|
|
55199
|
+
process.once("SIGTERM", onSignal);
|
|
55150
55200
|
const spinner = opts.json ? null : ora4("Lançando browser…").start();
|
|
55151
55201
|
let browser = null;
|
|
55152
55202
|
const allFlows = [];
|
|
@@ -55190,6 +55240,9 @@ async function e2eCommand(opts) {
|
|
|
55190
55240
|
if (spinner)
|
|
55191
55241
|
spinner.succeed(`${allFlows.length} flow capture(s), ${allPages.length} página(s)`);
|
|
55192
55242
|
} finally {
|
|
55243
|
+
watchdog.kill();
|
|
55244
|
+
process.off("SIGINT", onSignal);
|
|
55245
|
+
process.off("SIGTERM", onSignal);
|
|
55193
55246
|
await browser?.close().catch(() => {
|
|
55194
55247
|
return;
|
|
55195
55248
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/parity",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.2",
|
|
4
4
|
"description": "E2E parity validator for site migrations. Compares prod vs cand and reports UI, functional, SEO, visual, and Web Vitals deltas with an LLM-ranked HTML report.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|