refactor(web): add test-utils helpers for likes/quarantine/track/appState mocks (#375)

Pre-sweep checkpoint. Adds:
- fixtures/track.ts        — makeTrack() / makeTracks(n) (18 files duplicate today)
- mocks/likes.ts           — emptyLikesMock() (19 files duplicate today)
- mocks/quarantine.ts      — emptyQuarantineMock() (9 files duplicate today)
- mocks/appState.ts        — pageUrlModule(state) (8 files duplicate today)

The sweep commits that follow update test files to use these helpers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 15:31:53 -04:00
parent e8eff1baa1
commit 2357df83be
4 changed files with 99 additions and 0 deletions
+31
View File
@@ -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> = {}): 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> = {}): 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
})
);
}
+22
View File
@@ -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;
}
}
};
}
+25
View File
@@ -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)
};
}
+21
View File
@@ -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 })
};
}