@testdriverai/mcp 7.10.0-canary.0 → 7.10.1-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/.claude-plugin/marketplace.json +17 -0
- package/agent/index.js +116 -71
- package/agent/interface.js +13 -1
- package/agent/lib/commands.js +35 -15
- package/agent/lib/provision-commands.js +92 -11
- package/agent/lib/sandbox.js +263 -30
- package/agent/lib/sdk.js +16 -12
- package/agent/lib/system.js +107 -31
- package/ai/.claude-plugin/plugin.json +89 -4
- package/ai/agents/testdriver.md +4 -8
- package/ai/skills/testdriver-assert/SKILL.md +0 -1
- package/ai/skills/testdriver-aws-setup/SKILL.md +1 -1
- package/ai/skills/testdriver-caching/SKILL.md +13 -6
- package/ai/skills/testdriver-ci-cd/SKILL.md +57 -14
- package/ai/skills/testdriver-client/SKILL.md +10 -6
- package/ai/skills/testdriver-customizing-devices/SKILL.md +7 -5
- package/ai/skills/testdriver-dashcam/SKILL.md +36 -3
- package/ai/skills/testdriver-debugging-with-screenshots/SKILL.md +28 -4
- package/ai/skills/testdriver-device-config/SKILL.md +5 -5
- package/ai/skills/testdriver-find/SKILL.md +50 -11
- package/ai/skills/testdriver-generating-tests/SKILL.md +163 -8
- package/ai/skills/testdriver-hosted/SKILL.md +1 -1
- package/ai/skills/testdriver-machine-setup/SKILL.md +331 -0
- package/ai/skills/testdriver-making-assertions/SKILL.md +78 -2
- package/ai/skills/testdriver-mcp-workflow/SKILL.md +2 -2
- package/ai/skills/testdriver-parse/SKILL.md +1 -1
- package/ai/skills/testdriver-performing-actions/SKILL.md +97 -3
- package/ai/skills/testdriver-provision/SKILL.md +0 -20
- package/ai/skills/testdriver-quickstart/SKILL.md +299 -21
- package/ai/skills/testdriver-reusable-code/SKILL.md +3 -3
- package/ai/skills/testdriver-running-tests/SKILL.md +1 -1
- package/ai/skills/testdriver-screenshot/SKILL.md +5 -5
- package/ai/skills/testdriver-scroll/SKILL.md +30 -160
- package/ai/skills/testdriver-test-writer/SKILL.md +3 -5
- package/ai/skills/testdriver-testdriver/SKILL.md +3 -7
- package/ai/skills/testdriver-wait/SKILL.md +1 -1
- package/{docs/v7/ai.mdx → ai/skills/testdriver:ai/SKILL.md} +3 -4
- package/ai/skills/testdriver:assert/SKILL.md +315 -0
- package/ai/skills/testdriver:aws-setup/SKILL.md +448 -0
- package/ai/skills/testdriver:cache/SKILL.md +221 -0
- package/ai/skills/testdriver:caching/SKILL.md +124 -0
- package/ai/skills/testdriver:captcha/SKILL.md +158 -0
- package/ai/skills/testdriver:ci-cd/SKILL.md +659 -0
- package/ai/skills/testdriver:click/SKILL.md +286 -0
- package/ai/skills/testdriver:client/SKILL.md +477 -0
- package/ai/skills/testdriver:customizing-devices/SKILL.md +319 -0
- package/ai/skills/testdriver:dashcam/SKILL.md +451 -0
- package/ai/skills/testdriver:debugging-with-screenshots/SKILL.md +415 -0
- package/{docs/v7/device-config.mdx → ai/skills/testdriver:device-config/SKILL.md} +8 -8
- package/ai/skills/testdriver:double-click/SKILL.md +102 -0
- package/ai/skills/testdriver:elements/SKILL.md +605 -0
- package/ai/skills/testdriver:enterprise/SKILL.md +7 -0
- package/ai/skills/testdriver:errors/SKILL.md +246 -0
- package/ai/skills/testdriver:events/SKILL.md +356 -0
- package/ai/skills/testdriver:exec/SKILL.md +317 -0
- package/ai/skills/testdriver:find/SKILL.md +860 -0
- package/ai/skills/testdriver:focus-application/SKILL.md +293 -0
- package/ai/skills/testdriver:generating-tests/SKILL.md +25 -0
- package/ai/skills/testdriver:hosted/SKILL.md +156 -0
- package/ai/skills/testdriver:hover/SKILL.md +278 -0
- package/{docs/v7/locating-elements.mdx → ai/skills/testdriver:locating-elements/SKILL.md} +3 -3
- package/ai/skills/testdriver:making-assertions/SKILL.md +32 -0
- package/ai/skills/testdriver:mcp/SKILL.md +7 -0
- package/ai/skills/testdriver:mouse-down/SKILL.md +161 -0
- package/ai/skills/testdriver:mouse-up/SKILL.md +164 -0
- package/ai/skills/testdriver:parse/SKILL.md +236 -0
- package/ai/skills/testdriver:performing-actions/SKILL.md +53 -0
- package/ai/skills/testdriver:press-keys/SKILL.md +348 -0
- package/ai/skills/testdriver:provision/SKILL.md +331 -0
- package/ai/skills/testdriver:quickstart/SKILL.md +172 -0
- package/ai/skills/testdriver:redraw/SKILL.md +214 -0
- package/ai/skills/testdriver:reusable-code/SKILL.md +249 -0
- package/ai/skills/testdriver:right-click/SKILL.md +123 -0
- package/{docs/v7/running-tests.mdx → ai/skills/testdriver:running-tests/SKILL.md} +4 -4
- package/ai/skills/testdriver:screenshot/SKILL.md +248 -0
- package/ai/skills/testdriver:screenshots/SKILL.md +184 -0
- package/ai/skills/testdriver:scroll/SKILL.md +205 -0
- package/ai/skills/testdriver:secrets/SKILL.md +115 -0
- package/ai/skills/testdriver:self-hosted/SKILL.md +147 -0
- package/ai/skills/testdriver:test-results-json/SKILL.md +257 -0
- package/ai/skills/testdriver:testdriver/SKILL.md +624 -0
- package/ai/skills/testdriver:type/SKILL.md +357 -0
- package/ai/skills/testdriver:variables/SKILL.md +111 -0
- package/ai/skills/testdriver:wait/SKILL.md +50 -0
- package/{docs/v7/waiting-for-elements.mdx → ai/skills/testdriver:waiting-for-elements/SKILL.md} +3 -3
- package/ai/skills/testdriver:what-is-testdriver/SKILL.md +54 -0
- package/debugger/index.html +12 -0
- package/docs/_data/examples-manifest.json +54 -46
- package/docs/_data/examples-manifest.schema.json +12 -2
- package/docs/_scripts/extract-example-urls.js +21 -3
- package/docs/_scripts/generate-examples.js +64 -16
- package/docs/changelog.mdx +29 -2
- package/docs/claude-mcp-plugin.mdx +65 -18
- package/docs/docs.json +16 -33
- package/docs/snippets/tests/scroll-yaml.mdx +2 -2
- package/docs/v6/commands/scroll-until-image.mdx +1 -1
- package/docs/v6/commands/scroll-until-text.mdx +2 -2
- package/docs/v6/commands/scroll.mdx +2 -2
- package/docs/v7/_drafts/agents.mdx +0 -4
- package/docs/v7/_drafts/best-practices.mdx +0 -12
- package/docs/v7/_drafts/commands/scroll-until-image.mdx +1 -1
- package/docs/v7/_drafts/commands/scroll-until-text.mdx +2 -2
- package/docs/v7/_drafts/commands/scroll.mdx +1 -1
- package/docs/v7/_drafts/configuration.mdx +0 -12
- package/docs/v7/_drafts/faq.mdx +2 -10
- package/docs/v7/_drafts/readme.mdx +0 -1
- package/docs/v7/_drafts/troubleshooting.mdx +1 -4
- package/docs/v7/assert.mdx +0 -1
- package/docs/v7/aws-setup.mdx +1 -1
- package/docs/v7/caching.mdx +14 -10
- package/docs/v7/ci-cd.mdx +98 -14
- package/docs/v7/client.mdx +10 -6
- package/docs/v7/copilot/auto-healing.mdx +167 -18
- package/docs/v7/copilot/running-tests.mdx +915 -54
- package/docs/v7/customizing-devices.mdx +7 -5
- package/docs/v7/dashcam.mdx +36 -3
- package/docs/v7/debugging-with-screenshots.mdx +31 -7
- package/docs/v7/examples/ai.mdx +13 -8
- package/docs/v7/examples/assert.mdx +1 -1
- package/docs/v7/examples/chrome-extension.mdx +30 -13
- package/docs/v7/examples/element-not-found.mdx +1 -1
- package/docs/v7/examples/findall-coffee-icons.mdx +3 -2
- package/docs/v7/examples/formatted-logging.mdx +74 -0
- package/docs/v7/examples/hover-image.mdx +1 -1
- package/docs/v7/examples/hover-text-with-description.mdx +2 -2
- 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 +27 -27
- package/docs/v7/examples/match-image.mdx +1 -1
- package/docs/v7/examples/parse.mdx +3 -2
- package/docs/v7/examples/press-keys.mdx +2 -2
- package/docs/v7/examples/scroll-keyboard.mdx +2 -2
- 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/find.mdx +50 -11
- package/docs/v7/generating-tests.mdx +166 -10
- package/docs/v7/hosted.mdx +1 -1
- package/docs/v7/making-assertions.mdx +81 -4
- package/docs/v7/parse.mdx +1 -1
- package/docs/v7/performing-actions.mdx +100 -5
- package/docs/v7/provision.mdx +0 -20
- package/docs/v7/quickstart.mdx +299 -21
- package/docs/v7/reusable-code.mdx +3 -3
- package/docs/v7/screenshot.mdx +5 -5
- package/docs/v7/scroll.mdx +30 -160
- package/docs/v7/wait.mdx +1 -1
- package/examples/chrome-extension.test.mjs +29 -12
- package/examples/config.mjs +1 -1
- package/examples/launch-vscode-linux.test.mjs +25 -25
- package/interfaces/cli/commands/init.js +84 -2
- package/interfaces/vitest-plugin.mjs +51 -61
- package/lib/core/Dashcam.js +43 -2
- package/lib/core/index.d.ts +13 -1
- package/lib/github-comment.mjs +11 -5
- package/lib/init-project.js +98 -44
- package/lib/install-clients.js +470 -0
- package/lib/provision.js +749 -0
- package/lib/resolve-channel.js +2 -2
- package/lib/sentry.js +15 -2
- package/lib/vitest/hooks.mjs +44 -34
- package/mcp-server/README.md +32 -0
- package/mcp-server/dist/core/actions.d.ts +176 -0
- package/mcp-server/dist/core/actions.js +753 -0
- package/mcp-server/dist/env-utils.d.ts +45 -0
- package/mcp-server/dist/env-utils.js +63 -0
- package/mcp-server/dist/provision-types.d.ts +6 -2
- package/mcp-server/dist/provision-types.js +3 -1
- package/mcp-server/dist/server.mjs +449 -751
- package/mcp-server/package-lock.json +384 -1
- package/mcp-server/package.json +5 -2
- package/mcp-server/src/core/actions.ts +912 -0
- package/mcp-server/src/env-utils.test.ts +82 -0
- package/mcp-server/src/env-utils.ts +77 -0
- package/mcp-server/src/provision-types.ts +4 -1
- package/mcp-server/src/server.ts +533 -954
- package/mcp-server/tsconfig.json +1 -1
- package/mcp-server/vitest.config.ts +7 -0
- package/package.json +15 -3
- package/sdk.d.ts +11 -9
- package/sdk.js +116 -746
- package/setup/aws/spawn-runner.sh +24 -3
- package/docs/v7/_drafts/caching-ai.mdx +0 -215
- package/docs/v7/_drafts/caching.mdx +0 -366
- package/docs/v7/_drafts/prompt-cache.mdx +0 -200
- package/docs/v7/copilot/creating-tests.mdx +0 -156
- package/docs/v7/copilot/github.mdx +0 -143
- package/docs/v7/copilot/setup.mdx +0 -143
- package/docs/v7/examples/exec-output.mdx +0 -84
- package/docs/v7/examples/exec-pwsh.mdx +0 -82
- package/docs/v7/examples/focus-window.mdx +0 -61
- package/docs/v7/examples/scroll-until-image.mdx +0 -82
- package/docs/v7/examples/windows-installer.mdx +0 -95
- package/examples/formatted-logging.test.mjs +0 -27
- package/examples/match-image.test.mjs +0 -55
- package/manual/exec-stream-logs.test.mjs +0 -25
package/docs/v7/scroll.mdx
CHANGED
|
@@ -27,21 +27,21 @@ Scroll the page or active element in any direction using mouse wheel or keyboard
|
|
|
27
27
|
## Syntax
|
|
28
28
|
|
|
29
29
|
```javascript
|
|
30
|
-
await testdriver.scroll(direction,
|
|
30
|
+
await testdriver.scroll(direction, options)
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
## Parameters
|
|
34
34
|
|
|
35
35
|
<ParamField path="direction" type="string" default="down">
|
|
36
|
-
Direction to scroll: `'up'`, `'down'
|
|
36
|
+
Direction to scroll: `'up'`, `'down'`
|
|
37
37
|
</ParamField>
|
|
38
38
|
|
|
39
|
-
<ParamField path="
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
<ParamField path="options" type="object">
|
|
40
|
+
<Expandable title="properties">
|
|
41
|
+
<ParamField path="amount" type="number" default="300">
|
|
42
|
+
Amount to scroll in pixels
|
|
43
|
+
</ParamField>
|
|
44
|
+
</Expandable>
|
|
45
45
|
</ParamField>
|
|
46
46
|
|
|
47
47
|
## Returns
|
|
@@ -56,95 +56,34 @@ await testdriver.scroll(direction, amount, method)
|
|
|
56
56
|
// Scroll down (default)
|
|
57
57
|
await testdriver.scroll();
|
|
58
58
|
|
|
59
|
-
// Scroll down
|
|
60
|
-
await testdriver.scroll('down',
|
|
59
|
+
// Scroll down 5 clicks
|
|
60
|
+
await testdriver.scroll('down', { amount: 5 });
|
|
61
61
|
|
|
62
62
|
// Scroll up
|
|
63
63
|
await testdriver.scroll('up');
|
|
64
64
|
|
|
65
|
-
// Scroll up
|
|
66
|
-
await testdriver.scroll('up',
|
|
65
|
+
// Scroll up 2 clicks
|
|
66
|
+
await testdriver.scroll('up', { amount: 2 });
|
|
67
67
|
```
|
|
68
68
|
|
|
69
69
|
### Horizontal Scrolling
|
|
70
70
|
|
|
71
71
|
```javascript
|
|
72
72
|
// Scroll right
|
|
73
|
-
await testdriver.scroll('right',
|
|
73
|
+
await testdriver.scroll('right', { amount: 3 });
|
|
74
74
|
|
|
75
75
|
// Scroll left
|
|
76
|
-
await testdriver.scroll('left',
|
|
76
|
+
await testdriver.scroll('left', { amount: 3 });
|
|
77
77
|
```
|
|
78
78
|
|
|
79
79
|
### Scroll Methods
|
|
80
80
|
|
|
81
81
|
```javascript
|
|
82
|
-
// Mouse wheel scroll (
|
|
83
|
-
await testdriver.scroll('down',
|
|
84
|
-
|
|
85
|
-
// Keyboard scroll (uses Page Down/Up, more compatible)
|
|
86
|
-
await testdriver.scroll('down', 300, 'keyboard');
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Scroll Until Found
|
|
90
|
-
|
|
91
|
-
### scrollUntilText()
|
|
92
|
-
|
|
93
|
-
Scroll until specific text appears on screen.
|
|
94
|
-
|
|
95
|
-
```javascript
|
|
96
|
-
await testdriver.scrollUntilText(text, direction, maxDistance, textMatchMethod, method, invert)
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
**Parameters:**
|
|
100
|
-
- `text` (string) - Text to find
|
|
101
|
-
- `direction` (string) - Scroll direction (default: `'down'`)
|
|
102
|
-
- `maxDistance` (number) - Max pixels to scroll (default: 10000)
|
|
103
|
-
- `textMatchMethod` (string) - `'turbo'` or `'ai'` (default: `'turbo'`)
|
|
104
|
-
- `method` (string) - `'keyboard'` or `'mouse'` (default: `'keyboard'`)
|
|
105
|
-
- `invert` (boolean) - Scroll until text disappears (default: false)
|
|
106
|
-
|
|
107
|
-
**Examples:**
|
|
108
|
-
```javascript
|
|
109
|
-
// Scroll down until "Contact Us" appears
|
|
110
|
-
await testdriver.scrollUntilText('Contact Us');
|
|
111
|
-
|
|
112
|
-
// Scroll up to find text
|
|
113
|
-
await testdriver.scrollUntilText('Header', 'up');
|
|
114
|
-
|
|
115
|
-
// Scroll until text disappears
|
|
116
|
-
await testdriver.scrollUntilText('Loading...', 'down', 5000, 'turbo', 'keyboard', true);
|
|
82
|
+
// Mouse wheel scroll (default)
|
|
83
|
+
await testdriver.scroll('down', { amount: 3 });
|
|
117
84
|
|
|
118
|
-
//
|
|
119
|
-
await testdriver.
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
### scrollUntilImage()
|
|
123
|
-
|
|
124
|
-
Scroll until a visual element appears.
|
|
125
|
-
|
|
126
|
-
```javascript
|
|
127
|
-
await testdriver.scrollUntilImage(description, direction, maxDistance, method, path, invert)
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
**Parameters:**
|
|
131
|
-
- `description` (string) - Description of the image/element
|
|
132
|
-
- `direction` (string) - Scroll direction (default: `'down'`)
|
|
133
|
-
- `maxDistance` (number) - Max pixels to scroll (default: 10000)
|
|
134
|
-
- `method` (string) - `'keyboard'` or `'mouse'` (default: `'keyboard'`)
|
|
135
|
-
- `path` (string | null) - Path to image template (optional)
|
|
136
|
-
- `invert` (boolean) - Scroll until image disappears (default: false)
|
|
137
|
-
|
|
138
|
-
**Examples:**
|
|
139
|
-
```javascript
|
|
140
|
-
// Scroll until visual element appears
|
|
141
|
-
await testdriver.scrollUntilImage('red subscribe button');
|
|
142
|
-
|
|
143
|
-
// Scroll using image template
|
|
144
|
-
await testdriver.scrollUntilImage('', 'down', 10000, 'keyboard', './footer-logo.png');
|
|
145
|
-
|
|
146
|
-
// Scroll until image disappears
|
|
147
|
-
await testdriver.scrollUntilImage('loading spinner', 'down', 5000, 'keyboard', null, true);
|
|
85
|
+
// For keyboard-based scrolling, use pressKeys instead
|
|
86
|
+
await testdriver.pressKeys(['pagedown']);
|
|
148
87
|
```
|
|
149
88
|
|
|
150
89
|
## Best Practices
|
|
@@ -163,7 +102,7 @@ await testdriver.scrollUntilImage('loading spinner', 'down', 5000, 'keyboard', n
|
|
|
163
102
|
// await testdriver.find('page background').click();
|
|
164
103
|
|
|
165
104
|
// Now scroll will work properly
|
|
166
|
-
await testdriver.scroll('down'
|
|
105
|
+
await testdriver.scroll('down');
|
|
167
106
|
|
|
168
107
|
// If scroll still doesn't work, use Page Down directly
|
|
169
108
|
// await testdriver.pressKeys(['pagedown']);
|
|
@@ -171,84 +110,31 @@ await testdriver.scrollUntilImage('loading spinner', 'down', 5000, 'keyboard', n
|
|
|
171
110
|
</Check>
|
|
172
111
|
|
|
173
112
|
<Check>
|
|
174
|
-
**
|
|
175
|
-
|
|
176
|
-
```javascript
|
|
177
|
-
// For web pages, mouse scroll is usually smoother
|
|
178
|
-
await testdriver.scroll('down', 300, 'mouse');
|
|
179
|
-
|
|
180
|
-
// For desktop apps or when mouse doesn't work
|
|
181
|
-
await testdriver.scroll('down', 300, 'keyboard');
|
|
182
|
-
```
|
|
183
|
-
</Check>
|
|
184
|
-
|
|
185
|
-
<Check>
|
|
186
|
-
**Use scrollUntil for dynamic content**
|
|
113
|
+
**Control scroll distance with the options object**
|
|
187
114
|
|
|
188
115
|
```javascript
|
|
189
|
-
//
|
|
190
|
-
await testdriver.
|
|
116
|
+
// For web pages, mouse scroll works well
|
|
117
|
+
await testdriver.scroll('down', { amount: 3 });
|
|
191
118
|
|
|
192
|
-
|
|
193
|
-
await
|
|
194
|
-
```
|
|
195
|
-
</Check>
|
|
196
|
-
|
|
197
|
-
<Check>
|
|
198
|
-
**Set reasonable max distance**
|
|
199
|
-
|
|
200
|
-
```javascript
|
|
201
|
-
// Avoid infinite scrolling
|
|
202
|
-
await testdriver.scrollUntilText('Footer', 'down', 5000); // Max 5000px
|
|
119
|
+
// For desktop apps or when mouse doesn't work, use keyboard
|
|
120
|
+
await testdriver.pressKeys(['pagedown']);
|
|
203
121
|
```
|
|
204
122
|
</Check>
|
|
205
123
|
|
|
206
124
|
<Warning>
|
|
207
125
|
**Keyboard scroll uses Page Down/Up**
|
|
208
126
|
|
|
209
|
-
Keyboard scrolling typically moves by one "page" at a time, which may be more than the specified
|
|
127
|
+
Keyboard scrolling typically moves by one "page" at a time, which may be more than the specified click amount. It's more compatible but less precise than mouse scrolling.
|
|
210
128
|
</Warning>
|
|
211
129
|
|
|
212
130
|
## Use Cases
|
|
213
131
|
|
|
214
132
|
<AccordionGroup>
|
|
215
|
-
<Accordion title="Navigate to Footer">
|
|
216
|
-
```javascript
|
|
217
|
-
// Scroll to bottom of page
|
|
218
|
-
await testdriver.scrollUntilText('Contact Us');
|
|
219
|
-
|
|
220
|
-
const contactLink = await testdriver.find('Contact Us link');
|
|
221
|
-
await contactLink.click();
|
|
222
|
-
```
|
|
223
|
-
</Accordion>
|
|
224
|
-
|
|
225
|
-
<Accordion title="Load More Results">
|
|
226
|
-
```javascript
|
|
227
|
-
// Scroll to load more button
|
|
228
|
-
await testdriver.scrollUntilText('Load More');
|
|
229
|
-
|
|
230
|
-
const loadBtn = await testdriver.find('Load More button');
|
|
231
|
-
await loadBtn.click();
|
|
232
|
-
|
|
233
|
-
await new Promise(r => setTimeout(r, 2000));
|
|
234
|
-
```
|
|
235
|
-
</Accordion>
|
|
236
|
-
|
|
237
|
-
<Accordion title="Find Element in Long List">
|
|
238
|
-
```javascript
|
|
239
|
-
// Scroll through list to find item
|
|
240
|
-
await testdriver.scrollUntilText('Product #42');
|
|
241
|
-
|
|
242
|
-
const product = await testdriver.find('Product #42');
|
|
243
|
-
await product.click();
|
|
244
|
-
```
|
|
245
|
-
</Accordion>
|
|
246
|
-
|
|
247
133
|
<Accordion title="Infinite Scroll">
|
|
248
134
|
```javascript
|
|
249
135
|
// Scroll multiple times for infinite scroll
|
|
250
136
|
for (let i = 0; i < 5; i++) {
|
|
251
|
-
await testdriver.scroll('down',
|
|
137
|
+
await testdriver.scroll('down', { amount: 5 });
|
|
252
138
|
await new Promise(r => setTimeout(r, 1000)); // Wait for load
|
|
253
139
|
}
|
|
254
140
|
```
|
|
@@ -257,7 +143,7 @@ await testdriver.scrollUntilImage('loading spinner', 'down', 5000, 'keyboard', n
|
|
|
257
143
|
<Accordion title="Horizontal Gallery">
|
|
258
144
|
```javascript
|
|
259
145
|
// Navigate horizontal carousel
|
|
260
|
-
await testdriver.scroll('right',
|
|
146
|
+
await testdriver.scroll('right', { amount: 3 });
|
|
261
147
|
await new Promise(r => setTimeout(r, 500));
|
|
262
148
|
|
|
263
149
|
const nextImage = await testdriver.find('next image in carousel');
|
|
@@ -288,8 +174,8 @@ describe('Scrolling', () => {
|
|
|
288
174
|
it('should scroll to find elements', async () => {
|
|
289
175
|
await testdriver.focusApplication('Google Chrome');
|
|
290
176
|
|
|
291
|
-
// Scroll
|
|
292
|
-
await testdriver.
|
|
177
|
+
// Scroll down the page
|
|
178
|
+
await testdriver.scroll('down', { amount: 5 });
|
|
293
179
|
|
|
294
180
|
// Click footer link
|
|
295
181
|
const privacyLink = await testdriver.find('Privacy Policy link');
|
|
@@ -303,29 +189,13 @@ describe('Scrolling', () => {
|
|
|
303
189
|
|
|
304
190
|
// Scroll multiple times to load content
|
|
305
191
|
for (let i = 0; i < 3; i++) {
|
|
306
|
-
await testdriver.scroll('down',
|
|
192
|
+
await testdriver.scroll('down', { amount: 5 });
|
|
307
193
|
await new Promise(r => setTimeout(r, 1500)); // Wait for load
|
|
308
194
|
}
|
|
309
195
|
|
|
310
196
|
// Verify content loaded
|
|
311
197
|
await testdriver.assert('more than 10 items are visible');
|
|
312
198
|
});
|
|
313
|
-
|
|
314
|
-
it('should scroll until loading completes', async () => {
|
|
315
|
-
// Scroll until loading spinner disappears
|
|
316
|
-
await testdriver.scrollUntilImage(
|
|
317
|
-
'loading spinner',
|
|
318
|
-
'down',
|
|
319
|
-
5000,
|
|
320
|
-
'keyboard',
|
|
321
|
-
null,
|
|
322
|
-
true // invert - wait for it to disappear
|
|
323
|
-
);
|
|
324
|
-
|
|
325
|
-
// Now interact with loaded content
|
|
326
|
-
const firstResult = await testdriver.find('first search result');
|
|
327
|
-
await firstResult.click();
|
|
328
|
-
});
|
|
329
199
|
});
|
|
330
200
|
```
|
|
331
201
|
|
package/docs/v7/wait.mdx
CHANGED
|
@@ -49,4 +49,4 @@ await testdriver.wait();
|
|
|
49
49
|
const element = await testdriver.find('success message');
|
|
50
50
|
```
|
|
51
51
|
- Avoid excessively long timeouts to keep tests efficient.
|
|
52
|
-
- Use sparingly — TestDriver's [redraw detection](/v7/waiting-for-
|
|
52
|
+
- Use sparingly — TestDriver's [redraw detection](/v7/performing-actions#waiting-for-dynamic-content) automatically waits for screen and network stability after each action.
|
|
@@ -52,18 +52,35 @@ describe("Chrome Extension Test", () => {
|
|
|
52
52
|
// The hello-world extension adds a puzzle piece icon to the toolbar
|
|
53
53
|
// When clicked, it shows a popup with "Hello Extensions"
|
|
54
54
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
55
|
+
// Retry opening the extension popup and verifying it up to 3 times
|
|
56
|
+
let popupResult;
|
|
57
|
+
let lastError;
|
|
58
|
+
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
59
|
+
try {
|
|
60
|
+
// Click on the extensions button (puzzle piece icon) in Chrome toolbar
|
|
61
|
+
const extensionsButton = await testdriver.find("The extensions button in the Chrome toolbar", {zoom: true, verify: true, timeout: 10000});
|
|
62
|
+
await extensionsButton.click();
|
|
63
|
+
|
|
64
|
+
// Look for the hello world extension in the extensions menu
|
|
65
|
+
const helloExtension = await testdriver.find("Hello Extensions extension in the extensions dropdown", {zoom: true, verify: true, timeout: 10000});
|
|
66
|
+
await helloExtension.click();
|
|
67
|
+
|
|
68
|
+
await testdriver.wait(2000); // wait for the popup to open
|
|
69
|
+
|
|
70
|
+
// Verify the extension popup shows "Hello Extensions" text
|
|
71
|
+
popupResult = await testdriver.assert("a popup shows with the text 'Hello Extensions'");
|
|
72
|
+
if (popupResult) break;
|
|
73
|
+
} catch (err) {
|
|
74
|
+
lastError = err;
|
|
75
|
+
console.log(`Attempt ${attempt} failed:`, err.message);
|
|
76
|
+
}
|
|
77
|
+
if (attempt < 3) {
|
|
78
|
+
// Dismiss any open popup/menu before retrying
|
|
79
|
+
await testdriver.pressKeys(["escape"]);
|
|
80
|
+
await testdriver.wait(1000);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (!popupResult && lastError) throw lastError;
|
|
67
84
|
expect(popupResult).toBeTruthy();
|
|
68
85
|
});
|
|
69
86
|
|
package/examples/config.mjs
CHANGED
|
@@ -22,34 +22,34 @@ describe("Launch VS Code on Linux", () => {
|
|
|
22
22
|
},
|
|
23
23
|
);
|
|
24
24
|
|
|
25
|
-
it.skipIf(!isLinux)(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
// it.skipIf(!isLinux)(
|
|
26
|
+
// "should install and use a VS Code extension",
|
|
27
|
+
// async (context) => {
|
|
28
|
+
// const testdriver = TestDriver(context, { ...getDefaults(context) });
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
// // Launch VS Code with the Prettier extension installed
|
|
31
|
+
// await testdriver.provision.vscode({
|
|
32
|
+
// extensions: ["esbenp.prettier-vscode"],
|
|
33
|
+
// });
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
// const vsCodeWindow = await testdriver.find(
|
|
36
|
+
// "Visual Studio Code window",
|
|
37
|
+
// { timeout: 60000 }
|
|
38
|
+
// );
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
// expect(vsCodeWindow.found()).toBeTruthy();
|
|
41
41
|
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
// // Open the extensions panel to verify Prettier is installed
|
|
43
|
+
// await testdriver.pressKeys(["ctrl", "shift", "x"]);
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
);
|
|
45
|
+
// // Wait for extensions panel to open
|
|
46
|
+
// await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
47
|
+
|
|
48
|
+
// // Assert that Prettier extension is visible in the installed extensions
|
|
49
|
+
// const prettierVisible = await testdriver.assert(
|
|
50
|
+
// "Prettier extension is visible in the extensions panel or sidebar",
|
|
51
|
+
// );
|
|
52
|
+
// expect(prettierVisible).toBeTruthy();
|
|
53
|
+
// },
|
|
54
|
+
// );
|
|
55
55
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const BaseCommand = require("../lib/base.js");
|
|
2
2
|
const { createCommandDefinitions } = require("../../../agent/interface.js");
|
|
3
3
|
const { initProject } = require("../../../lib/init-project.js");
|
|
4
|
+
const { CLIENTS, detectClients } = require("../../../lib/install-clients.js");
|
|
4
5
|
const fs = require("fs");
|
|
5
6
|
const path = require("path");
|
|
6
7
|
const chalk = require("chalk");
|
|
@@ -22,13 +23,24 @@ const POLL_TIMEOUT = 900000; // 15 minutes
|
|
|
22
23
|
*/
|
|
23
24
|
class InitCommand extends BaseCommand {
|
|
24
25
|
async run() {
|
|
25
|
-
await this.parse(InitCommand);
|
|
26
|
+
const { flags } = await this.parse(InitCommand);
|
|
26
27
|
|
|
27
28
|
console.log(chalk.cyan("\n🚀 Initializing TestDriver project...\n"));
|
|
28
29
|
|
|
29
30
|
// Prompt for API key first
|
|
30
31
|
const apiKey = await this.promptForApiKey();
|
|
31
32
|
|
|
33
|
+
// Determine which AI clients to wire up: from --client flag, or interactively.
|
|
34
|
+
let clients;
|
|
35
|
+
if (flags.client) {
|
|
36
|
+
clients = flags.client
|
|
37
|
+
.split(",")
|
|
38
|
+
.map((c) => c.trim())
|
|
39
|
+
.filter(Boolean);
|
|
40
|
+
} else {
|
|
41
|
+
clients = await this.promptForClients();
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
// Helper to print progress messages with appropriate colors
|
|
33
45
|
const printProgress = (msg) => {
|
|
34
46
|
if (msg.startsWith("✓")) {
|
|
@@ -47,6 +59,10 @@ class InitCommand extends BaseCommand {
|
|
|
47
59
|
targetDir: process.cwd(),
|
|
48
60
|
apiKey: apiKey,
|
|
49
61
|
skipInstall: false,
|
|
62
|
+
// --no-sample-test sets flags["sample-test"] to false
|
|
63
|
+
skipSampleTest: flags["sample-test"] === false,
|
|
64
|
+
clients: clients && clients.length ? clients : undefined,
|
|
65
|
+
channel: channelConfig.active,
|
|
50
66
|
onProgress: printProgress,
|
|
51
67
|
});
|
|
52
68
|
|
|
@@ -159,6 +175,72 @@ class InitCommand extends BaseCommand {
|
|
|
159
175
|
}
|
|
160
176
|
}
|
|
161
177
|
|
|
178
|
+
/**
|
|
179
|
+
* Prompt the user to select which AI clients to install TestDriver into.
|
|
180
|
+
* Returns an array of client ids (possibly empty).
|
|
181
|
+
* @returns {Promise<string[]>}
|
|
182
|
+
*/
|
|
183
|
+
async promptForClients() {
|
|
184
|
+
const ids = Object.keys(CLIENTS);
|
|
185
|
+
const detected = new Set(detectClients(process.cwd()));
|
|
186
|
+
|
|
187
|
+
// Non-interactive stdin (CI, piped input, no TTY): don't prompt — fall back
|
|
188
|
+
// to detected clients so init can still scaffold all files unattended.
|
|
189
|
+
if (!process.stdin.isTTY) {
|
|
190
|
+
return [...detected];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
console.log(chalk.cyan("\n Which AI clients should TestDriver be installed into?\n"));
|
|
194
|
+
ids.forEach((id, i) => {
|
|
195
|
+
const c = CLIENTS[id];
|
|
196
|
+
const tag = detected.has(id) ? chalk.green(" (detected)") : "";
|
|
197
|
+
const web = c.type === "web" ? chalk.gray(" — web, manual steps") : "";
|
|
198
|
+
console.log(` ${chalk.cyan(i + 1)}. ${c.label}${tag}${web}`);
|
|
199
|
+
});
|
|
200
|
+
console.log(` ${chalk.cyan("a")}. All of the above`);
|
|
201
|
+
console.log(
|
|
202
|
+
chalk.gray(
|
|
203
|
+
"\n Enter numbers separated by commas (e.g. 1,3), 'a' for all, or press Enter to skip.\n",
|
|
204
|
+
),
|
|
205
|
+
);
|
|
206
|
+
|
|
207
|
+
const answer = await new Promise((resolve) => {
|
|
208
|
+
const rl = readline.createInterface({
|
|
209
|
+
input: process.stdin,
|
|
210
|
+
output: process.stdout,
|
|
211
|
+
});
|
|
212
|
+
// Default to detected clients if any, else skip.
|
|
213
|
+
const defaultHint = detected.size
|
|
214
|
+
? ` [${[...detected].map((d) => ids.indexOf(d) + 1).join(",")}]`
|
|
215
|
+
: "";
|
|
216
|
+
let answered = false;
|
|
217
|
+
rl.question(` Select clients${defaultHint}: `, (a) => {
|
|
218
|
+
answered = true;
|
|
219
|
+
rl.close();
|
|
220
|
+
resolve(a.trim());
|
|
221
|
+
});
|
|
222
|
+
// If stdin closes (EOF) before a line is entered, the question callback
|
|
223
|
+
// never fires — resolve empty so init doesn't hang.
|
|
224
|
+
rl.on("close", () => {
|
|
225
|
+
if (!answered) resolve("");
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (!answer) {
|
|
230
|
+
// Enter pressed: use detected clients if any, otherwise none.
|
|
231
|
+
return [...detected];
|
|
232
|
+
}
|
|
233
|
+
if (answer.toLowerCase() === "a" || answer.toLowerCase() === "all") {
|
|
234
|
+
return ["all"];
|
|
235
|
+
}
|
|
236
|
+
const selected = answer
|
|
237
|
+
.split(",")
|
|
238
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
239
|
+
.filter((n) => Number.isInteger(n) && n >= 1 && n <= ids.length)
|
|
240
|
+
.map((n) => ids[n - 1]);
|
|
241
|
+
return selected;
|
|
242
|
+
}
|
|
243
|
+
|
|
162
244
|
/**
|
|
163
245
|
* Browser-based login flow using device code
|
|
164
246
|
* @returns {Promise<string>} The API key
|
|
@@ -411,7 +493,7 @@ class InitCommand extends BaseCommand {
|
|
|
411
493
|
console.log(" 2. Use AI agents to write tests:");
|
|
412
494
|
console.log(chalk.gray(" Open VSCode/Cursor and use @testdriver agent\n"));
|
|
413
495
|
console.log(" 3. MCP server configured:");
|
|
414
|
-
console.log(chalk.gray(" TestDriver tools
|
|
496
|
+
console.log(chalk.gray(" TestDriver tools are now available in your selected AI client(s)\n"));
|
|
415
497
|
console.log(
|
|
416
498
|
" 4. For CI/CD, add TD_API_KEY to your GitHub repository secrets",
|
|
417
499
|
);
|