86 lines
4.6 KiB
TypeScript
86 lines
4.6 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
||
import type { CatalogComponent, FlowNode } from "../../src/types";
|
||
import type { RunContext } from "../../src/engine/types";
|
||
import { getHandler } from "../../src/engine/registry";
|
||
|
||
const mocks = vi.hoisted(() => ({ powershell: vi.fn(), execFile: vi.fn() }));
|
||
vi.mock("../driver/windows/powershell", () => ({ powershell: mocks.powershell, psString: (value: unknown) => `'${String(value ?? "").replace(/'/g, "''")}'` }));
|
||
vi.mock("node:child_process", () => ({ execFile: mocks.execFile }));
|
||
import "../../src/capabilities/data";
|
||
import "../../src/capabilities/control";
|
||
import "../../src/capabilities/system-extended";
|
||
import "../../src/capabilities/windows-system";
|
||
|
||
function run(id: string, args: Record<string, unknown> = {}, signal = new AbortController().signal) {
|
||
const node: FlowNode = { id: "regression", componentId: id, values: args, options: {}, position: { x: 0, y: 0 } };
|
||
return getHandler(id)!({ signal, log: vi.fn(), vars: {} } as unknown as RunContext, node, args, {} as CatalogComponent);
|
||
}
|
||
|
||
afterEach(() => { vi.useRealTimers(); vi.resetAllMocks(); });
|
||
|
||
describe.skipIf(process.platform !== "win32")("应用与系统组件缺陷回归", () => {
|
||
it("没有匹配进程时返回 [] / null,忽略空行和非法 PID", async () => {
|
||
mocks.powershell.mockResolvedValue("");
|
||
await expect(run("App_GetPids", { pattern: "missing.exe" })).resolves.toEqual([]);
|
||
await expect(run("App_GetPid", { pattern: "missing.exe" })).resolves.toBeNull();
|
||
mocks.powershell.mockResolvedValue("\r\n0\r\n123\r\nabc\r\n456\r\n");
|
||
await expect(run("App_GetPids", { pattern: "fixture.exe" })).resolves.toEqual([123, 456]);
|
||
});
|
||
|
||
it("毫秒等待读取 mileSecond,计时和取消均正确", async () => {
|
||
vi.useFakeTimers();
|
||
const waiting = Promise.resolve(run("Basic_Delay", { mileSecond: "250" }));
|
||
let done = false;
|
||
void waiting.then(() => { done = true; });
|
||
await vi.advanceTimersByTimeAsync(249);
|
||
expect(done).toBe(false);
|
||
await vi.advanceTimersByTimeAsync(1);
|
||
expect(done).toBe(true);
|
||
const controller = new AbortController();
|
||
const cancelled = Promise.resolve(run("Basic_Delay", { mileSecond: "3000" }, controller.signal));
|
||
const rejection = expect(cancelled).rejects.toMatchObject({ code: "cancelled" });
|
||
controller.abort();
|
||
await rejection;
|
||
});
|
||
|
||
it.each([
|
||
[42, "number", true], ["42", "number", false], [42, "1", true],
|
||
[[], "list", true], [{ key: 1 }, "dict", true], [null, "null", true],
|
||
[false, "bool", true], ["中文", "string", true], [3.5, "int", false]
|
||
])("按目标类型判断 %j → %s", (val, into_type, expected) => {
|
||
expect(run("Basic_JudgeDataType", { val, into_type })).toBe(expected);
|
||
});
|
||
|
||
it.each([
|
||
["42.5", "number", 42.5], ["42", "1", 42], ["-3.9", "int", -3],
|
||
["false", "bool", false], ["true", "bool", true], [42, "string", "42"]
|
||
])("执行类型转换 %j → %s", (val, into_type, expected) => {
|
||
expect(run("Basic_DataTypeConversion", { val, into_type })).toBe(expected);
|
||
});
|
||
|
||
it("非法数值或未知目标类型给出定位错误,避免静默返回原值", () => {
|
||
expect(() => run("Basic_DataTypeConversion", { val: "abc", into_type: "int" })).toThrow();
|
||
expect(() => run("Basic_DataTypeConversion", { val: 1, into_type: "unknown" })).toThrow();
|
||
expect(() => run("Basic_JudgeDataType", { val: 1, into_type: "unknown" })).toThrow();
|
||
});
|
||
|
||
it("关闭组件按所选模式执行,非法 PID 不降级为按名称批量关闭", async () => {
|
||
mocks.powershell.mockResolvedValue("");
|
||
await expect(run("App_KillV1", { close_type: "id", pid: "invalid", p_name: "fixture.exe" })).rejects.toMatchObject({ code: "validation_error" });
|
||
expect(mocks.powershell).not.toHaveBeenCalled();
|
||
await run("App_KillV1", { close_type: "name", pid: 123, p_name: "fixture.exe", kill_all: "0" });
|
||
expect(mocks.powershell.mock.calls[0][0]).toContain("SessionId");
|
||
expect(mocks.powershell.mock.calls[0][0]).not.toContain("Stop-Process -Id 123");
|
||
});
|
||
|
||
it("唤醒不声称已通过 Windows 登录解锁", async () => {
|
||
mocks.execFile.mockImplementation((_file, _args, _opts, callback) => callback(null, { stdout: "", stderr: "" }));
|
||
await expect(run("ScreenControl_ScreenUnlock")).resolves.toMatchObject({ awakened: true, loginUnlockSupported: false });
|
||
});
|
||
|
||
it("屏保自定义密码不受支持时,在触发桌面操作前明确报错", async () => {
|
||
await expect(run("System_ScreenSaverShowV1", { set_password: "1", password: "" })).rejects.toMatchObject({ code: "validation_error" });
|
||
expect(mocks.execFile).not.toHaveBeenCalled();
|
||
});
|
||
});
|