feat(web): add enqueueTrack, enqueueTracks, playRadio to player store

enqueueTrack/enqueueTracks append to the queue without disturbing
playback state (no auto-play on append, no state mutation when queue
was non-empty). playRadio fetches /api/radio?seed_track=<id> and
feeds the response into playQueue. Empty response leaves queue alone.
This commit is contained in:
2026-04-25 15:22:03 -04:00
parent 8e80b6e0de
commit 7dd77a4723
2 changed files with 74 additions and 1 deletions
+54
View File
@@ -279,3 +279,57 @@ describe('player store — shuffle + repeat + audio reports', () => {
expect(player.error).toBe('network lost');
});
});
import { enqueueTrack, enqueueTracks, playRadio } from './store.svelte';
import * as client from '$lib/api/client';
describe('player store — enqueue + playRadio', () => {
test('enqueueTrack appends to a non-empty queue, _index unchanged', () => {
playQueue([track('1'), track('2')], 1);
enqueueTrack(track('3'));
expect(player.queue.length).toBe(3);
expect(player.queue[2].id).toBe('3');
expect(player.index).toBe(1);
});
test('enqueueTrack on idle (empty queue) sets index=0 and _state stays idle', () => {
playQueue([]);
expect(player.state).toBe('idle');
enqueueTrack(track('1'));
expect(player.queue.length).toBe(1);
expect(player.index).toBe(0);
expect(player.state).toBe('idle');
});
test('enqueueTracks([]) is a no-op', () => {
playQueue([track('1')]);
enqueueTracks([]);
expect(player.queue.length).toBe(1);
});
test('enqueueTracks concatenates non-empty list', () => {
playQueue([track('1')]);
enqueueTracks([track('2'), track('3')]);
expect(player.queue.map((t) => t.id)).toEqual(['1', '2', '3']);
});
test('playRadio fetches /api/radio and feeds tracks into playQueue', async () => {
const apiGet = vi.spyOn(client.api, 'get').mockResolvedValueOnce({
tracks: [track('seed'), track('next')]
});
await playRadio('seed');
expect(apiGet).toHaveBeenCalledWith('/api/radio?seed_track=seed');
expect(player.queue.length).toBe(2);
expect(player.index).toBe(0);
expect(player.state).toBe('loading');
apiGet.mockRestore();
});
test('playRadio with empty response leaves queue alone', async () => {
playQueue([track('a')]);
const apiGet = vi.spyOn(client.api, 'get').mockResolvedValueOnce({ tracks: [] });
await playRadio('seed');
expect(player.queue.map((t) => t.id)).toEqual(['a']);
apiGet.mockRestore();
});
});