@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.
- package/docs/_data/examples-manifest.json +16 -16
- package/docs/_scripts/generate-examples.js +38 -45
- package/docs/agent.mdx +174 -93
- package/docs/assert.mdx +73 -269
- package/docs/aws-setup.mdx +196 -314
- package/docs/bring-your-own-machine.mdx +139 -206
- package/docs/cache.mdx +144 -151
- package/docs/caching.mdx +99 -89
- package/docs/captcha.mdx +126 -111
- package/docs/ci-cd.mdx +154 -543
- package/docs/click.mdx +74 -229
- package/docs/client.mdx +177 -231
- package/docs/copilot/auto-healing.mdx +86 -370
- package/docs/copilot/running-tests.mdx +121 -930
- package/docs/customizing-devices.mdx +74 -251
- package/docs/dashcam.mdx +72 -405
- package/docs/debugging-with-screenshots.mdx +105 -371
- package/docs/docs.json +75 -18
- package/docs/double-click.mdx +48 -68
- package/docs/errors.mdx +102 -176
- package/docs/events.mdx +170 -327
- package/docs/examples/{hover-text-with-description.mdx → add-to-cart.mdx} +30 -35
- package/docs/examples/assert.mdx +28 -41
- package/docs/examples/click-button.mdx +71 -0
- package/docs/examples/{hover-image.mdx → click-icon.mdx} +32 -28
- package/docs/examples/element-not-found.mdx +27 -26
- package/docs/examples/findall-coffee-icons.mdx +28 -32
- package/docs/examples/installer.mdx +61 -25
- package/docs/examples/launch-vscode-linux.mdx +28 -51
- package/docs/examples/navigate-and-scroll.mdx +82 -0
- package/docs/examples/parse.mdx +24 -28
- package/docs/examples/press-keys.mdx +28 -25
- package/docs/examples/scroll.mdx +25 -27
- package/docs/examples/type.mdx +26 -21
- package/docs/exec.mdx +92 -256
- package/docs/extract.mdx +69 -170
- package/docs/find.mdx +140 -736
- package/docs/focus-application.mdx +69 -246
- package/docs/generating-tests.mdx +103 -137
- package/docs/guide/best-practices-polling.mdx +65 -141
- package/docs/hosted.mdx +80 -144
- package/docs/hover.mdx +62 -235
- package/docs/locating-elements.mdx +74 -369
- package/docs/making-assertions.mdx +116 -74
- package/docs/mcp-setup.mdx +80 -63
- package/docs/mouse-down.mdx +48 -125
- package/docs/mouse-up.mdx +47 -129
- package/docs/options.mdx +75 -48
- package/docs/parse.mdx +95 -184
- package/docs/performing-actions.mdx +104 -94
- package/docs/press-keys.mdx +59 -299
- package/docs/provision.mdx +137 -182
- package/docs/quickstart-claude.mdx +85 -0
- package/docs/quickstart-github.mdx +62 -32
- package/docs/quickstart-manual.mdx +60 -33
- package/docs/quickstart-vscode.mdx +97 -0
- package/docs/quickstart.mdx +25 -9
- package/docs/redraw.mdx +101 -165
- package/docs/reusable-code.mdx +97 -200
- package/docs/right-click.mdx +47 -89
- package/docs/screenshot.mdx +47 -201
- package/docs/screenshots.mdx +87 -137
- package/docs/scroll.mdx +71 -154
- package/docs/secrets.mdx +96 -91
- package/docs/self-hosted.mdx +83 -142
- package/docs/snippets/enterprise.mdx +15 -0
- package/docs/styles.css +68 -0
- package/docs/test-results-json.mdx +167 -204
- package/docs/type.mdx +56 -304
- package/docs/variables.mdx +121 -81
- package/docs/wait.mdx +33 -31
- package/docs/what-is-testdriver.mdx +80 -32
- package/examples/{hover-text-with-description.test.mjs → add-to-cart.test.mjs} +4 -5
- package/examples/assert.test.mjs +3 -20
- package/examples/{hover-text.test.mjs → click-button.test.mjs} +3 -4
- package/examples/{hover-image.test.mjs → click-icon.test.mjs} +5 -6
- package/examples/element-not-found.test.mjs +3 -5
- package/examples/findall-coffee-icons.test.mjs +3 -3
- package/examples/installer.test.mjs +1 -1
- package/examples/launch-vscode-linux.test.mjs +6 -33
- package/examples/{scroll-keyboard.test.mjs → navigate-and-scroll.test.mjs} +4 -5
- package/examples/parse.test.mjs +1 -2
- package/examples/press-keys.test.mjs +4 -4
- package/examples/scroll.test.mjs +3 -6
- package/examples/type.test.mjs +5 -2
- package/lib/provision.js +6 -2
- package/package.json +1 -1
- package/sdk.d.ts +4 -2
- package/sdk.js +1 -1
- package/docs/claude-mcp-plugin.mdx +0 -244
- package/docs/elements.mdx +0 -606
- package/docs/examples/ai.mdx +0 -78
- package/docs/examples/captcha-api.mdx +0 -92
- package/docs/examples/chrome-extension.mdx +0 -156
- package/docs/examples/drag-and-drop.mdx +0 -100
- package/docs/examples/formatted-logging.mdx +0 -74
- package/docs/examples/hover-text.mdx +0 -70
- package/docs/examples/match-image.mdx +0 -97
- package/docs/examples/scroll-keyboard.mdx +0 -80
- 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": "
|
|
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/
|
|
36
|
-
"/examples/
|
|
37
|
-
"/examples/
|
|
38
|
-
"/examples/
|
|
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/
|
|
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 &
|
|
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-
|
|
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
|
|
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": {
|
package/docs/double-click.mdx
CHANGED
|
@@ -1,102 +1,82 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "doubleClick"
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
14
|
+
## element.doubleClick()
|
|
22
15
|
|
|
23
|
-
|
|
16
|
+
Double-clicks the element at the point where `find()` located it. It's the same as `element.click('double-click')`.
|
|
24
17
|
|
|
25
|
-
|
|
18
|
+
```typescript
|
|
19
|
+
element.doubleClick(): Promise<void>
|
|
20
|
+
```
|
|
26
21
|
|
|
27
|
-
|
|
28
|
-
|-----------|------|-------------|
|
|
29
|
-
| `selector` | `string` | The selector describing the element to double-click |
|
|
22
|
+
It takes no parameters.
|
|
30
23
|
|
|
31
|
-
|
|
24
|
+
### Errors
|
|
32
25
|
|
|
33
|
-
|
|
26
|
+
Throws an `ElementNotFoundError` if `find()` didn't locate the element. See [click()](/click#errors) for how to check `found()` first.
|
|
34
27
|
|
|
35
|
-
##
|
|
28
|
+
## Double-click at coordinates
|
|
36
29
|
|
|
37
|
-
|
|
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
|
-
|
|
41
|
-
await fileItem.doubleClick();
|
|
33
|
+
await testdriver.click({ x: 640, y: 360, action: 'double-click' });
|
|
42
34
|
```
|
|
43
35
|
|
|
44
|
-
|
|
36
|
+
Prefer `element.doubleClick()`, which keeps working when the layout or screen size changes.
|
|
45
37
|
|
|
46
|
-
|
|
47
|
-
await ai.doubleClick('README.md in the file list');
|
|
48
|
-
```
|
|
38
|
+
## Examples
|
|
49
39
|
|
|
50
|
-
###
|
|
40
|
+
### Open a file in VS Code
|
|
51
41
|
|
|
52
42
|
```javascript
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
###
|
|
47
|
+
### Select a word
|
|
69
48
|
|
|
70
49
|
```javascript
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
54
|
+
## Complete example
|
|
86
55
|
|
|
87
|
-
```javascript
|
|
88
|
-
|
|
89
|
-
|
|
56
|
+
```javascript tests/open-file.test.mjs
|
|
57
|
+
import { describe, expect, it } from 'vitest';
|
|
58
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
90
59
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
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
|
|
77
|
+
## Related
|
|
97
78
|
|
|
98
|
-
- [
|
|
99
|
-
- [
|
|
100
|
-
- [
|
|
101
|
-
- [
|
|
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: "
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
<
|
|
39
|
+
<ResponseField name="name" type="string">
|
|
36
40
|
Always `"ElementNotFoundError"`.
|
|
37
|
-
</
|
|
41
|
+
</ResponseField>
|
|
38
42
|
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
</
|
|
43
|
+
<ResponseField name="description" type="string">
|
|
44
|
+
The description you passed to `find()`.
|
|
45
|
+
</ResponseField>
|
|
42
46
|
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
</
|
|
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
|
-
<
|
|
48
|
-
|
|
49
|
-
</
|
|
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
|
-
<
|
|
52
|
-
|
|
53
|
-
</
|
|
55
|
+
<ResponseField name="timestamp" type="string">
|
|
56
|
+
When the error was created, as an ISO 8601 string.
|
|
57
|
+
</ResponseField>
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
URL to the cached image that was compared against.
|
|
57
|
-
</ParamField>
|
|
59
|
+
### Message
|
|
58
60
|
|
|
59
|
-
|
|
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
|
-
|
|
64
|
-
|
|
65
|
-
</ParamField>
|
|
63
|
+
```text
|
|
64
|
+
Element "Sign in button" not found.
|
|
66
65
|
|
|
67
|
-
|
|
66
|
+
=== Debug Information ===
|
|
67
|
+
Element searched for: "Sign in button"
|
|
68
|
+
Cache: MISS
|
|
68
69
|
|
|
69
|
-
|
|
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
|
-
|
|
74
|
+
Depending on the `find()` call, the debug block can also include:
|
|
84
75
|
|
|
85
|
-
|
|
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 `
|
|
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.
|
|
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.
|
|
97
|
-
console.log(error.
|
|
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
|
-
<
|
|
104
|
+
<ResponseField name="name" type="string">
|
|
107
105
|
Always `"AIError"`.
|
|
108
|
-
</
|
|
106
|
+
</ResponseField>
|
|
109
107
|
|
|
110
|
-
<
|
|
111
|
-
|
|
112
|
-
</
|
|
108
|
+
<ResponseField name="task" type="string">
|
|
109
|
+
The task you passed to `ai()`.
|
|
110
|
+
</ResponseField>
|
|
113
111
|
|
|
114
|
-
<
|
|
115
|
-
|
|
116
|
-
</
|
|
112
|
+
<ResponseField name="tries" type="number">
|
|
113
|
+
How many check attempts were made.
|
|
114
|
+
</ResponseField>
|
|
117
115
|
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
</
|
|
116
|
+
<ResponseField name="maxTries" type="number">
|
|
117
|
+
The `tries` limit for the call. Defaults to `7`.
|
|
118
|
+
</ResponseField>
|
|
121
119
|
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
</
|
|
120
|
+
<ResponseField name="duration" type="number">
|
|
121
|
+
How long the task ran, in milliseconds.
|
|
122
|
+
</ResponseField>
|
|
125
123
|
|
|
126
|
-
<
|
|
127
|
-
|
|
128
|
-
</
|
|
124
|
+
<ResponseField name="cause" type="Error">
|
|
125
|
+
The error that stopped the task.
|
|
126
|
+
</ResponseField>
|
|
129
127
|
|
|
130
|
-
<
|
|
131
|
-
|
|
132
|
-
</
|
|
128
|
+
<ResponseField name="timestamp" type="string">
|
|
129
|
+
When the error was created, as an ISO 8601 string.
|
|
130
|
+
</ResponseField>
|
|
133
131
|
|
|
134
|
-
|
|
135
|
-
ISO 8601 timestamp of when the error was created.
|
|
136
|
-
</ParamField>
|
|
132
|
+
### Message
|
|
137
133
|
|
|
138
|
-
|
|
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:
|
|
145
|
-
Tries: 3
|
|
146
|
-
Duration:
|
|
147
|
-
|
|
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
|
-
##
|
|
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
|
-
|
|
147
|
+
These failures throw an error whose class isn't exported. Check the message instead.
|
|
166
148
|
|
|
167
|
-
|
|
|
168
|
-
|
|
169
|
-
| `
|
|
170
|
-
| `
|
|
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.
|
|
156
|
+
await testdriver.assert('the cart shows 2 items');
|
|
195
157
|
} catch (error) {
|
|
196
|
-
if (error
|
|
197
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
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
|