fix(core): torn-down runtimes stop checking readiness, and entry tests fit their timeout under load (#4960)

* test(core): load the runtime once per file so the entry tests' first timeout covers only its own work

* fix(core): a torn-down runtime's pending readiness check no longer touches the next document
This commit is contained in:
Miguel Ángel
2026-10-03 19:54:28 +00:00
committed by GitHub
parent f7a91f6982
commit 660674f469
3 changed files with 49 additions and 18 deletions
+25 -18
View File
@@ -52,6 +52,30 @@ async function evaluateRuntime(): Promise<void> {
await import("./entry");
}
function resetRuntimeGlobals(): void {
vi.useRealTimers();
window.__hfRuntimeTeardown?.();
document.head.innerHTML = "";
document.body.innerHTML = "";
window.__timelines = {};
delete window.__player;
delete window.__playerReady;
delete window.__renderReady;
delete window.__hfTimelinesBuilding;
const win = window as {
__hyperframeRuntimeBootstrapped?: boolean;
__hfFirstPassHidden?: boolean;
};
delete win.__hyperframeRuntimeBootstrapped;
delete win.__hfFirstPassHidden;
delete (document as { readyState?: unknown }).readyState;
}
// The first evaluation transforms the whole runtime graph. Paying it while the file loads keeps
// that one-time cost out of the first test's timeout; every test still evaluates a fresh copy.
await evaluateRuntime();
resetRuntimeGlobals();
const visibility = (...els: HTMLElement[]) => els.map((el) => getComputedStyle(el).visibility);
const imageSkipped = (...clips: HTMLElement[]) =>
clips.map((clip) => getComputedStyle(clip.querySelector("img")!).display === "none");
@@ -70,24 +94,7 @@ const neverDecodes = (clip: HTMLElement) => {
};
describe("runtime entry", () => {
afterEach(() => {
vi.useRealTimers();
window.__hfRuntimeTeardown?.();
document.head.innerHTML = "";
document.body.innerHTML = "";
window.__timelines = {};
delete window.__player;
delete window.__playerReady;
delete window.__renderReady;
delete window.__hfTimelinesBuilding;
const win = window as {
__hyperframeRuntimeBootstrapped?: boolean;
__hfFirstPassHidden?: boolean;
};
delete win.__hyperframeRuntimeBootstrapped;
delete win.__hfFirstPassHidden;
delete (document as { readyState?: unknown }).readyState;
});
afterEach(resetRuntimeGlobals);
it("paints no timed clip, from script evaluation until the first visibility pass decides it", async () => {
servePreview();
+23
View File
@@ -3798,6 +3798,29 @@ describe("initSandboxRuntimeModular", () => {
expect(window.__renderReady).toBe(true);
});
it("a torn-down runtime's pending readiness check leaves the next document alone", () => {
vi.useFakeTimers();
try {
const root = document.createElement("div");
root.setAttribute("data-composition-id", "main");
root.setAttribute("data-root", "true");
root.setAttribute("data-start", "0");
document.body.appendChild(root);
window.__timelines = { main: createMockTimeline(10) };
initSandboxRuntimeModular();
window.__hfRuntimeTeardown?.();
// The next document is still batching its timelines when the old check fires.
delete window.__renderReady;
window.__hfTimelinesBuilding = true;
vi.runAllTimers();
expect(window.__renderReady).toBeUndefined();
} finally {
vi.useRealTimers();
}
});
it("sets __renderReady even without a GSAP timeline (CSS/WAAPI compositions)", () => {
const root = document.createElement("div");
root.setAttribute("data-composition-id", "main");
+1
View File
@@ -4114,6 +4114,7 @@ export function initSandboxRuntimeModular(): void {
});
maybePublishRenderReady = () => {
if (state.tornDown) return;
if (!externalCompositionsReady) {
window.__renderReady = false;
return;