// @vitest-environment node import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; import { buildSubCompositionHtml } from "./subComposition"; function makeTempProject(files: Record): string { const dir = mkdtempSync(join(tmpdir(), "hf-subcomp-preview-")); for (const [rel, content] of Object.entries(files)) { const full = join(dir, rel); mkdirSync(join(full, ".."), { recursive: true }); writeFileSync(full, content, "utf-8"); } return dir; } describe("buildSubCompositionHtml", () => { it("handles full HTML document compositions without nesting in ", () => { const dir = makeTempProject({ "index.html": ` Host`, "compositions/map-block.html": `
`, }); const html = buildSubCompositionHtml( dir, "compositions/map-block.html", "/api/runtime.js", "/api/projects/demo/preview/", ); expect(html).not.toBeNull(); // Must not nest a full HTML document inside const bodyStart = html!.indexOf(""); const afterBody = html!.slice(bodyStart); expect(afterBody).not.toContain(""); // Composition styles must be in , not lost expect(html).toContain(".map {"); expect(html).toContain("#root {"); // Image src preserved (no ../ rewrite needed for bare relative paths) expect(html).toContain('src="assets/map.png"'); // Base tag for asset resolution expect(html).toContain(''); // GSAP from the composition's own must be preserved expect(html).toContain("gsap@3.14.2"); // Body script content preserved expect(html).toContain('__timelines["map-block"]'); // and from composition head must not be dropped expect(html).toContain('rel="stylesheet"'); expect(html).toContain('href="styles/theme.css"'); expect(html).toContain('name="viewport"'); // attribute forwarded to the output expect(html).toContain('lang="en"'); }); it("handles raw fragment compositions (no template, no full document)", () => { const dir = makeTempProject({ "index.html": ` Host`, "compositions/card.html": `

Hello

`, }); const html = buildSubCompositionHtml( dir, "compositions/card.html", "/api/runtime.js", "/api/projects/demo/preview/", ); expect(html).not.toBeNull(); expect(html).toContain(''); // ../icon.svg from compositions/ rewrites to icon.svg at project root expect(html).toContain('src="icon.svg"'); expect(html).not.toContain('src="../icon.svg"'); expect(html).toContain("

Hello

"); }); it("rewrites sub-composition asset paths against the project root preview base", () => { const dir = makeTempProject({ "index.html": ` Test`, "compositions/hero.html": ``, }); const html = buildSubCompositionHtml( dir, "compositions/hero.html", "/api/runtime.js", "/api/projects/demo/preview/", ); expect(html).toContain(''); expect(html).toContain('src="logo.png"'); expect(html).toContain("background-image: url('poster.png')"); expect(html).toContain('url("fonts/brand.woff2")'); expect(html).not.toContain('src="../logo.png"'); expect(html).not.toContain("url('../poster.png')"); expect(html).not.toContain('url("../fonts/brand.woff2")'); }); });