diff --git a/web/src/lib/auth/store.test.ts b/web/src/lib/auth/store.test.ts index cc81499c..c6356643 100644 --- a/web/src/lib/auth/store.test.ts +++ b/web/src/lib/auth/store.test.ts @@ -1,12 +1,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; +import { apiClientMock } from '../../test-utils/mocks/client'; -vi.mock('$lib/api/client', () => ({ - api: { - get: vi.fn(), - post: vi.fn(), - del: vi.fn() - } -})); +vi.mock('$lib/api/client', () => apiClientMock()); vi.mock('$lib/query/client', () => ({ queryClient: { clear: vi.fn() } diff --git a/web/src/lib/components/AlbumCard.test.ts b/web/src/lib/components/AlbumCard.test.ts index 39327a4c..307c2b52 100644 --- a/web/src/lib/components/AlbumCard.test.ts +++ b/web/src/lib/components/AlbumCard.test.ts @@ -3,11 +3,10 @@ import { render, screen, fireEvent } from '@testing-library/svelte'; import type { AlbumRef, AlbumDetail, TrackRef } from '$lib/api/types'; import { FALLBACK_COVER } from '$lib/media/covers'; import { emptyLikesMock } from '../../test-utils/mocks/likes'; +import { apiClientMock } from '../../test-utils/mocks/client'; import { makeTrack } from '$test-utils/fixtures/track'; -vi.mock('$lib/api/client', () => ({ - api: { get: vi.fn() } -})); +vi.mock('$lib/api/client', () => apiClientMock()); vi.mock('$lib/player/store.svelte', () => ({ enqueueTracks: vi.fn(), playQueue: vi.fn() diff --git a/web/src/lib/components/AlbumMenu.test.ts b/web/src/lib/components/AlbumMenu.test.ts index 6db143d4..6fad4573 100644 --- a/web/src/lib/components/AlbumMenu.test.ts +++ b/web/src/lib/components/AlbumMenu.test.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/svelte'; -import { readable } from 'svelte/store'; import { emptyLikesMock } from '../../test-utils/mocks/likes'; +import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists'; import type { TrackRef } from '$lib/api/types'; import { makeTracks } from '$test-utils/fixtures/track'; @@ -14,12 +14,7 @@ vi.mock('$lib/api/albums', async (orig) => { return { ...actual, listAlbumTracks: vi.fn().mockResolvedValue([]) }; }); -vi.mock('$lib/api/playlists', () => ({ - createPlaylistsQuery: () => - readable({ data: { owned: [], public: [] }, isPending: false, isError: false }), - appendTracks: vi.fn().mockResolvedValue({ id: 'p1', tracks: [] }), - createPlaylist: vi.fn().mockResolvedValue({ id: 'p1', name: 'New' }) -})); +vi.mock('$lib/api/playlists', () => emptyPlaylistsMock()); vi.mock('$lib/player/store.svelte', () => ({ enqueueTracks: vi.fn() diff --git a/web/src/lib/components/ArtistCard.test.ts b/web/src/lib/components/ArtistCard.test.ts index c92dbfae..aa299870 100644 --- a/web/src/lib/components/ArtistCard.test.ts +++ b/web/src/lib/components/ArtistCard.test.ts @@ -1,12 +1,11 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/svelte'; import { emptyLikesMock } from '../../test-utils/mocks/likes'; +import { apiClientMock } from '../../test-utils/mocks/client'; import type { ArtistRef, TrackRef } from '$lib/api/types'; import { makeTrack, makeTracks } from '$test-utils/fixtures/track'; -vi.mock('$lib/api/client', () => ({ - api: { get: vi.fn() } -})); +vi.mock('$lib/api/client', () => apiClientMock()); vi.mock('$lib/player/store.svelte', () => ({ playQueue: vi.fn(), enqueueTracks: vi.fn() diff --git a/web/src/lib/components/ArtistMenu.test.ts b/web/src/lib/components/ArtistMenu.test.ts index d8439841..15812307 100644 --- a/web/src/lib/components/ArtistMenu.test.ts +++ b/web/src/lib/components/ArtistMenu.test.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/svelte'; -import { readable } from 'svelte/store'; import { emptyLikesMock } from '../../test-utils/mocks/likes'; +import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists'; import type { TrackRef } from '$lib/api/types'; import { makeTracks } from '$test-utils/fixtures/track'; @@ -13,12 +13,7 @@ vi.mock('$lib/api/artists', () => ({ listArtistTracks: vi.fn().mockResolvedValue([]) })); -vi.mock('$lib/api/playlists', () => ({ - createPlaylistsQuery: () => - readable({ data: { owned: [], public: [] }, isPending: false, isError: false }), - appendTracks: vi.fn().mockResolvedValue({ id: 'p1', tracks: [] }), - createPlaylist: vi.fn().mockResolvedValue({ id: 'p1', name: 'New' }) -})); +vi.mock('$lib/api/playlists', () => emptyPlaylistsMock()); vi.mock('$lib/player/store.svelte', () => ({ enqueueTracks: vi.fn() diff --git a/web/src/lib/components/CompactTrackCard.test.ts b/web/src/lib/components/CompactTrackCard.test.ts index 0dfb1532..0936ce2d 100644 --- a/web/src/lib/components/CompactTrackCard.test.ts +++ b/web/src/lib/components/CompactTrackCard.test.ts @@ -1,8 +1,8 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/svelte'; import type { TrackRef } from '$lib/api/types'; -import { readable } from 'svelte/store'; import { emptyLikesMock } from '../../test-utils/mocks/likes'; +import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists'; import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine'; import { makeTracks } from '$test-utils/fixtures/track'; @@ -14,12 +14,7 @@ vi.mock('$lib/api/admin/tracks', () => ({ removeTrack: vi.fn().mockResolvedValue({ deleted_track_id: 't1' }) })); -vi.mock('$lib/api/playlists', () => ({ - createPlaylistsQuery: () => - readable({ data: { owned: [], public: [] }, isPending: false, isError: false }), - appendTracks: vi.fn().mockResolvedValue({ id: 'p1', tracks: [] }), - createPlaylist: vi.fn().mockResolvedValue({ id: 'p1', name: 'New' }) -})); +vi.mock('$lib/api/playlists', () => emptyPlaylistsMock()); vi.mock('$lib/auth/store.svelte', () => ({ user: { get value() { return { id: 'u1', username: 'u', is_admin: false }; } } diff --git a/web/src/lib/components/TrackMenu.test.ts b/web/src/lib/components/TrackMenu.test.ts index 7a50a9dc..6e359649 100644 --- a/web/src/lib/components/TrackMenu.test.ts +++ b/web/src/lib/components/TrackMenu.test.ts @@ -1,7 +1,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/svelte'; -import { readable } from 'svelte/store'; import { emptyLikesMock } from '../../test-utils/mocks/likes'; +import { emptyPlaylistsMock } from '../../test-utils/mocks/playlists'; import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine'; import { makeTrack } from '$test-utils/fixtures/track'; @@ -29,12 +29,7 @@ vi.mock('$lib/api/admin/tracks', () => ({ removeTrack: vi.fn().mockResolvedValue({ deleted_track_id: 't1' }) })); -vi.mock('$lib/api/playlists', () => ({ - createPlaylistsQuery: () => - readable({ data: { owned: [], public: [] }, isPending: false, isError: false }), - appendTracks: vi.fn().mockResolvedValue({ id: 'p1', tracks: [] }), - createPlaylist: vi.fn().mockResolvedValue({ id: 'p1', name: 'New' }) -})); +vi.mock('$lib/api/playlists', () => emptyPlaylistsMock()); vi.mock('$lib/player/store.svelte', () => ({ playNext: vi.fn(), diff --git a/web/src/routes/discover/discover.test.ts b/web/src/routes/discover/discover.test.ts index 2172d626..c01b4580 100644 --- a/web/src/routes/discover/discover.test.ts +++ b/web/src/routes/discover/discover.test.ts @@ -1,6 +1,7 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/svelte'; import { mockQuery } from '../../test-utils/query'; +import { apiClientMock } from '../../test-utils/mocks/client'; import type { LidarrSearchResult } from '$lib/api/types'; // Lidarr search query factory and createRequest are mocked at the module @@ -18,9 +19,7 @@ vi.mock('$lib/api/requests', () => ({ createRequest: vi.fn().mockResolvedValue({ id: 'r1' }) })); -vi.mock('$lib/api/client', () => ({ - api: { get: vi.fn(), post: vi.fn(), put: vi.fn(), del: vi.fn() } -})); +vi.mock('$lib/api/client', () => apiClientMock()); import DiscoverPage from './+page.svelte'; import { createLidarrSearchQuery } from '$lib/api/lidarr'; diff --git a/web/src/routes/library/albums/page.test.ts b/web/src/routes/library/albums/page.test.ts index a48ef9a6..64a92275 100644 --- a/web/src/routes/library/albums/page.test.ts +++ b/web/src/routes/library/albums/page.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test, vi } from 'vitest'; import { render, screen } from '@testing-library/svelte'; import { readable } from 'svelte/store'; import { emptyLikesMock } from '../../../test-utils/mocks/likes'; +import { apiClientMock } from '../../../test-utils/mocks/client'; vi.mock('$lib/api/albums', () => ({ createAlbumsAlphaInfiniteQuery: () => readable({ @@ -25,7 +26,7 @@ vi.mock('$lib/api/albums', () => ({ }), ALBUM_PAGE_SIZE: 50 })); -vi.mock('$lib/api/client', () => ({ api: { get: vi.fn() } })); +vi.mock('$lib/api/client', () => apiClientMock()); vi.mock('$lib/player/store.svelte', () => ({ enqueueTracks: vi.fn(), playQueue: vi.fn() diff --git a/web/src/routes/library/artists/page.test.ts b/web/src/routes/library/artists/page.test.ts index 27e221d4..35d14f2e 100644 --- a/web/src/routes/library/artists/page.test.ts +++ b/web/src/routes/library/artists/page.test.ts @@ -2,6 +2,7 @@ import { describe, expect, test, vi } from 'vitest'; import { render, screen } from '@testing-library/svelte'; import { readable } from 'svelte/store'; import { emptyLikesMock } from '../../../test-utils/mocks/likes'; +import { apiClientMock } from '../../../test-utils/mocks/client'; vi.mock('$lib/api/queries', () => ({ createArtistsQuery: () => readable({ @@ -20,7 +21,7 @@ vi.mock('$lib/api/queries', () => ({ fetchNextPage: () => {} }) })); -vi.mock('$lib/api/client', () => ({ api: { get: vi.fn() } })); +vi.mock('$lib/api/client', () => apiClientMock()); vi.mock('$lib/player/store.svelte', () => ({ playQueue: vi.fn(), enqueueTracks: vi.fn() diff --git a/web/src/routes/library/liked/liked.test.ts b/web/src/routes/library/liked/liked.test.ts index 0211c94f..ee9ae4b4 100644 --- a/web/src/routes/library/liked/liked.test.ts +++ b/web/src/routes/library/liked/liked.test.ts @@ -1,6 +1,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/svelte'; import { mockInfiniteQuery } from '../../../test-utils/query'; +import { apiClientMock } from '../../../test-utils/mocks/client'; import type { ArtistRef, AlbumRef, TrackRef, Page } from '$lib/api/types'; import { readable } from 'svelte/store'; import { makeTrack } from '$test-utils/fixtures/track'; @@ -18,7 +19,7 @@ vi.mock('$lib/api/likes', () => ({ unlikeEntity: vi.fn() })); -vi.mock('$lib/api/client', () => ({ api: { get: vi.fn() } })); +vi.mock('$lib/api/client', () => apiClientMock()); vi.mock('$lib/player/store.svelte', () => ({ playQueue: vi.fn(), enqueueTrack: vi.fn(), enqueueTracks: vi.fn() })); diff --git a/web/src/routes/requests/requests.test.ts b/web/src/routes/requests/requests.test.ts index 8d29eb92..5639d09e 100644 --- a/web/src/routes/requests/requests.test.ts +++ b/web/src/routes/requests/requests.test.ts @@ -1,6 +1,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent, waitFor } from '@testing-library/svelte'; import { mockQuery } from '../../test-utils/query'; +import { apiClientMock } from '../../test-utils/mocks/client'; import type { LidarrRequest } from '$lib/api/types'; // Capture invalidateQueries on the mocked QueryClient so the cancel test can @@ -23,9 +24,7 @@ vi.mock('$lib/api/queries', () => ({ } })); -vi.mock('$lib/api/client', () => ({ - api: { get: vi.fn(), post: vi.fn(), put: vi.fn(), del: vi.fn() } -})); +vi.mock('$lib/api/client', () => apiClientMock()); import RequestsPage from './+page.svelte'; import { createMyRequestsQuery, cancelRequest } from '$lib/api/requests'; diff --git a/web/src/routes/search/albums/albums.test.ts b/web/src/routes/search/albums/albums.test.ts index 21beaeb6..c5529323 100644 --- a/web/src/routes/search/albums/albums.test.ts +++ b/web/src/routes/search/albums/albums.test.ts @@ -2,6 +2,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen, fireEvent } from '@testing-library/svelte'; import { mockInfiniteQuery } from '../../../test-utils/query'; import { emptyLikesMock } from '../../../test-utils/mocks/likes'; +import { apiClientMock } from '../../../test-utils/mocks/client'; import { pageUrlModule } from '$test-utils/mocks/appState'; import type { AlbumRef, Page } from '$lib/api/types'; @@ -13,9 +14,7 @@ vi.mock('$lib/api/queries', () => ({ createSearchAlbumsInfiniteQuery: vi.fn() })); -vi.mock('$lib/api/client', () => ({ - api: { get: vi.fn() } -})); +vi.mock('$lib/api/client', () => apiClientMock()); vi.mock('$lib/player/store.svelte', () => ({ enqueueTracks: vi.fn() })); diff --git a/web/src/routes/search/search.test.ts b/web/src/routes/search/search.test.ts index ae13d0a4..44bc3986 100644 --- a/web/src/routes/search/search.test.ts +++ b/web/src/routes/search/search.test.ts @@ -2,6 +2,7 @@ import { afterEach, describe, expect, test, vi } from 'vitest'; import { render, screen } from '@testing-library/svelte'; import { mockQuery } from '../../test-utils/query'; import { emptyLikesMock } from '../../test-utils/mocks/likes'; +import { apiClientMock } from '../../test-utils/mocks/client'; import { pageUrlModule } from '$test-utils/mocks/appState'; import type { ArtistRef, AlbumRef, SearchResponse } from '$lib/api/types'; import { makeTrack } from '$test-utils/fixtures/track'; @@ -23,9 +24,7 @@ vi.mock('$lib/player/store.svelte', () => ({ enqueueTracks: vi.fn() })); -vi.mock('$lib/api/client', () => ({ - api: { get: vi.fn() } -})); +vi.mock('$lib/api/client', () => apiClientMock()); vi.mock('$lib/api/likes', () => emptyLikesMock()); diff --git a/web/src/test-utils/mocks/client.ts b/web/src/test-utils/mocks/client.ts new file mode 100644 index 00000000..792751f0 --- /dev/null +++ b/web/src/test-utils/mocks/client.ts @@ -0,0 +1,22 @@ +// Default api-client mock for $lib/api/client. Provides spies for +// every HTTP verb so tests don't need to anticipate which methods +// the code under test might reach for. +// +// Usage: +// vi.mock('$lib/api/client', () => apiClientMock()); +// +// Tests that need a specific resolved value (e.g. events tests that +// assert on the play_event_id round trip) keep custom mocks inline. + +import { vi } from 'vitest'; + +export function apiClientMock() { + return { + api: { + get: vi.fn(), + post: vi.fn(), + put: vi.fn(), + del: vi.fn() + } + }; +} diff --git a/web/src/test-utils/mocks/playlists.ts b/web/src/test-utils/mocks/playlists.ts new file mode 100644 index 00000000..100488f8 --- /dev/null +++ b/web/src/test-utils/mocks/playlists.ts @@ -0,0 +1,23 @@ +// Default empty-playlists mock for $lib/api/playlists. The +// dominant shape across the menu / card tests: no playlists for +// the user, append/create as resolved spies. +// +// Usage: +// vi.mock('$lib/api/playlists', () => emptyPlaylistsMock()); +// +// Tests that exercise richer return payloads (AddToPlaylistMenu's +// full PlaylistDetail return, PlaylistCard's getPlaylist, route +// tests that need owned playlists pre-populated) keep custom +// mocks inline. + +import { readable } from 'svelte/store'; +import { vi } from 'vitest'; + +export function emptyPlaylistsMock() { + return { + createPlaylistsQuery: () => + readable({ data: { owned: [], public: [] }, isPending: false, isError: false }), + appendTracks: vi.fn().mockResolvedValue({ id: 'p1', tracks: [] }), + createPlaylist: vi.fn().mockResolvedValue({ id: 'p1', name: 'New' }) + }; +}