// @vitest-environment jsdom // // The control bar must not hide while the pointer rests on it. It used to: the 3 s timer only knew // "paused" and "a menu is open", so with the mouse parked on the row (about to press Next) the bar // faded out from under it. YouTube and Plex Web both hold the bar while hovered. import { describe, it, expect, vi, afterEach, beforeEach } from "vitest"; import { act, renderHook } from "@testing-library/react"; import { useControlsVisibility } from "./plexPlayerHooks"; const setup = (menuOpen = false) => { const video = document.createElement("video"); Object.defineProperty(video, "paused", { configurable: true, value: false }); return renderHook(() => useControlsVisibility({ barAutoHideRef: { current: true }, menuOpenRef: { current: menuOpen }, videoRef: { current: video }, }), ); }; describe("useControlsVisibility", () => { beforeEach(() => vi.useFakeTimers()); afterEach(() => vi.useRealTimers()); it("hides 3 s after the last activity while playing", () => { const { result } = setup(); act(() => result.current.wake()); act(() => { vi.advanceTimersByTime(3000); }); expect(result.current.uiVisible).toBe(false); }); it("stays up while the pointer is over the bar, and re-arms the timer when it leaves", () => { const { result } = setup(); act(() => { result.current.wake(); result.current.holdOnHover(true); }); act(() => { vi.advanceTimersByTime(10_000); }); expect(result.current.uiVisible).toBe(true); act(() => result.current.holdOnHover(false)); act(() => { vi.advanceTimersByTime(2900); }); expect(result.current.uiVisible).toBe(true); // a fresh 3 s from the leave, not from the wake act(() => { vi.advanceTimersByTime(100); }); expect(result.current.uiVisible).toBe(false); }); // A keyboard focus in the chrome holds the bar — but only for FOCUS_HOLD_MS // after the last key, not for the whole film. The 3 s timer finds the hold, re-arms for what is // left of it, and hides once it is over (the re-arm runs without recursion). it("holds for a keyboard focus until 10 s after the last key, then hides", () => { const { result } = setup(); act(() => { result.current.holdOnFocus(true); result.current.wake(); }); act(() => { vi.advanceTimersByTime(3000); }); expect(result.current.uiVisible).toBe(true); // the hold, not the 3 s act(() => { vi.advanceTimersByTime(6900); }); expect(result.current.uiVisible).toBe(true); // 9.9 s after the key act(() => { vi.advanceTimersByTime(100); }); expect(result.current.uiVisible).toBe(false); // the hold is over, focus or not }); // C3: on touch nothing moves a mouse, so a tap toggles the bar and playback arms the timer. it("toggle hides a shown bar at once and shows a hidden one", () => { const { result } = setup(); act(() => result.current.toggle()); expect(result.current.uiVisible).toBe(false); act(() => result.current.toggle()); expect(result.current.uiVisible).toBe(true); }); it("a tap does not hide the bar under an open menu", () => { const { result } = setup(true); act(() => result.current.toggle()); expect(result.current.uiVisible).toBe(true); }); });