@testdriverai/agent 7.11.229-test → 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/docs.json CHANGED
@@ -16,34 +16,36 @@
16
16
  {
17
17
  "group": "Overview",
18
18
  "pages": [
19
- "/agent",
20
- "/what-is-testdriver",
21
19
  {
22
- "group": "Quickstart",
20
+ "group": "Get started",
23
21
  "icon": "rocket",
24
22
  "pages": [
25
23
  "/quickstart",
26
24
  "/quickstart-github",
25
+ "/quickstart-claude",
26
+ "/quickstart-vscode",
27
27
  "/mcp-setup"
28
28
  ]
29
29
  },
30
+ "/agent",
31
+ "/what-is-testdriver",
30
32
  {
31
33
  "group": "Examples",
32
34
  "icon": "code",
33
35
  "pages": [
34
36
  "/examples/assert",
35
- "/examples/element-not-found",
36
- "/examples/findall-coffee-icons",
37
- "/examples/hover-image",
38
- "/examples/hover-text-with-description",
39
- "/examples/hover-text",
40
- "/examples/installer",
41
- "/examples/launch-vscode-linux",
42
- "/examples/parse",
37
+ "/examples/type",
38
+ "/examples/click-button",
39
+ "/examples/click-icon",
40
+ "/examples/add-to-cart",
43
41
  "/examples/press-keys",
44
- "/examples/scroll-keyboard",
45
42
  "/examples/scroll",
46
- "/examples/type"
43
+ "/examples/navigate-and-scroll",
44
+ "/examples/findall-coffee-icons",
45
+ "/examples/element-not-found",
46
+ "/examples/parse",
47
+ "/examples/installer",
48
+ "/examples/launch-vscode-linux"
47
49
  ]
48
50
  }
49
51
  ]
@@ -52,15 +54,15 @@
52
54
  "group": "Using TestDriver",
53
55
  "pages": [
54
56
  "/generating-tests",
57
+ "/performing-actions",
55
58
  "/copilot/running-tests",
56
59
  "/debugging-with-screenshots",
57
60
  "/caching",
58
- "/performing-actions",
59
61
  "/copilot/auto-healing"
60
62
  ]
61
63
  },
62
64
  {
63
- "group": "Building & Maintaining Tests",
65
+ "group": "Building & maintaining tests",
64
66
  "pages": [
65
67
  "/quickstart-manual",
66
68
  {
@@ -114,7 +116,6 @@
114
116
  "group": "Integrations",
115
117
  "icon": "plug",
116
118
  "pages": [
117
- "/github",
118
119
  "/ci-cd"
119
120
  ]
120
121
  },
@@ -124,7 +125,7 @@
124
125
  "pages": [
125
126
  "/hosted",
126
127
  {
127
- "group": "Self-Hosting",
128
+ "group": "Self-hosting",
128
129
  "icon": "server",
129
130
  "pages": [
130
131
  "/self-hosted",
@@ -137,7 +138,7 @@
137
138
  ]
138
139
  },
139
140
  {
140
- "group": "Advanced Topics",
141
+ "group": "Advanced topics",
141
142
  "pages": [
142
143
  "/variables",
143
144
  "/secrets",
@@ -548,6 +549,62 @@
548
549
  {
549
550
  "source": "/v7/:slug*",
550
551
  "destination": "/:slug*"
552
+ },
553
+ {
554
+ "source": "/examples/hover-image",
555
+ "destination": "/examples/click-icon"
556
+ },
557
+ {
558
+ "source": "/examples/hover-text",
559
+ "destination": "/examples/click-button"
560
+ },
561
+ {
562
+ "source": "/examples/hover-text-with-description",
563
+ "destination": "/examples/add-to-cart"
564
+ },
565
+ {
566
+ "source": "/examples/scroll-keyboard",
567
+ "destination": "/examples/navigate-and-scroll"
568
+ },
569
+ {
570
+ "source": "/examples/ai",
571
+ "destination": "/generating-tests"
572
+ },
573
+ {
574
+ "source": "/examples/captcha-api",
575
+ "destination": "/captcha"
576
+ },
577
+ {
578
+ "source": "/examples/chrome-extension",
579
+ "destination": "/provision"
580
+ },
581
+ {
582
+ "source": "/examples/drag-and-drop",
583
+ "destination": "/mouse-down"
584
+ },
585
+ {
586
+ "source": "/examples/formatted-logging",
587
+ "destination": "/dashcam"
588
+ },
589
+ {
590
+ "source": "/examples/match-image",
591
+ "destination": "/find"
592
+ },
593
+ {
594
+ "source": "/examples/scroll-until-text",
595
+ "destination": "/scroll"
596
+ },
597
+ {
598
+ "source": "/claude-mcp-plugin",
599
+ "destination": "/quickstart-claude"
600
+ },
601
+ {
602
+ "source": "/github",
603
+ "destination": "/quickstart-github"
604
+ },
605
+ {
606
+ "source": "/elements",
607
+ "destination": "/find"
551
608
  }
552
609
  ],
553
610
  "integrations": {
@@ -1,102 +1,82 @@
1
1
  ---
2
- title: "doubleClick"
3
- description: "Perform a double-click action on an element or at specific coordinates"
2
+ title: "doubleClick()"
3
+ sidebarTitle: "doubleClick"
4
+ description: "Double-click an element you found, for example to open a file or select a word."
4
5
  icon: "hand-pointer"
5
6
  ---
6
7
 
7
- ## Overview
8
-
9
- The `doubleClick()` method does a double-click action on an element. You can call it on an [`Element`](/core-concepts/elements) instance. Or you can use it with a selector.
10
-
11
- ## Syntax
8
+ Double-click an element you've located with [`find()`](/find):
12
9
 
13
10
  ```javascript
14
- // Double-click on an element
15
- await element.doubleClick();
16
-
17
- // Double-click using a selector
18
- await ai.doubleClick('selector');
11
+ await testdriver.find('report.pdf in the file list').doubleClick();
19
12
  ```
20
13
 
21
- ## Parameters
14
+ ## element.doubleClick()
22
15
 
23
- When called on an `Element`, no parameters are required.
16
+ Double-clicks the element at the point where `find()` located it. It's the same as `element.click('double-click')`.
24
17
 
25
- When called directly on the AI client:
18
+ ```typescript
19
+ element.doubleClick(): Promise<void>
20
+ ```
26
21
 
27
- | Parameter | Type | Description |
28
- |-----------|------|-------------|
29
- | `selector` | `string` | The selector describing the element to double-click |
22
+ It takes no parameters.
30
23
 
31
- ## Returns
24
+ ### Errors
32
25
 
33
- It returns a `Promise<void>`. The promise resolves when the double-click action completes.
26
+ Throws an `ElementNotFoundError` if `find()` didn't locate the element. See [click()](/click#errors) for how to check `found()` first.
34
27
 
35
- ## Examples
28
+ ## Double-click at coordinates
36
29
 
37
- ### Double-Click on Found Element
30
+ The client has no `doubleClick()` method. To double-click a point on the screen, pass `action: 'double-click'` to [`testdriver.click()`](/click#testdriver-click-options):
38
31
 
39
32
  ```javascript
40
- const fileItem = await ai.find('README.md file');
41
- await fileItem.doubleClick();
33
+ await testdriver.click({ x: 640, y: 360, action: 'double-click' });
42
34
  ```
43
35
 
44
- ### Direct Double-Click with Selector
36
+ Prefer `element.doubleClick()`, which keeps working when the layout or screen size changes.
45
37
 
46
- ```javascript
47
- await ai.doubleClick('README.md in the file list');
48
- ```
38
+ ## Examples
49
39
 
50
- ### Opening Files in VS Code
40
+ ### Open a file in VS Code
51
41
 
52
42
  ```javascript
53
- import { test } from 'vitest';
54
- import { vscode } from '@testdriver/sdk';
55
-
56
- test('opens a file by double-clicking', async () => {
57
- const { ai } = await vscode();
58
-
59
- // Double-click to open a file in the explorer
60
- await ai.doubleClick('package.json in the file explorer');
61
-
62
- // Verify the file opened
63
- const editor = await ai.find('text editor showing package.json');
64
- expect(editor).toBeTruthy();
65
- });
43
+ await testdriver.find('package.json in the Explorer sidebar').doubleClick();
44
+ await testdriver.assert('package.json is open in the editor');
66
45
  ```
67
46
 
68
- ### Opening Folders in File Manager
47
+ ### Select a word
69
48
 
70
49
  ```javascript
71
- import { test } from 'vitest';
72
- import { chrome } from '@testdriver/sdk';
73
-
74
- test('navigates folders in Google Drive', async () => {
75
- const { ai } = await chrome('https://drive.google.com');
76
-
77
- // Double-click to open a folder
78
- await ai.doubleClick('Documents folder');
79
-
80
- // Wait for folder to open
81
- await ai.find('breadcrumb showing Documents');
82
- });
50
+ await testdriver.find('the word "Checkout" in the page heading').doubleClick();
51
+ await testdriver.pressKeys(['ctrl', 'c']);
83
52
  ```
84
53
 
85
- ### Selecting Text with Double-Click
54
+ ## Complete example
86
55
 
87
- ```javascript
88
- // Double-click to select a word
89
- await ai.doubleClick('word "TestDriver" in the paragraph');
56
+ ```javascript tests/open-file.test.mjs
57
+ import { describe, expect, it } from 'vitest';
58
+ import { TestDriver } from 'testdriverai/vitest/hooks';
90
59
 
91
- // Verify selection
92
- const selectedText = await ai.exec('window.getSelection().toString()');
93
- expect(selectedText).toBe('TestDriver');
60
+ describe('VS Code', () => {
61
+ it('opens a file from the Explorer', async (context) => {
62
+ const testdriver = TestDriver(context);
63
+ await testdriver.exec({
64
+ language: 'sh',
65
+ code: 'mkdir -p /tmp/my-project && echo "{}" > /tmp/my-project/package.json',
66
+ });
67
+ await testdriver.provision.vscode({ workspace: '/tmp/my-project' });
68
+
69
+ await testdriver.find('package.json in the Explorer sidebar').doubleClick();
70
+
71
+ const result = await testdriver.assert('package.json is open in the editor');
72
+ expect(result).toBe(true);
73
+ });
74
+ });
94
75
  ```
95
76
 
96
- ## Related Methods
77
+ ## Related
97
78
 
98
- - [`click()`](/click) - Single click on an element
99
- - [`rightClick()`](/right-click) - Right-click to open context menu
100
- - [`mouseDown()`](/mouse-down) - Press mouse button without releasing
101
- - [`mouseUp()`](/mouse-up) - Release mouse button
102
- - [`hover()`](/hover) - Move mouse over element without clicking
79
+ - [click()](/click): single clicks, and clicks at coordinates
80
+ - [rightClick()](/right-click): open a context menu
81
+ - [find()](/find): locate the element to double-click
82
+ - [Perform actions](/performing-actions): every kind of action in one guide
package/docs/errors.mdx CHANGED
@@ -1,248 +1,174 @@
1
1
  ---
2
2
  title: "Errors"
3
3
  sidebarTitle: "Errors"
4
- description: "Custom error classes and error handling"
4
+ description: "The errors TestDriver throws, what they contain, and how to catch them."
5
5
  icon: "triangle-exclamation"
6
6
  mode: "wide"
7
7
  ---
8
8
 
9
- ## Overview
10
-
11
- TestDriver gives custom error classes with full debug information. The SDK exports them. You can use them for `instanceof` checks in your tests.
9
+ TestDriver exports two error classes you can check with `instanceof`:
12
10
 
13
11
  ```javascript
14
- import TestDriver, { ElementNotFoundError, AIError } from 'testdriverai';
12
+ import { ElementNotFoundError, AIError } from 'testdriverai';
15
13
  ```
16
14
 
15
+ - [`ElementNotFoundError`](#elementnotfounderror): you acted on an element that `find()` didn't locate.
16
+ - [`AIError`](#aierror): an `ai()` task failed.
17
+
18
+ Other failures, such as a failed assertion, throw a plain `Error` with a recognizable message. See [Other errors](#other-errors).
19
+
17
20
  ## ElementNotFoundError
18
21
 
19
- TestDriver throws this when `find()` cannot find an element on the screen. It also throws this when you call `click()` or `hover()` on an element that TestDriver did not find.
22
+ Thrown when you call an action on an element that [`find()`](/find) didn't locate:
23
+
24
+ - `click()`, `doubleClick()`, `rightClick()`
25
+ - `hover()`
26
+ - `mouseDown()`, `mouseUp()`
27
+
28
+ `find()` itself doesn't throw when nothing matches. It returns an element whose `found()` is `false`. Check `found()` when the element might not be there:
20
29
 
21
30
  ```javascript
22
- try {
23
- await testdriver.find('nonexistent button').click();
24
- } catch (error) {
25
- if (error instanceof ElementNotFoundError) {
26
- console.log(error.description); // "nonexistent button"
27
- console.log(error.screenshotPath); // path to debug screenshot
28
- console.log(error.pixelDiffPath); // path to pixel diff image
29
- }
31
+ const cookieBanner = await testdriver.find('Cookie consent banner', { timeout: 0 });
32
+ if (cookieBanner.found()) {
33
+ await testdriver.find('Accept all cookies button').click();
30
34
  }
31
35
  ```
32
36
 
33
37
  ### Properties
34
38
 
35
- <ParamField path="name" type="string">
39
+ <ResponseField name="name" type="string">
36
40
  Always `"ElementNotFoundError"`.
37
- </ParamField>
41
+ </ResponseField>
38
42
 
39
- <ParamField path="message" type="string">
40
- A message with a debug block. The block has the element description, the cache status, the similarity scores, and the AI response details.
41
- </ParamField>
43
+ <ResponseField name="description" type="string">
44
+ The description you passed to `find()`.
45
+ </ResponseField>
42
46
 
43
- <ParamField path="description" type="string">
44
- The original element description passed to `find()`.
45
- </ParamField>
47
+ <ResponseField name="message" type="string">
48
+ Says which element wasn't found, followed by a debug block. See below.
49
+ </ResponseField>
46
50
 
47
- <ParamField path="screenshotPath" type="string | null">
48
- Absolute path to a debug screenshot saved at the time of failure. Written to `<os.tmpdir>/testdriver-debug/screenshot-<timestamp>.png`.
49
- </ParamField>
51
+ <ResponseField name="aiResponse" type="object | null">
52
+ The response from the `find()` call, with screenshots and other large images removed. Includes fields such as `cacheHit`, `similarity`, and `reasoning` when the API returned them.
53
+ </ResponseField>
50
54
 
51
- <ParamField path="pixelDiffPath" type="string | null">
52
- Absolute path to a pixel diff image showing the comparison between the cached and current screenshots. Written to `<os.tmpdir>/testdriver-debug/pixel-diff-error-<timestamp>.png`. Only present when cache was involved.
53
- </ParamField>
55
+ <ResponseField name="timestamp" type="string">
56
+ When the error was created, as an ISO 8601 string.
57
+ </ResponseField>
54
58
 
55
- <ParamField path="cachedImagePath" type="string | null">
56
- URL to the cached image that was compared against.
57
- </ParamField>
59
+ ### Message
58
60
 
59
- <ParamField path="aiResponse" type="object | null">
60
- Sanitized AI response object. Large binary fields (`croppedImage`, `screenshot`, `pixelDiffImage`) are removed. Contains cache metadata like `similarity`, `cacheHit`, `cacheStrategy`, `cacheDiffPercent`, and `threshold`.
61
- </ParamField>
61
+ The message starts with the element's description, then adds what TestDriver knows about the failed `find()`:
62
62
 
63
- <ParamField path="timestamp" type="string">
64
- ISO 8601 timestamp of when the error was created.
65
- </ParamField>
63
+ ```text
64
+ Element "Sign in button" not found.
66
65
 
67
- ### Enhanced Message
66
+ === Debug Information ===
67
+ Element searched for: "Sign in button"
68
+ Cache: MISS
68
69
 
69
- The error message is automatically enhanced with debugging information:
70
-
71
- ```
72
- Element not found: "submit button"
73
-
74
- === Element Debug Info ===
75
- Element: submit button
76
- Cache Hit: false
77
- Similarity: 0.23
78
- Cache Strategy: pixel-diff
79
- Threshold: 0.05
80
- AI Response Element: null
70
+ AI Response:
71
+ The page shows a loading spinner. There is no sign in button yet.
81
72
  ```
82
73
 
83
- ### Stack Trace Cleaning
74
+ Depending on the `find()` call, the debug block can also include:
84
75
 
85
- Stack traces are automatically cleaned to remove internal SDK frames (`Element.*`, `sdk.js` internals), showing only your test code for easier debugging.
76
+ - The cache threshold and the required similarity.
77
+ - When the cache was hit: the cache strategy, the entry's age, and the pixel difference.
78
+ - The similarity score, marked `(below threshold)` when it's too low.
79
+
80
+ The stack trace starts at your test code, not inside the SDK.
86
81
 
87
82
  ## AIError
88
83
 
89
- Thrown when `act()` exhausts all retry attempts.
84
+ Thrown when a task you give to `ai()` fails:
90
85
 
91
86
  ```javascript
87
+ import { AIError } from 'testdriverai';
88
+
92
89
  try {
93
- await testdriver.act('perform complex workflow', { tries: 3 });
90
+ await testdriver.ai('Add the blue T-shirt to the cart', { tries: 3 });
94
91
  } catch (error) {
95
92
  if (error instanceof AIError) {
96
- console.log(error.task); // "perform complex workflow"
97
- console.log(error.tries); // 3
98
- console.log(error.duration); // 15234 (ms)
99
- console.log(error.cause); // underlying Error
93
+ console.log(`Stopped after ${error.tries} of ${error.maxTries} tries`);
94
+ console.log(error.cause?.message); // the error that stopped the task
100
95
  }
96
+ throw error;
101
97
  }
102
98
  ```
103
99
 
100
+ `act()` is the same method under another name and throws the same error.
101
+
104
102
  ### Properties
105
103
 
106
- <ParamField path="name" type="string">
104
+ <ResponseField name="name" type="string">
107
105
  Always `"AIError"`.
108
- </ParamField>
106
+ </ResponseField>
109
107
 
110
- <ParamField path="message" type="string">
111
- Enhanced message with execution details block.
112
- </ParamField>
108
+ <ResponseField name="task" type="string">
109
+ The task you passed to `ai()`.
110
+ </ResponseField>
113
111
 
114
- <ParamField path="task" type="string">
115
- The task description passed to `act()`.
116
- </ParamField>
112
+ <ResponseField name="tries" type="number">
113
+ How many check attempts were made.
114
+ </ResponseField>
117
115
 
118
- <ParamField path="tries" type="number">
119
- Number of attempts that were made.
120
- </ParamField>
116
+ <ResponseField name="maxTries" type="number">
117
+ The `tries` limit for the call. Defaults to `7`.
118
+ </ResponseField>
121
119
 
122
- <ParamField path="maxTries" type="number">
123
- Maximum number of attempts configured.
124
- </ParamField>
120
+ <ResponseField name="duration" type="number">
121
+ How long the task ran, in milliseconds.
122
+ </ResponseField>
125
123
 
126
- <ParamField path="duration" type="number">
127
- Total execution time in milliseconds.
128
- </ParamField>
124
+ <ResponseField name="cause" type="Error">
125
+ The error that stopped the task.
126
+ </ResponseField>
129
127
 
130
- <ParamField path="cause" type="Error | undefined">
131
- The underlying error that caused the final failure.
132
- </ParamField>
128
+ <ResponseField name="timestamp" type="string">
129
+ When the error was created, as an ISO 8601 string.
130
+ </ResponseField>
133
131
 
134
- <ParamField path="timestamp" type="string">
135
- ISO 8601 timestamp of when the error was created.
136
- </ParamField>
132
+ ### Message
137
133
 
138
- ### Enhanced Message
139
-
140
- ```
141
- AI failed: Element not found after 3 attempts
134
+ ```text
135
+ AI failed: <the cause's message>
142
136
 
143
137
  === AI Execution Details ===
144
- Task: perform complex workflow
145
- Tries: 3 / 3
146
- Duration: 15234ms
147
- Cause: ElementNotFoundError: Element not found: "submit button"
138
+ Task: "Add the blue T-shirt to the cart"
139
+ Tries: 3/3
140
+ Duration: 41250ms
141
+ Timestamp: 2026-10-21T14:30:00.000Z
142
+ Underlying error: <the cause's message>
148
143
  ```
149
144
 
150
- ## Internal Error Classes
151
-
152
- These errors are used internally by the agent and are not exported, but may appear as the `cause` of an `AIError`:
153
-
154
- ### MatchError
155
-
156
- Thrown when element matching fails (text, image, or assertion).
157
-
158
- | Property | Type | Description |
159
- |---|---|---|
160
- | `fatal` | `boolean` | If `true`, cannot be healed. Default: `false` |
161
- | `attachScreenshot` | `boolean` | Always `true` — a screenshot is attached to the error |
162
-
163
- ### CommandError
145
+ ## Other errors
164
146
 
165
- Thrown for invalid arguments or unsupported operations.
147
+ These failures throw an error whose class isn't exported. Check the message instead.
166
148
 
167
- | Property | Type | Description |
168
- |---|---|---|
169
- | `fatal` | `boolean` | Always `true` |
170
- | `attachScreenshot` | `boolean` | Always `false` |
171
-
172
- ## Soft Assert Mode
173
-
174
- Inside `act()`, assertions run in **soft assert mode**. When an assertion fails, it returns the failure result instead of throwing, allowing the AI to process the failure and adjust its approach.
175
-
176
- ```javascript
177
- // Inside act(), assertion failures don't throw
178
- await testdriver.act('verify the dashboard shows correct data', {
179
- tries: 3,
180
- });
181
- // The AI can see assertion results and self-correct
182
- ```
183
-
184
- This is automatic — you don't need to configure it. Regular `assert()` calls outside of `act()` will throw normally on failure.
185
-
186
- ## Error Handling Patterns
187
-
188
- ### Catching Specific Errors
149
+ | What failed | Message starts with |
150
+ | --- | --- |
151
+ | [`assert()`](/assert) | `AI Assertion failed:` followed by the assertion and the AI's explanation |
152
+ | [`exec()`](/exec) with `sh` or `pwsh`, when the command exits non-zero | `Command failed with exit code <code>:` followed by stderr |
189
153
 
190
154
  ```javascript
191
- import TestDriver, { ElementNotFoundError, AIError } from 'testdriverai';
192
-
193
155
  try {
194
- await testdriver.find('submit button').click();
156
+ await testdriver.assert('the cart shows 2 items');
195
157
  } catch (error) {
196
- if (error instanceof ElementNotFoundError) {
197
- // Element wasn't found — check screenshot for debugging
198
- console.log('Debug screenshot:', error.screenshotPath);
199
- } else if (error instanceof AIError) {
200
- // AI exhausted retries
201
- console.log(`Failed after ${error.tries} tries in ${error.duration}ms`);
202
- } else {
203
- throw error; // Unexpected error
158
+ if (error.message.startsWith('AI Assertion failed:')) {
159
+ // The assertion didn't hold
204
160
  }
161
+ throw error;
205
162
  }
206
163
  ```
207
164
 
208
- ### Using Debug Screenshots
165
+ <Note>
166
+ Inside `ai()`, a failed `assert()` resolves to `false` instead of throwing, so the AI can see the result and try another approach.
167
+ </Note>
209
168
 
210
- ```javascript
211
- try {
212
- const el = await testdriver.find('checkout button');
213
- await el.click();
214
- } catch (error) {
215
- if (error instanceof ElementNotFoundError) {
216
- // Screenshot of what the screen looked like
217
- console.log('Screen:', error.screenshotPath);
218
- // Pixel diff showing cache comparison
219
- console.log('Diff:', error.pixelDiffPath);
220
- // Full AI response metadata
221
- console.log('AI:', JSON.stringify(error.aiResponse, null, 2));
222
- }
223
- }
224
- ```
169
+ ## Related
225
170
 
226
- ## Types
227
-
228
- ```typescript
229
- class ElementNotFoundError extends Error {
230
- name: 'ElementNotFoundError';
231
- description: string;
232
- screenshotPath: string | null;
233
- pixelDiffPath: string | null;
234
- cachedImagePath: string | null;
235
- aiResponse: Record<string, any> | null;
236
- timestamp: string;
237
- }
238
-
239
- class AIError extends Error {
240
- name: 'AIError';
241
- task: string;
242
- tries: number;
243
- maxTries: number;
244
- duration: number;
245
- cause?: Error;
246
- timestamp: string;
247
- }
248
- ```
171
+ - [find()](/find): returns an element whose `found()` tells you whether it was located
172
+ - [assert()](/assert): check what's on screen
173
+ - [Debug a failing test](/debugging-with-screenshots): find out why a test failed
174
+ - [Events](/events): listen for errors as they happen