PRA-only-demo/packages/rpa-core/src/capabilities/files.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

68 lines
3.2 KiB
TypeScript

import { existsSync, readdirSync, readFileSync } from "node:fs";
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it, vi } from "vitest";
import type { CatalogComponent, FlowNode } from "../types";
import type { RunContext } from "../engine/types";
import { getHandler } from "../engine/registry";
import "./files";
function run(id: string, args: Record<string, unknown>): Promise<string> {
const node: FlowNode = { id: "file-test", componentId: id, values: args, options: {}, position: { x: 0, y: 0 } };
const handler = getHandler(id)!;
return Promise.resolve(handler({ signal: new AbortController().signal, log: vi.fn(), vars: {} } as unknown as RunContext, node, args, {} as CatalogComponent)) as Promise<string>;
}
const temporary: string[] = [];
afterEach(() => {
for (const dir of temporary.splice(0)) rm(dir, { recursive: true, force: true });
});
describe("file compression path semantics", () => {
it("defaults to a sibling archive and creates a copy instead of overwriting", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "rpa-file-compress-"));
temporary.push(dir);
const source = path.join(dir, "report.txt");
await writeFile(source, "hello", "utf8");
const first = await run("File_CompressFile", { file_type: "openFile", file_path: source, direct_type: "1" });
expect(first).toBe(path.join(dir, "report.txt.zip"));
expect(existsSync(first)).toBe(true);
const second = await run("File_CompressFile", { file_type: "openFile", file_path: source, direct_type: "1" });
expect(second).toBe(path.join(dir, "report.txt (1).zip"));
expect(existsSync(second)).toBe(true);
});
it("accepts an output directory when custom destination is selected", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "rpa-file-compress-"));
temporary.push(dir);
const source = path.join(dir, "logs");
const output = path.join(dir, "output");
await Promise.all([mkdir(source), mkdir(output)]);
await writeFile(path.join(source, "app.log"), "log", "utf8");
const target = await run("File_CompressFile", {
file_type: "openDirectory", folder_path: source, direct_type: "0", out_path: output
});
expect(target).toBe(path.join(output, "logs.zip"));
expect(existsSync(target)).toBe(true);
});
it("extracts beside the archive by default and to the selected folder for custom mode", async () => {
const dir = await mkdtemp(path.join(os.tmpdir(), "rpa-file-extract-"));
temporary.push(dir);
const source = path.join(dir, "logs.zip");
const AdmZip = (await import("adm-zip")).default;
const zip = new AdmZip();
zip.addFile("app.log", Buffer.from("log", "utf8"));
zip.writeZip(source);
const defaultTarget = await run("File_DecompressFile", { folder_path: source, direct_type: "1" });
expect(defaultTarget).toBe(dir);
expect(readFileSync(path.join(dir, "app.log"), "utf8")).toBe("log");
const custom = path.join(dir, "custom-output");
const customTarget = await run("File_DecompressFile", { folder_path: source, direct_type: "0", out_path: custom });
expect(customTarget).toBe(custom);
expect(readdirSync(custom)).toContain("app.log");
});
});