@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.
@@ -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);