Add restart-to-update banner: waiting SW detected via watchForWaitingUpdate, explicit user opt-in applies SKIP_WAITING + reload (no auto mid-session claim, ARCH §8)
Some checks failed
ci / check (push) Has been cancelled
Some checks failed
ci / check (push) Has been cancelled
This commit is contained in:
parent
be3f61518f
commit
e46b14ff07
6 changed files with 266 additions and 5 deletions
|
|
@ -6,7 +6,11 @@
|
|||
*/
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import { readFileSync, existsSync } from "node:fs";
|
||||
import { registerSW, skipWaitingForUpdate } from "../../src/platform/sw/register.js";
|
||||
import {
|
||||
registerSW,
|
||||
skipWaitingForUpdate,
|
||||
watchForWaitingUpdate,
|
||||
} from "../../src/platform/sw/register.js";
|
||||
|
||||
const SW_PATH = "public/sw.js";
|
||||
|
||||
|
|
@ -148,6 +152,68 @@ describe("SW registration — page bridge", () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe("watchForWaitingUpdate — restart-to-update discovery", () => {
|
||||
function withController(controller: unknown): void {
|
||||
Object.defineProperty(globalThis, "navigator", {
|
||||
value: { serviceWorker: { controller } },
|
||||
writable: true,
|
||||
configurable: true,
|
||||
});
|
||||
}
|
||||
|
||||
it("no controller (first install) → never fires", () => {
|
||||
withController(null);
|
||||
const onWaiting = vi.fn();
|
||||
const listeners: Record<string, () => void> = {};
|
||||
const reg = {
|
||||
waiting: { postMessage: vi.fn() },
|
||||
addEventListener: (ev: string, cb: () => void) => {
|
||||
listeners[ev] = cb;
|
||||
},
|
||||
} as unknown as ServiceWorkerRegistration;
|
||||
watchForWaitingUpdate(reg, onWaiting);
|
||||
expect(onWaiting).not.toHaveBeenCalled();
|
||||
expect(listeners.updatefound).toBeUndefined();
|
||||
});
|
||||
|
||||
it("already-waiting worker fires immediately", () => {
|
||||
withController({ scriptURL: "/sw.js" });
|
||||
const onWaiting = vi.fn();
|
||||
const reg = {
|
||||
waiting: { postMessage: vi.fn() },
|
||||
addEventListener: vi.fn(),
|
||||
} as unknown as ServiceWorkerRegistration;
|
||||
watchForWaitingUpdate(reg, onWaiting);
|
||||
expect(onWaiting).toHaveBeenCalledWith(reg);
|
||||
});
|
||||
|
||||
it("updatefound → installed waiting worker fires", () => {
|
||||
withController({ scriptURL: "/sw.js" });
|
||||
const onWaiting = vi.fn();
|
||||
let foundCb: (() => void) | undefined;
|
||||
const stateCbs: (() => void)[] = [];
|
||||
const installed = {
|
||||
state: "installed",
|
||||
addEventListener: (_ev: string, cb: () => void) => {
|
||||
stateCbs.push(cb);
|
||||
},
|
||||
};
|
||||
const reg = {
|
||||
waiting: null as unknown,
|
||||
installing: installed,
|
||||
addEventListener: (ev: string, cb: () => void) => {
|
||||
if (ev === "updatefound") foundCb = cb;
|
||||
},
|
||||
} as unknown as ServiceWorkerRegistration;
|
||||
watchForWaitingUpdate(reg, onWaiting);
|
||||
expect(onWaiting).not.toHaveBeenCalled();
|
||||
foundCb?.();
|
||||
(reg as { waiting: unknown }).waiting = { postMessage: vi.fn() };
|
||||
for (const cb of stateCbs) cb();
|
||||
expect(onWaiting).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Cache safety — versioning and shell assets", () => {
|
||||
it("vite build injects hashed assets into precache manifest", () => {
|
||||
const viteConfig = readFileSync("vite.config.ts", "utf8");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue