149 lines
5.3 KiB
TypeScript
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,
|
|
});
|
|
});
|
|
});
|