const { test } = require('node:test'); const assert = require('node:assert/strict'); const { progressiveSource, needsAlignment, create } = require('./audio-continuity'); test('iPhone progressive audio uses saved/EQ sources before remote fallbacks', () => { assert.equal(progressiveSource({ localUrl: 'blob:saved', audioUrl: 'remote' }, 'mux'), 'blob:saved'); assert.equal(progressiveSource({ eqAudioUrl: 'blob:eq', localUrl: 'blob:saved' }), 'blob:eq'); assert.equal(progressiveSource({ audioUrl: 'audio' }, 'mux'), 'audio'); assert.equal(progressiveSource({}, 'mux'), 'mux'); }); test('small drift and invalid clocks do not trigger an audible seek', () => { assert.equal(needsAlignment(50.05, 50), false); assert.equal(needsAlignment(49, 50), true); assert.equal(needsAlignment(NaN, 50), false); }); test('simultaneous play, visibility and watchdog resumes share one pending play', async () => { let finish, calls = 0, seeks = 0; const audio = { paused: true, get currentTime() { return 10; }, set currentTime(value) { seeks++; }, play() { calls++; return new Promise(resolve => { finish = resolve; }); } }; const controller = create(); controller.resume(audio, { currentTime: 10.05 }); controller.resume(audio, { currentTime: 12 }); const pending = controller.play(audio); assert.equal(calls, 1); assert.equal(seeks, 0); finish(); await pending; controller.resume(audio, audio); // never seek an element to itself assert.equal(seeks, 0); assert.equal(calls, 2); finish(); }); test('rejected autoplay can retry and only paused, misaligned secondaries seek', async () => { const controller = create(); let calls = 0; const audio = { paused: true, currentTime: 0, play() { calls++; return Promise.reject(new Error('OS suspension')); } }; await controller.play(audio); await controller.resume(audio, { currentTime: 4 }); assert.equal(calls, 2); assert.equal(audio.currentTime, 4); audio.paused = false; controller.resume(audio, { currentTime: 10 }); assert.equal(audio.currentTime, 4); assert.equal(calls, 2); }); test('a new play after pause/source reset is not blocked by a stale promise', async () => { const controller = create(), finishes = []; let calls = 0; const audio = { paused: true, play() { calls++; return new Promise(resolve => finishes.push(resolve)); } }; const old = controller.play(audio); controller.reset(audio); const fresh = controller.play(audio); assert.equal(calls, 2); finishes[0](); await old; assert.equal(controller.play(audio), fresh); // old completion cannot discard the new request finishes[1](); await fresh; }); test('an ended element is never resumed (last song must not restart from 0)', () => { let calls = 0; const audio = { paused: true, ended: true, currentTime: 200, play() { calls++; return Promise.resolve(); } }; create().resume(audio, { currentTime: 200 }); assert.equal(calls, 0); });