@decocms/parity 0.13.0 → 0.14.0
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 +1 -0
- package/dist/cli.js +16 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
11
11
|
|
|
12
12
|
* **`parity e2e` now automates selectors like `parity run` (issue #141).** Single-site runs previously saw only `DEFAULT_SELECTORS` + hand-written `.parityrc.json` — `e2e` never detected the platform, never ran LLM selector discovery, and never learned from its flow runs (so `learned-selectors.json`, keyed by platform, never applied). It now detects the platform, runs the same grounded LLM discovery + live-validation pass, threads the platform into every flow, and promotes selectors learned from real successful interactions. New flags mirror `run`: `--no-auto-selectors`, `--refresh-selectors`, `--no-learn`. The discovery pass is now shared code (`src/engine/selector-discovery-pass.ts`) used by both commands.
|
|
13
13
|
* **Discovery covers the journey variant/quantity keys.** LLM selector discovery now infers `variantRow`, `quantityIncrement`, `quantityInput`, `sizeSwatch`, and `colorSwatch` (grounded on the real PDP and live-validated) — exactly the keys single-site users kept hand-writing in `.parityrc.json`.
|
|
14
|
+
* **Configurable add-to-cart confirmation deadline (issue #143).** The purchase-journey / `e2e` add-to-cart step polled a hardcoded 3000ms for a success signal (URL→cart, minicart count increase, drawer open, success toast). On sites whose success toast is short-lived, or with slow TTFB / popup overlays, that could race the deadline and report a false "no signal" failure on an add-to-cart that actually worked. Now tunable via `.parityrc.json` `addToCartConfirmMs` or `--add-to-cart-timeout <ms>` (on `parity run` and `parity e2e`); a non-positive/NaN override is ignored and falls back to the 3000ms default.
|
|
14
15
|
|
|
15
16
|
### Fixed
|
|
16
17
|
|
package/dist/cli.js
CHANGED
|
@@ -41433,7 +41433,8 @@ var ParityRc = z.object({
|
|
|
41433
41433
|
validCode: z.string().optional()
|
|
41434
41434
|
}).optional(),
|
|
41435
41435
|
serverFnFloodBudget: z.number().optional(),
|
|
41436
|
-
serverFnPattern: z.string().optional()
|
|
41436
|
+
serverFnPattern: z.string().optional(),
|
|
41437
|
+
addToCartConfirmMs: z.number().optional()
|
|
41437
41438
|
});
|
|
41438
41439
|
var ParityIgnore = z.object({
|
|
41439
41440
|
ignoreSelectorsVisual: z.array(z.string()).default([]),
|
|
@@ -51331,6 +51332,11 @@ async function countProductCards(page) {
|
|
|
51331
51332
|
|
|
51332
51333
|
// src/engine/flows/purchase-journey.ts
|
|
51333
51334
|
var PURCHASE_JOURNEY_TOTAL_STEPS = 9;
|
|
51335
|
+
var DEFAULT_ADD_TO_CART_CONFIRM_MS = 3000;
|
|
51336
|
+
function resolveAddToCartConfirmMs(rc) {
|
|
51337
|
+
const v = rc.addToCartConfirmMs;
|
|
51338
|
+
return typeof v === "number" && Number.isFinite(v) && v > 0 ? v : DEFAULT_ADD_TO_CART_CONFIRM_MS;
|
|
51339
|
+
}
|
|
51334
51340
|
async function flowPurchaseJourney(ctx) {
|
|
51335
51341
|
const pages = [];
|
|
51336
51342
|
const steps = [];
|
|
@@ -52075,7 +52081,7 @@ async function findPlusButtonNear(input) {
|
|
|
52075
52081
|
return null;
|
|
52076
52082
|
}
|
|
52077
52083
|
async function validateAddToCart(page, ctx, cartCountBefore, beforeUrl) {
|
|
52078
|
-
const deadline = Date.now() +
|
|
52084
|
+
const deadline = Date.now() + resolveAddToCartConfirmMs(ctx.rc);
|
|
52079
52085
|
let lastErrorText;
|
|
52080
52086
|
while (Date.now() < deadline) {
|
|
52081
52087
|
const currentUrl = page.url();
|
|
@@ -54708,6 +54714,9 @@ async function e2eCommand(opts) {
|
|
|
54708
54714
|
}
|
|
54709
54715
|
const rc = loadParityRc();
|
|
54710
54716
|
rc.cep = opts.cep || rc.cep;
|
|
54717
|
+
if (typeof opts.addToCartTimeout === "number" && Number.isFinite(opts.addToCartTimeout)) {
|
|
54718
|
+
rc.addToCartConfirmMs = opts.addToCartTimeout;
|
|
54719
|
+
}
|
|
54711
54720
|
if (opts.searchTerms) {
|
|
54712
54721
|
rc.search = {
|
|
54713
54722
|
...rc.search ?? {},
|
|
@@ -58746,6 +58755,9 @@ async function runCommand(rawOpts) {
|
|
|
58746
58755
|
const failOn = opts.failOn.split(",").map((s) => s.trim()).filter(Boolean);
|
|
58747
58756
|
const rc = loadParityRc();
|
|
58748
58757
|
rc.cep = opts.cep || rc.cep;
|
|
58758
|
+
if (typeof opts.addToCartTimeout === "number" && Number.isFinite(opts.addToCartTimeout)) {
|
|
58759
|
+
rc.addToCartConfirmMs = opts.addToCartTimeout;
|
|
58760
|
+
}
|
|
58749
58761
|
const ignore = loadParityIgnore();
|
|
58750
58762
|
if (opts.clearCache) {
|
|
58751
58763
|
const cacheFile = join18(opts.output, "cache", "verdicts.json");
|
|
@@ -60218,7 +60230,7 @@ program.command("run").description([
|
|
|
60218
60230
|
" auto-selectors=ON (if LLM), learn=ON, cache=ON, visual-diff=ON,",
|
|
60219
60231
|
" warmup=OFF, bypass-cache=OFF, ci=OFF."
|
|
60220
60232
|
].join(`
|
|
60221
|
-
`)).option("--prod <url>", "Production URL (source of truth, e.g. Fresh site). Omit for a single site — the run will point you to `parity e2e` (issue #141).").requiredOption("--cand <url>", "Candidate URL (migrated site, e.g. TanStack)").option("--preset <name>", "Bundle of defaults: smoke (fast, ~30s, no LLM) | full (deep audit) | ci (CI-tuned)").option("--flows <list>", "Comma-separated flows: homepage,plp,pdp,purchase-journey", "purchase-journey").option("--viewports <list>", "Comma-separated viewports: mobile,desktop", "mobile,desktop").option("--cep <cep>", "CEP for shipping calculation", "01310-100").option("--runs <n>", "Repeat each measurement N times (median)", "1").option("--baseline <name>", "Compare against a saved baseline").option("--output <dir>", "Output directory", "./parity-output").option("--ci", "CI mode: stricter exit codes", false).option("--fail-on <severities>", "Comma-separated severities that cause exit 1", "critical").option("--open", "Open the HTML report after the run completes", false).option("--no-auto-selectors", "Disable LLM-based selector discovery (uses defaults instead)").option("--refresh-selectors", "Bypass selector cache and re-run discovery", false).option("--no-learn", "Don't write to learned-selectors.json (read-only mode)").option("--vitals-pages <n>", "Extra pages from sitemap to crawl for Vitals coverage (default 10)", (v) => Number(v), 10).option("--max-viewport-concurrency <n>", "How many viewports run concurrently during collect (default 2: mobile+desktop together). Set to 1 if you hit OOM with many viewports.", (v) => Number(v), 2).option("--visual-pages <n>", "Pages to compare visually via LLM (home + sampled PLPs/PDPs from sitemap, default 5)", (v) => Number(v), 5).option("--pages <list>", 'Comma-separated paths to compare visually (overrides sitemap discovery). E.g. "/,/account,/p/some-product". Use this when you want deterministic coverage instead of sampled.').option("--pages-file <path>", "Read paths to compare visually from a text file (one path per line). Lines starting with # are ignored. Overrides --pages when both are present.").option("--no-visual-diff", "Skip the visual diff capture pass entirely").option("--no-cache", "Disable the cross-run visual-diff cache (forces a fresh LLM judgment on every page).").option("--clear-cache", "Wipe the visual-diff verdict cache (parity-output/cache/verdicts.json) before the run.", false).option("--bypass-cache", "Bypass CDN/edge caches: append a cache-busting query param and send Cache-Control: no-cache on every request. Use right after a deploy to avoid false failures from stale CF edge content.", false).option("--warmup", "Before measurement, hit each target URL once (per viewport) with a cache-buster so the Worker serves a fresh response. Recommended after deploys.", false).option("--accept-prod-quirks", "Demote prod-side cart-empty journey failures (VTEX session quirk) from failed to skipped. The cart-reveal-mode-divergence check still emits critical if prod/cand markup intents differ, so this flag never masks a real regression. See issue #12.", false).option("--llm-timeout <seconds>", "Hard timeout for the LLM aggregation call (seconds). The run completes in offline mode if the LLM hangs past this. Default: 60.", (v) => Number(v), 60).option("--timeout <minutes>", "Hard wall-clock cap for the whole run (minutes). On expiry, parity writes a partial report and exits 130. Default: 30. Issue #56.", (v) => Number(v), 30).option("--flow <name>", "Alias for --flows when running a single flow (e.g. --flow cart). Issue #53.").option("--json <path|->", "Emit JSON-Lines (one line per check) to the given file path, or '-' for stdout. Schema versioned via leading metadata line. Issue #53.").option("--pt", "Tell the LLM to respond in Brazilian Portuguese. Only affects LLM-generated content (issues, explain, prompts) — the static HTML report stays in English. Issue #67.").option("--llm <provider>", "Force LLM provider: anthropic | openrouter | claude-code | none. Default: auto-detect (anthropic key → openrouter key → local claude CLI → none). Issue #66.").option("--llm-model <overrides>", "Per-feature model override, e.g. visual-diff=claude-opus-4-7,explain=claude-opus-4-7. Features: selector-discovery, step-recovery, search-terms, plp-matching, pdp-matching, section-understanding, visual-diff, issue-aggregation, explain, component-detection. Issue #66.").option("--llm-tier-default <tier>", "Override the default tier (haiku | sonnet | opus) for every feature that doesn't have a per-feature override. Issue #66.").option("--llm-model-default <model>", "Force every LLM call to use this exact model ID, ignoring per-feature defaults. Issue #66.").option("--no-interactive", "Disable the interactive selector prompt that auto-fires in a TTY without an LLM provider. Use in scripts and CI where stdin is technically a TTY but you don't want to pause. Issue #72.").option("--only <modules>", "Scope the run to these modules and/or single checks (comma-separated). Module names: e2e, seo, visual, vitals, cache, console, html, network. Single-check granularity via `check:<name>`, e.g. `--only e2e,check:cache-coverage`. Run `parity list modules` for the full check/flow mapping. Default: all modules. M3 module selection.").option("--skip <modules>", "Subtract these modules and/or single checks (comma-separated, same syntax as --only) from whatever base set was chosen (all modules, or --only's set if both are given). M3 module selection.").option("--why <text>", "Free-text reason for this run's scope, stored in report.json as `selectionReason`. Purely informational. M3 module selection.").action(async (opts) => {
|
|
60233
|
+
`)).option("--prod <url>", "Production URL (source of truth, e.g. Fresh site). Omit for a single site — the run will point you to `parity e2e` (issue #141).").requiredOption("--cand <url>", "Candidate URL (migrated site, e.g. TanStack)").option("--preset <name>", "Bundle of defaults: smoke (fast, ~30s, no LLM) | full (deep audit) | ci (CI-tuned)").option("--flows <list>", "Comma-separated flows: homepage,plp,pdp,purchase-journey", "purchase-journey").option("--viewports <list>", "Comma-separated viewports: mobile,desktop", "mobile,desktop").option("--cep <cep>", "CEP for shipping calculation", "01310-100").option("--add-to-cart-timeout <ms>", "How long (ms) the add-to-cart step polls for a success signal before failing. Default 3000. Raise/lower to match your site's success-toast lifetime (issue #143).", (v) => Number(v)).option("--runs <n>", "Repeat each measurement N times (median)", "1").option("--baseline <name>", "Compare against a saved baseline").option("--output <dir>", "Output directory", "./parity-output").option("--ci", "CI mode: stricter exit codes", false).option("--fail-on <severities>", "Comma-separated severities that cause exit 1", "critical").option("--open", "Open the HTML report after the run completes", false).option("--no-auto-selectors", "Disable LLM-based selector discovery (uses defaults instead)").option("--refresh-selectors", "Bypass selector cache and re-run discovery", false).option("--no-learn", "Don't write to learned-selectors.json (read-only mode)").option("--vitals-pages <n>", "Extra pages from sitemap to crawl for Vitals coverage (default 10)", (v) => Number(v), 10).option("--max-viewport-concurrency <n>", "How many viewports run concurrently during collect (default 2: mobile+desktop together). Set to 1 if you hit OOM with many viewports.", (v) => Number(v), 2).option("--visual-pages <n>", "Pages to compare visually via LLM (home + sampled PLPs/PDPs from sitemap, default 5)", (v) => Number(v), 5).option("--pages <list>", 'Comma-separated paths to compare visually (overrides sitemap discovery). E.g. "/,/account,/p/some-product". Use this when you want deterministic coverage instead of sampled.').option("--pages-file <path>", "Read paths to compare visually from a text file (one path per line). Lines starting with # are ignored. Overrides --pages when both are present.").option("--no-visual-diff", "Skip the visual diff capture pass entirely").option("--no-cache", "Disable the cross-run visual-diff cache (forces a fresh LLM judgment on every page).").option("--clear-cache", "Wipe the visual-diff verdict cache (parity-output/cache/verdicts.json) before the run.", false).option("--bypass-cache", "Bypass CDN/edge caches: append a cache-busting query param and send Cache-Control: no-cache on every request. Use right after a deploy to avoid false failures from stale CF edge content.", false).option("--warmup", "Before measurement, hit each target URL once (per viewport) with a cache-buster so the Worker serves a fresh response. Recommended after deploys.", false).option("--accept-prod-quirks", "Demote prod-side cart-empty journey failures (VTEX session quirk) from failed to skipped. The cart-reveal-mode-divergence check still emits critical if prod/cand markup intents differ, so this flag never masks a real regression. See issue #12.", false).option("--llm-timeout <seconds>", "Hard timeout for the LLM aggregation call (seconds). The run completes in offline mode if the LLM hangs past this. Default: 60.", (v) => Number(v), 60).option("--timeout <minutes>", "Hard wall-clock cap for the whole run (minutes). On expiry, parity writes a partial report and exits 130. Default: 30. Issue #56.", (v) => Number(v), 30).option("--flow <name>", "Alias for --flows when running a single flow (e.g. --flow cart). Issue #53.").option("--json <path|->", "Emit JSON-Lines (one line per check) to the given file path, or '-' for stdout. Schema versioned via leading metadata line. Issue #53.").option("--pt", "Tell the LLM to respond in Brazilian Portuguese. Only affects LLM-generated content (issues, explain, prompts) — the static HTML report stays in English. Issue #67.").option("--llm <provider>", "Force LLM provider: anthropic | openrouter | claude-code | none. Default: auto-detect (anthropic key → openrouter key → local claude CLI → none). Issue #66.").option("--llm-model <overrides>", "Per-feature model override, e.g. visual-diff=claude-opus-4-7,explain=claude-opus-4-7. Features: selector-discovery, step-recovery, search-terms, plp-matching, pdp-matching, section-understanding, visual-diff, issue-aggregation, explain, component-detection. Issue #66.").option("--llm-tier-default <tier>", "Override the default tier (haiku | sonnet | opus) for every feature that doesn't have a per-feature override. Issue #66.").option("--llm-model-default <model>", "Force every LLM call to use this exact model ID, ignoring per-feature defaults. Issue #66.").option("--no-interactive", "Disable the interactive selector prompt that auto-fires in a TTY without an LLM provider. Use in scripts and CI where stdin is technically a TTY but you don't want to pause. Issue #72.").option("--only <modules>", "Scope the run to these modules and/or single checks (comma-separated). Module names: e2e, seo, visual, vitals, cache, console, html, network. Single-check granularity via `check:<name>`, e.g. `--only e2e,check:cache-coverage`. Run `parity list modules` for the full check/flow mapping. Default: all modules. M3 module selection.").option("--skip <modules>", "Subtract these modules and/or single checks (comma-separated, same syntax as --only) from whatever base set was chosen (all modules, or --only's set if both are given). M3 module selection.").option("--why <text>", "Free-text reason for this run's scope, stored in report.json as `selectionReason`. Purely informational. M3 module selection.").action(async (opts) => {
|
|
60222
60234
|
if (opts.flow) {
|
|
60223
60235
|
opts.flows = opts.flow;
|
|
60224
60236
|
}
|
|
@@ -60232,7 +60244,7 @@ program.command("audit").description("Single-site absolute audit. Runs console +
|
|
|
60232
60244
|
}
|
|
60233
60245
|
process.exit(await auditCommand(opts));
|
|
60234
60246
|
});
|
|
60235
|
-
program.command("e2e").description("Single-site functional end-to-end. Runs all functional flows (homepage, plp, pdp, purchase-journey, search, cart-interactions, optionally login) against ONE URL, then runs all checks in single-site mode. Use for 'does this site actually work?' verification — broader than `audit` (which only does vitals/console/network/images/seo).").requiredOption("--url <url>", "Base URL of the site to test").option("--flows <list>", "Comma-separated flows. Default: homepage,plp,pdp,purchase-journey,search,cart-interactions (login is opt-in)", "").option("--viewports <list>", "Comma-separated viewports", "mobile,desktop").option("--cep <cep>", "CEP for shipping calculation", "01310-100").option("--search-terms <list>", "Comma-separated search terms to use (override LLM auto-discovery)").option("--login-email <email>", "Login email (also PARITY_LOGIN_EMAIL env var)").option("--login-password <pwd>", "Login password (also PARITY_LOGIN_PASSWORD env var)").option("--output <dir>", "Output directory", "./parity-output").option("--open", "Open the HTML report after the run completes", false).option("--json", "Emit one-line JSON instead of pretty text", false).option("--no-auto-selectors", "Disable LLM-based selector discovery (uses defaults instead)").option("--refresh-selectors", "Bypass selector cache and re-run discovery", false).option("--no-learn", "Don't write to learned-selectors.json (read-only mode)").option("--fail-on <severities>", "Comma-separated severities that cause exit 1 (default: critical,high)", "critical,high").option("--pt", "Tell the LLM to respond in Brazilian Portuguese. Issue #67.").action(async (opts) => {
|
|
60247
|
+
program.command("e2e").description("Single-site functional end-to-end. Runs all functional flows (homepage, plp, pdp, purchase-journey, search, cart-interactions, optionally login) against ONE URL, then runs all checks in single-site mode. Use for 'does this site actually work?' verification — broader than `audit` (which only does vitals/console/network/images/seo).").requiredOption("--url <url>", "Base URL of the site to test").option("--flows <list>", "Comma-separated flows. Default: homepage,plp,pdp,purchase-journey,search,cart-interactions (login is opt-in)", "").option("--viewports <list>", "Comma-separated viewports", "mobile,desktop").option("--cep <cep>", "CEP for shipping calculation", "01310-100").option("--add-to-cart-timeout <ms>", "How long (ms) the add-to-cart step polls for a success signal before failing. Default 3000. Raise/lower to match your site's success-toast lifetime (issue #143).", (v) => Number(v)).option("--search-terms <list>", "Comma-separated search terms to use (override LLM auto-discovery)").option("--login-email <email>", "Login email (also PARITY_LOGIN_EMAIL env var)").option("--login-password <pwd>", "Login password (also PARITY_LOGIN_PASSWORD env var)").option("--output <dir>", "Output directory", "./parity-output").option("--open", "Open the HTML report after the run completes", false).option("--json", "Emit one-line JSON instead of pretty text", false).option("--no-auto-selectors", "Disable LLM-based selector discovery (uses defaults instead)").option("--refresh-selectors", "Bypass selector cache and re-run discovery", false).option("--no-learn", "Don't write to learned-selectors.json (read-only mode)").option("--fail-on <severities>", "Comma-separated severities that cause exit 1 (default: critical,high)", "critical,high").option("--pt", "Tell the LLM to respond in Brazilian Portuguese. Issue #67.").action(async (opts) => {
|
|
60236
60248
|
if (opts.pt) {
|
|
60237
60249
|
const { setLlmLanguage: setLlmLanguage2 } = await Promise.resolve().then(() => (init_client(), exports_client));
|
|
60238
60250
|
setLlmLanguage2("pt");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/parity",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
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",
|