Files
TREK/client/src/native/offlineShell.test.ts
T
Maurice e3541535e2 fix(native): first visits no longer wait behind the offline warm-up, day rail follows the swipe
The iOS app pulls every file of a build into WebKit's cache so it can start
offline. It did so four at a time at normal priority, for all 1,900 files of
every new build including all 27 translations, and a page opened meanwhile
queued behind it: right after a server update, a first visit to Vacay took
five to seven seconds. The warm-up now runs two at a time at low priority,
takes only the user's language and English, and walks a build once.

The day rail in the trip header lit the new day only after the pager's snap
had landed and the plan had re-rendered, a second or two after the swipe.
The pager now reports the day a swipe heads for as soon as its page is the
nearer one, and the rail and its pill move with the finger; the plan still
switches when the snap lands.
2026-11-06 15:37:55 +01:00

103 lines
3.8 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
import { keepOfflineShellFresh, manifestFiles } from './offlineShell';
import { TrekShell } from './trekShell';
vi.mock('./trekShell', () => ({ TrekShell: { refreshOfflineCopy: vi.fn() } }));
const manifest = {
'index.html': {
file: 'assets/index-a1b2c3d4.js',
css: ['assets/index-e5f6a7b8.css'],
assets: ['assets/poppins-11223344.woff2'],
},
'src/pages/AtlasPage.tsx': { file: 'assets/AtlasPage-99887766.js', css: ['assets/index-e5f6a7b8.css'] },
_shared: { file: 'assets/shared-55443322.js', assets: 'not-a-list' },
broken: null,
};
beforeEach(() => {
vi.mocked(TrekShell.refreshOfflineCopy).mockReset().mockResolvedValue({ saved: true });
window.localStorage.clear();
});
describe('manifestFiles', () => {
it('collects every chunk, stylesheet and asset once', () => {
expect(manifestFiles(manifest).sort()).toEqual([
'assets/AtlasPage-99887766.js',
'assets/index-a1b2c3d4.js',
'assets/index-e5f6a7b8.css',
'assets/poppins-11223344.woff2',
'assets/shared-55443322.js',
]);
});
it('returns nothing for something that is not a manifest', () => {
expect(manifestFiles(null)).toEqual([]);
expect(manifestFiles('x')).toEqual([]);
});
});
describe('keepOfflineShellFresh', () => {
it('refreshes the stored start page and fetches every file of the build', async () => {
const fetched: string[] = [];
const fetchImpl = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
if (url === '/asset-manifest.json') {
expect(init).toEqual({ cache: 'no-store' });
return new Response(JSON.stringify(manifest));
}
fetched.push(url);
if (url.includes('shared')) throw new Error('offline for a moment');
return new Response('x');
}) as unknown as typeof fetch;
await keepOfflineShellFresh(fetchImpl);
expect(TrekShell.refreshOfflineCopy).toHaveBeenCalled();
expect(fetched.sort()).toEqual([
'/assets/AtlasPage-99887766.js',
'/assets/index-a1b2c3d4.js',
'/assets/index-e5f6a7b8.css',
'/assets/poppins-11223344.woff2',
'/assets/shared-55443322.js',
]);
});
it('stops after the start page when the build has no manifest', async () => {
const fetchImpl = vi.fn(async () => new Response('', { status: 404 })) as unknown as typeof fetch;
await keepOfflineShellFresh(fetchImpl);
expect(fetchImpl).toHaveBeenCalledTimes(1);
});
it('fetches at low priority, only the own language, and once per build', async () => {
const withLocales = {
...manifest,
'../shared/dist/i18n/de/index.mjs': { file: 'assets/de-aaaa1111.js' },
'../shared/dist/i18n/fr/index.mjs': { file: 'assets/fr-bbbb2222.js' },
'../shared/dist/i18n/en/index.mjs': { file: 'assets/en-cccc3333.js' },
};
const inits: (RequestInit | undefined)[] = [];
const fetched: string[] = [];
const fetchImpl = vi.fn(async (input: RequestInfo | URL, init?: RequestInit) => {
const url = String(input);
if (url === '/asset-manifest.json') return new Response(JSON.stringify(withLocales));
fetched.push(url);
inits.push(init);
return new Response('x');
}) as unknown as typeof fetch;
await keepOfflineShellFresh(fetchImpl, 'de');
expect(fetched).toContain('/assets/de-aaaa1111.js');
expect(fetched).toContain('/assets/en-cccc3333.js');
expect(fetched).not.toContain('/assets/fr-bbbb2222.js');
expect(inits.every((i) => (i as { priority?: string } | undefined)?.priority === 'low')).toBe(true);
const before = fetched.length;
await keepOfflineShellFresh(fetchImpl, 'de');
expect(fetched.length).toBe(before);
await keepOfflineShellFresh(fetchImpl, 'fr');
expect(fetched).toContain('/assets/fr-bbbb2222.js');
});
});