testdriverai 6.2.2 → 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 +15 -4
- 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,346 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "exec()"
|
|
3
|
+
sidebarTitle: "exec"
|
|
4
|
+
description: "Execute code or shell commands in the sandbox"
|
|
5
|
+
icon: "terminal"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Execute JavaScript code in the browser or PowerShell commands in the Windows sandbox environment.
|
|
11
|
+
|
|
12
|
+
## Syntax
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
await testdriver.exec(language, code, timeout, silent)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Parameters
|
|
19
|
+
|
|
20
|
+
<ParamField path="language" type="string" required>
|
|
21
|
+
Language to execute: `'js'` (JavaScript) or `'pwsh'` (PowerShell)
|
|
22
|
+
</ParamField>
|
|
23
|
+
|
|
24
|
+
<ParamField path="code" type="string" required>
|
|
25
|
+
Code or command to execute
|
|
26
|
+
</ParamField>
|
|
27
|
+
|
|
28
|
+
<ParamField path="timeout" type="number" required>
|
|
29
|
+
Timeout in milliseconds
|
|
30
|
+
</ParamField>
|
|
31
|
+
|
|
32
|
+
<ParamField path="silent" type="boolean" default="false">
|
|
33
|
+
Suppress output if `true`
|
|
34
|
+
</ParamField>
|
|
35
|
+
|
|
36
|
+
## Returns
|
|
37
|
+
|
|
38
|
+
`Promise<string>` - Command output
|
|
39
|
+
|
|
40
|
+
## JavaScript Execution
|
|
41
|
+
|
|
42
|
+
Execute JavaScript in the browser context (Windows sandbox only).
|
|
43
|
+
|
|
44
|
+
### DOM Manipulation
|
|
45
|
+
|
|
46
|
+
```javascript
|
|
47
|
+
// Click an element via JavaScript
|
|
48
|
+
await testdriver.exec('js', `
|
|
49
|
+
document.querySelector('#submit-button').click();
|
|
50
|
+
`, 5000);
|
|
51
|
+
|
|
52
|
+
// Fill a form
|
|
53
|
+
await testdriver.exec('js', `
|
|
54
|
+
document.querySelector('#username').value = 'testuser';
|
|
55
|
+
document.querySelector('#password').value = 'password123';
|
|
56
|
+
document.querySelector('#login-form').submit();
|
|
57
|
+
`, 5000);
|
|
58
|
+
|
|
59
|
+
// Scroll to element
|
|
60
|
+
await testdriver.exec('js', `
|
|
61
|
+
document.querySelector('#footer').scrollIntoView();
|
|
62
|
+
`, 5000);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### Reading Page Data
|
|
66
|
+
|
|
67
|
+
```javascript
|
|
68
|
+
// Get page title
|
|
69
|
+
const title = await testdriver.exec('js', 'document.title', 5000);
|
|
70
|
+
console.log('Page title:', title);
|
|
71
|
+
|
|
72
|
+
// Get all links
|
|
73
|
+
const links = await testdriver.exec('js', `
|
|
74
|
+
Array.from(document.querySelectorAll('a'))
|
|
75
|
+
.map(a => a.href)
|
|
76
|
+
.join('\\n')
|
|
77
|
+
`, 5000);
|
|
78
|
+
|
|
79
|
+
// Check if element exists
|
|
80
|
+
const exists = await testdriver.exec('js', `
|
|
81
|
+
document.querySelector('.error-message') !== null
|
|
82
|
+
`, 5000);
|
|
83
|
+
|
|
84
|
+
// Get element text
|
|
85
|
+
const text = await testdriver.exec('js', `
|
|
86
|
+
document.querySelector('.notification').textContent
|
|
87
|
+
`, 5000);
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## PowerShell Execution
|
|
91
|
+
|
|
92
|
+
Execute PowerShell commands in the Windows sandbox.
|
|
93
|
+
|
|
94
|
+
### Software Installation
|
|
95
|
+
|
|
96
|
+
```javascript
|
|
97
|
+
// Install npm package globally
|
|
98
|
+
await testdriver.exec('pwsh', 'npm install -g http-server', 30000);
|
|
99
|
+
|
|
100
|
+
// Install via Chocolatey
|
|
101
|
+
await testdriver.exec('pwsh', 'choco install firefox -y', 60000);
|
|
102
|
+
|
|
103
|
+
// Download and run installer
|
|
104
|
+
await testdriver.exec('pwsh', `
|
|
105
|
+
Invoke-WebRequest -Uri "https://example.com/setup.exe" -OutFile "C:\\setup.exe"
|
|
106
|
+
Start-Process -FilePath "C:\\setup.exe" -ArgumentList "/S" -Wait
|
|
107
|
+
`, 120000);
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### File Operations
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
// Create a file
|
|
114
|
+
await testdriver.exec('pwsh', `
|
|
115
|
+
Set-Content -Path "C:\\test.txt" -Value "Hello World"
|
|
116
|
+
`, 5000);
|
|
117
|
+
|
|
118
|
+
// Read a file
|
|
119
|
+
const content = await testdriver.exec('pwsh',
|
|
120
|
+
'Get-Content -Path "C:\\test.txt"',
|
|
121
|
+
5000
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
// Copy files
|
|
125
|
+
await testdriver.exec('pwsh',
|
|
126
|
+
'Copy-Item -Path "C:\\source.txt" -Destination "C:\\dest.txt"',
|
|
127
|
+
5000
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
// Delete files
|
|
131
|
+
await testdriver.exec('pwsh',
|
|
132
|
+
'Remove-Item -Path "C:\\test.txt"',
|
|
133
|
+
5000
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
// List directory
|
|
137
|
+
const files = await testdriver.exec('pwsh',
|
|
138
|
+
'Get-ChildItem -Path "C:\\Users\\testdriver\\Documents"',
|
|
139
|
+
5000
|
|
140
|
+
);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Process Management
|
|
144
|
+
|
|
145
|
+
```javascript
|
|
146
|
+
// List running processes
|
|
147
|
+
const processes = await testdriver.exec('pwsh', 'Get-Process', 5000);
|
|
148
|
+
|
|
149
|
+
// Start application
|
|
150
|
+
await testdriver.exec('pwsh', `
|
|
151
|
+
Start-Process "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" -ArgumentList "--start-maximized", "https://example.com"
|
|
152
|
+
`, 5000);
|
|
153
|
+
|
|
154
|
+
// Kill a process
|
|
155
|
+
await testdriver.exec('pwsh', 'Stop-Process -Name "chrome" -Force', 5000);
|
|
156
|
+
|
|
157
|
+
// Wait for process
|
|
158
|
+
await testdriver.exec('pwsh', 'Start-Process notepad -Wait', 30000);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Environment Variables
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
// Set environment variable (session)
|
|
165
|
+
await testdriver.exec('pwsh', '$env:MY_VAR = "value"', 5000);
|
|
166
|
+
|
|
167
|
+
// Get environment variable
|
|
168
|
+
const value = await testdriver.exec('pwsh', '$env:MY_VAR', 5000);
|
|
169
|
+
|
|
170
|
+
// Set persistent environment variable
|
|
171
|
+
await testdriver.exec('pwsh', `
|
|
172
|
+
[Environment]::SetEnvironmentVariable("MY_VAR", "value", "User")
|
|
173
|
+
`, 5000);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Network Operations
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
// Test connectivity
|
|
180
|
+
const pingResult = await testdriver.exec('pwsh',
|
|
181
|
+
'Test-NetConnection google.com',
|
|
182
|
+
10000
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
// Download file
|
|
186
|
+
await testdriver.exec('pwsh', `
|
|
187
|
+
Invoke-WebRequest -Uri "https://example.com/file.zip" -OutFile "C:\\Downloads\\file.zip"
|
|
188
|
+
`, 30000);
|
|
189
|
+
|
|
190
|
+
// Check if port is open
|
|
191
|
+
const portOpen = await testdriver.exec('pwsh',
|
|
192
|
+
'Test-NetConnection -ComputerName localhost -Port 3000',
|
|
193
|
+
5000
|
|
194
|
+
);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Silent Execution
|
|
198
|
+
|
|
199
|
+
Suppress output for background operations:
|
|
200
|
+
|
|
201
|
+
```javascript
|
|
202
|
+
// Silent installation
|
|
203
|
+
await testdriver.exec('pwsh', 'npm install -g some-package', 30000, true);
|
|
204
|
+
|
|
205
|
+
// Start background process
|
|
206
|
+
await testdriver.exec('pwsh', 'Start-Process notepad', 5000, true);
|
|
207
|
+
|
|
208
|
+
// Run setup script silently
|
|
209
|
+
await testdriver.exec('pwsh', '.\\setup.ps1', 60000, true);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Best Practices
|
|
213
|
+
|
|
214
|
+
<Check>
|
|
215
|
+
**Use appropriate timeouts**
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
// Quick operations: 5000ms
|
|
219
|
+
await testdriver.exec('js', 'document.title', 5000);
|
|
220
|
+
|
|
221
|
+
// Installations: 30000-60000ms
|
|
222
|
+
await testdriver.exec('pwsh', 'npm install -g package', 30000);
|
|
223
|
+
|
|
224
|
+
// Downloads or complex operations: 60000-120000ms
|
|
225
|
+
await testdriver.exec('pwsh', 'Install-Module Something', 120000);
|
|
226
|
+
```
|
|
227
|
+
</Check>
|
|
228
|
+
|
|
229
|
+
<Check>
|
|
230
|
+
**Handle errors gracefully**
|
|
231
|
+
|
|
232
|
+
```javascript
|
|
233
|
+
try {
|
|
234
|
+
await testdriver.exec('pwsh', 'Some-Command', 5000);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
console.error('Command failed:', error.message);
|
|
237
|
+
// Fallback or retry logic
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
</Check>
|
|
241
|
+
|
|
242
|
+
<Check>
|
|
243
|
+
**Use silent mode for background tasks**
|
|
244
|
+
|
|
245
|
+
```javascript
|
|
246
|
+
// Silent install
|
|
247
|
+
await testdriver.exec('pwsh', 'npm install -g tool', 30000, true);
|
|
248
|
+
|
|
249
|
+
// Background service
|
|
250
|
+
await testdriver.exec('pwsh', 'Start-Service MyService', 5000, true);
|
|
251
|
+
```
|
|
252
|
+
</Check>
|
|
253
|
+
|
|
254
|
+
<Warning>
|
|
255
|
+
**Escape strings properly in PowerShell**
|
|
256
|
+
|
|
257
|
+
Use proper escaping for special characters:
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
// Use backticks for newlines
|
|
261
|
+
await testdriver.exec('pwsh', `
|
|
262
|
+
Write-Host "Line 1\`nLine 2"
|
|
263
|
+
`, 5000);
|
|
264
|
+
|
|
265
|
+
// Use single quotes to avoid variable expansion
|
|
266
|
+
await testdriver.exec('pwsh',
|
|
267
|
+
"Write-Host 'Text with $special chars'",
|
|
268
|
+
5000
|
|
269
|
+
);
|
|
270
|
+
```
|
|
271
|
+
</Warning>
|
|
272
|
+
|
|
273
|
+
## Complete Example
|
|
274
|
+
|
|
275
|
+
```javascript
|
|
276
|
+
import { beforeAll, afterAll, describe, it } from 'vitest';
|
|
277
|
+
import TestDriver from 'testdriverai';
|
|
278
|
+
|
|
279
|
+
describe('Code Execution', () => {
|
|
280
|
+
let testdriver;
|
|
281
|
+
|
|
282
|
+
beforeAll(async () => {
|
|
283
|
+
client = new TestDriver(process.env.TD_API_KEY);
|
|
284
|
+
await testdriver.auth();
|
|
285
|
+
await testdriver.connect({ newSandbox: true });
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
afterAll(async () => {
|
|
289
|
+
await testdriver.disconnect();
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
it('should execute JavaScript in browser', async () => {
|
|
293
|
+
await testdriver.focusApplication('Google Chrome');
|
|
294
|
+
|
|
295
|
+
// Get page info via JavaScript
|
|
296
|
+
const title = await testdriver.exec('js', 'document.title', 5000);
|
|
297
|
+
console.log('Page title:', title);
|
|
298
|
+
|
|
299
|
+
// Manipulate DOM
|
|
300
|
+
await testdriver.exec('js', `
|
|
301
|
+
document.querySelector('#username').value = 'testuser';
|
|
302
|
+
`, 5000);
|
|
303
|
+
|
|
304
|
+
// Verify
|
|
305
|
+
const value = await testdriver.exec('js', `
|
|
306
|
+
document.querySelector('#username').value
|
|
307
|
+
`, 5000);
|
|
308
|
+
|
|
309
|
+
expect(value).toBe('testuser');
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
it('should install and use tools', async () => {
|
|
313
|
+
// Install tool
|
|
314
|
+
await testdriver.exec('pwsh', 'npm install -g http-server', 30000, true);
|
|
315
|
+
|
|
316
|
+
// Create HTML file
|
|
317
|
+
await testdriver.exec('pwsh', `
|
|
318
|
+
Set-Content -Path "C:\\index.html" -Value "<h1>Test Page</h1>"
|
|
319
|
+
`, 5000);
|
|
320
|
+
|
|
321
|
+
// Start server in background
|
|
322
|
+
await testdriver.exec('pwsh', `
|
|
323
|
+
Start-Process pwsh -ArgumentList "-Command", "http-server C:\\ -p 8080"
|
|
324
|
+
`, 5000, true);
|
|
325
|
+
|
|
326
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
327
|
+
|
|
328
|
+
// Launch browser
|
|
329
|
+
await testdriver.exec('pwsh', `
|
|
330
|
+
Start-Process chrome -ArgumentList "http://localhost:8080"
|
|
331
|
+
`, 5000);
|
|
332
|
+
|
|
333
|
+
await testdriver.focusApplication('Google Chrome');
|
|
334
|
+
|
|
335
|
+
// Verify page loaded
|
|
336
|
+
const content = await testdriver.exec('js', 'document.body.textContent', 5000);
|
|
337
|
+
expect(content).toContain('Test Page');
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Related Methods
|
|
343
|
+
|
|
344
|
+
- [`focusApplication()`](/v7/api/focusApplication) - Focus apps before exec
|
|
345
|
+
- [`find()`](/v7/api/find) - Locate elements (alternative to DOM manipulation)
|
|
346
|
+
- [`type()`](/v7/api/type) - Type text (alternative to JS form filling)
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "find()"
|
|
3
|
+
sidebarTitle: "find"
|
|
4
|
+
description: "Locate UI elements using natural language"
|
|
5
|
+
icon: "magnifying-glass"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Locate UI elements on screen using AI-powered natural language descriptions. Returns an `Element` object that can be interacted with.
|
|
11
|
+
|
|
12
|
+
## Syntax
|
|
13
|
+
|
|
14
|
+
```javascript
|
|
15
|
+
const element = await testdriver.find(description)
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Parameters
|
|
19
|
+
|
|
20
|
+
<ParamField path="description" type="string" required>
|
|
21
|
+
Natural language description of the element to find
|
|
22
|
+
</ParamField>
|
|
23
|
+
|
|
24
|
+
## Returns
|
|
25
|
+
|
|
26
|
+
`Promise<Element>` - Element instance that has been automatically located
|
|
27
|
+
|
|
28
|
+
## Examples
|
|
29
|
+
|
|
30
|
+
### Basic Element Finding
|
|
31
|
+
|
|
32
|
+
```javascript
|
|
33
|
+
// Find by role
|
|
34
|
+
const button = await testdriver.find('submit button');
|
|
35
|
+
const input = await testdriver.find('email input field');
|
|
36
|
+
|
|
37
|
+
// Find by text content
|
|
38
|
+
const link = await testdriver.find('Contact Us link');
|
|
39
|
+
const heading = await testdriver.find('Welcome heading');
|
|
40
|
+
|
|
41
|
+
// Find by visual appearance
|
|
42
|
+
const icon = await testdriver.find('red warning icon');
|
|
43
|
+
const image = await testdriver.find('company logo image');
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Finding with Context
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
// Provide location context
|
|
50
|
+
const field = await testdriver.find('username input in the login form');
|
|
51
|
+
const button = await testdriver.find('delete button in the top right corner');
|
|
52
|
+
|
|
53
|
+
// Describe nearby elements
|
|
54
|
+
const input = await testdriver.find('input field below the email label');
|
|
55
|
+
const checkbox = await testdriver.find('checkbox next to "Remember me"');
|
|
56
|
+
|
|
57
|
+
// Describe visual position
|
|
58
|
+
const menu = await testdriver.find('hamburger menu icon in the top left');
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Interacting with Found Elements
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
// Find and click
|
|
65
|
+
const submitBtn = await testdriver.find('submit button');
|
|
66
|
+
await submitBtn.click();
|
|
67
|
+
|
|
68
|
+
// Find and verify
|
|
69
|
+
const message = await testdriver.find('success message');
|
|
70
|
+
if (message.found()) {
|
|
71
|
+
console.log('Success message appeared');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Find and extract info
|
|
75
|
+
const price = await testdriver.find('product price');
|
|
76
|
+
console.log('Price location:', price.coordinates);
|
|
77
|
+
console.log('Price text:', price.text);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Element Object
|
|
81
|
+
|
|
82
|
+
The returned `Element` object provides:
|
|
83
|
+
|
|
84
|
+
### Methods
|
|
85
|
+
|
|
86
|
+
- `found()` - Check if element was located
|
|
87
|
+
- `click(action)` - Click the element
|
|
88
|
+
- `hover()` - Hover over the element
|
|
89
|
+
- `doubleClick()` - Double-click the element
|
|
90
|
+
- `rightClick()` - Right-click the element
|
|
91
|
+
- `find(newDescription)` - Re-locate with optional new description
|
|
92
|
+
|
|
93
|
+
### Properties
|
|
94
|
+
|
|
95
|
+
- `coordinates` - Element position `{x, y, centerX, centerY}`
|
|
96
|
+
- `x`, `y` - Top-left coordinates
|
|
97
|
+
- `centerX`, `centerY` - Center coordinates
|
|
98
|
+
- `text` - Text content (if available)
|
|
99
|
+
- `screenshot` - Base64 screenshot (if available)
|
|
100
|
+
- `confidence` - AI confidence score
|
|
101
|
+
- `width`, `height` - Element dimensions
|
|
102
|
+
- `boundingBox` - Complete bounding box
|
|
103
|
+
|
|
104
|
+
See [Elements Reference](/v7/api/elements) for complete details.
|
|
105
|
+
|
|
106
|
+
## Best Practices
|
|
107
|
+
|
|
108
|
+
<Check>
|
|
109
|
+
**Be specific in descriptions**
|
|
110
|
+
|
|
111
|
+
More specific descriptions improve accuracy:
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
// ✅ Good
|
|
115
|
+
await testdriver.find('blue submit button below the email field');
|
|
116
|
+
|
|
117
|
+
// ❌ Too vague
|
|
118
|
+
await testdriver.find('button');
|
|
119
|
+
```
|
|
120
|
+
</Check>
|
|
121
|
+
|
|
122
|
+
<Check>
|
|
123
|
+
**Always check if found**
|
|
124
|
+
|
|
125
|
+
Verify elements were located before interacting:
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
const element = await testdriver.find('login button');
|
|
129
|
+
if (!element.found()) {
|
|
130
|
+
throw new Error('Login button not found');
|
|
131
|
+
}
|
|
132
|
+
await element.click();
|
|
133
|
+
```
|
|
134
|
+
</Check>
|
|
135
|
+
|
|
136
|
+
<Check>
|
|
137
|
+
**Include visual or positional context**
|
|
138
|
+
|
|
139
|
+
```javascript
|
|
140
|
+
// Include color
|
|
141
|
+
await testdriver.find('red error icon');
|
|
142
|
+
|
|
143
|
+
// Include position
|
|
144
|
+
await testdriver.find('search button in the top navigation bar');
|
|
145
|
+
|
|
146
|
+
// Include nearby text
|
|
147
|
+
await testdriver.find('checkbox next to "I agree to terms"');
|
|
148
|
+
```
|
|
149
|
+
</Check>
|
|
150
|
+
|
|
151
|
+
## Polling for Dynamic Elements
|
|
152
|
+
|
|
153
|
+
For elements that may not be immediately visible:
|
|
154
|
+
|
|
155
|
+
```javascript
|
|
156
|
+
// Poll until element appears
|
|
157
|
+
let loginButton;
|
|
158
|
+
const maxAttempts = 30;
|
|
159
|
+
|
|
160
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
161
|
+
loginButton = await testdriver.find('login button');
|
|
162
|
+
if (loginButton.found()) break;
|
|
163
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (!loginButton.found()) {
|
|
167
|
+
throw new Error('Login button never appeared');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
await loginButton.click();
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Helper Function
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
async function waitForElement(testdriver, description, timeout = 30000) {
|
|
177
|
+
const startTime = Date.now();
|
|
178
|
+
|
|
179
|
+
while (Date.now() - startTime < timeout) {
|
|
180
|
+
const element = await testdriver.find(description);
|
|
181
|
+
if (element.found()) return element;
|
|
182
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
throw new Error(`Element "${description}" not found after ${timeout}ms`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Usage
|
|
189
|
+
const button = await waitForElement(testdriver, 'submit button', 10000);
|
|
190
|
+
await button.click();
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
## Use Cases
|
|
194
|
+
|
|
195
|
+
<AccordionGroup>
|
|
196
|
+
<Accordion title="Form Fields">
|
|
197
|
+
```javascript
|
|
198
|
+
const emailField = await testdriver.find('email input field');
|
|
199
|
+
await emailField.click();
|
|
200
|
+
await testdriver.type('user@example.com');
|
|
201
|
+
|
|
202
|
+
const passwordField = await testdriver.find('password input');
|
|
203
|
+
await passwordField.click();
|
|
204
|
+
await testdriver.type('MyP@ssw0rd');
|
|
205
|
+
```
|
|
206
|
+
</Accordion>
|
|
207
|
+
|
|
208
|
+
<Accordion title="Buttons and Links">
|
|
209
|
+
```javascript
|
|
210
|
+
const submitBtn = await testdriver.find('submit button');
|
|
211
|
+
await submitBtn.click();
|
|
212
|
+
|
|
213
|
+
const cancelLink = await testdriver.find('cancel link');
|
|
214
|
+
await cancelLink.click();
|
|
215
|
+
|
|
216
|
+
const menuIcon = await testdriver.find('hamburger menu icon');
|
|
217
|
+
await menuIcon.click();
|
|
218
|
+
```
|
|
219
|
+
</Accordion>
|
|
220
|
+
|
|
221
|
+
<Accordion title="Dynamic Content">
|
|
222
|
+
```javascript
|
|
223
|
+
// Wait for loading to complete
|
|
224
|
+
let content;
|
|
225
|
+
for (let i = 0; i < 30; i++) {
|
|
226
|
+
content = await testdriver.find('results table');
|
|
227
|
+
if (content.found()) break;
|
|
228
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Interact with loaded content
|
|
232
|
+
const firstRow = await testdriver.find('first row in the results table');
|
|
233
|
+
await firstRow.click();
|
|
234
|
+
```
|
|
235
|
+
</Accordion>
|
|
236
|
+
|
|
237
|
+
<Accordion title="Complex UI Elements">
|
|
238
|
+
```javascript
|
|
239
|
+
// Modals and dialogs
|
|
240
|
+
const modal = await testdriver.find('confirmation dialog');
|
|
241
|
+
if (modal.found()) {
|
|
242
|
+
const confirmBtn = await testdriver.find('confirm button in the dialog');
|
|
243
|
+
await confirmBtn.click();
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Dropdown menus
|
|
247
|
+
const dropdown = await testdriver.find('country dropdown');
|
|
248
|
+
await dropdown.click();
|
|
249
|
+
|
|
250
|
+
const option = await testdriver.find('United States option');
|
|
251
|
+
await option.click();
|
|
252
|
+
```
|
|
253
|
+
</Accordion>
|
|
254
|
+
</AccordionGroup>
|
|
255
|
+
|
|
256
|
+
## Complete Example
|
|
257
|
+
|
|
258
|
+
```javascript
|
|
259
|
+
import { beforeAll, afterAll, describe, it, expect } from 'vitest';
|
|
260
|
+
import TestDriver from 'testdriverai';
|
|
261
|
+
|
|
262
|
+
describe('Element Finding', () => {
|
|
263
|
+
let testdriver;
|
|
264
|
+
|
|
265
|
+
beforeAll(async () => {
|
|
266
|
+
client = new TestDriver(process.env.TD_API_KEY);
|
|
267
|
+
await testdriver.auth();
|
|
268
|
+
await testdriver.connect({ newSandbox: true });
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
afterAll(async () => {
|
|
272
|
+
await testdriver.disconnect();
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('should find and interact with elements', async () => {
|
|
276
|
+
await testdriver.focusApplication('Google Chrome');
|
|
277
|
+
|
|
278
|
+
// Find login form elements
|
|
279
|
+
const usernameField = await testdriver.find('username input field');
|
|
280
|
+
expect(usernameField.found()).toBe(true);
|
|
281
|
+
|
|
282
|
+
await usernameField.click();
|
|
283
|
+
await testdriver.type('testuser');
|
|
284
|
+
|
|
285
|
+
// Find with context
|
|
286
|
+
const passwordField = await testdriver.find('password input below username');
|
|
287
|
+
await passwordField.click();
|
|
288
|
+
await testdriver.type('password123');
|
|
289
|
+
|
|
290
|
+
// Find button
|
|
291
|
+
const submitBtn = await testdriver.find('green submit button');
|
|
292
|
+
expect(submitBtn.found()).toBe(true);
|
|
293
|
+
|
|
294
|
+
console.log('Button location:', submitBtn.centerX, submitBtn.centerY);
|
|
295
|
+
|
|
296
|
+
await submitBtn.click();
|
|
297
|
+
|
|
298
|
+
// Wait for success message
|
|
299
|
+
let successMsg;
|
|
300
|
+
for (let i = 0; i < 10; i++) {
|
|
301
|
+
successMsg = await testdriver.find('success notification');
|
|
302
|
+
if (successMsg.found()) break;
|
|
303
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
expect(successMsg.found()).toBe(true);
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
## Related Methods
|
|
312
|
+
|
|
313
|
+
- [`click()`](/v7/api/click) - Click on found elements
|
|
314
|
+
- [`hover()`](/v7/api/hover) - Hover over elements
|
|
315
|
+
- [`assert()`](/v7/api/assert) - Verify element states
|
|
316
|
+
- [Elements Reference](/v7/api/elements) - Complete Element API
|