/** * Adapter-selection regression guards. * * TRACES: UR-003, UR-004 | DR-150 | UT-149 * * The selection rule has two inputs and one hard safety property: * * - Rust says which backend the platform has (`backendKind`). * - The user opts in with `experimentalNativeVideo`. * - **The flag off must force HTML5 even when Rust says native.** That is the * regression guard: a broken spike must not be able to ship as the default. * * These are pure functions, so the whole matrix is testable without a device. */ import { describe, expect, it } from "vitest"; import { createAdapter } from "./index"; import { Html5PlayerAdapter } from "./html5Adapter"; import { NativePlayerAdapter } from "./nativeAdapter"; import type { AdapterHost } from "./types"; const host: AdapterHost = { reportState: () => {}, reportPosition: () => {}, reportEnded: () => {}, } as unknown as AdapterHost; const bridge = { getElement: () => null, } as any; describe("createAdapter", () => { it("returns the native adapter when Rust says native and the flag is on", () => { const adapter = createAdapter({ backendKind: "native", host, bridge, experimentalNativeVideo: true, }); expect(adapter).toBeInstanceOf(NativePlayerAdapter); expect(adapter.kind).toBe("native"); }); // The regression guard: the flag is a suppressor, so off must beat Rust. it("forces HTML5 when the flag is off even though Rust says native", () => { const adapter = createAdapter({ backendKind: "native", host, bridge, experimentalNativeVideo: false, }); expect(adapter).toBeInstanceOf(Html5PlayerAdapter); expect(adapter.kind).toBe("html5"); }); it("returns the HTML5 adapter when Rust says html5 and the flag is off", () => { const adapter = createAdapter({ backendKind: "html5", host, bridge, experimentalNativeVideo: false, }); expect(adapter).toBeInstanceOf(Html5PlayerAdapter); }); // The flag must never *promote* a platform Rust said has no native backend // (e.g. Linux, where WebKitGTK cannot composite a surface behind the webview). it("stays on HTML5 when Rust says html5 even with the flag on", () => { const adapter = createAdapter({ backendKind: "html5", host, bridge, experimentalNativeVideo: true, }); expect(adapter).toBeInstanceOf(Html5PlayerAdapter); }); it("defaults to HTML5 when the flag is omitted entirely", () => { const adapter = createAdapter({ backendKind: "native", host, bridge }); expect(adapter).toBeInstanceOf(Html5PlayerAdapter); }); it("requires a bridge for the HTML5 adapter", () => { expect(() => createAdapter({ backendKind: "html5", host, experimentalNativeVideo: false }) ).toThrow(/bridge/i); }); // The native adapter owns no DOM element, so it must not demand a bridge. it("does not require a bridge for the native adapter", () => { expect(() => createAdapter({ backendKind: "native", host, experimentalNativeVideo: true }) ).not.toThrow(); }); });