diff --git a/web/eslint.config.js b/web/eslint.config.js index 3867729..8769989 100644 --- a/web/eslint.config.js +++ b/web/eslint.config.js @@ -23,10 +23,10 @@ export default defineConfig([ js.configs.recommended, ...pluginVue.configs['flat/essential'], - + { ...pluginVitest.configs.recommended, - files: ['src/**/__tests__/*'], + files: ['src/**/__tests__/*', 'src/test/**'], }, skipFormatting, ]) diff --git a/web/src/stores/__tests__/butterfliu.spec.js b/web/src/stores/__tests__/butterfliu.spec.js index bae1a90..476214e 100644 --- a/web/src/stores/__tests__/butterfliu.spec.js +++ b/web/src/stores/__tests__/butterfliu.spec.js @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest' +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' import { useButterfliuStore } from '../butterfliu' describe('butterfliu store', () => { @@ -6,13 +6,22 @@ describe('butterfliu store', () => { vi.restoreAllMocks() }) + afterEach(() => { + vi.unstubAllGlobals() + }) + + function mockFetch(impl) { + vi.stubGlobal('fetch', vi.fn().mockImplementation(impl)) + return fetch + } + describe('fetchLibraries', () => { it('should fetch and set libraries on success', async () => { const mockLibs = [{ id: 1, name: 'Test Lib', path: '/music' }] - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockLibs), - }) + })) const store = useButterfliuStore() await store.fetchLibraries() @@ -22,10 +31,10 @@ describe('butterfliu store', () => { }) it('should set error on failure', async () => { - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: false, status: 500, - }) + })) const store = useButterfliuStore() await store.fetchLibraries() @@ -34,7 +43,7 @@ describe('butterfliu store', () => { }) it('should set error on network failure', async () => { - global.fetch = vi.fn().mockRejectedValue(new Error('Network error')) + mockFetch(() => Promise.reject(new Error('Network error'))) const store = useButterfliuStore() await store.fetchLibraries() @@ -46,25 +55,25 @@ describe('butterfliu store', () => { describe('scanLibrary', () => { it('should POST to scan endpoint and return result', async () => { const mockResult = { added: 5, updated: 2 } - global.fetch = vi.fn().mockResolvedValue({ + const fetchMock = mockFetch(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockResult), - }) + })) const store = useButterfliuStore() const result = await store.scanLibrary(1) expect(result).toEqual(mockResult) - expect(global.fetch).toHaveBeenCalledWith('/api/libraries/1/scan', { + expect(fetchMock).toHaveBeenCalledWith('/api/libraries/1/scan', { method: 'POST', }) }) it('should set error on failure', async () => { - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: false, status: 404, - }) + })) const store = useButterfliuStore() const result = await store.scanLibrary(99) @@ -77,16 +86,16 @@ describe('butterfliu store', () => { describe('addLibrary', () => { it('should POST to libraries endpoint with name and path', async () => { const mockLib = { id: 1, name: 'New Lib', path: '/new' } - global.fetch = vi.fn().mockResolvedValue({ + const fetchMock = mockFetch(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockLib), - }) + })) const store = useButterfliuStore() const result = await store.addLibrary('New Lib', '/new') expect(result).toEqual(mockLib) - expect(global.fetch).toHaveBeenCalledWith('/api/libraries', { + expect(fetchMock).toHaveBeenCalledWith('/api/libraries', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'New Lib', path: '/new' }), @@ -94,10 +103,10 @@ describe('butterfliu store', () => { }) it('should set error on failure', async () => { - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: false, status: 400, - }) + })) const store = useButterfliuStore() const result = await store.addLibrary('Bad', '/path') @@ -110,25 +119,25 @@ describe('butterfliu store', () => { describe('deleteLibrary', () => { it('should DELETE the library', async () => { const mockResult = { success: true } - global.fetch = vi.fn().mockResolvedValue({ + const fetchMock = mockFetch(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockResult), - }) + })) const store = useButterfliuStore() const result = await store.deleteLibrary(1) expect(result).toEqual(mockResult) - expect(global.fetch).toHaveBeenCalledWith('/api/libraries/1', { + expect(fetchMock).toHaveBeenCalledWith('/api/libraries/1', { method: 'DELETE', }) }) it('should set error on failure', async () => { - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: false, status: 500, - }) + })) const store = useButterfliuStore() const result = await store.deleteLibrary(1) @@ -141,10 +150,10 @@ describe('butterfliu store', () => { describe('fetchLibrarySongs', () => { it('should fetch songs for a library', async () => { const mockSongs = [{ id: 1, title: 'Song 1' }] - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockSongs), - }) + })) const store = useButterfliuStore() const songs = await store.fetchLibrarySongs(1) @@ -153,10 +162,10 @@ describe('butterfliu store', () => { }) it('should throw on failure', async () => { - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: false, status: 500, - }) + })) const store = useButterfliuStore() await expect(store.fetchLibrarySongs(1)).rejects.toThrow('HTTP 500') @@ -169,23 +178,23 @@ describe('butterfliu store', () => { { id: 1, title: 'Song 1' }, { id: 2, title: 'Song 2' }, ] - global.fetch = vi.fn().mockResolvedValue({ + const fetchMock = mockFetch(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockSongs), - }) + })) const store = useButterfliuStore() const songs = await store.fetchAllSongs() expect(songs).toEqual(mockSongs) - expect(global.fetch).toHaveBeenCalledWith('/api/songs') + expect(fetchMock).toHaveBeenCalledWith('/api/songs') }) it('should throw on failure', async () => { - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: false, status: 503, - }) + })) const store = useButterfliuStore() await expect(store.fetchAllSongs()).rejects.toThrow('HTTP 503') @@ -195,10 +204,10 @@ describe('butterfliu store', () => { describe('fetchArtists', () => { it('should fetch artists', async () => { const mockArtists = ['Artist 1', 'Artist 2'] - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockArtists), - }) + })) const store = useButterfliuStore() const artists = await store.fetchArtists() @@ -210,10 +219,10 @@ describe('butterfliu store', () => { describe('fetchAlbums', () => { it('should fetch albums', async () => { const mockAlbums = ['Album 1', 'Album 2'] - global.fetch = vi.fn().mockResolvedValue({ + mockFetch(() => Promise.resolve({ ok: true, json: () => Promise.resolve(mockAlbums), - }) + })) const store = useButterfliuStore() const albums = await store.fetchAlbums() diff --git a/web/src/test/setup.js b/web/src/test/setup.js index 4c0a10c..7bb27cf 100644 --- a/web/src/test/setup.js +++ b/web/src/test/setup.js @@ -52,7 +52,7 @@ class MockAudio { } } -global.Audio = MockAudio +globalThis.Audio = MockAudio beforeEach(() => { setActivePinia(createPinia())