Files
2026-07-24 12:46:44 +05:30

67 lines
2.5 KiB
TypeScript

import { expect, test } from "bun:test";
import { renderDocument, renderDocumentStream } from "../src/index.ts";
test("escapes metadata and script URLs while preserving trusted rendered body", () => {
const html = renderDocument({
meta: { title: '</title><script>alert("x")</script>', description: '" onload="x' },
body: "<main>trusted</main>",
scripts: ['"><script>alert(1)</script>'],
});
expect(html).not.toContain('</title><script>alert("x")</script>');
expect(html).toContain("&lt;/title&gt;");
expect(html).toContain("<main>trusted</main>");
expect(html).not.toContain("<script>alert(1)</script>");
});
test("deduplicates runtime scripts and module preloads", () => {
const html = renderDocument({
meta: { title: "Page" },
body: "",
scripts: ["/app.js", "/app.js"],
});
expect(html.match(/rel="modulepreload"/g)).toHaveLength(1);
expect(html.match(/src="\/app\.js"/g)).toHaveLength(1);
});
test("always emits a document language and preserves an explicit language", () => {
const fallback = renderDocument({ meta: {}, body: "" });
expect(fallback).toContain('<html lang="en">');
const explicit = renderDocument({
meta: {},
body: "",
htmlAttrs: ' data-theme="dark" lang="fr"',
});
expect(explicit).toContain('<html data-theme="dark" lang="fr">');
expect(explicit).not.toContain('lang="en"');
});
test("merges framework head and scripts into an application document template", () => {
const html = renderDocument({
meta: { title: "Document layout" },
body: "",
htmlAttrs: ' data-theme="dark"',
scripts: ["/page.js"],
documentTemplate:
'<html data-ui-font="system"><head><meta name="custom" content="yes" /></head><body><div id="app">Page</div></body></html>',
});
expect(html).toStartWith("<!doctype html>");
expect(html).toContain('<html data-ui-font="system" data-theme="dark" lang="en">');
expect(html).toContain("<title>Document layout</title>");
expect(html).toContain('<meta name="custom" content="yes" />');
expect(html).toContain('<script type="module" src="/page.js"></script>');
expect(html.match(/<html/g)).toHaveLength(1);
expect(html.match(/<body/g)).toHaveLength(1);
});
test("streams async body chunks inside the document shell", async () => {
async function* body() {
yield "<h1>Shell</h1>";
yield "<p>Later</p>";
}
const html = await new Response(
renderDocumentStream({ meta: { title: "Stream" }, body: body() }),
).text();
expect(html).toContain('<div id="app"><h1>Shell</h1><p>Later</p></div>');
});