mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-08-31 02:41:44 +00:00
* feat(player): report runtime data application * fix(player): fail closed on runtime data delivery * fix(player): close runtime delivery and sandbox gaps * fix(player): satisfy runtime contract and CodeQL * refactor(player): simplify runtime tag scanner * style(player): apply repository formatter * fix(player): type runtime tag boundaries * fix(core): mint guest-local runtime-data ids in a separate space from host ids
82 lines
3.3 KiB
TypeScript
82 lines
3.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { ensureRuntimeBeforeBodyScripts } from "./runtime-in-srcdoc.js";
|
|
|
|
const URL = "https://cdn.example/hyperframe.runtime.iife.js";
|
|
|
|
/** What a pasted component looks like: it reads its variables during parse. */
|
|
const COMPONENT_PAGE = `<!doctype html><html><head><title>t</title></head><body>
|
|
<div data-hf-ui-root data-composition-variables='[{"id":"count","default":"3"}]'></div>
|
|
<script>var vars = window.__hyperframes && window.__hyperframes.getVariables
|
|
? window.__hyperframes.getVariables() : {};</script>
|
|
</body></html>`;
|
|
|
|
describe("ensureRuntimeBeforeBodyScripts", () => {
|
|
it("puts the runtime ahead of the script that reads variables", () => {
|
|
// The whole bug: the probe appended the runtime after load, so this guard
|
|
// always took the empty branch and the component used its hardcoded
|
|
// defaults. badge-pop with count 10 rendered 3.
|
|
const out = ensureRuntimeBeforeBodyScripts(COMPONENT_PAGE, URL);
|
|
|
|
expect(out).toContain(`<script src="${URL}"></script>`);
|
|
expect(out.indexOf(URL)).toBeLessThan(out.indexOf("getVariables"));
|
|
});
|
|
|
|
it("puts it inside head, where an external script blocks the parser", () => {
|
|
const out = ensureRuntimeBeforeBodyScripts(COMPONENT_PAGE, URL);
|
|
|
|
// Landing after </head> would not block body parsing in the same way.
|
|
expect(out.indexOf(URL)).toBeGreaterThan(out.indexOf("<head>"));
|
|
expect(out.indexOf(URL)).toBeLessThan(out.indexOf("</head>"));
|
|
});
|
|
|
|
it("does not add a second copy when the page already links it", () => {
|
|
const already = `<html><head><script src="${URL}"></script></head><body></body></html>`;
|
|
|
|
expect(ensureRuntimeBeforeBodyScripts(already, URL)).toBe(already);
|
|
});
|
|
|
|
it("leaves a CLI-rendered page alone, which inlines the runtime already", () => {
|
|
// The engine inlines it and defines the global on the way in. A second
|
|
// copy would re-initialise the runtime underneath a live composition.
|
|
const rendered = `<html><head><script>window.__hyperframes = {};</script></head><body></body></html>`;
|
|
|
|
expect(ensureRuntimeBeforeBodyScripts(rendered, URL)).toBe(rendered);
|
|
});
|
|
|
|
it("falls back to before <body> when there is no head", () => {
|
|
const out = ensureRuntimeBeforeBodyScripts("<html><body><script>x</script></body></html>", URL);
|
|
|
|
expect(out.indexOf(URL)).toBeLessThan(out.indexOf("<body>"));
|
|
});
|
|
|
|
it("handles a bare fragment by going first", () => {
|
|
const out = ensureRuntimeBeforeBodyScripts("<div>hi</div><script>x</script>", URL);
|
|
|
|
expect(out.startsWith(`<script src="${URL}"></script>`)).toBe(true);
|
|
});
|
|
|
|
it("is a no-op on empty input", () => {
|
|
expect(ensureRuntimeBeforeBodyScripts("", URL)).toBe("");
|
|
});
|
|
|
|
it("survives a head tag carrying attributes", () => {
|
|
const out = ensureRuntimeBeforeBodyScripts(
|
|
`<html><head lang="en" data-x><script>read()</script></head><body></body></html>`,
|
|
URL,
|
|
);
|
|
|
|
expect(out.indexOf(URL)).toBeLessThan(out.indexOf("read()"));
|
|
});
|
|
|
|
it("does not mistake a longer tag name for head", () => {
|
|
const out = ensureRuntimeBeforeBodyScripts(
|
|
`<html><header><script>early()</script></header><body><script>read()</script></body></html>`,
|
|
URL,
|
|
);
|
|
|
|
expect(out.indexOf(URL)).toBeLessThan(out.indexOf("<body>"));
|
|
expect(out.indexOf(URL)).toBeGreaterThan(out.indexOf("</header>"));
|
|
});
|
|
});
|