PRA-only-demo/packages/rpa-core/src/capabilities/extended.test.ts
yu caa8e19332 阶段1-rpa-core建包: engine+31个纯Node能力迁入packages/rpa-core, 领域类型单一源化, createEngine工厂收拢模块级状态
- engine/(registry/types/runtime/expression)物理迁入包, 解耦shared/types(域类型归包, 宿主type-only re-export)
- 31个纯Node能力+27个测试迁入包; 宿主留守Electron/浏览器/AI依赖能力, index.ts经@rpa/core/capabilities拉起
- 新增createEngine()/defaultEngine: excel缓存/Excel句柄/db连接/FTP客户端/OCR实例/拟真设置挂引擎实例
- remaining-core子流程依赖改为host-bridge注入, 包内不再import宿主flows
- 宿主main/index.ts启动时注入子流程源; electron-vite/vitest/tsconfig配@rpa/core源码别名
- 验证: typecheck绿, 快照909个id diff为零, 332测试过(2个桌面冒烟因会话无交互SetForegroundWindow被拒, 环境阻塞)
2026-11-04 12:36:03 +08:00

108 lines
5.2 KiB
TypeScript

import { mkdtemp, readFile, readdir, rm } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import ExcelJS from "exceljs";
import { afterEach, describe, expect, it } from "vitest";
import type { CatalogComponent, FlowNode } from "../types";
import type { RunContext } from "../engine/types";
import { getHandler } from "../engine/registry";
import "./extended";
const directories: string[] = [];
const context = {} as RunContext;
const component = {} as CatalogComponent;
const node = (componentId: string): FlowNode => ({
id: `extended_${componentId}`,
componentId,
position: { x: 0, y: 0 },
values: {},
options: {}
});
async function temporaryDirectory(): Promise<string> {
const directory = await mkdtemp(path.join(os.tmpdir(), "rpa-extended-"));
directories.push(directory);
return directory;
}
async function run(componentId: string, args: Record<string, unknown>): Promise<unknown> {
const handler = getHandler(componentId);
expect(handler, `${componentId} should be registered`).toBeTruthy();
return handler!(context, node(componentId), args, component);
}
afterEach(async () => {
await Promise.all(directories.splice(0).map((directory) => rm(directory, { recursive: true, force: true })));
});
describe("extended local capabilities", () => {
it("registers every audited component id", () => {
for (const id of [
"File_GetFileList_V1", "File_GetFolderList", "File_GetFolderList_V1", "File_MoveFolder",
"File_Write_V1", "File_Copy_V1", "File_Rename_V1", "File_ReadXml", "File_EmptyFolder",
"Bivartable_ReadDfShape", "Bivartable_SortDf", "Bivartable_DoSort", "Bivartable_DelDfColRow",
"Bivartable_ReadTable", "Excel_Cs_ReadCell", "Excel_GetRowsCount", "Excel_GetColumnsCount",
"Excel_Cs_GetEditRange"
]) expect(getHandler(id), id).toBeTruthy();
});
it("performs file listing, copy, rename, XML read, move, and empty operations", async () => {
const root = await temporaryDirectory();
const source = path.join(root, "source");
const nested = path.join(source, "nested");
await run("File_Write_V1", { path: path.join(source, "alpha.xml"), content: "<root>RPA</root>" });
await run("File_Write_V1", { path: path.join(nested, "beta.txt"), content: "beta" });
await expect(run("File_GetFileList_V1", { folder_path: source, recursive: "1", is_full_path: "0" }))
.resolves.toEqual(["alpha.xml", "beta.txt"]);
await expect(run("File_GetFolderList", { folder_path: source, recursive: "1" }))
.resolves.toEqual([nested]);
await expect(run("File_ReadXml", { path: path.join(source, "alpha.xml") }))
.resolves.toBe("<root>RPA</root>");
const copied = await run("File_Copy_V1", {
file_type: "file", file_path: path.join(source, "alpha.xml"), target_path: root,
rename: "1", name: "copied.xml", action: "1"
}) as string;
await expect(readFile(copied, "utf8")).resolves.toContain("RPA");
const renamed = await run("File_Rename_V1", { file_type: "file", file_path: copied, name: "renamed.xml", action: "1" }) as string;
await expect(readFile(renamed, "utf8")).resolves.toContain("RPA");
const moved = path.join(root, "moved");
await run("File_MoveFolder", { pathSrc: nested, pathDest: moved, overwrite: "True" });
await expect(readFile(path.join(moved, "beta.txt"), "utf8")).resolves.toBe("beta");
await run("File_EmptyFolder", { folder_path: moved });
await expect(readdir(moved)).resolves.toEqual([]);
});
it("sorts, measures, deletes, and selects two-dimensional data", async () => {
const input = [["name", "score"], ["B", 2], ["A", 3], ["C", 1]];
await expect(run("Bivartable_ReadDfShape", { df: input })).resolves.toEqual([4, 2]);
await expect(run("Bivartable_SortDf", { df: input, name: "score", sort: "True" }))
.resolves.toEqual([["name", "score"], ["C", 1], ["B", 2], ["A", 3]]);
await expect(run("Bivartable_DoSort", { df: [[3], [1], [2]], sortby: "1", basceeding: "False" }))
.resolves.toEqual([[3], [2], [1]]);
await expect(run("Bivartable_DelDfColRow", { df: input, delRange: "col", delType: "name", del_col_name_list: ["score"] }))
.resolves.toEqual([["name"], ["B"], ["A"], ["C"]]);
await expect(run("Bivartable_ReadTable", { df: input, start_row: "2", end_row: "3", start_column: "A", end_column: "B" }))
.resolves.toEqual([["B", 2], ["A", 3]]);
});
it("reads Excel cells, dimensions, and the edited range", async () => {
const root = await temporaryDirectory();
const filePath = path.join(root, "extended.xlsx");
const workbook = new ExcelJS.Workbook();
const sheet = workbook.addWorksheet("Data");
sheet.getCell("B2").value = "RPA";
sheet.getCell("D5").value = 42;
await workbook.xlsx.writeFile(filePath);
await expect(run("Excel_Cs_ReadCell", { workbook: filePath, sheet: "Data", column: "B", row: "2" })).resolves.toBe("RPA");
await expect(run("Excel_GetRowsCount", { objExcel: filePath, sheet: "Data" })).resolves.toBe(5);
await expect(run("Excel_GetColumnsCount", { objExcel: filePath, sheet: "Data" })).resolves.toBe(4);
await expect(run("Excel_Cs_GetEditRange", { workbook: { path: filePath }, sheet: "Data" })).resolves.toEqual({
startRow: 2, startColumn: 2, endRow: 5, endColumn: 4, range: "B2:D5"
});
});
});