import assert from 'node:assert/strict'; import test from 'node:test'; import { createAutoplayReadyGate } from './autoplay-ready-gate'; test('autoplay ready gate suppresses duplicate media signals unless forced while paused', async () => { const commands: Array> = []; const scheduled: Array<() => void> = []; const gate = createAutoplayReadyGate({ isAppOwnedFlowInFlight: () => false, getCurrentMediaPath: () => '/media/video.mkv', getCurrentVideoPath: () => null, getPlaybackPaused: () => true, getMpvClient: () => ({ connected: true, requestProperty: async () => true, send: ({ command }: { command: Array }) => { commands.push(command); }, }) as never, signalPluginAutoplayReady: () => { commands.push(['script-message', 'subminer-autoplay-ready']); }, schedule: (callback) => { scheduled.push(callback); return 1 as never; }, logDebug: () => {}, }); gate.maybeSignalPluginAutoplayReady({ text: '字幕', tokens: null }); gate.maybeSignalPluginAutoplayReady({ text: '字幕', tokens: null }); gate.maybeSignalPluginAutoplayReady({ text: '字幕', tokens: null }, { forceWhilePaused: true }); await new Promise((resolve) => setTimeout(resolve, 0)); const firstScheduled = scheduled.shift(); firstScheduled?.(); await new Promise((resolve) => setTimeout(resolve, 0)); assert.deepEqual(commands.filter((command) => command[0] === 'script-message'), [ ['script-message', 'subminer-autoplay-ready'], ]); assert.ok( commands.some( (command) => command[0] === 'set_property' && command[1] === 'pause' && command[2] === false, ), ); assert.equal(scheduled.length > 0, true); }); test('autoplay ready gate retry loop does not re-signal plugin readiness', async () => { const commands: Array> = []; const scheduled: Array<() => void> = []; const gate = createAutoplayReadyGate({ isAppOwnedFlowInFlight: () => false, getCurrentMediaPath: () => '/media/video.mkv', getCurrentVideoPath: () => null, getPlaybackPaused: () => true, getMpvClient: () => ({ connected: true, requestProperty: async () => true, send: ({ command }: { command: Array }) => { commands.push(command); }, }) as never, signalPluginAutoplayReady: () => { commands.push(['script-message', 'subminer-autoplay-ready']); }, schedule: (callback) => { scheduled.push(callback); return 1 as never; }, logDebug: () => {}, }); gate.maybeSignalPluginAutoplayReady({ text: '字幕', tokens: null }, { forceWhilePaused: true }); await new Promise((resolve) => setTimeout(resolve, 0)); for (const callback of scheduled.splice(0, 3)) { callback(); await new Promise((resolve) => setTimeout(resolve, 0)); } assert.deepEqual(commands.filter((command) => command[0] === 'script-message'), [ ['script-message', 'subminer-autoplay-ready'], ]); assert.equal( commands.filter( (command) => command[0] === 'set_property' && command[1] === 'pause' && command[2] === false, ).length > 0, true, ); });