// @rstest-environment jsdom import { expect, test, beforeEach, rstest } from '@rstest/core'; import { loadQueueState, loadPlayerState, startPersistence, } from '../src/store/persist'; import { queueInitialState, type QueueState } from '../src/store/slices/queue'; import { playerInitialState, type PlayerState, } from '../src/store/slices/player'; import { upsertInstance, setActiveInstanceId, instanceStorage, } from '../src/config/instances'; beforeEach(() => { localStorage.clear(); const inst = upsertInstance('http://test.local'); setActiveInstanceId(inst.id); }); const sampleQueue: QueueState = { entries: [ { trackId: 't1', title: 'A', artistName: 'X', albumTitle: 'Alb', durationMs: 1000, }, { trackId: 't2', title: 'B', artistName: 'Y', albumTitle: 'Alb', durationMs: 2000, }, ], currentIndex: 1, source: 'album', sourceId: 'alb-1', sourceName: 'My Album', }; test('loaders fall back to initial state with nothing persisted', () => { expect(loadQueueState()).toEqual(queueInitialState); expect(loadPlayerState()).toEqual(playerInitialState); }); test('loadQueueState restores a persisted queue', () => { instanceStorage.set('queue', JSON.stringify(sampleQueue)); expect(loadQueueState()).toEqual(sampleQueue); }); test('loadQueueState guards a currentIndex past the entries array', () => { instanceStorage.set( 'queue', JSON.stringify({ ...sampleQueue, currentIndex: 99 }), ); expect(loadQueueState().currentIndex).toBe(0); }); test('loadPlayerState restores fields but never auto-resumes playback', () => { instanceStorage.set( 'player', JSON.stringify({ currentTrackId: 't2', position: 42, volume: 0.5, muted: true, repeat: 'all', shuffle: true, // a stale isPlaying:true must not survive a reload isPlaying: true, }), ); const loaded = loadPlayerState(); expect(loaded.currentTrackId).toBe('t2'); expect(loaded.position).toBe(42); expect(loaded.volume).toBe(0.5); expect(loaded.repeat).toBe('all'); expect(loaded.isPlaying).toBe(false); }); test('corrupt JSON falls back to initial state', () => { instanceStorage.set('queue', '{not json'); expect(loadQueueState()).toEqual(queueInitialState); }); test('startPersistence flushes changed state to storage after throttle', () => { rstest.useFakeTimers(); let state = { queue: queueInitialState, player: playerInitialState, } as { queue: QueueState; player: PlayerState }; let listener: (() => void) | null = null; const store = { getState: () => state as never, subscribe: (l: () => void) => { listener = l; return () => {}; }, }; startPersistence(store); // mutate + notify state = { ...state, queue: sampleQueue }; listener!(); // nothing written before the throttle window elapses expect(instanceStorage.get('queue')).toBeNull(); rstest.advanceTimersByTime(1000); expect(JSON.parse(instanceStorage.get('queue')!).currentIndex).toBe(1); rstest.useRealTimers(); });