chore(deps): SvelteKit 3 with adapter-static 4 and TypeScript 6, full-tree npm audit (#5021)

Merges Renovate's kit 3 (PR #149) and adapter-static 4 (PR #148) bumps,
plus the migration they need. The mechanical part is `sv migrate
sveltekit-3`, run one task at a time and reviewed:

- svelte.config.js is gone. Its options move into sveltekit() in
  vite.config.ts, exported as kitOptions so vitest.config.ts runs the
  same kit setup, including the $test-utils alias the tests import.
- $lib becomes #lib through package.json "imports". There is no
  src/lib/index, so only the "#lib/*" entry is kept.
- tsconfig extends $app/tsconfig.
- Peer floors raised to kit 3's requirements: svelte ^5.57.1, vite
  ^8.0.12, svelte-check ^4.7.5.

By hand, from the codemod's list of non-automated tasks:

- goto's replaceState option is now replace; keepFocus becomes
  reset: false. For the search typeahead, reset: false also stops the
  scroll-to-top, which is wanted while typing.
- The test setup mocks drop pushState/replaceState and $app/paths
  base/assets, which kit 3 removed, and mock refreshAll in place of
  invalidateAll.
- The other flagged files only read page.url or goto internal routes,
  so they needed no change.

TypeScript goes to ^6, not the ^7 Renovate offers: kit 3 declares
typescript ^6 as a peer and svelte-check 4.7 accepts ^5 || ^6. Move to
7 once both accept it.

With Tailwind 4 and kit 3 in, `npm audit` on the whole tree reports 0,
so the web lane now audits every dependency rather than only what
ships to browsers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-08 10:50:39 -04:00
co-authored by Claude Opus 5.5
186 changed files with 916 additions and 924 deletions
+19 -19
View File
@@ -3,14 +3,14 @@
import { page } from '$app/state';
import { goto } from '$app/navigation';
import { QueryClientProvider } from '@tanstack/svelte-query';
import { queryClient } from '$lib/query/client';
import { user } from '$lib/auth/store.svelte';
import { isPublicRoute } from '$lib/auth/publicRoutes';
import Shell from '$lib/components/Shell.svelte';
import QueueDrawer from '$lib/components/QueueDrawer.svelte';
import SelectionBar from '$lib/components/SelectionBar.svelte';
import ToastHost from '$lib/components/ToastHost.svelte';
import { selection, clearSelection } from '$lib/selection/store.svelte';
import { queryClient } from '#lib/query/client.js';
import { user } from '#lib/auth/store.svelte.js';
import { isPublicRoute } from '#lib/auth/publicRoutes.js';
import Shell from '#lib/components/Shell.svelte';
import QueueDrawer from '#lib/components/QueueDrawer.svelte';
import SelectionBar from '#lib/components/SelectionBar.svelte';
import ToastHost from '#lib/components/ToastHost.svelte';
import { selection, clearSelection } from '#lib/selection/store.svelte.js';
import {
registerAudioEl,
reportTimeUpdate,
@@ -20,21 +20,21 @@
closeQueueDrawer,
consumePendingRestorePosition,
deriveFadeScalar
} from '$lib/player/store.svelte';
import { useMediaSession } from '$lib/player/mediaSession.svelte';
import { useEventsDispatcher } from '$lib/player/events.svelte';
import { useServerEvents } from '$lib/serverEvents.svelte';
import { useGlobalShortcuts } from '$lib/player/shortcuts.svelte';
import { applyMetaThemeColor } from '$lib/theme/applyMetaThemeColor.svelte';
import { audioLoader } from '$lib/player/audioLoader';
import { attachStallRetry } from '$lib/player/stallRetry';
} from '#lib/player/store.svelte.js';
import { useMediaSession } from '#lib/player/mediaSession.svelte.js';
import { useEventsDispatcher } from '#lib/player/events.svelte.js';
import { useServerEvents } from '#lib/serverEvents.svelte.js';
import { useGlobalShortcuts } from '#lib/player/shortcuts.svelte.js';
import { applyMetaThemeColor } from '#lib/theme/applyMetaThemeColor.svelte.js';
import { audioLoader } from '#lib/player/audioLoader.js';
import { attachStallRetry } from '#lib/player/stallRetry.js';
import {
applyGain,
ensureGains,
ensureGraph,
gainDbAt,
resumeGraph
} from '$lib/player/gainStage.svelte';
} from '#lib/player/gainStage.svelte.js';
import { untrack } from 'svelte';
let { children } = $props<{ children: import('svelte').Snippet }>();
@@ -47,9 +47,9 @@
const isPublic = isPublicRoute(path);
if (user.value === null && !isPublic) {
const returnTo = path + page.url.search;
goto('/login?returnTo=' + encodeURIComponent(returnTo), { replaceState: true });
goto('/login?returnTo=' + encodeURIComponent(returnTo), { replace: true });
} else if (user.value !== null && isPublic) {
goto('/', { replaceState: true });
goto('/', { replace: true });
}
});
+3 -3
View File
@@ -1,7 +1,7 @@
import type { LayoutLoad } from './$types';
import { bootstrap, user } from '$lib/auth/store.svelte';
import { restoreQueue } from '$lib/player/store.svelte';
import { loadNormalization } from '$lib/stores/normalization.svelte';
import { bootstrap, user } from '#lib/auth/store.svelte.js';
import { restoreQueue } from '#lib/player/store.svelte.js';
import { loadNormalization } from '#lib/stores/normalization.svelte.js';
export const ssr = false; // adapter-static fallback; we're SPA-only
export const prerender = false;
+20 -20
View File
@@ -1,17 +1,17 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { createHomeQuery } from '$lib/api/home';
import HorizontalScrollRow from '$lib/components/HorizontalScrollRow.svelte';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import ArtistCard from '$lib/components/ArtistCard.svelte';
import CompactTrackCard from '$lib/components/CompactTrackCard.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import PlaylistCard from '$lib/components/PlaylistCard.svelte';
import PlaylistPlaceholderCard from '$lib/components/PlaylistPlaceholderCard.svelte';
import { createPlaylistsQuery } from '$lib/api/playlists';
import { createSystemPlaylistsStatusQuery } from '$lib/api/me';
import type { Playlist } from '$lib/api/types';
import { pageTitle } from '#lib/branding.js';
import { createHomeQuery } from '#lib/api/home.js';
import HorizontalScrollRow from '#lib/components/HorizontalScrollRow.svelte';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import ArtistCard from '#lib/components/ArtistCard.svelte';
import CompactTrackCard from '#lib/components/CompactTrackCard.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
import PlaylistCard from '#lib/components/PlaylistCard.svelte';
import PlaylistPlaceholderCard from '#lib/components/PlaylistPlaceholderCard.svelte';
import { createPlaylistsQuery } from '#lib/api/playlists.js';
import { createSystemPlaylistsStatusQuery } from '#lib/api/me.js';
import type { Playlist } from '#lib/api/types.js';
const queryStore = createHomeQuery();
const query = $derived($queryStore);
@@ -212,7 +212,7 @@
title="You might like"
ariaLabel="You might like albums"
>
{#snippet item(album: import('$lib/api/types').AlbumRef)}
{#snippet item(album: import('#lib/api/types.js').AlbumRef)}
<div class="w-40"><AlbumCard {album} /></div>
{/snippet}
</HorizontalScrollRow>
@@ -223,7 +223,7 @@
title={data.you_might_like_albums.length === 0 ? 'You might like' : undefined}
ariaLabel="You might like artists"
>
{#snippet item(artist: import('$lib/api/types').ArtistRef)}
{#snippet item(artist: import('#lib/api/types.js').ArtistRef)}
<div class="w-36"><ArtistCard {artist} /></div>
{/snippet}
</HorizontalScrollRow>
@@ -244,7 +244,7 @@
title="Recently added"
ariaLabel="Recently added"
>
{#snippet item(album: import('$lib/api/types').AlbumRef)}
{#snippet item(album: import('#lib/api/types.js').AlbumRef)}
<div class="w-48"><AlbumCard {album} /></div>
{/snippet}
</HorizontalScrollRow>
@@ -267,7 +267,7 @@
title="Rediscover"
ariaLabel="Rediscover albums"
>
{#snippet item(album: import('$lib/api/types').AlbumRef)}
{#snippet item(album: import('#lib/api/types.js').AlbumRef)}
<!-- Rediscover sits below Recently Added in the size
hierarchy. w-40 keeps it readable without competing
with the freshest content above. -->
@@ -281,7 +281,7 @@
title={data.rediscover_albums.length === 0 ? 'Rediscover' : undefined}
ariaLabel="Rediscover artists"
>
{#snippet item(artist: import('$lib/api/types').ArtistRef)}
{#snippet item(artist: import('#lib/api/types.js').ArtistRef)}
<div class="w-36"><ArtistCard {artist} /></div>
{/snippet}
</HorizontalScrollRow>
@@ -302,7 +302,7 @@
title="Most played"
ariaLabel="Most played"
>
{#snippet item(track: import('$lib/api/types').TrackRef, globalIdx: number)}
{#snippet item(track: import('#lib/api/types.js').TrackRef, globalIdx: number)}
<CompactTrackCard
{track}
sectionTracks={data.most_played_tracks}
@@ -326,7 +326,7 @@
title="Last played"
ariaLabel="Last played artists"
>
{#snippet item(artist: import('$lib/api/types').ArtistRef)}
{#snippet item(artist: import('#lib/api/types.js').ArtistRef)}
<div class="w-36"><ArtistCard {artist} /></div>
{/snippet}
</HorizontalScrollRow>
+1 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import AdminTabs from '$lib/components/AdminTabs.svelte';
import AdminTabs from '#lib/components/AdminTabs.svelte';
let { children } = $props<{ children: import('svelte').Snippet }>();
</script>
+1 -1
View File
@@ -1,5 +1,5 @@
import { redirect } from '@sveltejs/kit';
import { user } from '$lib/auth/store.svelte';
import { user } from '#lib/auth/store.svelte.js';
import type { LayoutLoad } from './$types';
// Hard route gate: runs before the layout (and any child page) renders.
+9 -9
View File
@@ -1,8 +1,8 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Disc3, Album, Music2, Check, X, RotateCcw, Trash2, Cloud, ChevronRight } from 'lucide-svelte';
import StageBadge from '$lib/components/StageBadge.svelte';
import LoudnessSettingsCard from '$lib/components/LoudnessSettingsCard.svelte';
import StageBadge from '#lib/components/StageBadge.svelte';
import LoudnessSettingsCard from '#lib/components/LoudnessSettingsCard.svelte';
import { stageState } from './stage-state';
import { useQueryClient } from '@tanstack/svelte-query';
import {
@@ -20,17 +20,17 @@
refetchMissingCovers,
triggerScan,
researchMissingArt
} from '$lib/api/admin';
import { qk } from '$lib/api/queries';
import { errCode } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
import { runMutation } from '$lib/util/runMutation';
} from '#lib/api/admin.js';
import { qk } from '#lib/api/queries.js';
import { errCode } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
import { runMutation } from '#lib/util/runMutation.js';
import type {
AdminQuarantineRow,
LidarrRequest,
LidarrRequestKind,
LidarrQuarantineReason
} from '$lib/api/types';
} from '#lib/api/types.js';
// Top-of-admin overview. Shows the connection card + the most recent
// 5 of each actionable queue (requests, quarantine) with inline
+3 -3
View File
@@ -8,7 +8,7 @@ const userState = vi.hoisted(() => ({
current: null as { id: string; username: string; is_admin: boolean } | null
}));
vi.mock('$lib/auth/store.svelte', () => ({
vi.mock('#lib/auth/store.svelte.js', () => ({
user: { get value() { return userState.current; } }
}));
@@ -16,7 +16,7 @@ vi.mock('$app/state', () => ({
page: { url: new URL('http://localhost/admin') }
}));
vi.mock('$lib/api/admin', async () => {
vi.mock('#lib/api/admin.js', async () => {
const { readable } = await import('svelte/store');
return {
createAdminRequestsQuery: vi.fn(),
@@ -106,7 +106,7 @@ import {
deleteQuarantineFile,
deleteQuarantineViaLidarr,
researchMissingArt
} from '$lib/api/admin';
} from '#lib/api/admin.js';
import OverviewPage from './+page.svelte';
function setupOverview(opts: {
@@ -1,5 +1,5 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Copy, Download, Activity } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import {
@@ -9,10 +9,10 @@
updateUserDebugMode,
type AdminDiagnostic,
type DiagnosticsFilter
} from '$lib/api/admin';
import { qk } from '$lib/api/queries';
import { errMessage } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
} from '#lib/api/admin.js';
import { qk } from '#lib/api/queries.js';
import { errMessage } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
// Device diagnostics timeline (M9). The operator enables debug-mode on
// an account (remotely, here), then watches the account's device(s)
+7 -7
View File
@@ -1,17 +1,17 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Copy, TriangleAlert } from 'lucide-svelte';
import {
createDuplicatesQuery,
runDuplicateSweep,
dismissDuplicateGroup,
mergeDuplicateGroup
} from '$lib/api/admin';
import { errMessage } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
import { relativeTime } from '$lib/utils/relativeTime';
import type { AdminDuplicateGroup, AdminDuplicateMember } from '$lib/api/types';
import FingerprintSettingsCard from '$lib/components/FingerprintSettingsCard.svelte';
} from '#lib/api/admin.js';
import { errMessage } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
import { relativeTime } from '#lib/utils/relativeTime.js';
import type { AdminDuplicateGroup, AdminDuplicateMember } from '#lib/api/types.js';
import FingerprintSettingsCard from '#lib/components/FingerprintSettingsCard.svelte';
// Tracks the duplicate sweep believes hold one recording (#3912). Dismissing a
// group says "these are not duplicates", and the sweep will not propose that
@@ -1,9 +1,9 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
import type { AdminDuplicatesResponse } from '$lib/api/types';
import type { AdminDuplicatesResponse } from '#lib/api/types.js';
vi.mock('$lib/api/admin', () => ({
vi.mock('#lib/api/admin.js', () => ({
createDuplicatesQuery: vi.fn(),
runDuplicateSweep: vi.fn().mockResolvedValue({ started: true }),
dismissDuplicateGroup: vi.fn().mockResolvedValue(undefined),
@@ -29,7 +29,7 @@ import {
dismissDuplicateGroup,
mergeDuplicateGroup,
runDuplicateSweep
} from '$lib/api/admin';
} from '#lib/api/admin.js';
const HOUR = 3_600_000;
const ago = (ms: number) => new Date(Date.now() - ms).toISOString();
+11 -11
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { tick } from 'svelte';
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Save, RefreshCw, Trash2 } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import {
@@ -22,16 +22,16 @@
type CoverProvider,
type TagProvider,
type SMTPConfig
} from '$lib/api/admin';
import { qk } from '$lib/api/queries';
import { errCode } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
import Modal from '$lib/components/Modal.svelte';
import AcoustIDCard from '$lib/components/AcoustIDCard.svelte';
import NetworkSettingsCard from '$lib/components/NetworkSettingsCard.svelte';
import PublicAddressCard from '$lib/components/PublicAddressCard.svelte';
import NotificationEmailCard from '$lib/components/NotificationEmailCard.svelte';
import type { LidarrConfig, LidarrTestResult } from '$lib/api/types';
} from '#lib/api/admin.js';
import { qk } from '#lib/api/queries.js';
import { errCode } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
import Modal from '#lib/components/Modal.svelte';
import AcoustIDCard from '#lib/components/AcoustIDCard.svelte';
import NetworkSettingsCard from '#lib/components/NetworkSettingsCard.svelte';
import PublicAddressCard from '#lib/components/PublicAddressCard.svelte';
import NotificationEmailCard from '#lib/components/NotificationEmailCard.svelte';
import type { LidarrConfig, LidarrTestResult } from '#lib/api/types.js';
// Lidarr connection panel. The "saved api key" is masked as "***" on GET —
// never displayed in the input. The input always starts empty; sending an
@@ -1,8 +1,8 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor, within } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
import type { LidarrConfig } from '$lib/api/types';
import type { CoverProvider, TagProvider, SMTPConfig } from '$lib/api/admin';
import type { LidarrConfig } from '#lib/api/types.js';
import type { CoverProvider, TagProvider, SMTPConfig } from '#lib/api/admin.js';
// useQueryClient is wrapped so the page can call invalidateQueries() without
// a real QueryClient context. Per-file mock OVERRIDES the vitest.setup default
@@ -13,7 +13,7 @@ vi.mock('@tanstack/svelte-query', async (orig) => {
return { ...actual, useQueryClient: () => ({ invalidateQueries }) };
});
vi.mock('$lib/api/admin', () => ({
vi.mock('#lib/api/admin.js', () => ({
createLidarrConfigQuery: vi.fn(),
createQualityProfilesQuery: vi.fn(),
createMetadataProfilesQuery: vi.fn(),
@@ -57,7 +57,7 @@ import {
createSMTPConfigQuery,
updateSMTPConfig,
testSMTPConfig
} from '$lib/api/admin';
} from '#lib/api/admin.js';
const cfgConnected: LidarrConfig = {
enabled: true,
@@ -1,11 +1,11 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { FolderX, Music2 } from 'lucide-svelte';
import { createMissingFilesQuery } from '$lib/api/admin';
import { relativeTime, timeUntil } from '$lib/utils/relativeTime';
import { coverUrl } from '$lib/media/covers';
import ReacquisitionSettingsCard from '$lib/components/ReacquisitionSettingsCard.svelte';
import type { AdminMissingGroup } from '$lib/api/types';
import { createMissingFilesQuery } from '#lib/api/admin.js';
import { relativeTime, timeUntil } from '#lib/utils/relativeTime.js';
import { coverUrl } from '#lib/media/covers.js';
import ReacquisitionSettingsCard from '#lib/components/ReacquisitionSettingsCard.svelte';
import type { AdminMissingGroup } from '#lib/api/types.js';
// Files the scan looked for and could not find. Read-only on purpose:
// a missing file keeps its track row, its play history and its likes,
@@ -1,12 +1,12 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
import type { AdminMissingResponse } from '$lib/api/types';
import type { AdminMissingResponse } from '#lib/api/types.js';
// The page now embeds ReacquisitionSettingsCard, which loads its own settings
// from this same module on mount. Stubbing both keeps these tests about the
// missing-files list — the card has its own suite.
vi.mock('$lib/api/admin', () => ({
vi.mock('#lib/api/admin.js', () => ({
createMissingFilesQuery: vi.fn(),
getReacquisitionSettings: vi.fn().mockResolvedValue({
enabled: true,
@@ -22,7 +22,7 @@ vi.mock('$lib/api/admin', () => ({
}));
import AdminMissingFilesPage from './+page.svelte';
import { createMissingFilesQuery } from '$lib/api/admin';
import { createMissingFilesQuery } from '#lib/api/admin.js';
const DAY = 24 * 3_600_000;
@@ -1,6 +1,6 @@
import { describe, it, expect } from 'vitest';
import { stageState } from './stage-state';
import type { ScanStatus } from '$lib/api/admin';
import type { ScanStatus } from '#lib/api/admin.js';
const FINISHED_AT = '2026-05-06T12:00:00Z';
@@ -1,19 +1,19 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Copy, Trash2, CheckCircle2 } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import RowActionsMenu, { type RowAction } from '$lib/components/RowActionsMenu.svelte';
import RowActionsMenu, { type RowAction } from '#lib/components/RowActionsMenu.svelte';
import {
createAdminPlaybackErrorsQuery,
resolvePlaybackError,
deleteQuarantineFile
} from '$lib/api/admin';
import { qk } from '$lib/api/queries';
import { errMessage } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
import Modal from '$lib/components/Modal.svelte';
import type { AdminPlaybackError, PlaybackErrorResolution } from '$lib/api/types';
import { relativeTime } from '$lib/utils/relativeTime';
} from '#lib/api/admin.js';
import { qk } from '#lib/api/queries.js';
import { errMessage } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
import Modal from '#lib/components/Modal.svelte';
import type { AdminPlaybackError, PlaybackErrorResolution } from '#lib/api/types.js';
import { relativeTime } from '#lib/utils/relativeTime.js';
// Client-reported playback errors inbox. Two tabs — Unresolved
// (default) / Resolved. Per-row: copy details to clipboard, delete
+11 -11
View File
@@ -1,22 +1,22 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Music2, RotateCcw, Trash2, Cloud, Play, ChevronRight } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import RowActionsMenu, { type RowAction } from '$lib/components/RowActionsMenu.svelte';
import RowActionsMenu, { type RowAction } from '#lib/components/RowActionsMenu.svelte';
import {
createAdminQuarantineQuery,
resolveQuarantine,
deleteQuarantineFile,
deleteQuarantineViaLidarr
} from '$lib/api/admin';
import { qk } from '$lib/api/queries';
import { errMessage } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
import { playRadio } from '$lib/player/store.svelte';
import { coverUrl } from '$lib/media/covers';
import Modal from '$lib/components/Modal.svelte';
import type { AdminQuarantineRow, LidarrQuarantineReason } from '$lib/api/types';
import { relativeTime } from '$lib/utils/relativeTime';
} from '#lib/api/admin.js';
import { qk } from '#lib/api/queries.js';
import { errMessage } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
import { playRadio } from '#lib/player/store.svelte.js';
import { coverUrl } from '#lib/media/covers.js';
import Modal from '#lib/components/Modal.svelte';
import type { AdminQuarantineRow, LidarrQuarantineReason } from '#lib/api/types.js';
import { relativeTime } from '#lib/utils/relativeTime.js';
// Aggregated triage queue. One row per track, with per-row resolution
// actions: Resolve (clears reports), Delete file (Bronze; modal-confirm),
@@ -1,18 +1,18 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
import type { AdminQuarantineRow } from '$lib/api/types';
import type { AdminQuarantineRow } from '#lib/api/types.js';
// Wrap useQueryClient so the page can call invalidateQueries() without a real
// QueryClient context. Everything else from svelte-query passes through.
vi.mock('$lib/api/admin', () => ({
vi.mock('#lib/api/admin.js', () => ({
createAdminQuarantineQuery: vi.fn(),
resolveQuarantine: vi.fn(),
deleteQuarantineFile: vi.fn(),
deleteQuarantineViaLidarr: vi.fn()
}));
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/player/store.svelte.js', () => ({
playRadio: vi.fn()
}));
@@ -22,8 +22,8 @@ import {
resolveQuarantine,
deleteQuarantineFile,
deleteQuarantineViaLidarr
} from '$lib/api/admin';
import { playRadio } from '$lib/player/store.svelte';
} from '#lib/api/admin.js';
import { playRadio } from '#lib/player/store.svelte.js';
const baseRow: AdminQuarantineRow = {
track_id: 't-001',
+8 -8
View File
@@ -1,20 +1,20 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Disc3, Album, Music2, Check, X, SlidersHorizontal } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import RowActionsMenu, { type RowAction } from '$lib/components/RowActionsMenu.svelte';
import RowActionsMenu, { type RowAction } from '#lib/components/RowActionsMenu.svelte';
import {
createAdminRequestsQuery,
createQualityProfilesQuery,
createRootFoldersQuery,
approveRequest,
rejectRequest
} from '$lib/api/admin';
import { errMessage } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
import StatusPill from '$lib/components/StatusPill.svelte';
import Modal from '$lib/components/Modal.svelte';
import type { LidarrRequest, LidarrRequestKind, LidarrRequestStatus } from '$lib/api/types';
} from '#lib/api/admin.js';
import { errMessage } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
import StatusPill from '#lib/components/StatusPill.svelte';
import Modal from '#lib/components/Modal.svelte';
import type { LidarrRequest, LidarrRequestKind, LidarrRequestStatus } from '#lib/api/types.js';
// Approval queue. Tabs filter by status; rows expose Override/Approve/Reject
// per spec. The override modal collapses by default — most requests get the
@@ -1,11 +1,11 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
import type { LidarrRequest } from '$lib/api/types';
import type { LidarrRequest } from '#lib/api/types.js';
// Wrap useQueryClient so the page can call invalidateQueries() without a
// real QueryClient context. Everything else from svelte-query passes through.
vi.mock('$lib/api/admin', () => ({
vi.mock('#lib/api/admin.js', () => ({
createAdminRequestsQuery: vi.fn(),
createQualityProfilesQuery: vi.fn(),
createRootFoldersQuery: vi.fn(),
@@ -20,7 +20,7 @@ import {
createRootFoldersQuery,
approveRequest,
rejectRequest
} from '$lib/api/admin';
} from '#lib/api/admin.js';
const baseRow: LidarrRequest = {
id: 'r1',
+1 -1
View File
@@ -1,4 +1,4 @@
import type { ScanStatus } from '$lib/api/admin';
import type { ScanStatus } from '#lib/api/admin.js';
export type StageState = 'pending' | 'in_progress' | 'done' | 'skipped';
+4 -4
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import {
getTuning,
patchTuning,
@@ -14,9 +14,9 @@
type TrendsResponse,
type TrendSeries,
type TrendMarker
} from '$lib/api/tuning';
import { errMessage } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
} from '#lib/api/tuning.js';
import { errMessage } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
// The defaults-discovery lab (#1250): DB-backed scoring-weight
// profiles + taste-build knobs with live effect. This card exists to
+4 -4
View File
@@ -1,8 +1,8 @@
import { describe, expect, test, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import type { TuningSnapshot, TrendsResponse } from '$lib/api/tuning';
import type { TuningSnapshot, TrendsResponse } from '#lib/api/tuning.js';
vi.mock('$lib/api/tuning', async (orig) => {
vi.mock('#lib/api/tuning.js', async (orig) => {
const actual = (await orig()) as Record<string, unknown>;
return {
...actual,
@@ -14,10 +14,10 @@ vi.mock('$lib/api/tuning', async (orig) => {
});
const pushToast = vi.fn();
vi.mock('$lib/stores/toast.svelte', () => ({ pushToast: (...a: unknown[]) => pushToast(...a) }));
vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: (...a: unknown[]) => pushToast(...a) }));
import TuningPage from './+page.svelte';
import { getTuning, patchTuning, resetTuning, getTrends } from '$lib/api/tuning';
import { getTuning, patchTuning, resetTuning, getTrends } from '#lib/api/tuning.js';
const weights = (over: Partial<Record<string, number>> = {}) => ({
base_weight: 1,
+7 -7
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { useQueryClient } from '@tanstack/svelte-query';
import { qk } from '$lib/api/queries';
import { qk } from '#lib/api/queries.js';
import {
updateUserAdmin,
createInvite,
@@ -15,11 +15,11 @@
type AdminUser,
type AdminInvite,
type CreateUserInput
} from '$lib/api/admin';
import { errCode } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
import Modal from '$lib/components/Modal.svelte';
import RowActionsMenu, { type RowAction } from '$lib/components/RowActionsMenu.svelte';
} from '#lib/api/admin.js';
import { errCode } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
import Modal from '#lib/components/Modal.svelte';
import RowActionsMenu, { type RowAction } from '#lib/components/RowActionsMenu.svelte';
import { Shield, ShieldOff, CheckCircle2, XCircle, KeyRound, Trash2, Copy } from 'lucide-svelte';
const client = useQueryClient();
+3 -3
View File
@@ -1,11 +1,11 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
import type { AdminUser, AdminInvite } from '$lib/api/admin';
import type { AdminUser, AdminInvite } from '#lib/api/admin.js';
// Wrap useQueryClient so the page can call invalidateQueries() without a real
// QueryClient context. Everything else from svelte-query passes through.
vi.mock('$lib/api/admin', () => ({
vi.mock('#lib/api/admin.js', () => ({
createAdminUsersQuery: vi.fn(),
createAdminInvitesQuery: vi.fn(),
listUsers: vi.fn(),
@@ -30,7 +30,7 @@ import {
deleteUser,
resetUserPassword,
updateUserAutoApprove
} from '$lib/api/admin';
} from '#lib/api/admin.js';
const alice: AdminUser = {
id: 'u1',
+15 -15
View File
@@ -1,21 +1,21 @@
<script lang="ts">
import { Play, Shuffle } from 'lucide-svelte';
import { page } from '$app/state';
import { createAlbumQuery } from '$lib/api/queries';
import { qk } from '$lib/api/queries';
import { pageTitle } from '$lib/branding';
import TrackRow from '$lib/components/TrackRow.svelte';
import LibrarySkeleton from '$lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import LikeButton from '$lib/components/LikeButton.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import { formatDuration } from '$lib/media/duration';
import { FALLBACK_COVER } from '$lib/media/covers';
import type { ApiError } from '$lib/api/client';
import { playQueue } from '$lib/player/store.svelte';
import { user } from '$lib/auth/store.svelte';
import { refetchAlbumCover } from '$lib/api/admin';
import { errCode } from '$lib/api/errors';
import { createAlbumQuery } from '#lib/api/queries.js';
import { qk } from '#lib/api/queries.js';
import { pageTitle } from '#lib/branding.js';
import TrackRow from '#lib/components/TrackRow.svelte';
import LibrarySkeleton from '#lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import LikeButton from '#lib/components/LikeButton.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
import { formatDuration } from '#lib/media/duration.js';
import { FALLBACK_COVER } from '#lib/media/covers.js';
import type { ApiError } from '#lib/api/client.js';
import { playQueue } from '#lib/player/store.svelte.js';
import { user } from '#lib/auth/store.svelte.js';
import { refetchAlbumCover } from '#lib/api/admin.js';
import { errCode } from '#lib/api/errors.js';
import { useQueryClient } from '@tanstack/svelte-query';
const id = $derived(page.params.id ?? '');
+4 -4
View File
@@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/svelte';
import { flushSync } from 'svelte';
import { mockQuery } from '../../../test-utils/query';
import { emptyLikesMock } from '../../../test-utils/mocks/likes';
import type { AlbumDetail, TrackRef } from '$lib/api/types';
import type { AlbumDetail, TrackRef } from '#lib/api/types.js';
import { makeTrack } from '$test-utils/fixtures/track';
const state = vi.hoisted(() => ({ pageParams: { id: 'xyz' } as Record<string, string> }));
@@ -15,15 +15,15 @@ vi.mock('$app/state', () => ({
}
}));
vi.mock('$lib/api/queries', () => ({
vi.mock('#lib/api/queries.js', () => ({
qk: { album: (id: string) => ['album', id] },
createAlbumQuery: vi.fn()
}));
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
import AlbumPage from './+page.svelte';
import { createAlbumQuery } from '$lib/api/queries';
import { createAlbumQuery } from '#lib/api/queries.js';
function track(id: string, title: string, number: number, dur: number): TrackRef {
return makeTrack({
+14 -14
View File
@@ -5,20 +5,20 @@
createArtistQuery,
createSimilarArtistsQuery,
createArtistTopTracksQuery
} from '$lib/api/queries';
import { pageTitle } from '$lib/branding';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import ArtistCard from '$lib/components/ArtistCard.svelte';
import CompactTrackCard from '$lib/components/CompactTrackCard.svelte';
import HorizontalScrollRow from '$lib/components/HorizontalScrollRow.svelte';
import LikeButton from '$lib/components/LikeButton.svelte';
import LibrarySkeleton from '$lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import { api } from '$lib/api/client';
import { playQueue } from '$lib/player/store.svelte';
import type { ApiError } from '$lib/api/client';
import type { ArtistRef, TrackRef } from '$lib/api/types';
} from '#lib/api/queries.js';
import { pageTitle } from '#lib/branding.js';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import ArtistCard from '#lib/components/ArtistCard.svelte';
import CompactTrackCard from '#lib/components/CompactTrackCard.svelte';
import HorizontalScrollRow from '#lib/components/HorizontalScrollRow.svelte';
import LikeButton from '#lib/components/LikeButton.svelte';
import LibrarySkeleton from '#lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
import { api } from '#lib/api/client.js';
import { playQueue } from '#lib/player/store.svelte.js';
import type { ApiError } from '#lib/api/client.js';
import type { ArtistRef, TrackRef } from '#lib/api/types.js';
const id = $derived(page.params.id ?? '');
const queryStore = $derived(createArtistQuery(id));
+4 -4
View File
@@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/svelte';
import { flushSync } from 'svelte';
import { mockQuery } from '../../../test-utils/query';
import { emptyLikesMock } from '../../../test-utils/mocks/likes';
import type { ArtistDetail, AlbumRef } from '$lib/api/types';
import type { ArtistDetail, AlbumRef } from '#lib/api/types.js';
const state = vi.hoisted(() => ({ pageParams: { id: 'abc' } as Record<string, string> }));
@@ -14,21 +14,21 @@ vi.mock('$app/state', () => ({
}
}));
vi.mock('$lib/api/queries', () => ({
vi.mock('#lib/api/queries.js', () => ({
qk: { artist: (id: string) => ['artist', id] },
createArtistQuery: vi.fn(),
createSimilarArtistsQuery: vi.fn(),
createArtistTopTracksQuery: vi.fn()
}));
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
import ArtistPage from './+page.svelte';
import {
createArtistQuery,
createSimilarArtistsQuery,
createArtistTopTracksQuery
} from '$lib/api/queries';
} from '#lib/api/queries.js';
function album(id: string, title: string, year?: number): AlbumRef {
return {
+10 -10
View File
@@ -1,18 +1,18 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { createLidarrSearchQuery } from '$lib/api/lidarr';
import { createRequest } from '$lib/api/requests';
import { qk } from '$lib/api/queries';
import { pageTitle } from '#lib/branding.js';
import { createLidarrSearchQuery } from '#lib/api/lidarr.js';
import { createRequest } from '#lib/api/requests.js';
import { qk } from '#lib/api/queries.js';
import { useQueryClient } from '@tanstack/svelte-query';
import DiscoverResultCard from '$lib/components/DiscoverResultCard.svelte';
import DiscoverTabs from '$lib/components/DiscoverTabs.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import SuggestionFeed from '$lib/components/SuggestionFeed.svelte';
import Modal from '$lib/components/Modal.svelte';
import DiscoverResultCard from '#lib/components/DiscoverResultCard.svelte';
import DiscoverTabs from '#lib/components/DiscoverTabs.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import SuggestionFeed from '#lib/components/SuggestionFeed.svelte';
import Modal from '#lib/components/Modal.svelte';
import type {
LidarrRequestKind,
LidarrSearchResult
} from '$lib/api/types';
} from '#lib/api/types.js';
const queryClient = useQueryClient();
+8 -8
View File
@@ -2,7 +2,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';
import type { LidarrSearchResult } from '#lib/api/types.js';
import { pageUrlModule } from '../../test-utils/mocks/appState';
// $app/state / $app/navigation must be mocked or rendering the page
@@ -17,7 +17,7 @@ vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
// Lidarr search query factory and createRequest are mocked at the module
// level so each test can shape what the page sees without standing up a
// real QueryClient + network.
vi.mock('$lib/api/lidarr', () => ({
vi.mock('#lib/api/lidarr.js', () => ({
createLidarrSearchQuery: vi.fn()
}));
@@ -26,18 +26,18 @@ vi.mock('$lib/api/lidarr', () => ({
// snoozing: a factory-shaped module mock must export everything the
// component tree imports, or rendering the feed throws before any
// assertion runs.
vi.mock('$lib/api/suggestions', () => ({
vi.mock('#lib/api/suggestions.js', () => ({
createSuggestionsQuery: vi.fn(),
createSnoozesQuery: vi.fn(),
snoozeSuggestion: vi.fn().mockResolvedValue(undefined),
unsnoozeSuggestion: vi.fn().mockResolvedValue(undefined)
}));
vi.mock('$lib/api/requests', () => ({
vi.mock('#lib/api/requests.js', () => ({
createRequest: vi.fn().mockResolvedValue({ id: 'r1' })
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('#lib/api/client.js', () => apiClientMock());
// The page calls useQueryClient() to invalidate qk.myRequests() after a
// submit (#369); stub it so rendering doesn't require a real QueryClient and
@@ -49,9 +49,9 @@ vi.mock('@tanstack/svelte-query', async (importOriginal) => {
});
import DiscoverPage from './+page.svelte';
import { createLidarrSearchQuery } from '$lib/api/lidarr';
import { createSuggestionsQuery, createSnoozesQuery } from '$lib/api/suggestions';
import { createRequest } from '$lib/api/requests';
import { createLidarrSearchQuery } from '#lib/api/lidarr.js';
import { createSuggestionsQuery, createSnoozesQuery } from '#lib/api/suggestions.js';
import { createRequest } from '#lib/api/requests.js';
const mockedCreateQuery = createLidarrSearchQuery as ReturnType<typeof vi.fn>;
const mockedCreateSuggestionsQuery = createSuggestionsQuery as ReturnType<typeof vi.fn>;
+3 -3
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { forgotPassword } from '$lib/auth/store.svelte';
import { rateLimitMessage } from '$lib/api/client';
import { pageTitle } from '#lib/branding.js';
import { forgotPassword } from '#lib/auth/store.svelte.js';
import { rateLimitMessage } from '#lib/api/client.js';
let email = $state('');
let submitted = $state(false);
@@ -1,11 +1,11 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, fireEvent, screen, waitFor } from '@testing-library/svelte';
vi.mock('$lib/auth/store.svelte', () => ({
vi.mock('#lib/auth/store.svelte.js', () => ({
forgotPassword: vi.fn(),
}));
import { forgotPassword } from '$lib/auth/store.svelte';
import { forgotPassword } from '#lib/auth/store.svelte.js';
import ForgotPasswordPage from './+page.svelte';
describe('/forgot-password page', () => {
+10 -10
View File
@@ -1,14 +1,14 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { createAlbumsAlphaInfiniteQuery } from '$lib/api/albums';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import AlphabeticalGrid from '$lib/components/AlphabeticalGrid.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import InfiniteScrollSentinel from '$lib/components/InfiniteScrollSentinel.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import type { AlbumRef } from '$lib/api/types';
import { pageTitle } from '#lib/branding.js';
import { createAlbumsAlphaInfiniteQuery } from '#lib/api/albums.js';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import AlphabeticalGrid from '#lib/components/AlphabeticalGrid.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import InfiniteScrollSentinel from '#lib/components/InfiniteScrollSentinel.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
import type { AlbumRef } from '#lib/api/types.js';
const queryStore = createAlbumsAlphaInfiniteQuery();
const query = $derived($queryStore);
+4 -4
View File
@@ -4,7 +4,7 @@ import { readable } from 'svelte/store';
import { emptyLikesMock } from '../../../test-utils/mocks/likes';
import { apiClientMock } from '../../../test-utils/mocks/client';
vi.mock('$lib/api/albums', () => ({
vi.mock('#lib/api/albums.js', () => ({
createAlbumsAlphaInfiniteQuery: () => readable({
data: { pages: [{
items: [
@@ -26,12 +26,12 @@ vi.mock('$lib/api/albums', () => ({
}),
ALBUM_PAGE_SIZE: 50
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/player/store.svelte.js', () => ({
enqueueTracks: vi.fn(),
playQueue: vi.fn()
}));
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
// AlbumCard renders LikeButton, which calls useQueryClient() to get the
// cache for invalidation. The page test isn't wrapped in a QueryClientProvider,
// so we stub useQueryClient with a noop client.
+10 -10
View File
@@ -1,14 +1,14 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { createArtistsQuery } from '$lib/api/queries';
import ArtistCard from '$lib/components/ArtistCard.svelte';
import AlphabeticalGrid from '$lib/components/AlphabeticalGrid.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import InfiniteScrollSentinel from '$lib/components/InfiniteScrollSentinel.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import type { ArtistRef } from '$lib/api/types';
import { pageTitle } from '#lib/branding.js';
import { createArtistsQuery } from '#lib/api/queries.js';
import ArtistCard from '#lib/components/ArtistCard.svelte';
import AlphabeticalGrid from '#lib/components/AlphabeticalGrid.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import InfiniteScrollSentinel from '#lib/components/InfiniteScrollSentinel.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
import type { ArtistRef } from '#lib/api/types.js';
const queryStore = $derived(createArtistsQuery('alpha'));
const query = $derived($queryStore);
+5 -5
View File
@@ -4,7 +4,7 @@ import { readable } from 'svelte/store';
import { emptyLikesMock } from '../../../test-utils/mocks/likes';
import { apiClientMock } from '../../../test-utils/mocks/client';
vi.mock('$lib/api/queries', () => ({
vi.mock('#lib/api/queries.js', () => ({
createArtistsQuery: () => readable({
data: { pages: [{
items: [
@@ -21,15 +21,15 @@ vi.mock('$lib/api/queries', () => ({
fetchNextPage: () => {}
})
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(),
enqueueTracks: vi.fn()
}));
vi.mock('$lib/api/artists', () => ({
vi.mock('#lib/api/artists.js', () => ({
listArtistTracks: vi.fn().mockResolvedValue([])
}));
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
import Page from './+page.svelte';
describe('/library/artists', () => {
+7 -7
View File
@@ -1,18 +1,18 @@
<script lang="ts">
import { page } from '$app/state';
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { ChevronLeft } from 'lucide-svelte';
import {
createGenresQuery,
listAlbumsByGenre,
BROWSE_PAGE_SIZE,
type GenreCount
} from '$lib/api/browse';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import type { AlbumRef } from '$lib/api/types';
} from '#lib/api/browse.js';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import type { AlbumRef } from '#lib/api/types.js';
const indexStore = createGenresQuery();
const index = $derived($indexStore);
+6 -6
View File
@@ -4,7 +4,7 @@ import { mockQuery } from '$test-utils/query';
import { pageUrlModule } from '$test-utils/mocks/appState';
import { apiClientMock } from '$test-utils/mocks/client';
import { emptyLikesMock } from '$test-utils/mocks/likes';
import type { AlbumRef } from '$lib/api/types';
import type { AlbumRef } from '#lib/api/types.js';
const pageState = vi.hoisted(() => ({
pageUrl: new URL('http://localhost/library/genres')
@@ -12,15 +12,15 @@ const pageState = vi.hoisted(() => ({
vi.mock('$app/state', () => pageUrlModule(pageState));
vi.mock('$lib/api/browse', () => ({
vi.mock('#lib/api/browse.js', () => ({
BROWSE_PAGE_SIZE: 2,
createGenresQuery: vi.fn(),
listAlbumsByGenre: vi.fn()
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(),
playRadio: vi.fn(),
enqueueTrack: vi.fn(),
@@ -29,7 +29,7 @@ vi.mock('$lib/player/store.svelte', () => ({
}));
import GenresPage from './+page.svelte';
import { createGenresQuery, listAlbumsByGenre } from '$lib/api/browse';
import { createGenresQuery, listAlbumsByGenre } from '#lib/api/browse.js';
const asMock = (fn: unknown) => fn as ReturnType<typeof vi.fn>;
+7 -7
View File
@@ -1,13 +1,13 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Music2, RotateCcw } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import { createMyQuarantineQuery, unflagTrack } from '$lib/api/quarantine';
import { qk } from '$lib/api/queries';
import type { LidarrQuarantineMineRow, LidarrQuarantineReason } from '$lib/api/types';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import { coverUrl } from '$lib/media/covers';
import { relativeTime } from '$lib/utils/relativeTime';
import { createMyQuarantineQuery, unflagTrack } from '#lib/api/quarantine.js';
import { qk } from '#lib/api/queries.js';
import type { LidarrQuarantineMineRow, LidarrQuarantineReason } from '#lib/api/types.js';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import { coverUrl } from '#lib/media/covers.js';
import { relativeTime } from '#lib/utils/relativeTime.js';
const client = useQueryClient();
const queryStore = createMyQuarantineQuery();
+3 -3
View File
@@ -2,14 +2,14 @@ import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
vi.mock('$lib/api/quarantine', () => ({
vi.mock('#lib/api/quarantine.js', () => ({
createMyQuarantineQuery: vi.fn(),
unflagTrack: vi.fn().mockResolvedValue(undefined)
}));
import HiddenPage from './+page.svelte';
import { createMyQuarantineQuery, unflagTrack } from '$lib/api/quarantine';
import type { LidarrQuarantineMineRow } from '$lib/api/types';
import { createMyQuarantineQuery, unflagTrack } from '#lib/api/quarantine.js';
import type { LidarrQuarantineMineRow } from '#lib/api/types.js';
const baseRow: LidarrQuarantineMineRow = {
track_id: 't1',
+9 -9
View File
@@ -1,13 +1,13 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { createHistoryQuery } from '$lib/api/history';
import { groupByDay } from '$lib/utils/dayGroup';
import HistoryRow from '$lib/components/HistoryRow.svelte';
import InfiniteScrollSentinel from '$lib/components/InfiniteScrollSentinel.svelte';
import LibrarySkeleton from '$lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import { pageTitle } from '#lib/branding.js';
import { createHistoryQuery } from '#lib/api/history.js';
import { groupByDay } from '#lib/utils/dayGroup.js';
import HistoryRow from '#lib/components/HistoryRow.svelte';
import InfiniteScrollSentinel from '#lib/components/InfiniteScrollSentinel.svelte';
import LibrarySkeleton from '#lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
const queryStore = createHistoryQuery();
const query = $derived($queryStore);
+6 -6
View File
@@ -1,7 +1,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen } from '@testing-library/svelte';
import HistoryPage from './+page.svelte';
import type { HistoryEvent, HistoryResponse } from '$lib/api/history';
import type { HistoryEvent, HistoryResponse } from '#lib/api/history.js';
import { makeTrack } from '$test-utils/fixtures/track';
const mkTrack = (id: string) => makeTrack({ id: `track-${id}`, title: `Song ${id}` });
@@ -20,9 +20,9 @@ let mockIsFetchingNextPage = false;
const mockFetchNextPage = vi.fn();
const mockRefetch = vi.fn();
vi.mock('$lib/api/history', async () => {
const actual = await vi.importActual<typeof import('$lib/api/history')>(
'$lib/api/history'
vi.mock('#lib/api/history.js', async () => {
const actual = await vi.importActual<typeof import('#lib/api/history.js')>(
'#lib/api/history.js'
);
return {
...actual,
@@ -44,11 +44,11 @@ vi.mock('$lib/api/history', async () => {
};
});
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn()
}));
vi.mock('$lib/branding', () => ({
vi.mock('#lib/branding.js', () => ({
pageTitle: (s: string) => `Minstrel · ${s}`
}));
+7 -7
View File
@@ -1,15 +1,15 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import {
createLikedTracksInfiniteQuery,
createLikedAlbumsInfiniteQuery,
createLikedArtistsInfiniteQuery
} from '$lib/api/likes';
import ArtistCard from '$lib/components/ArtistCard.svelte';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import TrackRow from '$lib/components/TrackRow.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
} from '#lib/api/likes.js';
import ArtistCard from '#lib/components/ArtistCard.svelte';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import TrackRow from '#lib/components/TrackRow.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
const artistsStore = $derived(createLikedArtistsInfiniteQuery());
const albumsStore = $derived(createLikedAlbumsInfiniteQuery());
+5 -5
View File
@@ -2,11 +2,11 @@ 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 type { ArtistRef, AlbumRef, TrackRef, Page } from '#lib/api/types.js';
import { readable } from 'svelte/store';
import { makeTrack } from '$test-utils/fixtures/track';
vi.mock('$lib/api/likes', () => ({
vi.mock('#lib/api/likes.js', () => ({
createLikedIdsQuery: () => readable({
data: { track_ids: [], album_ids: [], artist_ids: [] },
isPending: false,
@@ -19,8 +19,8 @@ vi.mock('$lib/api/likes', () => ({
unlikeEntity: vi.fn()
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(), enqueueTrack: vi.fn(), enqueueTracks: vi.fn(),
player: { current: undefined }
}));
@@ -30,7 +30,7 @@ import {
createLikedTracksInfiniteQuery,
createLikedAlbumsInfiniteQuery,
createLikedArtistsInfiniteQuery
} from '$lib/api/likes';
} from '#lib/api/likes.js';
function page<T>(items: T[], total: number, offset = 0, limit = 50): Page<T> {
return { items, total, offset, limit };
@@ -1,14 +1,14 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { goto } from '$app/navigation';
import { Plus } from 'lucide-svelte';
import PlaylistCard from '$lib/components/PlaylistCard.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import QuickFilter from '$lib/components/QuickFilter.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import { createPlaylistsQuery, createPlaylist } from '$lib/api/playlists';
import PlaylistCard from '#lib/components/PlaylistCard.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import QuickFilter from '#lib/components/QuickFilter.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import { createPlaylistsQuery, createPlaylist } from '#lib/api/playlists.js';
import { useQueryClient } from '@tanstack/svelte-query';
import { qk } from '$lib/api/queries';
import { qk } from '#lib/api/queries.js';
const playlistsStore = $derived(createPlaylistsQuery());
const playlistsQuery = $derived($playlistsStore);
@@ -1,9 +1,9 @@
import { describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
import type { Playlist } from '$lib/api/types';
import type { Playlist } from '#lib/api/types.js';
vi.mock('$lib/api/playlists', () => ({
vi.mock('#lib/api/playlists.js', () => ({
createPlaylistsQuery: vi.fn(),
createPlaylist: vi.fn().mockResolvedValue({ id: 'p-new', name: 'X' })
}));
@@ -11,7 +11,7 @@ vi.mock('$lib/api/playlists', () => ({
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
import PlaylistsPage from './+page.svelte';
import { createPlaylistsQuery, createPlaylist } from '$lib/api/playlists';
import { createPlaylistsQuery, createPlaylist } from '#lib/api/playlists.js';
const mockedCreatePlaylistsQuery = createPlaylistsQuery as ReturnType<typeof vi.fn>;
const mockedCreatePlaylist = createPlaylist as ReturnType<typeof vi.fn>;
+6 -6
View File
@@ -1,17 +1,17 @@
<script lang="ts">
import { page } from '$app/state';
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { ChevronLeft } from 'lucide-svelte';
import {
createAlbumYearsQuery,
listAlbumsByYear,
BROWSE_PAGE_SIZE,
type YearCount
} from '$lib/api/browse';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import EmptyState from '$lib/components/EmptyState.svelte';
import type { AlbumRef } from '$lib/api/types';
} from '#lib/api/browse.js';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import EmptyState from '#lib/components/EmptyState.svelte';
import type { AlbumRef } from '#lib/api/types.js';
const indexStore = createAlbumYearsQuery();
const index = $derived($indexStore);
+6 -6
View File
@@ -4,7 +4,7 @@ import { mockQuery } from '$test-utils/query';
import { pageUrlModule } from '$test-utils/mocks/appState';
import { apiClientMock } from '$test-utils/mocks/client';
import { emptyLikesMock } from '$test-utils/mocks/likes';
import type { AlbumRef } from '$lib/api/types';
import type { AlbumRef } from '#lib/api/types.js';
const pageState = vi.hoisted(() => ({
pageUrl: new URL('http://localhost/library/years')
@@ -12,15 +12,15 @@ const pageState = vi.hoisted(() => ({
vi.mock('$app/state', () => pageUrlModule(pageState));
vi.mock('$lib/api/browse', () => ({
vi.mock('#lib/api/browse.js', () => ({
BROWSE_PAGE_SIZE: 2,
createAlbumYearsQuery: vi.fn(),
listAlbumsByYear: vi.fn()
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(),
playRadio: vi.fn(),
enqueueTrack: vi.fn(),
@@ -29,7 +29,7 @@ vi.mock('$lib/player/store.svelte', () => ({
}));
import YearsPage from './+page.svelte';
import { createAlbumYearsQuery, listAlbumsByYear } from '$lib/api/browse';
import { createAlbumYearsQuery, listAlbumsByYear } from '#lib/api/browse.js';
const asMock = (fn: unknown) => fn as ReturnType<typeof vi.fn>;
+4 -4
View File
@@ -1,9 +1,9 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { page } from '$app/state';
import { goto } from '$app/navigation';
import { login } from '$lib/auth/store.svelte';
import { rateLimitMessage, type ApiError } from '$lib/api/client';
import { login } from '#lib/auth/store.svelte.js';
import { rateLimitMessage, type ApiError } from '#lib/api/client.js';
let username = $state('');
let password = $state('');
@@ -27,7 +27,7 @@
try {
await login(username, password);
const dest = safeReturnTo(page.url.searchParams.get('returnTo'));
goto(dest, { replaceState: true });
goto(dest, { replace: true });
} catch (err) {
const apiErr = err as ApiError;
const throttled = rateLimitMessage(err);
+5 -5
View File
@@ -13,13 +13,13 @@ vi.mock('$app/navigation', () => ({
goto: vi.fn()
}));
vi.mock('$lib/auth/store.svelte', () => ({
vi.mock('#lib/auth/store.svelte.js', () => ({
login: vi.fn(),
user: { value: null }
}));
import LoginPage from './+page.svelte';
import { login } from '$lib/auth/store.svelte';
import { login } from '#lib/auth/store.svelte.js';
import { goto } from '$app/navigation';
afterEach(() => {
@@ -81,7 +81,7 @@ describe('login page', () => {
render(LoginPage);
await submit();
await waitFor(() => {
expect(goto).toHaveBeenCalledWith('/artists/abc', { replaceState: true });
expect(goto).toHaveBeenCalledWith('/artists/abc', { replace: true });
});
});
@@ -90,7 +90,7 @@ describe('login page', () => {
render(LoginPage);
await submit();
await waitFor(() => {
expect(goto).toHaveBeenCalledWith('/', { replaceState: true });
expect(goto).toHaveBeenCalledWith('/', { replace: true });
});
});
@@ -105,7 +105,7 @@ describe('login page', () => {
render(LoginPage);
await submit();
await waitFor(() => {
expect(goto).toHaveBeenCalledWith('/', { replaceState: true });
expect(goto).toHaveBeenCalledWith('/', { replace: true });
});
});
+8 -8
View File
@@ -11,14 +11,14 @@
togglePlay, skipNext, skipPrev, seekTo, setVolume,
toggleShuffle, cycleRepeat,
toggleQueueDrawer
} from '$lib/player/store.svelte';
import { formatDuration } from '$lib/media/duration';
import { FALLBACK_COVER, coverUrl } from '$lib/media/covers';
import { useSmoothPosition } from '$lib/player/smoothPosition.svelte';
import LikeButton from '$lib/components/LikeButton.svelte';
import TrackMenu from '$lib/components/TrackMenu.svelte';
import QueueList from '$lib/components/QueueList.svelte';
import { pageTitle } from '$lib/branding';
} from '#lib/player/store.svelte.js';
import { formatDuration } from '#lib/media/duration.js';
import { FALLBACK_COVER, coverUrl } from '#lib/media/covers.js';
import { useSmoothPosition } from '#lib/player/smoothPosition.svelte.js';
import LikeButton from '#lib/components/LikeButton.svelte';
import TrackMenu from '#lib/components/TrackMenu.svelte';
import QueueList from '#lib/components/QueueList.svelte';
import { pageTitle } from '#lib/branding.js';
const current = $derived(player.current);
+7 -7
View File
@@ -2,10 +2,10 @@ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { render, screen, within } from '@testing-library/svelte';
import { readable } from 'svelte/store';
import { emptyLikesMock } from '../test-utils/mocks/likes';
import type { Playlist } from '$lib/api/types';
import type { Playlist } from '#lib/api/types.js';
// Mock all the queries the home page constructs.
vi.mock('$lib/api/home', () => ({
vi.mock('#lib/api/home.js', () => ({
createHomeQuery: () =>
readable({
data: {
@@ -23,20 +23,20 @@ vi.mock('$lib/api/home', () => ({
})
}));
vi.mock('$lib/api/playlists', () => ({
vi.mock('#lib/api/playlists.js', () => ({
createPlaylistsQuery: vi.fn()
}));
vi.mock('$lib/api/me', () => ({
vi.mock('#lib/api/me.js', () => ({
createSystemPlaylistsStatusQuery: vi.fn()
}));
// LikeButton imports — needed because the home renders cards that may use it.
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
import Page from './+page.svelte';
import { createPlaylistsQuery } from '$lib/api/playlists';
import { createSystemPlaylistsStatusQuery } from '$lib/api/me';
import { createPlaylistsQuery } from '#lib/api/playlists.js';
import { createSystemPlaylistsStatusQuery } from '#lib/api/me.js';
const emptyPlaylistsResponse = { owned: [], public: [] };
+12 -12
View File
@@ -2,11 +2,11 @@
import { page } from '$app/state';
import { goto } from '$app/navigation';
import { untrack } from 'svelte';
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Pencil, Trash2, Link as LinkIcon } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import PlaylistTrackRow from '$lib/components/PlaylistTrackRow.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import PlaylistTrackRow from '#lib/components/PlaylistTrackRow.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import {
createPlaylistQuery,
updatePlaylist,
@@ -15,15 +15,15 @@
reorderPlaylist,
refreshSystem,
systemShuffle
} from '$lib/api/playlists';
import { qk } from '$lib/api/queries';
import { user } from '$lib/auth/store.svelte';
import { errCode, errMessage } from '$lib/api/errors';
import { playQueue } from '$lib/player/store.svelte';
import { systemPlaylistRefetch } from '$lib/playlists/systemRefetch';
import { systemRebuilt } from '$lib/serverEvents.svelte';
import { pushToast } from '$lib/stores/toast.svelte';
import type { TrackRef } from '$lib/api/types';
} from '#lib/api/playlists.js';
import { qk } from '#lib/api/queries.js';
import { user } from '#lib/auth/store.svelte.js';
import { errCode, errMessage } from '#lib/api/errors.js';
import { playQueue } from '#lib/player/store.svelte.js';
import { systemPlaylistRefetch } from '#lib/playlists/systemRefetch.js';
import { systemRebuilt } from '#lib/serverEvents.svelte.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
import type { TrackRef } from '#lib/api/types.js';
// SvelteKit types page.params.id as string | undefined (the route
// could in principle render with the slot empty); empty-string fallback
@@ -2,7 +2,7 @@ import { describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
import { mockQuery } from '../../../test-utils/query';
import { emptyQuarantineMock } from '../../../test-utils/mocks/quarantine';
import type { PlaylistDetail } from '$lib/api/types';
import type { PlaylistDetail } from '#lib/api/types.js';
// Project canonical: $app/state (rune-state), not $app/stores (legacy
// reactive store). The page module exports an object with a `params`
@@ -13,7 +13,7 @@ vi.mock('$app/state', () => ({
vi.mock('$app/navigation', () => ({ goto: vi.fn() }));
vi.mock('$lib/api/playlists', () => ({
vi.mock('#lib/api/playlists.js', () => ({
createPlaylistQuery: vi.fn(),
updatePlaylist: vi.fn().mockResolvedValue(undefined),
deletePlaylist: vi.fn().mockResolvedValue(undefined),
@@ -22,11 +22,11 @@ vi.mock('$lib/api/playlists', () => ({
refreshSystem: vi.fn().mockResolvedValue({ playlist_id: 'p1', track_count: 5, track_ids: [] })
}));
vi.mock('$lib/auth/store.svelte', () => ({
vi.mock('#lib/auth/store.svelte.js', () => ({
user: { value: { id: 'u-self', username: 'me', is_admin: false } }
}));
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(),
enqueueTracks: vi.fn(),
playNext: vi.fn(),
@@ -35,15 +35,15 @@ vi.mock('$lib/player/store.svelte', () => ({
}));
// Cascade through PlaylistTrackRow's mocks (LikeButton + TrackMenu transitively).
vi.mock('$lib/api/likes', () => ({
vi.mock('#lib/api/likes.js', () => ({
createLikedIdsQuery: () => ({ subscribe: () => () => {}, data: { track_ids: [], album_ids: [], artist_ids: [] } }),
likeEntity: vi.fn(),
unlikeEntity: vi.fn()
}));
vi.mock('$lib/api/quarantine', () => emptyQuarantineMock());
vi.mock('#lib/api/quarantine.js', () => emptyQuarantineMock());
import PlaylistDetailPage from './+page.svelte';
import { createPlaylistQuery, deletePlaylist, refreshSystem } from '$lib/api/playlists';
import { createPlaylistQuery, deletePlaylist, refreshSystem } from '#lib/api/playlists.js';
const mockedQuery = createPlaylistQuery as ReturnType<typeof vi.fn>;
+5 -5
View File
@@ -1,10 +1,10 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { goto } from '$app/navigation';
import { onMount } from 'svelte';
import { getSetupStatus, register } from '$lib/auth/store.svelte';
import { errCode } from '$lib/api/errors';
import { rateLimitMessage } from '$lib/api/client';
import { getSetupStatus, register } from '#lib/auth/store.svelte.js';
import { errCode } from '#lib/api/errors.js';
import { rateLimitMessage } from '#lib/api/client.js';
let username = $state('');
let password = $state('');
@@ -63,7 +63,7 @@
setupToken: setupToken.trim() || undefined,
displayName: displayName || undefined,
});
await goto('/', { replaceState: true });
await goto('/', { replace: true });
} catch (err: unknown) {
error = rateLimitMessage(err) ?? errorMessageFor(errCode(err));
} finally {
+4 -4
View File
@@ -1,7 +1,7 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor } from '@testing-library/svelte';
vi.mock('$lib/branding', () => ({
vi.mock('#lib/branding.js', () => ({
pageTitle: (s: string) => `${s} · Minstrel`
}));
@@ -9,14 +9,14 @@ vi.mock('$app/navigation', () => ({
goto: vi.fn()
}));
vi.mock('$lib/auth/store.svelte', () => ({
vi.mock('#lib/auth/store.svelte.js', () => ({
register: vi.fn(),
getSetupStatus: vi.fn().mockResolvedValue({ setup_required: false }),
user: { value: null }
}));
import RegisterPage from './+page.svelte';
import { getSetupStatus, register } from '$lib/auth/store.svelte';
import { getSetupStatus, register } from '#lib/auth/store.svelte.js';
import { goto } from '$app/navigation';
afterEach(() => {
@@ -60,7 +60,7 @@ describe('/register page', () => {
}));
});
await waitFor(() => {
expect(goto).toHaveBeenCalledWith('/', { replaceState: true });
expect(goto).toHaveBeenCalledWith('/', { replace: true });
});
});
+7 -7
View File
@@ -1,13 +1,13 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { Disc3, Album, Music2, X } from 'lucide-svelte';
import { useQueryClient } from '@tanstack/svelte-query';
import { createMyRequestsQuery, cancelRequest } from '$lib/api/requests';
import { qk } from '$lib/api/queries';
import StatusPill from '$lib/components/StatusPill.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import DiscoverTabs from '$lib/components/DiscoverTabs.svelte';
import type { LidarrRequest, LidarrRequestKind } from '$lib/api/types';
import { createMyRequestsQuery, cancelRequest } from '#lib/api/requests.js';
import { qk } from '#lib/api/queries.js';
import StatusPill from '#lib/components/StatusPill.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import DiscoverTabs from '#lib/components/DiscoverTabs.svelte';
import type { LidarrRequest, LidarrRequestKind } from '#lib/api/types.js';
const queryStore = createMyRequestsQuery();
const query = $derived($queryStore);
+6 -6
View File
@@ -2,7 +2,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';
import type { LidarrRequest } from '#lib/api/types.js';
import { pageUrlModule } from '../../test-utils/mocks/appState';
// $app/state / $app/navigation must be mocked or rendering the page
@@ -23,22 +23,22 @@ vi.mock('@tanstack/svelte-query', async (orig) => {
return { ...actual, useQueryClient: () => ({ invalidateQueries }) };
});
vi.mock('$lib/api/requests', () => ({
vi.mock('#lib/api/requests.js', () => ({
createMyRequestsQuery: vi.fn(),
cancelRequest: vi.fn().mockResolvedValue({ id: 'r1' })
}));
vi.mock('$lib/api/queries', () => ({
vi.mock('#lib/api/queries.js', () => ({
qk: {
myRequests: () => ['myRequests']
}
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('#lib/api/client.js', () => apiClientMock());
import RequestsPage from './+page.svelte';
import { createMyRequestsQuery, cancelRequest } from '$lib/api/requests';
import { qk } from '$lib/api/queries';
import { createMyRequestsQuery, cancelRequest } from '#lib/api/requests.js';
import { qk } from '#lib/api/queries.js';
const mockedCreateMyRequestsQuery = createMyRequestsQuery as ReturnType<typeof vi.fn>;
const mockedCancelRequest = cancelRequest as ReturnType<typeof vi.fn>;
@@ -1,10 +1,10 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/state';
import { pageTitle } from '$lib/branding';
import { resetPassword } from '$lib/auth/store.svelte';
import { errCode } from '$lib/api/errors';
import { rateLimitMessage } from '$lib/api/client';
import { pageTitle } from '#lib/branding.js';
import { resetPassword } from '#lib/auth/store.svelte.js';
import { errCode } from '#lib/api/errors.js';
import { rateLimitMessage } from '#lib/api/client.js';
let newPassword = $state('');
let confirmPassword = $state('');
@@ -27,7 +27,7 @@
submitting = true;
try {
await resetPassword(token, newPassword);
await goto('/login?reset=ok', { replaceState: true });
await goto('/login?reset=ok', { replace: true });
} catch (e: unknown) {
const code = errCode(e);
const throttled = rateLimitMessage(e);
@@ -1,7 +1,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, fireEvent, screen, waitFor } from '@testing-library/svelte';
vi.mock('$lib/auth/store.svelte', () => ({
vi.mock('#lib/auth/store.svelte.js', () => ({
resetPassword: vi.fn(),
}));
@@ -13,7 +13,7 @@ vi.mock('$app/state', () => ({
page: { params: { token: 'test-token-abc' } },
}));
import { resetPassword } from '$lib/auth/store.svelte';
import { resetPassword } from '#lib/auth/store.svelte.js';
import { goto } from '$app/navigation';
import ResetPasswordPage from './+page.svelte';
@@ -36,7 +36,7 @@ describe('/reset-password/[token] page', () => {
await fireEvent.input(screen.getByLabelText('Confirm new password'), { target: { value: 'abcd1234' } });
await fireEvent.click(screen.getByRole('button', { name: 'Set password' }));
expect(resetPassword).toHaveBeenCalledWith('test-token-abc', 'abcd1234');
await waitFor(() => expect(goto).toHaveBeenCalledWith('/login?reset=ok', expect.objectContaining({ replaceState: true })));
await waitFor(() => expect(goto).toHaveBeenCalledWith('/login?reset=ok', expect.objectContaining({ replace: true })));
});
it('shows clear error on invalid_token', async () => {
+10 -10
View File
@@ -1,15 +1,15 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { page } from '$app/state';
import { createSearchQuery } from '$lib/api/queries';
import ArtistCard from '$lib/components/ArtistCard.svelte';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import TrackRow from '$lib/components/TrackRow.svelte';
import SearchSkeleton from '$lib/components/SearchSkeleton.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import { playRadio } from '$lib/player/store.svelte';
import type { TrackRef } from '$lib/api/types';
import { createSearchQuery } from '#lib/api/queries.js';
import ArtistCard from '#lib/components/ArtistCard.svelte';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import TrackRow from '#lib/components/TrackRow.svelte';
import SearchSkeleton from '#lib/components/SearchSkeleton.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
import { playRadio } from '#lib/player/store.svelte.js';
import type { TrackRef } from '#lib/api/types.js';
const q = $derived((page.url.searchParams.get('q') ?? '').trim());
const queryStore = $derived(q ? createSearchQuery(q) : null);
+6 -6
View File
@@ -1,11 +1,11 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { page } from '$app/state';
import { createSearchAlbumsInfiniteQuery } from '$lib/api/queries';
import AlbumCard from '$lib/components/AlbumCard.svelte';
import LibrarySkeleton from '$lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import { createSearchAlbumsInfiniteQuery } from '#lib/api/queries.js';
import AlbumCard from '#lib/components/AlbumCard.svelte';
import LibrarySkeleton from '#lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
const q = $derived((page.url.searchParams.get('q') ?? '').trim());
const queryStore = $derived(q ? createSearchAlbumsInfiniteQuery(q) : null);
+6 -6
View File
@@ -4,25 +4,25 @@ 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';
import type { AlbumRef, Page } from '#lib/api/types.js';
const state = vi.hoisted(() => ({ pageUrl: new URL('http://localhost/search/albums?q=miles') }));
vi.mock('$app/state', () => pageUrlModule(state));
vi.mock('$lib/api/queries', () => ({
vi.mock('#lib/api/queries.js', () => ({
createSearchAlbumsInfiniteQuery: vi.fn()
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('#lib/player/store.svelte.js', () => ({
enqueueTracks: vi.fn()
}));
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
import AlbumsOverflow from './+page.svelte';
import { createSearchAlbumsInfiniteQuery } from '$lib/api/queries';
import { createSearchAlbumsInfiniteQuery } from '#lib/api/queries.js';
function page<T>(items: T[], total: number, offset = 0, limit = 50): Page<T> {
return { items, total, offset, limit };
+6 -6
View File
@@ -1,11 +1,11 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { page } from '$app/state';
import { createSearchArtistsInfiniteQuery } from '$lib/api/queries';
import ArtistCard from '$lib/components/ArtistCard.svelte';
import LibrarySkeleton from '$lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import { createSearchArtistsInfiniteQuery } from '#lib/api/queries.js';
import ArtistCard from '#lib/components/ArtistCard.svelte';
import LibrarySkeleton from '#lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
const q = $derived((page.url.searchParams.get('q') ?? '').trim());
const queryStore = $derived(q ? createSearchArtistsInfiniteQuery(q) : null);
@@ -3,20 +3,20 @@ import { render, screen, fireEvent } from '@testing-library/svelte';
import { mockInfiniteQuery } from '../../../test-utils/query';
import { emptyLikesMock } from '../../../test-utils/mocks/likes';
import { pageUrlModule } from '$test-utils/mocks/appState';
import type { ArtistRef, Page } from '$lib/api/types';
import type { ArtistRef, Page } from '#lib/api/types.js';
const state = vi.hoisted(() => ({ pageUrl: new URL('http://localhost/search/artists?q=miles') }));
vi.mock('$app/state', () => pageUrlModule(state));
vi.mock('$lib/api/queries', () => ({
vi.mock('#lib/api/queries.js', () => ({
createSearchArtistsInfiniteQuery: vi.fn()
}));
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
import ArtistsOverflow from './+page.svelte';
import { createSearchArtistsInfiniteQuery } from '$lib/api/queries';
import { createSearchArtistsInfiniteQuery } from '#lib/api/queries.js';
function page<T>(items: T[], total: number, offset = 0, limit = 50): Page<T> {
return { items, total, offset, limit };
+6 -6
View File
@@ -4,7 +4,7 @@ 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 type { ArtistRef, AlbumRef, SearchResponse } from '#lib/api/types.js';
import { makeTrack } from '$test-utils/fixtures/track';
const state = vi.hoisted(() => ({
@@ -13,11 +13,11 @@ const state = vi.hoisted(() => ({
vi.mock('$app/state', () => pageUrlModule(state));
vi.mock('$lib/api/queries', () => ({
vi.mock('#lib/api/queries.js', () => ({
createSearchQuery: vi.fn()
}));
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(),
playRadio: vi.fn(),
enqueueTrack: vi.fn(),
@@ -25,12 +25,12 @@ vi.mock('$lib/player/store.svelte', () => ({
player: { current: undefined }
}));
vi.mock('$lib/api/client', () => apiClientMock());
vi.mock('#lib/api/client.js', () => apiClientMock());
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
import SearchPage from './+page.svelte';
import { createSearchQuery } from '$lib/api/queries';
import { createSearchQuery } from '#lib/api/queries.js';
function emptyResp(): SearchResponse {
return {
+8 -8
View File
@@ -1,13 +1,13 @@
<script lang="ts">
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { page } from '$app/state';
import { createSearchTracksInfiniteQuery } from '$lib/api/queries';
import TrackRow from '$lib/components/TrackRow.svelte';
import LibrarySkeleton from '$lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '$lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '$lib/utils/useDelayed.svelte';
import { playRadio } from '$lib/player/store.svelte';
import type { TrackRef } from '$lib/api/types';
import { createSearchTracksInfiniteQuery } from '#lib/api/queries.js';
import TrackRow from '#lib/components/TrackRow.svelte';
import LibrarySkeleton from '#lib/components/LibrarySkeleton.svelte';
import ApiErrorBanner from '#lib/components/ApiErrorBanner.svelte';
import { useDelayed } from '#lib/utils/useDelayed.svelte.js';
import { playRadio } from '#lib/player/store.svelte.js';
import type { TrackRef } from '#lib/api/types.js';
const q = $derived((page.url.searchParams.get('q') ?? '').trim());
const queryStore = $derived(q ? createSearchTracksInfiniteQuery(q) : null);
+7 -7
View File
@@ -4,31 +4,31 @@ import { mockInfiniteQuery } from '../../../test-utils/query';
import { emptyLikesMock } from '../../../test-utils/mocks/likes';
import { emptyQuarantineMock } from '../../../test-utils/mocks/quarantine';
import { pageUrlModule } from '$test-utils/mocks/appState';
import type { TrackRef, Page } from '$lib/api/types';
import type { TrackRef, Page } from '#lib/api/types.js';
import { makeTrack } from '$test-utils/fixtures/track';
const state = vi.hoisted(() => ({ pageUrl: new URL('http://localhost/search/tracks?q=miles') }));
vi.mock('$app/state', () => pageUrlModule(state));
vi.mock('$lib/api/queries', () => ({
vi.mock('#lib/api/queries.js', () => ({
createSearchTracksInfiniteQuery: vi.fn()
}));
vi.mock('$lib/player/store.svelte', () => ({
vi.mock('#lib/player/store.svelte.js', () => ({
playQueue: vi.fn(),
playRadio: vi.fn(),
enqueueTrack: vi.fn(),
player: { current: undefined }
}));
vi.mock('$lib/api/likes', () => emptyLikesMock());
vi.mock('#lib/api/likes.js', () => emptyLikesMock());
vi.mock('$lib/api/quarantine', () => emptyQuarantineMock());
vi.mock('#lib/api/quarantine.js', () => emptyQuarantineMock());
import TracksOverflow from './+page.svelte';
import { createSearchTracksInfiniteQuery } from '$lib/api/queries';
import { playRadio } from '$lib/player/store.svelte';
import { createSearchTracksInfiniteQuery } from '#lib/api/queries.js';
import { playRadio } from '#lib/player/store.svelte.js';
function page<T>(items: T[], total: number, offset = 0, limit = 50): Page<T> {
return { items, total, offset, limit };
+13 -13
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { pageTitle } from '$lib/branding';
import { pageTitle } from '#lib/branding.js';
import { useQueryClient } from '@tanstack/svelte-query';
import type { CreateQueryResult, CreateMutationResult } from '@tanstack/svelte-query';
import {
@@ -8,15 +8,15 @@
createTokenMutation,
createEnabledMutation,
type LBStatus
} from '$lib/api/listenbrainz';
} from '#lib/api/listenbrainz.js';
import {
createRecommendationMetricsQuery,
type RecommendationMetrics,
type SurfaceIntent,
type MetricDelta
} from '$lib/api/metrics';
import { theme, setTheme, type ThemePreference } from '$lib/stores/theme.svelte';
import { player, setCrossfade } from '$lib/player/store.svelte';
} from '#lib/api/metrics.js';
import { theme, setTheme, type ThemePreference } from '#lib/stores/theme.svelte.js';
import { player, setCrossfade } from '#lib/player/store.svelte.js';
import {
updateProfile,
changePassword,
@@ -24,14 +24,14 @@
getSubsonicPasswordStatus,
generateSubsonicPassword,
clearSubsonicPassword
} from '$lib/api/me';
import { errCode } from '$lib/api/errors';
import { pushToast } from '$lib/stores/toast.svelte';
import MobileAppDownload from '$lib/components/MobileAppDownload.svelte';
import ServerVersion from '$lib/components/ServerVersion.svelte';
import ActiveSessions from '$lib/components/ActiveSessions.svelte';
import NormalizationSetting from '$lib/components/NormalizationSetting.svelte';
import NotificationSettings from '$lib/components/NotificationSettings.svelte';
} from '#lib/api/me.js';
import { errCode } from '#lib/api/errors.js';
import { pushToast } from '#lib/stores/toast.svelte.js';
import MobileAppDownload from '#lib/components/MobileAppDownload.svelte';
import ServerVersion from '#lib/components/ServerVersion.svelte';
import ActiveSessions from '#lib/components/ActiveSessions.svelte';
import NormalizationSetting from '#lib/components/NormalizationSetting.svelte';
import NotificationSettings from '#lib/components/NotificationSettings.svelte';
const queryClient = useQueryClient();
+6 -6
View File
@@ -1,7 +1,7 @@
import { describe, expect, test, vi, beforeEach } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
vi.mock('$lib/api/listenbrainz', () => {
vi.mock('#lib/api/listenbrainz.js', () => {
// Mocks must look like a Svelte readable store. The page reads e.g.
// $status.isPending, which is shorthand for "subscribe to status,
// then read .isPending on the emitted value." Returning an object
@@ -27,8 +27,8 @@ vi.mock('$lib/api/listenbrainz', () => {
// The Notifications section has its own tests (NotificationSettings.test.ts);
// here it only needs a settled query so the page renders.
vi.mock('$lib/api/notifications', async (importOriginal) => {
const mod = await importOriginal<typeof import('$lib/api/notifications')>();
vi.mock('#lib/api/notifications.js', async (importOriginal) => {
const mod = await importOriginal<typeof import('#lib/api/notifications.js')>();
const { mockQuery } = await import('../../test-utils/query');
return {
...mod,
@@ -39,8 +39,8 @@ vi.mock('$lib/api/notifications', async (importOriginal) => {
});
// The Volume leveling setting loads on mount; keep it off the network.
vi.mock('$lib/api/normalization', async (importOriginal) => {
const mod = await importOriginal<typeof import('$lib/api/normalization')>();
vi.mock('#lib/api/normalization.js', async (importOriginal) => {
const mod = await importOriginal<typeof import('#lib/api/normalization.js')>();
return {
...mod,
getNormalization: vi.fn().mockResolvedValue(mod.DEFAULT_NORMALIZATION),
@@ -48,7 +48,7 @@ vi.mock('$lib/api/normalization', async (importOriginal) => {
};
});
vi.mock('$lib/api/metrics', () => ({
vi.mock('#lib/api/metrics.js', () => ({
createRecommendationMetricsQuery: () => ({
subscribe: (run: (v: unknown) => void) => {
run({ isPending: false, isError: false, data: { window_days: 30, baseline: null, groups: [] } });
+10 -10
View File
@@ -1,9 +1,9 @@
import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent, waitFor, within } from '@testing-library/svelte';
import { readable, writable } from 'svelte/store';
import type { LBStatus } from '$lib/api/listenbrainz';
import type { LBStatus } from '#lib/api/listenbrainz.js';
vi.mock('$lib/api/listenbrainz', () => ({
vi.mock('#lib/api/listenbrainz.js', () => ({
createLBStatusQuery: vi.fn(),
createTokenMutation: vi.fn(),
createEnabledMutation: vi.fn(),
@@ -13,8 +13,8 @@ vi.mock('$lib/api/listenbrainz', () => ({
// The Notifications section has its own tests (NotificationSettings.test.ts);
// here it only needs a settled query so the page renders.
vi.mock('$lib/api/notifications', async (importOriginal) => {
const mod = await importOriginal<typeof import('$lib/api/notifications')>();
vi.mock('#lib/api/notifications.js', async (importOriginal) => {
const mod = await importOriginal<typeof import('#lib/api/notifications.js')>();
const { mockQuery } = await import('../../test-utils/query');
return {
...mod,
@@ -25,8 +25,8 @@ vi.mock('$lib/api/notifications', async (importOriginal) => {
});
// The Volume leveling setting loads on mount; keep it off the network.
vi.mock('$lib/api/normalization', async (importOriginal) => {
const mod = await importOriginal<typeof import('$lib/api/normalization')>();
vi.mock('#lib/api/normalization.js', async (importOriginal) => {
const mod = await importOriginal<typeof import('#lib/api/normalization.js')>();
return {
...mod,
getNormalization: vi.fn().mockResolvedValue(mod.DEFAULT_NORMALIZATION),
@@ -34,7 +34,7 @@ vi.mock('$lib/api/normalization', async (importOriginal) => {
};
});
vi.mock('$lib/api/me', () => ({
vi.mock('#lib/api/me.js', () => ({
updateProfile: vi.fn(),
changePassword: vi.fn(),
// Default to a resolved value so the page's $effect doesn't crash
@@ -51,7 +51,7 @@ const metricsMock = vi.hoisted(() => ({
data: { window_days: 30, baseline: null, groups: [] } as unknown
}));
vi.mock('$lib/api/metrics', () => ({
vi.mock('#lib/api/metrics.js', () => ({
createRecommendationMetricsQuery: () => ({
subscribe: (run: (v: unknown) => void) => {
run({ isPending: false, isError: false, data: metricsMock.data });
@@ -67,7 +67,7 @@ import {
createEnabledMutation,
setListenBrainzToken,
setListenBrainzEnabled
} from '$lib/api/listenbrainz';
} from '#lib/api/listenbrainz.js';
import {
updateProfile,
changePassword,
@@ -75,7 +75,7 @@ import {
getSubsonicPasswordStatus,
generateSubsonicPassword,
clearSubsonicPassword
} from '$lib/api/me';
} from '#lib/api/me.js';
function mockStatusStore(data: LBStatus) {
return readable({ data, isPending: false, isError: false });