@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,207 @@
|
|
|
1
|
+
const { Desktop } = require("../index.js");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test for Element.getRangeValue() and setRangeValue() methods
|
|
5
|
+
*
|
|
6
|
+
* Note: This test requires an application with slider controls.
|
|
7
|
+
* Common apps with sliders: Volume mixer, Media players, Settings apps
|
|
8
|
+
*/
|
|
9
|
+
async function testRangeValue() {
|
|
10
|
+
console.log("đī¸ Testing Element range value methods...");
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const desktop = new Desktop();
|
|
14
|
+
|
|
15
|
+
// Try to find a slider control in any running application
|
|
16
|
+
console.log("Searching for slider controls...");
|
|
17
|
+
|
|
18
|
+
// Common roles for range controls: slider, scrollbar, progressbar
|
|
19
|
+
const sliderLocator = desktop.locator("role:slider");
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const slider = await sliderLocator.first(2000);
|
|
23
|
+
console.log(`â
Found slider: ${slider.name() || '(unnamed)'}`);
|
|
24
|
+
|
|
25
|
+
// Test: Get current value
|
|
26
|
+
console.log("Test: Get current range value");
|
|
27
|
+
const currentValue = slider.getRangeValue();
|
|
28
|
+
console.log(` Current value: ${currentValue}`);
|
|
29
|
+
|
|
30
|
+
if (typeof currentValue !== 'number') {
|
|
31
|
+
throw new Error(`Expected number, got ${typeof currentValue}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Test: Set a new value (try to set it to 50 if current value is different)
|
|
35
|
+
console.log("Test: Set range value");
|
|
36
|
+
const targetValue = currentValue !== 50 ? 50 : 75;
|
|
37
|
+
slider.setRangeValue(targetValue);
|
|
38
|
+
console.log(` Set value to: ${targetValue}`);
|
|
39
|
+
|
|
40
|
+
// Wait a bit for the UI to update
|
|
41
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
42
|
+
|
|
43
|
+
// Verify the value changed (allow small tolerance for rounding)
|
|
44
|
+
const newValue = slider.getRangeValue();
|
|
45
|
+
console.log(` New value: ${newValue}`);
|
|
46
|
+
|
|
47
|
+
const tolerance = 5;
|
|
48
|
+
if (Math.abs(newValue - targetValue) > tolerance) {
|
|
49
|
+
console.log(` â ī¸ Value changed but not exactly to target (tolerance: ${tolerance})`);
|
|
50
|
+
} else {
|
|
51
|
+
console.log(` â
Value set successfully`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Restore original value
|
|
55
|
+
slider.setRangeValue(currentValue);
|
|
56
|
+
console.log(` Restored original value: ${currentValue}`);
|
|
57
|
+
|
|
58
|
+
return true;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error.message && error.message.includes("Timed out")) {
|
|
61
|
+
console.log("âšī¸ No slider found - this is acceptable if no apps with sliders are running");
|
|
62
|
+
console.log(" Try running: Volume mixer, Windows Media Player, or Settings app");
|
|
63
|
+
return true; // Not a test failure, just no suitable UI available
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
} catch (error) {
|
|
68
|
+
console.error("â Range value test failed:", error.message);
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Test range value with scrollbar (another type of range control)
|
|
75
|
+
*/
|
|
76
|
+
async function testScrollbarRange() {
|
|
77
|
+
console.log("đī¸ Testing scrollbar range values...");
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const desktop = new Desktop();
|
|
81
|
+
|
|
82
|
+
// Try to find a scrollbar
|
|
83
|
+
console.log("Searching for scrollbar controls...");
|
|
84
|
+
const scrollbarLocator = desktop.locator("role:scrollbar");
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const scrollbar = await scrollbarLocator.first(2000);
|
|
88
|
+
console.log(`â
Found scrollbar: ${scrollbar.name() || '(unnamed)'}`);
|
|
89
|
+
|
|
90
|
+
// Just verify we can read the value (don't modify scrollbars as it affects UI)
|
|
91
|
+
const scrollValue = scrollbar.getRangeValue();
|
|
92
|
+
console.log(` Scrollbar position: ${scrollValue}`);
|
|
93
|
+
|
|
94
|
+
if (typeof scrollValue !== 'number') {
|
|
95
|
+
throw new Error(`Expected number, got ${typeof scrollValue}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
console.log("â
Successfully read scrollbar value");
|
|
99
|
+
return true;
|
|
100
|
+
} catch (error) {
|
|
101
|
+
if (error.message && error.message.includes("Timed out")) {
|
|
102
|
+
console.log("âšī¸ No scrollbar found - this is acceptable");
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
throw error;
|
|
106
|
+
}
|
|
107
|
+
} catch (error) {
|
|
108
|
+
console.error("â Scrollbar range test failed:", error.message);
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Test error handling for non-range elements
|
|
115
|
+
*/
|
|
116
|
+
async function testRangeValueError() {
|
|
117
|
+
console.log("đī¸ Testing range value error handling...");
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const desktop = new Desktop();
|
|
121
|
+
|
|
122
|
+
// Try to get range value from a non-range element (like a button)
|
|
123
|
+
console.log("Test: Get range value from non-range element");
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const button = await desktop.locator("role:button").first(2000);
|
|
127
|
+
|
|
128
|
+
// This should throw an error or return a default value
|
|
129
|
+
try {
|
|
130
|
+
const rangeValue = button.getRangeValue();
|
|
131
|
+
console.log(` â ī¸ Got value from non-range element: ${rangeValue} (unexpected but not fatal)`);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
console.log(` â
Correctly threw error: ${err.message}`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return true;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (error.message && error.message.includes("Timed out")) {
|
|
139
|
+
console.log("âšī¸ No button found for error test");
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error("â Range value error test failed:", error.message);
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Main test runner
|
|
152
|
+
*/
|
|
153
|
+
async function runRangeValueTests() {
|
|
154
|
+
console.log("đ Starting Element range value tests...\n");
|
|
155
|
+
|
|
156
|
+
let passed = 0;
|
|
157
|
+
let total = 0;
|
|
158
|
+
|
|
159
|
+
// Test 1: Basic range value get/set
|
|
160
|
+
total++;
|
|
161
|
+
if (await testRangeValue()) {
|
|
162
|
+
passed++;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
console.log(); // Empty line
|
|
166
|
+
|
|
167
|
+
// Test 2: Scrollbar range
|
|
168
|
+
total++;
|
|
169
|
+
if (await testScrollbarRange()) {
|
|
170
|
+
passed++;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
console.log(); // Empty line
|
|
174
|
+
|
|
175
|
+
// Test 3: Error handling
|
|
176
|
+
total++;
|
|
177
|
+
if (await testRangeValueError()) {
|
|
178
|
+
passed++;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
console.log(); // Empty line
|
|
182
|
+
|
|
183
|
+
// Results
|
|
184
|
+
if (passed === total) {
|
|
185
|
+
console.log(`đ All range value tests passed! (${passed}/${total})`);
|
|
186
|
+
process.exit(0);
|
|
187
|
+
} else {
|
|
188
|
+
console.log(`â Some tests failed: ${passed}/${total} passed`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Export for use in other test files
|
|
194
|
+
module.exports = {
|
|
195
|
+
testRangeValue,
|
|
196
|
+
testScrollbarRange,
|
|
197
|
+
testRangeValueError,
|
|
198
|
+
runRangeValueTests,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// Run tests if this file is executed directly
|
|
202
|
+
if (require.main === module) {
|
|
203
|
+
runRangeValueTests().catch((error) => {
|
|
204
|
+
console.error("đĨ Test runner crashed:", error);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
const { Desktop } = require("../index.js");
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test for Element.scrollIntoView() method
|
|
5
|
+
*
|
|
6
|
+
* Note: This test requires an application with scrollable content and off-screen elements.
|
|
7
|
+
* Common apps: Browser with long pages, File Explorer, Settings with long lists
|
|
8
|
+
*/
|
|
9
|
+
async function testScrollIntoView() {
|
|
10
|
+
console.log("đ Testing Element.scrollIntoView()...");
|
|
11
|
+
|
|
12
|
+
try {
|
|
13
|
+
const desktop = new Desktop();
|
|
14
|
+
|
|
15
|
+
// Try to find a window with scrollable content
|
|
16
|
+
console.log("Searching for scrollable windows...");
|
|
17
|
+
|
|
18
|
+
const windowLocator = desktop.locator("role:window");
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const window = await windowLocator.first(2000);
|
|
22
|
+
console.log(`â
Found window: ${window.name()}`);
|
|
23
|
+
|
|
24
|
+
// Try to find a scrollbar (indicates scrollable content)
|
|
25
|
+
const scrollbarLocator = window.locator("role:scrollbar");
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
const scrollbar = await scrollbarLocator.first(1000);
|
|
29
|
+
console.log(` Found scrollbar - window has scrollable content`);
|
|
30
|
+
|
|
31
|
+
// Try to find any element within the window
|
|
32
|
+
const elementLocator = window.locator("role:button|role:text|role:link");
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
const elements = await elementLocator.all(2000, 3);
|
|
36
|
+
|
|
37
|
+
if (elements.length > 0) {
|
|
38
|
+
// Pick an element (preferably not the first one)
|
|
39
|
+
const targetElement = elements.length > 5 ? elements[5] : elements[0];
|
|
40
|
+
console.log(` Testing with element: ${targetElement.name() || '(unnamed)'}`);
|
|
41
|
+
|
|
42
|
+
// Test: Scroll into view
|
|
43
|
+
console.log("Test: Scroll element into view");
|
|
44
|
+
targetElement.scrollIntoView();
|
|
45
|
+
console.log(` â
scrollIntoView() executed successfully`);
|
|
46
|
+
|
|
47
|
+
// Wait a bit for scroll animation
|
|
48
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
49
|
+
|
|
50
|
+
// Verify element is visible
|
|
51
|
+
if (targetElement.isVisible()) {
|
|
52
|
+
console.log(` â
Element is visible after scroll`);
|
|
53
|
+
} else {
|
|
54
|
+
console.log(` â ī¸ Element may not be fully visible (acceptable for some UIs)`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return true;
|
|
58
|
+
} else {
|
|
59
|
+
console.log("âšī¸ No suitable elements found for scrolling test");
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.log(` âšī¸ Could not find elements for scroll test: ${error.message}`);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
} catch (error) {
|
|
67
|
+
console.log(` âšī¸ No scrollbar found - window may not have scrollable content`);
|
|
68
|
+
return true;
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
if (error.message && error.message.includes("Timed out")) {
|
|
72
|
+
console.log("âšī¸ No window found - this is acceptable");
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
} catch (error) {
|
|
78
|
+
console.error("â scrollIntoView test failed:", error.message);
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Test scrollIntoView with already visible element
|
|
85
|
+
*/
|
|
86
|
+
async function testScrollIntoViewAlreadyVisible() {
|
|
87
|
+
console.log("đ Testing scrollIntoView with already visible element...");
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const desktop = new Desktop();
|
|
91
|
+
|
|
92
|
+
// Find any visible window
|
|
93
|
+
console.log("Test: Scroll already visible element");
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const window = await desktop.locator("role:window").first(2000);
|
|
97
|
+
|
|
98
|
+
// The window itself should be visible
|
|
99
|
+
if (window.isVisible()) {
|
|
100
|
+
// Call scrollIntoView on already visible element (should be a no-op)
|
|
101
|
+
window.scrollIntoView();
|
|
102
|
+
console.log(` â
scrollIntoView() on visible element completed`);
|
|
103
|
+
return true;
|
|
104
|
+
} else {
|
|
105
|
+
console.log(` âšī¸ Window not visible for test`);
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.log(` âšī¸ No window found for visible element test`);
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
console.error("â scrollIntoView visible element test failed:", error.message);
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Test scrollIntoView multiple times (should be idempotent)
|
|
120
|
+
*/
|
|
121
|
+
async function testScrollIntoViewIdempotent() {
|
|
122
|
+
console.log("đ Testing scrollIntoView idempotence...");
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const desktop = new Desktop();
|
|
126
|
+
|
|
127
|
+
console.log("Test: Call scrollIntoView multiple times");
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const button = await desktop.locator("role:button").first(2000);
|
|
131
|
+
|
|
132
|
+
// Call scrollIntoView multiple times
|
|
133
|
+
button.scrollIntoView();
|
|
134
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
135
|
+
|
|
136
|
+
button.scrollIntoView();
|
|
137
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
138
|
+
|
|
139
|
+
button.scrollIntoView();
|
|
140
|
+
|
|
141
|
+
console.log(` â
Multiple scrollIntoView calls completed successfully`);
|
|
142
|
+
return true;
|
|
143
|
+
} catch (error) {
|
|
144
|
+
if (error.message && error.message.includes("Timed out")) {
|
|
145
|
+
console.log(` âšī¸ No button found for idempotence test`);
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
throw error;
|
|
149
|
+
}
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.error("â scrollIntoView idempotence test failed:", error.message);
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Test scrollIntoView before click (common use case)
|
|
158
|
+
*/
|
|
159
|
+
async function testScrollIntoViewBeforeClick() {
|
|
160
|
+
console.log("đ Testing scrollIntoView before click pattern...");
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
const desktop = new Desktop();
|
|
164
|
+
|
|
165
|
+
console.log("Test: Scroll into view then click");
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const button = await desktop.locator("role:button").first(2000);
|
|
169
|
+
|
|
170
|
+
// Common pattern: ensure element is visible before clicking
|
|
171
|
+
button.scrollIntoView();
|
|
172
|
+
await new Promise(resolve => setTimeout(resolve, 300));
|
|
173
|
+
|
|
174
|
+
// We won't actually click (to avoid disrupting UI), just verify pattern works
|
|
175
|
+
console.log(` â
scrollIntoView before click pattern works`);
|
|
176
|
+
return true;
|
|
177
|
+
} catch (error) {
|
|
178
|
+
if (error.message && error.message.includes("Timed out")) {
|
|
179
|
+
console.log(` âšī¸ No button found for click pattern test`);
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.error("â scrollIntoView before click test failed:", error.message);
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Main test runner
|
|
192
|
+
*/
|
|
193
|
+
async function runScrollIntoViewTests() {
|
|
194
|
+
console.log("đ Starting Element.scrollIntoView() tests...\n");
|
|
195
|
+
|
|
196
|
+
let passed = 0;
|
|
197
|
+
let total = 0;
|
|
198
|
+
|
|
199
|
+
// Test 1: Basic scroll into view
|
|
200
|
+
total++;
|
|
201
|
+
if (await testScrollIntoView()) {
|
|
202
|
+
passed++;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
console.log(); // Empty line
|
|
206
|
+
|
|
207
|
+
// Test 2: Already visible element
|
|
208
|
+
total++;
|
|
209
|
+
if (await testScrollIntoViewAlreadyVisible()) {
|
|
210
|
+
passed++;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
console.log(); // Empty line
|
|
214
|
+
|
|
215
|
+
// Test 3: Idempotence
|
|
216
|
+
total++;
|
|
217
|
+
if (await testScrollIntoViewIdempotent()) {
|
|
218
|
+
passed++;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
console.log(); // Empty line
|
|
222
|
+
|
|
223
|
+
// Test 4: Before click pattern
|
|
224
|
+
total++;
|
|
225
|
+
if (await testScrollIntoViewBeforeClick()) {
|
|
226
|
+
passed++;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
console.log(); // Empty line
|
|
230
|
+
|
|
231
|
+
// Results
|
|
232
|
+
if (passed === total) {
|
|
233
|
+
console.log(`đ All scrollIntoView tests passed! (${passed}/${total})`);
|
|
234
|
+
process.exit(0);
|
|
235
|
+
} else {
|
|
236
|
+
console.log(`â Some tests failed: ${passed}/${total} passed`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Export for use in other test files
|
|
242
|
+
module.exports = {
|
|
243
|
+
testScrollIntoView,
|
|
244
|
+
testScrollIntoViewAlreadyVisible,
|
|
245
|
+
testScrollIntoViewIdempotent,
|
|
246
|
+
testScrollIntoViewBeforeClick,
|
|
247
|
+
runScrollIntoViewTests,
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
// Run tests if this file is executed directly
|
|
251
|
+
if (require.main === module) {
|
|
252
|
+
runScrollIntoViewTests().catch((error) => {
|
|
253
|
+
console.error("đĨ Test runner crashed:", error);
|
|
254
|
+
process.exit(1);
|
|
255
|
+
});
|
|
256
|
+
}
|