@elizaos/computeruse 0.24.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +34 -0
- package/build.rs +10 -0
- package/computeruse.darwin-arm64.node +0 -0
- package/index.d.ts +0 -0
- package/index.js +327 -0
- package/package.json +74 -0
- package/scripts/sync-version.js +60 -0
- package/src/desktop.rs +2763 -0
- package/src/element.rs +1341 -0
- package/src/exceptions.rs +65 -0
- package/src/lib.rs +26 -0
- package/src/locator.rs +172 -0
- package/src/selector.rs +158 -0
- package/src/types.rs +963 -0
- package/src/window_manager.rs +342 -0
- package/tests/comprehensive-ui-elements.test.js +524 -0
- package/tests/cross-app-verification.test.js +243 -0
- package/tests/desktop-verify.test.js +169 -0
- package/tests/element-chaining.test.js +158 -0
- package/tests/element-range.test.js +207 -0
- package/tests/element-scroll-into-view.test.js +256 -0
- package/tests/element-value.test.js +264 -0
- package/tests/execute-browser-script-wrapper.test.js +135 -0
- package/tests/fixtures/sample-browser-script.js +7 -0
- package/tests/fixtures/script-with-env.js +16 -0
- package/tests/locator-validate.test.js +260 -0
- package/tests/locator-waitfor.test.js +286 -0
- package/wrapper.d.ts +84 -0
- package/wrapper.js +344 -0
- package/wrapper.ts +394 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
const { Desktop } = require("../index.js");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test for Locator.waitFor() method with 'exists' condition
|
|
5
|
+
*/
|
|
6
|
+
async function testWaitForExists() {
|
|
7
|
+
console.log("🕐 Testing Locator.waitFor('exists')...");
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const desktop = new Desktop();
|
|
11
|
+
|
|
12
|
+
// Get any available application for testing
|
|
13
|
+
const apps = desktop.applications();
|
|
14
|
+
if (apps.length === 0) {
|
|
15
|
+
throw new Error("No applications found for testing");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const testApp = apps[0];
|
|
19
|
+
console.log(`📱 Testing with app: ${testApp.name()}`);
|
|
20
|
+
|
|
21
|
+
// Test: Wait for a window to exist (should succeed immediately)
|
|
22
|
+
console.log("Test: Wait for window to exist");
|
|
23
|
+
const element = await desktop.locator("role:window").waitFor("exists", 5000);
|
|
24
|
+
|
|
25
|
+
if (!element) {
|
|
26
|
+
throw new Error("Expected element to be returned");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
console.log(`✅ Found window: ${element.name()}`);
|
|
30
|
+
return true;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error("❌ WaitFor exists test failed:", error.message);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Test waitFor() with 'visible' condition
|
|
39
|
+
*/
|
|
40
|
+
async function testWaitForVisible() {
|
|
41
|
+
console.log("🕐 Testing Locator.waitFor('visible')...");
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
const desktop = new Desktop();
|
|
45
|
+
|
|
46
|
+
// Test: Wait for a visible window
|
|
47
|
+
console.log("Test: Wait for window to be visible");
|
|
48
|
+
const element = await desktop.locator("role:window").waitFor("visible", 5000);
|
|
49
|
+
|
|
50
|
+
// Check that the element is actually visible
|
|
51
|
+
if (!element.isVisible()) {
|
|
52
|
+
throw new Error("Element should be visible");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(`✅ Found visible window: ${element.name()}`);
|
|
56
|
+
return true;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error("❌ WaitFor visible test failed:", error.message);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Test waitFor() timeout behavior
|
|
65
|
+
*/
|
|
66
|
+
async function testWaitForTimeout() {
|
|
67
|
+
console.log("🕐 Testing Locator.waitFor() timeout...");
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
const desktop = new Desktop();
|
|
71
|
+
|
|
72
|
+
// Test: Wait for a non-existent element (should timeout)
|
|
73
|
+
console.log("Test: Wait for non-existent element (expecting timeout)");
|
|
74
|
+
let timedOut = false;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
await desktop
|
|
78
|
+
.locator("role:button|ThisButtonDoesNotExist12345XYZ")
|
|
79
|
+
.waitFor("exists", 1000);
|
|
80
|
+
} catch (err) {
|
|
81
|
+
if (err.message.includes("Timed out") || err.message.includes("timeout")) {
|
|
82
|
+
timedOut = true;
|
|
83
|
+
console.log("✅ Correctly timed out");
|
|
84
|
+
} else {
|
|
85
|
+
throw new Error(`Unexpected error: ${err.message}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!timedOut) {
|
|
90
|
+
throw new Error("Expected timeout error");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return true;
|
|
94
|
+
} catch (error) {
|
|
95
|
+
console.error("❌ WaitFor timeout test failed:", error.message);
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Test waitFor() with different conditions
|
|
102
|
+
*/
|
|
103
|
+
async function testWaitForConditions() {
|
|
104
|
+
console.log("🕐 Testing Locator.waitFor() with different conditions...");
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
const desktop = new Desktop();
|
|
108
|
+
|
|
109
|
+
// Test each condition on a window (which should be visible and enabled)
|
|
110
|
+
const conditions = ["exists", "visible", "enabled"];
|
|
111
|
+
|
|
112
|
+
for (const condition of conditions) {
|
|
113
|
+
console.log(`Test: waitFor('${condition}')`);
|
|
114
|
+
const element = await desktop
|
|
115
|
+
.locator("role:window")
|
|
116
|
+
.waitFor(condition, 5000);
|
|
117
|
+
|
|
118
|
+
if (!element) {
|
|
119
|
+
throw new Error(`No element returned for condition '${condition}'`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
console.log(`✅ Condition '${condition}' met`);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return true;
|
|
126
|
+
} catch (error) {
|
|
127
|
+
console.error("❌ WaitFor conditions test failed:", error.message);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Test waitFor() with invalid condition
|
|
134
|
+
*/
|
|
135
|
+
async function testWaitForInvalidCondition() {
|
|
136
|
+
console.log("🕐 Testing Locator.waitFor() with invalid condition...");
|
|
137
|
+
|
|
138
|
+
try {
|
|
139
|
+
const desktop = new Desktop();
|
|
140
|
+
|
|
141
|
+
// Test: Invalid condition should throw error
|
|
142
|
+
console.log("Test: Wait with invalid condition");
|
|
143
|
+
let errorThrown = false;
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await desktop.locator("role:window").waitFor("invalid_condition", 1000);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
if (err.message.includes("Invalid condition")) {
|
|
149
|
+
errorThrown = true;
|
|
150
|
+
console.log("✅ Correctly rejected invalid condition");
|
|
151
|
+
} else {
|
|
152
|
+
throw new Error(`Unexpected error: ${err.message}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if (!errorThrown) {
|
|
157
|
+
throw new Error("Expected error for invalid condition");
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return true;
|
|
161
|
+
} catch (error) {
|
|
162
|
+
console.error("❌ WaitFor invalid condition test failed:", error.message);
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Test waitFor() with chaining
|
|
169
|
+
*/
|
|
170
|
+
async function testWaitForChaining() {
|
|
171
|
+
console.log("🕐 Testing Locator.waitFor() with chaining...");
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const desktop = new Desktop();
|
|
175
|
+
const apps = desktop.applications();
|
|
176
|
+
|
|
177
|
+
if (apps.length === 0) {
|
|
178
|
+
throw new Error("No applications found for testing");
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
const testApp = apps[0];
|
|
182
|
+
|
|
183
|
+
// Test: Wait with chained locator
|
|
184
|
+
console.log("Test: waitFor with chained locator");
|
|
185
|
+
const element = await testApp
|
|
186
|
+
.locator("role:window")
|
|
187
|
+
.waitFor("visible", 3000);
|
|
188
|
+
|
|
189
|
+
console.log(`✅ Found element via chain: ${element.name()}`);
|
|
190
|
+
return true;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
// This might fail if the app has no window, which is acceptable
|
|
193
|
+
if (error.message.includes("Timed out")) {
|
|
194
|
+
console.log("ℹ️ No window found in chain (acceptable)");
|
|
195
|
+
return true;
|
|
196
|
+
}
|
|
197
|
+
console.error("❌ WaitFor chaining test failed:", error.message);
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Main test runner
|
|
204
|
+
*/
|
|
205
|
+
async function runWaitForTests() {
|
|
206
|
+
console.log("🚀 Starting Locator.waitFor() tests...\n");
|
|
207
|
+
|
|
208
|
+
let passed = 0;
|
|
209
|
+
let total = 0;
|
|
210
|
+
|
|
211
|
+
// Test 1: Wait for exists
|
|
212
|
+
total++;
|
|
213
|
+
if (await testWaitForExists()) {
|
|
214
|
+
passed++;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
console.log(); // Empty line
|
|
218
|
+
|
|
219
|
+
// Test 2: Wait for visible
|
|
220
|
+
total++;
|
|
221
|
+
if (await testWaitForVisible()) {
|
|
222
|
+
passed++;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
console.log(); // Empty line
|
|
226
|
+
|
|
227
|
+
// Test 3: Timeout behavior
|
|
228
|
+
total++;
|
|
229
|
+
if (await testWaitForTimeout()) {
|
|
230
|
+
passed++;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log(); // Empty line
|
|
234
|
+
|
|
235
|
+
// Test 4: Different conditions
|
|
236
|
+
total++;
|
|
237
|
+
if (await testWaitForConditions()) {
|
|
238
|
+
passed++;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
console.log(); // Empty line
|
|
242
|
+
|
|
243
|
+
// Test 5: Invalid condition
|
|
244
|
+
total++;
|
|
245
|
+
if (await testWaitForInvalidCondition()) {
|
|
246
|
+
passed++;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
console.log(); // Empty line
|
|
250
|
+
|
|
251
|
+
// Test 6: Chaining
|
|
252
|
+
total++;
|
|
253
|
+
if (await testWaitForChaining()) {
|
|
254
|
+
passed++;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
console.log(); // Empty line
|
|
258
|
+
|
|
259
|
+
// Results
|
|
260
|
+
if (passed === total) {
|
|
261
|
+
console.log(`🎉 All waitFor tests passed! (${passed}/${total})`);
|
|
262
|
+
process.exit(0);
|
|
263
|
+
} else {
|
|
264
|
+
console.log(`❌ Some tests failed: ${passed}/${total} passed`);
|
|
265
|
+
process.exit(1);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Export for use in other test files
|
|
270
|
+
module.exports = {
|
|
271
|
+
testWaitForExists,
|
|
272
|
+
testWaitForVisible,
|
|
273
|
+
testWaitForTimeout,
|
|
274
|
+
testWaitForConditions,
|
|
275
|
+
testWaitForInvalidCondition,
|
|
276
|
+
testWaitForChaining,
|
|
277
|
+
runWaitForTests,
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// Run tests if this file is executed directly
|
|
281
|
+
if (require.main === module) {
|
|
282
|
+
runWaitForTests().catch((error) => {
|
|
283
|
+
console.error("💥 Test runner crashed:", error);
|
|
284
|
+
process.exit(1);
|
|
285
|
+
});
|
|
286
|
+
}
|
package/wrapper.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// Re-export everything from the native bindings
|
|
2
|
+
export * from "./index";
|
|
3
|
+
|
|
4
|
+
/** Thrown when an element is not found. */
|
|
5
|
+
export class ElementNotFoundError extends Error {
|
|
6
|
+
constructor(message: string);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Thrown when an operation times out. */
|
|
10
|
+
export class TimeoutError extends Error {
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Thrown when permission is denied. */
|
|
15
|
+
export class PermissionDeniedError extends Error {
|
|
16
|
+
constructor(message: string);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Thrown for platform-specific errors. */
|
|
20
|
+
export class PlatformError extends Error {
|
|
21
|
+
constructor(message: string);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Thrown for unsupported operations. */
|
|
25
|
+
export class UnsupportedOperationError extends Error {
|
|
26
|
+
constructor(message: string);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Thrown for unsupported platforms. */
|
|
30
|
+
export class UnsupportedPlatformError extends Error {
|
|
31
|
+
constructor(message: string);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Thrown for invalid arguments. */
|
|
35
|
+
export class InvalidArgumentError extends Error {
|
|
36
|
+
constructor(message: string);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Thrown for internal errors. */
|
|
40
|
+
export class InternalError extends Error {
|
|
41
|
+
constructor(message: string);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Browser script execution types
|
|
45
|
+
export type BrowserScriptEnv = Record<string, unknown>;
|
|
46
|
+
export type BrowserScriptFunction<
|
|
47
|
+
T = unknown,
|
|
48
|
+
Env extends BrowserScriptEnv = BrowserScriptEnv,
|
|
49
|
+
> = (env: Env) => T | Promise<T>;
|
|
50
|
+
export interface BrowserScriptOptions<
|
|
51
|
+
Env extends BrowserScriptEnv = BrowserScriptEnv,
|
|
52
|
+
> {
|
|
53
|
+
file: string;
|
|
54
|
+
env?: Env;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Augment Desktop class with browser script methods
|
|
58
|
+
declare module "./index.d" {
|
|
59
|
+
interface Desktop {
|
|
60
|
+
executeBrowserScript<
|
|
61
|
+
T = unknown,
|
|
62
|
+
Env extends BrowserScriptEnv = BrowserScriptEnv,
|
|
63
|
+
>(
|
|
64
|
+
fn: BrowserScriptFunction<T, Env>,
|
|
65
|
+
env?: Env,
|
|
66
|
+
): Promise<T>;
|
|
67
|
+
executeBrowserScript<Env extends BrowserScriptEnv = BrowserScriptEnv>(
|
|
68
|
+
options: BrowserScriptOptions<Env>,
|
|
69
|
+
): Promise<string>;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
interface Element {
|
|
73
|
+
executeBrowserScript<
|
|
74
|
+
T = unknown,
|
|
75
|
+
Env extends BrowserScriptEnv = BrowserScriptEnv,
|
|
76
|
+
>(
|
|
77
|
+
fn: BrowserScriptFunction<T, Env>,
|
|
78
|
+
env?: Env,
|
|
79
|
+
): Promise<T>;
|
|
80
|
+
executeBrowserScript<Env extends BrowserScriptEnv = BrowserScriptEnv>(
|
|
81
|
+
options: BrowserScriptOptions<Env>,
|
|
82
|
+
): Promise<string>;
|
|
83
|
+
}
|
|
84
|
+
}
|
package/wrapper.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
var wrapper_exports = {};
|
|
30
|
+
__export(wrapper_exports, {
|
|
31
|
+
ClickType: () => import_index.ClickType,
|
|
32
|
+
ClusteredBoundsEntry: () => import_index.ClusteredBoundsEntry,
|
|
33
|
+
ClusteredFormattingResult: () => import_index.ClusteredFormattingResult,
|
|
34
|
+
Desktop: () => Desktop,
|
|
35
|
+
Element: () => Element,
|
|
36
|
+
ElementNotFoundError: () => ElementNotFoundError,
|
|
37
|
+
ElementSource: () => import_index.ElementSource,
|
|
38
|
+
InternalError: () => InternalError,
|
|
39
|
+
InvalidArgumentError: () => InvalidArgumentError,
|
|
40
|
+
Locator: () => Locator,
|
|
41
|
+
PermissionDeniedError: () => PermissionDeniedError,
|
|
42
|
+
PlatformError: () => PlatformError,
|
|
43
|
+
PropertyLoadingMode: () => import_index.PropertyLoadingMode,
|
|
44
|
+
Selector: () => Selector,
|
|
45
|
+
TextPosition: () => import_index.TextPosition,
|
|
46
|
+
TimeoutError: () => TimeoutError,
|
|
47
|
+
TreeOutputFormat: () => import_index.TreeOutputFormat,
|
|
48
|
+
UnsupportedOperationError: () => UnsupportedOperationError,
|
|
49
|
+
UnsupportedPlatformError: () => UnsupportedPlatformError,
|
|
50
|
+
VisionType: () => import_index.VisionType,
|
|
51
|
+
WindowManager: () => WindowManager
|
|
52
|
+
});
|
|
53
|
+
module.exports = __toCommonJS(wrapper_exports);
|
|
54
|
+
var native = __toESM(require("./index.js"));
|
|
55
|
+
var util = __toESM(require("util"));
|
|
56
|
+
var fs = __toESM(require("fs"));
|
|
57
|
+
var path = __toESM(require("path"));
|
|
58
|
+
var import_index = require("./index.js");
|
|
59
|
+
function patchInspector(Klass, methodName = "toString", forcePlainObject = false) {
|
|
60
|
+
if (!Klass || typeof Klass !== "function") {
|
|
61
|
+
console.log("inspect not a function");
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
const proto = Klass.prototype;
|
|
65
|
+
const original = proto[util.inspect.custom];
|
|
66
|
+
proto[util.inspect.custom] = function(...args) {
|
|
67
|
+
if (typeof this[methodName] === "function") {
|
|
68
|
+
const result = this[methodName](...args);
|
|
69
|
+
if (forcePlainObject && result && typeof result === "object") {
|
|
70
|
+
return { ...result };
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
if (typeof original === "function") {
|
|
75
|
+
return original.apply(this, args);
|
|
76
|
+
}
|
|
77
|
+
return { ...this };
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function wrapNativeFunction(fn) {
|
|
81
|
+
if (typeof fn !== "function") return fn;
|
|
82
|
+
return function(...args) {
|
|
83
|
+
try {
|
|
84
|
+
const result = fn.apply(this, args);
|
|
85
|
+
if (result instanceof Promise) {
|
|
86
|
+
return result.catch((error) => {
|
|
87
|
+
throw mapNativeError(error);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
return result;
|
|
91
|
+
} catch (error) {
|
|
92
|
+
throw mapNativeError(error);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
function wrapClassMethods(Class) {
|
|
97
|
+
const prototype = Class.prototype;
|
|
98
|
+
const methods = Object.getOwnPropertyNames(prototype);
|
|
99
|
+
methods.forEach((method) => {
|
|
100
|
+
if (method !== "constructor" && typeof prototype[method] === "function") {
|
|
101
|
+
prototype[method] = wrapNativeFunction(prototype[method]);
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
return Class;
|
|
105
|
+
}
|
|
106
|
+
function wrapClass(Class, ...inspectOptions) {
|
|
107
|
+
const Wrapped = wrapClassMethods(Class);
|
|
108
|
+
patchInspector(Wrapped, ...inspectOptions || []);
|
|
109
|
+
return Wrapped;
|
|
110
|
+
}
|
|
111
|
+
class ElementNotFoundError extends Error {
|
|
112
|
+
constructor(message) {
|
|
113
|
+
super(message);
|
|
114
|
+
this.name = "ElementNotFoundError";
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
class TimeoutError extends Error {
|
|
118
|
+
constructor(message) {
|
|
119
|
+
super(message);
|
|
120
|
+
this.name = "TimeoutError";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
class PermissionDeniedError extends Error {
|
|
124
|
+
constructor(message) {
|
|
125
|
+
super(message);
|
|
126
|
+
this.name = "PermissionDeniedError";
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
class PlatformError extends Error {
|
|
130
|
+
constructor(message) {
|
|
131
|
+
super(message);
|
|
132
|
+
this.name = "PlatformError";
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
class UnsupportedOperationError extends Error {
|
|
136
|
+
constructor(message) {
|
|
137
|
+
super(message);
|
|
138
|
+
this.name = "UnsupportedOperationError";
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
class UnsupportedPlatformError extends Error {
|
|
142
|
+
constructor(message) {
|
|
143
|
+
super(message);
|
|
144
|
+
this.name = "UnsupportedPlatformError";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
class InvalidArgumentError extends Error {
|
|
148
|
+
constructor(message) {
|
|
149
|
+
super(message);
|
|
150
|
+
this.name = "InvalidArgumentError";
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
class InternalError extends Error {
|
|
154
|
+
constructor(message) {
|
|
155
|
+
super(message);
|
|
156
|
+
this.name = "InternalError";
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
function mapNativeError(error) {
|
|
160
|
+
if (!error.message) return error;
|
|
161
|
+
const message = error.message;
|
|
162
|
+
if (message.startsWith("ELEMENT_NOT_FOUND:")) {
|
|
163
|
+
return new ElementNotFoundError(
|
|
164
|
+
message.replace("ELEMENT_NOT_FOUND:", "").trim()
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
if (message.startsWith("OPERATION_TIMED_OUT:")) {
|
|
168
|
+
return new TimeoutError(message.replace("OPERATION_TIMED_OUT:", "").trim());
|
|
169
|
+
}
|
|
170
|
+
if (message.startsWith("PERMISSION_DENIED:")) {
|
|
171
|
+
return new PermissionDeniedError(
|
|
172
|
+
message.replace("PERMISSION_DENIED:", "").trim()
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (message.startsWith("PLATFORM_ERROR:")) {
|
|
176
|
+
return new PlatformError(message.replace("PLATFORM_ERROR:", "").trim());
|
|
177
|
+
}
|
|
178
|
+
if (message.startsWith("UNSUPPORTED_OPERATION:")) {
|
|
179
|
+
return new UnsupportedOperationError(
|
|
180
|
+
message.replace("UNSUPPORTED_OPERATION:", "").trim()
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
if (message.startsWith("UNSUPPORTED_PLATFORM:")) {
|
|
184
|
+
return new UnsupportedPlatformError(
|
|
185
|
+
message.replace("UNSUPPORTED_PLATFORM:", "").trim()
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
if (message.startsWith("INVALID_ARGUMENT:")) {
|
|
189
|
+
return new InvalidArgumentError(
|
|
190
|
+
message.replace("INVALID_ARGUMENT:", "").trim()
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
if (message.startsWith("INTERNAL_ERROR:")) {
|
|
194
|
+
return new InternalError(message.replace("INTERNAL_ERROR:", "").trim());
|
|
195
|
+
}
|
|
196
|
+
return error;
|
|
197
|
+
}
|
|
198
|
+
async function enhancedExecuteBrowserScript(scriptOrFunction, processOrEnv, timeoutMs) {
|
|
199
|
+
const isDesktop = this.constructor?.name === "Desktop";
|
|
200
|
+
let process;
|
|
201
|
+
let envOrOptions;
|
|
202
|
+
if (isDesktop) {
|
|
203
|
+
if (typeof processOrEnv !== "string") {
|
|
204
|
+
throw new Error(
|
|
205
|
+
"Desktop.executeBrowserScript requires 'process' as second argument (e.g., 'chrome', 'msedge', 'firefox'). Usage: desktop.executeBrowserScript(script, 'chrome', timeoutMs?)"
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
process = processOrEnv;
|
|
209
|
+
} else {
|
|
210
|
+
envOrOptions = processOrEnv;
|
|
211
|
+
}
|
|
212
|
+
let script;
|
|
213
|
+
let env = {};
|
|
214
|
+
let shouldInjectEnv = false;
|
|
215
|
+
if (typeof scriptOrFunction === "string") {
|
|
216
|
+
if (scriptOrFunction.endsWith(".ts") || scriptOrFunction.endsWith(".js")) {
|
|
217
|
+
const filePath = path.resolve(scriptOrFunction);
|
|
218
|
+
if (!fs.existsSync(filePath)) {
|
|
219
|
+
throw new Error(`Browser script file not found: ${filePath}`);
|
|
220
|
+
}
|
|
221
|
+
let fileContent = fs.readFileSync(filePath, "utf-8");
|
|
222
|
+
if (filePath.endsWith(".ts")) {
|
|
223
|
+
try {
|
|
224
|
+
const esbuild = require("esbuild");
|
|
225
|
+
const result = await esbuild.transform(fileContent, {
|
|
226
|
+
loader: "ts",
|
|
227
|
+
target: "es2020",
|
|
228
|
+
format: "iife"
|
|
229
|
+
});
|
|
230
|
+
fileContent = result.code;
|
|
231
|
+
} catch (e) {
|
|
232
|
+
console.warn(
|
|
233
|
+
"esbuild not found - using TypeScript file as-is:",
|
|
234
|
+
e.message
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
script = fileContent;
|
|
239
|
+
env = envOrOptions || {};
|
|
240
|
+
shouldInjectEnv = true;
|
|
241
|
+
} else {
|
|
242
|
+
script = scriptOrFunction;
|
|
243
|
+
}
|
|
244
|
+
} else if (typeof scriptOrFunction === "function") {
|
|
245
|
+
const funcString = scriptOrFunction.toString();
|
|
246
|
+
env = envOrOptions || {};
|
|
247
|
+
script = `
|
|
248
|
+
(async function() {
|
|
249
|
+
const fn = ${funcString};
|
|
250
|
+
const result = await fn(${JSON.stringify(env)});
|
|
251
|
+
|
|
252
|
+
// Auto-stringify result if it's an object
|
|
253
|
+
if (result !== undefined && result !== null) {
|
|
254
|
+
if (typeof result === 'object') {
|
|
255
|
+
return JSON.stringify(result);
|
|
256
|
+
}
|
|
257
|
+
return String(result);
|
|
258
|
+
}
|
|
259
|
+
return "undefined";
|
|
260
|
+
})()
|
|
261
|
+
`;
|
|
262
|
+
} else if (typeof scriptOrFunction === "object" && scriptOrFunction.file) {
|
|
263
|
+
const options = scriptOrFunction;
|
|
264
|
+
const filePath = path.resolve(options.file);
|
|
265
|
+
if (!fs.existsSync(filePath)) {
|
|
266
|
+
throw new Error(`Browser script file not found: ${filePath}`);
|
|
267
|
+
}
|
|
268
|
+
let fileContent = fs.readFileSync(filePath, "utf-8");
|
|
269
|
+
if (filePath.endsWith(".ts")) {
|
|
270
|
+
try {
|
|
271
|
+
const esbuild = require("esbuild");
|
|
272
|
+
const result = await esbuild.transform(fileContent, {
|
|
273
|
+
loader: "ts",
|
|
274
|
+
target: "es2020",
|
|
275
|
+
format: "iife"
|
|
276
|
+
});
|
|
277
|
+
fileContent = result.code;
|
|
278
|
+
} catch (e) {
|
|
279
|
+
console.warn(
|
|
280
|
+
"esbuild not found - using TypeScript file as-is:",
|
|
281
|
+
e.message
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
script = fileContent;
|
|
286
|
+
env = options.env || {};
|
|
287
|
+
shouldInjectEnv = true;
|
|
288
|
+
} else {
|
|
289
|
+
throw new Error(
|
|
290
|
+
"Invalid argument to executeBrowserScript: expected string, function, or {file, env} object"
|
|
291
|
+
);
|
|
292
|
+
}
|
|
293
|
+
if (shouldInjectEnv && env && Object.keys(env).length > 0) {
|
|
294
|
+
const envObject = `const env = ${JSON.stringify(env)};`;
|
|
295
|
+
script = `${envObject}
|
|
296
|
+
${script}`;
|
|
297
|
+
}
|
|
298
|
+
const resultStr = isDesktop ? await this._originalExecuteBrowserScript(script, process, timeoutMs) : await this._originalExecuteBrowserScript(script);
|
|
299
|
+
if (typeof scriptOrFunction === "function") {
|
|
300
|
+
try {
|
|
301
|
+
return JSON.parse(resultStr);
|
|
302
|
+
} catch (e) {
|
|
303
|
+
return resultStr;
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
return resultStr;
|
|
307
|
+
}
|
|
308
|
+
const Desktop = wrapClassMethods(native.Desktop);
|
|
309
|
+
const Element = wrapClass(native.Element);
|
|
310
|
+
const Locator = wrapClass(native.Locator);
|
|
311
|
+
const Selector = wrapClass(native.Selector);
|
|
312
|
+
const WindowManager = wrapClassMethods(native.WindowManager);
|
|
313
|
+
if (Desktop.prototype.executeBrowserScript) {
|
|
314
|
+
Desktop.prototype._originalExecuteBrowserScript = Desktop.prototype.executeBrowserScript;
|
|
315
|
+
Desktop.prototype.executeBrowserScript = enhancedExecuteBrowserScript;
|
|
316
|
+
}
|
|
317
|
+
if (Element.prototype.executeBrowserScript) {
|
|
318
|
+
Element.prototype._originalExecuteBrowserScript = Element.prototype.executeBrowserScript;
|
|
319
|
+
Element.prototype.executeBrowserScript = enhancedExecuteBrowserScript;
|
|
320
|
+
}
|
|
321
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
322
|
+
0 && (module.exports = {
|
|
323
|
+
ClickType,
|
|
324
|
+
ClusteredBoundsEntry,
|
|
325
|
+
ClusteredFormattingResult,
|
|
326
|
+
Desktop,
|
|
327
|
+
Element,
|
|
328
|
+
ElementNotFoundError,
|
|
329
|
+
ElementSource,
|
|
330
|
+
InternalError,
|
|
331
|
+
InvalidArgumentError,
|
|
332
|
+
Locator,
|
|
333
|
+
PermissionDeniedError,
|
|
334
|
+
PlatformError,
|
|
335
|
+
PropertyLoadingMode,
|
|
336
|
+
Selector,
|
|
337
|
+
TextPosition,
|
|
338
|
+
TimeoutError,
|
|
339
|
+
TreeOutputFormat,
|
|
340
|
+
UnsupportedOperationError,
|
|
341
|
+
UnsupportedPlatformError,
|
|
342
|
+
VisionType,
|
|
343
|
+
WindowManager
|
|
344
|
+
});
|