// @vitest-environment jsdom // // E7·S5c — the local-upload dialog. It's a general file upload (any type, no format list): a picked // file surfaces its name+size, and Upload streams it through api.uploadDownload (the XHR-progress // path). This drives the real dialog against a mocked api so we can assert the file + optional name // actually reach the ingest call, and that the success toast points at the library. i18n is // key-passthrough. import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import { render, screen, fireEvent, waitFor, cleanup } from "@testing-library/react"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const h = vi.hoisted(() => ({ uploadDownload: vi.fn(async (_f: File, _o: unknown) => ({ id: 42 })), notify: vi.fn(), })); vi.mock("react-i18next", async (importOriginal) => { const actual = await importOriginal(); return { ...actual, useTranslation: () => ({ t: (key: string) => key }) }; }); vi.mock("../lib/api", () => ({ api: { uploadDownload: h.uploadDownload } })); vi.mock("../lib/downloads", () => ({ invalidateDownloads: vi.fn() })); vi.mock("../lib/notifications", () => ({ notify: h.notify })); vi.mock("../lib/nav", () => ({ navigateTo: vi.fn() })); import UploadDialog from "./UploadDialog"; const wrap = (onClose = vi.fn()) => { const qc = new QueryClient(); return render( , ); }; // The dialog portals into document.body, so query the whole document, not the render container. const fileInput = () => document.querySelector('input[type="file"]') as HTMLInputElement; beforeEach(() => { h.uploadDownload.mockClear(); h.notify.mockClear(); }); afterEach(cleanup); describe("UploadDialog", () => { it("shows the picked file's name and uploads it with the optional name", async () => { const onClose = vi.fn(); wrap(onClose); const f = new File(["hello world"], "notes.pdf", { type: "application/pdf" }); fireEvent.change(fileInput(), { target: { files: [f] } }); // The drop zone now surfaces the file name. expect(screen.getByText("notes.pdf")).toBeTruthy(); // Optional display name. const nameBox = screen.getByPlaceholderText("downloads.upload.namePlaceholder"); fireEvent.change(nameBox, { target: { value: "My notes" } }); fireEvent.click(screen.getByText("downloads.upload.submit")); await waitFor(() => expect(h.uploadDownload).toHaveBeenCalledTimes(1)); const [passedFile, opts] = h.uploadDownload.mock.calls[0]!; expect(passedFile).toBe(f); expect((opts as { displayName?: string }).displayName).toBe("My notes"); await waitFor(() => expect(h.notify).toHaveBeenCalledTimes(1)); await waitFor(() => expect(onClose).toHaveBeenCalled()); }); it("keeps Upload disabled until a file is chosen", () => { wrap(); const btn = screen.getByText("downloads.upload.submit").closest("button")!; expect(btn.disabled).toBe(true); }); });