diff --git a/client/space_lua/render_lua_html.test.ts b/client/space_lua/render_lua_html.test.ts new file mode 100644 index 00000000..7c006c8c --- /dev/null +++ b/client/space_lua/render_lua_html.test.ts @@ -0,0 +1,343 @@ +import { expect, test } from "vitest"; +import { renderResultToHtml } from "./render_lua_html.ts"; +import { LuaTable } from "./runtime.ts"; +import { makeLuaFloat } from "./numeric.ts"; +import { LIQ_NULL } from "./liq_null.ts"; + +// ── Nil / empty values ────────────────────────────────────────────── + +test("null renders as empty span with dataType nil", async () => { + const r = await renderResultToHtml(null); + expect(r).toEqual({ html: ``, dataType: "nil" }); +}); + +test("undefined renders as empty span with dataType nil", async () => { + const r = await renderResultToHtml(undefined); + expect(r).toEqual({ html: ``, dataType: "nil" }); +}); + +test("LIQ_NULL (SQL NULL) renders as empty span with dataType nil", async () => { + const r = await renderResultToHtml(LIQ_NULL); + expect(r).toEqual({ html: ``, dataType: "nil" }); +}); + +// ── Strings ───────────────────────────────────────────────────────── + +test("string renders with default escaping", async () => { + const r = await renderResultToHtml("hello world"); + expect(r).toEqual({ html: "hello world", dataType: "string" }); +}); + +test("empty string is rendered (not treated as nil)", async () => { + const r = await renderResultToHtml(""); + expect(r).toEqual({ html: "", dataType: "string" }); +}); + +test("string with HTML special chars is escaped", async () => { + const r = await renderResultToHtml('"Tom & Jerry"'); + expect(r).toEqual({ + html: '<b>"Tom & Jerry"</b>', + dataType: "string", + }); +}); + +// ── Numbers ───────────────────────────────────────────────────────── + +test("integer number", async () => { + const r = await renderResultToHtml(42); + expect(r).toEqual({ html: "42", dataType: "number" }); +}); + +test("zero", async () => { + const r = await renderResultToHtml(0); + expect(r).toEqual({ html: "0", dataType: "number" }); +}); + +test("non-integer float", async () => { + const r = await renderResultToHtml(1.5); + expect(r).toEqual({ html: "1.5", dataType: "number" }); +}); + +test("tagged float (integer-valued)", async () => { + const r = await renderResultToHtml(makeLuaFloat(2)); + expect(r).toEqual({ html: "2.0", dataType: "number" }); +}); + +// ── Booleans ──────────────────────────────────────────────────────── + +test("boolean true", async () => { + const r = await renderResultToHtml(true); + expect(r).toEqual({ html: "true", dataType: "boolean" }); +}); + +test("boolean false", async () => { + const r = await renderResultToHtml(false); + expect(r).toEqual({ html: "false", dataType: "boolean" }); +}); + +// ── Fallback (unknown object type) ────────────────────────────────── + +test("non-matching object is stringified via renderInline", async () => { + const d = new Date("2024-01-15T00:00:00.000Z"); + const r = await renderResultToHtml(d); + expect(r.dataType).toBe("string"); + expect(r.html).toContain("2024"); // toString includes year +}); + +// ── Custom renderInline ───────────────────────────────────────────── + +test("custom renderInline is used for strings", async () => { + const custom = (s: string) => `${s}`; + const r = await renderResultToHtml("hello", custom); + expect(r).toEqual({ html: "hello", dataType: "string" }); +}); + +// ── LuaTable: empty ───────────────────────────────────────────────── + +test("empty LuaTable", async () => { + const r = await renderResultToHtml(new LuaTable()); + expect(r).toEqual({ + html: `
`, + dataType: "table", + }); +}); + +// ── LuaTable: pure array of scalars ───────────────────────────────── + +test("LuaTable array of scalars renders as