testdriverai 7.9.9-test → 7.9.11-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/agent/lib/sandbox.js +6 -6
- package/ai/agents/testdriver.md +3 -7
- package/ai/skills/testdriver-find/SKILL.md +11 -11
- package/ai/skills/testdriver-performing-actions/SKILL.md +0 -1
- package/ai/skills/testdriver-screenshot/SKILL.md +1 -1
- package/ai/skills/testdriver-scroll/SKILL.md +20 -150
- package/ai/skills/testdriver-test-writer/SKILL.md +0 -2
- package/ai/skills/testdriver-testdriver/SKILL.md +3 -7
- package/docs/_data/examples-manifest.json +42 -42
- 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/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/readme.mdx +0 -1
- package/docs/v7/aws-setup.mdx +1 -1
- package/docs/v7/ci-cd.mdx +1 -1
- package/docs/v7/client.mdx +5 -5
- package/docs/v7/customizing-devices.mdx +1 -1
- package/docs/v7/device-config.mdx +5 -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/element-not-found.mdx +1 -1
- package/docs/v7/examples/exec-output.mdx +1 -1
- package/docs/v7/examples/exec-pwsh.mdx +1 -1
- package/docs/v7/examples/findall-coffee-icons.mdx +1 -1
- package/docs/v7/examples/focus-window.mdx +1 -1
- package/docs/v7/examples/hover-image.mdx +1 -1
- package/docs/v7/examples/hover-text-with-description.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/parse.mdx +1 -1
- package/docs/v7/examples/press-keys.mdx +1 -1
- package/docs/v7/examples/prompt.mdx +1 -1
- package/docs/v7/examples/scroll-keyboard.mdx +3 -3
- package/docs/v7/examples/scroll-until-image.mdx +3 -3
- package/docs/v7/examples/scroll.mdx +2 -2
- package/docs/v7/examples/type.mdx +1 -1
- package/docs/v7/examples/windows-installer.mdx +1 -1
- package/docs/v7/find.mdx +11 -11
- package/docs/v7/hosted.mdx +1 -1
- package/docs/v7/parse.mdx +1 -1
- package/docs/v7/performing-actions.mdx +0 -1
- package/docs/v7/reusable-code.mdx +3 -3
- package/docs/v7/running-tests.mdx +1 -1
- package/docs/v7/screenshot.mdx +5 -5
- package/docs/v7/scroll.mdx +30 -160
- package/lib/github-comment.mjs +11 -5
- package/package.json +1 -1
- package/sdk.d.ts +2 -2
- package/sdk.js +7 -3
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/lib/github-comment.mjs
CHANGED
|
@@ -8,7 +8,13 @@
|
|
|
8
8
|
* - Links to test runs
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Lazily load Octokit to avoid hard failure when @octokit/rest is not installed
|
|
13
|
+
*/
|
|
14
|
+
async function getOctokit(token) {
|
|
15
|
+
const { Octokit } = await import('@octokit/rest');
|
|
16
|
+
return new Octokit({ auth: token });
|
|
17
|
+
}
|
|
12
18
|
|
|
13
19
|
/**
|
|
14
20
|
* Format test duration in human-readable format
|
|
@@ -334,7 +340,7 @@ export async function postGitHubComment(options) {
|
|
|
334
340
|
throw new Error('Either prNumber or commitSha must be provided');
|
|
335
341
|
}
|
|
336
342
|
|
|
337
|
-
const octokit =
|
|
343
|
+
const octokit = await getOctokit(token);
|
|
338
344
|
|
|
339
345
|
if (prNumber) {
|
|
340
346
|
// Comment on PR
|
|
@@ -374,7 +380,7 @@ export async function updateGitHubComment(options) {
|
|
|
374
380
|
throw new Error('Token, owner, repo, and commentId are required');
|
|
375
381
|
}
|
|
376
382
|
|
|
377
|
-
const octokit =
|
|
383
|
+
const octokit = await getOctokit(token);
|
|
378
384
|
|
|
379
385
|
const response = await octokit.rest.issues.updateComment({
|
|
380
386
|
owner,
|
|
@@ -402,7 +408,7 @@ export async function findExistingComment(options) {
|
|
|
402
408
|
return null;
|
|
403
409
|
}
|
|
404
410
|
|
|
405
|
-
const octokit =
|
|
411
|
+
const octokit = await getOctokit(token);
|
|
406
412
|
|
|
407
413
|
const comments = await octokit.rest.issues.listComments({
|
|
408
414
|
owner,
|
|
@@ -435,7 +441,7 @@ export async function postOrUpdateTestResults(testRunData, testCases, githubOpti
|
|
|
435
441
|
|
|
436
442
|
if (existingComment) {
|
|
437
443
|
// Delete the old comment
|
|
438
|
-
const octokit =
|
|
444
|
+
const octokit = await getOctokit(githubOptions.token);
|
|
439
445
|
await octokit.rest.issues.deleteComment({
|
|
440
446
|
owner: githubOptions.owner,
|
|
441
447
|
repo: githubOptions.repo,
|
package/package.json
CHANGED
package/sdk.d.ts
CHANGED
|
@@ -282,7 +282,7 @@ export interface TestDriverOptions {
|
|
|
282
282
|
/** Enable/disable Dashcam video recording (default: true) */
|
|
283
283
|
dashcam?: boolean;
|
|
284
284
|
/**
|
|
285
|
-
* Enable automatic screenshots before and after each command (default:
|
|
285
|
+
* Enable automatic screenshots before and after each command (default: false)
|
|
286
286
|
* Screenshots are saved to .testdriver/screenshots/<test>/ with descriptive filenames
|
|
287
287
|
* Format: <seq>-<action>-<phase>-L<line>-<description>.png
|
|
288
288
|
* Example: 001-click-before-L42-submit-button.png
|
|
@@ -1086,7 +1086,7 @@ export default class TestDriverSDK {
|
|
|
1086
1086
|
find(description: string, cacheThreshold?: number): ChainableElementPromise;
|
|
1087
1087
|
find(
|
|
1088
1088
|
description: string,
|
|
1089
|
-
options?: { cacheThreshold?: number; cacheKey?: string; timeout?: number; confidence?: number; type?: "text" | "image" | "ui" | "any"; ai?: AIConfig; cache?: { thresholds?: { screen?: number; element?: number } } },
|
|
1089
|
+
options?: { cacheThreshold?: number; cacheKey?: string; timeout?: number; confidence?: number; type?: "text" | "image" | "ui" | "any"; zoom?: boolean | number; verify?: boolean; ai?: AIConfig; cache?: { thresholds?: { screen?: number; element?: number } } },
|
|
1090
1090
|
): ChainableElementPromise;
|
|
1091
1091
|
|
|
1092
1092
|
/**
|
package/sdk.js
CHANGED
|
@@ -481,7 +481,8 @@ class Element {
|
|
|
481
481
|
let cacheKey = null;
|
|
482
482
|
let cacheThreshold = null;
|
|
483
483
|
let perCommandThresholds = null; // Per-command { screen, element } override
|
|
484
|
-
let zoom =
|
|
484
|
+
let zoom = false; // Default to disabled
|
|
485
|
+
let verify = false; // Default to disabled (skip AI verification)
|
|
485
486
|
let perCommandAi = null; // Per-command AI config override
|
|
486
487
|
|
|
487
488
|
let minConfidence = null; // Minimum confidence threshold
|
|
@@ -494,8 +495,10 @@ class Element {
|
|
|
494
495
|
// New: options is an object with cacheKey and/or cacheThreshold
|
|
495
496
|
cacheKey = options.cacheKey || null;
|
|
496
497
|
cacheThreshold = options.cacheThreshold ?? null;
|
|
497
|
-
// zoom defaults to
|
|
498
|
-
zoom = options.zoom
|
|
498
|
+
// zoom defaults to false unless explicitly set to true
|
|
499
|
+
zoom = options.zoom === true;
|
|
500
|
+
// verify defaults to false unless explicitly set to true
|
|
501
|
+
verify = options.verify === true;
|
|
499
502
|
// Minimum confidence threshold: fail find if AI confidence is below this value
|
|
500
503
|
minConfidence = options.confidence ?? null;
|
|
501
504
|
// Element type hint for prompt wrapping
|
|
@@ -569,6 +572,7 @@ class Element {
|
|
|
569
572
|
os: this.sdk.os,
|
|
570
573
|
resolution: this.sdk.resolution,
|
|
571
574
|
zoom: zoom === true ? 1 : zoom === false ? 0 : zoom,
|
|
575
|
+
skipVerify: !verify,
|
|
572
576
|
confidence: minConfidence,
|
|
573
577
|
type: elementType,
|
|
574
578
|
ai: {
|