@decocms/parity 0.17.1 → 0.17.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/CHANGELOG.md +6 -0
- package/dist/cli.js +52 -29
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.17.3](https://github.com/decocms/parity/compare/v0.17.2...v0.17.3) (2026-07-28)
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
* **`open-minicart` fallback click when already-open drawer is empty (issue #159).** Sites that use a react-query on-demand cart (query `enabled` only when a cart-intent signal is `true`) never hydrate the drawer when it opens via an add-to-cart toast side-effect — the intent signal is only set on an explicit cart-icon click, so the panel renders empty and `validateCartContainsTitle` finds 0 items. When `cartOpenMethod === "already-open"` and validation returns `found=false`, parity now looks up the `minicartTrigger` and clicks it explicitly (mirroring what the user does), waits for cart hydration, then re-validates. The fallback is skipped entirely when the drawer hydrates normally via the initial validation.
|
|
15
|
+
|
|
10
16
|
## [0.17.0](https://github.com/decocms/parity/compare/v0.16.0...v0.17.0) (2026-07-28)
|
|
11
17
|
|
|
12
18
|
### Added
|
package/dist/cli.js
CHANGED
|
@@ -51258,6 +51258,16 @@ async function validateCartContainsTitle(page, expectedTitle, ctx) {
|
|
|
51258
51258
|
await page.waitForTimeout(2000);
|
|
51259
51259
|
observed = await sweepTitles();
|
|
51260
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
|
+
}
|
|
51261
51271
|
dlog2(ctx, ` validateCartContainsTitle: observed ${observed.length} titles`);
|
|
51262
51272
|
if (observed.length === 0) {
|
|
51263
51273
|
return { found: false, observedTitles: [], method: "none" };
|
|
@@ -52036,49 +52046,62 @@ async function flowPurchaseJourney(ctx) {
|
|
|
52036
52046
|
const beforeUrl7 = page.url();
|
|
52037
52047
|
const spBefore7 = screenshotPath(ctx, "pj-7-minicart-before");
|
|
52038
52048
|
await screenshotStable(page, { path: spBefore7, fullPage: false });
|
|
52039
|
-
let miniHit = await firstVisibleLocator(page, selFor(ctx, "minicartTrigger"));
|
|
52040
|
-
let miniRecovered = false;
|
|
52041
|
-
if (!miniHit && budget.remaining > 0) {
|
|
52042
|
-
const recovery = await attemptRecovery(page, ctx, "open-minicart", "Abrir o minicart/drawer do carrinho", selFor(ctx, "minicartTrigger"));
|
|
52043
|
-
if (recovery) {
|
|
52044
|
-
miniHit = recovery;
|
|
52045
|
-
miniRecovered = true;
|
|
52046
|
-
budget.remaining--;
|
|
52047
|
-
}
|
|
52048
|
-
}
|
|
52049
52049
|
let cartOpenMethod = "failed";
|
|
52050
52050
|
let miniText = "";
|
|
52051
52051
|
let cartRevealMode = "unknown";
|
|
52052
52052
|
let revealDiagnostics;
|
|
52053
52053
|
const drawerAlreadyOpen = await isCartRevealed(page, expectedProductTitle, ctx) !== null;
|
|
52054
|
-
|
|
52055
|
-
|
|
52056
|
-
|
|
52057
|
-
|
|
52058
|
-
|
|
52059
|
-
|
|
52060
|
-
|
|
52061
|
-
|
|
52062
|
-
|
|
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"));
|
|
52063
52064
|
if (recovery) {
|
|
52064
|
-
budget.remaining--;
|
|
52065
52065
|
miniHit = recovery;
|
|
52066
52066
|
miniRecovered = true;
|
|
52067
|
-
|
|
52068
|
-
cartOpenMethod = openResult.method;
|
|
52069
|
-
revealDiagnostics = openResult.diagnostics;
|
|
52067
|
+
budget.remaining--;
|
|
52070
52068
|
}
|
|
52071
52069
|
}
|
|
52072
|
-
|
|
52073
|
-
|
|
52074
|
-
|
|
52075
|
-
|
|
52076
|
-
|
|
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
|
+
}
|
|
52077
52088
|
}
|
|
52078
52089
|
}
|
|
52079
52090
|
let step7Validation;
|
|
52080
52091
|
if (expectedProductTitle && cartOpenMethod !== "failed") {
|
|
52081
|
-
|
|
52092
|
+
let v = await validateCartContainsTitle(page, expectedProductTitle, ctx);
|
|
52093
|
+
if (cartOpenMethod === "already-open" && !v.found) {
|
|
52094
|
+
const triggerHit = await firstVisibleLocator(page, selFor(ctx, "minicartTrigger"));
|
|
52095
|
+
if (triggerHit) {
|
|
52096
|
+
dlog2(ctx, "step 7: already-open drawer empty — clicking minicartTrigger to activate on-demand query");
|
|
52097
|
+
miniHit = triggerHit;
|
|
52098
|
+
await triggerHit.locator.click({ timeout: 3000 }).catch(() => {
|
|
52099
|
+
return;
|
|
52100
|
+
});
|
|
52101
|
+
await waitForCartHydration(page);
|
|
52102
|
+
v = await validateCartContainsTitle(page, expectedProductTitle, ctx);
|
|
52103
|
+
}
|
|
52104
|
+
}
|
|
52082
52105
|
let reasonText;
|
|
52083
52106
|
if (!v.found) {
|
|
52084
52107
|
if (v.observedTitles.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/parity",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.3",
|
|
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",
|