|
| 1 | +import type { ReactNode } from "react"; |
| 2 | +import { act } from "react"; |
| 3 | +import { createRoot } from "react-dom/client"; |
| 4 | +import { expect, it, vi } from "vite-plus/test"; |
| 5 | + |
| 6 | +const harness = vi.hoisted(() => ({ |
| 7 | + failImage: () => {}, |
| 8 | + Passthrough: ({ children }: { children?: unknown }) => children, |
| 9 | +})); |
| 10 | + |
| 11 | +vi.mock("react-native", () => ({ |
| 12 | + Pressable: harness.Passthrough, |
| 13 | + StyleSheet: { absoluteFill: {} }, |
| 14 | + View: harness.Passthrough, |
| 15 | +})); |
| 16 | +vi.mock("expo-image", () => ({ |
| 17 | + Image: (props: { onError: () => void }) => { |
| 18 | + harness.failImage = props.onError; |
| 19 | + return <img alt="" />; |
| 20 | + }, |
| 21 | +})); |
| 22 | +vi.mock("expo/fetch", () => ({ fetch: vi.fn() })); |
| 23 | +vi.mock("react-native-svg", () => ({ parse: vi.fn(), SvgAst: () => null })); |
| 24 | +vi.mock("@t3tools/client-runtime/state/assets", () => ({ MISSING_FILE_ACCESS_MESSAGE: "" })); |
| 25 | +vi.mock("../../components/ui/AppActivityIndicator", () => ({ AppActivityIndicator: () => null })); |
| 26 | +vi.mock("../../components/FilePreviewGallery", () => ({ |
| 27 | + useFilePreviewGallery: () => (source: unknown) => source, |
| 28 | +})); |
| 29 | +vi.mock("../../components/ui/AppText", () => ({ |
| 30 | + AppText: ({ children }: { children?: ReactNode }) => <span>{children}</span>, |
| 31 | +})); |
| 32 | +vi.mock("../../components/MediaActionsMenu", () => ({ MediaActionsMenu: harness.Passthrough })); |
| 33 | +vi.mock("../../components/ui/NativePresentation", () => ({ |
| 34 | + PresentationSource: harness.Passthrough, |
| 35 | +})); |
| 36 | +vi.mock("../../state/mediaActions", () => ({ useMediaActions: () => ({ actions: [] }) })); |
| 37 | +vi.mock("../../state/assets", () => ({ useAssetUrlState: vi.fn() })); |
| 38 | + |
| 39 | +import { ThreadMarkdownImageView } from "./ThreadMarkdownImage"; |
| 40 | + |
| 41 | +it("retries a failed image once its URI comes back through null", async () => { |
| 42 | + (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true; |
| 43 | + const container = document.createElement("div"); |
| 44 | + const root = createRoot(container); |
| 45 | + const view = (uri: string | null) => ( |
| 46 | + <ThreadMarkdownImageView |
| 47 | + uri={uri} |
| 48 | + sourceKey="attachment:a" |
| 49 | + unavailable={false} |
| 50 | + alt={null} |
| 51 | + onPressPreview={() => {}} |
| 52 | + /> |
| 53 | + ); |
| 54 | + try { |
| 55 | + await act(async () => root.render(view("https://example.test/a.png"))); |
| 56 | + await act(async () => harness.failImage()); |
| 57 | + expect(container.textContent).toContain("Image unavailable"); |
| 58 | + |
| 59 | + await act(async () => root.render(view(null))); |
| 60 | + await act(async () => root.render(view("https://example.test/a.png"))); |
| 61 | + expect(container.textContent).not.toContain("Image unavailable"); |
| 62 | + expect(container.querySelector("img")).not.toBeNull(); |
| 63 | + } finally { |
| 64 | + await act(async () => root.unmount()); |
| 65 | + } |
| 66 | +}); |
0 commit comments