@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/events.mdx CHANGED
@@ -1,358 +1,201 @@
1
1
  ---
2
2
  title: "Events"
3
3
  sidebarTitle: "Events"
4
- description: "Listen to SDK lifecycle events with wildcard support"
4
+ description: "Listen to what TestDriver does during a test through its event emitter."
5
5
  icon: "bolt"
6
6
  mode: "wide"
7
7
  ---
8
8
 
9
- ## Overview
10
-
11
- TestDriver uses [EventEmitter2](https://github.com/EventEmitter2/EventEmitter2) for its event system. Events use a namespace pattern with colons. They permit wildcard listeners.
12
-
13
- Use the emitter through `testdriver.emitter`:
14
-
15
- ```javascript
16
- testdriver.emitter.on('command:start', (data) => {
17
- console.log(`Running: ${data.command}`);
18
- });
19
- ```
20
-
21
- ### Configuration
22
-
23
- The internal emitter is created with:
24
-
25
- ```javascript
26
- new EventEmitter2({
27
- wildcard: true,
28
- delimiter: ':',
29
- maxListeners: 20,
30
- verboseMemoryLeak: false,
31
- ignoreErrors: false,
32
- });
33
- ```
34
-
35
- ### Wildcard Listeners
36
-
37
- Use `*` to match one level. Use `**` to match many levels:
38
-
39
- ```javascript
40
- // Match all log events
41
- testdriver.emitter.on('log:*', (message) => {
42
- console.log(message);
43
- });
44
-
45
- // Match all events in any namespace
46
- testdriver.emitter.on('**', (...args) => {
47
- console.log('Event:', this.event, args);
48
- });
49
- ```
50
-
51
- ## Event Reference
52
-
53
- ### Command Events
54
-
55
- Emitted during the execution of SDK commands (`click`, `type`, `find`, etc.).
56
-
57
- | Event | Payload |
58
- |---|---|
59
- | `command:start` | `{ command, depth, data, timestamp, sourcePosition }` |
60
- | `command:success` | `{ command, depth, data, duration, response, timestamp, sourcePosition }` |
61
- | `command:error` | `{ command, depth, data, error, duration, timestamp, sourcePosition }` |
62
- | `command:status` | `{ command, status: "executing", data, depth, timestamp }` |
63
- | `command:progress` | `{ command, status: "completed", timing, data, depth, timestamp }` |
64
-
65
- ```javascript
66
- testdriver.emitter.on('command:start', ({ command, data }) => {
67
- console.log(`Starting ${command}`, data);
68
- });
69
-
70
- testdriver.emitter.on('command:error', ({ command, error, duration }) => {
71
- console.error(`${command} failed after ${duration}ms: ${error}`);
72
- });
73
- ```
74
-
75
- ### Step Events
76
-
77
- Emitted for each AI reasoning step within a command.
78
-
79
- | Event | Payload |
80
- |---|---|
81
- | `step:start` | `{ stepIndex, prompt, commandCount, timestamp, sourcePosition }` |
82
- | `step:success` | `{ stepIndex, prompt, commandCount, duration, timestamp, sourcePosition }` |
83
- | `step:error` | `{ stepIndex, prompt, error, duration?, timestamp, sourcePosition? }` |
84
-
85
- ```javascript
86
- testdriver.emitter.on('step:start', ({ stepIndex, prompt }) => {
87
- console.log(`Step ${stepIndex}: ${prompt}`);
88
- });
89
- ```
90
-
91
- ### Test Events
92
-
93
- Emitted when a test file execution starts.
94
-
95
- | Event | Payload |
96
- |---|---|
97
- | `test:start` | `{ filePath, timestamp }` |
98
- | `test:success` | *Emitted on test completion* |
99
- | `test:error` | *Emitted on test failure* |
100
-
101
- ### Log Events
102
-
103
- Emitted for all log output from the SDK.
104
-
105
- | Event | Payload |
106
- |---|---|
107
- | `log:log` | `(message: string)` — general log message |
108
- | `log:warn` | `(message: string)` — warning message |
109
- | `log:debug` | `(message: string)` — debug output (only when `VERBOSE`/`DEBUG`/`TD_DEBUG` env set) |
110
- | `log:info` | `(message: string)` — informational message |
111
- | `log:error` | `(message: string)` — error message |
112
- | `log:narration` | `(message: string, overwrite?: boolean)` — in-place status line |
113
- | `log:markdown` | `(markdown: string)` — full static markdown content |
114
- | `log:markdown:start` | `(streamId: string)` — begin streaming markdown |
115
- | `log:markdown:chunk` | `(streamId: string, chunk: string)` — incremental chunk |
116
- | `log:markdown:end` | `(streamId: string)` — end streaming markdown |
9
+ TestDriver reports what it's doing through an event emitter. Get it with [`getEmitter()`](/client#getemitter) and listen with `on()`:
117
10
 
118
11
  ```javascript
119
- // Capture all logs
120
- testdriver.emitter.on('log:*', function (message) {
121
- console.log(`[${this.event}]`, message);
122
- });
123
- ```
124
-
125
- ### Screen Capture Events
126
-
127
- Emitted during screenshot capture.
12
+ const emitter = testdriver.getEmitter();
128
13
 
129
- | Event | Payload |
130
- |---|---|
131
- | `screen-capture:start` | `{ scale, silent, display }` |
132
- | `screen-capture:end` | `{ scale, silent, display }` |
133
- | `screen-capture:error` | `{ error, scale, silent, display }` |
134
-
135
- ### Sandbox Events
136
-
137
- Emitted for sandbox WebSocket lifecycle and communication.
138
-
139
- | Event | Payload |
140
- |---|---|
141
- | `sandbox:connected` | *No payload* — WebSocket connection established |
142
- | `sandbox:authenticated` | `{ traceId }` — authentication successful |
143
- | `sandbox:error` | `(err: Error \| string)` — connection or sandbox error |
144
- | `sandbox:sent` | `(message: object)` — WebSocket message sent |
145
- | `sandbox:received` | *No payload* — successful message reply received |
146
- | `sandbox:progress` | `{ step, message }` — sandbox setup progress |
147
-
148
- ```javascript
149
- testdriver.emitter.on('sandbox:connected', () => {
150
- console.log('Connected to sandbox');
151
- });
152
-
153
- testdriver.emitter.on('sandbox:progress', ({ step, message }) => {
154
- console.log(`Sandbox: [${step}] ${message}`);
14
+ emitter.on('sandbox:progress', ({ step, message }) => {
15
+ console.log(`[${step}] ${message}`);
155
16
  });
156
17
  ```
157
18
 
158
- ### Redraw Events
159
-
160
- Emitted during screen stability detection. See [Redraw](/redraw) for more details.
161
-
162
- | Event | Payload |
163
- |---|---|
164
- | `redraw:status` | `{ redraw: { enabled, settled, hasChangedFromInitial, consecutiveFramesStable, diffFromInitial, diffFromLast, text }, network: { enabled, settled, rxBytes, txBytes, text }, timeout: { isTimeout, elapsed, max, text } }` |
165
- | `redraw:complete` | `{ screenSettled, hasChangedFromInitial, consecutiveFramesStable, networkSettled, isTimeout, timeElapsed }` |
166
-
167
- ```javascript
168
- testdriver.emitter.on('redraw:complete', (result) => {
169
- if (result.isTimeout) {
170
- console.warn('Redraw timed out after', result.timeElapsed, 'ms');
171
- }
172
- });
173
- ```
19
+ The emitter is an [EventEmitter2](https://github.com/EventEmitter2/EventEmitter2) instance. Event names are segments separated by colons, such as `redraw:complete`.
174
20
 
175
- ### File Events
21
+ ## Listen in a test
176
22
 
177
- Emitted during file load/save operations in the agent.
23
+ The emitter exists as soon as you create the client, so add listeners before the first command:
178
24
 
179
- | Event | Payload |
180
- |---|---|
181
- | `file:start` | `{ operation: "load" \| "save" \| "run", filePath, timestamp }` |
182
- | `file:stop` | `{ operation, filePath, duration, success, sourceMap?, reason?, timestamp }` |
183
- | `file:load` | `{ filePath, size, timestamp }` |
184
- | `file:save` | `{ filePath, size, timestamp }` |
185
- | `file:diff` | `{ filePath, diff: { patches, sourceMaps, summary: { additions, deletions, modifications } }, timestamp }` |
186
- | `file:error` | `{ operation, filePath, error, duration?, timestamp }` |
25
+ ```javascript tests/login.test.mjs
26
+ import { describe, expect, it } from 'vitest';
27
+ import { TestDriver } from 'testdriverai/vitest/hooks';
187
28
 
188
- ### Error Events
29
+ describe('Login', () => {
30
+ it('shows an error for an empty form', async (context) => {
31
+ const testdriver = TestDriver(context);
32
+ const emitter = testdriver.getEmitter();
189
33
 
190
- Emitted for errors at various severity levels.
34
+ const clicks = [];
35
+ emitter.on('mouse-click', ({ x, y, button }) => {
36
+ clicks.push({ x, y, button });
37
+ });
191
38
 
192
- | Event | Payload |
193
- |---|---|
194
- | `error:fatal` | `(error: string \| Error)` — terminates the process |
195
- | `error:general` | `(message: string)` — non-fatal error |
196
- | `error:sandbox` | `(err: Error \| string)` — sandbox/WebSocket error |
39
+ await testdriver.provision.chrome({ url: 'https://testdriver-sandbox.vercel.app/login' });
40
+ await testdriver.find('Sign in button below the password field').click();
197
41
 
198
- ```javascript
199
- testdriver.emitter.on('error:*', function (err) {
200
- console.error(`[${this.event}]`, err);
42
+ console.log(clicks); // e.g. [{ x: 683, y: 412, button: 'left' }]
43
+ expect(await testdriver.assert('an error says the fields are required')).toBe(true);
44
+ });
201
45
  });
202
46
  ```
203
47
 
204
- ### SDK Events
205
-
206
- Emitted for API request lifecycle.
48
+ ## Use wildcards
207
49
 
208
- | Event | Payload |
209
- |---|---|
210
- | `sdk:request` | `{ path }` — outgoing API request |
211
- | `sdk:response` | `{ path }` — API response received |
212
- | `sdk:retry` | `{ path, attempt, error, delayMs }` — request retry |
213
-
214
- ### Other Events
215
-
216
- | Event | Payload |
217
- |---|---|
218
- | `exit` | `(exitCode: number)` — `0` for success, `1` for failure |
219
- | `status` | `(message: string)` — general status updates |
220
- | `mouse-click` | `{ x, y, button, click, double }` — mouse click performed |
221
- | `terminal:stdout` | Terminal stdout output |
222
- | `terminal:stderr` | Terminal stderr output |
223
-
224
- ## Practical Examples
225
-
226
- ### Custom Test Reporter
227
-
228
- ```javascript
229
- const results = [];
230
-
231
- testdriver.emitter.on('command:success', ({ command, duration }) => {
232
- results.push({ command, duration, status: 'pass' });
233
- });
234
-
235
- testdriver.emitter.on('command:error', ({ command, duration, error }) => {
236
- results.push({ command, duration, status: 'fail', error });
237
- });
238
-
239
- // After test completes
240
- afterAll(() => {
241
- console.table(results);
242
- });
243
- ```
244
-
245
- ### Progress Monitoring
50
+ `*` matches one segment. `**` matches any number of segments. Use a `function` listener, not an arrow function, to read the event's name from `this.event`:
246
51
 
247
52
  ```javascript
248
- testdriver.emitter.on('step:start', ({ stepIndex, prompt }) => {
249
- process.stdout.write(`\r Step ${stepIndex}: ${prompt}`);
250
- });
251
-
252
- testdriver.emitter.on('command:progress', ({ command, timing }) => {
253
- process.stdout.write(`\r ${command} completed in ${timing}ms`);
53
+ // Every error event: error:fatal, error:sdk, and so on
54
+ emitter.on('error:*', function (error) {
55
+ console.error(`[${this.event}]`, error);
254
56
  });
255
- ```
256
-
257
- ### Debug Logging
258
57
 
259
- ```javascript
260
- // Log every event (verbose)
261
- testdriver.emitter.on('**', function (...args) {
262
- console.debug(`[EVENT] ${this.event}`, ...args);
58
+ // Every event, with its arguments
59
+ emitter.on('**', function (...args) {
60
+ console.debug(this.event, ...args);
263
61
  });
264
62
  ```
265
63
 
266
- ## Types
267
-
268
- ```typescript
269
- interface CommandStartEvent {
270
- command: string;
271
- depth: number;
272
- data: Record<string, any>;
273
- timestamp: number;
274
- sourcePosition: SourcePosition;
275
- }
276
-
277
- interface CommandSuccessEvent {
278
- command: string;
279
- depth: number;
280
- data: Record<string, any>;
281
- duration: number;
282
- response: any;
283
- timestamp: number;
284
- sourcePosition: SourcePosition;
285
- }
286
-
287
- interface CommandErrorEvent {
288
- command: string;
289
- depth: number;
290
- data: Record<string, any>;
291
- error: string;
292
- duration: number;
293
- timestamp: number;
294
- sourcePosition: SourcePosition;
295
- }
296
-
297
- interface StepStartEvent {
298
- stepIndex: number;
299
- prompt: string;
300
- commandCount: number;
301
- timestamp: number;
302
- sourcePosition: SourcePosition;
303
- }
304
-
305
- interface StepSuccessEvent {
306
- stepIndex: number;
307
- prompt: string;
308
- commandCount: number;
309
- duration: number;
310
- timestamp: number;
311
- sourcePosition: SourcePosition;
312
- }
313
-
314
- interface RedrawStatusEvent {
315
- redraw: {
316
- enabled: boolean;
317
- settled: boolean;
318
- hasChangedFromInitial: boolean;
319
- consecutiveFramesStable: number;
320
- diffFromInitial: number;
321
- diffFromLast: number;
322
- text: string;
323
- };
324
- network: {
325
- enabled: boolean;
326
- settled: boolean;
327
- rxBytes: number;
328
- txBytes: number;
329
- text: string;
330
- };
331
- timeout: {
332
- isTimeout: boolean;
333
- elapsed: number;
334
- max: number;
335
- text: string;
336
- };
337
- }
338
-
339
- interface RedrawCompleteEvent {
340
- screenSettled: boolean;
341
- hasChangedFromInitial: boolean;
342
- consecutiveFramesStable: number;
343
- networkSettled: boolean;
344
- isTimeout: boolean;
345
- timeElapsed: number;
346
- }
347
-
348
- interface SandboxProgressEvent {
349
- step: string;
350
- message: string;
351
- }
352
-
353
- interface SourcePosition {
354
- file: string;
355
- line: number;
356
- column: number;
357
- }
358
- ```
64
+ `log:*` matches `log:log` and `log:warn` but not `log:markdown:start`, which has three segments. Use `log:**` to match all of them.
65
+
66
+ ## Log events
67
+
68
+ The SDK prints these to the console unless you turn logging off with the `logging` option.
69
+
70
+ | Event | Arguments | When |
71
+ | --- | --- | --- |
72
+ | `log:log` | `(message: string)` | General output, such as each action and `find()` |
73
+ | `log:warn` | `(message: string)` | A warning |
74
+ | `log:debug` | `(message: string)` | Debug detail. Always emitted, but printed only when `VERBOSE` or `TD_DEBUG` is set |
75
+ | `log:narration` | `(message: string, overwrite?: boolean)` | Progress detail for an action, such as how long a redraw wait took |
76
+ | `log:markdown` | `(markdown: string)` | A complete Markdown message from `ai()` |
77
+ | `log:markdown:start` | `(streamId: string)` | `ai()` starts streaming a Markdown message |
78
+ | `log:markdown:chunk` | `(streamId: string, chunk: string)` | The next piece of that message |
79
+ | `log:markdown:end` | `(streamId: string)` | The message is complete |
80
+
81
+ There's no `log:info` or `log:error` in the event list. Listen to `log:log` and `error:*` instead.
82
+
83
+ ## Error events
84
+
85
+ | Event | Arguments | When |
86
+ | --- | --- | --- |
87
+ | `error:fatal` | `(error: string \| Error)` | TestDriver hit an error it can't recover from |
88
+ | `error:general` | `(message: string)` | A non-fatal error |
89
+ | `error:sdk` | `({ message, code, ... })` | A request to the TestDriver API failed |
90
+ | `error:sandbox` | `(message: string)` | The sandbox reported an error, or the realtime connection to it failed |
91
+
92
+ ## Sandbox events
93
+
94
+ | Event | Arguments | When |
95
+ | --- | --- | --- |
96
+ | `sandbox:authenticated` | `({ traceId })` | The connection to the sandbox is authenticated |
97
+ | `sandbox:connected` | none | The client is connected to a sandbox |
98
+ | `sandbox:progress` | `({ step, message })` | A setup step while the sandbox starts, or recovery after a dropped connection |
99
+ | `sandbox:sent` | `(message: object)` | A message was sent to the sandbox |
100
+ | `sandbox:received` | none | The sandbox replied to a message |
101
+ | `sandbox:file` | `(message: object)` | The sandbox sent a file message |
102
+ | `show-window` | `(url: string)` | The live preview URL is ready to open |
103
+
104
+ ## Action events
105
+
106
+ | Event | Arguments | When |
107
+ | --- | --- | --- |
108
+ | `mouse-click` | `({ x, y, button, double })` | A click, double-click, right-click, `mouseDown()`, or `mouseUp()` was sent. `button` is `'left'` or `'right'`, and `double` is `true` for a double-click |
109
+ | `exec:output` | `({ chunk, requestId })` | A piece of output from a running [`exec()`](/exec) command |
110
+ | `screen-capture:start` | `({ scale, silent, display })` | TestDriver starts capturing the screen |
111
+ | `screen-capture:end` | `({ scale, silent, display })` | The capture finished |
112
+ | `screen-capture:error` | `({ error, scale, silent, display })` | The capture failed |
113
+
114
+ ## Redraw events
115
+
116
+ Emitted only when [redraw detection](/redraw) is on.
117
+
118
+ | Event | Arguments | When |
119
+ | --- | --- | --- |
120
+ | `redraw:status` | `(status: object)` | Every 500 ms while TestDriver waits for the screen to settle |
121
+ | `redraw:complete` | `(result: object)` | The wait ended, either settled or timed out |
122
+
123
+ <ResponseField name="redraw:status" type="object">
124
+ <Expandable title="properties">
125
+ <ResponseField name="redraw" type="object">
126
+ Screen state: `enabled`, `settled`, `hasChangedFromInitial`, `consecutiveFramesStable` (booleans), `diffFromInitial` and `diffFromLast` (percent of pixels that differ), and `text` (a short summary for the console).
127
+ </ResponseField>
128
+ <ResponseField name="network" type="object">
129
+ Network state: `enabled`, `settled` (booleans), `rxBytes` and `txBytes` (bytes since the last check), and `text`.
130
+ </ResponseField>
131
+ <ResponseField name="timeout" type="object">
132
+ Timing: `isTimeout` (boolean), `elapsed` and `max` (milliseconds), and `text`.
133
+ </ResponseField>
134
+ </Expandable>
135
+ </ResponseField>
136
+
137
+ <ResponseField name="redraw:complete" type="object">
138
+ <Expandable title="properties">
139
+ <ResponseField name="screenSettled" type="boolean">
140
+ Whether the screen settled. `true` when the screen check is off.
141
+ </ResponseField>
142
+ <ResponseField name="networkSettled" type="boolean">
143
+ Whether network traffic settled. `true` when the network check is off.
144
+ </ResponseField>
145
+ <ResponseField name="hasChangedFromInitial" type="boolean">
146
+ Whether the screen changed after the action.
147
+ </ResponseField>
148
+ <ResponseField name="consecutiveFramesStable" type="boolean">
149
+ Whether the last frames stopped changing.
150
+ </ResponseField>
151
+ <ResponseField name="isTimeout" type="boolean">
152
+ Whether the wait ended because it ran out of time.
153
+ </ResponseField>
154
+ <ResponseField name="timeElapsed" type="number">
155
+ How long the wait took, in milliseconds.
156
+ </ResponseField>
157
+ </Expandable>
158
+ </ResponseField>
159
+
160
+ ## API request events
161
+
162
+ | Event | Arguments | When |
163
+ | --- | --- | --- |
164
+ | `sdk:request` | `({ path })` | A request to the TestDriver API starts |
165
+ | `sdk:response` | `({ path })` | The API responded |
166
+ | `sdk:retry` | `({ path, attempt, error, delayMs })` | A failed request will be retried after `delayMs` milliseconds |
167
+
168
+ ## ai() command events
169
+
170
+ These fire for the commands the AI runs inside [`ai()`](/errors#aierror). Commands you call yourself, such as `testdriver.click()`, don't emit them.
171
+
172
+ | Event | Arguments |
173
+ | --- | --- |
174
+ | `command:start` | `({ command, depth, data, timestamp, sourcePosition })` |
175
+ | `command:status` | `({ command, status: 'executing', data, depth, timestamp })` |
176
+ | `command:progress` | `({ command, status: 'completed', timing, data, depth, timestamp })` |
177
+ | `command:success` | `({ command, depth, data, duration, response, timestamp, sourcePosition })` |
178
+ | `command:error` | `({ command, depth, data, error, duration, timestamp, sourcePosition })` |
179
+
180
+ - `command` is the command name, and `data` is the full command the AI generated.
181
+ - `error` is the error message as a string.
182
+ - `timing` and `duration` are in milliseconds.
183
+
184
+ ## Other events
185
+
186
+ | Event | Arguments | When |
187
+ | --- | --- | --- |
188
+ | `runner:log` | `({ level, message, timestamp })` | A log line from the sandbox's runner. Sent only when debug logging is on |
189
+ | `exit` | `(exitCode: number)` | The agent stopped. `0` means success, `1` means failure |
190
+
191
+ The event list also contains names that Vitest tests don't emit:
192
+
193
+ - `step:*`, `test:*`, and `file:*` come from running YAML test files.
194
+ - `command:stop`, `command:location`, `sandbox:error`, `sandbox:disconnected`, `sdk:error`, `sdk:progress`, `status`, `mouse-move`, `terminal:stdout`, `terminal:stderr`, `matches:show`, and `vm:show` aren't currently emitted.
195
+
196
+ ## Related
197
+
198
+ - [getEmitter()](/client#getemitter): get the client's emitter
199
+ - [Redraw](/redraw): what the redraw events measure
200
+ - [Errors](/errors): the errors TestDriver throws
201
+ - [Debug a failing test](/debugging-with-screenshots): find out why a test failed
@@ -1,22 +1,16 @@
1
1
  ---
2
- title: "Hover Text With Description Test Example"
3
- sidebarTitle: "Hover Text With Description"
4
- description: "TestDriver SDK - Hover Text With Description Test (Vitest) Converted from: testdriver/acceptance/hover-text-with-description.yaml."
2
+ title: "Add an item to a cart"
3
+ sidebarTitle: "Add to cart"
4
+ description: "Log in to a demo store, add a product to the cart, and assert that it's there."
5
5
  icon: "hand-pointer"
6
6
  mode: "wide"
7
7
  ---
8
8
 
9
- ## Overview
9
+ This test logs in to a demo store, adds a product to the cart, and asserts that the cart has an item.
10
10
 
11
- TestDriver SDK - Hover Text With Description Test (Vitest) Converted from: testdriver/acceptance/hover-text-with-description.yaml
11
+ ## Watch it run
12
12
 
13
- Review the source code below to understand the implementation details and patterns used.
14
-
15
- ## Live Test Run
16
-
17
- Watch this test execute in a real sandbox environment:
18
-
19
- {/* hover-text-with-description.test.mjs output */}
13
+ {/* add-to-cart.test.mjs output */}
20
14
  <iframe
21
15
  src="https://api-test.testdriver.ai/api/v1/testdriver/testcase/6ad6b5f0022b6043aeec2beb/replay"
22
16
  width="100%"
@@ -25,20 +19,15 @@ Watch this test execute in a real sandbox environment:
25
19
  allow="fullscreen"
26
20
  />
27
21
 
28
- ## Source Code
29
-
30
- ```javascript title="hover-text-with-description.test.mjs"
31
- /**
32
- * TestDriver SDK - Hover Text With Description Test (Vitest)
33
- * Converted from: testdriver/acceptance/hover-text-with-description.yaml
34
- */
22
+ ## The test
35
23
 
24
+ ```javascript title="add-to-cart.test.mjs"
36
25
  import { describe, expect, it } from "vitest";
37
26
  import { TestDriver } from "testdriverai/vitest/hooks";
38
27
 
39
28
  /**
40
- * Perform login flow for SauceLabs demo app
41
- * @param {TestDriver} client - TestDriver client
29
+ * Log in to the TestDriver sandbox demo store.
30
+ * @param {object} client - TestDriver instance
42
31
  * @param {string} username - Username (default: 'standard_user')
43
32
  */
44
33
  async function performLogin(client, username = "standard_user") {
@@ -55,14 +44,11 @@ async function performLogin(client, username = "standard_user") {
55
44
 
56
45
  describe("Hover Text With Description Test", () => {
57
46
  it("should add TestDriver Hat to cart and verify", async (context) => {
58
- const testdriver = TestDriver(context, { ip: context.ip || process.env.TD_IP,
59
- headless: true,
60
- });
47
+ const testdriver = TestDriver(context);
61
48
  await testdriver.provision.chrome({
62
49
  url: "http://testdriver-sandbox.vercel.app/login",
63
50
  });
64
51
 
65
- //
66
52
  // Perform login first
67
53
  await performLogin(testdriver);
68
54
 
@@ -85,20 +71,29 @@ describe("Hover Text With Description Test", () => {
85
71
  });
86
72
  ```
87
73
 
88
- ## Running This Example
74
+ ## How it works
75
+
76
+ - Each `find()` description starts with the visible label, then adds context ("Add to Cart, add to cart button under TestDriver Hat").
77
+ - The context tells the AI which of several identical buttons to pick.
78
+ - `performLogin()` is the reusable helper from [Click an icon](/examples/click-icon). It reads the on-screen password with `extract()` and types it with `secret: true`.
79
+ - `assert()` checks the result in plain language, not by inspecting markup.
80
+
81
+ ## Run it
82
+
83
+ The test is in the [TestDriver SDK repository](https://github.com/testdriverai/testdriverai/tree/main/examples). Clone it, install dependencies, and run the file:
89
84
 
90
85
  ```bash
91
- # Clone the TestDriver repository
92
86
  git clone https://github.com/testdriverai/testdriverai
93
-
94
- # Install dependencies
95
87
  cd testdriverai
96
88
  npm install
97
-
98
- # Run this specific example
99
- vitest run examples/hover-text-with-description.test.mjs
89
+ npx vitest run examples/add-to-cart.test.mjs
100
90
  ```
101
91
 
102
- <Note>
103
- Make sure you have `TD_API_KEY` set in your environment. Get one at [testdriver.ai](https://testdriver.ai).
104
- </Note>
92
+ The test reads your API key from `TD_API_KEY`. Create a key at [console.testdriver.ai/team/api-keys](https://console.testdriver.ai/team/api-keys). Then set it in your environment or a `.env` file.
93
+
94
+ ## Related
95
+
96
+ - [find()](/find)
97
+ - [click()](/click)
98
+ - [Locate elements](/locating-elements)
99
+ - [Reuse code across tests](/reusable-code)