datewise/tests/e2e/widget.e2e.mjs
metatroncubeswdev 123ca71d42 feat(widget): restyle storefront scheduler; booking stub with hold countdown
- segmented method control with line icons
- round calendar days with availability dots and scarcity badges
- pill time chips; confirmation stub shows method, place, date, time, fee
- colours derive from theme text colour + one merchant accent (light/dark safe)
- rename leftover ss- internals to dw-

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 21:17:09 -04:00

85 lines
4.3 KiB
JavaScript

/**
* Browser test for the storefront widget against tests/e2e/server.ts.
* node tests/e2e/widget.e2e.mjs (needs `npm i -D playwright` and the harness running)
*/
import { chromium } from "playwright";
import assert from "node:assert/strict";
const BASE = process.env.BASE ?? "http://localhost:4545";
const SHOTS = process.env.SHOTS ?? "/tmp";
const browser = await chromium.launch({ executablePath: process.env.CHROMIUM ?? undefined });
const page = await browser.newPage({ viewport: { width: 520, height: 1100 } });
const errors = [];
page.on("pageerror", (e) => errors.push(e.message));
const cart = () => page.evaluate(() => fetch("/cart.js").then((r) => r.json()));
const step = (s) => console.log("•", s);
await fetch(`${BASE}/__reset`);
await page.goto(`${BASE}/cart`);
await page.waitForSelector(".dw-methods");
step("shipping is blocked by the fresh-flower rule, with the reason shown");
const ship = page.locator('.dw-method[data-v="shipping"]');
assert.equal(await ship.getAttribute("aria-disabled"), "true");
assert.match(await ship.innerText(), /Fresh flowers are pickup or local delivery only/);
step("checkout is stopped until a time is chosen");
await page.click('button[name="checkout"]');
assert.match(await page.locator(".dw-live").innerText(), /choose a delivery or pickup time/i);
assert.equal(await page.locator("#checkedout").innerText(), "");
step("delivery: postal code → zone fee → pick first date and slot → hold");
await page.click('.dw-method[data-v="delivery"]');
await page.waitForSelector(".dw-postal");
await page.fill(".dw-postal input", "N2H 5Z6");
await page.click(".dw-postal button");
await page.waitForSelector(".dw-day.dw-avail");
await page.locator(".dw-day.dw-avail").first().click();
await page.locator('.dw-slot:not([aria-disabled="true"])').first().click();
await page.waitForSelector("[data-dw-timer]");
let c = await cart();
const sel = JSON.parse(c.attributes._datewise);
assert.equal(sel.m, "delivery");
const fees = c.items.filter((i) => i.properties._datewise_fee);
assert.equal(fees.length, 1, "exactly one fee line");
assert.equal(fees[0].final_price, sel.f, "fee line matches the quote");
assert.ok(c.attributes["Local delivery date"], "readable attribute for staff");
await page.screenshot({ path: `${SHOTS}/e2e-held.png`, fullPage: true });
step("a rival shopper takes the last place in that slot; ours stays held");
const rival = await (await fetch(`${BASE}/__rival-hold`, { method: "POST", body: JSON.stringify({ method: "delivery", locationId: sel.l, date: sel.d, slot: sel.s, postal: "N2H 5Z6" }) })).json();
assert.equal(rival.ok, true, "rival gets the 2nd of 2 places");
const rival2 = await (await fetch(`${BASE}/__rival-hold`, { method: "POST", body: JSON.stringify({ method: "delivery", locationId: sel.l, date: sel.d, slot: sel.s, postal: "N2H 5Z6" }) })).json();
assert.equal(rival2.ok, false, "third shopper is refused");
step("theme changes the cart quantity → widget re-checks and keeps the still-valid hold");
await page.locator("[data-k]").nth(2).click(); // + on the second line
await page.waitForTimeout(1200);
c = await cart();
assert.ok(c.attributes._datewise, "selection kept");
step("switching to pickup releases the delivery time and removes the fee");
await page.click('.dw-method[data-v="pickup"]');
await page.waitForTimeout(1500);
c = await cart();
assert.equal(c.attributes._datewise, undefined);
assert.equal(c.items.filter((i) => i.properties._datewise_fee).length, 0);
step("pickup hold → checkout goes through with the selection attached");
await page.locator(".dw-day.dw-avail").first().click();
await page.locator('.dw-slot:not([aria-disabled="true"])').first().click();
await page.waitForSelector("[data-dw-timer]");
await page.click('button[name="checkout"]');
await page.waitForFunction(() => document.getElementById("checkedout").textContent.startsWith("CHECKOUT"));
assert.match(await page.locator("#checkedout").innerText(), /"m\\?":\\?"pickup/);
await page.screenshot({ path: `${SHOTS}/e2e-pickup.png`, fullPage: true });
step("keyboard: arrow keys move between calendar days");
await page.locator(".dw-day[tabindex='0']").focus();
await page.keyboard.press("ArrowRight");
assert.equal(await page.evaluate(() => document.activeElement.classList.contains("dw-day")), true);
assert.deepEqual(errors, [], "no page errors");
console.log("\nAll widget checks passed");
await browser.close();