mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-11 22:02:16 +00:00
Without a connection a signed-in page opens the worker's offline page: signed-in pages carry `private, no-store` from the proxy and are never cached, so this is also what an offline launch of the installed app shows. The page now offers a real 64 px retry control, reloads by itself when the connection returns, follows the device's light or dark scheme and keeps clear of the notch and home indicator. The worker's comment on the page cache says plainly that only public pages land in it. The single reload after a stale-shell chunk failure moves into src/lib/pwa/chunk-reload.ts and now runs in the root error boundary too, and it recognises a failed dynamic import as WebKit and Firefox word it. pwa-offline.spec.ts runs in the service-worker project and proves the offline strip, the offline page and the automatic recovery against the real worker.
83 lines
2.7 KiB
TypeScript
83 lines
2.7 KiB
TypeScript
/**
|
|
* Unit tests for the chunk-error auto-reload guard in
|
|
* `src/lib/pwa/chunk-reload.ts` (both error boundaries call it).
|
|
*
|
|
* The guard is keyed on the running shell's build version: one reload
|
|
* attempt per BROKEN SHELL, not one per session. After a successful
|
|
* heal the reloaded page carries the new version and the guard re-arms
|
|
* by construction; the pre-v1.16.8 once-per-session key exhausted on
|
|
* multi-deploy days and stranded the user on the error page.
|
|
*/
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
chunkReloadGuardValue,
|
|
isChunkLoadError,
|
|
shouldAttemptChunkReload,
|
|
} from "@/lib/pwa/chunk-reload";
|
|
|
|
describe("shouldAttemptChunkReload", () => {
|
|
it("reloads when no attempt is recorded", () => {
|
|
expect(shouldAttemptChunkReload(null, "1.16.8")).toBe(true);
|
|
});
|
|
|
|
it("suppresses a second reload for the SAME shell version", () => {
|
|
const stored = chunkReloadGuardValue("1.16.8");
|
|
expect(shouldAttemptChunkReload(stored, "1.16.8")).toBe(false);
|
|
});
|
|
|
|
it("re-arms after a deploy — the reloaded shell carries a NEW version", () => {
|
|
const stored = chunkReloadGuardValue("1.16.8");
|
|
expect(shouldAttemptChunkReload(stored, "1.16.9")).toBe(true);
|
|
});
|
|
|
|
it("treats a legacy timestamp guard value as no attempt", () => {
|
|
// Pre-v1.16.8 sessions stored `String(Date.now())`; it never matches
|
|
// a guard value, so a stranded session heals on its next chunk error.
|
|
expect(shouldAttemptChunkReload("1765400000000", "1.16.8")).toBe(true);
|
|
});
|
|
|
|
it("degrades to once-per-shell-lifetime when no version is injected", () => {
|
|
expect(chunkReloadGuardValue("")).toBe("unversioned");
|
|
expect(shouldAttemptChunkReload(null, "")).toBe(true);
|
|
expect(shouldAttemptChunkReload("unversioned", "")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("isChunkLoadError", () => {
|
|
it.each([
|
|
[{ name: "ChunkLoadError", message: "x" }],
|
|
[{ name: "Error", message: "Loading chunk 123 failed." }],
|
|
[{ name: "Error", message: "Loading CSS chunk 7 failed." }],
|
|
[
|
|
{
|
|
name: "Error",
|
|
message: "Failed to load chunk /_next/static/chunks/a.js",
|
|
},
|
|
],
|
|
[
|
|
{
|
|
name: "TypeError",
|
|
message: "Failed to fetch dynamically imported module: /x.js",
|
|
},
|
|
],
|
|
// WebKit and Firefox word a failed dynamic import differently.
|
|
[{ name: "TypeError", message: "Importing a module script failed." }],
|
|
[
|
|
{
|
|
name: "TypeError",
|
|
message: "error loading dynamically imported module: /x.js",
|
|
},
|
|
],
|
|
])("recognises %j", (err) => {
|
|
expect(isChunkLoadError(err)).toBe(true);
|
|
});
|
|
|
|
it("leaves other errors to the error page", () => {
|
|
expect(
|
|
isChunkLoadError({ name: "TypeError", message: "x is undefined" }),
|
|
).toBe(false);
|
|
});
|
|
});
|