1import type { DesktopUpdateActionResult, DesktopUpdateState } from "@t3tools/contracts";
2import { afterEach, beforeEach, expect, it, vi } from "vite-plus/test";
3
4const mocks = vi.hoisted(() => ({ prepare: vi.fn(() => true), reload: vi.fn(), toast: vi.fn() }));
5vi.mock("~/uiRefresh", () => ({ prepareUiRefresh: mocks.prepare }));
6vi.mock("./ui/toast", () => ({
7 toastManager: { add: mocks.toast },
8 stackedThreadToast: (value: unknown) => value,
9}));
10vi.mock("./desktopUpdate.toast", () => ({ showDesktopUpdateDownloadedToast: mocks.toast }));
11
12import { downloadLocalUpdate, installLocalUpdate } from "./desktopUpdate.actions";
13
14const state: DesktopUpdateState = {
15 enabled: true,
16 status: "downloaded",
17 channel: "latest",
18 currentVersion: "0.0.1",
19 hostArch: "arm64",
20 appArch: "arm64",
21 runningUnderArm64Translation: false,
22 availableVersion: "0.0.2",
23 downloadedVersion: "0.0.2",
24 installKind: "ui",
25 releaseNotes: [],
26 omittedReleaseCount: 0,
27 downloadPercent: 100,
28 checkedAt: null,
29 message: null,
30 errorContext: null,
31 canRetry: true,
32};
33const result: DesktopUpdateActionResult = {
34 accepted: true,
35 completed: true,
36 state: { ...state, status: "idle", uiVersion: "0.0.2" },
37};
38
39beforeEach(() => {
40 mocks.prepare.mockReset().mockReturnValue(true);
41 mocks.reload.mockReset();
42 mocks.toast.mockReset();
43 vi.stubGlobal("window", { location: { reload: mocks.reload } });
44});
45afterEach(() => vi.unstubAllGlobals());
46
47it("downloads a UI release without activating it", async () => {
48 const bridge = {
49 openExternal: vi.fn(async () => true),
50 downloadUpdate: vi.fn(async () => ({ ...result, state })),
51 };
52 expect(await downloadLocalUpdate(bridge, { notifyDownloaded: false })).toBe(true);
53 expect(mocks.reload).not.toHaveBeenCalled();
54 expect(mocks.prepare).not.toHaveBeenCalled();
55});
56
57it("activates an automatic update only when state can roundtrip", async () => {
58 const bridge = {
59 openExternal: vi.fn(async () => true),
60 installUpdate: vi.fn(async () => result),
61 };
62 mocks.prepare.mockReturnValue(false);
63 expect(await installLocalUpdate(bridge, undefined, { automatic: true })).toBe(false);
64 expect(bridge.installUpdate).not.toHaveBeenCalled();
65 mocks.prepare.mockReturnValue(true);
66 expect(await installLocalUpdate(bridge, undefined, { automatic: true })).toBe(true);
67 expect(mocks.reload).toHaveBeenCalledOnce();
68});
69
70it("keeps the current UI if unsafe state appears while activation is in progress", async () => {
71 const bridge = {
72 openExternal: vi.fn(async () => true),
73 installUpdate: vi.fn(async () => {
74 mocks.prepare.mockReturnValue(false);
75 return result;
76 }),
77 };
78 expect(await installLocalUpdate(bridge, undefined, { automatic: true })).toBe(true);
79 expect(bridge.installUpdate).toHaveBeenCalledOnce();
80 expect(mocks.reload).not.toHaveBeenCalled();
81});