diff --git a/packages/ui/src/features/sessions/components/CloudArtifactDownloads.test.tsx b/packages/ui/src/features/sessions/components/CloudArtifactDownloads.test.tsx index c7cc9f8581..491f6c1b77 100644 --- a/packages/ui/src/features/sessions/components/CloudArtifactDownloads.test.tsx +++ b/packages/ui/src/features/sessions/components/CloudArtifactDownloads.test.tsx @@ -52,13 +52,25 @@ const task = { describe("CloudArtifactDownloads", () => { beforeEach(() => { getCloudAttachmentPreviewUrl.mockReset(); + vi.restoreAllMocks(); }); it("shows output artifacts and opens their download URL", async () => { getCloudAttachmentPreviewUrl.mockResolvedValue( "https://files.example/report.pdf", ); - const open = vi.spyOn(window, "open").mockImplementation(() => null); + const fetchArtifact = vi + .spyOn(window, "fetch") + .mockResolvedValue(new Response("file contents")); + const createObjectURL = vi + .spyOn(URL, "createObjectURL") + .mockReturnValue("blob:artifact"); + const revokeObjectURL = vi + .spyOn(URL, "revokeObjectURL") + .mockImplementation(() => undefined); + const click = vi + .spyOn(HTMLAnchorElement.prototype, "click") + .mockImplementation(() => undefined); render( @@ -70,15 +82,14 @@ describe("CloudArtifactDownloads", () => { expect(screen.getByText("12 KB")).toBeInTheDocument(); expect(screen.queryByText("handoff.pack")).not.toBeInTheDocument(); - fireEvent.click(screen.getByRole("button", { name: "Download" })); + fireEvent.click(screen.getByText("Download")); - await waitFor(() => - expect(open).toHaveBeenCalledWith( - "https://files.example/report.pdf", - "_blank", - "noopener,noreferrer", - ), + await waitFor(() => expect(click).toHaveBeenCalledOnce()); + expect(fetchArtifact).toHaveBeenCalledWith( + "https://files.example/report.pdf", ); + expect(createObjectURL).toHaveBeenCalledOnce(); + expect(revokeObjectURL).toHaveBeenCalledWith("blob:artifact"); expect(getCloudAttachmentPreviewUrl).toHaveBeenCalledWith( "task-1", "run-1", diff --git a/packages/ui/src/features/sessions/components/CloudArtifactDownloads.tsx b/packages/ui/src/features/sessions/components/CloudArtifactDownloads.tsx index 364629846c..a58b16835d 100644 --- a/packages/ui/src/features/sessions/components/CloudArtifactDownloads.tsx +++ b/packages/ui/src/features/sessions/components/CloudArtifactDownloads.tsx @@ -81,7 +81,14 @@ export function CloudArtifactDownloads({ toast.error("This file is no longer available"); return; } - window.open(url, "_blank", "noopener,noreferrer"); + const response = await fetch(url); + if (!response.ok) throw new Error("Artifact download failed"); + const objectUrl = URL.createObjectURL(await response.blob()); + const anchor = document.createElement("a"); + anchor.href = objectUrl; + anchor.download = artifact.name; + anchor.click(); + URL.revokeObjectURL(objectUrl); } catch { toast.error("Couldn't download file"); } finally {