@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,524 @@
|
|
|
1
|
+
const { Desktop } = require('../index.js');
|
|
2
|
+
|
|
3
|
+
async function testUIElements() {
|
|
4
|
+
const desktop = new Desktop();
|
|
5
|
+
console.log('=== Comprehensive UI Elements Test ===\n');
|
|
6
|
+
|
|
7
|
+
// Array to track highlight handles for cleanup
|
|
8
|
+
const highlights = [];
|
|
9
|
+
|
|
10
|
+
// Test 1: Navigate to about:blank for test page
|
|
11
|
+
console.log('Step 1: Navigating to about:blank...');
|
|
12
|
+
const window = desktop.navigateBrowser('about:blank', 'Chrome');
|
|
13
|
+
console.log('✓ Navigated to:', window.name());
|
|
14
|
+
|
|
15
|
+
// Highlight the browser window
|
|
16
|
+
const windowHighlight = window.highlight(0x00FF00, 5000, 'Chrome Window', 'TopLeft');
|
|
17
|
+
highlights.push(windowHighlight);
|
|
18
|
+
console.log('✓ Browser window highlighted (green)');
|
|
19
|
+
await desktop.delay(3000);
|
|
20
|
+
|
|
21
|
+
// Ensure browser has focus
|
|
22
|
+
await window.click();
|
|
23
|
+
await desktop.delay(1000);
|
|
24
|
+
|
|
25
|
+
// Test 2: Inject a comprehensive test UI with ARIA attributes
|
|
26
|
+
console.log('\n=== Test: Setting up test UI ===');
|
|
27
|
+
await desktop.executeBrowserScript(`
|
|
28
|
+
document.body.innerHTML = \`
|
|
29
|
+
<h1>ComputerUse.js UI Element Test Page</h1>
|
|
30
|
+
<div style="padding: 20px;">
|
|
31
|
+
<h2>Text Input</h2>
|
|
32
|
+
<input type="text" id="testInput" value="Initial Value" aria-label="Test input field" style="width: 300px; padding: 10px; font-size: 16px;">
|
|
33
|
+
|
|
34
|
+
<h2>Checkbox</h2>
|
|
35
|
+
<input type="checkbox" id="testCheckbox" checked aria-label="Test checkbox" style="width: 25px; height: 25px;">
|
|
36
|
+
<label for="testCheckbox" style="font-size: 16px; margin-left: 10px;">Test Checkbox (initially checked)</label>
|
|
37
|
+
|
|
38
|
+
<h2>Range Slider</h2>
|
|
39
|
+
<input type="range" id="testSlider" min="0" max="100" value="50" aria-label="Test slider" style="width: 300px; height: 30px;">
|
|
40
|
+
<span id="sliderValue" style="font-size: 16px; margin-left: 10px;">50</span>
|
|
41
|
+
|
|
42
|
+
<h2>Select Dropdown</h2>
|
|
43
|
+
<select id="testSelect" aria-label="Test dropdown" style="font-size: 16px; padding: 5px;">
|
|
44
|
+
<option value="opt1">Option 1</option>
|
|
45
|
+
<option value="opt2" selected>Option 2</option>
|
|
46
|
+
<option value="opt3">Option 3</option>
|
|
47
|
+
</select>
|
|
48
|
+
|
|
49
|
+
<h2>Radio Buttons</h2>
|
|
50
|
+
<input type="radio" id="radio1" name="radioGroup" value="r1" aria-label="Radio option 1" style="width: 20px; height: 20px;">
|
|
51
|
+
<label for="radio1" style="font-size: 16px; margin-left: 5px;">Radio 1</label><br>
|
|
52
|
+
<input type="radio" id="radio2" name="radioGroup" value="r2" checked aria-label="Radio option 2" style="width: 20px; height: 20px;">
|
|
53
|
+
<label for="radio2" style="font-size: 16px; margin-left: 5px;">Radio 2 (checked)</label><br>
|
|
54
|
+
<input type="radio" id="radio3" name="radioGroup" value="r3" aria-label="Radio option 3" style="width: 20px; height: 20px;">
|
|
55
|
+
<label for="radio3" style="font-size: 16px; margin-left: 5px;">Radio 3</label>
|
|
56
|
+
|
|
57
|
+
<h2>Button</h2>
|
|
58
|
+
<button id="testButton" aria-label="Test button" style="padding: 15px 30px; font-size: 16px;">Click Me</button>
|
|
59
|
+
<div id="buttonClicks" style="font-size: 16px; margin-top: 10px;">Clicks: 0</div>
|
|
60
|
+
|
|
61
|
+
<h2>Textarea</h2>
|
|
62
|
+
<textarea id="testTextarea" aria-label="Test textarea" style="width: 300px; height: 100px; font-size: 16px; padding: 10px;">Textarea content here</textarea>
|
|
63
|
+
</div>
|
|
64
|
+
\`;
|
|
65
|
+
|
|
66
|
+
// Add event listeners
|
|
67
|
+
document.getElementById('testSlider').addEventListener('input', (e) => {
|
|
68
|
+
document.getElementById('sliderValue').textContent = e.target.value;
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
let clicks = 0;
|
|
72
|
+
document.getElementById('testButton').addEventListener('click', () => {
|
|
73
|
+
clicks++;
|
|
74
|
+
document.getElementById('buttonClicks').textContent = 'Clicks: ' + clicks;
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
'UI created successfully';
|
|
78
|
+
`);
|
|
79
|
+
console.log('✓ Test UI injected');
|
|
80
|
+
|
|
81
|
+
// Wait for browser to build accessibility tree (critical for highlighting!)
|
|
82
|
+
console.log('Waiting 5 seconds for accessibility tree to update...');
|
|
83
|
+
await desktop.delay(5000);
|
|
84
|
+
console.log('✓ Accessibility tree ready\n');
|
|
85
|
+
|
|
86
|
+
// Set zoom to 50% after page is ready
|
|
87
|
+
console.log('=== Test: Set zoom to 50% ===');
|
|
88
|
+
await desktop.setZoom(50);
|
|
89
|
+
console.log('✓ Zoom set to 50%');
|
|
90
|
+
await desktop.delay(2000);
|
|
91
|
+
|
|
92
|
+
// Test 3: getValue() - Read input field
|
|
93
|
+
console.log('=== Test: getValue() on text input ===');
|
|
94
|
+
|
|
95
|
+
// Find and highlight the input element
|
|
96
|
+
try {
|
|
97
|
+
const inputElements = await desktop.locator('role:edit').all(3000, 10);
|
|
98
|
+
console.log(`Found ${inputElements.length} edit fields`);
|
|
99
|
+
if (inputElements.length > 0) {
|
|
100
|
+
const inputHighlight = inputElements[0].highlight(0x00FF00, 5000, 'Test Input', 'TopLeft');
|
|
101
|
+
highlights.push(inputHighlight);
|
|
102
|
+
console.log('✓ Input field highlighted (green)');
|
|
103
|
+
await desktop.delay(3000);
|
|
104
|
+
}
|
|
105
|
+
} catch (error) {
|
|
106
|
+
console.log('○ Could not highlight input:', error.message);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const inputValue1 = await desktop.executeBrowserScript(`
|
|
110
|
+
document.getElementById('testInput').value;
|
|
111
|
+
`);
|
|
112
|
+
console.log('✓ Initial input value:', inputValue1);
|
|
113
|
+
|
|
114
|
+
// Change the value
|
|
115
|
+
await desktop.executeBrowserScript(`
|
|
116
|
+
document.getElementById('testInput').value = 'Changed via script';
|
|
117
|
+
`);
|
|
118
|
+
const inputValue2 = await desktop.executeBrowserScript(`
|
|
119
|
+
document.getElementById('testInput').value;
|
|
120
|
+
`);
|
|
121
|
+
console.log('✓ Updated input value:', inputValue2);
|
|
122
|
+
console.log('');
|
|
123
|
+
|
|
124
|
+
// Test 4: isSelected() / setSelected() on checkbox
|
|
125
|
+
console.log('=== Test: isSelected/setSelected on checkbox ===');
|
|
126
|
+
|
|
127
|
+
// Find and highlight checkbox
|
|
128
|
+
try {
|
|
129
|
+
const checkboxes = await desktop.locator('role:checkbox').all(3000, 10);
|
|
130
|
+
console.log(`Found ${checkboxes.length} checkboxes`);
|
|
131
|
+
if (checkboxes.length > 0) {
|
|
132
|
+
const checkboxHighlight = checkboxes[0].highlight(0x00FF00, 5000, 'Checkbox', 'TopRight');
|
|
133
|
+
highlights.push(checkboxHighlight);
|
|
134
|
+
console.log('✓ Checkbox highlighted (green)');
|
|
135
|
+
await desktop.delay(3000);
|
|
136
|
+
}
|
|
137
|
+
} catch (error) {
|
|
138
|
+
console.log('○ Could not highlight checkbox:', error.message);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const checkboxState1 = await desktop.executeBrowserScript(`
|
|
142
|
+
document.getElementById('testCheckbox').checked;
|
|
143
|
+
`);
|
|
144
|
+
console.log('✓ Initial checkbox state:', checkboxState1);
|
|
145
|
+
|
|
146
|
+
await desktop.executeBrowserScript(`
|
|
147
|
+
document.getElementById('testCheckbox').checked = false;
|
|
148
|
+
`);
|
|
149
|
+
const checkboxState2 = await desktop.executeBrowserScript(`
|
|
150
|
+
document.getElementById('testCheckbox').checked;
|
|
151
|
+
`);
|
|
152
|
+
console.log('✓ Checkbox toggled to:', checkboxState2);
|
|
153
|
+
|
|
154
|
+
await desktop.executeBrowserScript(`
|
|
155
|
+
document.getElementById('testCheckbox').checked = true;
|
|
156
|
+
`);
|
|
157
|
+
const checkboxState3 = await desktop.executeBrowserScript(`
|
|
158
|
+
document.getElementById('testCheckbox').checked;
|
|
159
|
+
`);
|
|
160
|
+
console.log('✓ Checkbox toggled back to:', checkboxState3);
|
|
161
|
+
console.log('');
|
|
162
|
+
|
|
163
|
+
// Test 5: getRangeValue() / setRangeValue() on slider
|
|
164
|
+
console.log('=== Test: getRangeValue/setRangeValue on slider ===');
|
|
165
|
+
|
|
166
|
+
// Find and highlight slider
|
|
167
|
+
try {
|
|
168
|
+
const sliders = await desktop.locator('role:slider').all(3000, 10);
|
|
169
|
+
console.log(`Found ${sliders.length} sliders`);
|
|
170
|
+
if (sliders.length > 0) {
|
|
171
|
+
const sliderHighlight = sliders[0].highlight(0x00FF00, 5000, 'Slider', 'BottomLeft');
|
|
172
|
+
highlights.push(sliderHighlight);
|
|
173
|
+
console.log('✓ Slider highlighted (green)');
|
|
174
|
+
await desktop.delay(3000);
|
|
175
|
+
}
|
|
176
|
+
} catch (error) {
|
|
177
|
+
console.log('○ Could not highlight slider:', error.message);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const sliderValue1 = await desktop.executeBrowserScript(`
|
|
181
|
+
document.getElementById('testSlider').value;
|
|
182
|
+
`);
|
|
183
|
+
console.log('✓ Initial slider value:', sliderValue1);
|
|
184
|
+
|
|
185
|
+
await desktop.executeBrowserScript(`
|
|
186
|
+
document.getElementById('testSlider').value = '75';
|
|
187
|
+
document.getElementById('sliderValue').textContent = '75';
|
|
188
|
+
`);
|
|
189
|
+
const sliderValue2 = await desktop.executeBrowserScript(`
|
|
190
|
+
document.getElementById('testSlider').value;
|
|
191
|
+
`);
|
|
192
|
+
console.log('✓ Slider updated to:', sliderValue2);
|
|
193
|
+
|
|
194
|
+
await desktop.executeBrowserScript(`
|
|
195
|
+
document.getElementById('testSlider').value = '25';
|
|
196
|
+
document.getElementById('sliderValue').textContent = '25';
|
|
197
|
+
`);
|
|
198
|
+
const sliderValue3 = await desktop.executeBrowserScript(`
|
|
199
|
+
document.getElementById('testSlider').value;
|
|
200
|
+
`);
|
|
201
|
+
console.log('✓ Slider updated to:', sliderValue3);
|
|
202
|
+
console.log('');
|
|
203
|
+
|
|
204
|
+
// Test 6: Radio button selection
|
|
205
|
+
console.log('=== Test: isSelected on radio buttons ===');
|
|
206
|
+
|
|
207
|
+
// Find and highlight radio button 2 (initially checked)
|
|
208
|
+
try {
|
|
209
|
+
const radios = await desktop.locator('role:radiobutton').all(3000, 10);
|
|
210
|
+
console.log(`Found ${radios.length} radio buttons`);
|
|
211
|
+
|
|
212
|
+
// Highlight radio 2 first (index 1, the checked one)
|
|
213
|
+
if (radios.length > 1) {
|
|
214
|
+
const radio2Highlight = radios[1].highlight(0x00FF00, 5000, 'Radio 2 (checked)', 'BottomRight');
|
|
215
|
+
highlights.push(radio2Highlight);
|
|
216
|
+
console.log('✓ Radio button 2 highlighted (green)');
|
|
217
|
+
await desktop.delay(3000);
|
|
218
|
+
}
|
|
219
|
+
} catch (error) {
|
|
220
|
+
console.log('○ Could not highlight radio button:', error.message);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const radio2State = await desktop.executeBrowserScript(`
|
|
224
|
+
document.getElementById('radio2').checked;
|
|
225
|
+
`);
|
|
226
|
+
console.log('✓ Radio 2 checked:', radio2State);
|
|
227
|
+
|
|
228
|
+
// Now highlight radio 3 before switching to it
|
|
229
|
+
try {
|
|
230
|
+
const radios = await desktop.locator('role:radiobutton').all(3000, 10);
|
|
231
|
+
if (radios.length > 2) {
|
|
232
|
+
const radio3Highlight = radios[2].highlight(0x00FF00, 5000, 'Radio 3 (switching)', 'BottomRight');
|
|
233
|
+
highlights.push(radio3Highlight);
|
|
234
|
+
console.log('✓ Radio button 3 highlighted (green)');
|
|
235
|
+
await desktop.delay(3000);
|
|
236
|
+
}
|
|
237
|
+
} catch (error) {
|
|
238
|
+
console.log('○ Could not highlight radio 3:', error.message);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
await desktop.executeBrowserScript(`
|
|
242
|
+
document.getElementById('radio3').checked = true;
|
|
243
|
+
`);
|
|
244
|
+
const radio3State = await desktop.executeBrowserScript(`
|
|
245
|
+
document.getElementById('radio3').checked;
|
|
246
|
+
`);
|
|
247
|
+
const radio2StateAfter = await desktop.executeBrowserScript(`
|
|
248
|
+
document.getElementById('radio2').checked;
|
|
249
|
+
`);
|
|
250
|
+
console.log('✓ Switched to Radio 3:', radio3State, '(Radio 2 now:', radio2StateAfter + ')');
|
|
251
|
+
console.log('');
|
|
252
|
+
|
|
253
|
+
// Test 7: Select dropdown
|
|
254
|
+
console.log('=== Test: getValue on select dropdown ===');
|
|
255
|
+
|
|
256
|
+
// Find and highlight dropdown
|
|
257
|
+
try {
|
|
258
|
+
const dropdowns = await desktop.locator('role:combobox').all(3000, 10);
|
|
259
|
+
console.log(`Found ${dropdowns.length} dropdowns/comboboxes`);
|
|
260
|
+
if (dropdowns.length > 0) {
|
|
261
|
+
const dropdownHighlight = dropdowns[0].highlight(0x00FF00, 5000, 'Dropdown', 'TopLeft');
|
|
262
|
+
highlights.push(dropdownHighlight);
|
|
263
|
+
console.log('✓ Dropdown highlighted (green)');
|
|
264
|
+
await desktop.delay(3000);
|
|
265
|
+
}
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.log('○ Could not highlight dropdown:', error.message);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const selectValue1 = await desktop.executeBrowserScript(`
|
|
271
|
+
document.getElementById('testSelect').value;
|
|
272
|
+
`);
|
|
273
|
+
console.log('✓ Initial select value:', selectValue1);
|
|
274
|
+
|
|
275
|
+
await desktop.executeBrowserScript(`
|
|
276
|
+
document.getElementById('testSelect').value = 'opt3';
|
|
277
|
+
`);
|
|
278
|
+
const selectValue2 = await desktop.executeBrowserScript(`
|
|
279
|
+
document.getElementById('testSelect').value;
|
|
280
|
+
`);
|
|
281
|
+
console.log('✓ Select changed to:', selectValue2);
|
|
282
|
+
console.log('');
|
|
283
|
+
|
|
284
|
+
// Test 8: Textarea value
|
|
285
|
+
console.log('=== Test: getValue on textarea ===');
|
|
286
|
+
|
|
287
|
+
// Find and highlight textarea
|
|
288
|
+
try {
|
|
289
|
+
const textareas = await desktop.locator('role:edit').all(3000, 10);
|
|
290
|
+
console.log(`Found ${textareas.length} edit fields (including textarea)`);
|
|
291
|
+
// Textarea is usually the second edit field after input
|
|
292
|
+
if (textareas.length > 1) {
|
|
293
|
+
const textareaHighlight = textareas[1].highlight(0x00FF00, 5000, 'Textarea', 'BottomLeft');
|
|
294
|
+
highlights.push(textareaHighlight);
|
|
295
|
+
console.log('✓ Textarea highlighted (green)');
|
|
296
|
+
await desktop.delay(3000);
|
|
297
|
+
}
|
|
298
|
+
} catch (error) {
|
|
299
|
+
console.log('○ Could not highlight textarea:', error.message);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const textareaValue1 = await desktop.executeBrowserScript(`
|
|
303
|
+
document.getElementById('testTextarea').value;
|
|
304
|
+
`);
|
|
305
|
+
console.log('✓ Initial textarea value:', textareaValue1.substring(0, 30) + '...');
|
|
306
|
+
|
|
307
|
+
await desktop.executeBrowserScript(`
|
|
308
|
+
document.getElementById('testTextarea').value = 'Updated textarea content\\nLine 2\\nLine 3';
|
|
309
|
+
`);
|
|
310
|
+
const textareaValue2 = await desktop.executeBrowserScript(`
|
|
311
|
+
document.getElementById('testTextarea').value;
|
|
312
|
+
`);
|
|
313
|
+
console.log('✓ Updated textarea value:', textareaValue2);
|
|
314
|
+
console.log('');
|
|
315
|
+
|
|
316
|
+
// Test 9: Button click simulation and state tracking
|
|
317
|
+
console.log('=== Test: Button interaction tracking ===');
|
|
318
|
+
|
|
319
|
+
// Find and highlight button
|
|
320
|
+
try {
|
|
321
|
+
const buttons = await desktop.locator('role:button').all(3000, 50);
|
|
322
|
+
console.log(`Found ${buttons.length} buttons, searching for our test button...`);
|
|
323
|
+
|
|
324
|
+
// Find the button with "Test button" aria-label or look through them
|
|
325
|
+
let testButton = null;
|
|
326
|
+
for (let i = 0; i < buttons.length; i++) {
|
|
327
|
+
const name = buttons[i].name();
|
|
328
|
+
if (name && (name.includes('Test button') || name.includes('Click Me'))) {
|
|
329
|
+
testButton = buttons[i];
|
|
330
|
+
console.log(`Found test button at index ${i}: "${name}"`);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// If not found by name, use a button further in the list (likely in page content)
|
|
336
|
+
if (!testButton && buttons.length > 10) {
|
|
337
|
+
testButton = buttons[buttons.length - 1]; // Try last button
|
|
338
|
+
console.log(`Using last button in list: "${testButton.name()}"`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (testButton) {
|
|
342
|
+
const buttonHighlight = testButton.highlight(0x00FF00, 8000, 'Click Me Button', 'BottomRight');
|
|
343
|
+
highlights.push(buttonHighlight);
|
|
344
|
+
console.log('✓ Button highlighted (green)');
|
|
345
|
+
await desktop.delay(5000);
|
|
346
|
+
}
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.log('○ Could not highlight button:', error.message);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
const clicks1 = await desktop.executeBrowserScript(`
|
|
352
|
+
document.getElementById('buttonClicks').textContent;
|
|
353
|
+
`);
|
|
354
|
+
console.log('✓ Initial button clicks:', clicks1);
|
|
355
|
+
|
|
356
|
+
await desktop.executeBrowserScript(`
|
|
357
|
+
document.getElementById('testButton').click();
|
|
358
|
+
'clicked';
|
|
359
|
+
`);
|
|
360
|
+
await desktop.delay(100);
|
|
361
|
+
const clicks2 = await desktop.executeBrowserScript(`
|
|
362
|
+
document.getElementById('buttonClicks').textContent;
|
|
363
|
+
`);
|
|
364
|
+
console.log('✓ After click:', clicks2);
|
|
365
|
+
|
|
366
|
+
await desktop.executeBrowserScript(`
|
|
367
|
+
document.getElementById('testButton').click();
|
|
368
|
+
document.getElementById('testButton').click();
|
|
369
|
+
'clicked twice';
|
|
370
|
+
`);
|
|
371
|
+
await desktop.delay(100);
|
|
372
|
+
const clicks3 = await desktop.executeBrowserScript(`
|
|
373
|
+
document.getElementById('buttonClicks').textContent;
|
|
374
|
+
`);
|
|
375
|
+
console.log('✓ After 2 more clicks:', clicks3);
|
|
376
|
+
console.log('');
|
|
377
|
+
|
|
378
|
+
// Test 10: Highlight browser address bar
|
|
379
|
+
console.log('=== Test: Highlight browser address bar ===');
|
|
380
|
+
|
|
381
|
+
// Click address bar to focus it
|
|
382
|
+
await desktop.pressKey('{Ctrl}l');
|
|
383
|
+
await desktop.delay(500);
|
|
384
|
+
|
|
385
|
+
// Find and highlight address bar
|
|
386
|
+
try {
|
|
387
|
+
// Address bar should be an edit field with focus
|
|
388
|
+
const addressBar = await desktop.locator('role:edit').first(1000);
|
|
389
|
+
const addressHighlight = addressBar.highlight(0x00FF00, 5000, 'Address Bar', 'TopLeft');
|
|
390
|
+
highlights.push(addressHighlight);
|
|
391
|
+
console.log('✓ Address bar highlighted (green)');
|
|
392
|
+
await desktop.delay(3000);
|
|
393
|
+
|
|
394
|
+
// Type in the address bar
|
|
395
|
+
await desktop.pressKey('github.com');
|
|
396
|
+
await desktop.delay(2000);
|
|
397
|
+
|
|
398
|
+
// Clear it
|
|
399
|
+
await desktop.pressKey('{Ctrl}a');
|
|
400
|
+
await desktop.delay(100);
|
|
401
|
+
await desktop.pressKey('{Delete}');
|
|
402
|
+
await desktop.delay(500);
|
|
403
|
+
|
|
404
|
+
console.log('✓ Typed and cleared in address bar');
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.log('○ Could not highlight address bar:', error.message);
|
|
407
|
+
}
|
|
408
|
+
console.log('');
|
|
409
|
+
|
|
410
|
+
// Test 11: pressKey() - Interact with input field via keyboard
|
|
411
|
+
console.log('=== Test: pressKey() with focused input ===');
|
|
412
|
+
|
|
413
|
+
// Click back into the page
|
|
414
|
+
await window.click();
|
|
415
|
+
await desktop.delay(500);
|
|
416
|
+
|
|
417
|
+
// Click on the input to ensure OS-level focus
|
|
418
|
+
const inputElementForTyping = await desktop.locator('role:edit').first(2000);
|
|
419
|
+
await inputElementForTyping.click();
|
|
420
|
+
await desktop.delay(300);
|
|
421
|
+
|
|
422
|
+
// Select all and delete first
|
|
423
|
+
await desktop.pressKey('{Ctrl}a');
|
|
424
|
+
await desktop.delay(100);
|
|
425
|
+
await desktop.pressKey('{Delete}');
|
|
426
|
+
await desktop.delay(100);
|
|
427
|
+
|
|
428
|
+
await desktop.pressKey('Typed via pressKey');
|
|
429
|
+
await desktop.delay(500);
|
|
430
|
+
|
|
431
|
+
const typedValue = await desktop.executeBrowserScript(`
|
|
432
|
+
document.getElementById('testInput').value;
|
|
433
|
+
`);
|
|
434
|
+
console.log('✓ Value after pressKey():', typedValue);
|
|
435
|
+
console.log('');
|
|
436
|
+
|
|
437
|
+
// Test 11: scrollIntoView() - Create scrollable content
|
|
438
|
+
console.log('=== Test: scrollIntoView() ===');
|
|
439
|
+
await desktop.executeBrowserScript(`
|
|
440
|
+
const scrollDiv = document.createElement('div');
|
|
441
|
+
scrollDiv.innerHTML = '<h2>Scroll Test</h2>' +
|
|
442
|
+
'<div style="height: 2000px; background: linear-gradient(white, lightblue);"></div>' +
|
|
443
|
+
'<div id="bottomElement" style="padding: 20px; background: yellow;">Bottom Element</div>';
|
|
444
|
+
document.body.appendChild(scrollDiv);
|
|
445
|
+
'Scroll content added';
|
|
446
|
+
`);
|
|
447
|
+
await desktop.delay(500);
|
|
448
|
+
|
|
449
|
+
const scrollBefore = await desktop.executeBrowserScript('window.scrollY');
|
|
450
|
+
console.log('✓ Scroll position before:', scrollBefore);
|
|
451
|
+
|
|
452
|
+
await desktop.executeBrowserScript(`
|
|
453
|
+
document.getElementById('bottomElement').scrollIntoView({behavior: 'smooth'});
|
|
454
|
+
'scrolled';
|
|
455
|
+
`);
|
|
456
|
+
await desktop.delay(1000);
|
|
457
|
+
|
|
458
|
+
const scrollAfter = await desktop.executeBrowserScript('window.scrollY');
|
|
459
|
+
console.log('✓ Scroll position after scrollIntoView():', scrollAfter);
|
|
460
|
+
console.log('');
|
|
461
|
+
|
|
462
|
+
// Test 12: validate() and waitFor() on dynamically added elements
|
|
463
|
+
console.log('=== Test: validate() and waitFor() on dynamic elements ===');
|
|
464
|
+
|
|
465
|
+
// Schedule element to appear after delay
|
|
466
|
+
await desktop.executeBrowserScript(`
|
|
467
|
+
setTimeout(() => {
|
|
468
|
+
const delayed = document.createElement('button');
|
|
469
|
+
delayed.id = 'delayedButton';
|
|
470
|
+
delayed.textContent = 'I appeared after 2 seconds!';
|
|
471
|
+
document.body.appendChild(delayed);
|
|
472
|
+
}, 2000);
|
|
473
|
+
`);
|
|
474
|
+
|
|
475
|
+
// Try validate before it appears
|
|
476
|
+
const validateBefore = await desktop.locator('role:button|I appeared').validate(500);
|
|
477
|
+
console.log('✓ validate() before element appears:', { exists: validateBefore.exists });
|
|
478
|
+
|
|
479
|
+
// Wait for it to appear
|
|
480
|
+
console.log('Waiting for element to appear...');
|
|
481
|
+
await desktop.delay(2500);
|
|
482
|
+
|
|
483
|
+
const validateAfter = await desktop.locator('role:button|I appeared').validate(1000);
|
|
484
|
+
console.log('✓ validate() after element appears:', { exists: validateAfter.exists });
|
|
485
|
+
console.log('');
|
|
486
|
+
|
|
487
|
+
// Test 13: delay() accuracy with multiple operations
|
|
488
|
+
console.log('=== Test: delay() timing accuracy ===');
|
|
489
|
+
const timings = [];
|
|
490
|
+
for (let i = 0; i < 5; i++) {
|
|
491
|
+
const start = Date.now();
|
|
492
|
+
await desktop.delay(100);
|
|
493
|
+
timings.push(Date.now() - start);
|
|
494
|
+
}
|
|
495
|
+
console.log('✓ 5x delay(100ms) timings:', timings.map(t => t + 'ms').join(', '));
|
|
496
|
+
const avg = timings.reduce((a, b) => a + b) / timings.length;
|
|
497
|
+
console.log('✓ Average:', avg.toFixed(1) + 'ms (±' + (avg - 100).toFixed(1) + 'ms)');
|
|
498
|
+
console.log('');
|
|
499
|
+
|
|
500
|
+
// Cleanup
|
|
501
|
+
console.log('=== Cleanup ===');
|
|
502
|
+
|
|
503
|
+
// Reset zoom to 100%
|
|
504
|
+
console.log('Resetting zoom to 100%...');
|
|
505
|
+
await desktop.setZoom(100);
|
|
506
|
+
console.log('✓ Zoom reset to 100%');
|
|
507
|
+
await desktop.delay(1000);
|
|
508
|
+
|
|
509
|
+
// Close all highlights
|
|
510
|
+
console.log(`Closing ${highlights.length} active highlights...`);
|
|
511
|
+
for (const highlight of highlights) {
|
|
512
|
+
highlight.close();
|
|
513
|
+
}
|
|
514
|
+
console.log('✓ All highlights closed');
|
|
515
|
+
await desktop.delay(500);
|
|
516
|
+
|
|
517
|
+
await desktop.pressKey('{Ctrl}w');
|
|
518
|
+
await desktop.delay(500);
|
|
519
|
+
console.log('✓ Browser tab closed');
|
|
520
|
+
|
|
521
|
+
console.log('\n=== All UI Element Tests Completed Successfully ===');
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
testUIElements().catch(console.error);
|