import assert from 'node:assert/strict'; import test from 'node:test'; import { setVisibleOverlayVisible, updateVisibleOverlayVisibility } from './overlay-visibility'; type WindowTrackerStub = { isTracking: () => boolean; getGeometry: () => { x: number; y: number; width: number; height: number } | null; }; function createMainWindowRecorder() { const calls: string[] = []; const window = { isDestroyed: () => false, hide: () => { calls.push('hide'); }, show: () => { calls.push('show'); }, focus: () => { calls.push('focus'); }, setIgnoreMouseEvents: () => { calls.push('mouse-ignore'); }, }; return { window, calls }; } test('macOS keeps visible overlay hidden while tracker is not ready and emits one loading OSD', () => { const { window, calls } = createMainWindowRecorder(); let trackerWarning = false; const osdMessages: string[] = []; const tracker: WindowTrackerStub = { isTracking: () => false, getGeometry: () => null, }; const run = () => updateVisibleOverlayVisibility({ visibleOverlayVisible: true, mainWindow: window as never, windowTracker: tracker as never, trackerNotReadyWarningShown: trackerWarning, setTrackerNotReadyWarningShown: (shown: boolean) => { trackerWarning = shown; }, updateVisibleOverlayBounds: () => { calls.push('update-bounds'); }, ensureOverlayWindowLevel: () => { calls.push('ensure-level'); }, syncPrimaryOverlayWindowLayer: () => { calls.push('sync-layer'); }, enforceOverlayLayerOrder: () => { calls.push('enforce-order'); }, syncOverlayShortcuts: () => { calls.push('sync-shortcuts'); }, isMacOSPlatform: true, showOverlayLoadingOsd: (message: string) => { osdMessages.push(message); }, } as never); run(); run(); assert.equal(trackerWarning, true); assert.deepEqual(osdMessages, ['Overlay loading...']); assert.ok(calls.includes('hide')); assert.ok(!calls.includes('show')); }); test('non-macOS keeps fallback visible overlay behavior when tracker is not ready', () => { const { window, calls } = createMainWindowRecorder(); let trackerWarning = false; const tracker: WindowTrackerStub = { isTracking: () => false, getGeometry: () => null, }; updateVisibleOverlayVisibility({ visibleOverlayVisible: true, mainWindow: window as never, windowTracker: tracker as never, trackerNotReadyWarningShown: trackerWarning, setTrackerNotReadyWarningShown: (shown: boolean) => { trackerWarning = shown; }, updateVisibleOverlayBounds: () => { calls.push('update-bounds'); }, ensureOverlayWindowLevel: () => { calls.push('ensure-level'); }, syncPrimaryOverlayWindowLayer: () => { calls.push('sync-layer'); }, enforceOverlayLayerOrder: () => { calls.push('enforce-order'); }, syncOverlayShortcuts: () => { calls.push('sync-shortcuts'); }, isMacOSPlatform: false, showOverlayLoadingOsd: () => { calls.push('osd'); }, resolveFallbackBounds: () => ({ x: 12, y: 24, width: 640, height: 360 }), } as never); assert.equal(trackerWarning, true); assert.ok(calls.includes('update-bounds')); assert.ok(calls.includes('show')); assert.ok(calls.includes('focus')); assert.ok(!calls.includes('osd')); }); test('macOS keeps visible overlay hidden while tracker is not initialized yet', () => { const { window, calls } = createMainWindowRecorder(); let trackerWarning = false; const osdMessages: string[] = []; updateVisibleOverlayVisibility({ visibleOverlayVisible: true, mainWindow: window as never, windowTracker: null, trackerNotReadyWarningShown: trackerWarning, setTrackerNotReadyWarningShown: (shown: boolean) => { trackerWarning = shown; }, updateVisibleOverlayBounds: () => { calls.push('update-bounds'); }, ensureOverlayWindowLevel: () => { calls.push('ensure-level'); }, syncPrimaryOverlayWindowLayer: () => { calls.push('sync-layer'); }, enforceOverlayLayerOrder: () => { calls.push('enforce-order'); }, syncOverlayShortcuts: () => { calls.push('sync-shortcuts'); }, isMacOSPlatform: true, showOverlayLoadingOsd: (message: string) => { osdMessages.push(message); }, } as never); assert.equal(trackerWarning, true); assert.deepEqual(osdMessages, ['Overlay loading...']); assert.ok(calls.includes('hide')); assert.ok(!calls.includes('show')); assert.ok(!calls.includes('update-bounds')); }); test('setVisibleOverlayVisible does not mutate mpv subtitle visibility directly', () => { const calls: string[] = []; setVisibleOverlayVisible({ visible: true, setVisibleOverlayVisibleState: (visible) => { calls.push(`state:${visible}`); }, updateVisibleOverlayVisibility: () => { calls.push('update'); }, }); assert.deepEqual(calls, ['state:true', 'update']); }); test('macOS loading OSD can show again after overlay is hidden and retried', () => { const { window, calls } = createMainWindowRecorder(); const osdMessages: string[] = []; let trackerWarning = false; updateVisibleOverlayVisibility({ visibleOverlayVisible: true, mainWindow: window as never, windowTracker: null, trackerNotReadyWarningShown: trackerWarning, setTrackerNotReadyWarningShown: (shown: boolean) => { trackerWarning = shown; calls.push(`warn:${shown ? 'yes' : 'no'}`); }, updateVisibleOverlayBounds: () => { calls.push('update-bounds'); }, ensureOverlayWindowLevel: () => { calls.push('ensure-level'); }, syncPrimaryOverlayWindowLayer: () => { calls.push('sync-layer'); }, enforceOverlayLayerOrder: () => { calls.push('enforce-order'); }, syncOverlayShortcuts: () => { calls.push('sync-shortcuts'); }, isMacOSPlatform: true, showOverlayLoadingOsd: (message: string) => { osdMessages.push(message); }, } as never); updateVisibleOverlayVisibility({ visibleOverlayVisible: false, mainWindow: window as never, windowTracker: null, trackerNotReadyWarningShown: trackerWarning, setTrackerNotReadyWarningShown: (shown: boolean) => { trackerWarning = shown; calls.push(`warn:${shown ? 'yes' : 'no'}`); }, updateVisibleOverlayBounds: () => {}, ensureOverlayWindowLevel: () => {}, syncPrimaryOverlayWindowLayer: () => {}, enforceOverlayLayerOrder: () => {}, syncOverlayShortcuts: () => {}, isMacOSPlatform: true, showOverlayLoadingOsd: () => {}, } as never); updateVisibleOverlayVisibility({ visibleOverlayVisible: true, mainWindow: window as never, windowTracker: null, trackerNotReadyWarningShown: trackerWarning, setTrackerNotReadyWarningShown: (shown: boolean) => { trackerWarning = shown; calls.push(`warn:${shown ? 'yes' : 'no'}`); }, updateVisibleOverlayBounds: () => { calls.push('update-bounds'); }, ensureOverlayWindowLevel: () => { calls.push('ensure-level'); }, syncPrimaryOverlayWindowLayer: () => { calls.push('sync-layer'); }, enforceOverlayLayerOrder: () => { calls.push('enforce-order'); }, syncOverlayShortcuts: () => { calls.push('sync-shortcuts'); }, isMacOSPlatform: true, showOverlayLoadingOsd: (message: string) => { osdMessages.push(message); }, } as never); assert.deepEqual(osdMessages, ['Overlay loading...', 'Overlay loading...']); });