diff --git a/packages/player/src/composition-probe.test.ts b/packages/player/src/composition-probe.test.ts index 2b4a749ac4..1c6cab8e78 100644 --- a/packages/player/src/composition-probe.test.ts +++ b/packages/player/src/composition-probe.test.ts @@ -1,5 +1,10 @@ -import { describe, expect, it } from "vitest"; -import { readCompositionSizeFromDocument, runtimeCdnUrlForVersion } from "./composition-probe.js"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { + CompositionProbe, + readCompositionSizeFromDocument, + runtimeCdnUrlForVersion, +} from "./composition-probe.js"; +import { RUNTIME_CDN_URL } from "./runtime-url.js"; describe("readCompositionSizeFromDocument", () => { it("reads dimensions from the composition root", () => { @@ -36,3 +41,184 @@ describe("runtimeCdnUrlForVersion", () => { expect(() => runtimeCdnUrlForVersion("latest")).toThrow("Invalid HyperFrames runtime version"); }); }); + +// ── Runtime detection and injection ── +// +// `window.__hf` is a namespace, not a bridge. The core runtime creates it, but +// so does `@hyperframes/shader-transitions` (to publish `shaderTransitionsReady`), +// so an authored composition that uses shader transitions and registers its +// own `__timelines` carries `__hf` with no runtime behind it. The probe used +// to read that as "runtime present": it never injected the runtime, refused +// the direct-timeline adapter, and the embed timed out after 8 s. The only +// global the player ever drives is `__player`, so that is the bridge check. +describe("CompositionProbe runtime detection", () => { + type FakeTimeline = { + duration: () => number; + time: () => number; + seek: () => void; + play: () => void; + pause: () => void; + }; + type FakeWindow = { + __hf?: unknown; + __player?: unknown; + __timelines?: Record; + }; + type FakeScript = { src: string; onerror: (() => void) | null }; + + function fakeTimeline(duration = 10): FakeTimeline { + return { duration: () => duration, time: () => 0, seek() {}, play() {}, pause() {} }; + } + + // A minimal contentDocument so injection does not go through happy-dom's + // real `