fix(web): return a cleanup from the preload-error installer
Review feedback: window is shared across vitest tests, so repeated installs accumulated listeners. The installer now returns a remover and the tests detach in afterEach. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
8dc2dcbc13
commit
bd97f3c123
@@ -1,4 +1,4 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { installPreloadErrorReload } from "./reloadOnPreloadError";
|
||||
|
||||
function harness(now: () => number) {
|
||||
@@ -16,9 +16,16 @@ function harness(now: () => number) {
|
||||
}
|
||||
|
||||
describe("installPreloadErrorReload", () => {
|
||||
let cleanups: Array<() => void> = [];
|
||||
|
||||
afterEach(() => {
|
||||
cleanups.forEach((cleanup) => cleanup());
|
||||
cleanups = [];
|
||||
});
|
||||
|
||||
it("reloads when a dynamically imported chunk fails to load", () => {
|
||||
const { reload, deps } = harness(() => 1_000_000);
|
||||
installPreloadErrorReload(deps);
|
||||
cleanups.push(installPreloadErrorReload(deps));
|
||||
|
||||
window.dispatchEvent(new Event("vite:preloadError"));
|
||||
expect(reload).toHaveBeenCalledTimes(1);
|
||||
@@ -28,14 +35,14 @@ describe("installPreloadErrorReload", () => {
|
||||
let clock = 1_000_000;
|
||||
const { reload, deps } = harness(() => clock);
|
||||
|
||||
installPreloadErrorReload(deps);
|
||||
cleanups.push(installPreloadErrorReload(deps));
|
||||
window.dispatchEvent(new Event("vite:preloadError"));
|
||||
expect(reload).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Post-reload page still hits the error seconds later: give up instead
|
||||
// of reload-looping.
|
||||
clock += 5_000;
|
||||
installPreloadErrorReload(deps);
|
||||
cleanups.push(installPreloadErrorReload(deps));
|
||||
window.dispatchEvent(new Event("vite:preloadError"));
|
||||
expect(reload).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
@@ -44,12 +51,12 @@ describe("installPreloadErrorReload", () => {
|
||||
let clock = 1_000_000;
|
||||
const { reload, deps } = harness(() => clock);
|
||||
|
||||
installPreloadErrorReload(deps);
|
||||
cleanups.push(installPreloadErrorReload(deps));
|
||||
window.dispatchEvent(new Event("vite:preloadError"));
|
||||
expect(reload).toHaveBeenCalledTimes(1);
|
||||
|
||||
clock += 10 * 60_000;
|
||||
installPreloadErrorReload(deps);
|
||||
cleanups.push(installPreloadErrorReload(deps));
|
||||
window.dispatchEvent(new Event("vite:preloadError"));
|
||||
expect(reload).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
@@ -15,13 +15,13 @@ interface PreloadErrorReloadDeps {
|
||||
setItem: (key: string, value: string) => void;
|
||||
}
|
||||
|
||||
export function installPreloadErrorReload(deps?: Partial<PreloadErrorReloadDeps>): void {
|
||||
export function installPreloadErrorReload(deps?: Partial<PreloadErrorReloadDeps>): () => void {
|
||||
const reload = deps?.reload ?? (() => window.location.reload());
|
||||
const now = deps?.now ?? Date.now;
|
||||
const getItem = deps?.getItem ?? ((k: string) => sessionStorage.getItem(k));
|
||||
const setItem = deps?.setItem ?? ((k: string, v: string) => sessionStorage.setItem(k, v));
|
||||
|
||||
window.addEventListener("vite:preloadError", (event) => {
|
||||
const handler = (event: Event) => {
|
||||
const lastReloadAt = Number(getItem(GUARD_KEY) ?? 0);
|
||||
if (now() - lastReloadAt < GUARD_WINDOW_MS) {
|
||||
// We already reloaded moments ago and the chunk is still missing;
|
||||
@@ -31,5 +31,8 @@ export function installPreloadErrorReload(deps?: Partial<PreloadErrorReloadDeps>
|
||||
event.preventDefault();
|
||||
setItem(GUARD_KEY, String(now()));
|
||||
reload();
|
||||
});
|
||||
};
|
||||
|
||||
window.addEventListener("vite:preloadError", handler);
|
||||
return () => window.removeEventListener("vite:preloadError", handler);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user