testdriverai 7.4.4 → 7.5.0
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/.github/copilot-instructions.md +1 -1
- package/.github/skills/testdriver:performing-actions/SKILL.md +3 -0
- package/.github/skills/testdriver:waiting-for-elements/SKILL.md +16 -0
- package/CHANGELOG.md +4 -0
- package/agent/events.js +7 -0
- package/agent/index.js +24 -17
- package/agent/lib/sandbox.js +703 -428
- package/agent/lib/system.js +70 -1
- package/ai/agents/testdriver.md +1 -1
- package/ai/skills/testdriver:performing-actions/SKILL.md +3 -0
- package/ai/skills/testdriver:testdriver/SKILL.md +1 -1
- package/ai/skills/testdriver:waiting-for-elements/SKILL.md +16 -0
- package/docs/_data/examples-manifest.json +68 -68
- package/docs/guide/best-practices-polling.mdx +25 -5
- package/docs/v7/examples/ai.mdx +1 -1
- package/docs/v7/examples/assert.mdx +1 -1
- package/docs/v7/examples/chrome-extension.mdx +1 -1
- package/docs/v7/examples/drag-and-drop.mdx +1 -1
- package/docs/v7/examples/element-not-found.mdx +1 -1
- package/docs/v7/examples/hover-image.mdx +1 -1
- package/docs/v7/examples/hover-text.mdx +1 -1
- package/docs/v7/examples/installer.mdx +1 -1
- package/docs/v7/examples/launch-vscode-linux.mdx +1 -1
- package/docs/v7/examples/match-image.mdx +1 -1
- package/docs/v7/examples/press-keys.mdx +1 -1
- package/docs/v7/examples/scroll-keyboard.mdx +1 -1
- package/docs/v7/examples/scroll-until-image.mdx +1 -1
- package/docs/v7/examples/scroll-until-text.mdx +1 -1
- package/docs/v7/examples/scroll.mdx +1 -1
- package/docs/v7/examples/type.mdx +1 -1
- package/docs/v7/examples/windows-installer.mdx +1 -1
- package/docs/v7/performing-actions.mdx +3 -0
- package/docs/v7/wait.mdx +52 -0
- package/docs/v7/waiting-for-elements.mdx +18 -0
- package/examples/chrome-extension.test.mjs +2 -0
- package/examples/no-provision.test.mjs +2 -0
- package/lib/vitest/hooks.mjs +9 -0
- package/lib/vitest/setup-aws.mjs +1 -0
- package/manual/packer-hover-image.test.mjs +176 -0
- package/package.json +2 -1
- package/sdk.d.ts +14 -2
- package/sdk.js +10 -0
- package/setup/aws/cloudformation.yaml +1 -8
- package/setup/aws/spawn-runner.sh +2 -37
- package/vitest.config.mjs +1 -1
package/agent/lib/system.js
CHANGED
|
@@ -7,11 +7,80 @@ const Jimp = require("jimp");
|
|
|
7
7
|
const { events } = require("../events.js");
|
|
8
8
|
|
|
9
9
|
const createSystem = (emitter, sandbox, config) => {
|
|
10
|
+
|
|
11
|
+
// Download a screenshot from S3 when the runner returns an s3Key
|
|
12
|
+
// (screenshots exceed Ably's 64KB message limit)
|
|
13
|
+
const downloadFromS3 = async (s3Key) => {
|
|
14
|
+
const https = require("https");
|
|
15
|
+
const http = require("http");
|
|
16
|
+
const apiRoot = config["TD_API_ROOT"] || sandbox.apiRoot;
|
|
17
|
+
const apiKey = sandbox.apiKey;
|
|
18
|
+
|
|
19
|
+
// Step 1: Get presigned download URL from API
|
|
20
|
+
const body = JSON.stringify({ apiKey, s3Key });
|
|
21
|
+
const url = new URL(apiRoot + "/api/v7/runner/download-url");
|
|
22
|
+
const transport = url.protocol === "https:" ? https : http;
|
|
23
|
+
|
|
24
|
+
const downloadUrl = await new Promise((resolve, reject) => {
|
|
25
|
+
const req = transport.request(url, {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "application/json",
|
|
29
|
+
"Content-Length": Buffer.byteLength(body),
|
|
30
|
+
"Connection": "close",
|
|
31
|
+
},
|
|
32
|
+
}, (res) => {
|
|
33
|
+
let data = "";
|
|
34
|
+
res.on("data", (chunk) => { data += chunk; });
|
|
35
|
+
res.on("end", () => {
|
|
36
|
+
try {
|
|
37
|
+
const parsed = JSON.parse(data);
|
|
38
|
+
if (parsed.downloadUrl) {
|
|
39
|
+
resolve(parsed.downloadUrl);
|
|
40
|
+
} else {
|
|
41
|
+
reject(new Error("No downloadUrl in response: " + data));
|
|
42
|
+
}
|
|
43
|
+
} catch (e) {
|
|
44
|
+
reject(new Error("Failed to parse download-url response: " + data));
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
req.on("error", reject);
|
|
49
|
+
req.write(body);
|
|
50
|
+
req.end();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Step 2: Download the image from S3
|
|
54
|
+
const imageUrl = new URL(downloadUrl);
|
|
55
|
+
const s3Transport = imageUrl.protocol === "https:" ? https : http;
|
|
56
|
+
|
|
57
|
+
const imageBuffer = await new Promise((resolve, reject) => {
|
|
58
|
+
s3Transport.get(downloadUrl, { headers: { "Connection": "close" } }, (res) => {
|
|
59
|
+
const chunks = [];
|
|
60
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
61
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
62
|
+
res.on("error", reject);
|
|
63
|
+
}).on("error", reject);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return imageBuffer.toString("base64");
|
|
67
|
+
};
|
|
68
|
+
|
|
10
69
|
const screenshot = async (options) => {
|
|
11
|
-
let
|
|
70
|
+
let response = await sandbox.send({
|
|
12
71
|
type: "system.screenshot",
|
|
13
72
|
});
|
|
14
73
|
|
|
74
|
+
let base64;
|
|
75
|
+
|
|
76
|
+
// Runner returns { s3Key } for Ably (screenshots too large for 64KB limit)
|
|
77
|
+
// Runner returns { base64 } for direct/local connections
|
|
78
|
+
if (response.s3Key) {
|
|
79
|
+
base64 = await downloadFromS3(response.s3Key);
|
|
80
|
+
} else {
|
|
81
|
+
base64 = response.base64;
|
|
82
|
+
}
|
|
83
|
+
|
|
15
84
|
if (!base64) {
|
|
16
85
|
throw new Error("Failed to take screenshot: sandbox returned empty data");
|
|
17
86
|
}
|
package/ai/agents/testdriver.md
CHANGED
|
@@ -621,7 +621,7 @@ await testdriver.screenshot(1, false, true);
|
|
|
621
621
|
3. **⚠️ SHARE THE TEST REPORT URL** - After EVERY test run, find `TESTDRIVER_RUN_URL=https://console.testdriver.ai/runs/...` in the output and share it with the user. This is CRITICAL - users need to view the recording to understand what happened.
|
|
622
622
|
3. **Screenshots are automatic** - TestDriver captures screenshots before/after every command by default. Each screenshot filename includes the line number (e.g., `001-click-before-L42-submit-button.png`) making it easy to trace issues.
|
|
623
623
|
4. **⚠️ USE SCREENSHOT VIEWING FOR DEBUGGING** - When tests fail, use `list_local_screenshots` and `view_local_screenshot` MCP commands to see exactly what the UI looked like. The filenames tell you which line of code triggered each screenshot.
|
|
624
|
-
5.
|
|
624
|
+
5. **Use `wait()` for simple delays** - Use `await testdriver.wait(ms)` when you need a pause (e.g., after actions, for animations). For waiting for specific elements, prefer `find()` with a `timeout` option.
|
|
625
625
|
6. **Use MCP tools for development** - Build tests interactively with visual feedback
|
|
626
626
|
7. **Always check `sdk.d.ts`** for method signatures and types when debugging generated tests
|
|
627
627
|
8. **Look at test samples** in `node_modules/testdriverai/test` for working examples
|
|
@@ -29,6 +29,9 @@ await testdriver.find('dropdown menu').hover();
|
|
|
29
29
|
await testdriver.scroll('down', 500);
|
|
30
30
|
await testdriver.scrollUntilText('Footer content');
|
|
31
31
|
|
|
32
|
+
// Waiting
|
|
33
|
+
await testdriver.wait(2000); // Wait 2 seconds for animation/state change
|
|
34
|
+
|
|
32
35
|
// Extracting information from screen
|
|
33
36
|
const price = await testdriver.extract('the total price');
|
|
34
37
|
const orderNumber = await testdriver.extract('the order confirmation number');
|
|
@@ -611,7 +611,7 @@ await testdriver.screenshot(1, false, true);
|
|
|
611
611
|
3. **⚠️ SHARE THE TEST REPORT URL** - After EVERY test run, find `TESTDRIVER_RUN_URL=https://console.testdriver.ai/runs/...` in the output and share it with the user. This is CRITICAL - users need to view the recording to understand what happened.
|
|
612
612
|
3. **Screenshots are automatic** - TestDriver captures screenshots before/after every command by default. Each screenshot filename includes the line number (e.g., `001-click-before-L42-submit-button.png`) making it easy to trace issues.
|
|
613
613
|
4. **⚠️ USE SCREENSHOT VIEWING FOR DEBUGGING** - When tests fail, use `list_local_screenshots` and `view_local_screenshot` MCP commands to see exactly what the UI looked like. The filenames tell you which line of code triggered each screenshot.
|
|
614
|
-
5.
|
|
614
|
+
5. **Use `wait()` for simple delays** - Use `await testdriver.wait(ms)` when you need a pause (e.g., after actions, for animations). For waiting for specific elements, prefer `find()` with a `timeout` option.
|
|
615
615
|
6. **Use MCP tools for development** - Build tests interactively with visual feedback
|
|
616
616
|
7. **Always check `sdk.d.ts`** for method signatures and types when debugging generated tests
|
|
617
617
|
8. **Look at test samples** in `node_modules/testdriverai/test` for working examples
|
|
@@ -70,3 +70,19 @@ const testdriver = TestDriver(context, {
|
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
72
|
```
|
|
73
|
+
|
|
74
|
+
## Simple Delays with `wait()`
|
|
75
|
+
|
|
76
|
+
For simple pauses — waiting for animations, transitions, or state changes after an action — use `wait()`:
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
// Wait for an animation to complete
|
|
80
|
+
await testdriver.find('menu toggle').click();
|
|
81
|
+
await testdriver.wait(2000);
|
|
82
|
+
|
|
83
|
+
// Wait for a page transition to settle
|
|
84
|
+
await testdriver.find('next page button').click();
|
|
85
|
+
await testdriver.wait(1000);
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For waiting for specific **elements** to appear, prefer `find()` with a `timeout` option. Use `wait()` only for simple time-based pauses.
|
|
@@ -2,104 +2,104 @@
|
|
|
2
2
|
"$schema": "./examples-manifest.schema.json",
|
|
3
3
|
"examples": {
|
|
4
4
|
"assert.test.mjs": {
|
|
5
|
-
"url": "https://console.testdriver.ai/runs/
|
|
6
|
-
"lastUpdated": "2026-
|
|
5
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b6206a177a05bccd1cc",
|
|
6
|
+
"lastUpdated": "2026-03-03T00:32:25.279Z"
|
|
7
7
|
},
|
|
8
8
|
"drag-and-drop.test.mjs": {
|
|
9
|
-
"url": "https://console.testdriver.ai/runs/
|
|
10
|
-
"lastUpdated": "2026-
|
|
9
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b42fc0ac3cc632a918b",
|
|
10
|
+
"lastUpdated": "2026-03-03T00:32:25.275Z"
|
|
11
11
|
},
|
|
12
12
|
"exec-pwsh.test.mjs": {
|
|
13
|
-
"url": "https://console.testdriver.ai/runs/
|
|
14
|
-
"lastUpdated": "2026-
|
|
13
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b42565d339e8065f180",
|
|
14
|
+
"lastUpdated": "2026-03-03T00:32:25.275Z"
|
|
15
15
|
},
|
|
16
16
|
"match-image.test.mjs": {
|
|
17
|
-
"url": "https://console.testdriver.ai/runs/
|
|
18
|
-
"lastUpdated": "2026-
|
|
17
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b42258e8885264fc704",
|
|
18
|
+
"lastUpdated": "2026-03-03T00:32:25.275Z"
|
|
19
19
|
},
|
|
20
20
|
"scroll-until-text.test.mjs": {
|
|
21
|
-
"url": "https://console.testdriver.ai/runs/
|
|
22
|
-
"lastUpdated": "2026-
|
|
21
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b99dc33133fc0da9440",
|
|
22
|
+
"lastUpdated": "2026-03-03T00:32:25.282Z"
|
|
23
23
|
},
|
|
24
24
|
"hover-text-with-description.test.mjs": {
|
|
25
|
-
"url": "https://console.testdriver.ai/runs/
|
|
26
|
-
"lastUpdated": "2026-
|
|
25
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b820d4a5265e44e7d89",
|
|
26
|
+
"lastUpdated": "2026-03-03T00:32:25.282Z"
|
|
27
27
|
},
|
|
28
28
|
"windows-installer.test.mjs": {
|
|
29
|
-
"url": "https://console.testdriver.ai/runs/
|
|
30
|
-
"lastUpdated": "2026-
|
|
29
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b42565d339e8065f17f",
|
|
30
|
+
"lastUpdated": "2026-03-03T00:32:25.275Z"
|
|
31
31
|
},
|
|
32
32
|
"exec-output.test.mjs": {
|
|
33
|
-
"url": "https://console.testdriver.ai/runs/
|
|
34
|
-
"lastUpdated": "2026-
|
|
33
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b42fc0ac3cc632a918c",
|
|
34
|
+
"lastUpdated": "2026-03-03T00:32:25.275Z"
|
|
35
35
|
},
|
|
36
36
|
"chrome-extension.test.mjs": {
|
|
37
|
-
"url": "https://console.testdriver.ai/runs/
|
|
38
|
-
"lastUpdated": "2026-
|
|
37
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b91fc0ac3cc632a91a0",
|
|
38
|
+
"lastUpdated": "2026-03-03T00:32:25.282Z"
|
|
39
39
|
},
|
|
40
40
|
"launch-vscode-linux.test.mjs": {
|
|
41
|
-
"url": "https://console.testdriver.ai/runs/
|
|
42
|
-
"lastUpdated": "2026-
|
|
41
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b91113035da665496a6",
|
|
42
|
+
"lastUpdated": "2026-03-03T00:32:25.282Z"
|
|
43
43
|
},
|
|
44
44
|
"hover-image.test.mjs": {
|
|
45
|
-
"url": "https://console.testdriver.ai/runs/
|
|
46
|
-
"lastUpdated": "2026-
|
|
45
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b77fc0ac3cc632a9198",
|
|
46
|
+
"lastUpdated": "2026-03-03T00:32:25.279Z"
|
|
47
47
|
},
|
|
48
48
|
"installer.test.mjs": {
|
|
49
|
-
"url": "https://console.testdriver.ai/runs/
|
|
50
|
-
"lastUpdated": "2026-
|
|
49
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b570446888b52a4e1c9",
|
|
50
|
+
"lastUpdated": "2026-03-03T00:32:25.277Z"
|
|
51
51
|
},
|
|
52
52
|
"type.test.mjs": {
|
|
53
|
-
"url": "https://console.testdriver.ai/runs/
|
|
54
|
-
"lastUpdated": "2026-
|
|
53
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b8d49845ced0b71e2bf",
|
|
54
|
+
"lastUpdated": "2026-03-03T00:32:25.282Z"
|
|
55
55
|
},
|
|
56
56
|
"press-keys.test.mjs": {
|
|
57
|
-
"url": "https://console.testdriver.ai/runs/
|
|
58
|
-
"lastUpdated": "2026-
|
|
57
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b800d4a5265e44e7d86",
|
|
58
|
+
"lastUpdated": "2026-03-03T00:32:25.281Z"
|
|
59
59
|
},
|
|
60
60
|
"scroll-keyboard.test.mjs": {
|
|
61
|
-
"url": "https://console.testdriver.ai/runs/
|
|
62
|
-
"lastUpdated": "2026-
|
|
61
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b82258e8885264fc728",
|
|
62
|
+
"lastUpdated": "2026-03-03T00:32:25.281Z"
|
|
63
63
|
},
|
|
64
64
|
"scroll.test.mjs": {
|
|
65
|
-
"url": "https://console.testdriver.ai/runs/
|
|
66
|
-
"lastUpdated": "2026-
|
|
65
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b83113035da665496a3",
|
|
66
|
+
"lastUpdated": "2026-03-03T00:32:25.282Z"
|
|
67
67
|
},
|
|
68
68
|
"scroll-until-image.test.mjs": {
|
|
69
|
-
"url": "https://console.testdriver.ai/runs/
|
|
70
|
-
"lastUpdated": "2026-
|
|
69
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b4549845ced0b71e2b0",
|
|
70
|
+
"lastUpdated": "2026-03-03T00:32:25.276Z"
|
|
71
71
|
},
|
|
72
72
|
"prompt.test.mjs": {
|
|
73
|
-
"url": "https://console.testdriver.ai/runs/
|
|
74
|
-
"lastUpdated": "2026-
|
|
73
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b45b9fa9df65266123f",
|
|
74
|
+
"lastUpdated": "2026-03-03T00:32:25.276Z"
|
|
75
75
|
},
|
|
76
76
|
"focus-window.test.mjs": {
|
|
77
|
-
"url": "https://console.testdriver.ai/runs/
|
|
78
|
-
"lastUpdated": "2026-
|
|
77
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b4549845ced0b71e2b1",
|
|
78
|
+
"lastUpdated": "2026-03-03T00:32:25.276Z"
|
|
79
79
|
},
|
|
80
80
|
"captcha-api.test.mjs": {
|
|
81
81
|
"url": "https://console.testdriver.ai/runs/698f7df69e27ce1528d7d087/698f7fb0d3b320ad547d9d44",
|
|
82
82
|
"lastUpdated": "2026-02-13T19:55:05.951Z"
|
|
83
83
|
},
|
|
84
84
|
"element-not-found.test.mjs": {
|
|
85
|
-
"url": "https://console.testdriver.ai/runs/
|
|
86
|
-
"lastUpdated": "2026-
|
|
85
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b6f0d4a5265e44e7d7b",
|
|
86
|
+
"lastUpdated": "2026-03-03T00:32:25.279Z"
|
|
87
87
|
},
|
|
88
88
|
"formatted-logging.test.mjs": {
|
|
89
|
-
"url": "https://console.testdriver.ai/runs/
|
|
90
|
-
"lastUpdated": "2026-
|
|
89
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b60258e8885264fc711",
|
|
90
|
+
"lastUpdated": "2026-03-03T00:32:25.279Z"
|
|
91
91
|
},
|
|
92
92
|
"hover-text.test.mjs": {
|
|
93
|
-
"url": "https://console.testdriver.ai/runs/
|
|
94
|
-
"lastUpdated": "2026-
|
|
93
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b79dc33133fc0da9439",
|
|
94
|
+
"lastUpdated": "2026-03-03T00:32:25.280Z"
|
|
95
95
|
},
|
|
96
96
|
"no-provision.test.mjs": {
|
|
97
|
-
"url": "https://console.testdriver.ai/runs/
|
|
98
|
-
"lastUpdated": "2026-
|
|
97
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b7706a177a05bccd1cf",
|
|
98
|
+
"lastUpdated": "2026-03-03T00:32:25.279Z"
|
|
99
99
|
},
|
|
100
100
|
"ai.test.mjs": {
|
|
101
|
-
"url": "https://console.testdriver.ai/runs/
|
|
102
|
-
"lastUpdated": "2026-
|
|
101
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b8f06a177a05bccd1dc",
|
|
102
|
+
"lastUpdated": "2026-03-03T00:32:25.282Z"
|
|
103
103
|
},
|
|
104
104
|
"popup-loading.test.mjs": {
|
|
105
105
|
"url": "https://console.testdriver.ai/runs/698bc89f7140c3fa7daaca8d/698bca7f7140c3fa7daacbf7",
|
|
@@ -134,44 +134,44 @@
|
|
|
134
134
|
"lastUpdated": "2026-02-13T19:55:05.953Z"
|
|
135
135
|
},
|
|
136
136
|
"findall-coffee-icons.test.mjs": {
|
|
137
|
-
"url": "https://console.testdriver.ai/runs/
|
|
138
|
-
"lastUpdated": "2026-
|
|
137
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b7afc0ac3cc632a919b",
|
|
138
|
+
"lastUpdated": "2026-03-03T00:32:25.280Z"
|
|
139
139
|
},
|
|
140
140
|
"parse.test.mjs": {
|
|
141
|
-
"url": "https://console.testdriver.ai/runs/
|
|
142
|
-
"lastUpdated": "2026-
|
|
141
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b80aa712ecd3dea731c",
|
|
142
|
+
"lastUpdated": "2026-03-03T00:32:25.281Z"
|
|
143
143
|
},
|
|
144
144
|
"flake-diffthreshold-001.test.mjs": {
|
|
145
|
-
"url": "https://console.testdriver.ai/runs/
|
|
146
|
-
"lastUpdated": "2026-
|
|
145
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62bcafc0ac3cc632a91aa",
|
|
146
|
+
"lastUpdated": "2026-03-03T00:32:25.283Z"
|
|
147
147
|
},
|
|
148
148
|
"flake-noredraw-cache.test.mjs": {
|
|
149
|
-
"url": "https://console.testdriver.ai/runs/
|
|
150
|
-
"lastUpdated": "2026-
|
|
149
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62be21658b3dab5240bfc",
|
|
150
|
+
"lastUpdated": "2026-03-03T00:32:25.283Z"
|
|
151
151
|
},
|
|
152
152
|
"flake-noredraw-nocache.test.mjs": {
|
|
153
|
-
"url": "https://console.testdriver.ai/runs/
|
|
154
|
-
"lastUpdated": "2026-
|
|
153
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62bd292ffa2dc646f1446",
|
|
154
|
+
"lastUpdated": "2026-03-03T00:32:25.283Z"
|
|
155
155
|
},
|
|
156
156
|
"flake-diffthreshold-05.test.mjs": {
|
|
157
|
-
"url": "https://console.testdriver.ai/runs/
|
|
158
|
-
"lastUpdated": "2026-
|
|
157
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62c0efc0ac3cc632a91ae",
|
|
158
|
+
"lastUpdated": "2026-03-03T00:32:25.283Z"
|
|
159
159
|
},
|
|
160
160
|
"flake-redraw-cache.test.mjs": {
|
|
161
|
-
"url": "https://console.testdriver.ai/runs/
|
|
162
|
-
"lastUpdated": "2026-
|
|
161
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62be8565d339e8065f196",
|
|
162
|
+
"lastUpdated": "2026-03-03T00:32:25.283Z"
|
|
163
163
|
},
|
|
164
164
|
"flake-redraw-nocache.test.mjs": {
|
|
165
|
-
"url": "https://console.testdriver.ai/runs/
|
|
166
|
-
"lastUpdated": "2026-
|
|
165
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62bf3fc0ac3cc632a91ad",
|
|
166
|
+
"lastUpdated": "2026-03-03T00:32:25.283Z"
|
|
167
167
|
},
|
|
168
168
|
"flake-rocket-match.test.mjs": {
|
|
169
|
-
"url": "https://console.testdriver.ai/runs/
|
|
170
|
-
"lastUpdated": "2026-
|
|
169
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62b84258e8885264fc729",
|
|
170
|
+
"lastUpdated": "2026-03-03T00:32:25.282Z"
|
|
171
171
|
},
|
|
172
172
|
"flake-diffthreshold-01.test.mjs": {
|
|
173
|
-
"url": "https://console.testdriver.ai/runs/
|
|
174
|
-
"lastUpdated": "2026-
|
|
173
|
+
"url": "https://console.testdriver.ai/runs/69a62b3aaa712ecd3dea730a/69a62bd0348bcf90ca081079",
|
|
174
|
+
"lastUpdated": "2026-03-03T00:32:25.283Z"
|
|
175
175
|
}
|
|
176
176
|
}
|
|
177
177
|
}
|
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
# Best Practices: Element Polling
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**When waiting for elements to appear, prefer `find()` with a `timeout` option over `wait()`.**
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## When to Use `wait()` vs `find()` with Timeout
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
`wait()` is useful for **simple pauses** — after actions, for animations, or for state changes to settle:
|
|
8
|
+
|
|
9
|
+
```javascript
|
|
10
|
+
await testdriver.find('submit button').click();
|
|
11
|
+
await testdriver.wait(2000); // Wait for animation to complete
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
However, **don't use `wait()` to wait for elements to appear**. For that, use `find()` with a `timeout`:
|
|
15
|
+
|
|
16
|
+
```javascript
|
|
17
|
+
// ✅ GOOD: Polls until the element appears (up to 30s)
|
|
18
|
+
const element = await testdriver.find('success message', { timeout: 30000 });
|
|
19
|
+
|
|
20
|
+
// ❌ BAD: Arbitrary wait then hope the element is there
|
|
21
|
+
await testdriver.wait(5000);
|
|
22
|
+
const element = await testdriver.find('success message');
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Why Prefer `find()` with Timeout for Element Waiting?
|
|
26
|
+
|
|
27
|
+
Using arbitrary waits for element detection has problems:
|
|
8
28
|
|
|
9
29
|
1. **Brittle**: Fixed timeouts may be too short (causing flaky tests) or too long (wasting time)
|
|
10
30
|
2. **Slow**: You always wait the full duration, even if the element appears sooner
|
|
@@ -147,8 +167,8 @@ await waitForElement(testdriver, "Processing complete indicator", 10, 2000);
|
|
|
147
167
|
| Pattern | Use Case |
|
|
148
168
|
|---------|----------|
|
|
149
169
|
| **Polling with `find()`** | ✅ Waiting for UI elements to appear or disappear |
|
|
150
|
-
| **`wait()`** | ❌
|
|
170
|
+
| **`wait()`** | ✅ Simple delays (animations, state changes) — ❌ Don't use for element waiting |
|
|
151
171
|
| **Helper function** | ✅ Recommended for cleaner, reusable code |
|
|
152
172
|
| **Conditional polling** | ✅ For optional elements (dialogs, notifications) |
|
|
153
173
|
|
|
154
|
-
Remember: **If you're waiting for something to appear on screen, use `find()`
|
|
174
|
+
Remember: **If you're waiting for something to appear on screen, use `find()` with a `timeout` option, not `wait()`. Use `wait()` for simple pauses between actions.**
|
package/docs/v7/examples/ai.mdx
CHANGED
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* ai.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b8f06a177a05bccd1dc/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* assert.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b6206a177a05bccd1cc/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* chrome-extension.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b91fc0ac3cc632a91a0/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* drag-and-drop.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b42fc0ac3cc632a918b/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* element-not-found.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b6f0d4a5265e44e7d7b/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* hover-image.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b77fc0ac3cc632a9198/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* hover-text.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b79dc33133fc0da9439/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* installer.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b570446888b52a4e1c9/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* launch-vscode-linux.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b91113035da665496a6/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* match-image.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b42258e8885264fc704/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* press-keys.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b800d4a5265e44e7d86/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* scroll-keyboard.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b82258e8885264fc728/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* scroll-until-image.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b4549845ced0b71e2b0/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* scroll-until-text.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b99dc33133fc0da9440/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* scroll.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b83113035da665496a3/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* type.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b8d49845ced0b71e2bf/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -12,7 +12,7 @@ Watch this test execute in a real sandbox environment:
|
|
|
12
12
|
|
|
13
13
|
{/* windows-installer.test.mjs output */}
|
|
14
14
|
<iframe
|
|
15
|
-
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/
|
|
15
|
+
src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b42565d339e8065f17f/replay"
|
|
16
16
|
width="100%"
|
|
17
17
|
height="390"
|
|
18
18
|
style={{ border: "1px solid #333", borderRadius: "8px" }}
|
|
@@ -29,6 +29,9 @@ await testdriver.find('dropdown menu').hover();
|
|
|
29
29
|
await testdriver.scroll('down', 500);
|
|
30
30
|
await testdriver.scrollUntilText('Footer content');
|
|
31
31
|
|
|
32
|
+
// Waiting
|
|
33
|
+
await testdriver.wait(2000); // Wait 2 seconds for animation/state change
|
|
34
|
+
|
|
32
35
|
// Extracting information from screen
|
|
33
36
|
const price = await testdriver.extract('the total price');
|
|
34
37
|
const orderNumber = await testdriver.extract('the order confirmation number');
|