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.
Files changed (324) hide show
  1. package/.env.example +2 -0
  2. package/.github/workflows/linux-tests.yml +28 -0
  3. package/README.md +126 -0
  4. package/agent/index.js +7 -9
  5. package/agent/interface.js +13 -2
  6. package/agent/lib/commands.js +795 -136
  7. package/agent/lib/redraw.js +124 -39
  8. package/agent/lib/sandbox.js +40 -3
  9. package/agent/lib/sdk.js +21 -0
  10. package/agent/lib/valid-version.js +2 -2
  11. package/debugger/index.html +1 -1
  12. package/docs/docs.json +86 -71
  13. package/docs/guide/best-practices-polling.mdx +154 -0
  14. package/docs/v6/getting-started/self-hosting.mdx +3 -2
  15. package/docs/v7/_drafts/agents.mdx +852 -0
  16. package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
  17. package/docs/v7/_drafts/best-practices.mdx +486 -0
  18. package/docs/v7/_drafts/caching-ai.mdx +215 -0
  19. package/docs/v7/_drafts/caching-selectors.mdx +400 -0
  20. package/docs/v7/_drafts/caching.mdx +366 -0
  21. package/docs/v7/_drafts/cli-to-sdk-migration.mdx +425 -0
  22. package/docs/v7/_drafts/core.mdx +459 -0
  23. package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
  24. package/docs/v7/_drafts/debugging.mdx +349 -0
  25. package/docs/v7/_drafts/error-handling.mdx +501 -0
  26. package/docs/v7/_drafts/faq.mdx +393 -0
  27. package/docs/v7/_drafts/hooks.mdx +360 -0
  28. package/docs/v7/_drafts/implementation-plan.mdx +994 -0
  29. package/docs/v7/_drafts/init-command.mdx +95 -0
  30. package/docs/v7/_drafts/optimal-sdk-design.mdx +1348 -0
  31. package/docs/v7/_drafts/performance.mdx +517 -0
  32. package/docs/v7/_drafts/presets.mdx +210 -0
  33. package/docs/v7/_drafts/progressive-disclosure.mdx +230 -0
  34. package/docs/v7/_drafts/provision.mdx +266 -0
  35. package/docs/{QUICK_START_TEST_RECORDING.md → v7/_drafts/quick-start-test-recording.mdx} +3 -3
  36. package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
  37. package/docs/v7/{guides → _drafts}/self-hosting.mdx +1 -1
  38. package/docs/v7/_drafts/troubleshooting.mdx +526 -0
  39. package/docs/v7/_drafts/vitest-plugin.mdx +477 -0
  40. package/docs/v7/_drafts/vitest.mdx +535 -0
  41. package/docs/v7/api/{ai.mdx → act.mdx} +24 -24
  42. package/docs/v7/api/client.mdx +1 -1
  43. package/docs/v7/api/dashcam.mdx +497 -0
  44. package/docs/v7/api/doubleClick.mdx +102 -0
  45. package/docs/v7/api/elements.mdx +143 -41
  46. package/docs/v7/api/find.mdx +258 -0
  47. package/docs/v7/api/mouseDown.mdx +161 -0
  48. package/docs/v7/api/mouseUp.mdx +164 -0
  49. package/docs/v7/api/rightClick.mdx +123 -0
  50. package/docs/v7/api/type.mdx +51 -7
  51. package/docs/v7/features/ai-native.mdx +427 -0
  52. package/docs/v7/features/easy-to-write.mdx +351 -0
  53. package/docs/v7/features/enterprise.mdx +540 -0
  54. package/docs/v7/features/fast.mdx +424 -0
  55. package/docs/v7/features/observable.mdx +623 -0
  56. package/docs/v7/features/powerful.mdx +531 -0
  57. package/docs/v7/features/scalable.mdx +417 -0
  58. package/docs/v7/features/stable.mdx +514 -0
  59. package/docs/v7/getting-started/configuration.mdx +380 -0
  60. package/docs/v7/getting-started/generating-tests.mdx +525 -0
  61. package/docs/v7/getting-started/installation.mdx +486 -0
  62. package/docs/v7/getting-started/quickstart.mdx +320 -141
  63. package/docs/v7/getting-started/running-and-debugging.mdx +511 -0
  64. package/docs/v7/getting-started/setting-up-in-ci.mdx +612 -0
  65. package/docs/v7/getting-started/writing-tests.mdx +535 -0
  66. package/docs/v7/overview/what-is-testdriver.mdx +398 -0
  67. package/docs/v7/platforms/linux.mdx +308 -0
  68. package/docs/v7/platforms/macos.mdx +433 -0
  69. package/docs/v7/platforms/windows.mdx +430 -0
  70. package/docs/v7/playwright.mdx +3 -3
  71. package/docs/v7/presets/chrome-extension.mdx +223 -0
  72. package/docs/v7/presets/chrome.mdx +303 -0
  73. package/docs/v7/presets/electron.mdx +453 -0
  74. package/docs/v7/presets/vscode.mdx +417 -0
  75. package/docs/v7/presets/webapp.mdx +396 -0
  76. package/examples/run-tests-with-recording.sh +2 -2
  77. package/interfaces/cli/commands/init.js +358 -0
  78. package/interfaces/vitest-plugin.mjs +393 -103
  79. package/lib/core/Dashcam.js +506 -0
  80. package/lib/core/index.d.ts +150 -0
  81. package/lib/core/index.js +12 -0
  82. package/lib/presets/index.mjs +331 -0
  83. package/lib/vitest/hooks.d.ts +119 -0
  84. package/lib/vitest/hooks.mjs +316 -0
  85. package/lib/vitest/setup.mjs +44 -0
  86. package/package.json +13 -3
  87. package/sdk.d.ts +350 -44
  88. package/sdk.js +818 -105
  89. package/{self-hosted.yml → setup/aws/self-hosted.yml} +1 -1
  90. package/test/manual/test-console-logs.test.mjs +42 -0
  91. package/test/manual/test-init.sh +54 -0
  92. package/test/manual/test-provision-auth.mjs +22 -0
  93. package/test/testdriver/assert.test.mjs +41 -0
  94. package/test/testdriver/auto-cache-key-demo.test.mjs +56 -0
  95. package/test/testdriver/chrome-extension.test.mjs +89 -0
  96. package/{testdriver/acceptance-sdk → test/testdriver}/drag-and-drop.test.mjs +7 -19
  97. package/{testdriver/acceptance-sdk → test/testdriver}/element-not-found.test.mjs +6 -19
  98. package/{testdriver/acceptance-sdk → test/testdriver}/exec-js.test.mjs +6 -18
  99. package/{testdriver/acceptance-sdk → test/testdriver}/exec-output.test.mjs +9 -21
  100. package/{testdriver/acceptance-sdk → test/testdriver}/exec-pwsh.test.mjs +14 -26
  101. package/{testdriver/acceptance-sdk → test/testdriver}/focus-window.test.mjs +8 -20
  102. package/{testdriver/acceptance-sdk → test/testdriver}/formatted-logging.test.mjs +5 -20
  103. package/{testdriver/acceptance-sdk → test/testdriver}/hover-image.test.mjs +10 -19
  104. package/{testdriver/acceptance-sdk → test/testdriver}/hover-text-with-description.test.mjs +7 -19
  105. package/{testdriver/acceptance-sdk → test/testdriver}/hover-text.test.mjs +5 -19
  106. package/{testdriver/acceptance-sdk → test/testdriver}/match-image.test.mjs +7 -19
  107. package/{testdriver/acceptance-sdk → test/testdriver}/press-keys.test.mjs +5 -19
  108. package/{testdriver/acceptance-sdk → test/testdriver}/prompt.test.mjs +7 -19
  109. package/{testdriver/acceptance-sdk → test/testdriver}/scroll-keyboard.test.mjs +6 -20
  110. package/{testdriver/acceptance-sdk → test/testdriver}/scroll-until-image.test.mjs +6 -18
  111. package/test/testdriver/scroll-until-text.test.mjs +28 -0
  112. package/{testdriver/acceptance-sdk → test/testdriver}/scroll.test.mjs +12 -21
  113. package/test/testdriver/setup/lifecycleHelpers.mjs +262 -0
  114. package/{testdriver/acceptance-sdk → test/testdriver}/setup/testHelpers.mjs +25 -20
  115. package/test/testdriver/type.test.mjs +45 -0
  116. package/vitest.config.mjs +11 -56
  117. package/.github/dependabot.yml +0 -11
  118. package/.github/workflows/acceptance-linux.yml +0 -75
  119. package/.github/workflows/acceptance-sdk-tests.yml +0 -133
  120. package/.github/workflows/acceptance-tests.yml +0 -130
  121. package/.github/workflows/lint.yml +0 -27
  122. package/.github/workflows/publish-canary.yml +0 -40
  123. package/.github/workflows/publish-latest.yml +0 -61
  124. package/.github/workflows/test-install.yml +0 -29
  125. package/.vscode/extensions.json +0 -3
  126. package/.vscode/launch.json +0 -22
  127. package/.vscode/mcp.json +0 -9
  128. package/.vscode/settings.json +0 -14
  129. package/CODEOWNERS +0 -3
  130. package/MIGRATION.md +0 -389
  131. package/SDK_README.md +0 -1122
  132. package/_testdriver/acceptance/assert.yaml +0 -7
  133. package/_testdriver/acceptance/dashcam.yaml +0 -9
  134. package/_testdriver/acceptance/drag-and-drop.yaml +0 -49
  135. package/_testdriver/acceptance/embed.yaml +0 -9
  136. package/_testdriver/acceptance/exec-js.yaml +0 -29
  137. package/_testdriver/acceptance/exec-output.yaml +0 -43
  138. package/_testdriver/acceptance/exec-shell.yaml +0 -40
  139. package/_testdriver/acceptance/focus-window.yaml +0 -16
  140. package/_testdriver/acceptance/hover-image.yaml +0 -18
  141. package/_testdriver/acceptance/hover-text-with-description.yaml +0 -29
  142. package/_testdriver/acceptance/hover-text.yaml +0 -14
  143. package/_testdriver/acceptance/if-else.yaml +0 -31
  144. package/_testdriver/acceptance/match-image.yaml +0 -15
  145. package/_testdriver/acceptance/press-keys.yaml +0 -35
  146. package/_testdriver/acceptance/prompt.yaml +0 -11
  147. package/_testdriver/acceptance/remember.yaml +0 -27
  148. package/_testdriver/acceptance/screenshots/cart.png +0 -0
  149. package/_testdriver/acceptance/scroll-keyboard.yaml +0 -34
  150. package/_testdriver/acceptance/scroll-until-image.yaml +0 -26
  151. package/_testdriver/acceptance/scroll-until-text.yaml +0 -20
  152. package/_testdriver/acceptance/scroll.yaml +0 -33
  153. package/_testdriver/acceptance/snippets/login.yaml +0 -29
  154. package/_testdriver/acceptance/snippets/match-cart.yaml +0 -8
  155. package/_testdriver/acceptance/type.yaml +0 -29
  156. package/_testdriver/behavior/failure.yaml +0 -7
  157. package/_testdriver/behavior/hover-text.yaml +0 -13
  158. package/_testdriver/behavior/lifecycle/postrun.yaml +0 -10
  159. package/_testdriver/behavior/lifecycle/prerun.yaml +0 -8
  160. package/_testdriver/behavior/lifecycle/provision.yaml +0 -8
  161. package/_testdriver/behavior/secrets.yaml +0 -7
  162. package/_testdriver/edge-cases/dashcam-chrome.yaml +0 -8
  163. package/_testdriver/edge-cases/exec-pwsh-multiline.yaml +0 -10
  164. package/_testdriver/edge-cases/js-exception.yaml +0 -8
  165. package/_testdriver/edge-cases/js-promise.yaml +0 -19
  166. package/_testdriver/edge-cases/lifecycle/postrun.yaml +0 -10
  167. package/_testdriver/edge-cases/prompt-in-middle.yaml +0 -23
  168. package/_testdriver/edge-cases/prompt-nested.yaml +0 -7
  169. package/_testdriver/edge-cases/success-test.yaml +0 -9
  170. package/_testdriver/examples/android/example.yaml +0 -12
  171. package/_testdriver/examples/android/lifecycle/postrun.yaml +0 -11
  172. package/_testdriver/examples/android/lifecycle/provision.yaml +0 -47
  173. package/_testdriver/examples/android/readme.md +0 -7
  174. package/_testdriver/examples/chrome-extension/lifecycle/provision.yaml +0 -74
  175. package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
  176. package/_testdriver/examples/desktop/lifecycle/provision.yaml +0 -64
  177. package/_testdriver/examples/vscode-extension/lifecycle/provision.yaml +0 -73
  178. package/_testdriver/examples/web/lifecycle/postrun.yaml +0 -7
  179. package/_testdriver/examples/web/lifecycle/prerun.yaml +0 -22
  180. package/_testdriver/lifecycle/postrun.yaml +0 -8
  181. package/_testdriver/lifecycle/prerun.yaml +0 -15
  182. package/_testdriver/lifecycle/provision.yaml +0 -25
  183. package/debug-screenshot-1763401388589.png +0 -0
  184. package/mcp-server/AI_GUIDELINES.md +0 -57
  185. package/scripts/view-test-results.mjs +0 -96
  186. package/styles/.vale-config/2-MDX.ini +0 -5
  187. package/styles/Microsoft/AMPM.yml +0 -9
  188. package/styles/Microsoft/Accessibility.yml +0 -30
  189. package/styles/Microsoft/Acronyms.yml +0 -64
  190. package/styles/Microsoft/Adverbs.yml +0 -272
  191. package/styles/Microsoft/Auto.yml +0 -11
  192. package/styles/Microsoft/Avoid.yml +0 -14
  193. package/styles/Microsoft/Contractions.yml +0 -50
  194. package/styles/Microsoft/Dashes.yml +0 -13
  195. package/styles/Microsoft/DateFormat.yml +0 -8
  196. package/styles/Microsoft/DateNumbers.yml +0 -40
  197. package/styles/Microsoft/DateOrder.yml +0 -8
  198. package/styles/Microsoft/Ellipses.yml +0 -9
  199. package/styles/Microsoft/FirstPerson.yml +0 -16
  200. package/styles/Microsoft/Foreign.yml +0 -13
  201. package/styles/Microsoft/Gender.yml +0 -8
  202. package/styles/Microsoft/GenderBias.yml +0 -42
  203. package/styles/Microsoft/GeneralURL.yml +0 -11
  204. package/styles/Microsoft/HeadingAcronyms.yml +0 -7
  205. package/styles/Microsoft/HeadingColons.yml +0 -8
  206. package/styles/Microsoft/HeadingPunctuation.yml +0 -13
  207. package/styles/Microsoft/Headings.yml +0 -28
  208. package/styles/Microsoft/Hyphens.yml +0 -14
  209. package/styles/Microsoft/Negative.yml +0 -13
  210. package/styles/Microsoft/Ordinal.yml +0 -13
  211. package/styles/Microsoft/OxfordComma.yml +0 -8
  212. package/styles/Microsoft/Passive.yml +0 -183
  213. package/styles/Microsoft/Percentages.yml +0 -7
  214. package/styles/Microsoft/Plurals.yml +0 -7
  215. package/styles/Microsoft/Quotes.yml +0 -7
  216. package/styles/Microsoft/RangeTime.yml +0 -13
  217. package/styles/Microsoft/Semicolon.yml +0 -8
  218. package/styles/Microsoft/SentenceLength.yml +0 -6
  219. package/styles/Microsoft/Spacing.yml +0 -8
  220. package/styles/Microsoft/Suspended.yml +0 -7
  221. package/styles/Microsoft/Terms.yml +0 -42
  222. package/styles/Microsoft/URLFormat.yml +0 -9
  223. package/styles/Microsoft/Units.yml +0 -16
  224. package/styles/Microsoft/Vocab.yml +0 -25
  225. package/styles/Microsoft/We.yml +0 -11
  226. package/styles/Microsoft/Wordiness.yml +0 -127
  227. package/styles/Microsoft/meta.json +0 -4
  228. package/styles/alex/Ablist.yml +0 -274
  229. package/styles/alex/Condescending.yml +0 -16
  230. package/styles/alex/Gendered.yml +0 -110
  231. package/styles/alex/LGBTQ.yml +0 -55
  232. package/styles/alex/OCD.yml +0 -10
  233. package/styles/alex/Press.yml +0 -12
  234. package/styles/alex/ProfanityLikely.yml +0 -1289
  235. package/styles/alex/ProfanityMaybe.yml +0 -282
  236. package/styles/alex/ProfanityUnlikely.yml +0 -251
  237. package/styles/alex/README.md +0 -27
  238. package/styles/alex/Race.yml +0 -85
  239. package/styles/alex/Suicide.yml +0 -26
  240. package/styles/alex/meta.json +0 -4
  241. package/styles/config/vocabularies/Docs/accept.txt +0 -47
  242. package/styles/config/vocabularies/Docs/reject.txt +0 -4
  243. package/styles/proselint/Airlinese.yml +0 -8
  244. package/styles/proselint/AnimalLabels.yml +0 -48
  245. package/styles/proselint/Annotations.yml +0 -9
  246. package/styles/proselint/Apologizing.yml +0 -8
  247. package/styles/proselint/Archaisms.yml +0 -52
  248. package/styles/proselint/But.yml +0 -8
  249. package/styles/proselint/Cliches.yml +0 -782
  250. package/styles/proselint/CorporateSpeak.yml +0 -30
  251. package/styles/proselint/Currency.yml +0 -5
  252. package/styles/proselint/Cursing.yml +0 -15
  253. package/styles/proselint/DateCase.yml +0 -7
  254. package/styles/proselint/DateMidnight.yml +0 -7
  255. package/styles/proselint/DateRedundancy.yml +0 -10
  256. package/styles/proselint/DateSpacing.yml +0 -7
  257. package/styles/proselint/DenizenLabels.yml +0 -52
  258. package/styles/proselint/Diacritical.yml +0 -95
  259. package/styles/proselint/GenderBias.yml +0 -45
  260. package/styles/proselint/GroupTerms.yml +0 -39
  261. package/styles/proselint/Hedging.yml +0 -8
  262. package/styles/proselint/Hyperbole.yml +0 -6
  263. package/styles/proselint/Jargon.yml +0 -11
  264. package/styles/proselint/LGBTOffensive.yml +0 -13
  265. package/styles/proselint/LGBTTerms.yml +0 -15
  266. package/styles/proselint/Malapropisms.yml +0 -8
  267. package/styles/proselint/Needless.yml +0 -358
  268. package/styles/proselint/Nonwords.yml +0 -38
  269. package/styles/proselint/Oxymorons.yml +0 -22
  270. package/styles/proselint/P-Value.yml +0 -6
  271. package/styles/proselint/RASSyndrome.yml +0 -30
  272. package/styles/proselint/README.md +0 -12
  273. package/styles/proselint/Skunked.yml +0 -13
  274. package/styles/proselint/Spelling.yml +0 -17
  275. package/styles/proselint/Typography.yml +0 -11
  276. package/styles/proselint/Uncomparables.yml +0 -50
  277. package/styles/proselint/Very.yml +0 -6
  278. package/styles/proselint/meta.json +0 -15
  279. package/styles/write-good/Cliches.yml +0 -702
  280. package/styles/write-good/E-Prime.yml +0 -32
  281. package/styles/write-good/Illusions.yml +0 -11
  282. package/styles/write-good/Passive.yml +0 -183
  283. package/styles/write-good/README.md +0 -27
  284. package/styles/write-good/So.yml +0 -5
  285. package/styles/write-good/ThereIs.yml +0 -6
  286. package/styles/write-good/TooWordy.yml +0 -221
  287. package/styles/write-good/Weasel.yml +0 -29
  288. package/styles/write-good/meta.json +0 -4
  289. package/test/mcp-example-test.yaml +0 -27
  290. package/test/test_parser.js +0 -47
  291. package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +0 -61
  292. package/testdriver/acceptance-sdk/README.md +0 -128
  293. package/testdriver/acceptance-sdk/TEST_REPORTING.md +0 -245
  294. package/testdriver/acceptance-sdk/assert.test.mjs +0 -44
  295. package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +0 -42
  296. package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +0 -239
  297. package/testdriver/acceptance-sdk/type-checking-demo.js +0 -49
  298. package/testdriver/acceptance-sdk/type.test.mjs +0 -84
  299. package/vale.ini +0 -18
  300. package/vitest.config.example.js +0 -19
  301. package/vitest.config.mjs.bak +0 -44
  302. /package/docs/{ARCHITECTURE.md → v7/_drafts/architecture.mdx} +0 -0
  303. /package/docs/{AWESOME_LOGS_QUICK_REF.md → v7/_drafts/awesome-logs-quick-ref.mdx} +0 -0
  304. /package/{CONTRIBUTING.md → docs/v7/_drafts/contributing.mdx} +0 -0
  305. /package/docs/v7/{guides → _drafts}/migration.mdx +0 -0
  306. /package/{PLUGIN_MIGRATION.md → docs/v7/_drafts/plugin-migration.mdx} +0 -0
  307. /package/{PROMPT_CACHE.md → docs/v7/_drafts/prompt-cache.mdx} +0 -0
  308. /package/docs/{SDK_AWESOME_LOGS.md → v7/_drafts/sdk-awesome-logs.mdx} +0 -0
  309. /package/docs/{sdk-browser-rendering.md → v7/_drafts/sdk-browser-rendering.mdx} +0 -0
  310. /package/{SDK_LOGGING.md → docs/v7/_drafts/sdk-logging.mdx} +0 -0
  311. /package/{SDK_MIGRATION.md → docs/v7/_drafts/sdk-migration.mdx} +0 -0
  312. /package/docs/{TEST_RECORDING.md → v7/_drafts/test-recording.mdx} +0 -0
  313. /package/docs/v7/{README.md → overview/readme.mdx} +0 -0
  314. /package/{debug-locate-response.js → test/manual/debug-locate-response.js} +0 -0
  315. /package/{test-find-api.js → test/manual/test-find-api.js} +0 -0
  316. /package/{test-prompt-cache.js → test/manual/test-prompt-cache.js} +0 -0
  317. /package/{test-sandbox-render.js → test/manual/test-sandbox-render.js} +0 -0
  318. /package/{test-sdk-methods.js → test/manual/test-sdk-methods.js} +0 -0
  319. /package/{test-sdk-refactor.js → test/manual/test-sdk-refactor.js} +0 -0
  320. /package/{test-stack-trace.mjs → test/manual/test-stack-trace.mjs} +0 -0
  321. /package/{verify-element-api.js → test/manual/verify-element-api.js} +0 -0
  322. /package/{verify-types.js → test/manual/verify-types.js} +0 -0
  323. /package/{testdriver/acceptance-sdk → test/testdriver}/setup/globalTeardown.mjs +0 -0
  324. /package/{testdriver/acceptance-sdk → test/testdriver}/setup/vitestSetup.mjs +0 -0
@@ -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 }` or `null`
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
- console.log(`Top-left: (${coords.x}, ${coords.y})`);
216
- console.log(`Center: (${coords.centerX}, ${coords.centerY})`);
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
- Get a base64-encoded screenshot of the element (if available).
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
- console.log('Element screenshot:', element.screenshot);
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
- Get text content from the element (if available).
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
- console.log('Message text:', message.text);
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
- #### confidence
355
+ #### label
271
356
 
272
- Get the AI's confidence score for the element match.
357
+ Accessible label or name of the element (if available). Useful for verifying accessibility.
273
358
 
274
359
  ```javascript
275
- element.confidence
360
+ element.label
276
361
  ```
277
362
 
278
- **Returns:** `number | null` - Confidence score (0-1)
363
+ **Returns:** `string | null` - Accessible label
279
364
 
280
365
  **Example:**
281
366
  ```javascript
282
- const element = await testdriver.find('submit button');
283
- if (element.confidence < 0.8) {
284
- console.warn('Low confidence match:', element.confidence);
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
- #### width, height
374
+ #### confidence
289
375
 
290
- Get element dimensions (if available).
376
+ AI confidence score for the element match (0-1, where 1 is perfect confidence).
291
377
 
292
378
  ```javascript
293
- element.width
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.boundingBox
305
- ```
386
+ const element = await testdriver.find('submit button');
306
387
 
307
- **Returns:** `Object | null` - Bounding box with coordinates and dimensions
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
- #### label
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
- Get the element's label or accessible name (if available).
404
+ ### Property Availability
312
405
 
313
- ```javascript
314
- element.label
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
- **Returns:** `string | null`
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
 
@@ -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