Skip to content

Commit 0f685e1

Browse files
committed
chore: Artifact Visualization Tests
1 parent 1ded4a6 commit 0f685e1

8 files changed

Lines changed: 1142 additions & 0 deletions

File tree

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2+
import { render, screen, waitFor } from "@testing-library/react";
3+
import { userEvent } from "@testing-library/user-event";
4+
import { beforeEach, describe, expect, it, vi } from "vitest";
5+
6+
import type { ArtifactNodeResponse } from "@/api/types.gen";
7+
8+
import ArtifactVisualizer from "./ArtifactVisualizer";
9+
10+
vi.mock("@/providers/BackendProvider", () => ({
11+
useBackend: () => ({ backendUrl: "http://localhost:8000" }),
12+
}));
13+
14+
vi.mock("@/services/executionService", () => ({
15+
getArtifactSignedUrl: vi.fn().mockResolvedValue({
16+
signed_url: "https://storage.example.com/signed",
17+
}),
18+
}));
19+
20+
vi.mock("./TextVisualizer", () => ({
21+
TextVisualizerValue: ({ value }: { value: string }) => (
22+
<div data-testid="text-visualizer" data-value={value} />
23+
),
24+
TextVisualizerRemote: ({ signedUrl }: { signedUrl: string }) => (
25+
<div data-testid="text-visualizer" data-signed-url={signedUrl} />
26+
),
27+
}));
28+
29+
vi.mock("./ImageVisualizer", () => ({
30+
default: ({ src, name }: { src: string; name: string }) => (
31+
<div data-testid="image-visualizer" data-src={src} data-name={name} />
32+
),
33+
}));
34+
35+
vi.mock("./CsvVisualizer", () => ({
36+
CsvVisualizerValue: ({ value }: { value: string }) => (
37+
<div data-testid="csv-visualizer" data-value={value} />
38+
),
39+
CsvVisualizerRemote: ({ signedUrl }: { signedUrl: string }) => (
40+
<div data-testid="csv-visualizer" data-signed-url={signedUrl} />
41+
),
42+
}));
43+
44+
vi.mock("./JsonVisualizer", () => ({
45+
JsonVisualizerValue: ({ value, name }: { value: string; name: string }) => (
46+
<div data-testid="json-visualizer" data-value={value} data-name={name} />
47+
),
48+
JsonVisualizerRemote: ({
49+
signedUrl,
50+
name,
51+
}: {
52+
signedUrl: string;
53+
name: string;
54+
}) => (
55+
<div
56+
data-testid="json-visualizer"
57+
data-signed-url={signedUrl}
58+
data-name={name}
59+
/>
60+
),
61+
}));
62+
63+
vi.mock("./ParquetVisualizer", () => ({
64+
default: ({ signedUrl }: { signedUrl: string }) => (
65+
<div data-testid="parquet-visualizer" data-signed-url={signedUrl} />
66+
),
67+
}));
68+
69+
const queryClient = new QueryClient({
70+
defaultOptions: { queries: { retry: false } },
71+
});
72+
73+
const renderWithQuery = (ui: React.ReactElement) =>
74+
render(<QueryClientProvider client={queryClient}>{ui}</QueryClientProvider>);
75+
76+
const makeArtifact = (
77+
overrides?: Partial<ArtifactNodeResponse>,
78+
): ArtifactNodeResponse => ({
79+
id: "artifact-1",
80+
artifact_data: { total_size: 1024, is_dir: false },
81+
...overrides,
82+
});
83+
84+
beforeEach(() => {
85+
queryClient.clear();
86+
});
87+
88+
describe("ArtifactVisualizer", () => {
89+
describe("trigger button", () => {
90+
it("renders Eye + Preview button when no value is provided", () => {
91+
renderWithQuery(
92+
<ArtifactVisualizer
93+
artifact={makeArtifact()}
94+
name="output"
95+
type="text"
96+
/>,
97+
);
98+
99+
expect(screen.getByText("Preview")).toBeInTheDocument();
100+
});
101+
102+
it("renders Maximize2 button when value is provided", () => {
103+
renderWithQuery(
104+
<ArtifactVisualizer
105+
artifact={makeArtifact()}
106+
name="output"
107+
type="text"
108+
value="inline content"
109+
/>,
110+
);
111+
112+
expect(screen.queryByText("Preview")).not.toBeInTheDocument();
113+
// The maximize button should be present (ghost button without text)
114+
expect(screen.getByRole("button")).toBeInTheDocument();
115+
});
116+
});
117+
118+
it("returns null for non-visualizable types", () => {
119+
const { container } = renderWithQuery(
120+
<ArtifactVisualizer
121+
artifact={makeArtifact()}
122+
name="output"
123+
type="Unknown"
124+
/>,
125+
);
126+
127+
expect(container.innerHTML).toBe("");
128+
});
129+
130+
describe("inline value rendering", () => {
131+
it("renders TextVisualizerValue for text type", async () => {
132+
renderWithQuery(
133+
<ArtifactVisualizer
134+
artifact={makeArtifact()}
135+
name="output"
136+
type="text"
137+
value="hello"
138+
/>,
139+
);
140+
141+
await userEvent.click(screen.getByRole("button"));
142+
143+
await waitFor(() => {
144+
const viz = screen.getByTestId("text-visualizer");
145+
expect(viz).toHaveAttribute("data-value", "hello");
146+
});
147+
});
148+
149+
it("renders CsvVisualizerValue for csv type", async () => {
150+
renderWithQuery(
151+
<ArtifactVisualizer
152+
artifact={makeArtifact()}
153+
name="data"
154+
type="CSV"
155+
value={"a,b\n1,2"}
156+
/>,
157+
);
158+
159+
await userEvent.click(screen.getByRole("button"));
160+
161+
await waitFor(() => {
162+
const viz = screen.getByTestId("csv-visualizer");
163+
expect(viz).toHaveAttribute("data-value", "a,b\n1,2");
164+
});
165+
});
166+
167+
it("renders CsvVisualizerValue for tsv type", async () => {
168+
renderWithQuery(
169+
<ArtifactVisualizer
170+
artifact={makeArtifact()}
171+
name="data"
172+
type="TSV"
173+
value={"a\tb\n1\t2"}
174+
/>,
175+
);
176+
177+
await userEvent.click(screen.getByRole("button"));
178+
179+
await waitFor(() => {
180+
const viz = screen.getByTestId("csv-visualizer");
181+
expect(viz).toHaveAttribute("data-value", "a\tb\n1\t2");
182+
});
183+
});
184+
185+
it("renders JsonVisualizerValue for jsonobject type", async () => {
186+
renderWithQuery(
187+
<ArtifactVisualizer
188+
artifact={makeArtifact()}
189+
name="config"
190+
type="JsonObject"
191+
value='{"key":"val"}'
192+
/>,
193+
);
194+
195+
await userEvent.click(screen.getByRole("button"));
196+
197+
await waitFor(() => {
198+
const viz = screen.getByTestId("json-visualizer");
199+
expect(viz).toHaveAttribute("data-value", '{"key":"val"}');
200+
});
201+
});
202+
});
203+
204+
describe("signed URL rendering", () => {
205+
it("renders TextVisualizerRemote with signedUrl for text type", async () => {
206+
renderWithQuery(
207+
<ArtifactVisualizer artifact={makeArtifact()} name="log" type="text" />,
208+
);
209+
210+
await userEvent.click(screen.getByText("Preview"));
211+
212+
await waitFor(() => {
213+
const viz = screen.getByTestId("text-visualizer");
214+
expect(viz).toHaveAttribute(
215+
"data-signed-url",
216+
"https://storage.example.com/signed",
217+
);
218+
});
219+
});
220+
221+
it("renders ImageVisualizer for image type", async () => {
222+
renderWithQuery(
223+
<ArtifactVisualizer
224+
artifact={makeArtifact()}
225+
name="photo"
226+
type="image"
227+
/>,
228+
);
229+
230+
await userEvent.click(screen.getByText("Preview"));
231+
232+
await waitFor(() => {
233+
expect(screen.getByTestId("image-visualizer")).toBeInTheDocument();
234+
});
235+
});
236+
237+
it("renders ParquetVisualizer for apacheparquet type", async () => {
238+
renderWithQuery(
239+
<ArtifactVisualizer
240+
artifact={makeArtifact()}
241+
name="data"
242+
type="Apache Parquet"
243+
/>,
244+
);
245+
246+
await userEvent.click(screen.getByText("Preview"));
247+
248+
await waitFor(() => {
249+
expect(screen.getByTestId("parquet-visualizer")).toBeInTheDocument();
250+
});
251+
});
252+
});
253+
254+
describe("dialog header", () => {
255+
it("shows artifact name and type in dialog", async () => {
256+
renderWithQuery(
257+
<ArtifactVisualizer
258+
artifact={makeArtifact()}
259+
name="my-output"
260+
type="CSV"
261+
value="a,b"
262+
/>,
263+
);
264+
265+
await userEvent.click(screen.getByRole("button"));
266+
267+
await waitFor(() => {
268+
expect(screen.getByText("my-output")).toBeInTheDocument();
269+
expect(screen.getByText("CSV")).toBeInTheDocument();
270+
});
271+
});
272+
273+
it("shows artifact URI when available", async () => {
274+
renderWithQuery(
275+
<ArtifactVisualizer
276+
artifact={makeArtifact({
277+
artifact_data: {
278+
total_size: 100,
279+
is_dir: false,
280+
uri: "gs://bucket/path",
281+
},
282+
})}
283+
name="output"
284+
type="text"
285+
value="content"
286+
/>,
287+
);
288+
289+
await userEvent.click(screen.getByRole("button"));
290+
291+
await waitFor(() => {
292+
expect(screen.getByText("Copy URI")).toBeInTheDocument();
293+
});
294+
});
295+
});
296+
});

0 commit comments

Comments
 (0)