sx-browser.js evaluates test.sx directly in the browser — click "Run 81 tests" to see SX test itself. Uses the same Sx global that rendered the page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
97 lines
3.8 KiB
JavaScript
97 lines
3.8 KiB
JavaScript
// sx-test-runner.js — Run test.sx in the browser using sx-browser.js.
|
|
// Loaded on the /specs/testing page. Uses the Sx global.
|
|
(function() {
|
|
var NIL = Sx.NIL;
|
|
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
|
function deepEqual(a, b) {
|
|
if (a === b) return true;
|
|
if (isNil(a) && isNil(b)) return true;
|
|
if (typeof a !== typeof b) return false;
|
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
if (a.length !== b.length) return false;
|
|
for (var i = 0; i < a.length; i++) if (!deepEqual(a[i], b[i])) return false;
|
|
return true;
|
|
}
|
|
if (a && typeof a === "object" && b && typeof b === "object") {
|
|
var ka = Object.keys(a), kb = Object.keys(b);
|
|
if (ka.length !== kb.length) return false;
|
|
for (var j = 0; j < ka.length; j++) if (!deepEqual(a[ka[j]], b[ka[j]])) return false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
window.sxRunTests = function(srcId, outId, btnId) {
|
|
var src = document.getElementById(srcId).textContent;
|
|
var out = document.getElementById(outId);
|
|
var btn = document.getElementById(btnId);
|
|
|
|
var stack = [], passed = 0, failed = 0, num = 0, lines = [];
|
|
|
|
var env = {
|
|
"try-call": function(thunk) {
|
|
try {
|
|
Sx.eval([thunk], env);
|
|
return { ok: true };
|
|
} catch(e) {
|
|
return { ok: false, error: e.message || String(e) };
|
|
}
|
|
},
|
|
"report-pass": function(name) {
|
|
num++; passed++;
|
|
lines.push("ok " + num + " - " + stack.concat([name]).join(" > "));
|
|
},
|
|
"report-fail": function(name, error) {
|
|
num++; failed++;
|
|
lines.push("not ok " + num + " - " + stack.concat([name]).join(" > "));
|
|
lines.push(" # " + error);
|
|
},
|
|
"push-suite": function(name) { stack.push(name); },
|
|
"pop-suite": function() { stack.pop(); },
|
|
|
|
"equal?": function(a, b) { return deepEqual(a, b); },
|
|
"eq?": function(a, b) { return a === b; },
|
|
"boolean?": function(x) { return typeof x === "boolean"; },
|
|
"string-length": function(s) { return String(s).length; },
|
|
"substring": function(s, start, end) { return String(s).slice(start, end); },
|
|
"string-contains?": function(s, n) { return String(s).indexOf(n) !== -1; },
|
|
"upcase": function(s) { return String(s).toUpperCase(); },
|
|
"downcase": function(s) { return String(s).toLowerCase(); },
|
|
"reverse": function(c) { return c ? c.slice().reverse() : []; },
|
|
"flatten": function(c) {
|
|
var r = [];
|
|
for (var i = 0; i < (c||[]).length; i++) {
|
|
if (Array.isArray(c[i])) for (var j = 0; j < c[i].length; j++) r.push(c[i][j]);
|
|
else r.push(c[i]);
|
|
}
|
|
return r;
|
|
},
|
|
"has-key?": function(d, k) { return d && typeof d === "object" && k in d; },
|
|
"append": function(c, x) { return Array.isArray(x) ? (c||[]).concat(x) : (c||[]).concat([x]); },
|
|
};
|
|
|
|
try {
|
|
var t0 = performance.now();
|
|
var exprs = Sx.parseAll(src);
|
|
for (var i = 0; i < exprs.length; i++) Sx.eval(exprs[i], env);
|
|
var elapsed = Math.round(performance.now() - t0);
|
|
lines.push("");
|
|
lines.push("1.." + num);
|
|
lines.push("# tests " + (passed + failed));
|
|
lines.push("# pass " + passed);
|
|
if (failed > 0) lines.push("# fail " + failed);
|
|
lines.push("# time " + elapsed + "ms");
|
|
} catch(e) {
|
|
lines.push("");
|
|
lines.push("FATAL: " + (e.message || String(e)));
|
|
}
|
|
|
|
out.textContent = lines.join("\n");
|
|
out.style.display = "block";
|
|
btn.textContent = passed + "/" + (passed + failed) + " passed" + (failed === 0 ? "" : " (" + failed + " failed)");
|
|
btn.className = failed > 0
|
|
? "px-4 py-2 rounded-md bg-red-600 text-white font-medium text-sm cursor-default"
|
|
: "px-4 py-2 rounded-md bg-green-600 text-white font-medium text-sm cursor-default";
|
|
};
|
|
})();
|