testdriverai 7.3.12 → 7.3.13

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.
Files changed (133) hide show
  1. package/.github/skills/testdriver:ai/SKILL.md +204 -0
  2. package/.github/skills/testdriver:assert/SKILL.md +284 -0
  3. package/.github/skills/testdriver:aws-setup/SKILL.md +515 -0
  4. package/.github/skills/testdriver:caching/SKILL.md +124 -0
  5. package/.github/skills/testdriver:captcha/SKILL.md +159 -0
  6. package/.github/skills/testdriver:ci-cd/SKILL.md +602 -0
  7. package/.github/skills/testdriver:click/SKILL.md +286 -0
  8. package/.github/skills/testdriver:client/SKILL.md +339 -0
  9. package/.github/skills/testdriver:cloud/SKILL.md +119 -0
  10. package/.github/skills/testdriver:customizing-devices/SKILL.md +153 -0
  11. package/.github/skills/testdriver:dashcam/SKILL.md +418 -0
  12. package/.github/skills/testdriver:debugging-with-screenshots/SKILL.md +271 -0
  13. package/.github/skills/testdriver:device-config/SKILL.md +317 -0
  14. package/.github/skills/testdriver:double-click/SKILL.md +102 -0
  15. package/.github/skills/testdriver:elements/SKILL.md +605 -0
  16. package/.github/skills/testdriver:enterprise/SKILL.md +114 -0
  17. package/.github/skills/testdriver:examples/SKILL.md +7 -0
  18. package/.github/skills/testdriver:exec/SKILL.md +345 -0
  19. package/.github/skills/testdriver:find/SKILL.md +721 -0
  20. package/.github/skills/testdriver:focus-application/SKILL.md +293 -0
  21. package/.github/skills/testdriver:generating-tests/SKILL.md +36 -0
  22. package/.github/skills/testdriver:hover/SKILL.md +278 -0
  23. package/.github/skills/testdriver:locating-elements/SKILL.md +71 -0
  24. package/.github/skills/testdriver:making-assertions/SKILL.md +32 -0
  25. package/.github/skills/testdriver:mcp-workflow/SKILL.md +410 -0
  26. package/.github/skills/testdriver:mouse-down/SKILL.md +161 -0
  27. package/.github/skills/testdriver:mouse-up/SKILL.md +164 -0
  28. package/.github/skills/testdriver:performing-actions/SKILL.md +51 -0
  29. package/.github/skills/testdriver:press-keys/SKILL.md +348 -0
  30. package/.github/skills/testdriver:quickstart/SKILL.md +161 -0
  31. package/.github/skills/testdriver:reusable-code/SKILL.md +240 -0
  32. package/.github/skills/testdriver:right-click/SKILL.md +123 -0
  33. package/.github/skills/testdriver:running-tests/SKILL.md +181 -0
  34. package/.github/skills/testdriver:screenshot/SKILL.md +167 -0
  35. package/.github/skills/testdriver:scroll/SKILL.md +299 -0
  36. package/.github/skills/testdriver:secrets/SKILL.md +115 -0
  37. package/.github/skills/testdriver:self-hosted/SKILL.md +65 -0
  38. package/.github/skills/testdriver:test-writer/SKILL.md +451 -0
  39. package/.github/skills/testdriver:testdriver/SKILL.md +523 -0
  40. package/.github/skills/testdriver:testdriver-mechanic/SKILL.md +165 -0
  41. package/.github/skills/testdriver:type/SKILL.md +357 -0
  42. package/.github/skills/testdriver:variables/SKILL.md +111 -0
  43. package/.github/skills/testdriver:waiting-for-elements/SKILL.md +66 -0
  44. package/.github/skills/testdriver:what-is-testdriver/SKILL.md +54 -0
  45. package/.github/workflows/acceptance-windows-scheduled.yaml +6 -1
  46. package/.github/workflows/acceptance.yaml +0 -36
  47. package/.github/workflows/update-examples.yaml +53 -0
  48. package/CHANGELOG.md +4 -0
  49. package/agent/events.js +1 -0
  50. package/agent/index.js +8 -0
  51. package/agent/lib/commands.js +48 -29
  52. package/agent/lib/redraw.js +3 -1
  53. package/agent/lib/sandbox.js +166 -14
  54. package/agent/lib/sdk.js +142 -3
  55. package/agent/lib/system.js +4 -6
  56. package/ai/skills/testdriver:ai/SKILL.md +204 -0
  57. package/ai/skills/testdriver:assert/SKILL.md +315 -0
  58. package/ai/skills/testdriver:aws-setup/SKILL.md +448 -0
  59. package/ai/skills/testdriver:caching/SKILL.md +124 -0
  60. package/ai/skills/testdriver:captcha/SKILL.md +159 -0
  61. package/ai/skills/testdriver:ci-cd/SKILL.md +602 -0
  62. package/ai/skills/testdriver:click/SKILL.md +286 -0
  63. package/ai/skills/testdriver:client/SKILL.md +372 -0
  64. package/ai/skills/testdriver:cloud/SKILL.md +119 -0
  65. package/ai/skills/testdriver:customizing-devices/SKILL.md +153 -0
  66. package/ai/skills/testdriver:dashcam/SKILL.md +418 -0
  67. package/ai/skills/testdriver:debugging-with-screenshots/SKILL.md +401 -0
  68. package/ai/skills/testdriver:device-config/SKILL.md +317 -0
  69. package/ai/skills/testdriver:double-click/SKILL.md +102 -0
  70. package/ai/skills/testdriver:elements/SKILL.md +605 -0
  71. package/ai/skills/testdriver:enterprise/SKILL.md +114 -0
  72. package/ai/skills/testdriver:examples/SKILL.md +7 -0
  73. package/ai/skills/testdriver:exec/SKILL.md +345 -0
  74. package/ai/skills/testdriver:find/SKILL.md +745 -0
  75. package/ai/skills/testdriver:focus-application/SKILL.md +293 -0
  76. package/ai/skills/testdriver:generating-tests/SKILL.md +36 -0
  77. package/ai/skills/testdriver:hover/SKILL.md +278 -0
  78. package/ai/skills/testdriver:locating-elements/SKILL.md +71 -0
  79. package/ai/skills/testdriver:making-assertions/SKILL.md +32 -0
  80. package/ai/skills/testdriver:mcp-workflow/SKILL.md +410 -0
  81. package/ai/skills/testdriver:mouse-down/SKILL.md +161 -0
  82. package/ai/skills/testdriver:mouse-up/SKILL.md +164 -0
  83. package/ai/skills/testdriver:ocr/SKILL.md +235 -0
  84. package/ai/skills/testdriver:performing-actions/SKILL.md +51 -0
  85. package/ai/skills/testdriver:press-keys/SKILL.md +348 -0
  86. package/ai/skills/testdriver:quickstart/SKILL.md +146 -0
  87. package/ai/skills/testdriver:reusable-code/SKILL.md +240 -0
  88. package/ai/skills/testdriver:right-click/SKILL.md +123 -0
  89. package/ai/skills/testdriver:running-tests/SKILL.md +185 -0
  90. package/ai/skills/testdriver:screenshot/SKILL.md +248 -0
  91. package/ai/skills/testdriver:scroll/SKILL.md +335 -0
  92. package/ai/skills/testdriver:secrets/SKILL.md +115 -0
  93. package/ai/skills/testdriver:self-hosted/SKILL.md +65 -0
  94. package/ai/skills/testdriver:test-writer/SKILL.md +451 -0
  95. package/ai/skills/testdriver:testdriver/SKILL.md +631 -0
  96. package/ai/skills/testdriver:testdriver-mechanic/SKILL.md +165 -0
  97. package/ai/skills/testdriver:type/SKILL.md +357 -0
  98. package/ai/skills/testdriver:variables/SKILL.md +111 -0
  99. package/ai/skills/testdriver:waiting-for-elements/SKILL.md +66 -0
  100. package/ai/skills/testdriver:what-is-testdriver/SKILL.md +54 -0
  101. package/debugger/index.html +12 -2
  102. package/docs/v7/examples/scroll-keyboard.mdx +1 -1
  103. package/docs/v7/find.mdx +1 -0
  104. package/examples/config.mjs +1 -1
  105. package/examples/findall-coffee-icons.test.mjs +42 -0
  106. package/examples/flake-diffthreshold-001.test.mjs +9 -0
  107. package/examples/flake-diffthreshold-01.test.mjs +9 -0
  108. package/examples/flake-diffthreshold-05.test.mjs +9 -0
  109. package/examples/{z_flake-noredraw-cache.test.mjs → flake-noredraw-cache.test.mjs} +2 -2
  110. package/examples/{z_flake-noredraw-nocache.test.mjs → flake-noredraw-nocache.test.mjs} +2 -2
  111. package/examples/{z_flake-redraw-cache.test.mjs → flake-redraw-cache.test.mjs} +2 -2
  112. package/examples/{z_flake-redraw-nocache.test.mjs → flake-redraw-nocache.test.mjs} +2 -2
  113. package/examples/flake-rocket-match.test.mjs +30 -0
  114. package/examples/{z_flake-shared.mjs → flake-shared.mjs} +2 -2
  115. package/examples/parse.test.mjs +19 -0
  116. package/examples/scroll-keyboard.test.mjs +1 -1
  117. package/interfaces/cli/lib/base.js +6 -0
  118. package/interfaces/logger.js +51 -13
  119. package/interfaces/vitest-plugin.mjs +137 -0
  120. package/lib/core/index.d.ts +22 -0
  121. package/lib/init-project.js +105 -6
  122. package/lib/vitest/hooks.mjs +2 -5
  123. package/lib/vitest/setup-disable-defender.mjs +52 -0
  124. package/package.json +2 -1
  125. package/sdk-log-formatter.js +90 -0
  126. package/sdk.d.ts +88 -51
  127. package/sdk.js +126 -18
  128. package/setup/aws/disable-defender.sh +42 -0
  129. package/vitest.config.mjs +1 -3
  130. package/examples/z_flake-diffthreshold-001.test.mjs +0 -9
  131. package/examples/z_flake-diffthreshold-01.test.mjs +0 -9
  132. package/examples/z_flake-diffthreshold-05.test.mjs +0 -9
  133. /package/{examples → manual}/captcha-api.test.mjs +0 -0
@@ -0,0 +1,721 @@
1
+ ---
2
+ name: testdriver:find
3
+ description: Locate UI elements using natural language
4
+ ---
5
+ <!-- Generated from find.mdx. DO NOT EDIT. -->
6
+
7
+ ## Overview
8
+
9
+ Locate UI elements on screen using AI-powered natural language descriptions. Returns an `Element` object that can be interacted with.
10
+
11
+ ## Syntax
12
+
13
+ ```javascript
14
+ const element = await testdriver.find(description)
15
+ const element = await testdriver.find(description, options)
16
+ ```
17
+
18
+ ## Parameters
19
+
20
+ <ParamField path="description" type="string" required>
21
+ Natural language description of the element to find
22
+ </ParamField>
23
+
24
+ <ParamField path="options" type="object | number">
25
+ Optional configuration for finding and caching
26
+
27
+ <Expandable title="properties">
28
+ <ParamField path="cacheKey" type="string">
29
+ Custom cache key for storing element location. Use this to prevent cache pollution when using dynamic variables in prompts, or to share cache across tests.
30
+ </ParamField>
31
+
32
+ <ParamField path="cacheThreshold" type="number" default={0.05}>
33
+ Similarity threshold (0-1) for cache matching. Lower values require more similarity. Set to -1 to disable cache.
34
+ </ParamField>
35
+
36
+ <ParamField path="timeout" type="number">
37
+ Maximum time in milliseconds to poll for the element. Retries every 5 seconds until found or timeout expires.
38
+ </ParamField>
39
+
40
+ <ParamField path="zoom" type="boolean" default={false}>
41
+ Enable two-phase zoom mode for better precision in crowded UIs with many similar elements.
42
+ </ParamField>
43
+ </Expandable>
44
+ </ParamField>
45
+
46
+ ## Returns
47
+
48
+ `Promise<Element>` - Element instance that has been automatically located
49
+
50
+ ## Examples
51
+
52
+ ### Basic Element Finding
53
+
54
+ ```javascript
55
+ // Find by role
56
+ const button = await testdriver.find('submit button');
57
+ const input = await testdriver.find('email input field');
58
+
59
+ // Find by text content
60
+ const link = await testdriver.find('Contact Us link');
61
+ const heading = await testdriver.find('Welcome heading');
62
+
63
+ // Find by visual appearance
64
+ const icon = await testdriver.find('red warning icon');
65
+ const image = await testdriver.find('company logo image');
66
+ ```
67
+
68
+ ### Finding with Context
69
+
70
+ ```javascript
71
+ // Provide location context
72
+ const field = await testdriver.find('username input in the login form');
73
+ const button = await testdriver.find('delete button in the top right corner');
74
+
75
+ // Describe nearby elements
76
+ const input = await testdriver.find('input field below the email label');
77
+ const checkbox = await testdriver.find('checkbox next to "Remember me"');
78
+
79
+ // Describe visual position
80
+ const menu = await testdriver.find('hamburger menu icon in the top left');
81
+ ```
82
+
83
+ ### Interacting with Found Elements
84
+
85
+ ```javascript
86
+ // Find and click
87
+ const submitBtn = await testdriver.find('submit button');
88
+ await submitBtn.click();
89
+
90
+ // Find and verify
91
+ const message = await testdriver.find('success message');
92
+ if (message.found()) {
93
+ console.log('Success message appeared');
94
+ }
95
+
96
+ // Find and extract info
97
+ const price = await testdriver.find('product price');
98
+ console.log('Price location:', price.coordinates);
99
+ console.log('Price text:', price.text);
100
+ ```
101
+
102
+ ## Element Object
103
+
104
+ The returned `Element` object provides:
105
+
106
+ ### Methods
107
+
108
+ - `found()` - Check if element was located
109
+ - `click(action)` - Click the element
110
+ - `hover()` - Hover over the element
111
+ - `doubleClick()` - Double-click the element
112
+ - `rightClick()` - Right-click the element
113
+ - `find(newDescription)` - Re-locate with optional new description
114
+
115
+ ### Properties
116
+
117
+ - `coordinates` - Element position `{x, y, centerX, centerY}`
118
+ - `x`, `y` - Top-left coordinates
119
+ - `centerX`, `centerY` - Center coordinates
120
+ - `text` - Text content (if available)
121
+ - `screenshot` - Base64 screenshot (if available)
122
+ - `confidence` - AI confidence score
123
+ - `width`, `height` - Element dimensions
124
+ - `boundingBox` - Complete bounding box
125
+
126
+ See [Elements Reference](/v7/elements) for complete details.
127
+
128
+ ### JSON Serialization
129
+
130
+ Elements can be safely serialized using `JSON.stringify()` for logging and debugging. Circular references are automatically removed:
131
+
132
+ ```javascript
133
+ const element = await testdriver.find('login button');
134
+
135
+ // Safe to stringify - no circular reference errors
136
+ console.log(JSON.stringify(element, null, 2));
137
+
138
+ // Output includes useful debugging info:
139
+ // {
140
+ // "description": "login button",
141
+ // "coordinates": { "x": 100, "y": 200, "centerX": 150, "centerY": 225 },
142
+ // "found": true,
143
+ // "threshold": 0.01,
144
+ // "x": 100,
145
+ // "y": 200,
146
+ // "cache": {
147
+ // "hit": true,
148
+ // "strategy": "pixel-diff",
149
+ // "createdAt": "2025-12-09T10:30:00Z",
150
+ // "diffPercent": 0.0023,
151
+ // "imageUrl": "https://..."
152
+ // },
153
+ // "similarity": 0.98,
154
+ // "confidence": 0.95,
155
+ // "selector": "button#login",
156
+ // "aiResponse": "Found the blue login button..."
157
+ // }
158
+ ```
159
+
160
+ This is useful for:
161
+ - Debugging element detection issues
162
+ - Logging test execution details
163
+ - Sharing element information across processes
164
+ - Analyzing cache performance
165
+
166
+ ## Best Practices
167
+
168
+ <Check>
169
+ **Be specific in descriptions**
170
+
171
+ More specific descriptions improve accuracy:
172
+
173
+ ```javascript
174
+ // ✅ Good
175
+ await testdriver.find('blue submit button below the email field');
176
+
177
+ // ❌ Too vague
178
+ await testdriver.find('button');
179
+ ```
180
+ </Check>
181
+
182
+ <Check>
183
+ **Always check if found**
184
+
185
+ Verify elements were located before interacting:
186
+
187
+ ```javascript
188
+ const element = await testdriver.find('login button');
189
+ if (!element.found()) {
190
+ throw new Error('Login button not found');
191
+ }
192
+ await element.click();
193
+ ```
194
+ </Check>
195
+
196
+ <Check>
197
+ **Include visual or positional context**
198
+
199
+ ```javascript
200
+ // Include color
201
+ await testdriver.find('red error icon');
202
+
203
+ // Include position
204
+ await testdriver.find('search button in the top navigation bar');
205
+
206
+ // Include nearby text
207
+ await testdriver.find('checkbox next to "I agree to terms"');
208
+ ```
209
+ </Check>
210
+
211
+ ## Polling for Dynamic Elements
212
+
213
+ For elements that may not be immediately visible, use the `timeout` option to automatically poll:
214
+
215
+ ```javascript
216
+ // Poll for element (retries every 5 seconds until found or timeout)
217
+ const element = await testdriver.find('login button', { timeout: 30000 });
218
+ await element.click();
219
+ ```
220
+
221
+ The `timeout` option:
222
+ - Retries finding the element every 5 seconds
223
+ - Stops when the element is found or the timeout expires
224
+ - Logs progress during polling
225
+ - Returns the element (check `element.found()` if not throwing on failure)
226
+
227
+ ## Zoom Mode for Crowded UIs
228
+
229
+ When dealing with many similar icons or elements clustered together (like browser toolbars), enable `zoom` mode for better precision:
230
+
231
+ ```javascript
232
+ // Enable zoom for better precision in crowded UIs
233
+ const extensionsBtn = await testdriver.find('extensions puzzle icon in Chrome toolbar', { zoom: true });
234
+ await extensionsBtn.click();
235
+ ```
236
+
237
+ ### How Zoom Mode Works
238
+
239
+ 1. **Phase 1**: AI identifies the approximate location of the element
240
+ 2. **Phase 2**: A 30% crop of the screen is created around that location
241
+ 3. **Phase 3**: AI performs precise location on the zoomed/cropped image
242
+ 4. **Result**: Coordinates are converted back to absolute screen position
243
+
244
+ This two-phase approach gives the AI a higher-resolution view of the target area, improving accuracy when multiple similar elements are close together.
245
+
246
+ <Tip>
247
+ Use `zoom: true` when:
248
+ - Clicking small icons in toolbars
249
+ - Selecting from a grid of similar items
250
+ - Targeting elements in dense UI areas
251
+ - The default locate is clicking the wrong similar element
252
+ - You get an AI verification rejection like "The crosshair is located in the empty space of the browser's tab bar/title bar area" — this means the initial locate was imprecise and zoom will help the AI pinpoint the correct element
253
+ </Tip>
254
+
255
+ ```javascript
256
+ // Without zoom - may click wrong icon in toolbar
257
+ const icon = await testdriver.find('settings icon');
258
+
259
+ // With zoom - better precision for crowded areas
260
+ const icon = await testdriver.find('settings icon', { zoom: true });
261
+ ```
262
+
263
+ ## Cache Options
264
+
265
+ Control caching behavior to optimize performance, especially when using dynamic variables in prompts.
266
+
267
+ ### Custom Cache Key
268
+
269
+ Use `cacheKey` to prevent cache pollution when prompts contain variables:
270
+
271
+ ```javascript
272
+ // ❌ Without cacheKey - creates new cache entry for each email value
273
+ const email = 'user@example.com';
274
+ await testdriver.find(`input for ${email}`); // Cache miss every time
275
+
276
+ // ✅ With cacheKey - reuses cache regardless of variable
277
+ const email = 'user@example.com';
278
+ await testdriver.find(`input for ${email}`, {
279
+ cacheKey: 'email-input'
280
+ });
281
+
282
+ // Also useful for dynamic IDs, names, or other changing data
283
+ const orderId = generateOrderId();
284
+ await testdriver.find(`order ${orderId} status`, {
285
+ cacheKey: 'order-status' // Same cache for all orders
286
+ });
287
+ ```
288
+
289
+ ### Cache Threshold
290
+
291
+ Control how similar a cached result must be to reuse it:
292
+
293
+ ```javascript
294
+ // Default: 95% similarity required
295
+ await testdriver.find('submit button');
296
+
297
+ // Strict threshold - 99% similarity required
298
+ await testdriver.find('submit button', {
299
+ cacheThreshold: 0.01
300
+ });
301
+
302
+ // Disable cache entirely for this call
303
+ await testdriver.find('submit button', {
304
+ cacheThreshold: -1
305
+ });
306
+
307
+ // Combine cacheKey with threshold
308
+ await testdriver.find('submit button', {
309
+ cacheKey: 'submit-btn',
310
+ cacheThreshold: 0.01
311
+ });
312
+ ```
313
+
314
+ <Tip>
315
+ By default, TestDriver auto-generates a cache key from the SHA-256 hash of your test file. When you modify your test file, the hash changes automatically, invalidating stale cache entries.
316
+ </Tip>
317
+
318
+ ### Manual Polling (Alternative)
319
+
320
+ If you need custom polling logic:
321
+
322
+ ```javascript
323
+ async function waitForElement(testdriver, description, timeout = 30000) {
324
+ const startTime = Date.now();
325
+
326
+ while (Date.now() - startTime < timeout) {
327
+ const element = await testdriver.find(description);
328
+ if (element.found()) return element;
329
+ await new Promise(r => setTimeout(r, 1000));
330
+ }
331
+
332
+ throw new Error(`Element "${description}" not found after ${timeout}ms`);
333
+ }
334
+
335
+ // Usage
336
+ const button = await waitForElement(testdriver, 'submit button', 10000);
337
+ await button.click();
338
+ ```
339
+
340
+ ## Use Cases
341
+
342
+ <AccordionGroup>
343
+ <Accordion title="Form Fields">
344
+ ```javascript
345
+ const emailField = await testdriver.find('email input field');
346
+ await emailField.click();
347
+ await testdriver.type('user@example.com');
348
+
349
+ const passwordField = await testdriver.find('password input');
350
+ await passwordField.click();
351
+ await testdriver.type('MyP@ssw0rd');
352
+ ```
353
+ </Accordion>
354
+
355
+ <Accordion title="Buttons and Links">
356
+ ```javascript
357
+ const submitBtn = await testdriver.find('submit button');
358
+ await submitBtn.click();
359
+
360
+ const cancelLink = await testdriver.find('cancel link');
361
+ await cancelLink.click();
362
+
363
+ const menuIcon = await testdriver.find('hamburger menu icon');
364
+ await menuIcon.click();
365
+ ```
366
+ </Accordion>
367
+
368
+ <Accordion title="Dynamic Content">
369
+ ```javascript
370
+ // Wait for loading to complete
371
+ let content;
372
+ for (let i = 0; i < 30; i++) {
373
+ content = await testdriver.find('results table');
374
+ if (content.found()) break;
375
+ await new Promise(r => setTimeout(r, 1000));
376
+ }
377
+
378
+ // Interact with loaded content
379
+ const firstRow = await testdriver.find('first row in the results table');
380
+ await firstRow.click();
381
+ ```
382
+ </Accordion>
383
+
384
+ <Accordion title="Complex UI Elements">
385
+ ```javascript
386
+ // Modals and dialogs
387
+ const modal = await testdriver.find('confirmation dialog');
388
+ if (modal.found()) {
389
+ const confirmBtn = await testdriver.find('confirm button in the dialog');
390
+ await confirmBtn.click();
391
+ }
392
+
393
+ // Dropdown menus
394
+ const dropdown = await testdriver.find('country dropdown');
395
+ await dropdown.click();
396
+
397
+ const option = await testdriver.find('United States option');
398
+ await option.click();
399
+ ```
400
+ </Accordion>
401
+ </AccordionGroup>
402
+
403
+ ## Complete Example
404
+
405
+ ```javascript
406
+ import { beforeAll, afterAll, describe, it, expect } from 'vitest';
407
+ import TestDriver from 'testdriverai';
408
+
409
+ describe('Element Finding', () => {
410
+ let testdriver;
411
+
412
+ beforeAll(async () => {
413
+ client = new TestDriver(process.env.TD_API_KEY);
414
+ await testdriver.auth();
415
+ await testdriver.connect();
416
+ });
417
+
418
+ afterAll(async () => {
419
+ await testdriver.disconnect();
420
+ });
421
+
422
+ it('should find and interact with elements', async () => {
423
+ await testdriver.focusApplication('Google Chrome');
424
+
425
+ // Find login form elements
426
+ const usernameField = await testdriver.find('username input field');
427
+ expect(usernameField.found()).toBe(true);
428
+
429
+ await usernameField.click();
430
+ await testdriver.type('testuser');
431
+
432
+ // Find with context
433
+ const passwordField = await testdriver.find('password input below username');
434
+ await passwordField.click();
435
+ await testdriver.type('password123');
436
+
437
+ // Find button
438
+ const submitBtn = await testdriver.find('green submit button');
439
+ expect(submitBtn.found()).toBe(true);
440
+
441
+ console.log('Button location:', submitBtn.centerX, submitBtn.centerY);
442
+
443
+ await submitBtn.click();
444
+
445
+ // Wait for success message
446
+ let successMsg;
447
+ for (let i = 0; i < 10; i++) {
448
+ successMsg = await testdriver.find('success notification');
449
+ if (successMsg.found()) break;
450
+ await new Promise(r => setTimeout(r, 1000));
451
+ }
452
+
453
+ expect(successMsg.found()).toBe(true);
454
+ });
455
+ });
456
+ ```
457
+
458
+ ## Related Methods
459
+
460
+ - [`click()`](/v7/click) - Click on found elements
461
+ - [`hover()`](/v7/hover) - Hover over elements
462
+ - [`assert()`](/v7/assert) - Verify element states
463
+ - [Elements Reference](/v7/elements) - Complete Element API
464
+
465
+ ---
466
+
467
+ ## findAll()
468
+
469
+ Locate **all elements** matching a description, rather than just one.
470
+
471
+ ### Syntax
472
+
473
+ ```javascript
474
+ const elements = await testdriver.findAll(description, options)
475
+ ```
476
+
477
+ ### Parameters
478
+
479
+ <ParamField path="description" type="string" required>
480
+ Natural language description of elements to find
481
+ </ParamField>
482
+
483
+ <ParamField path="options" type="object | number">
484
+ Optional cache options (same as `find()`)
485
+
486
+ <Expandable title="properties">
487
+ <ParamField path="cacheKey" type="string">
488
+ Cache key for storing element location
489
+ </ParamField>
490
+
491
+ <ParamField path="cacheThreshold" type="number" default={-1}>
492
+ Similarity threshold (0-1) for cache matching. Set to -1 to disable cache.
493
+ </ParamField>
494
+ </Expandable>
495
+ </ParamField>
496
+
497
+ ### Returns
498
+
499
+ `Promise<Element[]>` - Array of Element instances
500
+
501
+ ### Examples
502
+
503
+ #### Basic Usage
504
+
505
+ ```javascript
506
+ // Find all matching elements
507
+ const buttons = await testdriver.findAll('button');
508
+ console.log(`Found ${buttons.length} buttons`);
509
+
510
+ // Interact with specific element
511
+ if (buttons.length > 0) {
512
+ await buttons[0].click(); // Click first button
513
+ }
514
+
515
+ // Iterate over all
516
+ for (const button of buttons) {
517
+ console.log(`Button at (${button.x}, ${button.y})`);
518
+ }
519
+ ```
520
+
521
+ #### Finding Multiple Items
522
+
523
+ ```javascript
524
+ // Find all list items
525
+ const items = await testdriver.findAll('list item');
526
+
527
+ // Find specific item by index
528
+ const thirdItem = items[2];
529
+ await thirdItem.click();
530
+
531
+ // Check all items
532
+ for (let i = 0; i < items.length; i++) {
533
+ console.log(`Item ${i + 1}: ${items[i].text || 'No text'}`);
534
+ }
535
+ ```
536
+
537
+ #### With Caching
538
+
539
+ ```javascript
540
+ // Cache element locations for faster subsequent runs
541
+ const menuItems = await testdriver.findAll('menu item', {
542
+ cacheKey: 'main-menu-items'
543
+ });
544
+
545
+ // First run: ~2-3 seconds (AI call)
546
+ // Subsequent runs: ~100ms (cache hit)
547
+ ```
548
+
549
+ #### Empty Results
550
+
551
+ ```javascript
552
+ // Returns empty array if nothing found (doesn't throw error)
553
+ const errors = await testdriver.findAll('error message');
554
+
555
+ if (errors.length === 0) {
556
+ console.log('No errors found - test passed!');
557
+ } else {
558
+ console.log(`Found ${errors.length} errors`);
559
+ }
560
+ ```
561
+
562
+ ### Differences from find()
563
+
564
+ | Feature | find() | findAll() |
565
+ |---------|--------|-----------|
566
+ | Return type | Single `Element` | Array of `Element[]` |
567
+ | If nothing found | Throws `ElementNotFoundError` | Returns empty array `[]` |
568
+ | Chainable | ✅ Yes: `await find('button').click()` | ❌ No (returns array) |
569
+ | Use case | One specific element | Multiple similar elements |
570
+ | Cache support | ✅ Yes | ✅ Yes |
571
+
572
+ ### Use Cases
573
+
574
+ <AccordionGroup>
575
+ <Accordion title="Table Rows">
576
+ ```javascript
577
+ // Find all rows in a table
578
+ const rows = await testdriver.findAll('table row');
579
+
580
+ // Click every row
581
+ for (const row of rows) {
582
+ await row.click();
583
+ await new Promise(r => setTimeout(r, 500)); // Wait between clicks
584
+ }
585
+
586
+ // Or click specific row
587
+ await rows[2].click(); // Click third row
588
+ ```
589
+ </Accordion>
590
+
591
+ <Accordion title="Checkboxes/Radio Buttons">
592
+ ```javascript
593
+ // Find all checkboxes
594
+ const checkboxes = await testdriver.findAll('checkbox');
595
+
596
+ // Check all boxes
597
+ for (const checkbox of checkboxes) {
598
+ await checkbox.click();
599
+ }
600
+
601
+ // Or select first unchecked
602
+ const unchecked = checkboxes[0];
603
+ await unchecked.click();
604
+ ```
605
+ </Accordion>
606
+
607
+ <Accordion title="Navigation Links">
608
+ ```javascript
609
+ // Find all navigation links
610
+ const navLinks = await testdriver.findAll('navigation link');
611
+
612
+ // Validate all are present
613
+ expect(navLinks.length).toBeGreaterThan(0);
614
+
615
+ // Click specific link by text
616
+ const homeLink = navLinks.find(link =>
617
+ link.text?.toLowerCase().includes('home')
618
+ );
619
+
620
+ if (homeLink) {
621
+ await homeLink.click();
622
+ }
623
+ ```
624
+ </Accordion>
625
+
626
+ <Accordion title="Conditional Interactions">
627
+ ```javascript
628
+ // Check if any error messages exist
629
+ const errors = await testdriver.findAll('error message');
630
+
631
+ if (errors.length > 0) {
632
+ console.log(`Found ${errors.length} validation errors`);
633
+
634
+ // Log each error location
635
+ errors.forEach((error, i) => {
636
+ console.log(`Error ${i + 1} at (${error.x}, ${error.y})`);
637
+ });
638
+ } else {
639
+ console.log('Form validation passed!');
640
+ }
641
+ ```
642
+ </Accordion>
643
+ </AccordionGroup>
644
+
645
+ ### Complete Example
646
+
647
+ ```javascript
648
+ import { test, expect } from 'vitest';
649
+ import { chrome } from 'testdriverai/presets';
650
+
651
+ test('select multiple items from list', async (context) => {
652
+ const { testdriver } = await chrome(context, {
653
+ url: 'https://example.com/products'
654
+ });
655
+
656
+ // Find all product cards
657
+ const products = await testdriver.findAll('product card');
658
+
659
+ expect(products.length).toBeGreaterThan(0);
660
+ console.log(`Found ${products.length} products`);
661
+
662
+ // Click first 3 products
663
+ const productsToSelect = Math.min(3, products.length);
664
+
665
+ for (let i = 0; i < productsToSelect; i++) {
666
+ await products[i].click();
667
+ console.log(`Selected product ${i + 1}`);
668
+ await new Promise(r => setTimeout(r, 500)); // Brief pause
669
+ }
670
+
671
+ // Verify selections
672
+ const selectedBadges = await testdriver.findAll('selected badge');
673
+ expect(selectedBadges.length).toBe(productsToSelect);
674
+ });
675
+ ```
676
+
677
+ ### Best Practices
678
+
679
+ <Check>
680
+ **Handle empty arrays gracefully**
681
+
682
+ ```javascript
683
+ // ✅ Good - check length first
684
+ const items = await testdriver.findAll('list item');
685
+ if (items.length > 0) {
686
+ await items[0].click();
687
+ }
688
+
689
+ // ❌ Bad - may throw error
690
+ const items = await testdriver.findAll('list item');
691
+ await items[0].click(); // Error if array is empty!
692
+ ```
693
+ </Check>
694
+
695
+ <Check>
696
+ **Use find() for single elements**
697
+
698
+ ```javascript
699
+ // ✅ Use find() when you need exactly one
700
+ const submitBtn = await testdriver.find('submit button');
701
+ await submitBtn.click();
702
+
703
+ // ❌ Unnecessary - findAll() returns array
704
+ const buttons = await testdriver.findAll('submit button');
705
+ await buttons[0].click(); // Extra array handling
706
+ ```
707
+ </Check>
708
+
709
+ <Check>
710
+ **Cache for performance**
711
+
712
+ ```javascript
713
+ // First run - slow (AI call)
714
+ const items = await testdriver.findAll('menu item', {
715
+ cacheKey: 'menu-items'
716
+ });
717
+
718
+ // Subsequent runs - fast (cache hit)
719
+ // ~10-20x faster than without cache
720
+ ```
721
+ </Check>