| 1 | import { |
| 2 | AuthOrchestrationOperateScope, |
| 3 | EnvironmentAuthorizationError, |
| 4 | EnvironmentId, |
| 5 | } from "@t3tools/contracts"; |
| 6 | import * as Cause from "effect/Cause"; |
| 7 | import { AsyncResult } from "effect/reactivity"; |
| 8 | import { assert, beforeEach, expect, it, vi } from "vite-plus/test"; |
| 9 | |
| 10 | const state = vi.hoisted(() => ({ |
| 11 | allowed: new Set<string>(), |
| 12 | run: vi.fn(), |
| 13 | getPreference: vi.fn(), |
| 14 | setPreference: vi.fn(), |
| 15 | })); |
| 16 | |
| 17 | vi.mock("react", () => ({ useCallback: (callback: unknown) => callback })); |
| 18 | vi.mock("./state/shell", () => ({ shellEnvironment: { openInEditor: "openInEditor" } })); |
| 19 | vi.mock("./state/use-atom-command", () => ({ useAtomCommand: () => state.run })); |
| 20 | vi.mock("./state/session", () => ({ |
| 21 | readEnvironmentScope: (environmentId: string, scope: string) => |
| 22 | scope === AuthOrchestrationOperateScope && state.allowed.has(environmentId), |
| 23 | })); |
| 24 | vi.mock("./hooks/useLocalStorage", () => ({ |
| 25 | getLocalStorageItem: state.getPreference, |
| 26 | setLocalStorageItem: state.setPreference, |
| 27 | useLocalStorage: vi.fn(), |
| 28 | })); |
| 29 | |
| 30 | import { useOpenInPreferredEditor } from "./editorPreferences"; |
| 31 | |
| 32 | const primary = EnvironmentId.make("primary"); |
| 33 | const secondary = EnvironmentId.make("secondary"); |
| 34 | |
| 35 | beforeEach(() => { |
| 36 | state.allowed.clear(); |
| 37 | state.run.mockReset().mockResolvedValue(AsyncResult.success(undefined)); |
| 38 | state.getPreference.mockReset().mockReturnValue(null); |
| 39 | state.setPreference.mockReset(); |
| 40 | }); |
| 41 | |
| 42 | it("does not use another environment's grant or change preferences when denied", async () => { |
| 43 | state.allowed.add(primary); |
| 44 | const open = useOpenInPreferredEditor(secondary, ["vscode"]); |
| 45 | const result = await open("/work/readme.md"); |
| 46 | assert(result._tag === "Failure"); |
| 47 | const error = Cause.squash(result.cause); |
| 48 | expect(error).toBeInstanceOf(EnvironmentAuthorizationError); |
| 49 | expect(error).toMatchObject({ requiredScope: AuthOrchestrationOperateScope }); |
| 50 | expect(state.run).not.toHaveBeenCalled(); |
| 51 | expect(state.getPreference).not.toHaveBeenCalled(); |
| 52 | expect(state.setPreference).not.toHaveBeenCalled(); |
| 53 | }); |
| 54 | |
| 55 | it("checks revocation and regrant when a retained editor callback is invoked", async () => { |
| 56 | state.allowed.add(secondary); |
| 57 | const open = useOpenInPreferredEditor(secondary, ["vscode"]); |
| 58 | state.allowed.delete(secondary); |
| 59 | expect((await open("/work/readme.md:5"))._tag).toBe("Failure"); |
| 60 | expect(state.run).not.toHaveBeenCalled(); |
| 61 | |
| 62 | state.allowed.add(secondary); |
| 63 | expect(await open("/work/readme.md:5")).toMatchObject({ _tag: "Success", value: "vscode" }); |
| 64 | expect(state.run).toHaveBeenCalledExactlyOnceWith({ |
| 65 | environmentId: secondary, |
| 66 | input: { cwd: "/work/readme.md:5", editor: "vscode" }, |
| 67 | }); |
| 68 | expect(state.setPreference).toHaveBeenCalledTimes(1); |
| 69 | }); |
| 70 | |
| 71 | it("keeps the stored available editor for an authorized launch", async () => { |
| 72 | state.allowed.add(secondary); |
| 73 | state.getPreference.mockReturnValue("cursor"); |
| 74 | const open = useOpenInPreferredEditor(secondary, ["vscode", "cursor"]); |
| 75 | expect(await open("/work")).toMatchObject({ _tag: "Success", value: "cursor" }); |
| 76 | expect(state.run).toHaveBeenCalledExactlyOnceWith({ |
| 77 | environmentId: secondary, |
| 78 | input: { cwd: "/work", editor: "cursor" }, |
| 79 | }); |
| 80 | expect(state.setPreference).not.toHaveBeenCalled(); |
| 81 | }); |
| 82 | |
| 83 | it("returns the existing failures for a missing environment or editor", async () => { |
| 84 | state.allowed.add(secondary); |
| 85 | expect((await useOpenInPreferredEditor(null, ["vscode"])("/work"))._tag).toBe("Failure"); |
| 86 | expect((await useOpenInPreferredEditor(secondary, [])("/work"))._tag).toBe("Failure"); |
| 87 | expect(state.run).not.toHaveBeenCalled(); |
| 88 | }); |