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