← back to Shopify Sample Shipping
proto-autoapply.mjs
123 lines
#!/usr/bin/env node
// TK-11333 — PROTOTYPE HARNESS (go/no-go for flipping). Answers the one unproven mechanic
// (DTD dissent hole #1): does the /discount/CODE?redirect=/cart permalink actually make FREE
// SHIPPING persist to CHECKOUT for a logged-in ELIGIBLE customer — or does it degrade to a
// page-reload / lost-on-reload / still-charged experience?
//
// It drives the REAL Chrome over CDP (openclaw, 127.0.0.1:18800 — same seam as admin-browser.mjs)
// so it uses whatever customer session is logged in THERE. Steve: in that Chrome, log in as a
// throwaway ELIGIBLE (in-segment) customer and have a saved US address before running with
// --checkout. The harness only manipulates a customer CART/checkout session (no admin writes).
//
// PREREQ: the test code must already exist. Either create TRADESHIP first (create-freeship-codes)
// or have Steve make a throwaway free-ship code in Admin — note it as a runbook step.
//
// node proto-autoapply.mjs --code TRADESHIP --variant 44090076954675 --checkout
// --domain designerwallcoverings.com (default domain shown)
//
// VERDICT (printed + verification/proto-autoapply-report.json):
// PASS — checkout shows $0 shipping for the eligible customer AND it survives a reload
// DEGRADE — permalink reloaded but code didn't stick / checkout still charges / lost on reload
// UNKNOWN — couldn't complete (CDP down / not logged in / no address / no checkout reached)
import fs from 'node:fs';
const arg = k => { const i = process.argv.indexOf('--' + k); return i >= 0 ? process.argv[i + 1] : undefined; };
const CODE = arg('code');
const DOMAIN = arg('domain') || 'designerwallcoverings.com';
const VARIANT = arg('variant'); // a Sample variant numeric id to add to cart
const DO_CHECKOUT = process.argv.includes('--checkout');
const CDP = arg('cdp') || 'http://127.0.0.1:18800';
if (!CODE) { console.error('need --code CODE'); process.exit(1); }
const OUT = new URL('./verification/proto-autoapply-report.json', import.meta.url);
const rep = { at: new Date().toISOString(), code: CODE, domain: DOMAIN, variant: VARIANT || null, steps: {}, verdict: 'UNKNOWN', notes: [] };
const base = `https://${DOMAIN}`;
let chromium;
try { ({ chromium } = await import('../Designer-Wallcoverings/node_modules/playwright/index.mjs')); }
catch (e) { rep.notes.push('playwright import failed: ' + e.message); finish(); }
let b;
try { b = await chromium.connectOverCDP(CDP); }
catch (e) { rep.notes.push('CDP connect failed (' + CDP + '): ' + e.message + ' — start openclaw real Chrome, then retry.'); finish(); }
try {
const ctx = b.contexts()[0] || await b.newContext();
const page = await ctx.newPage();
// 0) login state
await page.goto(`${base}/account`, { waitUntil: 'domcontentloaded', timeout: 45000 });
const onLogin = /\/account\/login/.test(page.url());
rep.steps.loggedIn = !onLogin;
if (onLogin) rep.notes.push('NOT logged in — log in as an eligible (in-segment) customer in the CDP Chrome, then re-run.');
// 1) add a sample to cart (if a variant was provided)
if (VARIANT) {
const added = await page.evaluate(async (v) => {
const r = await fetch('/cart/add.js', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ items: [{ id: Number(v), quantity: 1 }] }) });
return { ok: r.ok, status: r.status };
}, VARIANT);
rep.steps.addToCart = added;
}
const cartBefore = await page.evaluate(async () => (await (await fetch('/cart.js')).json()));
rep.steps.cartBefore = { item_count: cartBefore.item_count, total_price: cartBefore.total_price, total_discount: cartBefore.total_discount };
// 2) hit the discount permalink (this NAVIGATES = the "reload" the dissent worries about)
const t0 = Date.now();
await page.goto(`${base}/discount/${encodeURIComponent(CODE)}?redirect=/cart`, { waitUntil: 'domcontentloaded', timeout: 45000 });
rep.steps.permalink = { landedUrl: page.url(), navMs: Date.now() - t0, causedReload: true };
const cartAfter = await page.evaluate(async () => (await (await fetch('/cart.js')).json()));
rep.steps.cartAfter = { item_count: cartAfter.item_count, total_price: cartAfter.total_price, total_discount: cartAfter.total_discount, discount_apps: cartAfter.cart_level_discount_applications?.length ?? null };
const codeStuckInCart = (cartAfter.total_discount > 0) || (cartAfter.cart_level_discount_applications?.length > 0);
rep.steps.codeStuckInCart = codeStuckInCart;
// NOTE: cart.js does NOT reflect a SHIPPING discount (shipping is priced at checkout) — so
// a free-shipping code correctly shows total_discount:0 here. Presence of the code cookie is
// proven by reaching checkout below. We record cart state for diagnostics either way.
// 3) persistence across a reload of /cart
await page.reload({ waitUntil: 'domcontentloaded' });
const cartReload = await page.evaluate(async () => (await (await fetch('/cart.js')).json()));
rep.steps.cartAfterReload = { total_price: cartReload.total_price, total_discount: cartReload.total_discount };
// 4) checkout: the ONLY place a free-shipping code proves itself
if (DO_CHECKOUT) {
try {
await page.goto(`${base}/checkout`, { waitUntil: 'domcontentloaded', timeout: 60000 });
await page.waitForTimeout(3500);
const url = page.url();
const body = (await page.locator('body').innerText().catch(() => '')).slice(0, 20000);
// capture any shipping line + free markers
const shipLine = (body.match(/shipping[^\n]{0,60}/i) || [])[0] || '';
const freeMarker = /free/i.test(shipLine) || /\$?0\.00\s*(shipping|delivery)/i.test(body);
const shot = new URL('./verification/proto-checkout.png', import.meta.url).pathname;
await page.screenshot({ path: shot, fullPage: false }).catch(() => {});
rep.steps.checkout = { url, shippingLine: shipLine.replace(/\s+/g, ' ').trim(), freeMarker, screenshot: shot, onCheckoutDomain: /\/checkout|checkouts\//.test(url) };
// reload persistence at checkout
await page.reload({ waitUntil: 'domcontentloaded' }); await page.waitForTimeout(2500);
const body2 = (await page.locator('body').innerText().catch(() => '')).slice(0, 20000);
rep.steps.checkout.freeAfterReload = /free/i.test((body2.match(/shipping[^\n]{0,60}/i) || [''])[0]) || /\$?0\.00\s*(shipping|delivery)/i.test(body2);
} catch (e) { rep.notes.push('checkout step error: ' + e.message); }
}
// ---- VERDICT ----
if (!rep.steps.loggedIn) { rep.verdict = 'UNKNOWN'; rep.notes.push('verdict UNKNOWN — not logged in; the eligible-customer path is the whole test.'); }
else if (DO_CHECKOUT && rep.steps.checkout?.onCheckoutDomain) {
if (rep.steps.checkout.freeMarker && rep.steps.checkout.freeAfterReload) { rep.verdict = 'PASS'; rep.notes.push('Free shipping showed at checkout AND survived a reload — auto-apply is viable.'); }
else if (rep.steps.checkout.freeMarker) { rep.verdict = 'DEGRADE'; rep.notes.push('Free shipping showed at checkout but did NOT survive a reload — session-only, flaky.'); }
else { rep.verdict = 'DEGRADE'; rep.notes.push('Reached checkout but shipping was NOT free — code did not carry to checkout.'); }
} else { rep.verdict = 'UNKNOWN'; rep.notes.push('Ran without --checkout (or checkout not reached). Re-run with --checkout + a logged-in eligible customer + saved address for a real go/no-go.'); }
await b.close().catch(() => {});
} catch (e) { rep.notes.push('harness error: ' + e.message); try { await b.close(); } catch {} }
finish();
function finish() {
fs.writeFileSync(OUT, JSON.stringify(rep, null, 2) + '\n');
console.log('=== proto-autoapply — VERDICT:', rep.verdict, '===');
for (const [k, v] of Object.entries(rep.steps)) console.log(' ' + k + ':', JSON.stringify(v));
if (rep.notes.length) { console.log('notes:'); rep.notes.forEach(n => console.log(' - ' + n)); }
console.log('\nfull report -> verification/proto-autoapply-report.json');
console.log('\nGO/NO-GO: PASS => safe to create codes + flip band. DEGRADE => do NOT remove the band; keep a fallback band or rethink auto-apply. UNKNOWN => complete the manual prereqs and re-run.');
process.exit(0);
}