Files
minstrel/web/vitest.setup.ts
T
bvandeusenandClaude Opus 5.5 94a9c8cbe3 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>
2026-10-08 10:50:39 -04:00

105 lines
4.7 KiB
TypeScript

import '@testing-library/jest-dom/vitest';
import { beforeEach, afterEach, vi } from 'vitest';
import { render } from '@testing-library/svelte';
import ToastHost from './src/lib/components/ToastHost.svelte';
import { clearToast } from './src/lib/stores/toast.svelte';
// Default svelte-query mock for the whole test suite. Most component
// tests just need useQueryClient to return SOMETHING — they never assert
// on the result. Tests that need to assert on invalidateQueries (named
// mock pattern) override this with their own per-file vi.mock. The
// override wins because per-file mocks take precedence over setup-file
// mocks in vitest.
vi.mock('@tanstack/svelte-query', async (orig) => {
const actual = (await orig()) as Record<string, unknown>;
return { ...actual, useQueryClient: () => ({ invalidateQueries: vi.fn() }) };
});
// Default SvelteKit app-module mocks for the whole suite (#3943). Without
// them, any page or component that reaches $app/navigation or $app/state —
// the home page does, through AlbumCard → AlbumMenu — loads SvelteKit's
// client runtime, whose $app/paths reads __SVELTEKIT_PAYLOAD__ at module
// load. That global only exists when the kit plugin's define reaches the
// module, which it does not reliably under vitest: page.test.ts failed to
// load on one CI run and passed on the re-run of the same commit (#374
// was the same class). Stubbing the modules keeps every test off the
// client bootstrap. Tests that assert on goto or need a particular URL
// still mock per file (pageUrlModule in test-utils/mocks/appState.ts),
// and per-file mocks win over these.
vi.mock('$app/navigation', () => ({
goto: vi.fn(() => Promise.resolve()),
invalidate: vi.fn(() => Promise.resolve()),
refreshAll: vi.fn(() => Promise.resolve()),
preloadData: vi.fn(() => Promise.resolve()),
preloadCode: vi.fn(() => Promise.resolve()),
beforeNavigate: vi.fn(),
afterNavigate: vi.fn(),
onNavigate: vi.fn(),
disableScrollHandling: vi.fn()
}));
vi.mock('$app/state', () => ({
page: {
url: new URL('http://localhost/'),
params: {},
route: { id: null },
status: 200,
error: null,
data: {},
form: null,
state: {}
},
navigating: null,
updated: { current: false, check: () => Promise.resolve(false) }
}));
vi.mock('$app/paths', () => ({
resolve: (path: string) => path,
asset: (path: string) => path
}));
// Node 25 ships a partial localStorage global that lacks getItem/setItem/clear
// unless launched with --localstorage-file. jsdom sees Node's global and skips
// installing its own. Replace with an in-memory Storage-compatible polyfill so
// tests (and browser-only code under test) see a working localStorage.
class MemoryStorage implements Storage {
private store = new Map<string, string>();
get length() { return this.store.size; }
clear(): void { this.store.clear(); }
getItem(key: string): string | null { return this.store.get(key) ?? null; }
key(i: number): string | null { return Array.from(this.store.keys())[i] ?? null; }
removeItem(key: string): void { this.store.delete(key); }
setItem(key: string, value: string): void { this.store.set(key, String(value)); }
}
const memLocal = new MemoryStorage();
const memSession = new MemoryStorage();
Object.defineProperty(globalThis, 'localStorage', { configurable: true, value: memLocal });
Object.defineProperty(globalThis, 'sessionStorage', { configurable: true, value: memSession });
if (typeof window !== 'undefined') {
Object.defineProperty(window, 'localStorage', { configurable: true, value: memLocal });
Object.defineProperty(window, 'sessionStorage', { configurable: true, value: memSession });
}
// jsdom doesn't implement Element.prototype.scrollIntoView. Components that
// call it (queue auto-scroll to the now-playing row, the alphabetical rail)
// would throw an unhandled TypeError in tests — which fails the run even when
// every assertion passes. No-op it; tests never assert on scroll position.
if (typeof Element !== 'undefined' && !Element.prototype.scrollIntoView) {
Element.prototype.scrollIntoView = () => {};
}
// W-T3 moved toast rendering out of per-page markup into a single
// <ToastHost /> mounted in +layout.svelte. Tests render individual pages
// without the layout, so we mount ToastHost here so `pushToast()` calls
// from page code surface in the DOM the same way they do in production.
// `render()` from @testing-library/svelte registers auto-cleanup via the
// svelteTesting() vite plugin, so the host gets unmounted between tests
// alongside whatever component the test itself rendered. clearToast()
// resets the module-scope store state so a stale toast from one test
// can't leak into another.
beforeEach(() => {
render(ToastHost);
});
afterEach(() => {
clearToast();
});