1import { describe, it, expect, beforeEach, vi } from "vitest";234const store: Record<string, unknown> = {};56vi.stubGlobal("chrome", {7 storage: {8 local: {9 get: vi.fn(async (key: string | string[]) => (Array.isArray(key)10 ? Object.fromEntries(key.map((item) => [item, store[item]]))11 : { [key]: store[key] })),12 set: vi.fn(async (entries: Record<string, unknown>) => {13 Object.assign(store, entries);14 }),15 remove: vi.fn(async (key: string | string[]) => {16 for (const item of Array.isArray(key) ? key : [key]) delete store[item];17 }),18 },19 },20});212223const {24 PEACH_VERSION,25 RELEASES,26 importantReleasesSince,27 selectImportantReleases,28 pendingImportantReleases,29 recordInstalledVersion,30 hasReleaseNotes,31 shouldShowWhatsNew,32 markWhatsNewSeen,33} = await import("./release-notes");3435describe("release-notes", () => {36 beforeEach(() => {37 Object.keys(store).forEach((k) => delete store[k]);38 vi.clearAllMocks();39 });4041 it("PEACH_VERSION is a non-empty string", () => {42 expect(typeof PEACH_VERSION).toBe("string");43 expect(PEACH_VERSION.length).toBeGreaterThan(0);44 });4546 it("keeps sync recovery as its own breaking release", () => {47 const syncRecovery = RELEASES.find((release) =>48 release.notes.some((note) => {49 const body = note.body.toLowerCase();50 return body.includes("revoke") && body.includes("pair");51 })52 );53 expect(syncRecovery?.importance).toBe("breaking");54 expect(syncRecovery?.version).toBe("5.2.17");5556 const vaultV4 = RELEASES.find((release) => release.version === "5.1.3");57 expect(vaultV4?.importance).toBe("breaking");58 expect(vaultV4?.notes.some((note) => note.body.toLowerCase().includes("revoke"))).toBe(false);59 });6061 it("shows the sync recovery releases for upgrades from 5.2.15", () => {62 expect(importantReleasesSince("5.2.15").map((release) => release.version)).toEqual(["5.2.46", "5.2.17"]);63 });64 it("combines every important release newer than the installed version", () => {65 expect(importantReleasesSince("5.0.0").map((release) => release.version)).toEqual(["5.2.46", "5.2.17", "5.1.3"]);66 });6768 it("hasReleaseNotes returns true when notes exist", () => {69 expect(hasReleaseNotes()).toBe(true);70 });7172 it("combines several important patches across a skipped upgrade interval", () => {73 const selected = selectImportantReleases([74 { version: "3.0.0", releasedAt: "2026-03-03", importance: "critical", notes: [] },75 { version: "2.1.0", releasedAt: "2026-02-02", notes: [] },76 { version: "2.0.0", releasedAt: "2026-02-01", importance: "breaking", notes: [] },77 { version: "1.5.0", releasedAt: "2026-01-15", importance: "critical", notes: [] },78 ], "1.0.0", "3.0.0");79 expect(selected.map((release) => release.version)).toEqual(["3.0.0", "2.0.0", "1.5.0"]);80 });8182 it("shouldShowWhatsNew returns true when an important release was skipped", async () => {83 store["peach_release_notes_from"] = "5.0.0";84 expect(await shouldShowWhatsNew()).toBe(true);85 });8687 it("does not show the interruptive view after the current breaking release", async () => {88 store["peach_release_notes_from"] = "5.2.46";89 expect(await shouldShowWhatsNew()).toBe(false);90 });9192 it("preserves the earliest upgrade boundary across multiple updates", async () => {93 await recordInstalledVersion({ reason: "update", previousVersion: "5.0.0" } as chrome.runtime.InstalledDetails);94 await recordInstalledVersion({ reason: "update", previousVersion: "5.1.0" } as chrome.runtime.InstalledDetails);95 expect(store["peach_release_notes_from"]).toBe("5.0.0");96 expect((await pendingImportantReleases()).map((release) => release.version)).toEqual(["5.2.46", "5.2.17", "5.1.3"]);97 });9899 it("shouldShowWhatsNew returns false on a fresh install (nothing seen yet)", async () => {100 expect(await shouldShowWhatsNew()).toBe(false);101 });102103 it("markWhatsNewSeen stores the current version and suppresses reshowing", async () => {104 store["peach_release_notes_from"] = "5.0.0";105 expect(await shouldShowWhatsNew()).toBe(true);106 await markWhatsNewSeen();107 expect(await shouldShowWhatsNew()).toBe(false);108 expect(store["peach_release_notes_from"]).toBeUndefined();109 });110});