import { afterEach, expect, rs, test } from "@rstest/core"; import { prepareHtmlPreview } from "@/core/artifacts/html-assets"; const url = "https://app.example/api/threads/chat/artifacts/mnt/user-data/outputs/report.html"; const request = rs.spyOn(globalThis, "fetch"); afterEach(() => { request.mockReset(); }); test("preserves missing and legacy doctypes and comments outside the HTML element", async () => { for (const declaration of [ "", "", '', '', ]) { const content = `${declaration}
',
url,
);
expect(result.failedResources).toBe(0);
expect(
request.mock.calls.map(([input]) =>
typeof input === "string"
? input
: input instanceof URL
? input.href
: input.url,
),
).toEqual([
url.replace("report.html", "main.css"),
url.replace("report.html", "nested.css"),
url.replace("outputs/report.html", "uploads/icon.png"),
url.replace("report.html", "chart%2520.png"),
url.replace("report.html", "app.js"),
]);
expect(result.content).not.toContain('src="chart%2520.png"');
const document = new DOMParser().parseFromString(result.content, "text/html");
const css = atob(
document.querySelector("link")!.getAttribute("href")!.split(",")[1]!,
);
expect(css).toContain("data:text/css");
expect(
request.mock.calls.every(([, init]) => init?.redirect === "error"),
).toBe(true);
});
test("never fetches another thread, external origin, non-artifact route or encoded separators", async () => {
const result = await prepareHtmlPreview(
`


',
url,
);
expect(request).toHaveBeenCalledTimes(3);
expect(result.failedResources).toBe(2);
expect(result.content).toContain("Report");
expect(result.content).not.toContain('src="missing.png"');
});
test("rewrites inline CSS and ignores commented URL text", async () => {
request.mockResolvedValue(new Response("image"));
const result = await prepareHtmlPreview(
'',
url,
);
expect(request).toHaveBeenCalledTimes(1);
expect(result.content).toContain("/*url(no.png)*/");
expect(result.content).toContain("data:");
});
test("leaves public demos and previews without a resource URL on their existing path", async () => {
const content = '
';
for (const value of [
undefined,
"https://app.example/demo/threads/chat/report.html",
]) {
const result = await prepareHtmlPreview(content, value);
expect(result.content).toContain(content);
expect(request).not.toHaveBeenCalled();
}
});
test("preserves external bases and escaped external CSS references", async () => {
const result = await prepareHtmlPreview(
'
',
url,
);
expect(request).not.toHaveBeenCalled();
expect(result.content).toContain('src="image.png"');
expect(result.content).toContain("a\\)b.png");
});
test("supports responsive pictures, SVG fragments and CSS escaped filenames", async () => {
request.mockImplementation(async () => new Response("image"));
const result = await prepareHtmlPreview(
'
`).join(""),
url,
);
expect(request).toHaveBeenCalledTimes(32);
expect(result.failedResources).toBe(8);
request.mockClear();
request.mockImplementation(async (input) => {
const name = new URL(
typeof input === "string"
? input
: input instanceof URL
? input.href
: input.url,
).pathname
.split("/")
.pop()!;
return new Response(`@import "${Number.parseInt(name) + 1}.css";`);
});
const nested = await prepareHtmlPreview(
'',
url,
);
expect(request).toHaveBeenCalledTimes(9);
expect(nested.failedResources).toBe(1);
});
test("caps streamed resource bytes and the aggregate budget", async () => {
request.mockImplementation(
async () => new Response(new Uint8Array(4 * 1024 * 1024 + 1)),
);
const oversized = await prepareHtmlPreview('
', url);
expect(oversized.failedResources).toBe(1);
expect(oversized.content).toContain('src="data:,"');
request.mockClear();
request.mockImplementation(
async () => new Response(new Uint8Array(4 * 1024 * 1024)),
);
const aggregate = await prepareHtmlPreview(
Array.from({ length: 5 }, (_, i) => `
`).join(""),
url,
);
expect(aggregate.failedResources).toBe(1);
expect(request).toHaveBeenCalledTimes(4);
});
test("bounds repeated data URLs and preserves out-of-route CSS resolution", async () => {
request.mockImplementation(
async () => new Response(new Uint8Array(4 * 1024 * 1024)),
);
const repeated = await prepareHtmlPreview(
Array.from({ length: 8 }, () => '
').join(""),
url,
);
expect(request).toHaveBeenCalledTimes(1);
expect(repeated.failedResources).toBeGreaterThan(0);
expect(repeated.content.length).toBeLessThan(24 * 1024 * 1024);
request.mockClear();
request.mockResolvedValue(
new Response(".x{background:url(/public/chart.png)}"),
);
const css = await prepareHtmlPreview(
'',
url,
);
const document = new DOMParser().parseFromString(css.content, "text/html");
const stylesheet = atob(
document.querySelector("link")!.getAttribute("href")!.split(",")[1]!,
);
expect(stylesheet).toContain("https://app.example/public/chart.png");
expect(request).toHaveBeenCalledTimes(1);
});
test("quotes absolute CSS URLs that replace escaped unquoted references", async () => {
request.mockResolvedValue(
new Response(".chart{background:url(//cdn.example/chart\\(1\\).png)}"),
);
const result = await prepareHtmlPreview(
'',
url,
);
const document = new DOMParser().parseFromString(result.content, "text/html");
const stylesheet = atob(
document.querySelector("link")!.getAttribute("href")!.split(",")[1]!,
);
expect(stylesheet).toContain('url("https://cdn.example/chart(1).png")');
expect(result.failedResources).toBe(0);
});
test("aborts late responses before exposing prepared HTML", async () => {
const controller = new AbortController();
request.mockImplementation(async (input) => {
if (
(typeof input === "string"
? input
: input instanceof URL
? input.href
: input.url
).endsWith("late.png")
)
controller.abort();
return new Response("image");
});
await expect(
prepareHtmlPreview(
'
',
url,
controller.signal,
),
).rejects.toThrow();
});
test("validates original integrity before rewriting resources", async () => {
request.mockImplementation(async () => new Response("body"));
const result = await prepareHtmlPreview(
'',
url,
);
expect(request.mock.calls.map(([, init]) => init?.integrity)).toEqual([
"sha256-original",
"sha256-script",
]);
expect(result.content).not.toContain("sha256-original");
expect(result.content).not.toContain("sha256-script");
expect(result.content).toContain("sha256-external");
});
test("never reuses an unverified resource for a distinct integrity constraint", async () => {
request.mockImplementation(async (_input, init) => {
if (init?.integrity === "sha256-wrong")
throw new TypeError("Integrity mismatch");
return new Response("image");
});
const result = await prepareHtmlPreview(
'

',
url,
);
expect(request.mock.calls.map(([, init]) => init?.integrity)).toEqual([
undefined,
"sha256-valid",
"sha256-wrong",
]);
expect(result.failedResources).toBe(1);
expect(result.content).toContain('src="data:,"');
});
test("returns a partial preview when the preparation deadline expires", async () => {
rs.useFakeTimers();
try {
request.mockImplementation(
(_input, init) =>
new Promise((_resolve, reject) => {
init?.signal?.addEventListener(
"abort",
() => reject(new DOMException("Aborted", "AbortError")),
{ once: true },
);
}),
);
const pending = prepareHtmlPreview(
'
',
url,
);
await rs.advanceTimersByTimeAsync(10_000);
const result = await pending;
expect(result.failedResources).toBe(2);
expect(request).toHaveBeenCalledTimes(1);
expect(result.content).toContain("Report");
} finally {
rs.useRealTimers();
}
});