Files
WRNexusJS/packages/pwa/test/service-worker.test.ts
T

73 lines
2.6 KiB
TypeScript

import { expect, test } from "bun:test";
import { generateServiceWorker } from "../src/index.ts";
function worker(fetcher: (r: any) => Promise<Response>, failStore = false) {
const listeners: Record<string, (event: any) => void> = {};
const saved: string[] = [];
const cache = {
match: async () => undefined,
put: async (r: any) => {
if (failStore) throw Error("quota");
saved.push(r.url);
},
};
const self = {
location: { origin: "https://account.example.test" },
addEventListener: (name: string, fn: (event: any) => void) => (listeners[name] = fn),
skipWaiting() {},
clients: { claim: async () => {} },
};
new Function("self", "caches", "fetch", generateServiceWorker())(
self,
{ open: async () => cache, keys: async () => [], delete: async () => true },
fetcher,
);
return {
saved,
async fetch(path: string, mode = "cors") {
let result: Promise<Response> | undefined;
const req = new Request(new URL(path, self.location.origin));
Object.defineProperty(req, "mode", { value: mode });
listeners.fetch({
request: req,
respondWith: (r: Promise<Response>) => (result = r),
waitUntil: () => {},
});
return result;
},
};
}
test("handoffs and API calls bypass service worker interception", async () => {
const w = worker(async () => {
throw Error("must not intercept");
});
expect(await w.fetch("/organization-add?sso_code=one-time")).toBeUndefined();
expect(await w.fetch("/api/account")).toBeUndefined();
});
test("private responses and navigations never enter cache", async () => {
const w = worker(
async () => new Response("private", { headers: { "cache-control": "private, no-store" } }),
);
expect(await (await w.fetch("/profile"))?.text()).toBe("private");
expect(w.saved).toEqual([]);
const nav = worker(async () => new Response("personal page"));
await nav.fetch("/profile", "navigate");
expect(nav.saved).toEqual([]);
});
test("cache quota errors do not turn successful requests into network errors", async () => {
const w = worker(async () => new Response("asset"), true);
expect(await (await w.fetch("/app.js"))?.text()).toBe("asset");
});
test("offline without a cached fallback always returns a Response", async () => {
const w = worker(async () => {
throw Error("offline");
});
expect((await w.fetch("/profile", "navigate"))?.status).toBe(503);
});
test("public static assets remain cacheable", async () => {
const w = worker(
async () => new Response("asset", { headers: { "cache-control": "public, max-age=3600" } }),
);
await w.fetch("/app.js");
expect(w.saved).toHaveLength(1);
});