import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; const checkForDesktopUpdateMock = vi.hoisted(() => vi.fn()); const installPendingDesktopUpdateMock = vi.hoisted(() => vi.fn()); const isDesktopUpdaterAvailableMock = vi.hoisted(() => vi.fn()); const messageErrorMock = vi.hoisted(() => vi.fn()); const confirmMock = vi.hoisted(() => vi.fn()); vi.mock("../runtime", () => ({ checkForDesktopUpdate: checkForDesktopUpdateMock, installPendingDesktopUpdate: installPendingDesktopUpdateMock, isDesktopUpdaterAvailable: isDesktopUpdaterAvailableMock, })); vi.mock("element-plus", () => ({ ElMessage: { error: messageErrorMock, }, ElMessageBox: { confirm: confirmMock, }, })); const createStorage = (): Storage => { const data = new Map(); return { get length() { return data.size; }, clear: vi.fn(() => data.clear()), getItem: vi.fn((key: string) => data.get(key) ?? null), key: vi.fn((index: number) => Array.from(data.keys())[index] ?? null), removeItem: vi.fn((key: string) => data.delete(key)), setItem: vi.fn((key: string, value: string) => data.set(key, value)), }; }; const update = { version: "0.1.1", currentVersion: "0.1.0", notes: "桌面端稳定化", date: "2026-07-02", }; describe("desktop update manager", () => { beforeEach(() => { vi.useFakeTimers(); vi.setSystemTime(new Date("2026-07-02T00:00:00.000Z")); vi.resetModules(); vi.clearAllMocks(); Object.defineProperty(window, "localStorage", { value: createStorage(), configurable: true, }); isDesktopUpdaterAvailableMock.mockReturnValue(true); checkForDesktopUpdateMock.mockResolvedValue(null); installPendingDesktopUpdateMock.mockResolvedValue(undefined); confirmMock.mockResolvedValue(undefined); }); afterEach(async () => { const { stopDesktopUpdateManager } = await import("./desktopUpdateManager"); stopDesktopUpdateManager(); vi.clearAllTimers(); vi.useRealTimers(); }); it("records up-to-date checks without prompting", async () => { const { checkDesktopUpdateAndPrompt, getDesktopUpdateStatus } = await import("./desktopUpdateManager"); const status = await checkDesktopUpdateAndPrompt({ notifyWhenCurrent: true, promptWhenAvailable: true }); expect(status).toBe("up-to-date"); expect(confirmMock).not.toHaveBeenCalled(); expect(getDesktopUpdateStatus()).toMatchObject({ lastStatus: "up-to-date", pendingUpdate: null, lastError: "", }); }); it("suppresses the same postponed version for 24 hours", async () => { checkForDesktopUpdateMock.mockResolvedValue(update); confirmMock.mockRejectedValueOnce("cancel"); const { checkDesktopUpdateAndPrompt, getDesktopUpdateStatus } = await import("./desktopUpdateManager"); const postponed = await checkDesktopUpdateAndPrompt({ promptWhenAvailable: true }); const postponedStatus = getDesktopUpdateStatus(); const suppressed = await checkDesktopUpdateAndPrompt({ promptWhenAvailable: true }); expect(postponed).toBe("postponed"); expect(suppressed).toBe("suppressed"); expect(confirmMock).toHaveBeenCalledTimes(1); expect(installPendingDesktopUpdateMock).not.toHaveBeenCalled(); expect(postponedStatus.postponedUntil).toBe("2026-07-03T00:00:00.000Z"); expect(getDesktopUpdateStatus()).toMatchObject({ lastStatus: "suppressed", pendingUpdate: update, postponedUntil: "2026-07-03T00:00:00.000Z", }); }); it("keeps a pending update retryable when installation fails", async () => { checkForDesktopUpdateMock.mockResolvedValue(update); installPendingDesktopUpdateMock.mockRejectedValueOnce(new Error("install failed")); const { checkDesktopUpdateAndPrompt, getDesktopUpdateStatus } = await import("./desktopUpdateManager"); const status = await checkDesktopUpdateAndPrompt({ promptWhenAvailable: true }); expect(status).toBe("failed"); expect(messageErrorMock).toHaveBeenCalledWith("桌面端更新安装失败,请稍后重试或联系管理员。"); expect(getDesktopUpdateStatus()).toMatchObject({ installing: false, lastStatus: "failed", lastError: "桌面端更新安装失败", pendingUpdate: update, }); }); it("does not interrupt timed update checks when checking fails", async () => { checkForDesktopUpdateMock.mockRejectedValueOnce(new Error("feed unavailable")); const { checkDesktopUpdateAndPrompt, getDesktopUpdateStatus } = await import("./desktopUpdateManager"); const status = await checkDesktopUpdateAndPrompt(); expect(status).toBe("failed"); expect(messageErrorMock).not.toHaveBeenCalled(); expect(getDesktopUpdateStatus()).toMatchObject({ lastStatus: "failed", lastError: "feed unavailable", }); }); it("reports disabled updater builds explicitly", async () => { isDesktopUpdaterAvailableMock.mockReturnValue(false); const { checkDesktopUpdateAndPrompt, getDesktopUpdateStatus } = await import("./desktopUpdateManager"); const status = await checkDesktopUpdateAndPrompt({ notifyWhenCurrent: true }); expect(status).toBe("disabled"); expect(checkForDesktopUpdateMock).not.toHaveBeenCalled(); expect(getDesktopUpdateStatus()).toMatchObject({ available: false, lastStatus: "disabled", pendingUpdate: null, }); }); });