@decocms/parity 0.15.0 → 0.15.1
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 +4 -0
- package/dist/cli.js +36 -11
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
* **Configurable `minicartPanel` selector for open-minicart reveal detection (issue #149).** `isCartUiVisible` and the title-scope sweep in `validateCartContainsTitleQuick` used a hardcoded list of name-based patterns (`[role='dialog']`, `[class*='minicart']`, etc.) to detect that the cart drawer was open. Any site built with utility-CSS (Tailwind) and `data-qa-*` testing attributes — a common modern stack — has no class/attribute those patterns can match, so detection always returns `null` and the step reports "failed" even when the drawer is genuinely open. The new `minicartPanel` selector key (`.parityrc.json`) is tried first, ahead of all hardcoded patterns. Built-in defaults cover `[data-qa-minicart]`, `[data-minicart]`, `[data-minicart-drawer]`, `[data-testid='minicart']` and common class patterns — so the `data-qa-*` case is handled out-of-the-box. Override with a single selector that matches the drawer root for your specific site.
|
|
13
|
+
|
|
10
14
|
## [0.15.0](https://github.com/decocms/parity/compare/v0.14.0...v0.15.0) (2026-07-27)
|
|
11
15
|
|
|
12
16
|
### Added
|
package/dist/cli.js
CHANGED
|
@@ -41389,6 +41389,7 @@ var ParityRc = z.object({
|
|
|
41389
41389
|
quantityInput: z.string().optional(),
|
|
41390
41390
|
minicartCount: z.string().optional(),
|
|
41391
41391
|
cartOpenedIndicator: z.string().optional(),
|
|
41392
|
+
minicartPanel: z.string().optional(),
|
|
41392
41393
|
searchTrigger: z.string().optional(),
|
|
41393
41394
|
searchInput: z.string().optional(),
|
|
41394
41395
|
searchSuggestions: z.string().optional(),
|
|
@@ -49545,6 +49546,7 @@ var SelectorKey = z2.enum([
|
|
|
49545
49546
|
"quantityInput",
|
|
49546
49547
|
"minicartCount",
|
|
49547
49548
|
"cartOpenedIndicator",
|
|
49549
|
+
"minicartPanel",
|
|
49548
49550
|
"searchTrigger",
|
|
49549
49551
|
"searchInput",
|
|
49550
49552
|
"searchSuggestions",
|
|
@@ -49949,6 +49951,17 @@ var DEFAULT_SELECTORS = {
|
|
|
49949
49951
|
"[role='dialog']:has-text('adicionado')",
|
|
49950
49952
|
"[role='dialog']:has-text('added')"
|
|
49951
49953
|
],
|
|
49954
|
+
minicartPanel: [
|
|
49955
|
+
"[data-qa-minicart]",
|
|
49956
|
+
"[data-minicart]",
|
|
49957
|
+
"[data-minicart-drawer]",
|
|
49958
|
+
"[data-drawer='minicart']",
|
|
49959
|
+
"[data-testid='minicart']",
|
|
49960
|
+
"[data-testid='cart-drawer']",
|
|
49961
|
+
"[class*='minicart' i]",
|
|
49962
|
+
"[class*='cart-drawer' i]",
|
|
49963
|
+
"[class*='drawer-cart' i]"
|
|
49964
|
+
],
|
|
49952
49965
|
cartItemRow: [
|
|
49953
49966
|
"[data-cart-item]",
|
|
49954
49967
|
"[data-testid='cart-item']",
|
|
@@ -50750,8 +50763,10 @@ async function readCartCount(page, ctx) {
|
|
|
50750
50763
|
}
|
|
50751
50764
|
return 0;
|
|
50752
50765
|
}
|
|
50753
|
-
async function isCartUiVisible(page) {
|
|
50766
|
+
async function isCartUiVisible(page, ctx) {
|
|
50767
|
+
const configured = ctx ? [...selFor(ctx, "minicartPanel"), ...selFor(ctx, "cartOpenedIndicator")] : [];
|
|
50754
50768
|
return firstVisible(page, [
|
|
50769
|
+
...configured,
|
|
50755
50770
|
"[role='dialog']:visible",
|
|
50756
50771
|
"[aria-modal='true']:visible",
|
|
50757
50772
|
"[data-minicart][aria-hidden='false']",
|
|
@@ -50837,16 +50852,17 @@ async function detectCartRevealMode(page, trigger, drawerAlreadyOpen, viewport)
|
|
|
50837
50852
|
return "click-drawer";
|
|
50838
50853
|
return "unknown";
|
|
50839
50854
|
}
|
|
50840
|
-
async function isCartRevealed(page, expectedProductTitle) {
|
|
50855
|
+
async function isCartRevealed(page, expectedProductTitle, ctx) {
|
|
50841
50856
|
if (expectedProductTitle) {
|
|
50842
|
-
const v = await validateCartContainsTitleQuick(page, expectedProductTitle);
|
|
50857
|
+
const v = await validateCartContainsTitleQuick(page, expectedProductTitle, ctx);
|
|
50843
50858
|
if (v)
|
|
50844
50859
|
return `title-found:${v}`;
|
|
50845
50860
|
}
|
|
50846
|
-
return isCartUiVisible(page);
|
|
50861
|
+
return isCartUiVisible(page, ctx);
|
|
50847
50862
|
}
|
|
50848
|
-
async function validateCartContainsTitleQuick(page, expectedTitle) {
|
|
50863
|
+
async function validateCartContainsTitleQuick(page, expectedTitle, ctx) {
|
|
50849
50864
|
const quickSelectors = [
|
|
50865
|
+
...ctx ? selFor(ctx, "minicartPanel") : [],
|
|
50850
50866
|
"[role='dialog']",
|
|
50851
50867
|
"[class*='minicart' i]",
|
|
50852
50868
|
"[class*='cart' i]",
|
|
@@ -50883,7 +50899,7 @@ async function openMinicart(page, trigger, ctx, expectedProductTitle) {
|
|
|
50883
50899
|
const beforeUrl = page.url();
|
|
50884
50900
|
await dismissOverlays(page, ctx);
|
|
50885
50901
|
await page.waitForTimeout(800);
|
|
50886
|
-
const alreadyOpen = await isCartRevealed(page, expectedProductTitle);
|
|
50902
|
+
const alreadyOpen = await isCartRevealed(page, expectedProductTitle, ctx);
|
|
50887
50903
|
if (alreadyOpen) {
|
|
50888
50904
|
dlog2(ctx, ` openMinicart: already-open (matched ${alreadyOpen})`);
|
|
50889
50905
|
return { method: "already-open", url: beforeUrl, visibleMarker: alreadyOpen };
|
|
@@ -50896,7 +50912,7 @@ async function openMinicart(page, trigger, ctx, expectedProductTitle) {
|
|
|
50896
50912
|
return;
|
|
50897
50913
|
});
|
|
50898
50914
|
await page.waitForTimeout(1500);
|
|
50899
|
-
const hoverOpened = await isCartRevealed(page, expectedProductTitle);
|
|
50915
|
+
const hoverOpened = await isCartRevealed(page, expectedProductTitle, ctx);
|
|
50900
50916
|
if (hoverOpened) {
|
|
50901
50917
|
dlog2(ctx, ` openMinicart: hover opened drawer (${hoverOpened})`);
|
|
50902
50918
|
return { method: "hover", url: page.url(), visibleMarker: hoverOpened };
|
|
@@ -50921,7 +50937,7 @@ async function openMinicart(page, trigger, ctx, expectedProductTitle) {
|
|
|
50921
50937
|
return { method: "click-navigate", url: page.url(), visibleMarker: null };
|
|
50922
50938
|
}
|
|
50923
50939
|
await page.waitForTimeout(800);
|
|
50924
|
-
const tapOpened = await isCartRevealed(page, expectedProductTitle);
|
|
50940
|
+
const tapOpened = await isCartRevealed(page, expectedProductTitle, ctx);
|
|
50925
50941
|
if (tapOpened) {
|
|
50926
50942
|
dlog2(ctx, ` openMinicart: tap opened drawer (${tapOpened})`);
|
|
50927
50943
|
return { method: "click", url: page.url(), visibleMarker: tapOpened };
|
|
@@ -50946,7 +50962,7 @@ async function openMinicart(page, trigger, ctx, expectedProductTitle) {
|
|
|
50946
50962
|
return { method: "click-navigate", url: page.url(), visibleMarker: null };
|
|
50947
50963
|
}
|
|
50948
50964
|
await page.waitForTimeout(1500);
|
|
50949
|
-
const clickOpened = await isCartRevealed(page, expectedProductTitle);
|
|
50965
|
+
const clickOpened = await isCartRevealed(page, expectedProductTitle, ctx);
|
|
50950
50966
|
if (clickOpened) {
|
|
50951
50967
|
dlog2(ctx, ` openMinicart: click opened drawer (${clickOpened})`);
|
|
50952
50968
|
return { method: "click", url: afterClickUrl, visibleMarker: clickOpened };
|
|
@@ -50957,7 +50973,7 @@ async function openMinicart(page, trigger, ctx, expectedProductTitle) {
|
|
|
50957
50973
|
return;
|
|
50958
50974
|
});
|
|
50959
50975
|
await page.waitForTimeout(1500);
|
|
50960
|
-
const hoverOpened = await isCartRevealed(page, expectedProductTitle);
|
|
50976
|
+
const hoverOpened = await isCartRevealed(page, expectedProductTitle, ctx);
|
|
50961
50977
|
if (hoverOpened) {
|
|
50962
50978
|
dlog2(ctx, ` openMinicart: hover opened drawer (${hoverOpened})`);
|
|
50963
50979
|
return { method: "hover", url: page.url(), visibleMarker: hoverOpened };
|
|
@@ -51003,7 +51019,16 @@ function titlesMatch(observed, expected) {
|
|
|
51003
51019
|
return false;
|
|
51004
51020
|
}
|
|
51005
51021
|
async function validateCartContainsTitle(page, expectedTitle, ctx) {
|
|
51022
|
+
const panelScopedTitleSelectors = selFor(ctx, "minicartPanel").flatMap((p) => [
|
|
51023
|
+
`${p} a[href*='/p']`,
|
|
51024
|
+
`${p} [class*='name' i]`,
|
|
51025
|
+
`${p} [class*='title' i]`,
|
|
51026
|
+
`${p} [class*='product' i]`,
|
|
51027
|
+
`${p} [data-cart-item-name]`,
|
|
51028
|
+
`${p} [data-qa-product-name]`
|
|
51029
|
+
]);
|
|
51006
51030
|
const titleSelectors = [
|
|
51031
|
+
...panelScopedTitleSelectors,
|
|
51007
51032
|
"[data-cart-item-name]",
|
|
51008
51033
|
"[data-cart-item] [class*='title' i]",
|
|
51009
51034
|
"[data-cart-item] [class*='name' i]",
|
|
@@ -51861,7 +51886,7 @@ async function flowPurchaseJourney(ctx) {
|
|
|
51861
51886
|
let cartOpenMethod = "failed";
|
|
51862
51887
|
let miniText = "";
|
|
51863
51888
|
let cartRevealMode = "unknown";
|
|
51864
|
-
const drawerAlreadyOpen = await isCartRevealed(page, expectedProductTitle) !== null;
|
|
51889
|
+
const drawerAlreadyOpen = await isCartRevealed(page, expectedProductTitle, ctx) !== null;
|
|
51865
51890
|
if (miniHit) {
|
|
51866
51891
|
miniText = await miniHit.locator.innerText().catch(() => "");
|
|
51867
51892
|
cartRevealMode = await detectCartRevealMode(page, miniHit.locator, drawerAlreadyOpen, ctx.viewport).catch(() => "unknown");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/parity",
|
|
3
|
-
"version": "0.15.
|
|
3
|
+
"version": "0.15.1",
|
|
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",
|