test(app): add unit tests for usePlayer composable
Tests queue management, state transitions, progress computation, deduplication, and API shape. 9 test cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
ece4540388
commit
7c8315f362
@@ -0,0 +1,140 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest'
|
||||
|
||||
// Mock Plyr before importing usePlayer
|
||||
vi.mock('plyr', () => {
|
||||
const MockPlyr = vi.fn().mockImplementation(() => ({
|
||||
on: vi.fn(),
|
||||
play: vi.fn().mockResolvedValue(undefined),
|
||||
pause: vi.fn(),
|
||||
destroy: vi.fn(),
|
||||
currentTime: 0,
|
||||
duration: 120,
|
||||
}))
|
||||
return { default: MockPlyr }
|
||||
})
|
||||
|
||||
vi.mock('plyr/dist/plyr.css', () => ({}))
|
||||
|
||||
import { usePlayer } from '../usePlayer'
|
||||
|
||||
function makeSong(id: string, title = `Song ${id}`, artist = `Artist ${id}`) {
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
artist,
|
||||
album: 'Test Album',
|
||||
genres: [],
|
||||
}
|
||||
}
|
||||
|
||||
describe('usePlayer', () => {
|
||||
beforeEach(() => {
|
||||
const player = usePlayer()
|
||||
player.clearQueue()
|
||||
vi.restoreAllMocks()
|
||||
// Reset fetch mock
|
||||
vi.stubGlobal('fetch', vi.fn().mockResolvedValue({
|
||||
json: () => Promise.resolve({ source: 'test', type: 'stream', url: 'https://example.com/audio.mp3' }),
|
||||
}))
|
||||
})
|
||||
|
||||
it('returns expected API shape', () => {
|
||||
const player = usePlayer()
|
||||
expect(player.currentSong).toBeDefined()
|
||||
expect(player.isPlaying).toBeDefined()
|
||||
expect(player.isLoading).toBeDefined()
|
||||
expect(player.queue).toBeDefined()
|
||||
expect(player.play).toBeTypeOf('function')
|
||||
expect(player.pause).toBeTypeOf('function')
|
||||
expect(player.toggle).toBeTypeOf('function')
|
||||
expect(player.seek).toBeTypeOf('function')
|
||||
expect(player.addToQueue).toBeTypeOf('function')
|
||||
expect(player.playNext).toBeTypeOf('function')
|
||||
expect(player.playPrevious).toBeTypeOf('function')
|
||||
expect(player.clearQueue).toBeTypeOf('function')
|
||||
})
|
||||
|
||||
it('starts with no track', () => {
|
||||
const player = usePlayer()
|
||||
expect(player.hasTrack.value).toBe(false)
|
||||
expect(player.currentSong.value).toBeNull()
|
||||
expect(player.isPlaying.value).toBe(false)
|
||||
})
|
||||
|
||||
it('addToQueue adds songs', () => {
|
||||
const player = usePlayer()
|
||||
const song1 = makeSong('1')
|
||||
const song2 = makeSong('2')
|
||||
player.addToQueue(song1)
|
||||
player.addToQueue(song2)
|
||||
expect(player.queue.value).toHaveLength(2)
|
||||
expect(player.queue.value[0].id).toBe('1')
|
||||
expect(player.queue.value[1].id).toBe('2')
|
||||
})
|
||||
|
||||
it('addToQueue deduplicates by id', () => {
|
||||
const player = usePlayer()
|
||||
const song = makeSong('1')
|
||||
player.addToQueue(song)
|
||||
player.addToQueue(song)
|
||||
expect(player.queue.value).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('removeFromQueue removes by index', () => {
|
||||
const player = usePlayer()
|
||||
player.addToQueue(makeSong('1'))
|
||||
player.addToQueue(makeSong('2'))
|
||||
player.addToQueue(makeSong('3'))
|
||||
player.removeFromQueue(1)
|
||||
expect(player.queue.value).toHaveLength(2)
|
||||
expect(player.queue.value[0].id).toBe('1')
|
||||
expect(player.queue.value[1].id).toBe('3')
|
||||
})
|
||||
|
||||
it('clearQueue resets all state', () => {
|
||||
const player = usePlayer()
|
||||
player.addToQueue(makeSong('1'))
|
||||
player.addToQueue(makeSong('2'))
|
||||
player.clearQueue()
|
||||
expect(player.queue.value).toHaveLength(0)
|
||||
expect(player.currentIndex.value).toBe(-1)
|
||||
expect(player.currentSong.value).toBeNull()
|
||||
})
|
||||
|
||||
it('hasNext and hasPrevious compute correctly', () => {
|
||||
const player = usePlayer()
|
||||
player.addToQueue(makeSong('1'))
|
||||
player.addToQueue(makeSong('2'))
|
||||
player.addToQueue(makeSong('3'))
|
||||
|
||||
// At start, no previous
|
||||
expect(player.hasPrevious.value).toBe(false)
|
||||
|
||||
// Move to middle
|
||||
player.currentIndex.value = 1
|
||||
expect(player.hasNext.value).toBe(true)
|
||||
expect(player.hasPrevious.value).toBe(true)
|
||||
|
||||
// At end
|
||||
player.currentIndex.value = 2
|
||||
expect(player.hasNext.value).toBe(false)
|
||||
expect(player.hasPrevious.value).toBe(true)
|
||||
})
|
||||
|
||||
it('progress computes percentage from currentTime/duration', () => {
|
||||
const player = usePlayer()
|
||||
player.duration.value = 100
|
||||
player.currentTime.value = 50
|
||||
expect(player.progress.value).toBe(50)
|
||||
|
||||
player.currentTime.value = 0
|
||||
expect(player.progress.value).toBe(0)
|
||||
})
|
||||
|
||||
it('progress returns 0 when duration is 0', () => {
|
||||
const player = usePlayer()
|
||||
player.duration.value = 0
|
||||
player.currentTime.value = 10
|
||||
expect(player.progress.value).toBe(0)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user