MarketCart CI › Guides
Shopify Dawn cart drawer not updating: catch it in CI against /cart.js
The shopper clicks Add to cart, Shopify stores the item (/cart.js has it), but the Dawn cart drawer still says “Your cart is empty”, keeps the old quantity, or the header count stays at 0. The request succeeded, so most smoke tests pass. This page shows why it happens, a 30-line Playwright check you can run yourself, and how to run the same check on every theme change.
Why the drawer goes stale
Dawn's product form posts to /cart/add and asks Shopify to return the re-rendered cart-drawer and cart-icon-bubble sections in the same response; the drawer then swaps that HTML in. If a customization, an app script or a renamed section breaks that swap, the stored cart and the visible cart diverge. A check that only looks at the HTTP status, or that navigates to /cart (rendered fresh by the server), will not see it.
Reproduce it in one minute
Our public synthetic store has a planted version of this bug: stale drawer (Dawn 16.1.1 markup) and the healthy twin. Add the shirt in both and compare the drawer with /cart.js.
Minimal Playwright check
// npm i -D playwright-core && npx playwright-core install chromium-headless-shell
import { chromium } from 'playwright-core';
const url = process.argv[2]; // https://<your-store>/products/<handle>?variant=<id>
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(url);
const added = page.waitForResponse((r) => /\/cart\/add(\.js)?$/.test(new URL(r.url()).pathname));
await page.click('product-form button[name="add"]');
await added;
await page.waitForTimeout(800); // let the theme re-render the drawer
const stored = await page.evaluate(async () => (await fetch(window.Shopify.routes.root + 'cart.js')).json());
const visible = await page.evaluate(() => {
const drawer = document.querySelector('cart-drawer');
const open = !!drawer && (drawer.classList.contains('active') || drawer.hasAttribute('open'));
const rows = drawer ? [...drawer.querySelectorAll('.cart-item')].filter((r) => r.getClientRects().length) : [];
return { open, lines: rows.length, qty: rows.map((r) => Number(r.querySelector('input.quantity__input')?.value)) };
});
const ok = visible.open && visible.lines === stored.items.length &&
stored.items.every((it, i) => visible.qty[i] === it.quantity);
console.log({ storedLines: stored.items.length, storedQty: stored.items.map((i) => i.quantity), visible });
process.exitCode = ok ? 0 : 1; // 1 = the drawer shows something other than what Shopify stored
await browser.close();
Expected on the healthy page: visible: { open: true, lines: 1, qty: [1] }, exit 0. On the stale page: storedLines: 1 but visible.lines: 0, exit 1.
Same check with MarketCart CI
git clone https://github.com/paolomangiadev/marketcart-ci && cd marketcart-ci
npm ci && npx playwright-core install chromium-headless-shell
node integrations/cli/marketcart.ts demo
Actual output for the stale case: REGRESSION — stored 1 line(s), visible 0 line(s) in drawer; stored line Linen Shirt (Red / M, variant 4101) not visible. MarketCart also compares variant, quantity, line price, currency of the market the session actually got, the header badge and the total, and returns JSON plus an exit code (0 PASS, 1 REGRESSION, 2 inconclusive).
In GitHub Actions:
- uses: paolomangiadev/marketcart-ci@v0
with:
mode: demo # or mode: remote with your cases.json and a license secret
Tested versions
| Theme / surface | Synthetic fixture | Live Shopify store |
|---|---|---|
| Dawn 16.1.1, cart drawer | 11/11 cases as expected (incl. stale drawer, stale badge) | Not yet verified live |
| Dawn 16.1.1, cart page / notification | 10/10 | Verified on a development store (paced suite) |
| Dawn 15.4.1, drawer and page | 21/21 | Not verified live |
Pitfalls
- Checking
/cartinstead of the drawer hides the bug: the cart page is rendered fresh by the server. - If the drawer does not auto-open, open it from the header icon the way a shopper would before reading it.
- Compare amounts in the session's market currency; never convert.
- Space tests out: Shopify answered HTTP 429 after about four cart cycles in under a minute on our development store. MarketCart waits 15 s between cart cycles per store and honours Retry-After.
- Never automate checkout or payment pages.
Limits
Dawn-family and Horizon 4.4 markup only; other themes return UNSUPPORTED. The synthetic fixture is written by the same author as the engine, so it proves which bug classes are detected, not that every store behaves alike.
Updated 2026-11-07. Evidence: repository evidence folder.