import { describe, it, expect, beforeEach, vi } from "vitest"; import { setBackgroundAudioEnabled } from "./backgroundAudio"; /** * Bridge-reporting contract for the background-audio toggle. * * TRACES: UR-040 | IR-025, DR-051 | UT-062 * * Regression guard for the "screen lock kills video audio" bug: MainActivity * re-ran configureWebViewForMedia() on every onResume, re-calling * addJavascriptInterface over a live page. WebView then served a stale proxy — * `window.AndroidBackgroundAudio` stayed truthy but its methods were gone, so * `setEnabled` threw `TypeError: e.setEnabled is not a function`. * * The old implementation swallowed that with `bridge()?.setEnabled(...)` inside * a try/catch returning void, so the UI showed "armed" while native never got * the flag — and onStop's `if (backgroundAudioEnabled)` guard never dispatched * `jellytau-background`. Audio died the instant the screen locked. * * setBackgroundAudioEnabled must therefore REPORT whether native was actually * reached, so a dead bridge can never masquerade as an armed toggle. */ describe("setBackgroundAudioEnabled", () => { beforeEach(() => { delete (window as unknown as Record).AndroidBackgroundAudio; vi.restoreAllMocks(); }); it("reports success when the bridge is present and the call lands", () => { const setEnabled = vi.fn(); window.AndroidBackgroundAudio = { setEnabled }; expect(setBackgroundAudioEnabled(true)).toBe(true); expect(setEnabled).toHaveBeenCalledWith(true); }); it("reports failure when the bridge object is absent entirely", () => { expect(setBackgroundAudioEnabled(true)).toBe(false); }); it("reports failure for a stale proxy whose methods are gone", () => { // The exact shape of the bug: object present (so `?.` passes) but the // method is missing after re-injection over a live page. window.AndroidBackgroundAudio = {} as unknown as typeof window.AndroidBackgroundAudio; expect(setBackgroundAudioEnabled(true)).toBe(false); }); it("reports failure when the bridge method throws", () => { window.AndroidBackgroundAudio = { setEnabled: () => { throw new TypeError("e.setEnabled is not a function"); }, }; expect(setBackgroundAudioEnabled(true)).toBe(false); }); it("never throws out to the caller — the toggle must not break the player", () => { window.AndroidBackgroundAudio = { setEnabled: () => { throw new Error("boom"); }, }; expect(() => setBackgroundAudioEnabled(false)).not.toThrow(); }); });