49 tests run in headless Chrome via tests/run.sh: unit tests for the pure modules plus integration tests that drive the real page in an iframe (filtering, URL state, bag, limits, cross-tab sync, checkout, dialogs, accessibility and overflow checks).
103 lines
3.5 KiB
JavaScript
103 lines
3.5 KiB
JavaScript
import { PRODUCTS } from "../dist/js/catalog.js";
|
|
import {
|
|
MAX_QTY,
|
|
STORAGE_KEY,
|
|
cartCount,
|
|
cartLines,
|
|
cartSubtotal,
|
|
changeQty,
|
|
loadCart,
|
|
sanitizeCart,
|
|
saveCart,
|
|
} from "../dist/js/cart.js";
|
|
import { assert, equal, test } from "./harness.js";
|
|
|
|
const ids = PRODUCTS.map((p) => p.id);
|
|
|
|
function fakeStorage(initial = {}) {
|
|
const data = { ...initial };
|
|
return {
|
|
data,
|
|
getItem: (k) => (k in data ? data[k] : null),
|
|
setItem: (k, v) => void (data[k] = String(v)),
|
|
removeItem: (k) => void delete data[k],
|
|
};
|
|
}
|
|
const broken = {
|
|
getItem() { throw new Error("blocked"); },
|
|
setItem() { throw new Error("quota"); },
|
|
removeItem() { throw new Error("blocked"); },
|
|
};
|
|
|
|
test("cart: the storage key is unchanged, so existing bags carry over", () => {
|
|
equal(STORAGE_KEY, "fieldwork-bag-v1");
|
|
});
|
|
|
|
test("cart: sanitizeCart keeps valid lines", () => {
|
|
equal(sanitizeCart({ pack: 2, mug: 1 }, ids), { pack: 2, mug: 1 });
|
|
});
|
|
|
|
test("cart: sanitizeCart drops invalid quantities and unknown products", () => {
|
|
const dirty = { pack: -3, mug: 0, cap: 1.5, bottle: "2", lantern: NaN, blanket: null, ghost: 4 };
|
|
equal(sanitizeCart(dirty, ids), {});
|
|
equal(sanitizeCart({ pack: Infinity }, ids), {});
|
|
});
|
|
|
|
test("cart: sanitizeCart clamps oversized quantities", () => {
|
|
equal(sanitizeCart({ pack: 500 }, ids), { pack: MAX_QTY });
|
|
});
|
|
|
|
test("cart: sanitizeCart rejects non-objects and cannot be prototype-polluted", () => {
|
|
for (const bad of [null, undefined, [], "x", 5, true]) equal(sanitizeCart(bad, ids), {});
|
|
const polluted = JSON.parse('{"__proto__":{"pack":3},"constructor":{"mug":2}}');
|
|
equal(sanitizeCart(polluted, ids), {});
|
|
assert(({}).pack === undefined, "Object.prototype untouched");
|
|
});
|
|
|
|
test("cart: changeQty adds, removes at zero, and clamps to the limit", () => {
|
|
let cart = changeQty({}, "pack", 1);
|
|
equal(cart, { pack: 1 });
|
|
cart = changeQty(cart, "pack", 1);
|
|
equal(cart, { pack: 2 });
|
|
cart = changeQty(cart, "pack", -2);
|
|
equal(cart, {}, "line removed at zero");
|
|
equal(changeQty({}, "pack", -1), {}, "cannot go negative");
|
|
equal(changeQty({ pack: MAX_QTY }, "pack", 1), { pack: MAX_QTY }, "limit holds");
|
|
});
|
|
|
|
test("cart: changeQty is immutable", () => {
|
|
const original = Object.freeze({ pack: 1 });
|
|
const next = changeQty(original, "mug", 1);
|
|
equal(original, { pack: 1 });
|
|
assert(next !== original);
|
|
});
|
|
|
|
test("cart: counts, lines and subtotal (exact, in cents, in catalogue order)", () => {
|
|
const cart = { mug: 2, pack: 1 }; // inserted out of catalogue order
|
|
equal(cartCount(cart), 3);
|
|
equal(cartLines(cart, PRODUCTS).map((l) => [l.product.id, l.qty, l.total]), [
|
|
["pack", 1, 8600],
|
|
["mug", 2, 4800],
|
|
]);
|
|
equal(cartSubtotal(cart, PRODUCTS), 13400);
|
|
equal(cartSubtotal({}, PRODUCTS), 0);
|
|
});
|
|
|
|
test("cart: loadCart reads, sanitises, and survives bad storage", () => {
|
|
equal(loadCart(fakeStorage({ [STORAGE_KEY]: '{"pack":2}' }), ids), { pack: 2 });
|
|
equal(loadCart(fakeStorage({ [STORAGE_KEY]: "not json{" }), ids), {}, "corrupt JSON");
|
|
equal(loadCart(fakeStorage(), ids), {}, "nothing saved");
|
|
equal(loadCart(broken, ids), {}, "storage throws");
|
|
equal(loadCart(null, ids), {}, "storage unavailable");
|
|
});
|
|
|
|
test("cart: saveCart writes, clears when empty, and never throws", () => {
|
|
const storage = fakeStorage();
|
|
saveCart(storage, { pack: 1 });
|
|
equal(JSON.parse(storage.data[STORAGE_KEY]), { pack: 1 });
|
|
saveCart(storage, {});
|
|
assert(!(STORAGE_KEY in storage.data), "empty cart removes the key");
|
|
saveCart(broken, { pack: 1 });
|
|
saveCart(null, { pack: 1 });
|
|
});
|