mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-18 21:16:47 +00:00
test(maps): pin the RTL registration, and correct the precache figure
The guard in rtlText.ts had tests; the two lines that call it did not. setRTLTextPlugin appears at exactly two places in the client and no test touched either, so deleting a call, swapping an argument or dropping the deferred flag left the whole suite green. The engines are mocked because importing them for real builds a worker pool under jsdom, and the plugin URL is mocked so the payload never loads. The precache comment in vite.config.js was three releases stale, and it is the only record in the repo of what a service-worker install costs. A real build says 463 entries / 23,292 KiB, the plugin among them: it matches the js glob, so it is fetched at install by every user rather than on first RTL text as the branch description claims. That trade is the right one, since the alternative is a CDN the production CSP refuses and an unshaped map for anyone offline. Only the description of it was wrong. Measured against a real client build, which no CI workflow performs: none of them runs vite build or check:gl-split, so nothing here was covered before.
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
|
||||
/**
|
||||
* The two lines this fix actually adds. Everything else is a helper: the guard in
|
||||
* rtlText.ts has its own tests, but a guard nobody calls looks exactly like a
|
||||
* working fix from the outside. Deleting either registration, swapping an
|
||||
* argument or dropping the deferred flag left the whole suite green.
|
||||
*
|
||||
* The engines are mocked because importing them for real builds a worker pool
|
||||
* under jsdom, and the plugin URL is mocked so the 133 KB payload never loads.
|
||||
*/
|
||||
const { mapboxSet, maplibreSet } = vi.hoisted(() => ({
|
||||
mapboxSet: vi.fn(),
|
||||
maplibreSet: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('mapbox-gl', () => ({ default: { setRTLTextPlugin: mapboxSet } }));
|
||||
vi.mock('maplibre-gl', () => ({ default: { setRTLTextPlugin: maplibreSet } }));
|
||||
vi.mock('mapbox-gl/dist/mapbox-gl.css', () => ({}));
|
||||
vi.mock('maplibre-gl/dist/maplibre-gl.css', () => ({}));
|
||||
vi.mock('@mapbox/mapbox-gl-rtl-text/dist/mapbox-gl-rtl-text.js?url', () => ({ default: '/assets/rtl-plugin.js' }));
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
mapboxSet.mockReset();
|
||||
maplibreSet.mockReset();
|
||||
});
|
||||
|
||||
describe('the GL engines register the RTL text plugin (#2235)', () => {
|
||||
it('FE-MAP-RTL-001: importing the mapbox engine registers it deferred, with a null callback', async () => {
|
||||
await import('./mapbox');
|
||||
|
||||
expect(mapboxSet).toHaveBeenCalledTimes(1);
|
||||
// Mapbox GL 3 takes (url, callback, deferred). The third argument is what
|
||||
// keeps the payload off the wire until a map paints RTL text; dropping it
|
||||
// would fetch 133 KB for every user on every map.
|
||||
expect(mapboxSet).toHaveBeenCalledWith(expect.stringContaining('rtl'), null, true);
|
||||
});
|
||||
|
||||
it('FE-MAP-RTL-002: importing the maplibre engine registers it lazily', async () => {
|
||||
await import('./maplibre');
|
||||
|
||||
expect(maplibreSet).toHaveBeenCalledTimes(1);
|
||||
// MapLibre 5 takes (url, lazy) and returns a promise, which is the reason
|
||||
// the call stays with each engine instead of going through one wrapper.
|
||||
expect(maplibreSet).toHaveBeenCalledWith(expect.stringContaining('rtl'), true);
|
||||
});
|
||||
|
||||
it('FE-MAP-RTL-003: an engine still loads when its registration throws', async () => {
|
||||
mapboxSet.mockImplementation(() => { throw new Error('already registered'); });
|
||||
|
||||
const engine = await import('./mapbox');
|
||||
|
||||
expect(engine.default).toBeDefined();
|
||||
});
|
||||
|
||||
it('FE-MAP-RTL-004: a rejected registration promise does not surface as an unhandled rejection', async () => {
|
||||
maplibreSet.mockReturnValue(Promise.reject(new Error('already registered')));
|
||||
|
||||
const engine = await import('./maplibre');
|
||||
|
||||
expect(engine.default).toBeDefined();
|
||||
// Give the rejection a tick to go unhandled if nothing caught it.
|
||||
await new Promise((resolve) => setTimeout(resolve, 0));
|
||||
});
|
||||
});
|
||||
@@ -47,8 +47,14 @@ export default defineConfig(({ mode }) => ({
|
||||
// Every route chunk is precached alongside the shell, deliberately: for an
|
||||
// offline-first travel planner a route the user never opened before losing
|
||||
// signal still has to work. The trade is that splitting buys first paint and
|
||||
// not install size — 107 entries / 17,795 KiB before any of it, 220 /
|
||||
// 17,855 KiB now.
|
||||
// not install size: 107 entries / 17,795 KiB before any of it, 463 /
|
||||
// 23,292 KiB now (measured, not estimated).
|
||||
//
|
||||
// Keep this figure honest. #2228 traced PWA boot failures to the browser
|
||||
// evicting this origin's whole bucket, precached shell included, and this
|
||||
// comment is the only record of what the install actually costs. Anything
|
||||
// matching the globs below is fetched at service-worker install by every
|
||||
// user, whether or not they ever reach the code.
|
||||
globPatterns: ['**/*.{js,css,html,svg,png,woff,woff2,ttf}'],
|
||||
// build:analyze drops a treemap next to the app; it must never end up in a
|
||||
// precache manifest if someone ships that build by accident.
|
||||
|
||||
Reference in New Issue
Block a user