import { beforeEach, describe, expect, it, vi } from "vitest"; const capabilitiesMock = vi.hoisted(() => vi.fn()); const downloadFileMock = vi.hoisted(() => vi.fn()); const openFileMock = vi.hoisted(() => vi.fn()); const pickFilesMock = vi.hoisted(() => vi.fn()); const saveFileMock = vi.hoisted(() => vi.fn()); const messageInfoMock = vi.hoisted(() => vi.fn()); const messageSuccessMock = vi.hoisted(() => vi.fn()); const finishActivityMock = vi.hoisted(() => vi.fn()); const startActivityMock = vi.hoisted(() => vi.fn()); const updateActivityMock = vi.hoisted(() => vi.fn()); vi.mock("../runtime", () => ({ clientRuntime: { capabilities: capabilitiesMock, }, downloadFile: downloadFileMock, openFile: openFileMock, pickFiles: pickFilesMock, saveFile: saveFileMock, })); vi.mock("element-plus", () => ({ ElMessage: { info: messageInfoMock, success: messageSuccessMock, }, })); vi.mock("../session/desktopActivityCenter", () => ({ finishDesktopActivity: finishActivityMock, startDesktopActivity: startActivityMock, updateDesktopActivity: updateActivityMock, })); const output = { suggestedName: "report.pdf", mimeType: "application/pdf", data: new Blob(["content"], { type: "application/pdf" }), }; describe("file task feedback", () => { beforeEach(() => { vi.clearAllMocks(); capabilitiesMock.mockReturnValue({ nativeFiles: false }); pickFilesMock.mockResolvedValue([]); saveFileMock.mockResolvedValue("saved"); openFileMock.mockResolvedValue(undefined); downloadFileMock.mockResolvedValue(undefined); startActivityMock.mockReturnValue("activity-1"); }); it("reports native file picker selections", async () => { const files = [ new File(["a"], "a.txt", { type: "text/plain" }), new File(["b"], "b.txt", { type: "text/plain" }), ]; pickFilesMock.mockResolvedValue(files); const { pickFilesWithFeedback } = await import("./fileTaskFeedback"); const selected = await pickFilesWithFeedback({ multiple: true, title: "附件" }); expect(selected).toBe(files); expect(pickFilesMock).toHaveBeenCalledWith({ multiple: true, title: "附件" }); expect(messageSuccessMock).toHaveBeenCalledWith("已选择 2 个文件"); }); it("does not show a selection toast when the picker is cancelled", async () => { const { pickFilesWithFeedback } = await import("./fileTaskFeedback"); await expect(pickFilesWithFeedback({ multiple: true })).resolves.toEqual([]); expect(messageSuccessMock).not.toHaveBeenCalled(); }); it("uses download feedback for web save flows", async () => { const { saveFileWithFeedback } = await import("./fileTaskFeedback"); await expect(saveFileWithFeedback(output)).resolves.toBe("saved"); expect(saveFileMock).toHaveBeenCalledWith(output); expect(messageSuccessMock).toHaveBeenCalledWith("文件下载已开始"); }); it("downloads directly without invoking the save flow", async () => { const { downloadFileWithFeedback } = await import("./fileTaskFeedback"); await downloadFileWithFeedback(output); expect(downloadFileMock).toHaveBeenCalledWith(output); expect(saveFileMock).not.toHaveBeenCalled(); expect(messageSuccessMock).toHaveBeenCalledWith("文件下载已开始"); }); it("reports direct downloads through persistent desktop activity", async () => { capabilitiesMock.mockReturnValue({ nativeFiles: true }); const { downloadFileWithFeedback } = await import("./fileTaskFeedback"); await downloadFileWithFeedback(output, { title: "下载 report.pdf" }); expect(downloadFileMock).toHaveBeenCalledWith(output); expect(saveFileMock).not.toHaveBeenCalled(); expect(startActivityMock).toHaveBeenCalledWith({ kind: "download", title: "下载 report.pdf", detail: "正在准备下载", }); expect(finishActivityMock).toHaveBeenCalledWith("activity-1", "completed", { detail: "文件下载已开始" }); }); it("writes to a destination that was selected before the download", async () => { const destination = { kind: "web-file-handle", handle: { createWritable: vi.fn() } } as const; const { saveFileWithFeedback } = await import("./fileTaskFeedback"); await expect(saveFileWithFeedback(output, {}, destination)).resolves.toBe("saved"); expect(saveFileMock).toHaveBeenCalledWith(output, destination); expect(messageSuccessMock).toHaveBeenCalledWith("文件已保存"); }); it("uses persistent desktop activity for native save flows", async () => { capabilitiesMock.mockReturnValue({ nativeFiles: true }); const { saveFileWithFeedback } = await import("./fileTaskFeedback"); await expect(saveFileWithFeedback(output)).resolves.toBe("saved"); expect(startActivityMock).toHaveBeenCalledWith({ kind: "download", title: "保存文件", detail: "等待选择保存位置", }); expect(updateActivityMock).toHaveBeenCalledWith("activity-1", { detail: "等待选择保存位置", progress: 70, }); expect(finishActivityMock).toHaveBeenCalledWith("activity-1", "completed", { detail: "文件已保存" }); expect(messageSuccessMock).not.toHaveBeenCalled(); }); it("reports cancelled web save flows without treating them as downloads", async () => { saveFileMock.mockResolvedValue("cancelled"); const { saveFileWithFeedback } = await import("./fileTaskFeedback"); await expect(saveFileWithFeedback(output)).resolves.toBe("cancelled"); expect(messageInfoMock).toHaveBeenCalledWith("已取消保存文件"); expect(messageSuccessMock).not.toHaveBeenCalled(); }); it("reports cancelled native save flows through desktop activity", async () => { capabilitiesMock.mockReturnValue({ nativeFiles: true }); saveFileMock.mockResolvedValue("cancelled"); const { saveFileWithFeedback } = await import("./fileTaskFeedback"); await expect(saveFileWithFeedback(output)).resolves.toBe("cancelled"); expect(finishActivityMock).toHaveBeenCalledWith("activity-1", "cancelled", { detail: "已取消保存" }); expect(messageInfoMock).not.toHaveBeenCalled(); }); it("reports external open completion", async () => { const { openFileWithFeedback } = await import("./fileTaskFeedback"); await openFileWithFeedback(output); expect(openFileMock).toHaveBeenCalledWith(output); expect(messageSuccessMock).toHaveBeenCalledWith("文件已打开"); }); it("uses persistent desktop activity for native open flows", async () => { capabilitiesMock.mockReturnValue({ nativeFiles: true }); const { openFileWithFeedback } = await import("./fileTaskFeedback"); await openFileWithFeedback(output); expect(startActivityMock).toHaveBeenCalledWith({ kind: "open", title: "打开文件", detail: "正在准备文件", }); expect(finishActivityMock).toHaveBeenCalledWith("activity-1", "completed", { detail: "已交给系统打开" }); expect(messageSuccessMock).not.toHaveBeenCalled(); }); });