MarketCart CI

A release check for Shopify stores on Dawn-family themes. It adds an item in a real browser, then compares the cart Shopify stored (/cart.js) with what the shopper sees: line items, variant, quantity, line price, market currency, badge and total. The result is JSON plus an exit code, so a theme deploy fails before shoppers find the bug.

Open source CLI + GitHub ActionHosted: EUR 19 / 100 casesDawn family only Repository · llms.txt

Run the seeded demo now

Three cases against our synthetic fixture (a mock store with Dawn-style markup and planted defects), executed live by the hosted engine: a healthy cart, a stale drawer, and a live-vs-candidate pair where the candidate regressed. Results are cached for 10 minutes.

Check your own store (one free case)

1. Get your verification tag and paste it into Online Store › Themes › Edit code › layout/theme.liquid, inside <head>. This proves you control the store; we only test stores that carry it.

2. Pick one product variant to test.

The password is sent over HTTPS for this one run, used in memory and never stored or logged. Inconclusive results (BLOCKED, UNSUPPORTED, ERROR) do not use up your free case. Free checks stop for the day when the shared free budget is used.

Pricing

EUR 19 = 100 completed hosted cases, valid 30 days from activation. A live-vs-candidate pair uses 2 cases. Only PASS or REGRESSION results use credits; BLOCKED, UNSUPPORTED and ERROR are released. No subscription. The CLI and Action run locally for free.

Buy 100 cases — EUR 19

Sold through Payhip (seller: NorthGrid Digital). Your license key arrives automatically after payment and is checked on every job; refunds disable it.

Benchmark: 20 cases, expected vs actual

Run bench-20261107T141346Z, engine 0.1.0+49668598: 20/20 classified as expected, planted regressions caught 10/10, false PASS 0. The expected statuses were frozen before the engine was written. These are synthetic cases (one planted defect per scenario) by the same author as the engine: they show which bug classes are detected, not how a particular live store behaves. Methodology · raw JSON

Synthetic fixture: https://marketcart-fixture.pages.dev
CaseGroupPlantedExpectedActualEngine reason
C01healthy-baselineSingle default variant, qty 1, EUR market verified; stored == visible drawer/badge/total.PASSPASSstored == visible across 1 line(s), market IE/EUR verified in-session
C02healthy-baselineNon-default variant selected via ?variant; stored and visible both Blue / M.PASSPASSstored == visible across 1 line(s), market IE/EUR verified in-session
C03healthy-baselineQuantity 3; stored qty, visible qty, badge 3 and line total agree.PASSPASSstored == visible across 1 line(s), market IE/EUR verified in-session
C04healthy-baselineZero-decimal currency: JPY minor units are whole yen; ¥7,800 must equal stored 7800 without a /100 conversion.PASSPASSstored == visible across 1 line(s), market JP/JPY verified in-session
C05stale-visible-stateItem stored in cart but the drawer keeps its stale empty render.REGRESSIONREGRESSIONstored 1 line(s), visible 0 line(s) in drawer; stored line Linen Shirt (Red / M, variant 4101) not visible
C06stale-visible-stateDrawer correct, header cart badge not updated (adapter declares badge; fixture expects it).REGRESSIONREGRESSIONstored item_count 1, badge absent
C07stale-visible-stateStored qty 2, drawer shows stale qty 1.REGRESSIONREGRESSIONvariant 4101: stored qty 2, visible qty 1
C08item-quantity-driftShopper selected Blue / M (4102); theme JS posts the default variant 4101.REGRESSIONREGRESSIONrequested variant 4102 not in stored cart (stored: 4101); unexpected stored variant 4101
C09item-quantity-driftTwo items stored; drawer re-render drops the second line.REGRESSIONREGRESSIONstored 2 line(s), visible 1 line(s) in drawer; stored line Merino Socks (Default Title, variant 6301) not visible
C10item-quantity-driftServer persists qty 2 (clamp) while the optimistic UI shows the requested 3.REGRESSIONREGRESSIONvariant 4101: requested qty 3, stored qty 2; variant 4101: stored qty 2, visible qty 3; variant 4101: stored line 4800 EUR minor, visible 7200
C11currency-priceMarket IE/EUR verified in-session, but the stored cart currency is USD (wrong currency).REGRESSIONREGRESSIONstored cart currency USD, verified market currency EUR; variant 4101: visible "$26.00" indicates USD|CAD|AUD|NZD|MXN|SGD|HKD|CLP|COP|ARS, market EUR; visible total "$26.00" indicates USD|CAD|AUD|NZD|MXN|SGD|HKD|CLP|COP|A
C12currency-priceStored EUR 24.00; drawer shows the same number with the wrong currency ($24.00).REGRESSIONREGRESSIONvariant 4101: visible "$24.00" indicates USD|CAD|AUD|NZD|MXN|SGD|HKD|CLP|COP|ARS, market EUR; visible total "$24.00" indicates USD|CAD|AUD|NZD|MXN|SGD|HKD|CLP|COP|ARS, market EUR
C13currency-priceStored EUR 24.00; drawer shows a wrong amount (€20.00).REGRESSIONREGRESSIONvariant 4101: stored line 2400 EUR minor, visible 2000
C14currency-priceEquivalent localized formatting: de-DE '1.234,56 €' equals stored 123456 EUR minor units (dot = grouping, comma = decimal).PASSPASSstored == visible across 1 line(s), market DE/EUR verified in-session
C15live-candidateHealthy live vs unchanged healthy candidate: both pass, observations identical. Consumes 2 cases.PASSPASSlive and candidate both pass with identical stored/visible observations
C16live-candidateHealthy live vs broken candidate (stale drawer). Consumes 2 cases.REGRESSIONREGRESSIONcandidate regressed (live PASS)
C17inconclusiveStorefront answers 429 with a bot-challenge interstitial; never PASS.BLOCKEDBLOCKEDstorefront blocked automation (HTTP 429, challenge page)
C18inconclusiveNon-Dawn theme markup: no supported adapter; never PASS.UNSUPPORTEDUNSUPPORTEDno supported adapter: theme schema "Debut"
C19inconclusiveAdd-to-cart request hangs; per-case timeout fires (6 s fixture timeout, one bounded retry); never PASS.ERRORERRORcase timeout after 6000 ms
C20inconclusiveRequested JP/JPY but the store keeps serving IE/EUR in-session; the market cannot be verified, even though the cart itself is internally consistent; never PASS.UNSUPPORTEDUNSUPPORTEDrequested market JP/JPY not verified in-session (observed IE/EUR)

How it compares (scoped)

On the C05 stale-drawer and C08 wrong-variant synthetic fixtures, E-Com Checkout Auditor returned success where our parity contract expects REGRESSION. That is two synthetic cases, not a general superiority claim. PromiseCheck is a differently scoped Shopify Admin app, listed as a price anchor and not tested.

ProductWhat it checksResult on our fixturePriceEvidence
MarketCart CI (this project)Real browser; theme add-to-cart; /cart.js read independently of the DOM; drawer/cart page, badge, total, in-session market currencyC05 stale drawer: REGRESSION · C08 wrong variant: REGRESSION (20/20 synthetic cases as expected)EUR 19 / 100 completed hosted cases (30 days); CLI free locallyEXECUTED (synthetic fixture + Founder-owned live Dawn dev store) · source
E-Com Checkout Auditor (Apify Actor)Browser journey product → cart → checkout page, pass/fail per phaseC05 stale drawer: returned success (run nv2AeksMAjnh2ksTq) · C08 wrong variant: returned success (run bJKqMgPGZy7PxTBJq). Our parity contract expects REGRESSION for both. n = 2 synthetic casesUSD 0.05 per result (Apify Store, 2026-11-06)EXECUTED n=2 on our synthetic fixture · source
Shopify Variant Cartability Checker (Apify Actor)Ajax cart API transaction path (/products/x.js, /cart/add.js); never reads the rendered drawer by designInconclusive: our path-prefixed fixture is incompatible with root-relative tools (runs bs9d3gdsxiMNjjmig, fv8KT7HOpo32sX8cB)USD 0.005 per result (2026-11-06)EXECUTED, inconclusive (fixture limitation) · source
PromiseCheck: Cart Monitoring (Shopify app)Add to Cart, options and cart quantities on desktop/mobile; draft-theme vs saved live scan; hourly checks; screenshots, alertsNot tested (differently scoped Shopify Admin app; price anchor only)USD 79.99 / month, 14-day trial (listing read 2026-11-07)DOCUMENTED, UNTESTED · source
StoreCheck Markets (Shopify app)Live cart checks per market: availability, currency, totals, quantities, discountsNot testedFree manual / USD 9.99 per month automated (listing read 2026-11-06)DOCUMENTED, UNTESTED · source

FAQ

Why does the Dawn cart drawer show an empty or stale cart while /cart.js already has the item?

Dawn's product form posts to /cart/add.js and asks Shopify to return the re-rendered cart drawer and cart icon sections in the same response; the drawer then swaps in that HTML. If a customization, an app script or a missing section breaks that swap, Shopify has stored the item (it is in /cart.js) but the shopper still sees the old drawer and badge. MarketCart CI reads /cart.js independently of the page and compares it with what the drawer shows; any difference is a REGRESSION with the exact mismatch.

How do I catch a Shopify Markets currency mismatch in the cart?

Check three things in the same browser session: the market Shopify actually served (window.Shopify.country and window.Shopify.currency.active), the stored cart currency in /cart.js, and the currency shown next to every visible price and the total. They must agree, with no conversion applied. MarketCart CI does exactly that; if the session never gets the requested market, the result is UNSUPPORTED rather than a PASS you cannot trust.

How can I run cart regression tests in CI before publishing a Shopify theme?

Run the open-source MarketCart CI CLI or GitHub Action on every theme change. Describe cases as JSON (product URL, variant, quantity, market), optionally as a pair: the published theme versus an unpublished theme preview. The job exits 0 on PASS, 1 on REGRESSION and 2 when a case is inconclusive, so a failing cart blocks the deploy. Local runs are free; hosted runs use prepaid case credits.

Limits, stated plainly