返回 CodeWhale
win32-desktop.test.mjs
根目录 / crates / tui / plugins / computer-use / tests / win32-desktop.test.mjs
1 // Opt in only on a disposable interactive Windows runner. Real UIA and screen
2 // APIs operate on our own WinForms fixture; no user app or raw input is touched.
3 import { test } from 'node:test';
4 import assert from 'node:assert/strict';
5 import fs from 'node:fs';
6 import os from 'node:os';
7 import path from 'node:path';
8 import crypto from 'node:crypto';
9 import { spawn } from 'node:child_process';
10 import { fileURLToPath } from 'node:url';
11 import win32 from '../src/backends/win32.mjs';
12
13 test('Windows desktop: observe, set Unicode value, invoke and independently verify the selected window', {
14 skip: process.platform !== 'win32' || process.env.CU_WINDOWS_DESKTOP_TESTS !== '1', timeout: 90_000,
15 }, async t => {
16 const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'cu-win-desktop-'));
17 const title = `CU fixture ${crypto.randomUUID()}`;
18 const receipt = path.join(dir, 'state.json');
19 const child = spawn('powershell.exe', ['-NoProfile', '-NonInteractive', '-File', fileURLToPath(new URL('./fixtures/windows-desktop.ps1', import.meta.url)), title, receipt], { stdio: ['ignore', 'ignore', 'pipe'], windowsHide: false });
20 let errors = ''; child.stderr.on('data', chunk => { errors += chunk; });
21 const exited = new Promise(resolve => child.once('exit', resolve));
22 t.after(async () => { if (child.exitCode === null) child.kill(); await exited; fs.rmSync(dir, { recursive: true, force: true }); });
23 async function until(check) {
24 const deadline = Date.now() + 15_000;
25 while (Date.now() < deadline) {
26 let result; try { result = check(); } catch {}
27 if (result) return result;
28 assert.equal(child.exitCode, null, errors);
29 await new Promise(resolve => setTimeout(resolve, 100));
30 }
31 assert.fail(`fixture deadline: ${errors}`);
32 }
33 const read = () => JSON.parse(fs.readFileSync(receipt, 'utf8'));
34 await until(() => read().text === 'initial');
35 const backend = win32.create();
36 const displays = await backend.list_displays();
37 assert.ok(displays.length >= 1);
38 t.diagnostic(JSON.stringify({fixture:read(), windows:await backend.list_windows()}));
39 const state = await backend.get_app_state({ app_ref: { name: title }, detail: 'full' });
40 const edit = state.elements.find(e => e.role === 'Edit');
41 const button = state.elements.find(e => e.label === 'Apply fixture');
42 assert.ok(edit, JSON.stringify(state)); assert.ok(button, JSON.stringify(state));
43 assert.equal(edit.value, 'initial');
44 const target = el => ({ ...el, app_ref: { name: title }, windowIndex: 0 });
45 const text = "A漢😀 O'Brien";
46 assert.equal((await backend.set_value({ target: target(edit), value: text })).verified, true);
47 await until(() => read().text === text);
48 await backend.perform_action({ target: target(button), action: 'Invoke' });
49 await until(() => read().clicks === 1);
50 await assert.rejects(backend.set_value({ target: { ...target(edit), runtime_id: [123456789] }, value: 'wrong' }), /element_stale/);
51 await assert.rejects(backend.set_value({ target: { ...target(edit), window_runtime_id: [123456789] }, value: 'wrong' }), /element_stale/);
52 assert.equal(read().text, text); assert.equal(read().clicks, 1);
53 const shot = await backend.screenshot({ display: 1, path: path.join(dir, 'screen.png') });
54 const png = fs.readFileSync(shot.file);
55 assert.equal(png.subarray(1, 4).toString(), 'PNG');
56 assert.equal(png.readUInt32BE(16), shot.pixels.w); assert.equal(png.readUInt32BE(20), shot.pixels.h);
57 assert.deepEqual(shot.points, displays[0].points);
58 const region = [shot.points.x + 1, shot.points.y + 1, 20, 20];
59 const crop = await backend.screenshot({ region, path: path.join(dir, 'crop.png') });
60 assert.deepEqual(crop.points, { x: region[0], y: region[1], w: 20, h: 20 });
61 t.diagnostic('Real WinForms UIA value/invoke/read-back, stale identity refusal, screen and region capture passed; no raw input or packaged Engine claim.');
62 });
63
63 lines Plain Text