@testdriverai/agent 7.9.32-test → 7.9.34-test

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 (77) hide show
  1. package/ai/skills/testdriver:ai/SKILL.md +204 -0
  2. package/ai/skills/testdriver:assert/SKILL.md +315 -0
  3. package/ai/skills/testdriver:aws-setup/SKILL.md +448 -0
  4. package/ai/skills/testdriver:cache/SKILL.md +221 -0
  5. package/ai/skills/testdriver:caching/SKILL.md +124 -0
  6. package/ai/skills/testdriver:captcha/SKILL.md +158 -0
  7. package/ai/skills/testdriver:ci-cd/SKILL.md +602 -0
  8. package/ai/skills/testdriver:click/SKILL.md +286 -0
  9. package/ai/skills/testdriver:client/SKILL.md +477 -0
  10. package/ai/skills/testdriver:customizing-devices/SKILL.md +319 -0
  11. package/ai/skills/testdriver:dashcam/SKILL.md +451 -0
  12. package/ai/skills/testdriver:debugging-with-screenshots/SKILL.md +415 -0
  13. package/ai/skills/testdriver:device-config/SKILL.md +317 -0
  14. package/ai/skills/testdriver:double-click/SKILL.md +102 -0
  15. package/ai/skills/testdriver:elements/SKILL.md +605 -0
  16. package/ai/skills/testdriver:enterprise/SKILL.md +7 -0
  17. package/ai/skills/testdriver:errors/SKILL.md +246 -0
  18. package/ai/skills/testdriver:events/SKILL.md +356 -0
  19. package/ai/skills/testdriver:exec/SKILL.md +317 -0
  20. package/ai/skills/testdriver:find/SKILL.md +860 -0
  21. package/ai/skills/testdriver:focus-application/SKILL.md +293 -0
  22. package/ai/skills/testdriver:generating-tests/SKILL.md +36 -0
  23. package/ai/skills/testdriver:hosted/SKILL.md +156 -0
  24. package/ai/skills/testdriver:hover/SKILL.md +278 -0
  25. package/ai/skills/testdriver:locating-elements/SKILL.md +71 -0
  26. package/ai/skills/testdriver:making-assertions/SKILL.md +32 -0
  27. package/ai/skills/testdriver:mcp/SKILL.md +7 -0
  28. package/ai/skills/testdriver:mouse-down/SKILL.md +161 -0
  29. package/ai/skills/testdriver:mouse-up/SKILL.md +164 -0
  30. package/ai/skills/testdriver:parse/SKILL.md +236 -0
  31. package/ai/skills/testdriver:performing-actions/SKILL.md +53 -0
  32. package/ai/skills/testdriver:press-keys/SKILL.md +348 -0
  33. package/ai/skills/testdriver:provision/SKILL.md +331 -0
  34. package/ai/skills/testdriver:quickstart/SKILL.md +172 -0
  35. package/ai/skills/testdriver:redraw/SKILL.md +214 -0
  36. package/ai/skills/testdriver:reusable-code/SKILL.md +249 -0
  37. package/ai/skills/testdriver:right-click/SKILL.md +123 -0
  38. package/ai/skills/testdriver:running-tests/SKILL.md +185 -0
  39. package/ai/skills/testdriver:screenshot/SKILL.md +248 -0
  40. package/ai/skills/testdriver:screenshots/SKILL.md +184 -0
  41. package/ai/skills/testdriver:scroll/SKILL.md +205 -0
  42. package/ai/skills/testdriver:secrets/SKILL.md +115 -0
  43. package/ai/skills/testdriver:self-hosted/SKILL.md +147 -0
  44. package/ai/skills/testdriver:test-results-json/SKILL.md +257 -0
  45. package/ai/skills/testdriver:testdriver/SKILL.md +624 -0
  46. package/ai/skills/testdriver:type/SKILL.md +357 -0
  47. package/ai/skills/testdriver:variables/SKILL.md +111 -0
  48. package/ai/skills/testdriver:wait/SKILL.md +50 -0
  49. package/ai/skills/testdriver:waiting-for-elements/SKILL.md +90 -0
  50. package/ai/skills/testdriver:what-is-testdriver/SKILL.md +54 -0
  51. package/docs/_data/examples-manifest.json +42 -42
  52. package/docs/v7/debugging-with-screenshots.mdx +14 -0
  53. package/docs/v7/examples/ai.mdx +1 -1
  54. package/docs/v7/examples/assert.mdx +1 -1
  55. package/docs/v7/examples/chrome-extension.mdx +2 -2
  56. package/docs/v7/examples/element-not-found.mdx +1 -1
  57. package/docs/v7/examples/exec-output.mdx +1 -1
  58. package/docs/v7/examples/exec-pwsh.mdx +1 -1
  59. package/docs/v7/examples/findall-coffee-icons.mdx +1 -1
  60. package/docs/v7/examples/focus-window.mdx +1 -1
  61. package/docs/v7/examples/hover-image.mdx +1 -1
  62. package/docs/v7/examples/hover-text-with-description.mdx +1 -1
  63. package/docs/v7/examples/hover-text.mdx +1 -1
  64. package/docs/v7/examples/installer.mdx +1 -1
  65. package/docs/v7/examples/launch-vscode-linux.mdx +1 -1
  66. package/docs/v7/examples/parse.mdx +1 -1
  67. package/docs/v7/examples/press-keys.mdx +1 -1
  68. package/docs/v7/examples/prompt.mdx +1 -1
  69. package/docs/v7/examples/scroll-keyboard.mdx +1 -1
  70. package/docs/v7/examples/scroll-until-image.mdx +1 -1
  71. package/docs/v7/examples/scroll.mdx +1 -1
  72. package/docs/v7/examples/type.mdx +1 -1
  73. package/docs/v7/examples/windows-installer.mdx +1 -1
  74. package/docs/v7/find.mdx +33 -0
  75. package/examples/chrome-extension.test.mjs +1 -1
  76. package/package.json +1 -1
  77. package/setup/aws/spawn-runner.sh +24 -3
@@ -0,0 +1,860 @@
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" default={10000}>
37
+ Maximum time in milliseconds to poll for the element. Retries every 5 seconds until found or timeout expires. Defaults to `10000` (10 seconds). Set to `0` to disable polling and make a single attempt.
38
+ </ParamField>
39
+
40
+ <ParamField path="confidence" type="number">
41
+ Minimum confidence threshold (0-1). If the AI's confidence score for the found element is below this value, the find will be treated as a failure (`element.found()` returns `false`). Useful for ensuring high-quality matches in critical test steps.
42
+ </ParamField>
43
+
44
+ <ParamField path="type" type="string">
45
+ Element type hint that wraps the description for better matching. Accepted values:
46
+ - `"text"` — Wraps the prompt as `The text "..."`
47
+ - `"image"` — Wraps the prompt as `The image "..."`
48
+ - `"ui"` — Wraps the prompt as `The UI element "..."`
49
+ - `"any"` — No wrapping, uses the description as-is (default behavior)
50
+ </ParamField>
51
+
52
+ <ParamField path="zoom" type="boolean" default={false}>
53
+ Two-phase zoom mode for better precision in crowded UIs with many similar elements. Disabled by default.
54
+ </ParamField>
55
+
56
+ <ParamField path="verify" type="boolean" default={false}>
57
+ Enable AI verification of the located element. When `true`, a second AI call checks that the coordinates returned actually correspond to the requested element, catching hallucinated or incorrect positions. Disabled by default for lower latency.
58
+ </ParamField>
59
+
60
+ <ParamField path="ai" type="object">
61
+ AI sampling configuration for this find call (overrides global `ai` config from constructor).
62
+
63
+ <Expandable title="properties">
64
+ <ParamField path="temperature" type="number">
65
+ Controls randomness. `0` = deterministic. Default: `0` for find verification.
66
+ </ParamField>
67
+
68
+ <ParamField path="top" type="object">
69
+ Sampling parameters
70
+
71
+ <Expandable title="properties">
72
+ <ParamField path="p" type="number">
73
+ Top-P (nucleus sampling). Range: 0-1.
74
+ </ParamField>
75
+
76
+ <ParamField path="k" type="number">
77
+ Top-K sampling. `1` = most deterministic.
78
+ </ParamField>
79
+ </Expandable>
80
+ </ParamField>
81
+ </Expandable>
82
+ </ParamField>
83
+ </Expandable>
84
+ </ParamField>
85
+
86
+ ## Returns
87
+
88
+ `Promise<Element>` - Element instance that has been automatically located
89
+
90
+ ## Examples
91
+
92
+ ### Basic Element Finding
93
+
94
+ ```javascript
95
+ // Find by role
96
+ const button = await testdriver.find('submit button');
97
+ const input = await testdriver.find('email input field');
98
+
99
+ // Find by text content
100
+ const link = await testdriver.find('Contact Us link');
101
+ const heading = await testdriver.find('Welcome heading');
102
+
103
+ // Find by visual appearance
104
+ const icon = await testdriver.find('red warning icon');
105
+ const image = await testdriver.find('company logo image');
106
+ ```
107
+
108
+ ### Finding with Context
109
+
110
+ ```javascript
111
+ // Provide location context
112
+ const field = await testdriver.find('username input in the login form');
113
+ const button = await testdriver.find('delete button in the top right corner');
114
+
115
+ // Describe nearby elements
116
+ const input = await testdriver.find('input field below the email label');
117
+ const checkbox = await testdriver.find('checkbox next to "Remember me"');
118
+
119
+ // Describe visual position
120
+ const menu = await testdriver.find('hamburger menu icon in the top left');
121
+ ```
122
+
123
+ ### Interacting with Found Elements
124
+
125
+ ```javascript
126
+ // Find and click
127
+ const submitBtn = await testdriver.find('submit button');
128
+ await submitBtn.click();
129
+
130
+ // Find and verify
131
+ const message = await testdriver.find('success message');
132
+ if (message.found()) {
133
+ console.log('Success message appeared');
134
+ }
135
+
136
+ // Find and extract info
137
+ const price = await testdriver.find('product price');
138
+ console.log('Price location:', price.coordinates);
139
+ console.log('Price text:', price.text);
140
+ ```
141
+
142
+ ## Element Object
143
+
144
+ The returned `Element` object provides:
145
+
146
+ ### Methods
147
+
148
+ - `found()` - Check if element was located
149
+ - `click(action)` - Click the element
150
+ - `hover()` - Hover over the element
151
+ - `doubleClick()` - Double-click the element
152
+ - `rightClick()` - Right-click the element
153
+ - `find(newDescription)` - Re-locate with optional new description
154
+
155
+ ### Properties
156
+
157
+ - `coordinates` - Element position `{x, y, centerX, centerY}`
158
+ - `x`, `y` - Top-left coordinates
159
+ - `centerX`, `centerY` - Center coordinates
160
+ - `text` - Text content (if available)
161
+ - `screenshot` - Base64 screenshot (if available)
162
+ - `confidence` - AI confidence score
163
+ - `width`, `height` - Element dimensions
164
+ - `boundingBox` - Complete bounding box
165
+
166
+ See [Elements Reference](/v7/elements) for complete details.
167
+
168
+ ### JSON Serialization
169
+
170
+ Elements can be safely serialized using `JSON.stringify()` for logging and debugging. Circular references are automatically removed:
171
+
172
+ ```javascript
173
+ const element = await testdriver.find('login button');
174
+
175
+ // Safe to stringify - no circular reference errors
176
+ console.log(JSON.stringify(element, null, 2));
177
+
178
+ // Output includes useful debugging info:
179
+ // {
180
+ // "description": "login button",
181
+ // "coordinates": { "x": 100, "y": 200, "centerX": 150, "centerY": 225 },
182
+ // "found": true,
183
+ // "threshold": 0.01,
184
+ // "x": 100,
185
+ // "y": 200,
186
+ // "cache": {
187
+ // "hit": true,
188
+ // "strategy": "pixel-diff",
189
+ // "createdAt": "2025-12-09T10:30:00Z",
190
+ // "diffPercent": 0.0023,
191
+ // "imageUrl": "https://..."
192
+ // },
193
+ // "similarity": 0.98,
194
+ // "confidence": 0.95,
195
+ // "selector": "button#login",
196
+ // "aiResponse": "Found the blue login button..."
197
+ // }
198
+ ```
199
+
200
+ This is useful for:
201
+ - Debugging element detection issues
202
+ - Logging test execution details
203
+ - Sharing element information across processes
204
+ - Analyzing cache performance
205
+
206
+ ## Best Practices
207
+
208
+ <Check>
209
+ **Be specific in descriptions**
210
+
211
+ More specific descriptions improve accuracy:
212
+
213
+ ```javascript
214
+ // ✅ Good
215
+ await testdriver.find('blue submit button below the email field');
216
+
217
+ // ❌ Too vague
218
+ await testdriver.find('button');
219
+ ```
220
+ </Check>
221
+
222
+ <Check>
223
+ **Always check if found**
224
+
225
+ Verify elements were located before interacting:
226
+
227
+ ```javascript
228
+ const element = await testdriver.find('login button');
229
+ if (!element.found()) {
230
+ throw new Error('Login button not found');
231
+ }
232
+ await element.click();
233
+ ```
234
+ </Check>
235
+
236
+ <Check>
237
+ **Include visual or positional context**
238
+
239
+ ```javascript
240
+ // Include color
241
+ await testdriver.find('red error icon');
242
+
243
+ // Include position
244
+ await testdriver.find('search button in the top navigation bar');
245
+
246
+ // Include nearby text
247
+ await testdriver.find('checkbox next to "I agree to terms"');
248
+ ```
249
+ </Check>
250
+
251
+ ## Confidence Threshold
252
+
253
+ Require a minimum AI confidence score for element matches. If the confidence is below the threshold, `find()` treats the result as not found:
254
+
255
+ ```javascript
256
+ // Require at least 90% confidence
257
+ const element = await testdriver.find('submit button', { confidence: 0.9 });
258
+
259
+ if (!element.found()) {
260
+ // AI found something but wasn't confident enough
261
+ throw new Error('Could not confidently locate submit button');
262
+ }
263
+
264
+ await element.click();
265
+ ```
266
+
267
+ This is useful for:
268
+ - Critical test steps where an incorrect click could cause cascading failures
269
+ - Distinguishing between similar elements (e.g., multiple buttons)
270
+ - Failing fast when the UI has changed unexpectedly
271
+
272
+ ```javascript
273
+ // Combine with timeout for robust polling with confidence gate
274
+ const element = await testdriver.find('success notification', {
275
+ confidence: 0.85,
276
+ timeout: 15000,
277
+ });
278
+ ```
279
+
280
+ <Tip>
281
+ The `confidence` value is a float between 0 and 1 (e.g., `0.9` = 90%). The AI returns its confidence with each find result, which you can also read from `element.confidence` after a successful find.
282
+ </Tip>
283
+ ## Element Type
284
+
285
+ Use the `type` option to hint what kind of element you're looking for. This wraps your description into a more specific prompt for the AI, improving match accuracy — especially when users provide short or ambiguous descriptions.
286
+
287
+ ```javascript
288
+ // Find text on the page
289
+ const label = await testdriver.find('Sign In', { type: 'text' });
290
+ // AI prompt becomes: The text "Sign In"
291
+
292
+ // Find an image
293
+ const logo = await testdriver.find('company logo', { type: 'image' });
294
+ // AI prompt becomes: The image "company logo"
295
+
296
+ // Find a UI element (button, input, checkbox, etc.)
297
+ const btn = await testdriver.find('Submit', { type: 'ui' });
298
+ // AI prompt becomes: The UI element "Submit"
299
+
300
+ // No wrapping — same as omitting the option
301
+ const el = await testdriver.find('the blue submit button', { type: 'any' });
302
+ ```
303
+
304
+ | Type | Prompt sent to AI |
305
+ |------|----|
306
+ | `"text"` | `The text "..."` |
307
+ | `"image"` | `The image "..."` |
308
+ | `"ui"` | `The UI element "..."` |
309
+ | `"any"` | Original description (no wrapping) |
310
+
311
+ <Tip>
312
+ This is particularly useful for short descriptions like `"Submit"` or `"Login"` where the AI may not know whether to look for a button, a link, or visible text. Specifying `type` removes the ambiguity.
313
+ </Tip>
314
+ ## Polling for Dynamic Elements
315
+
316
+ By default, `find()` polls for up to 10 seconds (retrying every 5 seconds) until the element is found. You can customize this with the `timeout` option:
317
+
318
+ ```javascript
319
+ // Uses default 10s timeout - polls every 5 seconds
320
+ const element = await testdriver.find('login button');
321
+ await element.click();
322
+
323
+ // Custom timeout - wait up to 30 seconds
324
+ const element = await testdriver.find('login button', { timeout: 30000 });
325
+ await element.click();
326
+
327
+ // Disable polling - single attempt only
328
+ const element = await testdriver.find('login button', { timeout: 0 });
329
+ ```
330
+
331
+ The `timeout` option:
332
+ - Defaults to `10000` (10 seconds)
333
+ - Retries finding the element every 5 seconds
334
+ - Stops when the element is found or the timeout expires
335
+ - Logs progress during polling
336
+ - Returns the element (check `element.found()` if not throwing on failure)
337
+ - Set to `0` to disable polling and make a single attempt
338
+
339
+ ## Zoom Mode
340
+
341
+ Zoom mode is **disabled by default**. It uses a two-phase approach for better precision when locating elements, especially in crowded UIs with many similar elements.
342
+
343
+ To enable zoom for a specific find call, pass `zoom: true`:
344
+
345
+ ```javascript
346
+ // Enable zoom for better precision in crowded UIs
347
+ const extensionsBtn = await testdriver.find('extensions puzzle icon in Chrome toolbar', { zoom: true });
348
+ await extensionsBtn.click();
349
+
350
+ // Without zoom (default)
351
+ const largeButton = await testdriver.find('big hero button');
352
+ ```
353
+
354
+ ### How Zoom Mode Works
355
+
356
+ 1. **Phase 1**: AI identifies the approximate location of the element
357
+ 2. **Phase 2**: A 30% crop of the screen is created around that location
358
+ 3. **Phase 3**: AI performs precise location on the zoomed/cropped image
359
+ 4. **Result**: Coordinates are converted back to absolute screen position
360
+
361
+ This two-phase approach gives the AI a higher-resolution view of the target area, improving accuracy when multiple similar elements are close together.
362
+
363
+ <Tip>
364
+ You may want to enable zoom with `zoom: true` when:
365
+ - Targeting small elements in crowded UIs with many similar elements
366
+ - You need extra precision for closely spaced UI elements
367
+ </Tip>
368
+
369
+ ## Verify Mode
370
+
371
+ Verify mode is **disabled by default**. When enabled, a second AI call checks that the coordinates returned by `find()` actually correspond to the requested element, catching hallucinated or incorrect positions.
372
+
373
+ ```javascript
374
+ // Enable verification for critical interactions
375
+ const deleteBtn = await testdriver.find('delete account button', { verify: true });
376
+ await deleteBtn.click();
377
+ ```
378
+
379
+ ### How Verify Mode Works
380
+
381
+ 1. **Phase 1**: AI locates the element and returns coordinates
382
+ 2. **Phase 2**: A second AI call examines the screenshot at those coordinates to confirm the element matches the description
383
+ 3. **Result**: If verification fails, the find is retried or marked as not found
384
+
385
+ ### Combining Zoom and Verify
386
+
387
+ For maximum accuracy, enable both `zoom` and `verify` together. This is useful for critical interactions where clicking the wrong element could cause cascading failures:
388
+
389
+ ```javascript
390
+ // Maximum accuracy: zoom for precision + verify to catch hallucinations
391
+ const element = await testdriver.find('small cancel icon next to the subscription', {
392
+ zoom: true,
393
+ verify: true,
394
+ });
395
+ await element.click();
396
+ ```
397
+
398
+ <Warning>
399
+ Both `zoom` and `verify` add extra AI calls per `find()` invocation, which increases latency and API usage. When both are enabled, each find may make up to 3 AI calls. **Rate limiting may occur** if many find calls use these options in rapid succession. Use them selectively for critical interactions rather than on every find call.
400
+ </Warning>
401
+
402
+ ## Cache Options
403
+
404
+ Control caching behavior to optimize performance, especially when using dynamic variables in prompts.
405
+
406
+ ### Custom Cache Key
407
+
408
+ Use `cacheKey` to prevent cache pollution when prompts contain variables:
409
+
410
+ ```javascript
411
+ // ❌ Without cacheKey - creates new cache entry for each email value
412
+ const email = 'user@example.com';
413
+ await testdriver.find(`input for ${email}`); // Cache miss every time
414
+
415
+ // ✅ With cacheKey - reuses cache regardless of variable
416
+ const email = 'user@example.com';
417
+ await testdriver.find(`input for ${email}`, {
418
+ cacheKey: 'email-input'
419
+ });
420
+
421
+ // Also useful for dynamic IDs, names, or other changing data
422
+ const orderId = generateOrderId();
423
+ await testdriver.find(`order ${orderId} status`, {
424
+ cacheKey: 'order-status' // Same cache for all orders
425
+ });
426
+ ```
427
+
428
+ ### Cache Threshold
429
+
430
+ Control how similar a cached result must be to reuse it:
431
+
432
+ ```javascript
433
+ // Default: 95% similarity required
434
+ await testdriver.find('submit button');
435
+
436
+ // Strict threshold - 99% similarity required
437
+ await testdriver.find('submit button', {
438
+ cacheThreshold: 0.01
439
+ });
440
+
441
+ // Disable cache entirely for this call
442
+ await testdriver.find('submit button', {
443
+ cacheThreshold: -1
444
+ });
445
+
446
+ // Combine cacheKey with threshold
447
+ await testdriver.find('submit button', {
448
+ cacheKey: 'submit-btn',
449
+ cacheThreshold: 0.01
450
+ });
451
+ ```
452
+
453
+ <Tip>
454
+ 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.
455
+ </Tip>
456
+
457
+ ### Manual Polling (Alternative)
458
+
459
+ If you need custom polling logic:
460
+
461
+ ```javascript
462
+ async function waitForElement(testdriver, description, timeout = 30000) {
463
+ const startTime = Date.now();
464
+
465
+ while (Date.now() - startTime < timeout) {
466
+ const element = await testdriver.find(description);
467
+ if (element.found()) return element;
468
+ await new Promise(r => setTimeout(r, 1000));
469
+ }
470
+
471
+ throw new Error(`Element "${description}" not found after ${timeout}ms`);
472
+ }
473
+
474
+ // Usage
475
+ const button = await waitForElement(testdriver, 'submit button', 10000);
476
+ await button.click();
477
+ ```
478
+
479
+ ## Use Cases
480
+
481
+ <AccordionGroup>
482
+ <Accordion title="Form Fields">
483
+ ```javascript
484
+ const emailField = await testdriver.find('email input field');
485
+ await emailField.click();
486
+ await testdriver.type('user@example.com');
487
+
488
+ const passwordField = await testdriver.find('password input');
489
+ await passwordField.click();
490
+ await testdriver.type('MyP@ssw0rd');
491
+ ```
492
+ </Accordion>
493
+
494
+ <Accordion title="Buttons and Links">
495
+ ```javascript
496
+ const submitBtn = await testdriver.find('submit button');
497
+ await submitBtn.click();
498
+
499
+ const cancelLink = await testdriver.find('cancel link');
500
+ await cancelLink.click();
501
+
502
+ const menuIcon = await testdriver.find('hamburger menu icon');
503
+ await menuIcon.click();
504
+ ```
505
+ </Accordion>
506
+
507
+ <Accordion title="Dynamic Content">
508
+ ```javascript
509
+ // Wait for loading to complete
510
+ let content;
511
+ for (let i = 0; i < 30; i++) {
512
+ content = await testdriver.find('results table');
513
+ if (content.found()) break;
514
+ await new Promise(r => setTimeout(r, 1000));
515
+ }
516
+
517
+ // Interact with loaded content
518
+ const firstRow = await testdriver.find('first row in the results table');
519
+ await firstRow.click();
520
+ ```
521
+ </Accordion>
522
+
523
+ <Accordion title="Complex UI Elements">
524
+ ```javascript
525
+ // Modals and dialogs
526
+ const modal = await testdriver.find('confirmation dialog');
527
+ if (modal.found()) {
528
+ const confirmBtn = await testdriver.find('confirm button in the dialog');
529
+ await confirmBtn.click();
530
+ }
531
+
532
+ // Dropdown menus
533
+ const dropdown = await testdriver.find('country dropdown');
534
+ await dropdown.click();
535
+
536
+ const option = await testdriver.find('United States option');
537
+ await option.click();
538
+ ```
539
+ </Accordion>
540
+ </AccordionGroup>
541
+
542
+ ## Complete Example
543
+
544
+ ```javascript
545
+ import { beforeAll, afterAll, describe, it, expect } from 'vitest';
546
+ import TestDriver from 'testdriverai';
547
+
548
+ describe('Element Finding', () => {
549
+ let testdriver;
550
+
551
+ beforeAll(async () => {
552
+ client = new TestDriver(process.env.TD_API_KEY);
553
+ await testdriver.auth();
554
+ await testdriver.connect();
555
+ });
556
+
557
+ afterAll(async () => {
558
+ await testdriver.disconnect();
559
+ });
560
+
561
+ it('should find and interact with elements', async () => {
562
+ await testdriver.focusApplication('Google Chrome');
563
+
564
+ // Find login form elements
565
+ const usernameField = await testdriver.find('username input field');
566
+ expect(usernameField.found()).toBe(true);
567
+
568
+ await usernameField.click();
569
+ await testdriver.type('testuser');
570
+
571
+ // Find with context
572
+ const passwordField = await testdriver.find('password input below username');
573
+ await passwordField.click();
574
+ await testdriver.type('password123');
575
+
576
+ // Find button
577
+ const submitBtn = await testdriver.find('green submit button');
578
+ expect(submitBtn.found()).toBe(true);
579
+
580
+ console.log('Button location:', submitBtn.centerX, submitBtn.centerY);
581
+
582
+ await submitBtn.click();
583
+
584
+ // Wait for success message
585
+ let successMsg;
586
+ for (let i = 0; i < 10; i++) {
587
+ successMsg = await testdriver.find('success notification');
588
+ if (successMsg.found()) break;
589
+ await new Promise(r => setTimeout(r, 1000));
590
+ }
591
+
592
+ expect(successMsg.found()).toBe(true);
593
+ });
594
+ });
595
+ ```
596
+
597
+ ## Related Methods
598
+
599
+ - [`click()`](/v7/click) - Click on found elements
600
+ - [`hover()`](/v7/hover) - Hover over elements
601
+ - [`assert()`](/v7/assert) - Verify element states
602
+ - [Elements Reference](/v7/elements) - Complete Element API
603
+
604
+ ---
605
+
606
+ ## findAll()
607
+
608
+ Locate **all elements** matching a description, rather than just one.
609
+
610
+ ### Syntax
611
+
612
+ ```javascript
613
+ const elements = await testdriver.findAll(description, options)
614
+ ```
615
+
616
+ ### Parameters
617
+
618
+ <ParamField path="description" type="string" required>
619
+ Natural language description of elements to find
620
+ </ParamField>
621
+
622
+ <ParamField path="options" type="object | number">
623
+ Optional cache options (same as `find()`)
624
+
625
+ <Expandable title="properties">
626
+ <ParamField path="cacheKey" type="string">
627
+ Cache key for storing element location
628
+ </ParamField>
629
+
630
+ <ParamField path="cacheThreshold" type="number" default={-1}>
631
+ Similarity threshold (0-1) for cache matching. Set to -1 to disable cache.
632
+ </ParamField>
633
+ </Expandable>
634
+ </ParamField>
635
+
636
+ ### Returns
637
+
638
+ `Promise<Element[]>` - Array of Element instances
639
+
640
+ ### Examples
641
+
642
+ #### Basic Usage
643
+
644
+ ```javascript
645
+ // Find all matching elements
646
+ const buttons = await testdriver.findAll('button');
647
+ console.log(`Found ${buttons.length} buttons`);
648
+
649
+ // Interact with specific element
650
+ if (buttons.length > 0) {
651
+ await buttons[0].click(); // Click first button
652
+ }
653
+
654
+ // Iterate over all
655
+ for (const button of buttons) {
656
+ console.log(`Button at (${button.x}, ${button.y})`);
657
+ }
658
+ ```
659
+
660
+ #### Finding Multiple Items
661
+
662
+ ```javascript
663
+ // Find all list items
664
+ const items = await testdriver.findAll('list item');
665
+
666
+ // Find specific item by index
667
+ const thirdItem = items[2];
668
+ await thirdItem.click();
669
+
670
+ // Check all items
671
+ for (let i = 0; i < items.length; i++) {
672
+ console.log(`Item ${i + 1}: ${items[i].text || 'No text'}`);
673
+ }
674
+ ```
675
+
676
+ #### With Caching
677
+
678
+ ```javascript
679
+ // Cache element locations for faster subsequent runs
680
+ const menuItems = await testdriver.findAll('menu item', {
681
+ cacheKey: 'main-menu-items'
682
+ });
683
+
684
+ // First run: ~2-3 seconds (AI call)
685
+ // Subsequent runs: ~100ms (cache hit)
686
+ ```
687
+
688
+ #### Empty Results
689
+
690
+ ```javascript
691
+ // Returns empty array if nothing found (doesn't throw error)
692
+ const errors = await testdriver.findAll('error message');
693
+
694
+ if (errors.length === 0) {
695
+ console.log('No errors found - test passed!');
696
+ } else {
697
+ console.log(`Found ${errors.length} errors`);
698
+ }
699
+ ```
700
+
701
+ ### Differences from find()
702
+
703
+ | Feature | find() | findAll() |
704
+ |---------|--------|-----------|
705
+ | Return type | Single `Element` | Array of `Element[]` |
706
+ | If nothing found | Throws `ElementNotFoundError` | Returns empty array `[]` |
707
+ | Chainable | ✅ Yes: `await find('button').click()` | ❌ No (returns array) |
708
+ | Use case | One specific element | Multiple similar elements |
709
+ | Cache support | ✅ Yes | ✅ Yes |
710
+
711
+ ### Use Cases
712
+
713
+ <AccordionGroup>
714
+ <Accordion title="Table Rows">
715
+ ```javascript
716
+ // Find all rows in a table
717
+ const rows = await testdriver.findAll('table row');
718
+
719
+ // Click every row
720
+ for (const row of rows) {
721
+ await row.click();
722
+ await new Promise(r => setTimeout(r, 500)); // Wait between clicks
723
+ }
724
+
725
+ // Or click specific row
726
+ await rows[2].click(); // Click third row
727
+ ```
728
+ </Accordion>
729
+
730
+ <Accordion title="Checkboxes/Radio Buttons">
731
+ ```javascript
732
+ // Find all checkboxes
733
+ const checkboxes = await testdriver.findAll('checkbox');
734
+
735
+ // Check all boxes
736
+ for (const checkbox of checkboxes) {
737
+ await checkbox.click();
738
+ }
739
+
740
+ // Or select first unchecked
741
+ const unchecked = checkboxes[0];
742
+ await unchecked.click();
743
+ ```
744
+ </Accordion>
745
+
746
+ <Accordion title="Navigation Links">
747
+ ```javascript
748
+ // Find all navigation links
749
+ const navLinks = await testdriver.findAll('navigation link');
750
+
751
+ // Validate all are present
752
+ expect(navLinks.length).toBeGreaterThan(0);
753
+
754
+ // Click specific link by text
755
+ const homeLink = navLinks.find(link =>
756
+ link.text?.toLowerCase().includes('home')
757
+ );
758
+
759
+ if (homeLink) {
760
+ await homeLink.click();
761
+ }
762
+ ```
763
+ </Accordion>
764
+
765
+ <Accordion title="Conditional Interactions">
766
+ ```javascript
767
+ // Check if any error messages exist
768
+ const errors = await testdriver.findAll('error message');
769
+
770
+ if (errors.length > 0) {
771
+ console.log(`Found ${errors.length} validation errors`);
772
+
773
+ // Log each error location
774
+ errors.forEach((error, i) => {
775
+ console.log(`Error ${i + 1} at (${error.x}, ${error.y})`);
776
+ });
777
+ } else {
778
+ console.log('Form validation passed!');
779
+ }
780
+ ```
781
+ </Accordion>
782
+ </AccordionGroup>
783
+
784
+ ### Complete Example
785
+
786
+ ```javascript
787
+ import { test, expect } from 'vitest';
788
+ import { chrome } from 'testdriverai/presets';
789
+
790
+ test('select multiple items from list', async (context) => {
791
+ const { testdriver } = await chrome(context, {
792
+ url: 'https://example.com/products'
793
+ });
794
+
795
+ // Find all product cards
796
+ const products = await testdriver.findAll('product card');
797
+
798
+ expect(products.length).toBeGreaterThan(0);
799
+ console.log(`Found ${products.length} products`);
800
+
801
+ // Click first 3 products
802
+ const productsToSelect = Math.min(3, products.length);
803
+
804
+ for (let i = 0; i < productsToSelect; i++) {
805
+ await products[i].click();
806
+ console.log(`Selected product ${i + 1}`);
807
+ await new Promise(r => setTimeout(r, 500)); // Brief pause
808
+ }
809
+
810
+ // Verify selections
811
+ const selectedBadges = await testdriver.findAll('selected badge');
812
+ expect(selectedBadges.length).toBe(productsToSelect);
813
+ });
814
+ ```
815
+
816
+ ### Best Practices
817
+
818
+ <Check>
819
+ **Handle empty arrays gracefully**
820
+
821
+ ```javascript
822
+ // ✅ Good - check length first
823
+ const items = await testdriver.findAll('list item');
824
+ if (items.length > 0) {
825
+ await items[0].click();
826
+ }
827
+
828
+ // ❌ Bad - may throw error
829
+ const items = await testdriver.findAll('list item');
830
+ await items[0].click(); // Error if array is empty!
831
+ ```
832
+ </Check>
833
+
834
+ <Check>
835
+ **Use find() for single elements**
836
+
837
+ ```javascript
838
+ // ✅ Use find() when you need exactly one
839
+ const submitBtn = await testdriver.find('submit button');
840
+ await submitBtn.click();
841
+
842
+ // ❌ Unnecessary - findAll() returns array
843
+ const buttons = await testdriver.findAll('submit button');
844
+ await buttons[0].click(); // Extra array handling
845
+ ```
846
+ </Check>
847
+
848
+ <Check>
849
+ **Cache for performance**
850
+
851
+ ```javascript
852
+ // First run - slow (AI call)
853
+ const items = await testdriver.findAll('menu item', {
854
+ cacheKey: 'menu-items'
855
+ });
856
+
857
+ // Subsequent runs - fast (cache hit)
858
+ // ~10-20x faster than without cache
859
+ ```
860
+ </Check>