testdriverai 6.2.1 → 7.0.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/workflows/acceptance-linux.yml +75 -0
- package/.github/workflows/acceptance-sdk-tests.yml +133 -0
- package/.vscode/settings.json +5 -1
- package/MIGRATION.md +389 -0
- package/PLUGIN_MIGRATION.md +222 -0
- package/PROMPT_CACHE.md +200 -0
- package/SDK_LOGGING.md +222 -0
- package/SDK_MIGRATION.md +474 -0
- package/SDK_README.md +1122 -0
- package/{testdriver → _testdriver}/acceptance/drag-and-drop.yaml +2 -2
- package/{testdriver → _testdriver}/acceptance/snippets/login.yaml +1 -1
- package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
- package/{testdriver → _testdriver}/examples/web/lifecycle/prerun.yaml +6 -1
- package/{testdriver → _testdriver}/lifecycle/postrun.yaml +3 -2
- package/_testdriver/lifecycle/prerun.yaml +15 -0
- package/{testdriver → _testdriver}/lifecycle/provision.yaml +7 -2
- package/agent/index.js +258 -68
- package/agent/interface.js +15 -0
- package/agent/lib/cache.js +142 -0
- package/agent/lib/commander.js +1 -39
- package/agent/lib/commands.js +143 -188
- package/agent/lib/redraw.js +6 -3
- package/agent/lib/sandbox.js +19 -5
- package/agent/lib/sdk.js +1 -0
- package/agent/lib/system.js +0 -3
- package/agent/lib/validation.js +1 -7
- package/debug-locate-response.js +82 -0
- package/debug-screenshot-1763401388589.png +0 -0
- package/debugger/index.html +16 -5
- package/docs/ARCHITECTURE.md +424 -0
- package/docs/AWESOME_LOGS_QUICK_REF.md +100 -0
- package/docs/QUICK_START_TEST_RECORDING.md +215 -0
- package/docs/SDK_AWESOME_LOGS.md +468 -0
- package/docs/TEST_RECORDING.md +388 -0
- package/docs/docs.json +232 -152
- package/docs/sdk-browser-rendering.md +167 -0
- package/docs/v6/getting-started/self-hosting.mdx +407 -0
- package/docs/{guide → v6/guide}/dashcam.mdx +1 -1
- package/docs/{guide → v6/guide}/environment-variables.mdx +4 -5
- package/docs/{guide → v6/guide}/lifecycle.mdx +1 -1
- package/docs/v6/overview/comparison.mdx +101 -0
- package/docs/v7/README.md +135 -0
- package/docs/v7/api/ai.mdx +205 -0
- package/docs/v7/api/assert.mdx +285 -0
- package/docs/v7/api/assertions.mdx +403 -0
- package/docs/v7/api/click.mdx +287 -0
- package/docs/v7/api/client.mdx +322 -0
- package/docs/v7/api/elements.mdx +479 -0
- package/docs/v7/api/exec.mdx +346 -0
- package/docs/v7/api/find.mdx +316 -0
- package/docs/v7/api/focusApplication.mdx +294 -0
- package/docs/v7/api/hover.mdx +279 -0
- package/docs/v7/api/pressKeys.mdx +349 -0
- package/docs/v7/api/sandbox.mdx +404 -0
- package/docs/v7/api/scroll.mdx +300 -0
- package/docs/v7/api/type.mdx +314 -0
- package/docs/v7/commands/assert.mdx +45 -0
- package/docs/v7/commands/exec.mdx +282 -0
- package/docs/v7/commands/focus-application.mdx +44 -0
- package/docs/v7/commands/hover-image.mdx +69 -0
- package/docs/v7/commands/hover-text.mdx +47 -0
- package/docs/v7/commands/if.mdx +53 -0
- package/docs/v7/commands/match-image.mdx +67 -0
- package/docs/v7/commands/press-keys.mdx +87 -0
- package/docs/v7/commands/remember.mdx +49 -0
- package/docs/v7/commands/run.mdx +44 -0
- package/docs/v7/commands/scroll-until-image.mdx +66 -0
- package/docs/v7/commands/scroll-until-text.mdx +60 -0
- package/docs/v7/commands/scroll.mdx +69 -0
- package/docs/v7/commands/type.mdx +45 -0
- package/docs/v7/commands/wait-for-image.mdx +54 -0
- package/docs/v7/commands/wait-for-text.mdx +48 -0
- package/docs/v7/commands/wait.mdx +45 -0
- package/docs/v7/getting-started/quickstart.mdx +199 -0
- package/docs/v7/guides/migration.mdx +562 -0
- package/docs/{getting-started → v7/guides}/self-hosting.mdx +11 -12
- package/docs/v7/playwright.mdx +342 -0
- package/eslint.config.js +19 -1
- package/examples/run-tests-with-recording.sh +70 -0
- package/examples/screenshot-example.js +63 -0
- package/examples/sdk-awesome-logs-demo.js +177 -0
- package/examples/sdk-cache-thresholds.js +96 -0
- package/examples/sdk-element-properties.js +155 -0
- package/examples/sdk-simple-example.js +65 -0
- package/examples/test-recording-example.test.js +166 -0
- package/interfaces/cli/lib/base.js +10 -4
- package/interfaces/logger.js +2 -1
- package/interfaces/shared-test-state.mjs +69 -0
- package/interfaces/vitest-plugin.mjs +744 -0
- package/mcp-server/AI_GUIDELINES.md +57 -0
- package/package.json +18 -5
- package/schema.json +8 -29
- package/scripts/view-test-results.mjs +96 -0
- package/sdk-log-formatter.js +714 -0
- package/sdk.d.ts +735 -0
- package/sdk.js +1906 -0
- package/{.github/workflows/self-hosted.yml → self-hosted.yml} +13 -4
- package/setup/aws/cloudformation.yaml +9 -2
- package/test/mcp-example-test.yaml +27 -0
- package/test-find-api.js +73 -0
- package/test-prompt-cache.js +96 -0
- package/test-sandbox-render.js +28 -0
- package/test-sdk-methods.js +15 -0
- package/test-sdk-refactor.js +53 -0
- package/test-stack-trace.mjs +57 -0
- package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +61 -0
- package/testdriver/acceptance-sdk/README.md +128 -0
- package/testdriver/acceptance-sdk/TEST_REPORTING.md +245 -0
- package/testdriver/acceptance-sdk/assert.test.mjs +44 -0
- package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +70 -0
- package/testdriver/acceptance-sdk/element-not-found.test.mjs +38 -0
- package/testdriver/acceptance-sdk/exec-js.test.mjs +55 -0
- package/testdriver/acceptance-sdk/exec-output.test.mjs +71 -0
- package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +69 -0
- package/testdriver/acceptance-sdk/focus-window.test.mjs +48 -0
- package/testdriver/acceptance-sdk/formatted-logging.test.mjs +41 -0
- package/testdriver/acceptance-sdk/hover-image.test.mjs +43 -0
- package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +50 -0
- package/testdriver/acceptance-sdk/hover-text.test.mjs +41 -0
- package/testdriver/acceptance-sdk/match-image.test.mjs +48 -0
- package/testdriver/acceptance-sdk/press-keys.test.mjs +64 -0
- package/testdriver/acceptance-sdk/prompt.test.mjs +45 -0
- package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +52 -0
- package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +51 -0
- package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +42 -0
- package/testdriver/acceptance-sdk/scroll.test.mjs +50 -0
- package/testdriver/acceptance-sdk/setup/globalTeardown.mjs +11 -0
- package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +239 -0
- package/testdriver/acceptance-sdk/setup/testHelpers.mjs +648 -0
- package/testdriver/acceptance-sdk/setup/vitestSetup.mjs +40 -0
- package/testdriver/acceptance-sdk/type-checking-demo.js +49 -0
- package/testdriver/acceptance-sdk/type.test.mjs +84 -0
- package/verify-element-api.js +89 -0
- package/verify-types.js +0 -0
- package/vitest.config.example.js +19 -0
- package/vitest.config.mjs +65 -0
- package/vitest.config.mjs.bak +44 -0
- package/.github/workflows/acceptance-v6.yml +0 -169
- package/docs/overview/comparison.mdx +0 -82
- package/testdriver/lifecycle/prerun.yaml +0 -17
- /package/{testdriver/examples/desktop/lifecycle/prerun.yaml → .env.example} +0 -0
- /package/{testdriver → _testdriver}/acceptance/assert.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/dashcam.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/embed.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/exec-js.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/exec-output.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/exec-shell.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/focus-window.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/hover-image.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/hover-text-with-description.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/hover-text.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/if-else.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/match-image.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/press-keys.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/prompt.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/remember.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/screenshots/cart.png +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll-keyboard.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll-until-image.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll-until-text.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/scroll.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/snippets/match-cart.yaml +0 -0
- /package/{testdriver → _testdriver}/acceptance/type.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/failure.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/hover-text.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/lifecycle/postrun.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/lifecycle/prerun.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/behavior/secrets.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/dashcam-chrome.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/exec-pwsh-multiline.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/js-exception.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/js-promise.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/lifecycle/postrun.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/prompt-in-middle.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/prompt-nested.yaml +0 -0
- /package/{testdriver → _testdriver}/edge-cases/success-test.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/example.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/lifecycle/postrun.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/android/readme.md +0 -0
- /package/{testdriver → _testdriver}/examples/chrome-extension/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/desktop/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/vscode-extension/lifecycle/provision.yaml +0 -0
- /package/{testdriver → _testdriver}/examples/web/lifecycle/postrun.yaml +0 -0
- /package/docs/{account → v6/account}/dashboard.mdx +0 -0
- /package/docs/{account → v6/account}/enterprise.mdx +0 -0
- /package/docs/{account → v6/account}/pricing.mdx +0 -0
- /package/docs/{account → v6/account}/projects.mdx +0 -0
- /package/docs/{account → v6/account}/team.mdx +0 -0
- /package/docs/{action → v6/action}/ami.mdx +0 -0
- /package/docs/{action → v6/action}/performance.mdx +0 -0
- /package/docs/{action → v6/action}/secrets.mdx +0 -0
- /package/docs/{apps → v6/apps}/chrome-extensions.mdx +0 -0
- /package/docs/{apps → v6/apps}/desktop-apps.mdx +0 -0
- /package/docs/{apps → v6/apps}/mobile-apps.mdx +0 -0
- /package/docs/{apps → v6/apps}/static-websites.mdx +0 -0
- /package/docs/{apps → v6/apps}/tauri-apps.mdx +0 -0
- /package/docs/{bugs → v6/bugs}/jira.mdx +0 -0
- /package/docs/{cli → v6/cli}/overview.mdx +0 -0
- /package/docs/{commands → v6/commands}/assert.mdx +0 -0
- /package/docs/{commands → v6/commands}/exec.mdx +0 -0
- /package/docs/{commands → v6/commands}/focus-application.mdx +0 -0
- /package/docs/{commands → v6/commands}/hover-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/hover-text.mdx +0 -0
- /package/docs/{commands → v6/commands}/if.mdx +0 -0
- /package/docs/{commands → v6/commands}/match-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/press-keys.mdx +0 -0
- /package/docs/{commands → v6/commands}/remember.mdx +0 -0
- /package/docs/{commands → v6/commands}/run.mdx +0 -0
- /package/docs/{commands → v6/commands}/scroll-until-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/scroll-until-text.mdx +0 -0
- /package/docs/{commands → v6/commands}/scroll.mdx +0 -0
- /package/docs/{commands → v6/commands}/type.mdx +0 -0
- /package/docs/{commands → v6/commands}/wait-for-image.mdx +0 -0
- /package/docs/{commands → v6/commands}/wait-for-text.mdx +0 -0
- /package/docs/{commands → v6/commands}/wait.mdx +0 -0
- /package/docs/{exporting → v6/exporting}/junit.mdx +0 -0
- /package/docs/{exporting → v6/exporting}/playwright.mdx +0 -0
- /package/docs/{features → v6/features}/auto-healing.mdx +0 -0
- /package/docs/{features → v6/features}/generation.mdx +0 -0
- /package/docs/{features → v6/features}/parallel-testing.mdx +0 -0
- /package/docs/{features → v6/features}/reusable-snippets.mdx +0 -0
- /package/docs/{features → v6/features}/selectorless.mdx +0 -0
- /package/docs/{features → v6/features}/visual-assertions.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/ci.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/cli.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/editing.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/playwright.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/running.mdx +0 -0
- /package/docs/{getting-started → v6/getting-started}/vscode.mdx +0 -0
- /package/docs/{guide → v6/guide}/assertions.mdx +0 -0
- /package/docs/{guide → v6/guide}/authentication.mdx +0 -0
- /package/docs/{guide → v6/guide}/code.mdx +0 -0
- /package/docs/{guide → v6/guide}/locating.mdx +0 -0
- /package/docs/{guide → v6/guide}/protips.mdx +0 -0
- /package/docs/{guide → v6/guide}/variables.mdx +0 -0
- /package/docs/{guide → v6/guide}/waiting.mdx +0 -0
- /package/docs/{importing → v6/importing}/csv.mdx +0 -0
- /package/docs/{importing → v6/importing}/gherkin.mdx +0 -0
- /package/docs/{importing → v6/importing}/jira.mdx +0 -0
- /package/docs/{importing → v6/importing}/testrail.mdx +0 -0
- /package/docs/{integrations → v6/integrations}/electron.mdx +0 -0
- /package/docs/{integrations → v6/integrations}/netlify.mdx +0 -0
- /package/docs/{integrations → v6/integrations}/vercel.mdx +0 -0
- /package/docs/{interactive → v6/interactive}/explore.mdx +0 -0
- /package/docs/{interactive → v6/interactive}/run.mdx +0 -0
- /package/docs/{interactive → v6/interactive}/save.mdx +0 -0
- /package/docs/{overview → v6/overview}/faq.mdx +0 -0
- /package/docs/{overview → v6/overview}/performance.mdx +0 -0
- /package/docs/{overview → v6/overview}/quickstart.mdx +0 -0
- /package/docs/{overview → v6/overview}/what-is-testdriver.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/ai-chatbot.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/cookie-banner.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/file-upload.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/form-filling.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/log-in.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/pdf-generation.mdx +0 -0
- /package/docs/{scenarios → v6/scenarios}/spell-check.mdx +0 -0
- /package/docs/{security → v6/security}/action.mdx +0 -0
- /package/docs/{security → v6/security}/agent.mdx +0 -0
- /package/docs/{security → v6/security}/platform.mdx +0 -0
- /package/docs/{tutorials → v6/tutorials}/advanced-test.mdx +0 -0
- /package/docs/{tutorials → v6/tutorials}/basic-test.mdx +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Sandbox Management"
|
|
3
|
+
sidebarTitle: "Sandbox"
|
|
4
|
+
description: "Execute scripts and manage the sandbox environment"
|
|
5
|
+
icon: "server"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
The sandbox is a virtual machine where your tests run. You can execute shell commands, JavaScript, and manage applications within the sandbox environment.
|
|
11
|
+
|
|
12
|
+
## Code Execution
|
|
13
|
+
|
|
14
|
+
### exec()
|
|
15
|
+
|
|
16
|
+
Execute code or shell commands in the sandbox.
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
await testdriver.exec(language, code, timeout, silent)
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Parameters:**
|
|
23
|
+
- `language` (string) - Language to execute: `'js'` (JavaScript) or `'pwsh'` (PowerShell)
|
|
24
|
+
- `code` (string) - Code or command to execute
|
|
25
|
+
- `timeout` (number) - Timeout in milliseconds
|
|
26
|
+
- `silent` (boolean, optional) - Suppress output if `true`
|
|
27
|
+
|
|
28
|
+
**Returns:** `Promise<string>` - Command output
|
|
29
|
+
|
|
30
|
+
### JavaScript Execution
|
|
31
|
+
|
|
32
|
+
Execute JavaScript code in the browser context (Windows sandbox only).
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
// Execute JavaScript in the browser
|
|
36
|
+
const result = await testdriver.exec('js', 'document.title', 5000);
|
|
37
|
+
console.log('Page title:', result);
|
|
38
|
+
|
|
39
|
+
// Manipulate the DOM
|
|
40
|
+
await testdriver.exec('js', `
|
|
41
|
+
document.querySelector('#username').value = 'testuser';
|
|
42
|
+
`, 5000);
|
|
43
|
+
|
|
44
|
+
// Return data from the page
|
|
45
|
+
const elementText = await testdriver.exec('js', `
|
|
46
|
+
document.querySelector('.message').textContent
|
|
47
|
+
`, 5000);
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### PowerShell Execution
|
|
51
|
+
|
|
52
|
+
Execute PowerShell commands in the Windows sandbox.
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
// Run a simple command
|
|
56
|
+
const output = await testdriver.exec('pwsh', 'Get-Process chrome', 5000);
|
|
57
|
+
console.log('Chrome processes:', output);
|
|
58
|
+
|
|
59
|
+
// Install software
|
|
60
|
+
await testdriver.exec('pwsh', 'npm install -g dashcam@beta', 10000);
|
|
61
|
+
|
|
62
|
+
// Start an application
|
|
63
|
+
await testdriver.exec('pwsh', `
|
|
64
|
+
Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "https://example.com"
|
|
65
|
+
`, 10000);
|
|
66
|
+
|
|
67
|
+
// File operations
|
|
68
|
+
await testdriver.exec('pwsh', 'New-Item -Path "C:\\test.txt" -ItemType File', 5000);
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Silent Execution
|
|
72
|
+
|
|
73
|
+
Use the `silent` parameter to suppress output for background operations:
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
// Silent installation
|
|
77
|
+
await testdriver.exec('pwsh', 'npm install -g some-package', 10000, true);
|
|
78
|
+
|
|
79
|
+
// Start background process
|
|
80
|
+
await testdriver.exec('pwsh', 'Start-Process notepad', 5000, true);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Application Management
|
|
84
|
+
|
|
85
|
+
### focusApplication()
|
|
86
|
+
|
|
87
|
+
Bring an application window to the foreground.
|
|
88
|
+
|
|
89
|
+
```javascript
|
|
90
|
+
await testdriver.focusApplication(name)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Parameters:**
|
|
94
|
+
- `name` (string) - Application name (e.g., `'Google Chrome'`, `'Microsoft Edge'`, `'Notepad'`)
|
|
95
|
+
|
|
96
|
+
**Returns:** `Promise<string>` - Result message
|
|
97
|
+
|
|
98
|
+
**Example:**
|
|
99
|
+
```javascript
|
|
100
|
+
// Focus Chrome browser
|
|
101
|
+
await testdriver.focusApplication('Google Chrome');
|
|
102
|
+
|
|
103
|
+
// Focus Edge
|
|
104
|
+
await testdriver.focusApplication('Microsoft Edge');
|
|
105
|
+
|
|
106
|
+
// Focus Notepad
|
|
107
|
+
await testdriver.focusApplication('Notepad');
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
<Tip>
|
|
111
|
+
Call `focusApplication()` before interacting with UI elements to ensure the correct window is active.
|
|
112
|
+
</Tip>
|
|
113
|
+
|
|
114
|
+
## Common Sandbox Operations
|
|
115
|
+
|
|
116
|
+
### Installing Software
|
|
117
|
+
|
|
118
|
+
```javascript
|
|
119
|
+
// Install npm package globally
|
|
120
|
+
await testdriver.exec('pwsh', 'npm install -g package-name', 30000);
|
|
121
|
+
|
|
122
|
+
// Install via Chocolatey (if available)
|
|
123
|
+
await testdriver.exec('pwsh', 'choco install firefox -y', 60000);
|
|
124
|
+
|
|
125
|
+
// Download and install
|
|
126
|
+
await testdriver.exec('pwsh', `
|
|
127
|
+
Invoke-WebRequest -Uri "https://example.com/installer.exe" -OutFile "C:\\installer.exe"
|
|
128
|
+
Start-Process -FilePath "C:\\installer.exe" -ArgumentList "/S" -Wait
|
|
129
|
+
`, 120000);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### File Operations
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
// Create a file
|
|
136
|
+
await testdriver.exec('pwsh', `
|
|
137
|
+
Set-Content -Path "C:\\test.txt" -Value "Hello World"
|
|
138
|
+
`, 5000);
|
|
139
|
+
|
|
140
|
+
// Read a file
|
|
141
|
+
const content = await testdriver.exec('pwsh', 'Get-Content -Path "C:\\test.txt"', 5000);
|
|
142
|
+
|
|
143
|
+
// Copy files
|
|
144
|
+
await testdriver.exec('pwsh', 'Copy-Item -Path "C:\\source.txt" -Destination "C:\\dest.txt"', 5000);
|
|
145
|
+
|
|
146
|
+
// Delete files
|
|
147
|
+
await testdriver.exec('pwsh', 'Remove-Item -Path "C:\\test.txt"', 5000);
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Environment Variables
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
// Set environment variable
|
|
154
|
+
await testdriver.exec('pwsh', '$env:MY_VAR = "value"', 5000);
|
|
155
|
+
|
|
156
|
+
// Get environment variable
|
|
157
|
+
const value = await testdriver.exec('pwsh', '$env:MY_VAR', 5000);
|
|
158
|
+
|
|
159
|
+
// Set persistent environment variable
|
|
160
|
+
await testdriver.exec('pwsh', '[Environment]::SetEnvironmentVariable("MY_VAR", "value", "User")', 5000);
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Network Operations
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
// Test connectivity
|
|
167
|
+
const pingResult = await testdriver.exec('pwsh', 'Test-NetConnection google.com', 10000);
|
|
168
|
+
|
|
169
|
+
// Download file
|
|
170
|
+
await testdriver.exec('pwsh', `
|
|
171
|
+
Invoke-WebRequest -Uri "https://example.com/file.zip" -OutFile "C:\\Downloads\\file.zip"
|
|
172
|
+
`, 30000);
|
|
173
|
+
|
|
174
|
+
// Check if port is open
|
|
175
|
+
const portCheck = await testdriver.exec('pwsh', 'Test-NetConnection -ComputerName localhost -Port 3000', 5000);
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Process Management
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
// List running processes
|
|
182
|
+
const processes = await testdriver.exec('pwsh', 'Get-Process', 5000);
|
|
183
|
+
|
|
184
|
+
// Kill a process
|
|
185
|
+
await testdriver.exec('pwsh', 'Stop-Process -Name "chrome" -Force', 5000);
|
|
186
|
+
|
|
187
|
+
// Start a process and wait for it
|
|
188
|
+
await testdriver.exec('pwsh', 'Start-Process notepad -Wait', 30000);
|
|
189
|
+
|
|
190
|
+
// Start process with arguments
|
|
191
|
+
await testdriver.exec('pwsh', `
|
|
192
|
+
Start-Process "chrome.exe" -ArgumentList "--incognito", "https://example.com"
|
|
193
|
+
`, 5000);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Browser Automation with JavaScript
|
|
197
|
+
|
|
198
|
+
### DOM Manipulation
|
|
199
|
+
|
|
200
|
+
```javascript
|
|
201
|
+
// Click an element
|
|
202
|
+
await testdriver.exec('js', `
|
|
203
|
+
document.querySelector('#submit-button').click();
|
|
204
|
+
`, 5000);
|
|
205
|
+
|
|
206
|
+
// Fill a form
|
|
207
|
+
await testdriver.exec('js', `
|
|
208
|
+
document.querySelector('#username').value = 'user@example.com';
|
|
209
|
+
document.querySelector('#password').value = 'secret';
|
|
210
|
+
document.querySelector('#login-form').submit();
|
|
211
|
+
`, 5000);
|
|
212
|
+
|
|
213
|
+
// Scroll to element
|
|
214
|
+
await testdriver.exec('js', `
|
|
215
|
+
document.querySelector('#footer').scrollIntoView();
|
|
216
|
+
`, 5000);
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Reading Page Data
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
// Get page title
|
|
223
|
+
const title = await testdriver.exec('js', 'document.title', 5000);
|
|
224
|
+
|
|
225
|
+
// Get all links
|
|
226
|
+
const links = await testdriver.exec('js', `
|
|
227
|
+
Array.from(document.querySelectorAll('a')).map(a => a.href).join('\\n')
|
|
228
|
+
`, 5000);
|
|
229
|
+
|
|
230
|
+
// Check if element exists
|
|
231
|
+
const exists = await testdriver.exec('js', `
|
|
232
|
+
document.querySelector('.error-message') !== null
|
|
233
|
+
`, 5000);
|
|
234
|
+
|
|
235
|
+
// Get element text
|
|
236
|
+
const text = await testdriver.exec('js', `
|
|
237
|
+
document.querySelector('.notification').textContent
|
|
238
|
+
`, 5000);
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Waiting for Conditions
|
|
242
|
+
|
|
243
|
+
```javascript
|
|
244
|
+
// Wait for element to appear (using polling)
|
|
245
|
+
await testdriver.exec('js', `
|
|
246
|
+
await new Promise((resolve) => {
|
|
247
|
+
const interval = setInterval(() => {
|
|
248
|
+
if (document.querySelector('.loaded')) {
|
|
249
|
+
clearInterval(interval);
|
|
250
|
+
resolve();
|
|
251
|
+
}
|
|
252
|
+
}, 100);
|
|
253
|
+
});
|
|
254
|
+
`, 30000);
|
|
255
|
+
|
|
256
|
+
// Wait for page load
|
|
257
|
+
await testdriver.exec('js', `
|
|
258
|
+
if (document.readyState !== 'complete') {
|
|
259
|
+
await new Promise(resolve => window.addEventListener('load', resolve));
|
|
260
|
+
}
|
|
261
|
+
`, 10000);
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
## Complete Example
|
|
265
|
+
|
|
266
|
+
```javascript
|
|
267
|
+
import { beforeAll, afterAll, describe, it } from 'vitest';
|
|
268
|
+
import TestDriver from 'testdriverai';
|
|
269
|
+
|
|
270
|
+
describe('Sandbox Operations', () => {
|
|
271
|
+
let testdriver;
|
|
272
|
+
|
|
273
|
+
beforeAll(async () => {
|
|
274
|
+
client = new TestDriver(process.env.TD_API_KEY, {
|
|
275
|
+
os: 'windows',
|
|
276
|
+
resolution: '1366x768'
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
await testdriver.auth();
|
|
280
|
+
await testdriver.connect({ newSandbox: true });
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
afterAll(async () => {
|
|
284
|
+
await testdriver.disconnect();
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
it('should install and use a tool', async () => {
|
|
288
|
+
// Install a tool
|
|
289
|
+
await testdriver.exec('pwsh', 'npm install -g http-server', 30000, true);
|
|
290
|
+
|
|
291
|
+
// Create a simple HTML file
|
|
292
|
+
await testdriver.exec('pwsh', `
|
|
293
|
+
Set-Content -Path "C:\\index.html" -Value "<h1>Hello World</h1>"
|
|
294
|
+
`, 5000);
|
|
295
|
+
|
|
296
|
+
// Start HTTP server (background process)
|
|
297
|
+
await testdriver.exec('pwsh', `
|
|
298
|
+
Start-Process pwsh -ArgumentList "-Command", "http-server C:\\ -p 8080"
|
|
299
|
+
`, 5000, true);
|
|
300
|
+
|
|
301
|
+
// Wait for server to start
|
|
302
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
303
|
+
|
|
304
|
+
// Launch browser to view the page
|
|
305
|
+
await testdriver.exec('pwsh', `
|
|
306
|
+
Start-Process chrome -ArgumentList "http://localhost:8080"
|
|
307
|
+
`, 5000);
|
|
308
|
+
|
|
309
|
+
// Focus the browser
|
|
310
|
+
await testdriver.focusApplication('Google Chrome');
|
|
311
|
+
|
|
312
|
+
// Verify the page loaded
|
|
313
|
+
const pageText = await testdriver.exec('js', 'document.body.textContent', 5000);
|
|
314
|
+
expect(pageText).toContain('Hello World');
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
it('should manage files and processes', async () => {
|
|
318
|
+
// Create test file
|
|
319
|
+
await testdriver.exec('pwsh', `
|
|
320
|
+
"Test content" | Out-File -FilePath "C:\\test.txt"
|
|
321
|
+
`, 5000);
|
|
322
|
+
|
|
323
|
+
// Open file in notepad
|
|
324
|
+
await testdriver.exec('pwsh', 'Start-Process notepad C:\\test.txt', 5000);
|
|
325
|
+
|
|
326
|
+
// Focus notepad
|
|
327
|
+
await testdriver.focusApplication('Notepad');
|
|
328
|
+
|
|
329
|
+
// Wait a moment
|
|
330
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
331
|
+
|
|
332
|
+
// Close notepad
|
|
333
|
+
await testdriver.exec('pwsh', 'Stop-Process -Name notepad -Force', 5000);
|
|
334
|
+
});
|
|
335
|
+
});
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Best Practices
|
|
339
|
+
|
|
340
|
+
<AccordionGroup>
|
|
341
|
+
<Accordion title="Use appropriate timeouts">
|
|
342
|
+
Set realistic timeouts based on the operation:
|
|
343
|
+
|
|
344
|
+
```javascript
|
|
345
|
+
// Quick operations: 5000ms
|
|
346
|
+
await testdriver.exec('js', 'document.title', 5000);
|
|
347
|
+
|
|
348
|
+
// Installations: 30000-60000ms
|
|
349
|
+
await testdriver.exec('pwsh', 'npm install -g package', 30000);
|
|
350
|
+
|
|
351
|
+
// Downloads or complex operations: 60000-120000ms
|
|
352
|
+
await testdriver.exec('pwsh', 'Install-Module Something', 120000);
|
|
353
|
+
```
|
|
354
|
+
</Accordion>
|
|
355
|
+
|
|
356
|
+
<Accordion title="Handle errors gracefully">
|
|
357
|
+
Wrap exec calls in try-catch for better error handling:
|
|
358
|
+
|
|
359
|
+
```javascript
|
|
360
|
+
try {
|
|
361
|
+
await testdriver.exec('pwsh', 'Some-Command', 5000);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
console.error('Command failed:', error.message);
|
|
364
|
+
// Fall back or retry
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
</Accordion>
|
|
368
|
+
|
|
369
|
+
<Accordion title="Use silent mode for background operations">
|
|
370
|
+
Suppress output for installation and background tasks:
|
|
371
|
+
|
|
372
|
+
```javascript
|
|
373
|
+
// Silent install
|
|
374
|
+
await testdriver.exec('pwsh', 'npm install -g tool', 30000, true);
|
|
375
|
+
|
|
376
|
+
// Background process
|
|
377
|
+
await testdriver.exec('pwsh', 'Start-Process app', 5000, true);
|
|
378
|
+
```
|
|
379
|
+
</Accordion>
|
|
380
|
+
|
|
381
|
+
<Accordion title="Focus applications before interaction">
|
|
382
|
+
Always focus the target application before UI interactions:
|
|
383
|
+
|
|
384
|
+
```javascript
|
|
385
|
+
await testdriver.focusApplication('Google Chrome');
|
|
386
|
+
const button = await testdriver.find('submit button');
|
|
387
|
+
await button.click();
|
|
388
|
+
```
|
|
389
|
+
</Accordion>
|
|
390
|
+
|
|
391
|
+
<Accordion title="Escape strings properly in PowerShell">
|
|
392
|
+
Use proper escaping for special characters:
|
|
393
|
+
|
|
394
|
+
```javascript
|
|
395
|
+
// Use backticks for newlines in PowerShell strings
|
|
396
|
+
await testdriver.exec('pwsh', `
|
|
397
|
+
Write-Host "Line 1\`nLine 2"
|
|
398
|
+
`, 5000);
|
|
399
|
+
|
|
400
|
+
// Use single quotes to avoid variable expansion
|
|
401
|
+
await testdriver.exec('pwsh', "Write-Host 'Text with $special chars'", 5000);
|
|
402
|
+
```
|
|
403
|
+
</Accordion>
|
|
404
|
+
</AccordionGroup>
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "scroll()"
|
|
3
|
+
sidebarTitle: "scroll"
|
|
4
|
+
description: "Scroll pages and elements"
|
|
5
|
+
icon: "arrows-up-down"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Scroll the page or active element in any direction using mouse wheel or keyboard.
|
|
11
|
+
|
|
12
|
+
## Syntax
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
await testdriver.scroll(direction, amount, method)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Parameters
|
|
19
|
+
|
|
20
|
+
<ParamField path="direction" type="string" default="down">
|
|
21
|
+
Direction to scroll: `'up'`, `'down'`, `'left'`, `'right'`
|
|
22
|
+
</ParamField>
|
|
23
|
+
|
|
24
|
+
<ParamField path="amount" type="number" default="300">
|
|
25
|
+
Amount to scroll in pixels
|
|
26
|
+
</ParamField>
|
|
27
|
+
|
|
28
|
+
<ParamField path="method" type="string" default="mouse">
|
|
29
|
+
Scroll method: `'mouse'` or `'keyboard'`
|
|
30
|
+
</ParamField>
|
|
31
|
+
|
|
32
|
+
## Returns
|
|
33
|
+
|
|
34
|
+
`Promise<void>`
|
|
35
|
+
|
|
36
|
+
## Examples
|
|
37
|
+
|
|
38
|
+
### Basic Scrolling
|
|
39
|
+
|
|
40
|
+
```javascript
|
|
41
|
+
// Scroll down (default)
|
|
42
|
+
await testdriver.scroll();
|
|
43
|
+
|
|
44
|
+
// Scroll down 500 pixels
|
|
45
|
+
await testdriver.scroll('down', 500);
|
|
46
|
+
|
|
47
|
+
// Scroll up
|
|
48
|
+
await testdriver.scroll('up');
|
|
49
|
+
|
|
50
|
+
// Scroll up 200 pixels
|
|
51
|
+
await testdriver.scroll('up', 200);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Horizontal Scrolling
|
|
55
|
+
|
|
56
|
+
```javascript
|
|
57
|
+
// Scroll right
|
|
58
|
+
await testdriver.scroll('right', 300);
|
|
59
|
+
|
|
60
|
+
// Scroll left
|
|
61
|
+
await testdriver.scroll('left', 300);
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Scroll Methods
|
|
65
|
+
|
|
66
|
+
```javascript
|
|
67
|
+
// Mouse wheel scroll (smooth, pixel-precise)
|
|
68
|
+
await testdriver.scroll('down', 300, 'mouse');
|
|
69
|
+
|
|
70
|
+
// Keyboard scroll (uses Page Down/Up, more compatible)
|
|
71
|
+
await testdriver.scroll('down', 300, 'keyboard');
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Scroll Until Found
|
|
75
|
+
|
|
76
|
+
### scrollUntilText()
|
|
77
|
+
|
|
78
|
+
Scroll until specific text appears on screen.
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
await testdriver.scrollUntilText(text, direction, maxDistance, textMatchMethod, method, invert)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
**Parameters:**
|
|
85
|
+
- `text` (string) - Text to find
|
|
86
|
+
- `direction` (string) - Scroll direction (default: `'down'`)
|
|
87
|
+
- `maxDistance` (number) - Max pixels to scroll (default: 10000)
|
|
88
|
+
- `textMatchMethod` (string) - `'turbo'` or `'ai'` (default: `'turbo'`)
|
|
89
|
+
- `method` (string) - `'keyboard'` or `'mouse'` (default: `'keyboard'`)
|
|
90
|
+
- `invert` (boolean) - Scroll until text disappears (default: false)
|
|
91
|
+
|
|
92
|
+
**Examples:**
|
|
93
|
+
```javascript
|
|
94
|
+
// Scroll down until "Contact Us" appears
|
|
95
|
+
await testdriver.scrollUntilText('Contact Us');
|
|
96
|
+
|
|
97
|
+
// Scroll up to find text
|
|
98
|
+
await testdriver.scrollUntilText('Header', 'up');
|
|
99
|
+
|
|
100
|
+
// Scroll until text disappears
|
|
101
|
+
await testdriver.scrollUntilText('Loading...', 'down', 5000, 'turbo', 'keyboard', true);
|
|
102
|
+
|
|
103
|
+
// Use AI matching for fuzzy text
|
|
104
|
+
await testdriver.scrollUntilText('footer content', 'down', 10000, 'ai');
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### scrollUntilImage()
|
|
108
|
+
|
|
109
|
+
Scroll until a visual element appears.
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
await testdriver.scrollUntilImage(description, direction, maxDistance, method, path, invert)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Parameters:**
|
|
116
|
+
- `description` (string) - Description of the image/element
|
|
117
|
+
- `direction` (string) - Scroll direction (default: `'down'`)
|
|
118
|
+
- `maxDistance` (number) - Max pixels to scroll (default: 10000)
|
|
119
|
+
- `method` (string) - `'keyboard'` or `'mouse'` (default: `'keyboard'`)
|
|
120
|
+
- `path` (string | null) - Path to image template (optional)
|
|
121
|
+
- `invert` (boolean) - Scroll until image disappears (default: false)
|
|
122
|
+
|
|
123
|
+
**Examples:**
|
|
124
|
+
```javascript
|
|
125
|
+
// Scroll until visual element appears
|
|
126
|
+
await testdriver.scrollUntilImage('red subscribe button');
|
|
127
|
+
|
|
128
|
+
// Scroll using image template
|
|
129
|
+
await testdriver.scrollUntilImage('', 'down', 10000, 'keyboard', './footer-logo.png');
|
|
130
|
+
|
|
131
|
+
// Scroll until image disappears
|
|
132
|
+
await testdriver.scrollUntilImage('loading spinner', 'down', 5000, 'keyboard', null, true);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Best Practices
|
|
136
|
+
|
|
137
|
+
<Check>
|
|
138
|
+
**Choose the right scroll method**
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
// For web pages, mouse scroll is usually smoother
|
|
142
|
+
await testdriver.scroll('down', 300, 'mouse');
|
|
143
|
+
|
|
144
|
+
// For desktop apps or when mouse doesn't work
|
|
145
|
+
await testdriver.scroll('down', 300, 'keyboard');
|
|
146
|
+
```
|
|
147
|
+
</Check>
|
|
148
|
+
|
|
149
|
+
<Check>
|
|
150
|
+
**Use scrollUntil for dynamic content**
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
// Instead of guessing scroll amount
|
|
154
|
+
await testdriver.scrollUntilText('Load More button');
|
|
155
|
+
|
|
156
|
+
const loadMoreBtn = await testdriver.find('Load More button');
|
|
157
|
+
await loadMoreBtn.click();
|
|
158
|
+
```
|
|
159
|
+
</Check>
|
|
160
|
+
|
|
161
|
+
<Check>
|
|
162
|
+
**Set reasonable max distance**
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
// Avoid infinite scrolling
|
|
166
|
+
await testdriver.scrollUntilText('Footer', 'down', 5000); // Max 5000px
|
|
167
|
+
```
|
|
168
|
+
</Check>
|
|
169
|
+
|
|
170
|
+
<Warning>
|
|
171
|
+
**Keyboard scroll uses Page Down/Up**
|
|
172
|
+
|
|
173
|
+
Keyboard scrolling typically moves by one "page" at a time, which may be more than the specified pixel amount. It's more compatible but less precise than mouse scrolling.
|
|
174
|
+
</Warning>
|
|
175
|
+
|
|
176
|
+
## Use Cases
|
|
177
|
+
|
|
178
|
+
<AccordionGroup>
|
|
179
|
+
<Accordion title="Navigate to Footer">
|
|
180
|
+
```javascript
|
|
181
|
+
// Scroll to bottom of page
|
|
182
|
+
await testdriver.scrollUntilText('Contact Us');
|
|
183
|
+
|
|
184
|
+
const contactLink = await testdriver.find('Contact Us link');
|
|
185
|
+
await contactLink.click();
|
|
186
|
+
```
|
|
187
|
+
</Accordion>
|
|
188
|
+
|
|
189
|
+
<Accordion title="Load More Results">
|
|
190
|
+
```javascript
|
|
191
|
+
// Scroll to load more button
|
|
192
|
+
await testdriver.scrollUntilText('Load More');
|
|
193
|
+
|
|
194
|
+
const loadBtn = await testdriver.find('Load More button');
|
|
195
|
+
await loadBtn.click();
|
|
196
|
+
|
|
197
|
+
await new Promise(r => setTimeout(r, 2000));
|
|
198
|
+
```
|
|
199
|
+
</Accordion>
|
|
200
|
+
|
|
201
|
+
<Accordion title="Find Element in Long List">
|
|
202
|
+
```javascript
|
|
203
|
+
// Scroll through list to find item
|
|
204
|
+
await testdriver.scrollUntilText('Product #42');
|
|
205
|
+
|
|
206
|
+
const product = await testdriver.find('Product #42');
|
|
207
|
+
await product.click();
|
|
208
|
+
```
|
|
209
|
+
</Accordion>
|
|
210
|
+
|
|
211
|
+
<Accordion title="Infinite Scroll">
|
|
212
|
+
```javascript
|
|
213
|
+
// Scroll multiple times for infinite scroll
|
|
214
|
+
for (let i = 0; i < 5; i++) {
|
|
215
|
+
await testdriver.scroll('down', 500);
|
|
216
|
+
await new Promise(r => setTimeout(r, 1000)); // Wait for load
|
|
217
|
+
}
|
|
218
|
+
```
|
|
219
|
+
</Accordion>
|
|
220
|
+
|
|
221
|
+
<Accordion title="Horizontal Gallery">
|
|
222
|
+
```javascript
|
|
223
|
+
// Navigate horizontal carousel
|
|
224
|
+
await testdriver.scroll('right', 300);
|
|
225
|
+
await new Promise(r => setTimeout(r, 500));
|
|
226
|
+
|
|
227
|
+
const nextImage = await testdriver.find('next image in carousel');
|
|
228
|
+
await nextImage.click();
|
|
229
|
+
```
|
|
230
|
+
</Accordion>
|
|
231
|
+
</AccordionGroup>
|
|
232
|
+
|
|
233
|
+
## Complete Example
|
|
234
|
+
|
|
235
|
+
```javascript
|
|
236
|
+
import { beforeAll, afterAll, describe, it } from 'vitest';
|
|
237
|
+
import TestDriver from 'testdriverai';
|
|
238
|
+
|
|
239
|
+
describe('Scrolling', () => {
|
|
240
|
+
let testdriver;
|
|
241
|
+
|
|
242
|
+
beforeAll(async () => {
|
|
243
|
+
client = new TestDriver(process.env.TD_API_KEY);
|
|
244
|
+
await testdriver.auth();
|
|
245
|
+
await testdriver.connect({ newSandbox: true });
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
afterAll(async () => {
|
|
249
|
+
await testdriver.disconnect();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should scroll to find elements', async () => {
|
|
253
|
+
await testdriver.focusApplication('Google Chrome');
|
|
254
|
+
|
|
255
|
+
// Scroll to footer
|
|
256
|
+
await testdriver.scrollUntilText('Contact Information');
|
|
257
|
+
|
|
258
|
+
// Click footer link
|
|
259
|
+
const privacyLink = await testdriver.find('Privacy Policy link');
|
|
260
|
+
await privacyLink.click();
|
|
261
|
+
|
|
262
|
+
await testdriver.assert('privacy policy page is displayed');
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
it('should handle infinite scroll', async () => {
|
|
266
|
+
await testdriver.focusApplication('Google Chrome');
|
|
267
|
+
|
|
268
|
+
// Scroll multiple times to load content
|
|
269
|
+
for (let i = 0; i < 3; i++) {
|
|
270
|
+
await testdriver.scroll('down', 500);
|
|
271
|
+
await new Promise(r => setTimeout(r, 1500)); // Wait for load
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Verify content loaded
|
|
275
|
+
await testdriver.assert('more than 10 items are visible');
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('should scroll until loading completes', async () => {
|
|
279
|
+
// Scroll until loading spinner disappears
|
|
280
|
+
await testdriver.scrollUntilImage(
|
|
281
|
+
'loading spinner',
|
|
282
|
+
'down',
|
|
283
|
+
5000,
|
|
284
|
+
'keyboard',
|
|
285
|
+
null,
|
|
286
|
+
true // invert - wait for it to disappear
|
|
287
|
+
);
|
|
288
|
+
|
|
289
|
+
// Now interact with loaded content
|
|
290
|
+
const firstResult = await testdriver.find('first search result');
|
|
291
|
+
await firstResult.click();
|
|
292
|
+
});
|
|
293
|
+
});
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Related Methods
|
|
297
|
+
|
|
298
|
+
- [`find()`](/v7/api/find) - Locate elements after scrolling
|
|
299
|
+
- [`pressKeys()`](/v7/api/pressKeys) - Use Page Down/Up keys
|
|
300
|
+
- [`wait()`](/v7/api/wait) - Wait after scrolling
|