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