/** * #280: the app loads the library when a view needs it, not at startup. * * Every collection used to be fetched together at `DOMContentLoaded` * and refetched on every invalidation, whether or not anything was * showing it. On a 26 138-track library the track list was 20.5 MB of * that and cost the backend ~170 MB of transient allocation — for * someone looking at Home, which draws none of it. * * **This file must not load the track list before the first test.** It * asserts the state the app is actually left in by its own startup, so * a test added above that asks for tracks would invalidate the * precondition rather than silently pass. */ import { describe, expect, it, beforeEach } from 'vitest'; import { libraryStore } from '@store/library-store'; import { Events } from '../../src/events'; import { calls, emit, flush, resetHarness, stub, } from '@test/support/harness'; const TRACKS = 'library.Library.GetTrackTable'; const SMALL = [ 'library.Library.GetAlbums', 'library.Library.GetArtists', 'library.Library.GetGenres', ]; /** * Wait for the store's own idle warm-up to land. * * It asks for the three small collections on `requestIdleCallback`, so * when it has happened is the browser's decision — waiting for the * effect rather than for a duration is the only way this is not a race. */ function stubReads(): void { stub(TRACKS, { strings: [''] }); for (const path of SMALL) stub(path, []); } async function waitForWarm(): Promise { for (let i = 0; i < 200 && libraryStore.getCachedAlbums() === null; i++) { await flush(); } expect(libraryStore.getCachedAlbums(), 'the warm-up ran').not.toBeNull(); } describe('what the app loads on its own (#280)', () => { beforeEach(async () => { stubReads(); await waitForWarm(); // resetHarness clears the stubs as well as the recorded calls. resetHarness(); stubReads(); }); it('leaves the track list alone', () => { expect(libraryStore.getCachedTracks()).toBeNull(); expect(calls(TRACKS)).toEqual([]); }); it('does not load it to answer a scan', async () => { emit(Events.LibraryScanComplete); await flush(); // The small collections are in use, so they are refreshed; the // track list nobody has opened is not. expect(calls().map((c) => c.path).sort()).toEqual([...SMALL].sort()); }); it('loads it for the view that draws it, and refreshes it from then on', async () => { libraryStore.prefetch('tracks'); await flush(); expect(calls(TRACKS)).toHaveLength(1); expect(libraryStore.getCachedTracks()).not.toBeNull(); resetHarness(); emit(Events.LibraryScanComplete); await flush(); expect(calls(TRACKS), 'in use now, so a scan refreshes it').toHaveLength(1); }); it('is asked for nothing by a prefetch for a view it has no data for', async () => { libraryStore.prefetch('home'); libraryStore.prefetch('settings'); libraryStore.prefetch('explore'); await flush(); expect(calls()).toEqual([]); }); });