testdriverai 7.0.0 → 7.1.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/AGENTS.md +550 -0
- package/CODEOWNERS +0 -1
- package/README.md +126 -0
- package/agent/index.js +43 -18
- package/agent/lib/commands.js +794 -135
- package/agent/lib/redraw.js +124 -39
- package/agent/lib/sandbox.js +10 -1
- package/agent/lib/sdk.js +21 -0
- package/docs/MIGRATION.md +425 -0
- package/docs/PRESETS.md +210 -0
- package/docs/docs.json +91 -37
- package/docs/guide/best-practices-polling.mdx +154 -0
- package/docs/v7/api/dashcam.mdx +497 -0
- package/docs/v7/api/doubleClick.mdx +102 -0
- package/docs/v7/api/mouseDown.mdx +161 -0
- package/docs/v7/api/mouseUp.mdx +164 -0
- package/docs/v7/api/rightClick.mdx +123 -0
- package/docs/v7/getting-started/configuration.mdx +380 -0
- package/docs/v7/getting-started/quickstart.mdx +273 -140
- package/docs/v7/guides/best-practices.mdx +486 -0
- package/docs/v7/guides/caching-ai.mdx +215 -0
- package/docs/v7/guides/caching-selectors.mdx +292 -0
- package/docs/v7/guides/caching.mdx +366 -0
- package/docs/v7/guides/ci-cd/azure.mdx +587 -0
- package/docs/v7/guides/ci-cd/circleci.mdx +523 -0
- package/docs/v7/guides/ci-cd/github-actions.mdx +457 -0
- package/docs/v7/guides/ci-cd/gitlab.mdx +498 -0
- package/docs/v7/guides/ci-cd/jenkins.mdx +664 -0
- package/docs/v7/guides/ci-cd/travis.mdx +438 -0
- package/docs/v7/guides/debugging.mdx +349 -0
- package/docs/v7/guides/faq.mdx +393 -0
- package/docs/v7/guides/performance.mdx +517 -0
- package/docs/v7/guides/troubleshooting.mdx +526 -0
- package/docs/v7/guides/vitest-plugin.mdx +477 -0
- package/docs/v7/guides/vitest.mdx +535 -0
- package/docs/v7/platforms/linux.mdx +308 -0
- package/docs/v7/platforms/macos.mdx +433 -0
- package/docs/v7/platforms/windows.mdx +430 -0
- package/docs/v7/presets/chrome-extension.mdx +223 -0
- package/docs/v7/presets/chrome.mdx +287 -0
- package/docs/v7/presets/electron.mdx +435 -0
- package/docs/v7/presets/vscode.mdx +398 -0
- package/docs/v7/presets/webapp.mdx +396 -0
- package/docs/v7/progressive-apis/CORE.md +459 -0
- package/docs/v7/progressive-apis/HOOKS.md +360 -0
- package/docs/v7/progressive-apis/PROGRESSIVE_DISCLOSURE.md +230 -0
- package/docs/v7/progressive-apis/PROVISION.md +266 -0
- package/interfaces/vitest-plugin.mjs +186 -100
- package/package.json +12 -1
- package/sdk.d.ts +335 -42
- package/sdk.js +756 -95
- package/src/core/Dashcam.js +469 -0
- package/src/core/index.d.ts +150 -0
- package/src/core/index.js +12 -0
- package/src/presets/index.mjs +331 -0
- package/src/vitest/extended.mjs +108 -0
- package/src/vitest/hooks.d.ts +119 -0
- package/src/vitest/hooks.mjs +298 -0
- package/src/vitest/index.mjs +64 -0
- package/src/vitest/lifecycle.mjs +277 -0
- package/src/vitest/utils.mjs +150 -0
- package/test/dashcam.test.js +137 -0
- package/testdriver/acceptance-sdk/assert.test.mjs +13 -31
- package/testdriver/acceptance-sdk/auto-cache-key-demo.test.mjs +56 -0
- package/testdriver/acceptance-sdk/chrome-extension.test.mjs +89 -0
- package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +7 -19
- package/testdriver/acceptance-sdk/element-not-found.test.mjs +6 -19
- package/testdriver/acceptance-sdk/exec-js.test.mjs +6 -18
- package/testdriver/acceptance-sdk/exec-output.test.mjs +8 -20
- package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +13 -25
- package/testdriver/acceptance-sdk/focus-window.test.mjs +8 -20
- package/testdriver/acceptance-sdk/formatted-logging.test.mjs +5 -20
- package/testdriver/acceptance-sdk/hooks-example.test.mjs +38 -0
- package/testdriver/acceptance-sdk/hover-image.test.mjs +10 -19
- package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +7 -19
- package/testdriver/acceptance-sdk/hover-text.test.mjs +5 -19
- package/testdriver/acceptance-sdk/match-image.test.mjs +7 -19
- package/testdriver/acceptance-sdk/presets-example.test.mjs +87 -0
- package/testdriver/acceptance-sdk/press-keys.test.mjs +5 -19
- package/testdriver/acceptance-sdk/prompt.test.mjs +6 -18
- package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +6 -20
- package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +6 -18
- package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +9 -23
- package/testdriver/acceptance-sdk/scroll.test.mjs +12 -21
- package/testdriver/acceptance-sdk/setup/testHelpers.mjs +124 -352
- package/testdriver/acceptance-sdk/sully-ai.test.mjs +234 -0
- package/testdriver/acceptance-sdk/test-console-logs.test.mjs +42 -0
- package/testdriver/acceptance-sdk/type.test.mjs +19 -58
- package/vitest.config.mjs +1 -0
- package/.vscode/mcp.json +0 -9
- package/MIGRATION.md +0 -389
- package/PLUGIN_MIGRATION.md +0 -222
- package/PROMPT_CACHE.md +0 -200
- package/SDK_LOGGING.md +0 -222
- package/SDK_MIGRATION.md +0 -474
- package/SDK_README.md +0 -1122
- package/debug-screenshot-1763401388589.png +0 -0
- package/examples/run-tests-with-recording.sh +0 -70
- package/examples/screenshot-example.js +0 -63
- package/examples/sdk-awesome-logs-demo.js +0 -177
- package/examples/sdk-cache-thresholds.js +0 -96
- package/examples/sdk-element-properties.js +0 -155
- package/examples/sdk-simple-example.js +0 -65
- package/examples/test-recording-example.test.js +0 -166
- package/mcp-server/AI_GUIDELINES.md +0 -57
- package/test-find-api.js +0 -73
- package/test-prompt-cache.js +0 -96
- package/test-sandbox-render.js +0 -28
- package/test-sdk-methods.js +0 -15
- package/test-sdk-refactor.js +0 -53
- package/test-stack-trace.mjs +0 -57
- package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +0 -239
package/docs/docs.json
CHANGED
|
@@ -22,55 +22,109 @@
|
|
|
22
22
|
{
|
|
23
23
|
"group": "Getting Started",
|
|
24
24
|
"pages": [
|
|
25
|
-
"/v7/getting-started/quickstart"
|
|
26
|
-
]
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
"group": "API Reference",
|
|
30
|
-
"pages": [
|
|
25
|
+
"/v7/getting-started/quickstart",
|
|
31
26
|
{
|
|
32
|
-
"group": "
|
|
27
|
+
"group": "Configuration",
|
|
33
28
|
"icon": "gear",
|
|
34
29
|
"pages": [
|
|
35
|
-
"/v7/
|
|
36
|
-
|
|
30
|
+
"/v7/getting-started/configuration",
|
|
31
|
+
{
|
|
32
|
+
"group": "Platforms",
|
|
33
|
+
"icon": "server",
|
|
34
|
+
"pages": [
|
|
35
|
+
"/v7/platforms/linux",
|
|
36
|
+
"/v7/platforms/windows",
|
|
37
|
+
"/v7/platforms/macos"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
37
40
|
]
|
|
38
|
-
}
|
|
41
|
+
}
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"group": "Examples",
|
|
46
|
+
"icon": "code",
|
|
47
|
+
"pages": [
|
|
48
|
+
"/v7/presets/chrome",
|
|
49
|
+
"/v7/presets/chrome-extension",
|
|
50
|
+
"/v7/presets/vscode",
|
|
51
|
+
"/v7/presets/electron"
|
|
52
|
+
]
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"group": "Interactions",
|
|
56
|
+
"icon": "bolt",
|
|
57
|
+
"pages": [
|
|
58
|
+
"/v7/api/find",
|
|
59
|
+
"/v7/api/ai",
|
|
60
|
+
"/v7/api/click",
|
|
61
|
+
"/v7/api/doubleClick",
|
|
62
|
+
"/v7/api/rightClick",
|
|
63
|
+
"/v7/api/hover",
|
|
64
|
+
"/v7/api/mouseDown",
|
|
65
|
+
"/v7/api/mouseUp",
|
|
66
|
+
"/v7/api/type",
|
|
67
|
+
"/v7/api/pressKeys",
|
|
68
|
+
"/v7/api/scroll",
|
|
69
|
+
"/v7/api/focusApplication",
|
|
70
|
+
"/v7/api/assert",
|
|
71
|
+
"/v7/api/exec"
|
|
72
|
+
]
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"group": "Core Concepts",
|
|
76
|
+
"icon": "book",
|
|
77
|
+
"pages": [
|
|
78
|
+
"/v7/api/client",
|
|
79
|
+
"/v7/api/sandbox",
|
|
80
|
+
"/v7/api/elements",
|
|
81
|
+
"/v7/api/dashcam",
|
|
82
|
+
"/v7/api/assertions"
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"group": "Progressive APIs",
|
|
87
|
+
"icon": "layer-group",
|
|
88
|
+
"pages": [
|
|
89
|
+
"/v7/progressive-apis/PROGRESSIVE_DISCLOSURE",
|
|
90
|
+
"/v7/progressive-apis/PROVISION",
|
|
91
|
+
"/v7/progressive-apis/HOOKS"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
{
|
|
96
|
+
"group": "Guides",
|
|
97
|
+
"icon": "book-open",
|
|
98
|
+
"pages": [
|
|
99
|
+
"/v7/guides/vitest",
|
|
100
|
+
"/v7/guides/vitest-plugin",
|
|
101
|
+
"/v7/guides/debugging",
|
|
102
|
+
"/v7/guides/best-practices",
|
|
103
|
+
"/v7/guides/troubleshooting",
|
|
104
|
+
"/v7/guides/faq",
|
|
39
105
|
{
|
|
40
|
-
"group": "
|
|
41
|
-
"icon": "
|
|
106
|
+
"group": "Caching",
|
|
107
|
+
"icon": "bolt",
|
|
42
108
|
"pages": [
|
|
43
|
-
"/v7/
|
|
109
|
+
"/v7/guides/caching-ai",
|
|
110
|
+
"/v7/guides/caching-selectors"
|
|
44
111
|
]
|
|
45
112
|
},
|
|
46
113
|
{
|
|
47
|
-
"group": "
|
|
48
|
-
"icon": "
|
|
114
|
+
"group": "CI/CD Integration",
|
|
115
|
+
"icon": "arrows-spin",
|
|
49
116
|
"pages": [
|
|
50
|
-
"/v7/
|
|
51
|
-
"/v7/
|
|
52
|
-
"/v7/
|
|
53
|
-
"/v7/
|
|
54
|
-
"/v7/
|
|
55
|
-
"/v7/
|
|
56
|
-
"/v7/
|
|
57
|
-
"/v7/api/focusApplication",
|
|
58
|
-
"/v7/api/assert"
|
|
117
|
+
"/v7/guides/ci-cd/overview",
|
|
118
|
+
"/v7/guides/ci-cd/github-actions",
|
|
119
|
+
"/v7/guides/ci-cd/gitlab",
|
|
120
|
+
"/v7/guides/ci-cd/circleci",
|
|
121
|
+
"/v7/guides/ci-cd/jenkins",
|
|
122
|
+
"/v7/guides/ci-cd/azure",
|
|
123
|
+
"/v7/guides/ci-cd/travis"
|
|
59
124
|
]
|
|
60
125
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
"icon": "book",
|
|
64
|
-
"pages": [
|
|
65
|
-
"/v7/api/elements",
|
|
66
|
-
"/v7/api/assertions"
|
|
67
|
-
]
|
|
68
|
-
}
|
|
69
|
-
]
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
"group": "Guides",
|
|
73
|
-
"pages": [
|
|
126
|
+
"/v7/guides/performance",
|
|
127
|
+
"/v7/guides/self-hosting",
|
|
74
128
|
"/v7/guides/migration"
|
|
75
129
|
]
|
|
76
130
|
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Best Practices: Element Polling
|
|
2
|
+
|
|
3
|
+
**⚠️ CRITICAL: Never use `wait()` for waiting for elements to appear.**
|
|
4
|
+
|
|
5
|
+
## Why Avoid `wait()`?
|
|
6
|
+
|
|
7
|
+
Arbitrary waits with `wait()` have several problems:
|
|
8
|
+
|
|
9
|
+
1. **Brittle**: Fixed timeouts may be too short (causing flaky tests) or too long (wasting time)
|
|
10
|
+
2. **Slow**: You always wait the full duration, even if the element appears sooner
|
|
11
|
+
3. **Unreliable**: Network conditions, system load, and other factors affect timing
|
|
12
|
+
4. **Hard to debug**: When tests fail, you don't know if it was a timing issue or actual failure
|
|
13
|
+
|
|
14
|
+
## The Right Way: Element Polling with `find()`
|
|
15
|
+
|
|
16
|
+
TestDriver's `find()` method is designed for element detection. Use it in a polling loop to wait for elements:
|
|
17
|
+
|
|
18
|
+
### Basic Polling Pattern
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
// ❌ WRONG: Using wait()
|
|
22
|
+
await testdriver.wait(2000);
|
|
23
|
+
const button = await testdriver.find("Submit button");
|
|
24
|
+
|
|
25
|
+
// ✅ CORRECT: Polling with find()
|
|
26
|
+
let button;
|
|
27
|
+
for (let i = 0; i < 10; i++) {
|
|
28
|
+
try {
|
|
29
|
+
button = await testdriver.find("Submit button");
|
|
30
|
+
if (button.found()) break;
|
|
31
|
+
} catch (e) {
|
|
32
|
+
if (i === 9) throw e; // Re-throw on last attempt
|
|
33
|
+
}
|
|
34
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Helper Function for Polling
|
|
39
|
+
|
|
40
|
+
Create a reusable helper function:
|
|
41
|
+
|
|
42
|
+
```javascript
|
|
43
|
+
async function waitForElement(testdriver, description, maxAttempts = 10, delayMs = 1000) {
|
|
44
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
45
|
+
try {
|
|
46
|
+
const element = await testdriver.find(description);
|
|
47
|
+
if (element.found()) {
|
|
48
|
+
return element;
|
|
49
|
+
}
|
|
50
|
+
} catch (e) {
|
|
51
|
+
if (i === maxAttempts - 1) throw e;
|
|
52
|
+
}
|
|
53
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
54
|
+
}
|
|
55
|
+
throw new Error(`Element not found after ${maxAttempts} attempts: ${description}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Usage
|
|
59
|
+
const emailField = await waitForElement(testdriver, "Email input field");
|
|
60
|
+
await emailField.click();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## When to Use Polling
|
|
64
|
+
|
|
65
|
+
Use element polling in these scenarios:
|
|
66
|
+
|
|
67
|
+
- **After navigation**: Waiting for a new page to load
|
|
68
|
+
- **After user action**: Waiting for UI updates (form submission, modal opening, etc.)
|
|
69
|
+
- **Dynamic content**: Waiting for AJAX-loaded elements
|
|
70
|
+
- **State transitions**: Waiting for loading spinners to disappear or success messages to appear
|
|
71
|
+
|
|
72
|
+
## Example: Complete Login Flow
|
|
73
|
+
|
|
74
|
+
```javascript
|
|
75
|
+
import { chrome } from "testdriverai/presets";
|
|
76
|
+
|
|
77
|
+
// Helper function
|
|
78
|
+
async function waitForElement(testdriver, description, maxAttempts = 10, delayMs = 1000) {
|
|
79
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
80
|
+
try {
|
|
81
|
+
const element = await testdriver.find(description);
|
|
82
|
+
if (element.found()) return element;
|
|
83
|
+
} catch (e) {
|
|
84
|
+
if (i === maxAttempts - 1) throw e;
|
|
85
|
+
}
|
|
86
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
87
|
+
}
|
|
88
|
+
throw new Error(`Element not found after ${maxAttempts} attempts: ${description}`);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
it("should log in successfully", async (context) => {
|
|
92
|
+
const { testdriver } = await chrome(context, {
|
|
93
|
+
url: 'https://example.com/login',
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
// Wait for login page to load
|
|
97
|
+
const emailField = await waitForElement(testdriver, "Email input field");
|
|
98
|
+
await emailField.click();
|
|
99
|
+
await testdriver.type("user@example.com");
|
|
100
|
+
|
|
101
|
+
const passwordField = await testdriver.find("Password input field");
|
|
102
|
+
await passwordField.click();
|
|
103
|
+
await testdriver.type("password123");
|
|
104
|
+
|
|
105
|
+
const loginButton = await testdriver.find("Login button");
|
|
106
|
+
await loginButton.click();
|
|
107
|
+
|
|
108
|
+
// Wait for dashboard to load after login
|
|
109
|
+
await waitForElement(testdriver, "Dashboard welcome message");
|
|
110
|
+
|
|
111
|
+
// Verify login successful
|
|
112
|
+
const isLoggedIn = await testdriver.assert("user is logged in to dashboard");
|
|
113
|
+
expect(isLoggedIn).toBeTruthy();
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Advanced: Conditional Polling
|
|
118
|
+
|
|
119
|
+
For elements that may or may not appear (like dialogs or notifications):
|
|
120
|
+
|
|
121
|
+
```javascript
|
|
122
|
+
// Try to find and dismiss optional dialog
|
|
123
|
+
try {
|
|
124
|
+
const dialog = await waitForElement(testdriver, "Cookie consent dialog", 3, 500);
|
|
125
|
+
const acceptButton = await testdriver.find("Accept button");
|
|
126
|
+
await acceptButton.click();
|
|
127
|
+
console.log("Dismissed cookie dialog");
|
|
128
|
+
} catch {
|
|
129
|
+
console.log("No cookie dialog found, continuing...");
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Configuration
|
|
134
|
+
|
|
135
|
+
Adjust polling parameters based on your needs:
|
|
136
|
+
|
|
137
|
+
```javascript
|
|
138
|
+
// Quick polling for fast UI updates (check every 300ms, up to 3 seconds)
|
|
139
|
+
await waitForElement(testdriver, "Success message", 10, 300);
|
|
140
|
+
|
|
141
|
+
// Patient polling for slow operations (check every 2s, up to 20 seconds)
|
|
142
|
+
await waitForElement(testdriver, "Processing complete indicator", 10, 2000);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Summary
|
|
146
|
+
|
|
147
|
+
| Pattern | Use Case |
|
|
148
|
+
|---------|----------|
|
|
149
|
+
| **Polling with `find()`** | ✅ Waiting for UI elements to appear or disappear |
|
|
150
|
+
| **`wait()`** | ❌ NEVER use for element waiting |
|
|
151
|
+
| **Helper function** | ✅ Recommended for cleaner, reusable code |
|
|
152
|
+
| **Conditional polling** | ✅ For optional elements (dialogs, notifications) |
|
|
153
|
+
|
|
154
|
+
Remember: **If you're waiting for something to appear on screen, use `find()` in a polling loop, not `wait()`.**
|