From 46348413c14b49df23fa037f02b23022a98232a7 Mon Sep 17 00:00:00 2001 From: yu <191972707@qq.com> Date: Tue, 3 Nov 2026 16:38:03 +0800 Subject: [PATCH] =?UTF-8?q?=E9=94=AE=E7=9B=98=E8=BE=93=E5=85=A5=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E4=BF=AE=E5=A4=8D:=20=E6=8E=A5=E5=85=A5=20words=20?= =?UTF-8?q?=E5=AD=97=E6=AE=B5=E4=B8=8E=E4=B8=89=E7=A7=8D=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/capabilities/desktop.ts | 16 ++++++- .../capabilities/keyboard-send-smoke.test.ts | 48 +++++++++++++++++++ 2 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 src/main/capabilities/keyboard-send-smoke.test.ts diff --git a/src/main/capabilities/desktop.ts b/src/main/capabilities/desktop.ts index bf98cd1..2d536b2 100644 --- a/src/main/capabilities/desktop.ts +++ b/src/main/capabilities/desktop.ts @@ -99,10 +99,22 @@ export function psString(value: unknown): string { } const keyboardSend: CapabilityHandler = async (_ctx, node, args) => { - const keys = text(args, ["keys", "key", "text", "value"]); + // 字段: words=输入内容(键盘输入), keys/key=按键组合(按键系列), interval=输入间隔(V1毫秒/基础版秒), fuctype=输入方式(0后台接口/1人工模拟/2剪贴板粘贴) + const keys = text(args, ["words", "keys", "key", "text", "value"]); if (!keys) throw new RuntimeError("validation_error", "键盘输入为空", { nodeId: node.id, componentId: node.componentId }); await manualDelay(); - await powershell(`Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait(${psString(keys)})`); + const rawInterval = number(args, ["interval"], 0); + const intervalMs = node.componentId === "Keyboard_Send" ? Math.max(0, Math.round(rawInterval * 1000)) : Math.max(0, Math.round(rawInterval)); + const mode = text(args, ["fuctype"], "0"); + const isTextSend = node.componentId === "Keyboard_Send" || node.componentId === "Keyboard_Send_V1"; + // 文本输入需转义 SendKeys 特殊字符(+^%~(){}[])按字面输入;按键组合组件(Keyboard_Press)与粘贴模式保留原文 + const escape = isTextSend && mode !== "2" ? `$t = $t -replace '[+^%~(){}[\\]]','{$0}'; ` : ""; + const send = mode === "2" + ? `Set-Clipboard -Value $t; Start-Sleep -Milliseconds 120; [System.Windows.Forms.SendKeys]::SendWait('^v')` + : mode === "1" + ? `foreach ($ch in $t.ToCharArray()) { $c = [string]$ch; if ('+^%~(){}[]'.Contains($c)) { $c = '{' + $c + '}' }; [System.Windows.Forms.SendKeys]::SendWait($c); Start-Sleep -Milliseconds ${intervalMs} }` + : `[System.Windows.Forms.SendKeys]::SendWait($t)`; + await powershell(`Add-Type -AssemblyName System.Windows.Forms; $t = ${psString(keys)}; ${escape}${send}`); await manualDelay(); return keys; }; diff --git a/src/main/capabilities/keyboard-send-smoke.test.ts b/src/main/capabilities/keyboard-send-smoke.test.ts new file mode 100644 index 0000000..ebc89c2 --- /dev/null +++ b/src/main/capabilities/keyboard-send-smoke.test.ts @@ -0,0 +1,48 @@ +import { describe, expect, it } from "vitest"; +import type { CatalogComponent, FlowNode } from "../../shared/types"; +import { getHandler } from "../engine/registry"; +import type { RunContext } from "../engine/types"; +import "./desktop"; + +const component = {} as CatalogComponent; +const node = (id: string): FlowNode => ({ id: "smoke-kb", componentId: id, position: { x: 0, y: 0 }, values: {}, options: {} }); + +const ctx = { + runId: "kb-smoke", flowId: "kb-smoke-flow", flow: {} as RunContext["flow"], + vars: {}, secrets: {}, signal: new AbortController().signal, evidenceDir: "D:/office-test2", + emit: () => {}, log: () => {}, componentOf: () => undefined +} as unknown as RunContext; + +async function invoke(componentId: string, args: Record): Promise { + const handler = getHandler(componentId); + expect(handler).toBeTypeOf("function"); + return handler!(ctx, node(componentId), args, component); +} + +describe("键盘输入组件真实冒烟", () => { + it("words 字段取值(此前读不到报「键盘输入为空」), 后台接口模式整段发送特殊字符不抛错", async () => { + const out = await invoke("Keyboard_Send_V1", { words: "哈哈哈(测试)100%^2", interval: "10", fuctype: "0" }); + expect(out).toBe("哈哈哈(测试)100%^2"); + console.log("[键盘输入] 后台接口模式整段发送 ✓:", out); + }, 60000); + + it("人工模拟模式逐字符+间隔毫秒真实发送", async () => { + const out = await invoke("Keyboard_Send_V1", { words: "逐字输入ABC", interval: "5", fuctype: "1" }); + expect(out).toBe("逐字输入ABC"); + console.log("[键盘输入] 人工模拟逐字符 ✓:", out); + }, 60000); + + it("剪切板粘贴模式: 原文进入剪贴板并发送 Ctrl+V", async () => { + const content = `粘贴${Date.now()}`; + const out = await invoke("Keyboard_Send_V1", { words: content, fuctype: "2" }); + expect(out).toBe(content); + const clip = await invoke("Clipboard_GetText", {}) as string; + expect(clip).toBe(content); + console.log("[键盘输入] 剪贴板粘贴模式 ✓ 剪贴板内容一致:", clip); + }, 60000); + + it("空输入仍报「键盘输入为空」", async () => { + await expect(invoke("Keyboard_Send_V1", { words: "", interval: "10" })).rejects.toThrow("键盘输入为空"); + console.log("[键盘输入] 空输入校验 ✓"); + }, 60000); +});