All four collections were fetched at DOMContentLoaded and refetched on every invalidation, whichever view was showing. On a 26 138-track library the track list was 20.5 MB of that, and encoding it cost the backend ~170 MB of transient allocation — paid by someone looking at Home, which draws none of it. - The store warms only albums, artists and genres, on idle after first paint: 1.6 MB together, and what made those views instant. - A nav item prefetches on hover and on keyboard focus, which is the ~100 ms before the click that a cold open would otherwise wait. - An invalidation refetches what something had loaded, and nothing else — a scan no longer loads the track list of a library whose Tracks view nobody has opened. - `index.html`'s first-paint `<track-list>` is `view-hidden`, and `index.ts` no longer activates it: it is markup, not a decision about which view the launch lands on, and activating it was what fetched the whole list for a landing on Home. The track list itself loads on view activation, which the shell drives. Measured on 50 000 tracks: backend RSS at rest 543 → 296 MB, peak 571 → 296 MB, Go heap held 361 → 125 MB, JS heap 31.8 → 18 MB, binding bytes at rest 35.9 → 12.1 MB, heap after a browse 36.5 → 22.7 MB. Tracks first open 26 ms; slowest view open 57 ms. Closes #280
59 lines
2.1 KiB
TypeScript
59 lines
2.1 KiB
TypeScript
/**
|
|
* #280: launching the app must not load the track list.
|
|
*
|
|
* Every collection used to be fetched at `DOMContentLoaded` and
|
|
* refetched on every invalidation, whichever view was showing. On a
|
|
* 26 138-track library the track list was 20.5 MB of that, and encoding
|
|
* it cost the backend ~170 MB of transient allocation — for someone
|
|
* looking at Home, which draws none of it. Measured on 50 000 tracks:
|
|
* 543 MB of backend RSS at rest before, 296 MB after.
|
|
*
|
|
* The negative assertion is the point, and it needs its complement: "no
|
|
* `GetTrackTable`" also holds on a build that fetches nothing at all,
|
|
* so the same spec opens Tracks and watches it arrive.
|
|
*/
|
|
import { test, expect, bindingCalls, navigateTo } from '../support/fixtures.js';
|
|
|
|
const TRACKS = 'library.Library.GetTrackTable';
|
|
|
|
/** The seed's default page, which is what a launch lands on. */
|
|
const LANDING_VIEW = 'home';
|
|
|
|
test('launching on Home does not load the track list', async ({ app }) => {
|
|
// Past the store's idle warm-up: "nothing was fetched" has to mean
|
|
// nothing, not "nothing has happened yet".
|
|
await app.waitForTimeout(3_500);
|
|
|
|
const calls = await bindingCalls(app);
|
|
|
|
expect(calls, 'the warm-up for the small collections ran').toContain(
|
|
'library.Library.GetAlbums',
|
|
);
|
|
expect(calls, `the track list was fetched at launch (${LANDING_VIEW})`).not.toContain(
|
|
TRACKS,
|
|
);
|
|
});
|
|
|
|
test('opening Tracks loads it, and only then', async ({ app }) => {
|
|
await app.waitForTimeout(1_000);
|
|
expect(await bindingCalls(app)).not.toContain(TRACKS);
|
|
|
|
await navigateTo(app, 'tracks');
|
|
await expect(app.getByTestId('track-row').first()).toBeVisible();
|
|
|
|
expect(await bindingCalls(app)).toContain(TRACKS);
|
|
});
|
|
|
|
test('a hover on the nav item starts the load before the click', async ({ app }) => {
|
|
await app.waitForTimeout(1_000);
|
|
|
|
const nav = app.getByTestId('nav-tracks');
|
|
|
|
// The pointer entering the item is the whole mechanism; nothing is
|
|
// clicked, so the data must arrive because of the hover alone.
|
|
await nav.hover();
|
|
await expect
|
|
.poll(async () => (await bindingCalls(app)).includes(TRACKS))
|
|
.toBe(true);
|
|
});
|