128 lines
4.4 KiB
JavaScript
128 lines
4.4 KiB
JavaScript
// @vitest-environment jsdom
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
__resetFrontendMaintenanceForTests,
|
|
forceFrontendUpdateAndClearLocal,
|
|
invalidateFrontendCachesAfterError,
|
|
} from "@/services/frontendMaintenance.js";
|
|
|
|
const installBrowserMaintenanceMocks = ({ indexedDatabaseNames = [] } = {}) => {
|
|
const deletedCaches = [];
|
|
const deletedIndexedDatabases = [];
|
|
const update = vi.fn(async () => true);
|
|
const unregister = vi.fn(async () => true);
|
|
const getRegistrations = vi.fn(async () => [{ update, unregister }]);
|
|
const caches = {
|
|
keys: vi.fn(async () => ["pleno-api-cache", "pleno-website-cache"]),
|
|
delete: vi.fn(async (cacheName) => {
|
|
deletedCaches.push(cacheName);
|
|
return true;
|
|
}),
|
|
};
|
|
const indexedDB = {
|
|
databases: vi.fn(async () => indexedDatabaseNames.map((name) => ({ name }))),
|
|
deleteDatabase: vi.fn((databaseName) => {
|
|
deletedIndexedDatabases.push(databaseName);
|
|
const request = {};
|
|
queueMicrotask(() => request.onsuccess?.());
|
|
return request;
|
|
}),
|
|
};
|
|
|
|
Object.defineProperty(window, "caches", {
|
|
configurable: true,
|
|
value: caches,
|
|
});
|
|
Object.defineProperty(navigator, "serviceWorker", {
|
|
configurable: true,
|
|
value: { getRegistrations },
|
|
});
|
|
Object.defineProperty(window, "indexedDB", {
|
|
configurable: true,
|
|
value: indexedDB,
|
|
});
|
|
|
|
return { caches, deletedCaches, deletedIndexedDatabases, getRegistrations, indexedDB, unregister, update };
|
|
};
|
|
|
|
describe("frontend maintenance", () => {
|
|
beforeEach(() => {
|
|
__resetFrontendMaintenanceForTests();
|
|
});
|
|
|
|
afterEach(() => {
|
|
__resetFrontendMaintenanceForTests();
|
|
vi.restoreAllMocks();
|
|
Reflect.deleteProperty(window, "caches");
|
|
Reflect.deleteProperty(window, "indexedDB");
|
|
Reflect.deleteProperty(navigator, "serviceWorker");
|
|
});
|
|
|
|
it("invalidates browser caches and asks service workers to update after frontend errors", async () => {
|
|
const mocks = installBrowserMaintenanceMocks();
|
|
|
|
const result = await invalidateFrontendCachesAfterError({ now: 1000 });
|
|
|
|
expect(result).toEqual({
|
|
skipped: false,
|
|
cacheNames: ["pleno-api-cache", "pleno-website-cache"],
|
|
serviceWorkerRegistrations: 1,
|
|
});
|
|
expect(mocks.deletedCaches).toEqual(["pleno-api-cache", "pleno-website-cache"]);
|
|
expect(mocks.update).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("throttles repeated error cache invalidation", async () => {
|
|
const mocks = installBrowserMaintenanceMocks();
|
|
|
|
await invalidateFrontendCachesAfterError({ now: 1000 });
|
|
const result = await invalidateFrontendCachesAfterError({ now: 1500 });
|
|
|
|
expect(result.skipped).toBe(true);
|
|
expect(mocks.caches.keys).toHaveBeenCalledTimes(1);
|
|
expect(mocks.update).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("clears all local state, unregisters service workers, clears caches, deletes IndexedDB, and reloads", async () => {
|
|
const mocks = installBrowserMaintenanceMocks({
|
|
indexedDatabaseNames: ["workbox-expiration", "truckwash-offline"],
|
|
});
|
|
const reload = vi.fn();
|
|
localStorage.setItem("token", "secret-token");
|
|
localStorage.setItem("draft", "local-value");
|
|
sessionStorage.setItem("draft", "value");
|
|
|
|
const result = await forceFrontendUpdateAndClearLocal({ reload });
|
|
|
|
expect(result.cacheNames).toEqual(["pleno-api-cache", "pleno-website-cache"]);
|
|
expect(result.indexedDatabaseNames).toEqual(["workbox-expiration", "truckwash-offline"]);
|
|
expect(mocks.unregister).toHaveBeenCalledTimes(1);
|
|
expect(mocks.deletedCaches).toEqual(["pleno-api-cache", "pleno-website-cache"]);
|
|
expect(mocks.deletedIndexedDatabases).toEqual(["workbox-expiration", "truckwash-offline"]);
|
|
expect(localStorage.getItem("token")).toBeNull();
|
|
expect(localStorage.getItem("draft")).toBeNull();
|
|
expect(sessionStorage.getItem("draft")).toBeNull();
|
|
expect(reload).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("falls back to known IndexedDB names when database enumeration is unavailable", async () => {
|
|
const mocks = installBrowserMaintenanceMocks();
|
|
delete mocks.indexedDB.databases;
|
|
|
|
const result = await forceFrontendUpdateAndClearLocal({ reload: vi.fn() });
|
|
|
|
expect(result.indexedDatabaseNames).toEqual([
|
|
"workbox-expiration",
|
|
"workbox-background-sync",
|
|
"pleno",
|
|
"truckwash",
|
|
]);
|
|
expect(mocks.deletedIndexedDatabases).toEqual([
|
|
"workbox-expiration",
|
|
"workbox-background-sync",
|
|
"pleno",
|
|
"truckwash",
|
|
]);
|
|
});
|
|
});
|