Add dependency-free browser test suite and README

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).
This commit is contained in:
2026-11-07 21:18:38 -07:00
parent 7d34ab22f8
commit eb101c3e05
10 changed files with 652 additions and 0 deletions
+52
View File
@@ -0,0 +1,52 @@
/** A minimal test harness: no dependencies, runs in any browser. */
const tests = [];
export const test = (name, fn) => tests.push({ name, fn });
export function assert(condition, message = "assertion failed") {
if (!condition) throw new Error(message);
}
export function equal(actual, expected, message = "") {
const a = JSON.stringify(actual);
const e = JSON.stringify(expected);
if (a !== e) throw new Error(`${message ? `${message}: ` : ""}expected ${e}, got ${a}`);
}
/** Assert that `fn` throws (sync or async); optionally that its message matches `pattern`. */
export async function throws(fn, pattern) {
try {
await fn();
} catch (error) {
if (pattern && !pattern.test(String(error && error.message))) {
throw new Error(`threw "${error.message}", expected to match ${pattern}`);
}
return;
}
throw new Error("expected a throw, but nothing was thrown");
}
export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
export async function runAll() {
const log = document.getElementById("log");
let failed = 0;
for (const { name, fn } of tests) {
const item = document.createElement("li");
try {
await fn();
item.className = "pass";
item.textContent = name;
} catch (error) {
failed++;
item.className = "fail";
item.textContent = `${name} -- ${error && error.message}`;
console.error(name, error);
}
log.append(item);
}
const summary = document.getElementById("summary");
summary.textContent = `${failed ? "FAIL" : "PASS"} ${tests.length - failed}/${tests.length} tests`;
document.documentElement.dataset.result = failed ? "fail" : "pass";
}