rastrillo / pwa Public

Clone
git clone https://amadan.net/rastrillo/pwa

Plain git — no account needed to clone.

Download

Download this file

1import { readFileSync } from "node:fs";
2import vm from "node:vm";
3import test from "node:test";
4import assert from "node:assert/strict";
5
6const source = readFileSync(new URL("../js/worker.js", import.meta.url), "utf8");
7function worker(fetch = async () => { throw new Error("offline"); }) {
8 const listeners = {};
9 let activations = 0;
10 const self = {
11 registration: { scope: "https://app.example/app/" }, fetch,
12 addEventListener: (name, fn) => { listeners[name] = fn; },
13 skipWaiting: async () => { activations++; },
14 };
15 vm.runInNewContext(source, { self, URL, Response });
16 self.RastrilloPWA.install();
17 return { listeners, self, activations: () => activations };
18}
19function navigate(w, request = {}) {
20 let promise;
21 w.listeners.fetch({
22 request: { url: "https://app.example/app/inbox", method: "GET", mode: "navigate", ...request },
23 respondWith: (p) => { promise = p; },
24 });
25 return promise;
26}
27
28test("offline navigation returns a public, non-cacheable fallback", async () => {
29 const response = await navigate(worker());
30 assert.equal(response.status, 503);
31 assert.equal(response.headers.get("cache-control"), "no-store");
32 assert.match(await response.text(), /Connect to the internet/);
33});
34
35test("HTTP authentication and server errors remain visible", async () => {
36 for (const status of [401, 403, 500]) {
37 const w = worker(async (_request, options) => {
38 assert.equal(options.cache, "no-store");
39 return new Response("server response", { status });
40 });
41 const response = await navigate(w);
42 assert.equal(response.status, status);
43 assert.equal(await response.text(), "server response");
44 }
45});
46
47test("API calls, writes, external and out-of-scope navigations are untouched", () => {
48 const w = worker(() => { assert.fail("must not fetch"); });
49 for (const request of [
50 { mode: "cors" }, { method: "POST" }, { url: "https://other.example/app/" },
51 { url: "https://app.example/another-app/" },
52 ]) assert.equal(navigate(w, request), undefined);
53});
54
55test("activation requires an explicit message from a controlled-scope page", async () => {
56 const w = worker();
57 assert.equal(w.listeners.install, undefined);
58 assert.equal(w.listeners.activate, undefined);
59 const send = async (url, type = "rastrillo:pwa:activate") => {
60 let pending;
61 w.listeners.message({ data: { type }, source: { url }, waitUntil: (p) => { pending = p; } });
62 await pending;
63 };
64 await send("https://elsewhere.example/app/");
65 await send("https://app.example/other/");
66 await send("https://app.example/app/", "unrelated");
67 assert.equal(w.activations(), 0);
68 await send("https://app.example/app/");
69 assert.equal(w.activations(), 1);
70 assert.throws(() => w.self.RastrilloPWA.install(), /once/);
71});
72