|
|
@@ -45,6 +45,131 @@ export default function playbackCoordinatorTest() {
|
|
|
coordinator.clearRuntime()
|
|
|
})
|
|
|
|
|
|
+ it('playSongPropagatesStartIndexAndSourceAndUpdatesBridge', 0, async () => {
|
|
|
+ const coordinator = PlaybackCoordinator.getInstance()
|
|
|
+ const bridge = PlaybackStateBridge.getInstance()
|
|
|
+ const song = new VideoItem('Song C', '3', '/music/c.flac', 0, 0, '2026-04-02')
|
|
|
+ const runtimeCalls: string[] = []
|
|
|
+
|
|
|
+ bridge.reset()
|
|
|
+ coordinator.clearRuntime()
|
|
|
+ coordinator.setRuntime({
|
|
|
+ playQueue: async (_queue, startIndex, source) => {
|
|
|
+ runtimeCalls.push(`${startIndex}:${source}`)
|
|
|
+ },
|
|
|
+ playOrPause: async () => {},
|
|
|
+ playNext: async () => {},
|
|
|
+ playPrevious: async () => {},
|
|
|
+ seekTo: async () => {}
|
|
|
+ })
|
|
|
+
|
|
|
+ await coordinator.playSong(song, 'playlist-detail')
|
|
|
+
|
|
|
+ const snapshot = bridge.getSnapshot()
|
|
|
+ expect(runtimeCalls.join(',')).assertEqual('0:playlist-detail')
|
|
|
+ expect(snapshot.currentIndex).assertEqual(0)
|
|
|
+ expect(snapshot.currentSong?.filePath ?? '').assertEqual('/music/c.flac')
|
|
|
+ coordinator.clearRuntime()
|
|
|
+ })
|
|
|
+
|
|
|
+ it('clearRuntimeOnlyRemovesMatchingInstance', 0, async () => {
|
|
|
+ const coordinator = PlaybackCoordinator.getInstance()
|
|
|
+ let playOrPauseCount = 0
|
|
|
+
|
|
|
+ const runtimeA: PlaybackRuntime = {
|
|
|
+ playQueue: async () => {},
|
|
|
+ playOrPause: async () => {
|
|
|
+ playOrPauseCount++
|
|
|
+ },
|
|
|
+ playNext: async () => {},
|
|
|
+ playPrevious: async () => {},
|
|
|
+ seekTo: async () => {}
|
|
|
+ }
|
|
|
+
|
|
|
+ const runtimeB: PlaybackRuntime = {
|
|
|
+ playQueue: async () => {},
|
|
|
+ playOrPause: async () => {},
|
|
|
+ playNext: async () => {},
|
|
|
+ playPrevious: async () => {},
|
|
|
+ seekTo: async () => {}
|
|
|
+ }
|
|
|
+
|
|
|
+ coordinator.clearRuntime()
|
|
|
+ coordinator.setRuntime(runtimeA)
|
|
|
+ coordinator.clearRuntime(runtimeB)
|
|
|
+
|
|
|
+ await coordinator.playOrPause()
|
|
|
+ expect(playOrPauseCount).assertEqual(1)
|
|
|
+
|
|
|
+ coordinator.clearRuntime(runtimeA)
|
|
|
+ await coordinator.playOrPause()
|
|
|
+ expect(playOrPauseCount).assertEqual(1)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('playQueueWithoutRuntimeKeepsBridgeState', 0, async () => {
|
|
|
+ const coordinator = PlaybackCoordinator.getInstance()
|
|
|
+ const bridge = PlaybackStateBridge.getInstance()
|
|
|
+ const initialSong = new VideoItem('Initial', 'init', '/music/initial.flac', 0, 0, '2026-04-02')
|
|
|
+
|
|
|
+ bridge.reset()
|
|
|
+ bridge.replaceQueue([initialSong], 0)
|
|
|
+ const before = bridge.getSnapshot()
|
|
|
+
|
|
|
+ coordinator.clearRuntime()
|
|
|
+ await coordinator.playQueue(
|
|
|
+ [new VideoItem('Song D', '4', '/music/d.flac', 0, 0, '2026-04-02')],
|
|
|
+ 0,
|
|
|
+ 'charts'
|
|
|
+ )
|
|
|
+
|
|
|
+ const after = bridge.getSnapshot()
|
|
|
+ expect(after.currentIndex).assertEqual(before.currentIndex)
|
|
|
+ expect(after.currentSong?.filePath ?? '').assertEqual(before.currentSong?.filePath ?? '')
|
|
|
+ expect(after.queue.length).assertEqual(before.queue.length)
|
|
|
+ })
|
|
|
+
|
|
|
+ it('playQueueRollsBackOnRuntimeError', 0, async () => {
|
|
|
+ const coordinator = PlaybackCoordinator.getInstance()
|
|
|
+ const bridge = PlaybackStateBridge.getInstance()
|
|
|
+ const previousSong = new VideoItem('Previous', 'prev', '/music/prev.flac', 0, 0, '2026-04-02')
|
|
|
+
|
|
|
+ bridge.reset()
|
|
|
+ bridge.replaceQueue([previousSong], 0)
|
|
|
+ const snapshotBefore = bridge.getSnapshot()
|
|
|
+
|
|
|
+ let threwError = false
|
|
|
+ coordinator.clearRuntime()
|
|
|
+ coordinator.setRuntime({
|
|
|
+ playQueue: async () => {
|
|
|
+ throw new Error('boom')
|
|
|
+ },
|
|
|
+ playOrPause: async () => {},
|
|
|
+ playNext: async () => {},
|
|
|
+ playPrevious: async () => {},
|
|
|
+ seekTo: async () => {}
|
|
|
+ })
|
|
|
+
|
|
|
+ try {
|
|
|
+ await coordinator.playQueue(
|
|
|
+ [
|
|
|
+ new VideoItem('Song E', '5', '/music/e.flac', 0, 0, '2026-04-02'),
|
|
|
+ new VideoItem('Song F', '6', '/music/f.flac', 0, 0, '2026-04-02')
|
|
|
+ ],
|
|
|
+ 0,
|
|
|
+ 'playlist'
|
|
|
+ )
|
|
|
+ } catch (error) {
|
|
|
+ threwError = true
|
|
|
+ }
|
|
|
+
|
|
|
+ expect(threwError).assertEqual(true)
|
|
|
+ const snapshotAfter = bridge.getSnapshot()
|
|
|
+ expect(snapshotAfter.queue.length).assertEqual(snapshotBefore.queue.length)
|
|
|
+ expect(snapshotAfter.currentIndex).assertEqual(snapshotBefore.currentIndex)
|
|
|
+ expect(snapshotAfter.currentSong?.filePath ?? '').assertEqual(snapshotBefore.currentSong?.filePath ?? '')
|
|
|
+ coordinator.clearRuntime()
|
|
|
+ })
|
|
|
+
|
|
|
it('playSongWrapsSingleSongQueue', 0, async () => {
|
|
|
const coordinator = PlaybackCoordinator.getInstance()
|
|
|
let queueLength = 0
|