diff --git a/web/src/test-utils/fixtures/track.ts b/web/src/test-utils/fixtures/track.ts new file mode 100644 index 00000000..018065bb --- /dev/null +++ b/web/src/test-utils/fixtures/track.ts @@ -0,0 +1,31 @@ +// Fixture builders for TrackRef. Use in test setup to avoid +// re-declaring the same literal across 18+ files. + +import type { TrackRef } from '$lib/api/types'; + +export function makeTrack(overrides: Partial = {}): TrackRef { + return { + id: 't1', + title: 'Test Track', + album_id: 'a1', + album_title: 'Test Album', + artist_id: 'ar1', + artist_name: 'Test Artist', + track_number: 1, + disc_number: 1, + duration_sec: 180, + stream_url: '/api/tracks/t1/stream', + ...overrides + }; +} + +export function makeTracks(n: number, overrides: Partial = {}): TrackRef[] { + return Array.from({ length: n }, (_, i) => + makeTrack({ + id: `t${i + 1}`, + title: `Test Track ${i + 1}`, + stream_url: `/api/tracks/t${i + 1}/stream`, + ...overrides + }) + ); +} diff --git a/web/src/test-utils/mocks/appState.ts b/web/src/test-utils/mocks/appState.ts new file mode 100644 index 00000000..e5cead52 --- /dev/null +++ b/web/src/test-utils/mocks/appState.ts @@ -0,0 +1,22 @@ +// $app/state mock factory. The vi.hoisted state holder still has to +// live in each test file (vitest mock-hoisting transform requires +// it) but the getter shape is centralized here. +// +// Usage: +// const pageState = vi.hoisted(() => ({ +// pageUrl: new URL('http://localhost/search?q=miles') +// })); +// vi.mock('$app/state', () => pageUrlModule(pageState)); +// afterEach(() => { pageState.pageUrl = new URL('http://localhost/search?q=miles'); }); + +export type PageUrlState = { pageUrl: URL }; + +export function pageUrlModule(state: PageUrlState) { + return { + page: { + get url() { + return state.pageUrl; + } + } + }; +} diff --git a/web/src/test-utils/mocks/likes.ts b/web/src/test-utils/mocks/likes.ts new file mode 100644 index 00000000..a18a21a7 --- /dev/null +++ b/web/src/test-utils/mocks/likes.ts @@ -0,0 +1,25 @@ +// Default empty-LikedIds mock for $lib/api/likes. Mirrors the +// TanStack Query readable shape (data + isPending + isError) that +// 19 test files were duplicating verbatim. +// +// Usage: +// vi.mock('$lib/api/likes', () => emptyLikesMock()); +// +// Tests that assert on liked-state changes (LikeButton.test.ts) keep +// their state-driven mocks inline. + +import { readable } from 'svelte/store'; +import { vi } from 'vitest'; + +export function emptyLikesMock() { + return { + createLikedIdsQuery: () => + readable({ + data: { track_ids: [], album_ids: [], artist_ids: [] }, + isPending: false, + isError: false + }), + likeEntity: vi.fn().mockResolvedValue(undefined), + unlikeEntity: vi.fn().mockResolvedValue(undefined) + }; +} diff --git a/web/src/test-utils/mocks/quarantine.ts b/web/src/test-utils/mocks/quarantine.ts new file mode 100644 index 00000000..49ecb5eb --- /dev/null +++ b/web/src/test-utils/mocks/quarantine.ts @@ -0,0 +1,21 @@ +// Default empty-quarantine mock for $lib/api/quarantine. The +// dominant shape across 9 test files: empty list + spy mutations. +// +// Usage: +// vi.mock('$lib/api/quarantine', () => emptyQuarantineMock()); +// +// Tests that assert on per-row state (FlagPopover, playlist tests) +// keep custom mocks inline. + +import { readable } from 'svelte/store'; +import { vi } from 'vitest'; + +export function emptyQuarantineMock() { + return { + flagTrack: vi.fn().mockResolvedValue({}), + unflagTrack: vi.fn().mockResolvedValue(undefined), + listMyQuarantine: vi.fn().mockResolvedValue([]), + createMyQuarantineQuery: () => + readable({ data: [], isPending: false, isError: false }) + }; +}