| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | import type { DesktopUpdateState } from "@t3tools/contracts"; | 1 | import type { DesktopUpdateState } from "@t3tools/contracts"; |
| 2 | import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; | 2 | import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; |
| 3 | import { AtomRegistry } from "effect/unstable/reactivity"; | 3 | import { AtomRegistry } from "effect/unstable/reactivity"; |
| 4 | import { describe, expect, it, vi } from "vite-plus/test"; | 4 | import { afterEach, describe, expect, it, vi } from "vite-plus/test"; |
| 5 | | 5 | |
| 6 | import { createDesktopUpdateStateAtom } from "./desktopUpdate"; | 6 | import { createDesktopUpdateStateAtom } from "./desktopUpdate"; |
| 7 | | 7 | |
| ... | @@ -22,6 +22,10 @@ const baseState: DesktopUpdateState = { | ... | @@ -22,6 +22,10 @@ const baseState: DesktopUpdateState = { |
| 22 | canRetry: false, | 22 | canRetry: false, |
| 23 | }; | 23 | }; |
| 24 | | 24 | |
| | 25 | afterEach(() => { |
| | 26 | vi.restoreAllMocks(); |
| | 27 | }); |
| | 28 | |
| 25 | describe("desktopUpdateStateAtom", () => { | 29 | describe("desktopUpdateStateAtom", () => { |
| 26 | it("loads once, retains state, and follows desktop update events", async () => { | 30 | it("loads once, retains state, and follows desktop update events", async () => { |
| 27 | let listener: ((state: DesktopUpdateState) => void) | undefined; | 31 | let listener: ((state: DesktopUpdateState) => void) | undefined; |
| ... | @@ -91,8 +95,11 @@ describe("desktopUpdateStateAtom", () => { | ... | @@ -91,8 +95,11 @@ describe("desktopUpdateStateAtom", () => { |
| 91 | | 95 | |
| 92 | it("keeps listening when the initial desktop state read fails", async () => { | 96 | it("keeps listening when the initial desktop state read fails", async () => { |
| 93 | let listener: ((state: DesktopUpdateState) => void) | undefined; | 97 | let listener: ((state: DesktopUpdateState) => void) | undefined; |
| | 98 | const cause = new Error("IPC unavailable"); |
| | 99 | const reportError = vi.spyOn(console, "log").mockImplementation(() => undefined); |
| | 100 | const getUpdateState = vi.fn(async () => Promise.reject(cause)); |
| 94 | const atom = createDesktopUpdateStateAtom(() => ({ | 101 | const atom = createDesktopUpdateStateAtom(() => ({ |
| 95 | getUpdateState: async () => Promise.reject(new Error("IPC unavailable")), | 102 | getUpdateState, |
| 96 | onUpdateState: (nextListener) => { | 103 | onUpdateState: (nextListener) => { |
| 97 | listener = nextListener; | 104 | listener = nextListener; |
| 98 | return () => undefined; | 105 | return () => undefined; |
| ... | @@ -102,6 +109,17 @@ describe("desktopUpdateStateAtom", () => { | ... | @@ -102,6 +109,17 @@ describe("desktopUpdateStateAtom", () => { |
| 102 | registry.mount(atom); | 109 | registry.mount(atom); |
| 103 | | 110 | |
| 104 | await vi.waitFor(() => expect(listener).toBeDefined()); | 111 | await vi.waitFor(() => expect(listener).toBeDefined()); |
| | 112 | await vi.waitFor(() => expect(reportError).toHaveBeenCalledOnce()); |
| | 113 | expect(getUpdateState).toHaveBeenCalledTimes(3); |
| | 114 | const [, errorMessage, errorContext] = reportError.mock.calls[0] ?? []; |
| | 115 | expect(errorMessage).toBe("Failed to read the initial desktop update state after 3 attempts."); |
| | 116 | expect(errorContext).toMatchObject({ |
| | 117 | errorTag: "DesktopUpdateStateReadError", |
| | 118 | attemptCount: 3, |
| | 119 | }); |
| | 120 | expect(errorContext).not.toHaveProperty("error"); |
| | 121 | expect(errorContext).not.toHaveProperty("cause"); |
| | 122 | |
| 105 | listener?.(baseState); | 123 | listener?.(baseState); |
| 106 | await vi.waitFor(() => { | 124 | await vi.waitFor(() => { |
| 107 | expect(AsyncResult.getOrElse(registry.get(atom), () => null)).toEqual(baseState); | 125 | expect(AsyncResult.getOrElse(registry.get(atom), () => null)).toEqual(baseState); |