Files
siftlode/frontend/src/lib/notifHistory.dom.test.ts
T
peter cb8b99e0e7 fix(notifications): keep the history under a key of its own, so an older build never reads its new shape
A build from before the change renders history rows as text; an object there crashed its
Notifications page after a rollback. The old key is read once while the new one does not exist,
and never written.
2026-10-29 23:54:48 +01:00

73 lines
2.5 KiB
TypeScript

// @vitest-environment jsdom
//
// The notice history changed shape (a notice keeps what to say, not the said text). A build from
// before it reads the old key and renders its rows as text, so an object there crashed its
// Notifications page after a rollback. The history now lives under a key of its own: the old one is
// read once, while the new one does not exist yet, and never written.
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
import { msg, raw, sayNotif, type NotifText } from "./notifications";
const OLD_KEY = "siftlode.notifications";
// A row as the build before wrote it: its text already said.
const oldRow = (id: number, message: string) => ({
id,
level: "info",
message,
ts: 1,
read: true,
dismissed: true,
transient: false,
repeat: 1,
requiresInteraction: false,
});
const saidHistory = (key: string) =>
(JSON.parse(localStorage.getItem(key) ?? "[]") as { message: NotifText }[]).map((n) =>
sayNotif(n.message),
);
beforeAll(async () => {
await import("./notifications");
await import("./storage");
}, 60_000);
beforeEach(() => {
localStorage.clear();
sessionStorage.clear();
vi.resetModules(); // the store loads at import: each case needs a fresh one
});
describe("the notice history's own key", { timeout: 30_000 }, () => {
it("carries the old history forward and leaves the old key as the build before wrote it", async () => {
const old = JSON.stringify([oldRow(7, "older")]);
localStorage.setItem("siftlode.owner", "5");
localStorage.setItem(OLD_KEY, old);
const notifications = await import("./notifications");
const { claimLocalData, LS } = await import("./storage");
claimLocalData(5);
notifications.notify({ message: msg("common.copy.done") });
expect(LS.notifHistory).not.toBe(OLD_KEY);
expect(localStorage.getItem(OLD_KEY)).toBe(old);
expect(saidHistory(LS.notifHistory)).toEqual(["older", "Copied to the clipboard"]);
});
it("reads the old key only while the new one does not exist", async () => {
const { LS } = await import("./storage");
localStorage.setItem("siftlode.owner", "5");
localStorage.setItem(OLD_KEY, JSON.stringify([oldRow(7, "older")]));
localStorage.setItem(
LS.notifHistory,
JSON.stringify([{ ...oldRow(8, ""), message: raw("newer") }]),
);
const notifications = await import("./notifications");
const { claimLocalData } = await import("./storage");
claimLocalData(5);
expect(notifications.getNotifications().map((n) => sayNotif(n.message))).toEqual(["newer"]);
});
});