Files
ctms/frontend/src/session/desktopUpdateManager.test.ts
T
2026-07-02 10:04:31 +08:00

149 lines
5.3 KiB
TypeScript

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<string, string>();
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,
});
});
});