/** * The chunk-recovery guard. * * The messages below are the real ones each engine throws — the detection is a * string match against three vendors who agree on nothing, so a fixture copied * from the wrong browser would pass while production kept failing. */ import assert from 'node:assert/strict'; import { test } from 'node:test'; import { canRecoverByReloading, isStaleChunkError } from './staleChunk'; /** `sessionStorage` and `location` do not exist under `node:test`. */ const store = new Map(); (globalThis as unknown as { window: unknown }).window = { sessionStorage: { getItem: (k: string) => store.get(k) ?? null, setItem: (k: string, v: string) => void store.set(k, v), }, location: { reload: () => {} }, }; test('recognises what each browser actually throws', () => { // Chrome and Edge — the message from the reported failure. assert.ok( isStaleChunkError( new Error( 'Failed to fetch dynamically imported module: https://app.nearledaily.com/assets/InventoryPage-BAzj-ICF.js', ), ), ); // Firefox. assert.ok(isStaleChunkError(new Error('error loading dynamically imported module: /assets/x.js'))); // Safari. assert.ok(isStaleChunkError(new Error('Importing a module script failed.'))); // Vite's own preload helper, for a split chunk's stylesheet. assert.ok(isStaleChunkError(new Error('Unable to preload CSS for /assets/x.css'))); }); test('does not mistake a real bug for a missing chunk', () => { // The distinction that matters: reloading past one of these would hide a // genuine crash behind an endless refresh. assert.equal(isStaleChunkError(new TypeError("Cannot read properties of undefined (reading 'map')")), false); assert.equal(isStaleChunkError(new Error('Request failed (HTTP 500)')), false); assert.equal(isStaleChunkError(null), false); assert.equal(isStaleChunkError(undefined), false); }); test('reloads once, then refuses so it cannot loop', () => { store.clear(); const error = new Error('Failed to fetch dynamically imported module: /assets/a.js'); assert.equal(canRecoverByReloading(error), true, 'first failure should reload'); // Stamp a reload as having just happened. store.set('nearle:stale-chunk-reload-at', String(Date.now())); assert.equal( canRecoverByReloading(error), false, 'a second failure inside the cooldown must surface, not reload again', ); }); test('recovers again from a later deploy', () => { store.clear(); // A reload well outside the 30s window — a different deploy, hours later. store.set('nearle:stale-chunk-reload-at', String(Date.now() - 60 * 60_000)); assert.equal( canRecoverByReloading(new Error('Failed to fetch dynamically imported module: /assets/b.js')), true, ); }); test('never reloads for an error that is not a missing chunk', () => { store.clear(); assert.equal(canRecoverByReloading(new TypeError('x is not a function')), false); });