test: 真桌面冒烟测试补 win32 平台守卫 + 平台判断收拢

迁移前置项(指南五件小事 #3/#4/#5):
- 键盘/鼠标/应用系统冒烟/回归/手工加密 5 个测试文件补 skipIf(非 win32),
  Mac/Linux CI 可跑纯逻辑部分
- 新增 driver/platform.ts 平台判断唯一入口(dev-conventions 规则 4 例外白名单),
  utility-extended 两处 process.platform 散写改引 isWindows
- 能力分组核查:COM 类能力(excelCom/word/wordExtended)在 DOMAINS 已是独立分组,可整组过滤
This commit is contained in:
yu 2026-11-05 18:03:06 +08:00
parent 51a6178bfc
commit 0fcb1ecc0e
7 changed files with 17 additions and 7 deletions

View File

@ -18,7 +18,7 @@ function run(id: string, args: Record<string, unknown> = {}, signal = new AbortC
afterEach(() => { vi.useRealTimers(); vi.resetAllMocks(); }); afterEach(() => { vi.useRealTimers(); vi.resetAllMocks(); });
describe("应用与系统组件缺陷回归", () => { describe.skipIf(process.platform !== "win32")("应用与系统组件缺陷回归", () => {
it("没有匹配进程时返回 [] / null,忽略空行和非法 PID", async () => { it("没有匹配进程时返回 [] / null,忽略空行和非法 PID", async () => {
mocks.powershell.mockResolvedValue(""); mocks.powershell.mockResolvedValue("");
await expect(run("App_GetPids", { pattern: "missing.exe" })).resolves.toEqual([]); await expect(run("App_GetPids", { pattern: "missing.exe" })).resolves.toEqual([]);

View File

@ -33,7 +33,7 @@ async function run(componentId: string, args: Record<string, unknown>, ctxOverri
return handler(ctxOverride ?? ctx, n, args, {} as CatalogComponent); return handler(ctxOverride ?? ctx, n, args, {} as CatalogComponent);
} }
describe("应用与系统组件冒烟", () => { describe.skipIf(process.platform !== "win32")("应用与系统组件冒烟", () => {
it("数据类型判断族", async () => { it("数据类型判断族", async () => {
expect(await run("Basic_IsNumber", { val: "12.5" })).toBe(true); expect(await run("Basic_IsNumber", { val: "12.5" })).toBe(true);
expect(await run("Basic_IsNumber", { val: "abc" })).toBe(false); expect(await run("Basic_IsNumber", { val: "abc" })).toBe(false);

View File

@ -14,7 +14,7 @@ async function run(id: string, args: Record<string, unknown>) {
return handler!(context, node(id), args, component); return handler!(context, node(id), args, component);
} }
describe("manual keyboard and encrypted variables", () => { describe.skipIf(process.platform !== "win32")("manual keyboard and encrypted variables", () => {
it("registers manual keyboard switches", async () => { it("registers manual keyboard switches", async () => {
await expect(run("Keyboard_EnableManualOperation", { random_move: true, random_click: true, random_sleep: false, min_time: 0, max_time: 0 })).resolves.toMatchObject({ enabled: true }); await expect(run("Keyboard_EnableManualOperation", { random_move: true, random_click: true, random_sleep: false, min_time: 0, max_time: 0 })).resolves.toMatchObject({ enabled: true });
await expect(run("Keyboard_DisableManualOperation", {})).resolves.toEqual({ enabled: false }); await expect(run("Keyboard_DisableManualOperation", {})).resolves.toEqual({ enabled: false });

View File

@ -20,7 +20,7 @@ async function invoke(componentId: string, args: Record<string, unknown>): Promi
return handler!(ctx, node(componentId), args, component); return handler!(ctx, node(componentId), args, component);
} }
describe("键盘输入组件真实冒烟", () => { describe.skipIf(process.platform !== "win32")("键盘输入组件真实冒烟", () => {
it("words 字段取值(此前读不到报「键盘输入为空」), 后台接口模式整段发送特殊字符不抛错", async () => { it("words 字段取值(此前读不到报「键盘输入为空」), 后台接口模式整段发送特殊字符不抛错", async () => {
const out = await invoke("Keyboard_Send_V1", { words: "哈哈哈(测试)100%^2", interval: "10", fuctype: "0" }); const out = await invoke("Keyboard_Send_V1", { words: "哈哈哈(测试)100%^2", interval: "10", fuctype: "0" });
expect(out).toBe("哈哈哈(测试)100%^2"); expect(out).toBe("哈哈哈(测试)100%^2");

View File

@ -52,7 +52,7 @@ afterEach(async () => {
if (original) await invoke("Mouse_MoveToV2", { location: { x: original[0], y: original[1] }, position: "0", move_type: "2", speed: 100 }); if (original) await invoke("Mouse_MoveToV2", { location: { x: original[0], y: original[1] }, position: "0", move_type: "2", speed: 100 });
}); });
describe("移动鼠标组件真实冒烟", () => { describe.skipIf(process.platform !== "win32")("移动鼠标组件真实冒烟", () => {
it("对象坐标 x/y JSON 不再报无效, 瞬间移动真实落位", async () => { it("对象坐标 x/y JSON 不再报无效, 瞬间移动真实落位", async () => {
original = await cursorPos(); original = await cursorPos();
// 用户屏幕框选生成的浮点对象坐标(此前直接报「鼠标坐标无效」) // 用户屏幕框选生成的浮点对象坐标(此前直接报「鼠标坐标无效」)

View File

@ -4,6 +4,7 @@ import path from "node:path";
import { promisify } from "node:util"; import { promisify } from "node:util";
import { register } from "../engine/registry"; import { register } from "../engine/registry";
import { RuntimeError } from "../engine/types"; import { RuntimeError } from "../engine/types";
import { isWindows } from "../driver/platform";
type Table = unknown[][]; type Table = unknown[][];
@ -257,7 +258,7 @@ register("System_ExecuteCommandV1", async (_ctx, node, args) => {
const timeout = Math.max(1_000, Number(args.timeoutMs || 120_000)); const timeout = Math.max(1_000, Number(args.timeoutMs || 120_000));
let executable: string; let executable: string;
let commandArgs: string[]; let commandArgs: string[];
if (process.platform === "win32") { if (isWindows) {
if (mode === "2") { if (mode === "2") {
const file = path.resolve(requiredText(args.bat_file, "BAT文件", node.id, node.componentId)); const file = path.resolve(requiredText(args.bat_file, "BAT文件", node.id, node.componentId));
await fs.access(file); await fs.access(file);
@ -280,7 +281,7 @@ register("System_ExecuteCommandV1", async (_ctx, node, args) => {
commandArgs = ["-lc", command]; commandArgs = ["-lc", command];
} }
// cmd.exe 自己解析 /c 后的命令;不能让 Node 再把 BAT 路径引号转成反斜杠引号。 // cmd.exe 自己解析 /c 后的命令;不能让 Node 再把 BAT 路径引号转成反斜杠引号。
const result = await execFileAsync(executable, commandArgs, { cwd, timeout, windowsHide: true, windowsVerbatimArguments: process.platform === "win32" && mode !== "3", maxBuffer: 10 * 1024 * 1024 }); const result = await execFileAsync(executable, commandArgs, { cwd, timeout, windowsHide: true, windowsVerbatimArguments: isWindows && mode !== "3", maxBuffer: 10 * 1024 * 1024 });
return bool(args.get_ret, true) ? String(result.stdout).trimEnd() : true; return bool(args.get_ret, true) ? String(result.stdout).trimEnd() : true;
}); });

View File

@ -0,0 +1,9 @@
/**
* 平台判断唯一入口(dev-conventions 规则 4 的例外白名单)。
*
* 规范:平台差异只允许出现在两处——`driver/<平台>/` 目录下的驱动实现,以及本文件;
* 能力文件不得直接调用 `process.platform`,需要平台分支时从这里取值。
*/
export const isWindows = process.platform === "win32";
export const isMacOS = process.platform === "darwin";
export const isLinux = process.platform === "linux";