testdriverai 7.0.0 → 7.1.1
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/.env.example +2 -0
- package/.github/workflows/linux-tests.yml +28 -0
- package/README.md +126 -0
- package/agent/index.js +7 -9
- package/agent/interface.js +13 -2
- package/agent/lib/commands.js +795 -136
- package/agent/lib/redraw.js +124 -39
- package/agent/lib/sandbox.js +40 -3
- package/agent/lib/sdk.js +21 -0
- package/agent/lib/valid-version.js +2 -2
- package/debugger/index.html +1 -1
- package/docs/docs.json +86 -71
- package/docs/guide/best-practices-polling.mdx +154 -0
- package/docs/v6/getting-started/self-hosting.mdx +3 -2
- package/docs/v7/_drafts/agents.mdx +852 -0
- package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
- package/docs/v7/_drafts/best-practices.mdx +486 -0
- package/docs/v7/_drafts/caching-ai.mdx +215 -0
- package/docs/v7/_drafts/caching-selectors.mdx +400 -0
- package/docs/v7/_drafts/caching.mdx +366 -0
- package/docs/v7/_drafts/cli-to-sdk-migration.mdx +425 -0
- package/docs/v7/_drafts/core.mdx +459 -0
- package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
- package/docs/v7/_drafts/debugging.mdx +349 -0
- package/docs/v7/_drafts/error-handling.mdx +501 -0
- package/docs/v7/_drafts/faq.mdx +393 -0
- package/docs/v7/_drafts/hooks.mdx +360 -0
- package/docs/v7/_drafts/implementation-plan.mdx +994 -0
- package/docs/v7/_drafts/init-command.mdx +95 -0
- package/docs/v7/_drafts/optimal-sdk-design.mdx +1348 -0
- package/docs/v7/_drafts/performance.mdx +517 -0
- package/docs/v7/_drafts/presets.mdx +210 -0
- package/docs/v7/_drafts/progressive-disclosure.mdx +230 -0
- package/docs/v7/_drafts/provision.mdx +266 -0
- package/docs/{QUICK_START_TEST_RECORDING.md → v7/_drafts/quick-start-test-recording.mdx} +3 -3
- package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
- package/docs/v7/{guides → _drafts}/self-hosting.mdx +1 -1
- package/docs/v7/_drafts/troubleshooting.mdx +526 -0
- package/docs/v7/_drafts/vitest-plugin.mdx +477 -0
- package/docs/v7/_drafts/vitest.mdx +535 -0
- package/docs/v7/api/{ai.mdx → act.mdx} +24 -24
- package/docs/v7/api/client.mdx +1 -1
- package/docs/v7/api/dashcam.mdx +497 -0
- package/docs/v7/api/doubleClick.mdx +102 -0
- package/docs/v7/api/elements.mdx +143 -41
- package/docs/v7/api/find.mdx +258 -0
- package/docs/v7/api/mouseDown.mdx +161 -0
- package/docs/v7/api/mouseUp.mdx +164 -0
- package/docs/v7/api/rightClick.mdx +123 -0
- package/docs/v7/api/type.mdx +51 -7
- package/docs/v7/features/ai-native.mdx +427 -0
- package/docs/v7/features/easy-to-write.mdx +351 -0
- package/docs/v7/features/enterprise.mdx +540 -0
- package/docs/v7/features/fast.mdx +424 -0
- package/docs/v7/features/observable.mdx +623 -0
- package/docs/v7/features/powerful.mdx +531 -0
- package/docs/v7/features/scalable.mdx +417 -0
- package/docs/v7/features/stable.mdx +514 -0
- package/docs/v7/getting-started/configuration.mdx +380 -0
- package/docs/v7/getting-started/generating-tests.mdx +525 -0
- package/docs/v7/getting-started/installation.mdx +486 -0
- package/docs/v7/getting-started/quickstart.mdx +320 -141
- package/docs/v7/getting-started/running-and-debugging.mdx +511 -0
- package/docs/v7/getting-started/setting-up-in-ci.mdx +612 -0
- package/docs/v7/getting-started/writing-tests.mdx +535 -0
- package/docs/v7/overview/what-is-testdriver.mdx +398 -0
- package/docs/v7/platforms/linux.mdx +308 -0
- package/docs/v7/platforms/macos.mdx +433 -0
- package/docs/v7/platforms/windows.mdx +430 -0
- package/docs/v7/playwright.mdx +3 -3
- package/docs/v7/presets/chrome-extension.mdx +223 -0
- package/docs/v7/presets/chrome.mdx +303 -0
- package/docs/v7/presets/electron.mdx +453 -0
- package/docs/v7/presets/vscode.mdx +417 -0
- package/docs/v7/presets/webapp.mdx +396 -0
- package/examples/run-tests-with-recording.sh +2 -2
- package/interfaces/cli/commands/init.js +358 -0
- package/interfaces/vitest-plugin.mjs +393 -103
- package/lib/core/Dashcam.js +506 -0
- package/lib/core/index.d.ts +150 -0
- package/lib/core/index.js +12 -0
- package/lib/presets/index.mjs +331 -0
- package/lib/vitest/hooks.d.ts +119 -0
- package/lib/vitest/hooks.mjs +316 -0
- package/lib/vitest/setup.mjs +44 -0
- package/package.json +13 -3
- package/sdk.d.ts +350 -44
- package/sdk.js +818 -105
- package/{self-hosted.yml → setup/aws/self-hosted.yml} +1 -1
- package/test/manual/test-console-logs.test.mjs +42 -0
- package/test/manual/test-init.sh +54 -0
- package/test/manual/test-provision-auth.mjs +22 -0
- package/test/testdriver/assert.test.mjs +41 -0
- package/test/testdriver/auto-cache-key-demo.test.mjs +56 -0
- package/test/testdriver/chrome-extension.test.mjs +89 -0
- package/{testdriver/acceptance-sdk → test/testdriver}/drag-and-drop.test.mjs +7 -19
- package/{testdriver/acceptance-sdk → test/testdriver}/element-not-found.test.mjs +6 -19
- package/{testdriver/acceptance-sdk → test/testdriver}/exec-js.test.mjs +6 -18
- package/{testdriver/acceptance-sdk → test/testdriver}/exec-output.test.mjs +9 -21
- package/{testdriver/acceptance-sdk → test/testdriver}/exec-pwsh.test.mjs +14 -26
- package/{testdriver/acceptance-sdk → test/testdriver}/focus-window.test.mjs +8 -20
- package/{testdriver/acceptance-sdk → test/testdriver}/formatted-logging.test.mjs +5 -20
- package/{testdriver/acceptance-sdk → test/testdriver}/hover-image.test.mjs +10 -19
- package/{testdriver/acceptance-sdk → test/testdriver}/hover-text-with-description.test.mjs +7 -19
- package/{testdriver/acceptance-sdk → test/testdriver}/hover-text.test.mjs +5 -19
- package/{testdriver/acceptance-sdk → test/testdriver}/match-image.test.mjs +7 -19
- package/{testdriver/acceptance-sdk → test/testdriver}/press-keys.test.mjs +5 -19
- package/{testdriver/acceptance-sdk → test/testdriver}/prompt.test.mjs +7 -19
- package/{testdriver/acceptance-sdk → test/testdriver}/scroll-keyboard.test.mjs +6 -20
- package/{testdriver/acceptance-sdk → test/testdriver}/scroll-until-image.test.mjs +6 -18
- package/test/testdriver/scroll-until-text.test.mjs +28 -0
- package/{testdriver/acceptance-sdk → test/testdriver}/scroll.test.mjs +12 -21
- package/test/testdriver/setup/lifecycleHelpers.mjs +262 -0
- package/{testdriver/acceptance-sdk → test/testdriver}/setup/testHelpers.mjs +25 -20
- package/test/testdriver/type.test.mjs +45 -0
- package/vitest.config.mjs +11 -56
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/acceptance-linux.yml +0 -75
- package/.github/workflows/acceptance-sdk-tests.yml +0 -133
- package/.github/workflows/acceptance-tests.yml +0 -130
- package/.github/workflows/lint.yml +0 -27
- package/.github/workflows/publish-canary.yml +0 -40
- package/.github/workflows/publish-latest.yml +0 -61
- package/.github/workflows/test-install.yml +0 -29
- package/.vscode/extensions.json +0 -3
- package/.vscode/launch.json +0 -22
- package/.vscode/mcp.json +0 -9
- package/.vscode/settings.json +0 -14
- package/CODEOWNERS +0 -3
- package/MIGRATION.md +0 -389
- package/SDK_README.md +0 -1122
- package/_testdriver/acceptance/assert.yaml +0 -7
- package/_testdriver/acceptance/dashcam.yaml +0 -9
- package/_testdriver/acceptance/drag-and-drop.yaml +0 -49
- package/_testdriver/acceptance/embed.yaml +0 -9
- package/_testdriver/acceptance/exec-js.yaml +0 -29
- package/_testdriver/acceptance/exec-output.yaml +0 -43
- package/_testdriver/acceptance/exec-shell.yaml +0 -40
- package/_testdriver/acceptance/focus-window.yaml +0 -16
- package/_testdriver/acceptance/hover-image.yaml +0 -18
- package/_testdriver/acceptance/hover-text-with-description.yaml +0 -29
- package/_testdriver/acceptance/hover-text.yaml +0 -14
- package/_testdriver/acceptance/if-else.yaml +0 -31
- package/_testdriver/acceptance/match-image.yaml +0 -15
- package/_testdriver/acceptance/press-keys.yaml +0 -35
- package/_testdriver/acceptance/prompt.yaml +0 -11
- package/_testdriver/acceptance/remember.yaml +0 -27
- package/_testdriver/acceptance/screenshots/cart.png +0 -0
- package/_testdriver/acceptance/scroll-keyboard.yaml +0 -34
- package/_testdriver/acceptance/scroll-until-image.yaml +0 -26
- package/_testdriver/acceptance/scroll-until-text.yaml +0 -20
- package/_testdriver/acceptance/scroll.yaml +0 -33
- package/_testdriver/acceptance/snippets/login.yaml +0 -29
- package/_testdriver/acceptance/snippets/match-cart.yaml +0 -8
- package/_testdriver/acceptance/type.yaml +0 -29
- package/_testdriver/behavior/failure.yaml +0 -7
- package/_testdriver/behavior/hover-text.yaml +0 -13
- package/_testdriver/behavior/lifecycle/postrun.yaml +0 -10
- package/_testdriver/behavior/lifecycle/prerun.yaml +0 -8
- package/_testdriver/behavior/lifecycle/provision.yaml +0 -8
- package/_testdriver/behavior/secrets.yaml +0 -7
- package/_testdriver/edge-cases/dashcam-chrome.yaml +0 -8
- package/_testdriver/edge-cases/exec-pwsh-multiline.yaml +0 -10
- package/_testdriver/edge-cases/js-exception.yaml +0 -8
- package/_testdriver/edge-cases/js-promise.yaml +0 -19
- package/_testdriver/edge-cases/lifecycle/postrun.yaml +0 -10
- package/_testdriver/edge-cases/prompt-in-middle.yaml +0 -23
- package/_testdriver/edge-cases/prompt-nested.yaml +0 -7
- package/_testdriver/edge-cases/success-test.yaml +0 -9
- package/_testdriver/examples/android/example.yaml +0 -12
- package/_testdriver/examples/android/lifecycle/postrun.yaml +0 -11
- package/_testdriver/examples/android/lifecycle/provision.yaml +0 -47
- package/_testdriver/examples/android/readme.md +0 -7
- package/_testdriver/examples/chrome-extension/lifecycle/provision.yaml +0 -74
- package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
- package/_testdriver/examples/desktop/lifecycle/provision.yaml +0 -64
- package/_testdriver/examples/vscode-extension/lifecycle/provision.yaml +0 -73
- package/_testdriver/examples/web/lifecycle/postrun.yaml +0 -7
- package/_testdriver/examples/web/lifecycle/prerun.yaml +0 -22
- package/_testdriver/lifecycle/postrun.yaml +0 -8
- package/_testdriver/lifecycle/prerun.yaml +0 -15
- package/_testdriver/lifecycle/provision.yaml +0 -25
- package/debug-screenshot-1763401388589.png +0 -0
- package/mcp-server/AI_GUIDELINES.md +0 -57
- package/scripts/view-test-results.mjs +0 -96
- package/styles/.vale-config/2-MDX.ini +0 -5
- package/styles/Microsoft/AMPM.yml +0 -9
- package/styles/Microsoft/Accessibility.yml +0 -30
- package/styles/Microsoft/Acronyms.yml +0 -64
- package/styles/Microsoft/Adverbs.yml +0 -272
- package/styles/Microsoft/Auto.yml +0 -11
- package/styles/Microsoft/Avoid.yml +0 -14
- package/styles/Microsoft/Contractions.yml +0 -50
- package/styles/Microsoft/Dashes.yml +0 -13
- package/styles/Microsoft/DateFormat.yml +0 -8
- package/styles/Microsoft/DateNumbers.yml +0 -40
- package/styles/Microsoft/DateOrder.yml +0 -8
- package/styles/Microsoft/Ellipses.yml +0 -9
- package/styles/Microsoft/FirstPerson.yml +0 -16
- package/styles/Microsoft/Foreign.yml +0 -13
- package/styles/Microsoft/Gender.yml +0 -8
- package/styles/Microsoft/GenderBias.yml +0 -42
- package/styles/Microsoft/GeneralURL.yml +0 -11
- package/styles/Microsoft/HeadingAcronyms.yml +0 -7
- package/styles/Microsoft/HeadingColons.yml +0 -8
- package/styles/Microsoft/HeadingPunctuation.yml +0 -13
- package/styles/Microsoft/Headings.yml +0 -28
- package/styles/Microsoft/Hyphens.yml +0 -14
- package/styles/Microsoft/Negative.yml +0 -13
- package/styles/Microsoft/Ordinal.yml +0 -13
- package/styles/Microsoft/OxfordComma.yml +0 -8
- package/styles/Microsoft/Passive.yml +0 -183
- package/styles/Microsoft/Percentages.yml +0 -7
- package/styles/Microsoft/Plurals.yml +0 -7
- package/styles/Microsoft/Quotes.yml +0 -7
- package/styles/Microsoft/RangeTime.yml +0 -13
- package/styles/Microsoft/Semicolon.yml +0 -8
- package/styles/Microsoft/SentenceLength.yml +0 -6
- package/styles/Microsoft/Spacing.yml +0 -8
- package/styles/Microsoft/Suspended.yml +0 -7
- package/styles/Microsoft/Terms.yml +0 -42
- package/styles/Microsoft/URLFormat.yml +0 -9
- package/styles/Microsoft/Units.yml +0 -16
- package/styles/Microsoft/Vocab.yml +0 -25
- package/styles/Microsoft/We.yml +0 -11
- package/styles/Microsoft/Wordiness.yml +0 -127
- package/styles/Microsoft/meta.json +0 -4
- package/styles/alex/Ablist.yml +0 -274
- package/styles/alex/Condescending.yml +0 -16
- package/styles/alex/Gendered.yml +0 -110
- package/styles/alex/LGBTQ.yml +0 -55
- package/styles/alex/OCD.yml +0 -10
- package/styles/alex/Press.yml +0 -12
- package/styles/alex/ProfanityLikely.yml +0 -1289
- package/styles/alex/ProfanityMaybe.yml +0 -282
- package/styles/alex/ProfanityUnlikely.yml +0 -251
- package/styles/alex/README.md +0 -27
- package/styles/alex/Race.yml +0 -85
- package/styles/alex/Suicide.yml +0 -26
- package/styles/alex/meta.json +0 -4
- package/styles/config/vocabularies/Docs/accept.txt +0 -47
- package/styles/config/vocabularies/Docs/reject.txt +0 -4
- package/styles/proselint/Airlinese.yml +0 -8
- package/styles/proselint/AnimalLabels.yml +0 -48
- package/styles/proselint/Annotations.yml +0 -9
- package/styles/proselint/Apologizing.yml +0 -8
- package/styles/proselint/Archaisms.yml +0 -52
- package/styles/proselint/But.yml +0 -8
- package/styles/proselint/Cliches.yml +0 -782
- package/styles/proselint/CorporateSpeak.yml +0 -30
- package/styles/proselint/Currency.yml +0 -5
- package/styles/proselint/Cursing.yml +0 -15
- package/styles/proselint/DateCase.yml +0 -7
- package/styles/proselint/DateMidnight.yml +0 -7
- package/styles/proselint/DateRedundancy.yml +0 -10
- package/styles/proselint/DateSpacing.yml +0 -7
- package/styles/proselint/DenizenLabels.yml +0 -52
- package/styles/proselint/Diacritical.yml +0 -95
- package/styles/proselint/GenderBias.yml +0 -45
- package/styles/proselint/GroupTerms.yml +0 -39
- package/styles/proselint/Hedging.yml +0 -8
- package/styles/proselint/Hyperbole.yml +0 -6
- package/styles/proselint/Jargon.yml +0 -11
- package/styles/proselint/LGBTOffensive.yml +0 -13
- package/styles/proselint/LGBTTerms.yml +0 -15
- package/styles/proselint/Malapropisms.yml +0 -8
- package/styles/proselint/Needless.yml +0 -358
- package/styles/proselint/Nonwords.yml +0 -38
- package/styles/proselint/Oxymorons.yml +0 -22
- package/styles/proselint/P-Value.yml +0 -6
- package/styles/proselint/RASSyndrome.yml +0 -30
- package/styles/proselint/README.md +0 -12
- package/styles/proselint/Skunked.yml +0 -13
- package/styles/proselint/Spelling.yml +0 -17
- package/styles/proselint/Typography.yml +0 -11
- package/styles/proselint/Uncomparables.yml +0 -50
- package/styles/proselint/Very.yml +0 -6
- package/styles/proselint/meta.json +0 -15
- package/styles/write-good/Cliches.yml +0 -702
- package/styles/write-good/E-Prime.yml +0 -32
- package/styles/write-good/Illusions.yml +0 -11
- package/styles/write-good/Passive.yml +0 -183
- package/styles/write-good/README.md +0 -27
- package/styles/write-good/So.yml +0 -5
- package/styles/write-good/ThereIs.yml +0 -6
- package/styles/write-good/TooWordy.yml +0 -221
- package/styles/write-good/Weasel.yml +0 -29
- package/styles/write-good/meta.json +0 -4
- package/test/mcp-example-test.yaml +0 -27
- package/test/test_parser.js +0 -47
- package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +0 -61
- package/testdriver/acceptance-sdk/README.md +0 -128
- package/testdriver/acceptance-sdk/TEST_REPORTING.md +0 -245
- package/testdriver/acceptance-sdk/assert.test.mjs +0 -44
- package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +0 -42
- package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +0 -239
- package/testdriver/acceptance-sdk/type-checking-demo.js +0 -49
- package/testdriver/acceptance-sdk/type.test.mjs +0 -84
- package/vale.ini +0 -18
- package/vitest.config.example.js +0 -19
- package/vitest.config.mjs.bak +0 -44
- /package/docs/{ARCHITECTURE.md → v7/_drafts/architecture.mdx} +0 -0
- /package/docs/{AWESOME_LOGS_QUICK_REF.md → v7/_drafts/awesome-logs-quick-ref.mdx} +0 -0
- /package/{CONTRIBUTING.md → docs/v7/_drafts/contributing.mdx} +0 -0
- /package/docs/v7/{guides → _drafts}/migration.mdx +0 -0
- /package/{PLUGIN_MIGRATION.md → docs/v7/_drafts/plugin-migration.mdx} +0 -0
- /package/{PROMPT_CACHE.md → docs/v7/_drafts/prompt-cache.mdx} +0 -0
- /package/docs/{SDK_AWESOME_LOGS.md → v7/_drafts/sdk-awesome-logs.mdx} +0 -0
- /package/docs/{sdk-browser-rendering.md → v7/_drafts/sdk-browser-rendering.mdx} +0 -0
- /package/{SDK_LOGGING.md → docs/v7/_drafts/sdk-logging.mdx} +0 -0
- /package/{SDK_MIGRATION.md → docs/v7/_drafts/sdk-migration.mdx} +0 -0
- /package/docs/{TEST_RECORDING.md → v7/_drafts/test-recording.mdx} +0 -0
- /package/docs/v7/{README.md → overview/readme.mdx} +0 -0
- /package/{debug-locate-response.js → test/manual/debug-locate-response.js} +0 -0
- /package/{test-find-api.js → test/manual/test-find-api.js} +0 -0
- /package/{test-prompt-cache.js → test/manual/test-prompt-cache.js} +0 -0
- /package/{test-sandbox-render.js → test/manual/test-sandbox-render.js} +0 -0
- /package/{test-sdk-methods.js → test/manual/test-sdk-methods.js} +0 -0
- /package/{test-sdk-refactor.js → test/manual/test-sdk-refactor.js} +0 -0
- /package/{test-stack-trace.mjs → test/manual/test-stack-trace.mjs} +0 -0
- /package/{verify-element-api.js → test/manual/verify-element-api.js} +0 -0
- /package/{verify-types.js → test/manual/verify-types.js} +0 -0
- /package/{testdriver/acceptance-sdk → test/testdriver}/setup/globalTeardown.mjs +0 -0
- /package/{testdriver/acceptance-sdk → test/testdriver}/setup/vitestSetup.mjs +0 -0
package/docs/v7/api/elements.mdx
CHANGED
|
@@ -194,11 +194,11 @@ await target.mouseUp();
|
|
|
194
194
|
|
|
195
195
|
### Properties
|
|
196
196
|
|
|
197
|
-
Element properties provide additional information about located elements
|
|
197
|
+
Element properties provide additional information about located elements. Properties are available after a successful `find()` call.
|
|
198
198
|
|
|
199
199
|
#### coordinates
|
|
200
200
|
|
|
201
|
-
Get the element's coordinates object.
|
|
201
|
+
Get the element's coordinates object containing all position information.
|
|
202
202
|
|
|
203
203
|
```javascript
|
|
204
204
|
const coords = element.getCoordinates()
|
|
@@ -206,115 +206,217 @@ const coords = element.getCoordinates()
|
|
|
206
206
|
element.coordinates
|
|
207
207
|
```
|
|
208
208
|
|
|
209
|
-
**Returns:** Object with `{ x, y, centerX, centerY }`
|
|
209
|
+
**Returns:** `Object | null` - Coordinate object with `{ x, y, centerX, centerY }`
|
|
210
210
|
|
|
211
211
|
**Example:**
|
|
212
212
|
```javascript
|
|
213
213
|
const button = await testdriver.find('submit button');
|
|
214
214
|
const coords = button.coordinates;
|
|
215
|
-
|
|
216
|
-
|
|
215
|
+
|
|
216
|
+
if (coords) {
|
|
217
|
+
console.log(`Top-left: (${coords.x}, ${coords.y})`);
|
|
218
|
+
console.log(`Center: (${coords.centerX}, ${coords.centerY})`);
|
|
219
|
+
}
|
|
217
220
|
```
|
|
218
221
|
|
|
219
222
|
#### x, y, centerX, centerY
|
|
220
223
|
|
|
221
|
-
Direct access to coordinate values.
|
|
224
|
+
Direct access to coordinate values. Always available after successful `find()`.
|
|
222
225
|
|
|
223
226
|
```javascript
|
|
224
|
-
element.x // Top-left X coordinate
|
|
225
|
-
element.y // Top-left Y coordinate
|
|
226
|
-
element.centerX // Center X coordinate
|
|
227
|
-
element.centerY // Center Y coordinate
|
|
227
|
+
element.x // Top-left X coordinate (number)
|
|
228
|
+
element.y // Top-left Y coordinate (number)
|
|
229
|
+
element.centerX // Center X coordinate (number)
|
|
230
|
+
element.centerY // Center Y coordinate (number)
|
|
228
231
|
```
|
|
229
232
|
|
|
230
233
|
**Example:**
|
|
231
234
|
```javascript
|
|
232
235
|
const button = await testdriver.find('submit button');
|
|
236
|
+
console.log(`Button at: (${button.x}, ${button.y})`);
|
|
233
237
|
console.log(`Button center: (${button.centerX}, ${button.centerY})`);
|
|
238
|
+
|
|
239
|
+
// Use for custom mouse operations
|
|
240
|
+
await testdriver.click(button.centerX, button.centerY);
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### width, height
|
|
244
|
+
|
|
245
|
+
Element dimensions in pixels. Available when AI detects element bounds.
|
|
246
|
+
|
|
247
|
+
```javascript
|
|
248
|
+
element.width // Width in pixels (number | null)
|
|
249
|
+
element.height // Height in pixels (number | null)
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
**Example:**
|
|
253
|
+
```javascript
|
|
254
|
+
const button = await testdriver.find('submit button');
|
|
255
|
+
|
|
256
|
+
if (button.width && button.height) {
|
|
257
|
+
console.log(`Button size: ${button.width}x${button.height}px`);
|
|
258
|
+
|
|
259
|
+
// Check if button is large enough
|
|
260
|
+
if (button.width < 50) {
|
|
261
|
+
console.warn('Button might be too small');
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
#### boundingBox
|
|
267
|
+
|
|
268
|
+
Complete bounding box information including position and dimensions.
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
element.boundingBox
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
**Returns:** `Object | null` - Bounding box with all dimension data
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
{
|
|
278
|
+
x: number, // Top-left X
|
|
279
|
+
y: number, // Top-left Y
|
|
280
|
+
width: number, // Width in pixels
|
|
281
|
+
height: number // Height in pixels
|
|
282
|
+
}
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
**Example:**
|
|
286
|
+
```javascript
|
|
287
|
+
const element = await testdriver.find('dialog box');
|
|
288
|
+
|
|
289
|
+
if (element.boundingBox) {
|
|
290
|
+
const { x, y, width, height } = element.boundingBox;
|
|
291
|
+
console.log(`Dialog: ${width}x${height} at (${x}, ${y})`);
|
|
292
|
+
|
|
293
|
+
// Calculate if element is in viewport
|
|
294
|
+
const rightEdge = x + width;
|
|
295
|
+
const bottomEdge = y + height;
|
|
296
|
+
console.log(`Element extends to (${rightEdge}, ${bottomEdge})`);
|
|
297
|
+
}
|
|
234
298
|
```
|
|
235
299
|
|
|
236
300
|
#### screenshot
|
|
237
301
|
|
|
238
|
-
|
|
302
|
+
Base64-encoded PNG screenshot of the screen when element was found. Only available in DEBUG mode or when an error occurs.
|
|
239
303
|
|
|
240
304
|
```javascript
|
|
241
305
|
element.screenshot
|
|
242
306
|
```
|
|
243
307
|
|
|
244
|
-
**Returns:** `string | null` - Base64-encoded image
|
|
308
|
+
**Returns:** `string | null` - Base64-encoded PNG image
|
|
245
309
|
|
|
246
310
|
**Example:**
|
|
247
311
|
```javascript
|
|
248
312
|
const element = await testdriver.find('error message');
|
|
313
|
+
|
|
249
314
|
if (element.screenshot) {
|
|
250
|
-
|
|
315
|
+
// Save screenshot to file
|
|
316
|
+
const fs = require('fs');
|
|
317
|
+
const base64Data = element.screenshot.replace(/^data:image\/\w+;base64,/, '');
|
|
318
|
+
fs.writeFileSync('element-screenshot.png', Buffer.from(base64Data, 'base64'));
|
|
319
|
+
console.log('Screenshot saved');
|
|
251
320
|
}
|
|
252
321
|
```
|
|
253
322
|
|
|
323
|
+
<Warning>
|
|
324
|
+
Screenshots can be large. They're automatically excluded from error messages to prevent memory issues.
|
|
325
|
+
</Warning>
|
|
326
|
+
|
|
254
327
|
#### text
|
|
255
328
|
|
|
256
|
-
|
|
329
|
+
Text content extracted from the element by AI (if available).
|
|
257
330
|
|
|
258
331
|
```javascript
|
|
259
332
|
element.text
|
|
260
333
|
```
|
|
261
334
|
|
|
262
|
-
**Returns:** `string | null`
|
|
335
|
+
**Returns:** `string | null` - Element's text content
|
|
263
336
|
|
|
264
337
|
**Example:**
|
|
265
338
|
```javascript
|
|
266
339
|
const message = await testdriver.find('notification message');
|
|
267
|
-
|
|
340
|
+
|
|
341
|
+
if (message.text) {
|
|
342
|
+
console.log('Message says:', message.text);
|
|
343
|
+
|
|
344
|
+
// Use text content in assertions
|
|
345
|
+
if (message.text.includes('success')) {
|
|
346
|
+
console.log('Success message detected');
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Another example - extracting button label
|
|
351
|
+
const button = await testdriver.find('blue button');
|
|
352
|
+
console.log('Button text:', button.text); // "Submit"
|
|
268
353
|
```
|
|
269
354
|
|
|
270
|
-
####
|
|
355
|
+
#### label
|
|
271
356
|
|
|
272
|
-
|
|
357
|
+
Accessible label or name of the element (if available). Useful for verifying accessibility.
|
|
273
358
|
|
|
274
359
|
```javascript
|
|
275
|
-
element.
|
|
360
|
+
element.label
|
|
276
361
|
```
|
|
277
362
|
|
|
278
|
-
**Returns:** `
|
|
363
|
+
**Returns:** `string | null` - Accessible label
|
|
279
364
|
|
|
280
365
|
**Example:**
|
|
281
366
|
```javascript
|
|
282
|
-
const
|
|
283
|
-
|
|
284
|
-
|
|
367
|
+
const input = await testdriver.find('first input field');
|
|
368
|
+
|
|
369
|
+
if (input.label) {
|
|
370
|
+
console.log('Input label:', input.label); // "Email Address"
|
|
285
371
|
}
|
|
286
372
|
```
|
|
287
373
|
|
|
288
|
-
####
|
|
374
|
+
#### confidence
|
|
289
375
|
|
|
290
|
-
|
|
376
|
+
AI confidence score for the element match (0-1, where 1 is perfect confidence).
|
|
291
377
|
|
|
292
378
|
```javascript
|
|
293
|
-
element.
|
|
294
|
-
element.height
|
|
379
|
+
element.confidence
|
|
295
380
|
```
|
|
296
381
|
|
|
297
|
-
**Returns:** `number | null`
|
|
298
|
-
|
|
299
|
-
#### boundingBox
|
|
300
|
-
|
|
301
|
-
Get the complete bounding box information.
|
|
382
|
+
**Returns:** `number | null` - Confidence score between 0 and 1
|
|
302
383
|
|
|
384
|
+
**Example:**
|
|
303
385
|
```javascript
|
|
304
|
-
element.
|
|
305
|
-
```
|
|
386
|
+
const element = await testdriver.find('submit button');
|
|
306
387
|
|
|
307
|
-
|
|
388
|
+
if (element.confidence !== null) {
|
|
389
|
+
const percentage = (element.confidence * 100).toFixed(1);
|
|
390
|
+
console.log(`Match confidence: ${percentage}%`);
|
|
391
|
+
|
|
392
|
+
if (element.confidence < 0.8) {
|
|
393
|
+
console.warn('⚠️ Low confidence match - element might not be correct');
|
|
394
|
+
} else if (element.confidence > 0.95) {
|
|
395
|
+
console.log('✅ High confidence match');
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
```
|
|
308
399
|
|
|
309
|
-
|
|
400
|
+
<Tip>
|
|
401
|
+
Confidence scores below 0.8 may indicate the element description was ambiguous or the wrong element was found.
|
|
402
|
+
</Tip>
|
|
310
403
|
|
|
311
|
-
|
|
404
|
+
### Property Availability
|
|
312
405
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
406
|
+
| Property | When Available |
|
|
407
|
+
|----------|---------------|
|
|
408
|
+
| `x`, `y`, `centerX`, `centerY` | ✅ Always after successful `find()` |
|
|
409
|
+
| `coordinates` | ✅ Always after successful `find()` |
|
|
410
|
+
| `width`, `height` | ⚠️ When AI detects element bounds |
|
|
411
|
+
| `boundingBox` | ⚠️ When AI detects element bounds |
|
|
412
|
+
| `text` | ⚠️ When AI extracts text content |
|
|
413
|
+
| `label` | ⚠️ When element has accessible label |
|
|
414
|
+
| `confidence` | ✅ Always after AI element finding |
|
|
415
|
+
| `screenshot` | ⚠️ Only in DEBUG mode or on errors |
|
|
316
416
|
|
|
317
|
-
|
|
417
|
+
<Note>
|
|
418
|
+
Properties marked with ⚠️ may be `null` depending on what the AI could detect from the screenshot.
|
|
419
|
+
</Note>
|
|
318
420
|
|
|
319
421
|
## Polling for Elements
|
|
320
422
|
|
package/docs/v7/api/find.mdx
CHANGED
|
@@ -314,3 +314,261 @@ describe('Element Finding', () => {
|
|
|
314
314
|
- [`hover()`](/v7/api/hover) - Hover over elements
|
|
315
315
|
- [`assert()`](/v7/api/assert) - Verify element states
|
|
316
316
|
- [Elements Reference](/v7/api/elements) - Complete Element API
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## findAll()
|
|
321
|
+
|
|
322
|
+
Locate **all elements** matching a description, rather than just one.
|
|
323
|
+
|
|
324
|
+
### Syntax
|
|
325
|
+
|
|
326
|
+
```javascript
|
|
327
|
+
const elements = await testdriver.findAll(description, options)
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Parameters
|
|
331
|
+
|
|
332
|
+
<ParamField path="description" type="string" required>
|
|
333
|
+
Natural language description of elements to find
|
|
334
|
+
</ParamField>
|
|
335
|
+
|
|
336
|
+
<ParamField path="options" type="object | number">
|
|
337
|
+
Optional cache options (same as `find()`)
|
|
338
|
+
|
|
339
|
+
<Expandable title="properties">
|
|
340
|
+
<ParamField path="cacheKey" type="string">
|
|
341
|
+
Cache key for storing element location
|
|
342
|
+
</ParamField>
|
|
343
|
+
|
|
344
|
+
<ParamField path="cacheThreshold" type="number" default={-1}>
|
|
345
|
+
Similarity threshold (0-1) for cache matching. Set to -1 to disable cache.
|
|
346
|
+
</ParamField>
|
|
347
|
+
</Expandable>
|
|
348
|
+
</ParamField>
|
|
349
|
+
|
|
350
|
+
### Returns
|
|
351
|
+
|
|
352
|
+
`Promise<Element[]>` - Array of Element instances
|
|
353
|
+
|
|
354
|
+
### Examples
|
|
355
|
+
|
|
356
|
+
#### Basic Usage
|
|
357
|
+
|
|
358
|
+
```javascript
|
|
359
|
+
// Find all matching elements
|
|
360
|
+
const buttons = await testdriver.findAll('button');
|
|
361
|
+
console.log(`Found ${buttons.length} buttons`);
|
|
362
|
+
|
|
363
|
+
// Interact with specific element
|
|
364
|
+
if (buttons.length > 0) {
|
|
365
|
+
await buttons[0].click(); // Click first button
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// Iterate over all
|
|
369
|
+
for (const button of buttons) {
|
|
370
|
+
console.log(`Button at (${button.x}, ${button.y})`);
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
#### Finding Multiple Items
|
|
375
|
+
|
|
376
|
+
```javascript
|
|
377
|
+
// Find all list items
|
|
378
|
+
const items = await testdriver.findAll('list item');
|
|
379
|
+
|
|
380
|
+
// Find specific item by index
|
|
381
|
+
const thirdItem = items[2];
|
|
382
|
+
await thirdItem.click();
|
|
383
|
+
|
|
384
|
+
// Check all items
|
|
385
|
+
for (let i = 0; i < items.length; i++) {
|
|
386
|
+
console.log(`Item ${i + 1}: ${items[i].text || 'No text'}`);
|
|
387
|
+
}
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
#### With Caching
|
|
391
|
+
|
|
392
|
+
```javascript
|
|
393
|
+
// Cache element locations for faster subsequent runs
|
|
394
|
+
const menuItems = await testdriver.findAll('menu item', {
|
|
395
|
+
cacheKey: 'main-menu-items'
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// First run: ~2-3 seconds (AI call)
|
|
399
|
+
// Subsequent runs: ~100ms (cache hit)
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
#### Empty Results
|
|
403
|
+
|
|
404
|
+
```javascript
|
|
405
|
+
// Returns empty array if nothing found (doesn't throw error)
|
|
406
|
+
const errors = await testdriver.findAll('error message');
|
|
407
|
+
|
|
408
|
+
if (errors.length === 0) {
|
|
409
|
+
console.log('No errors found - test passed!');
|
|
410
|
+
} else {
|
|
411
|
+
console.log(`Found ${errors.length} errors`);
|
|
412
|
+
}
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
### Differences from find()
|
|
416
|
+
|
|
417
|
+
| Feature | find() | findAll() |
|
|
418
|
+
|---------|--------|-----------|
|
|
419
|
+
| Return type | Single `Element` | Array of `Element[]` |
|
|
420
|
+
| If nothing found | Throws `ElementNotFoundError` | Returns empty array `[]` |
|
|
421
|
+
| Chainable | ✅ Yes: `await find('button').click()` | ❌ No (returns array) |
|
|
422
|
+
| Use case | One specific element | Multiple similar elements |
|
|
423
|
+
| Cache support | ✅ Yes | ✅ Yes |
|
|
424
|
+
|
|
425
|
+
### Use Cases
|
|
426
|
+
|
|
427
|
+
<AccordionGroup>
|
|
428
|
+
<Accordion title="Table Rows">
|
|
429
|
+
```javascript
|
|
430
|
+
// Find all rows in a table
|
|
431
|
+
const rows = await testdriver.findAll('table row');
|
|
432
|
+
|
|
433
|
+
// Click every row
|
|
434
|
+
for (const row of rows) {
|
|
435
|
+
await row.click();
|
|
436
|
+
await new Promise(r => setTimeout(r, 500)); // Wait between clicks
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Or click specific row
|
|
440
|
+
await rows[2].click(); // Click third row
|
|
441
|
+
```
|
|
442
|
+
</Accordion>
|
|
443
|
+
|
|
444
|
+
<Accordion title="Checkboxes/Radio Buttons">
|
|
445
|
+
```javascript
|
|
446
|
+
// Find all checkboxes
|
|
447
|
+
const checkboxes = await testdriver.findAll('checkbox');
|
|
448
|
+
|
|
449
|
+
// Check all boxes
|
|
450
|
+
for (const checkbox of checkboxes) {
|
|
451
|
+
await checkbox.click();
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// Or select first unchecked
|
|
455
|
+
const unchecked = checkboxes[0];
|
|
456
|
+
await unchecked.click();
|
|
457
|
+
```
|
|
458
|
+
</Accordion>
|
|
459
|
+
|
|
460
|
+
<Accordion title="Navigation Links">
|
|
461
|
+
```javascript
|
|
462
|
+
// Find all navigation links
|
|
463
|
+
const navLinks = await testdriver.findAll('navigation link');
|
|
464
|
+
|
|
465
|
+
// Validate all are present
|
|
466
|
+
expect(navLinks.length).toBeGreaterThan(0);
|
|
467
|
+
|
|
468
|
+
// Click specific link by text
|
|
469
|
+
const homeLink = navLinks.find(link =>
|
|
470
|
+
link.text?.toLowerCase().includes('home')
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
if (homeLink) {
|
|
474
|
+
await homeLink.click();
|
|
475
|
+
}
|
|
476
|
+
```
|
|
477
|
+
</Accordion>
|
|
478
|
+
|
|
479
|
+
<Accordion title="Conditional Interactions">
|
|
480
|
+
```javascript
|
|
481
|
+
// Check if any error messages exist
|
|
482
|
+
const errors = await testdriver.findAll('error message');
|
|
483
|
+
|
|
484
|
+
if (errors.length > 0) {
|
|
485
|
+
console.log(`Found ${errors.length} validation errors`);
|
|
486
|
+
|
|
487
|
+
// Log each error location
|
|
488
|
+
errors.forEach((error, i) => {
|
|
489
|
+
console.log(`Error ${i + 1} at (${error.x}, ${error.y})`);
|
|
490
|
+
});
|
|
491
|
+
} else {
|
|
492
|
+
console.log('Form validation passed!');
|
|
493
|
+
}
|
|
494
|
+
```
|
|
495
|
+
</Accordion>
|
|
496
|
+
</AccordionGroup>
|
|
497
|
+
|
|
498
|
+
### Complete Example
|
|
499
|
+
|
|
500
|
+
```javascript
|
|
501
|
+
import { test, expect } from 'vitest';
|
|
502
|
+
import { chrome } from 'testdriverai/presets';
|
|
503
|
+
|
|
504
|
+
test('select multiple items from list', async (context) => {
|
|
505
|
+
const { testdriver } = await chrome(context, {
|
|
506
|
+
url: 'https://example.com/products'
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
// Find all product cards
|
|
510
|
+
const products = await testdriver.findAll('product card');
|
|
511
|
+
|
|
512
|
+
expect(products.length).toBeGreaterThan(0);
|
|
513
|
+
console.log(`Found ${products.length} products`);
|
|
514
|
+
|
|
515
|
+
// Click first 3 products
|
|
516
|
+
const productsToSelect = Math.min(3, products.length);
|
|
517
|
+
|
|
518
|
+
for (let i = 0; i < productsToSelect; i++) {
|
|
519
|
+
await products[i].click();
|
|
520
|
+
console.log(`Selected product ${i + 1}`);
|
|
521
|
+
await new Promise(r => setTimeout(r, 500)); // Brief pause
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Verify selections
|
|
525
|
+
const selectedBadges = await testdriver.findAll('selected badge');
|
|
526
|
+
expect(selectedBadges.length).toBe(productsToSelect);
|
|
527
|
+
});
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
### Best Practices
|
|
531
|
+
|
|
532
|
+
<Check>
|
|
533
|
+
**Handle empty arrays gracefully**
|
|
534
|
+
|
|
535
|
+
```javascript
|
|
536
|
+
// ✅ Good - check length first
|
|
537
|
+
const items = await testdriver.findAll('list item');
|
|
538
|
+
if (items.length > 0) {
|
|
539
|
+
await items[0].click();
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// ❌ Bad - may throw error
|
|
543
|
+
const items = await testdriver.findAll('list item');
|
|
544
|
+
await items[0].click(); // Error if array is empty!
|
|
545
|
+
```
|
|
546
|
+
</Check>
|
|
547
|
+
|
|
548
|
+
<Check>
|
|
549
|
+
**Use find() for single elements**
|
|
550
|
+
|
|
551
|
+
```javascript
|
|
552
|
+
// ✅ Use find() when you need exactly one
|
|
553
|
+
const submitBtn = await testdriver.find('submit button');
|
|
554
|
+
await submitBtn.click();
|
|
555
|
+
|
|
556
|
+
// ❌ Unnecessary - findAll() returns array
|
|
557
|
+
const buttons = await testdriver.findAll('submit button');
|
|
558
|
+
await buttons[0].click(); // Extra array handling
|
|
559
|
+
```
|
|
560
|
+
</Check>
|
|
561
|
+
|
|
562
|
+
<Check>
|
|
563
|
+
**Cache for performance**
|
|
564
|
+
|
|
565
|
+
```javascript
|
|
566
|
+
// First run - slow (AI call)
|
|
567
|
+
const items = await testdriver.findAll('menu item', {
|
|
568
|
+
cacheKey: 'menu-items'
|
|
569
|
+
});
|
|
570
|
+
|
|
571
|
+
// Subsequent runs - fast (cache hit)
|
|
572
|
+
// ~10-20x faster than without cache
|
|
573
|
+
```
|
|
574
|
+
</Check>
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "mouseDown"
|
|
3
|
+
description: "Press the mouse button without releasing it"
|
|
4
|
+
icon: "arrow-pointer"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
The `mouseDown()` method presses the mouse button at an element's location without releasing it. This is useful for drag operations, custom gestures, or when you need precise control over mouse events. You can either call it on an [`Element`](/v7/core-concepts/elements) instance or use it directly with a selector.
|
|
10
|
+
|
|
11
|
+
## Syntax
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
// Mouse down on an element
|
|
15
|
+
await element.mouseDown();
|
|
16
|
+
|
|
17
|
+
// Mouse down using a selector
|
|
18
|
+
await ai.mouseDown('selector');
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Parameters
|
|
22
|
+
|
|
23
|
+
When called on an `Element`, no parameters are required.
|
|
24
|
+
|
|
25
|
+
When called directly on the AI client:
|
|
26
|
+
|
|
27
|
+
| Parameter | Type | Description |
|
|
28
|
+
|-----------|------|-------------|
|
|
29
|
+
| `selector` | `string` | The selector describing the element where the mouse button should be pressed |
|
|
30
|
+
|
|
31
|
+
## Returns
|
|
32
|
+
|
|
33
|
+
Returns a `Promise<void>` that resolves when the mouse button is pressed.
|
|
34
|
+
|
|
35
|
+
## Examples
|
|
36
|
+
|
|
37
|
+
### Basic Drag Operation
|
|
38
|
+
|
|
39
|
+
```javascript
|
|
40
|
+
// Start dragging an item
|
|
41
|
+
const dragItem = await ai.find('file to drag');
|
|
42
|
+
await dragItem.mouseDown();
|
|
43
|
+
|
|
44
|
+
// Move to drop target
|
|
45
|
+
const dropTarget = await ai.find('folder to drop into');
|
|
46
|
+
await dropTarget.hover();
|
|
47
|
+
|
|
48
|
+
// Release
|
|
49
|
+
await ai.mouseUp();
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Drag and Drop with Direct Selectors
|
|
53
|
+
|
|
54
|
+
```javascript
|
|
55
|
+
await ai.mouseDown('draggable card');
|
|
56
|
+
await ai.hover('drop zone');
|
|
57
|
+
await ai.mouseUp();
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Selecting Multiple Items
|
|
61
|
+
|
|
62
|
+
```javascript
|
|
63
|
+
import { test } from 'vitest';
|
|
64
|
+
import { chrome } from '@testdriver/sdk';
|
|
65
|
+
|
|
66
|
+
test('selects multiple items with click and drag', async () => {
|
|
67
|
+
const { ai } = await chrome('https://app.example.com');
|
|
68
|
+
|
|
69
|
+
// Start selection at first item
|
|
70
|
+
await ai.mouseDown('first item in grid');
|
|
71
|
+
|
|
72
|
+
// Drag to last item
|
|
73
|
+
await ai.hover('last item in grid');
|
|
74
|
+
|
|
75
|
+
// Release to complete selection
|
|
76
|
+
await ai.mouseUp();
|
|
77
|
+
|
|
78
|
+
// Verify multiple items selected
|
|
79
|
+
const selectedItems = await ai.find('selected items count');
|
|
80
|
+
expect(selectedItems.text).toContain('5 items selected');
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Custom Drawing Application
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
test('draws on canvas', async () => {
|
|
88
|
+
const { ai } = await chrome('https://drawing-app.example.com');
|
|
89
|
+
|
|
90
|
+
// Start drawing
|
|
91
|
+
await ai.mouseDown('canvas at top-left corner');
|
|
92
|
+
|
|
93
|
+
// Draw a line by moving mouse
|
|
94
|
+
await ai.hover('canvas at center');
|
|
95
|
+
await ai.hover('canvas at bottom-right corner');
|
|
96
|
+
|
|
97
|
+
// Stop drawing
|
|
98
|
+
await ai.mouseUp();
|
|
99
|
+
|
|
100
|
+
// Verify something was drawn
|
|
101
|
+
const canvas = await ai.exec('document.querySelector("canvas").toDataURL()');
|
|
102
|
+
expect(canvas).toBeTruthy();
|
|
103
|
+
});
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Long Press Gesture
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
test('triggers long press menu', async () => {
|
|
110
|
+
const { ai } = await chrome('https://mobile-app.example.com');
|
|
111
|
+
|
|
112
|
+
// Press and hold
|
|
113
|
+
await ai.mouseDown('message in chat');
|
|
114
|
+
|
|
115
|
+
// Wait for long-press menu
|
|
116
|
+
await new Promise(resolve => setTimeout(resolve, 500));
|
|
117
|
+
|
|
118
|
+
// Verify menu appeared before releasing
|
|
119
|
+
const menu = await ai.find('message options menu');
|
|
120
|
+
expect(menu).toBeTruthy();
|
|
121
|
+
|
|
122
|
+
// Release
|
|
123
|
+
await ai.mouseUp();
|
|
124
|
+
});
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Resizing UI Elements
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
test('resizes panel', async () => {
|
|
131
|
+
const { ai } = await vscode();
|
|
132
|
+
|
|
133
|
+
// Grab resize handle
|
|
134
|
+
await ai.mouseDown('sidebar resize handle');
|
|
135
|
+
|
|
136
|
+
// Drag to new position
|
|
137
|
+
await ai.hover('position 300 pixels from left edge');
|
|
138
|
+
|
|
139
|
+
// Release
|
|
140
|
+
await ai.mouseUp();
|
|
141
|
+
|
|
142
|
+
// Verify new size
|
|
143
|
+
const sidebar = await ai.find('sidebar');
|
|
144
|
+
expect(sidebar.width).toBeGreaterThan(250);
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Important Notes
|
|
149
|
+
|
|
150
|
+
- Always pair `mouseDown()` with [`mouseUp()`](/v7/api/mouseUp) to complete the gesture
|
|
151
|
+
- The mouse button remains pressed until `mouseUp()` is called
|
|
152
|
+
- Use [`hover()`](/v7/api/hover) to move the mouse while the button is pressed
|
|
153
|
+
- For simple drag operations, consider using `ai()` with a natural language description like `"drag file to folder"`
|
|
154
|
+
|
|
155
|
+
## Related Methods
|
|
156
|
+
|
|
157
|
+
- [`mouseUp()`](/v7/api/mouseUp) - Release the mouse button
|
|
158
|
+
- [`hover()`](/v7/api/hover) - Move mouse to element
|
|
159
|
+
- [`click()`](/v7/api/click) - Full click (mouseDown + mouseUp)
|
|
160
|
+
- [`doubleClick()`](/v7/api/doubleClick) - Double-click on element
|
|
161
|
+
- [`rightClick()`](/v7/api/rightClick) - Right-click for context menu
|