import { describe, expect, it, vi } from "vitest"; import { clearSidebarCallouts, createSidebarCalloutState, dismissSidebarCallout, loadDismissedCalloutKeys, selectActiveSidebarCallout, serializeDismissedCalloutKeys, showSidebarCallout, unregisterSidebarCallout, } from "./sidebar-callout-state"; describe("sidebar callout state", () => { it("shows the highest-priority callout first, then reveals the next when dismissed", () => { let state = createSidebarCalloutState(); state = showSidebarCallout(state, { id: "onboarding", priority: 10, title: "Set up scripts", }).state; state = showSidebarCallout(state, { id: "update", priority: 200, title: "Update available", }).state; expect(selectActiveSidebarCallout(state)?.title).toBe("Update available"); state = dismissSidebarCallout(state, "update").state; expect(selectActiveSidebarCallout(state)?.title).toBe("Set up scripts"); }); it("replaces a callout by id without duplicating the queue item", () => { let state = createSidebarCalloutState(); state = showSidebarCallout(state, { id: "daemon", title: "Old daemon", description: "v1", }).state; state = showSidebarCallout(state, { id: "daemon", title: "New daemon", description: "v2", }).state; expect(state.callouts).toMatchObject([ { id: "daemon", title: "New daemon", description: "v2", }, ]); }); it("unregisters only the registration returned by show", () => { let state = createSidebarCalloutState(); const oldRegistration = showSidebarCallout(state, { id: "update", title: "Old" }); state = oldRegistration.state; state = showSidebarCallout(state, { id: "update", title: "New" }).state; state = unregisterSidebarCallout(state, { id: "update", token: oldRegistration.token }); expect(selectActiveSidebarCallout(state)?.title).toBe("New"); }); it("persists dismissals by dismissal key and hides matching future callouts", () => { const onDismiss = vi.fn(); let state = loadDismissedCalloutKeys(createSidebarCalloutState(), new Set()); state = showSidebarCallout(state, { id: "update", dismissalKey: "desktop-update:available:1.2.3", title: "Update available", onDismiss, }).state; const result = dismissSidebarCallout(state, "update"); state = result.state; expect(result.dismissalKey).toBe("desktop-update:available:1.2.3"); expect(serializeDismissedCalloutKeys(state.dismissedKeys)).toBe( JSON.stringify(["desktop-update:available:1.2.3"]), ); expect(onDismiss).not.toHaveBeenCalled(); result.dismissedCallout?.onDismiss?.(); expect(onDismiss).toHaveBeenCalledOnce(); state = showSidebarCallout(state, { id: "update", dismissalKey: "desktop-update:available:1.2.3", title: "Dismissed update", }).state; expect(selectActiveSidebarCallout(state)).toBeNull(); state = showSidebarCallout(state, { id: "update", dismissalKey: "desktop-update:available:1.2.4", title: "New update", }).state; expect(selectActiveSidebarCallout(state)?.title).toBe("New update"); }); it("waits for dismissal storage before showing dismissible callouts", () => { let state = createSidebarCalloutState(); state = showSidebarCallout(state, { id: "update", dismissalKey: "desktop-update:available:1.2.3", title: "Update available", }).state; expect(selectActiveSidebarCallout(state)).toBeNull(); state = loadDismissedCalloutKeys(state, new Set()); expect(selectActiveSidebarCallout(state)?.title).toBe("Update available"); }); it("clears visible callouts without dropping dismissal state", () => { let state = loadDismissedCalloutKeys(createSidebarCalloutState(), new Set(["dismissed"])); state = showSidebarCallout(state, { id: "visible", title: "Visible" }).state; state = clearSidebarCallouts(state); expect(state.callouts).toEqual([]); expect(state.dismissedKeys).toEqual(new Set(["dismissed"])); }); });