release-notes.test.ts

4.6 KB · raw

1import { describe, it, expect, beforeEach, vi } from "vitest";23// Mock chrome.storage.local before importing the module under test.4const 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});2122// Import after globals are stubbed so the module captures the mock.23const {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 the major Sync security repair notice explicit", () => {47    const syncRecovery = RELEASES.find((release) => release.version === "5.2.46");48    const body = syncRecovery?.notes[0]?.body.toLowerCase() ?? "";49    expect(syncRecovery?.importance).toBe("breaking");50    expect(body).toContain("update peach on every device");51    expect(body).toContain("open sync settings");52    expect(body).toContain("master password");53    expect(body).toContain("reset sync");54    expect(body).toContain("pair or re-pair every device");55    expect(body).toContain("revocation");56    expect(body).toContain("vault and recovery words are unchanged");57    expect(body).toContain("sorry");5859    const vaultV4 = RELEASES.find((release) => release.version === "5.1.3");60    expect(vaultV4?.importance).toBe("breaking");61    expect(vaultV4?.notes.some((note) => note.body.toLowerCase().includes("revoke"))).toBe(false);62  });63  it("shows the sync recovery releases for upgrades from 5.2.15", () => {64    expect(importantReleasesSince("5.2.15").map((release) => release.version)).toEqual(["5.2.46", "5.2.17"]);65  });66  it("combines every important release newer than the installed version", () => {67    expect(importantReleasesSince("5.0.0").map((release) => release.version)).toEqual(["5.2.46", "5.2.17", "5.1.3"]);68  });697071  it("hasReleaseNotes returns true when notes exist", () => {72    expect(hasReleaseNotes()).toBe(true);73  });7475  it("combines several important patches across a skipped upgrade interval", () => {76    const selected = selectImportantReleases([77      { version: "3.0.0", releasedAt: "2026-03-03", importance: "critical", notes: [] },78      { version: "2.1.0", releasedAt: "2026-02-02", notes: [] },79      { version: "2.0.0", releasedAt: "2026-02-01", importance: "breaking", notes: [] },80      { version: "1.5.0", releasedAt: "2026-01-15", importance: "critical", notes: [] },81    ], "1.0.0", "3.0.0");82    expect(selected.map((release) => release.version)).toEqual(["3.0.0", "2.0.0", "1.5.0"]);83  });8485  it("shouldShowWhatsNew returns true when an important release was skipped", async () => {86    store["peach_release_notes_from"] = "5.0.0";87    expect(await shouldShowWhatsNew()).toBe(true);88  });8990  it("does not show the interruptive view after the current breaking release", async () => {91    store["peach_release_notes_from"] = "5.2.46";92    expect(await shouldShowWhatsNew()).toBe(false);93  });9495  it("preserves the earliest upgrade boundary across multiple updates", async () => {96    await recordInstalledVersion({ reason: "update", previousVersion: "5.0.0" } as chrome.runtime.InstalledDetails);97    await recordInstalledVersion({ reason: "update", previousVersion: "5.1.0" } as chrome.runtime.InstalledDetails);98    expect(store["peach_release_notes_from"]).toBe("5.0.0");99    expect((await pendingImportantReleases()).map((release) => release.version)).toEqual(["5.2.46", "5.2.17", "5.1.3"]);100  });101102  it("shouldShowWhatsNew returns false on a fresh install (nothing seen yet)", async () => {103    expect(await shouldShowWhatsNew()).toBe(false);104  });105106  it("markWhatsNewSeen stores the current version and suppresses reshowing", async () => {107    store["peach_release_notes_from"] = "5.0.0";108    expect(await shouldShowWhatsNew()).toBe(true);109    await markWhatsNewSeen();110    expect(await shouldShowWhatsNew()).toBe(false);111    expect(store["peach_release_notes_from"]).toBeUndefined();112  });113});