// Run with: node --test tests/js/
const test = require("node:test");
const assert = require("node:assert/strict");
const { escapeHtml, escapeAttr } = require("../../web/js/html-escape.js");
test("escapes the five HTML-significant characters", () => {
assert.equal(escapeHtml(`&<>"'`), "&<>"'");
});
test("a double quote cannot break out of an attribute", () => {
const payload = `" onmouseover="alert(1)`;
const escaped = escapeHtml(payload);
assert.ok(!escaped.includes(`"`), escaped);
assert.equal(escaped, `" onmouseover="alert(1)`);
assert.equal(``, ``);
});
test("a single quote cannot break out of a single-quoted attribute", () => {
const escaped = escapeHtml(`' onclick='alert(1)`);
assert.ok(!escaped.includes(`'`), escaped);
assert.equal(escaped, `' onclick='alert(1)`);
});
test("a script tag is neutralised in text context", () => {
assert.equal(escapeHtml(""), "<script>x</script>");
});
test("unicode and ordinary text pass through unchanged", () => {
assert.equal(escapeHtml("héllo wörld 日本語 🙂"), "héllo wörld 日本語 🙂");
assert.equal(escapeHtml("plain text, no specials"), "plain text, no specials");
});
test("already escaped text is escaped again, never double-decoded", () => {
assert.equal(escapeHtml("&"), "&");
});
test("null and undefined become the empty string", () => {
assert.equal(escapeHtml(null), "");
assert.equal(escapeHtml(undefined), "");
});
test("numbers and booleans are coerced to strings", () => {
assert.equal(escapeHtml(42), "42");
assert.equal(escapeHtml(0), "0");
assert.equal(escapeHtml(1.5), "1.5");
assert.equal(escapeHtml(false), "false");
});
test("objects are coerced through String()", () => {
assert.equal(escapeHtml({ toString: () => "" }), "<b>");
});
test("escapeAttr is the same function as escapeHtml", () => {
assert.equal(escapeAttr, escapeHtml);
assert.equal(escapeAttr(`a"b`), "a"b");
});