| 1 | import { EnvironmentId, ThreadId } from "@t3tools/contracts"; |
| 2 | import { AsyncResult } from "effect/reactivity"; |
| 3 | import { act, type ComponentProps, type ReactNode } from "react"; |
| 4 | import { create, type ReactTestRenderer } from "react-test-renderer"; |
| 5 | import { expect, it, vi } from "vite-plus/test"; |
| 6 | |
| 7 | const mint = vi.hoisted(() => vi.fn()); |
| 8 | vi.mock("@effect/atom-react", () => ({ useAtomValue: () => null })); |
| 9 | vi.mock("../hooks/useTheme", () => ({ useTheme: () => ({ resolvedTheme: "dark" }) })); |
| 10 | vi.mock("../hooks/useFileMetadata", () => ({ useFileMetadata: () => undefined })); |
| 11 | vi.mock("../hooks/useSettings", async (importOriginal) => { |
| 12 | const actual = await importOriginal<typeof import("../hooks/useSettings")>(); |
| 13 | const settings = actual.getClientSettings(); |
| 14 | return { |
| 15 | ...actual, |
| 16 | useClientSettings: (select: (value: typeof settings) => unknown) => select(settings), |
| 17 | }; |
| 18 | }); |
| 19 | vi.mock("./ui/tooltip", async () => { |
| 20 | const { cloneElement, isValidElement } = await import("react"); |
| 21 | return { |
| 22 | Tooltip: ({ children }: { children: ReactNode }) => <>{children}</>, |
| 23 | TooltipTrigger({ |
| 24 | render, |
| 25 | children, |
| 26 | }: ComponentProps<typeof import("./ui/tooltip").TooltipTrigger>) { |
| 27 | if (!isValidElement(render)) return <>{children}</>; |
| 28 | return children === undefined ? render : cloneElement(render, undefined, children); |
| 29 | }, |
| 30 | TooltipPopup: () => null, |
| 31 | }; |
| 32 | }); |
| 33 | vi.mock("../state/use-atom-query-runner", () => ({ useAtomQueryRunner: () => mint })); |
| 34 | vi.mock("../state/use-atom-command", () => ({ useAtomCommand: () => vi.fn() })); |
| 35 | vi.mock("../state/session", async (importOriginal) => ({ |
| 36 | ...(await importOriginal<typeof import("../state/session")>()), |
| 37 | readEnvironmentScope: () => false, |
| 38 | useEnvironmentScope: () => false, |
| 39 | usePreparedConnection: () => ({ _tag: "Some", value: { httpBaseUrl: "https://host.test" } }), |
| 40 | })); |
| 41 | vi.mock("../state/entities", () => ({ |
| 42 | readEnvironmentSupportsServerBrowser: () => false, |
| 43 | readThreadShell: () => null, |
| 44 | useProjects: () => [], |
| 45 | useServerConfigs: () => new Map(), |
| 46 | })); |
| 47 | vi.mock("../remoteOpen", () => ({ |
| 48 | useRemoteOpenResolution: () => ({ state: { mode: "local-exec" }, isResolved: true }), |
| 49 | })); |
| 50 | vi.mock("../editorPreferences", () => ({ |
| 51 | useOpenInPreferredEditor: () => vi.fn(), |
| 52 | usePreferredEditor: () => [null, vi.fn()], |
| 53 | })); |
| 54 | vi.mock("~/lib/openPullRequestLink", () => ({ |
| 55 | findProjectForChangeRequest: () => undefined, |
| 56 | matchesLinkedPullRequestUrl: () => false, |
| 57 | parseChangeRequestUrl: () => null, |
| 58 | useOpenChangeRequestLink: () => vi.fn(), |
| 59 | })); |
| 60 | |
| 61 | import ChatMarkdown from "./ChatMarkdown"; |
| 62 | |
| 63 | it("opens host media through server authorization before the client grant loads", async () => { |
| 64 | vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true); |
| 65 | mint.mockResolvedValue( |
| 66 | AsyncResult.success({ relativeUrl: "/api/assets/image.png", expiresAt: 1 }), |
| 67 | ); |
| 68 | const onImageExpand = vi.fn(); |
| 69 | const threadRef = { |
| 70 | environmentId: EnvironmentId.make("media-environment"), |
| 71 | threadId: ThreadId.make("media-thread"), |
| 72 | }; |
| 73 | let renderer: ReactTestRenderer | undefined; |
| 74 | try { |
| 75 | await act(async () => { |
| 76 | renderer = create( |
| 77 | <ChatMarkdown |
| 78 | cwd="/repo" |
| 79 | threadRef={threadRef} |
| 80 | text="[Open image](/tmp/image.png)" |
| 81 | onImageExpand={onImageExpand} |
| 82 | />, |
| 83 | ); |
| 84 | }); |
| 85 | await act(async () => { |
| 86 | const link = renderer!.root |
| 87 | .findAllByType("a") |
| 88 | .find((node) => node.props.href === "/tmp/image.png"); |
| 89 | expect(link).toBeDefined(); |
| 90 | link!.props.onClick({ preventDefault: vi.fn(), stopPropagation: vi.fn() }); |
| 91 | }); |
| 92 | expect(onImageExpand).toHaveBeenCalledWith( |
| 93 | expect.objectContaining({ |
| 94 | images: [expect.objectContaining({ src: "https://host.test/api/assets/image.png" })], |
| 95 | }), |
| 96 | ); |
| 97 | } finally { |
| 98 | await act(async () => renderer?.unmount()); |
| 99 | vi.unstubAllGlobals(); |
| 100 | } |
| 101 | }); |