import { mkdtemp, readFile, rm } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import ExcelJS from "exceljs"; import { getHandler } from "../engine/registry"; import type { CatalogComponent, FlowNode } from "../types"; import type { RunContext } from "../engine/types"; import "./excel-com"; const directories: string[] = []; const context = {} as RunContext; const component = {} as CatalogComponent; const node = (id: string): FlowNode => ({ id, componentId: id, position: { x: 0, y: 0 }, values: {}, options: {} }); async function run(id: string, args: Record) { const handler = getHandler(id); expect(handler, id).toBeTruthy(); return handler!(context, node(id), args, component); } afterEach(async () => Promise.all(directories.splice(0).map((directory) => rm(directory, { recursive: true, force: true })))); const registered = [ "Excel_Cs_Group", "Excel_Cs_DeletePicture", "Excel_Cs_RefreshPivotTable", "Excel_Cs_DeletePictureV2", "Excel_Cs_Encrypt", "Excel_Cs_Move", "Excel_Cs_InsertPicture", "Excel_Cs_Merge", "Excel_InsertImage", "Excel_Cs_InsertPivotView", "Excel_Cs_Interop", "Excel_Cs_InsertPivotTable", "Excel_Cs_RunMacro", "Excel_Loop", "Excel_Cs_Export", "Excel_Cs_ExcelAddin", "Excel_Cs_InsertPictureV2", "Excel_Cs_FilterPivotTable", "Excel_Cs_RunMacroV2", "Excel_Cs_SetSlicer", "Excel_ExecuteMacro", "Excel_Cs_RefreshAll", "Excel_DeleteImage", "Excel_ExportRangeAsPicture" ]; describe("Excel COM capabilities", () => { it("registers every Excel COM capability", () => { for (const id of registered) expect(getHandler(id), id).toBeTruthy(); }); it("groups rows, moves sheets, reads dynamic properties and loops content", async () => { if (process.platform !== "win32") return; const directory = await mkdtemp(path.join(os.tmpdir(), "rpa-excel-com-")); directories.push(directory); const filePath = path.join(directory, "workbook.xlsx"); const workbook = new ExcelJS.Workbook(); workbook.addWorksheet("Sheet1").getCell("A1").value = "A1"; workbook.addWorksheet("Second").getCell("A1").value = "S1"; await workbook.xlsx.writeFile(filePath); await expect(run("Excel_Cs_Group", { workbook: { path: filePath }, sheet: "Sheet1", group: 0, groupType: 0, startRow: 1, endRow: 2, save: true })).resolves.toMatchObject({ grouped: true }); await expect(run("Excel_Cs_Interop", { workbook: { path: filePath }, obj: 2, sheet: "Sheet1", property: "Name" })).resolves.toMatchObject({ value: "Sheet1" }); await expect(run("Excel_Cs_Move", { workbook: { path: filePath }, sheet: "Second", mode: "按序号", index: 1, save: true })).resolves.toMatchObject({ moved: true }); await expect(run("Excel_Cs_RefreshAll", { workbook: { path: filePath }, save: true })).resolves.toMatchObject({ refreshed: true }); const loop = await run("Excel_Loop", { objExcel: { path: filePath } }) as unknown[][]; expect(loop.length).toBeGreaterThanOrEqual(2); expect(loop.flat().join(",")).toContain("A1"); }, 120000); it("exports a workbook to PDF through Excel COM", async () => { if (process.platform !== "win32") return; const directory = await mkdtemp(path.join(os.tmpdir(), "rpa-excel-pdf-")); directories.push(directory); const filePath = path.join(directory, "source.xlsx"); const workbook = new ExcelJS.Workbook(); workbook.addWorksheet("Report").getCell("A1").value = "PDF"; await workbook.xlsx.writeFile(filePath); const output = await run("Excel_Cs_Export", { workbook: { path: filePath }, option: "全部", path: directory, name: "report.pdf", cover: true }) as string; expect(output).toBe(path.join(directory, "report.pdf")); expect((await readFile(output)).subarray(0, 5).toString()).toMatch(/^%PDF-/); }, 120000); });