import { describe, it, expect, beforeEach, vi } from "vitest"; // Mock chrome.storage.local before importing the module under test. const store: Record = {}; vi.stubGlobal("chrome", { storage: { local: { get: vi.fn(async (key: string | string[]) => (Array.isArray(key) ? Object.fromEntries(key.map((item) => [item, store[item]])) : { [key]: store[key] })), set: vi.fn(async (entries: Record) => { Object.assign(store, entries); }), remove: vi.fn(async (key: string | string[]) => { for (const item of Array.isArray(key) ? key : [key]) delete store[item]; }), }, }, }); // Import after globals are stubbed so the module captures the mock. const { PEACH_VERSION, RELEASES, importantReleasesSince, selectImportantReleases, pendingImportantReleases, recordInstalledVersion, hasReleaseNotes, shouldShowWhatsNew, markWhatsNewSeen, } = await import("./release-notes"); describe("release-notes", () => { beforeEach(() => { Object.keys(store).forEach((k) => delete store[k]); vi.clearAllMocks(); }); it("PEACH_VERSION is a non-empty string", () => { expect(typeof PEACH_VERSION).toBe("string"); expect(PEACH_VERSION.length).toBeGreaterThan(0); }); it("keeps sync recovery as its own breaking release", () => { const syncRecovery = RELEASES.find((release) => release.notes.some((note) => { const body = note.body.toLowerCase(); return body.includes("revoke") && body.includes("pair"); }) ); expect(syncRecovery?.importance).toBe("breaking"); expect(syncRecovery?.version).toBe("5.2.17"); const vaultV4 = RELEASES.find((release) => release.version === "5.1.3"); expect(vaultV4?.importance).toBe("breaking"); expect(vaultV4?.notes.some((note) => note.body.toLowerCase().includes("revoke"))).toBe(false); }); it("shows the sync recovery release for upgrades from 5.2.15", () => { expect(importantReleasesSince("5.2.15").map((release) => release.version)).toEqual(["5.2.17"]); }); it("hasReleaseNotes returns true when notes exist", () => { expect(hasReleaseNotes()).toBe(true); }); it("combines every important release newer than the installed version", () => { expect(importantReleasesSince("5.0.0").map((release) => release.version)).toEqual(["5.2.17", "5.1.3"]); }); it("combines several important patches across a skipped upgrade interval", () => { const selected = selectImportantReleases([ { version: "3.0.0", releasedAt: "2026-03-03", importance: "critical", notes: [] }, { version: "2.1.0", releasedAt: "2026-02-02", notes: [] }, { version: "2.0.0", releasedAt: "2026-02-01", importance: "breaking", notes: [] }, { version: "1.5.0", releasedAt: "2026-01-15", importance: "critical", notes: [] }, ], "1.0.0", "3.0.0"); expect(selected.map((release) => release.version)).toEqual(["3.0.0", "2.0.0", "1.5.0"]); }); it("shouldShowWhatsNew returns true when an important release was skipped", async () => { store["peach_release_notes_from"] = "5.0.0"; expect(await shouldShowWhatsNew()).toBe(true); }); it("does not show the interruptive view after the current breaking release", async () => { store["peach_release_notes_from"] = "5.2.17"; expect(await shouldShowWhatsNew()).toBe(false); }); it("preserves the earliest upgrade boundary across multiple updates", async () => { await recordInstalledVersion({ reason: "update", previousVersion: "5.0.0" } as chrome.runtime.InstalledDetails); await recordInstalledVersion({ reason: "update", previousVersion: "5.1.0" } as chrome.runtime.InstalledDetails); expect(store["peach_release_notes_from"]).toBe("5.0.0"); expect((await pendingImportantReleases()).map((release) => release.version)).toEqual(["5.2.17", "5.1.3"]); }); it("shouldShowWhatsNew returns false on a fresh install (nothing seen yet)", async () => { expect(await shouldShowWhatsNew()).toBe(false); }); it("markWhatsNewSeen stores the current version and suppresses reshowing", async () => { store["peach_release_notes_from"] = "5.0.0"; expect(await shouldShowWhatsNew()).toBe(true); await markWhatsNewSeen(); expect(await shouldShowWhatsNew()).toBe(false); expect(store["peach_release_notes_from"]).toBeUndefined(); }); });