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
package/SDK_MIGRATION.md
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# TestDriver SDK Migration Guide
|
|
2
|
+
|
|
3
|
+
## New Element Finding API
|
|
4
|
+
|
|
5
|
+
We've introduced a new, more flexible API for finding and interacting with elements. The new `find()` API provides better control and enables polling patterns for dynamic content.
|
|
6
|
+
|
|
7
|
+
## Quick Comparison
|
|
8
|
+
|
|
9
|
+
### Old API (Deprecated)
|
|
10
|
+
|
|
11
|
+
```javascript
|
|
12
|
+
// Find and click text
|
|
13
|
+
await client.hoverText("Sign In", "black button below password", "click");
|
|
14
|
+
|
|
15
|
+
// Wait for text to appear
|
|
16
|
+
await client.waitForText("Login button", 10000);
|
|
17
|
+
|
|
18
|
+
// Find and click image
|
|
19
|
+
await client.hoverImage("submit button icon", "click");
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### New API (Recommended)
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
// Find and click text or image
|
|
26
|
+
const element = await client
|
|
27
|
+
.find("Sign In, black button below password")
|
|
28
|
+
.find();
|
|
29
|
+
await element.click();
|
|
30
|
+
|
|
31
|
+
// Poll until element appears (replaces waitForText/waitForImage)
|
|
32
|
+
let element = client.find("login button");
|
|
33
|
+
while (!element.found()) {
|
|
34
|
+
console.log("waiting for element to be found");
|
|
35
|
+
element = await element.find();
|
|
36
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
37
|
+
}
|
|
38
|
+
await element.click();
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Migration Examples
|
|
42
|
+
|
|
43
|
+
### Example 1: Simple Click
|
|
44
|
+
|
|
45
|
+
**Before:**
|
|
46
|
+
|
|
47
|
+
```javascript
|
|
48
|
+
await client.hoverText("Submit", "submit button", "click");
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**After:**
|
|
52
|
+
|
|
53
|
+
```javascript
|
|
54
|
+
const submitBtn = await client.find("Submit button");
|
|
55
|
+
await submitBtn.click();
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Example 2: Waiting for Elements
|
|
59
|
+
|
|
60
|
+
**Before:**
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
await client.waitForText("Welcome", 10000);
|
|
64
|
+
await client.hoverText("Welcome", null, "click");
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**After:**
|
|
68
|
+
|
|
69
|
+
```javascript
|
|
70
|
+
let element;
|
|
71
|
+
const maxAttempts = 10;
|
|
72
|
+
let attempts = 0;
|
|
73
|
+
|
|
74
|
+
while (!element?.found() && attempts < maxAttempts) {
|
|
75
|
+
element = await client.find("Welcome");
|
|
76
|
+
if (!element.found()) {
|
|
77
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
78
|
+
}
|
|
79
|
+
attempts++;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (element?.found()) {
|
|
83
|
+
await element.click();
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Example 3: Hover Actions
|
|
88
|
+
|
|
89
|
+
**Before:**
|
|
90
|
+
|
|
91
|
+
```javascript
|
|
92
|
+
await client.hoverText("Menu", null, "hover");
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**After:**
|
|
96
|
+
|
|
97
|
+
```javascript
|
|
98
|
+
const menu = await client.find("Menu").find();
|
|
99
|
+
await menu.hover();
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Example 4: Different Click Types
|
|
103
|
+
|
|
104
|
+
**Before:**
|
|
105
|
+
|
|
106
|
+
```javascript
|
|
107
|
+
await client.hoverText("File", null, "right-click");
|
|
108
|
+
await client.hoverText("Save", null, "double-click");
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**After:**
|
|
112
|
+
|
|
113
|
+
```javascript
|
|
114
|
+
const file = await client.find("File").find();
|
|
115
|
+
await file.rightClick();
|
|
116
|
+
|
|
117
|
+
const save = await client.find("Save").find();
|
|
118
|
+
await save.doubleClick();
|
|
119
|
+
|
|
120
|
+
// Or use the generic click() method
|
|
121
|
+
await file.click("right-click");
|
|
122
|
+
await save.click("double-click");
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Example 5: Conditional Logic
|
|
126
|
+
|
|
127
|
+
**Before:**
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
try {
|
|
131
|
+
await client.waitForText("Error message", 2000);
|
|
132
|
+
// Handle error
|
|
133
|
+
} catch (e) {
|
|
134
|
+
// No error present
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**After:**
|
|
139
|
+
|
|
140
|
+
```javascript
|
|
141
|
+
const errorMsg = await client.find("Error message").find();
|
|
142
|
+
if (errorMsg.found()) {
|
|
143
|
+
// Handle error
|
|
144
|
+
console.log("Error found at:", errorMsg.getCoordinates());
|
|
145
|
+
} else {
|
|
146
|
+
// No error present
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## New Element API Reference
|
|
151
|
+
|
|
152
|
+
### `client.find(description)`
|
|
153
|
+
|
|
154
|
+
Creates an Element instance for finding and interacting with elements.
|
|
155
|
+
|
|
156
|
+
**Parameters:**
|
|
157
|
+
|
|
158
|
+
- `description` (string): Natural language description of the element
|
|
159
|
+
|
|
160
|
+
**Returns:** `Element` instance
|
|
161
|
+
|
|
162
|
+
**Example:**
|
|
163
|
+
|
|
164
|
+
```javascript
|
|
165
|
+
const button = client.find("the sign in button");
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### `element.find([newDescription])`
|
|
169
|
+
|
|
170
|
+
Attempts to locate the element on screen.
|
|
171
|
+
|
|
172
|
+
**Parameters:**
|
|
173
|
+
|
|
174
|
+
- `newDescription` (optional string): New description to search for
|
|
175
|
+
|
|
176
|
+
**Returns:** `Promise<Element>` - The same Element instance (for chaining)
|
|
177
|
+
|
|
178
|
+
**Example:**
|
|
179
|
+
|
|
180
|
+
```javascript
|
|
181
|
+
const element = await client.find("login button").find();
|
|
182
|
+
|
|
183
|
+
// Or with a new description
|
|
184
|
+
element = await element.find("sign in button");
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### `element.found()`
|
|
188
|
+
|
|
189
|
+
Check if the element was successfully located.
|
|
190
|
+
|
|
191
|
+
**Returns:** `boolean` - true if element coordinates were found
|
|
192
|
+
|
|
193
|
+
**Example:**
|
|
194
|
+
|
|
195
|
+
```javascript
|
|
196
|
+
const element = await client.find("button").find();
|
|
197
|
+
if (element.found()) {
|
|
198
|
+
console.log("Element found!");
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### `element.click([action])`
|
|
203
|
+
|
|
204
|
+
Click on the element.
|
|
205
|
+
|
|
206
|
+
**Parameters:**
|
|
207
|
+
|
|
208
|
+
- `action` (optional): Click action type - `'click'`, `'right-click'`, `'double-click'`
|
|
209
|
+
|
|
210
|
+
**Returns:** `Promise<void>`
|
|
211
|
+
|
|
212
|
+
**Example:**
|
|
213
|
+
|
|
214
|
+
```javascript
|
|
215
|
+
await element.click();
|
|
216
|
+
await element.click("right-click");
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### `element.hover()`
|
|
220
|
+
|
|
221
|
+
Hover over the element.
|
|
222
|
+
|
|
223
|
+
**Returns:** `Promise<void>`
|
|
224
|
+
|
|
225
|
+
**Example:**
|
|
226
|
+
|
|
227
|
+
```javascript
|
|
228
|
+
await element.hover();
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### `element.doubleClick()`
|
|
232
|
+
|
|
233
|
+
Double-click on the element. Convenience method for `element.click('double-click')`.
|
|
234
|
+
|
|
235
|
+
**Returns:** `Promise<void>`
|
|
236
|
+
|
|
237
|
+
**Example:**
|
|
238
|
+
|
|
239
|
+
```javascript
|
|
240
|
+
await element.doubleClick();
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### `element.rightClick()`
|
|
244
|
+
|
|
245
|
+
Right-click on the element. Convenience method for `element.click('right-click')`.
|
|
246
|
+
|
|
247
|
+
**Returns:** `Promise<void>`
|
|
248
|
+
|
|
249
|
+
**Example:**
|
|
250
|
+
|
|
251
|
+
```javascript
|
|
252
|
+
await element.rightClick();
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### `element.mouseDown()`
|
|
256
|
+
|
|
257
|
+
Press mouse button down on this element (useful for drag operations).
|
|
258
|
+
|
|
259
|
+
**Returns:** `Promise<void>`
|
|
260
|
+
|
|
261
|
+
**Example:**
|
|
262
|
+
|
|
263
|
+
```javascript
|
|
264
|
+
const source = await client.find("draggable item").find();
|
|
265
|
+
await source.mouseDown();
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### `element.mouseUp()`
|
|
269
|
+
|
|
270
|
+
Release mouse button on this element (useful for drag operations).
|
|
271
|
+
|
|
272
|
+
**Returns:** `Promise<void>`
|
|
273
|
+
|
|
274
|
+
**Example:**
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
const target = await client.find("drop zone").find();
|
|
278
|
+
await target.mouseUp();
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
### `element.getCoordinates()`
|
|
282
|
+
|
|
283
|
+
Get the screen coordinates of the element.
|
|
284
|
+
|
|
285
|
+
**Returns:** `{x, y, centerX, centerY}` or `null` if not found
|
|
286
|
+
|
|
287
|
+
**Example:**
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
const coords = element.getCoordinates();
|
|
291
|
+
if (coords) {
|
|
292
|
+
console.log(`Element at: ${coords.x}, ${coords.y}`);
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### `element.getResponse()`
|
|
297
|
+
|
|
298
|
+
Get the full API response data from the locate operation.
|
|
299
|
+
|
|
300
|
+
**Returns:** `Object | null` - Full response containing all available data
|
|
301
|
+
|
|
302
|
+
**Example:**
|
|
303
|
+
|
|
304
|
+
```javascript
|
|
305
|
+
const response = element.getResponse();
|
|
306
|
+
console.log("Full response:", response);
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Element Properties
|
|
310
|
+
|
|
311
|
+
The Element class exposes many properties from the API response:
|
|
312
|
+
|
|
313
|
+
### Coordinate Properties
|
|
314
|
+
|
|
315
|
+
- `element.x` - X coordinate (top-left corner)
|
|
316
|
+
- `element.y` - Y coordinate (top-left corner)
|
|
317
|
+
- `element.centerX` - X coordinate of element center
|
|
318
|
+
- `element.centerY` - Y coordinate of element center
|
|
319
|
+
|
|
320
|
+
### Dimension Properties
|
|
321
|
+
|
|
322
|
+
- `element.width` - Width of the element (if available)
|
|
323
|
+
- `element.height` - Height of the element (if available)
|
|
324
|
+
- `element.boundingBox` - Bounding box data (if available)
|
|
325
|
+
|
|
326
|
+
### Match Quality Properties
|
|
327
|
+
|
|
328
|
+
- `element.confidence` - Confidence score of the match (0-1)
|
|
329
|
+
- `element.screenshot` - Base64 encoded screenshot of the element
|
|
330
|
+
- `element.text` - Text content of the element (if available)
|
|
331
|
+
- `element.label` - Label/aria-label of the element (if available)
|
|
332
|
+
|
|
333
|
+
### Example Usage
|
|
334
|
+
|
|
335
|
+
```javascript
|
|
336
|
+
const button = await client.find("login button");
|
|
337
|
+
|
|
338
|
+
if (button.found()) {
|
|
339
|
+
console.log("Position:", { x: button.x, y: button.y });
|
|
340
|
+
console.log("Center:", { x: button.centerX, y: button.centerY });
|
|
341
|
+
console.log("Size:", { width: button.width, height: button.height });
|
|
342
|
+
console.log("Confidence:", button.confidence);
|
|
343
|
+
console.log("Text:", button.text);
|
|
344
|
+
|
|
345
|
+
// Save screenshot for debugging
|
|
346
|
+
if (button.screenshot) {
|
|
347
|
+
const fs = require("fs");
|
|
348
|
+
fs.writeFileSync("button.png", Buffer.from(button.screenshot, "base64"));
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// Conditional actions based on properties
|
|
352
|
+
if (button.confidence > 0.8) {
|
|
353
|
+
await button.click();
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
For a complete example, see `examples/sdk-element-properties.js`.
|
|
359
|
+
|
|
360
|
+
## Common Patterns
|
|
361
|
+
|
|
362
|
+
### Pattern 1: Find and Click
|
|
363
|
+
|
|
364
|
+
```javascript
|
|
365
|
+
const element = await client.find("description").find();
|
|
366
|
+
if (element.found()) {
|
|
367
|
+
await element.click();
|
|
368
|
+
}
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Pattern 2: Polling with Timeout
|
|
372
|
+
|
|
373
|
+
```javascript
|
|
374
|
+
const element = client.find("element description");
|
|
375
|
+
const timeoutMs = 10000;
|
|
376
|
+
const startTime = Date.now();
|
|
377
|
+
|
|
378
|
+
while (!element.found() && Date.now() - startTime < timeoutMs) {
|
|
379
|
+
element = await element.find();
|
|
380
|
+
if (!element.found()) {
|
|
381
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (!element.found()) {
|
|
386
|
+
throw new Error("Element not found within timeout");
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
### Pattern 3: Retry with Different Descriptions
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
let element = client.find("primary button");
|
|
394
|
+
element = await element.find();
|
|
395
|
+
|
|
396
|
+
if (!element.found()) {
|
|
397
|
+
element = await element.find("submit button");
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (!element.found()) {
|
|
401
|
+
element = await element.find("blue button on the right");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (element.found()) {
|
|
405
|
+
await element.click();
|
|
406
|
+
}
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
### Pattern 4: Helper Function for Waiting
|
|
410
|
+
|
|
411
|
+
```javascript
|
|
412
|
+
async function waitForElement(client, description, timeoutMs = 10000) {
|
|
413
|
+
const element = client.find(description);
|
|
414
|
+
const startTime = Date.now();
|
|
415
|
+
|
|
416
|
+
while (!element.found() && Date.now() - startTime < timeoutMs) {
|
|
417
|
+
await element.find();
|
|
418
|
+
if (!element.found()) {
|
|
419
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (!element.found()) {
|
|
424
|
+
throw new Error(`Element "${description}" not found within ${timeoutMs}ms`);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
return element;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Usage
|
|
431
|
+
const button = await waitForElement(client, "login button", 5000);
|
|
432
|
+
await button.click();
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
## Deprecated Methods
|
|
436
|
+
|
|
437
|
+
The following methods are now deprecated and will be removed in a future version:
|
|
438
|
+
|
|
439
|
+
- ❌ `client.hoverText()` → ✅ Use `client.find().find()` + `element.click()`
|
|
440
|
+
- ❌ `client.hoverImage()` → ✅ Use `client.find().find()` + `element.click()`
|
|
441
|
+
- ❌ `client.waitForText()` → ✅ Use polling pattern with `client.find()`
|
|
442
|
+
- ❌ `client.waitForImage()` → ✅ Use polling pattern with `client.find()`
|
|
443
|
+
- ❌ `client.wait()` → ✅ Use element polling instead of arbitrary waits when possible
|
|
444
|
+
|
|
445
|
+
## Benefits of the New API
|
|
446
|
+
|
|
447
|
+
1. **More Explicit**: Clear separation between finding and interacting
|
|
448
|
+
2. **Better Error Handling**: Can check if element exists before interacting
|
|
449
|
+
3. **Flexible Polling**: Custom polling logic for dynamic content
|
|
450
|
+
4. **Unified Interface**: Same API for text and images
|
|
451
|
+
5. **More Control**: Access to element state and coordinates
|
|
452
|
+
6. **Composable**: Element instances can be stored and reused
|
|
453
|
+
|
|
454
|
+
## TypeScript Support
|
|
455
|
+
|
|
456
|
+
The new API is fully typed:
|
|
457
|
+
|
|
458
|
+
```typescript
|
|
459
|
+
import TestDriver, { Element } from "testdriverai";
|
|
460
|
+
|
|
461
|
+
const client = new TestDriver(process.env.TD_API_KEY);
|
|
462
|
+
await client.connect();
|
|
463
|
+
|
|
464
|
+
const element: Element = await client.find("button").find();
|
|
465
|
+
const found: boolean = element.found();
|
|
466
|
+
const coords = element.getCoordinates(); // {x, y, centerX, centerY} | null
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
## Questions?
|
|
470
|
+
|
|
471
|
+
For more examples, see:
|
|
472
|
+
|
|
473
|
+
- `examples/sdk-find-example.js` - Comprehensive examples of the new API
|
|
474
|
+
- `testdriver/acceptance-sdk/hover-text.test.mjs` - Updated test example
|