import { expect } from "@std/expect"; import { CommonIframeSandboxElement } from "../src/common-iframe-sandbox.ts"; /** Returns the next report from this fixture's guest. */ function report(id: string) { return new Promise>((resolve) => { const receive = (event: MessageEvent) => { if (event.data?.fixture !== id) return; globalThis.removeEventListener("message", receive); resolve(event.data); }; globalThis.addEventListener("message", receive); }); } /** Returns HTML which reports its effective style and retained input. */ function guest(id: string) { return ` `; } for (const outerFrameUrl of [undefined, "/outer-frame.html"]) { Deno.test(`iframe presentation updates in ${outerFrameUrl ?? "srcdoc"} without replacing the guest`, async () => { const id = crypto.randomUUID(); const element = new CommonIframeSandboxElement(); element.outerFrameUrl = outerFrameUrl; const fontURL = new URL("/presentation.css", location.href).href; element.presentation = { css: ":root { --example-ink: rgb(12, 34, 56); }", attributes: { "data-skin": "host", "data-caption": "text", }, stylesheets: [fontURL], }; element.src = guest(id); const first = report(id); document.body.append(element); try { const initial = await first; expect(initial.color).toBe("rgb(12, 34, 56)"); expect(initial.startupInk).toBe("rgb(12, 34, 56)"); expect(initial.skin).toBe("host"); expect(initial.caption).toBe("text"); expect(initial.mode).toBe("CSS1Compat"); expect(initial.fallback).toBe("false"); expect(initial.fonts).toBe(fontURL); expect(initial.fontToken).toBe("fixture"); const changed = report(id); element.presentation = { css: ":root { --example-ink: rgb(65, 43, 21); }", attributes: { "data-skin": "next" }, stylesheets: [], }; const next = await changed; expect(next.color).toBe("rgb(65, 43, 21)"); expect(next.skin).toBe("next"); expect(next.fonts).toBe(""); expect(next.fontToken).toBe(""); expect(next.input).toBe("edited"); expect(next.incarnation).toBe(initial.incarnation); const guestWindow = element.accessForTestingOnly.iframeRef.value ?.contentWindow ?.frames[0]; expect(guestWindow).toBeDefined(); const unrelated = report(id); guestWindow!.postMessage({ type: "presentation", data: null }, "*"); guestWindow!.postMessage("presentation-test-probe", "*"); const afterUnrelated = await unrelated; expect(afterUnrelated.skin).toBe("next"); expect(afterUnrelated.color).toBe(next.color); const cleared = report(id); element.presentation = null; const plain = await cleared; expect(plain.skin).toBe("authored"); expect(plain.color).not.toBe(next.color); expect(plain.input).toBe("edited"); expect(plain.incarnation).toBe(initial.incarnation); const reloadedID = crypto.randomUUID(); const reloaded = report(reloadedID); element.src = guest(reloadedID); element.presentation = { css: ":root { --example-ink: rgb(98, 76, 54); }", attributes: { "data-skin": "replacement" }, stylesheets: [], }; const replacement = await reloaded; expect(replacement.color).toBe("rgb(98, 76, 54)"); expect(replacement.skin).toBe("replacement"); expect(replacement.incarnation).not.toBe(initial.incarnation); } finally { element.remove(); } }); } for (const outerFrameUrl of [undefined, "/outer-frame.html"]) { Deno.test(`iframe presentation honors guest meta CSP in ${outerFrameUrl ?? "srcdoc"}`, async () => { const id = crypto.randomUUID(); const element = new CommonIframeSandboxElement(); element.outerFrameUrl = outerFrameUrl; const stylesheet = new URL("/presentation.css", location.href).href; element.presentation = { css: ":root { --blocked-ink: injected; }", attributes: {}, stylesheets: [stylesheet], }; element.src = ` `; const loaded = report(id); document.body.append(element); try { const value = await loaded; expect(value.inline).toBe(""); expect(value.external).toBe(""); } finally { element.remove(); } }); }