testdriverai 7.1.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 (325) hide show
  1. package/.env.example +2 -0
  2. package/.github/workflows/linux-tests.yml +28 -0
  3. package/agent/index.js +18 -45
  4. package/agent/interface.js +13 -2
  5. package/agent/lib/commands.js +1 -1
  6. package/agent/lib/redraw.js +1 -1
  7. package/agent/lib/sandbox.js +30 -2
  8. package/agent/lib/valid-version.js +2 -2
  9. package/debugger/index.html +1 -1
  10. package/docs/docs.json +86 -125
  11. package/docs/v6/getting-started/self-hosting.mdx +3 -2
  12. package/docs/v7/_drafts/agents.mdx +852 -0
  13. package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
  14. package/docs/v7/{guides → _drafts}/caching-selectors.mdx +125 -17
  15. package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
  16. package/docs/v7/_drafts/error-handling.mdx +501 -0
  17. package/docs/v7/_drafts/implementation-plan.mdx +994 -0
  18. package/docs/v7/_drafts/init-command.mdx +95 -0
  19. package/docs/v7/_drafts/optimal-sdk-design.mdx +1348 -0
  20. package/docs/v7/_drafts/plugin-migration.mdx +222 -0
  21. package/docs/v7/_drafts/prompt-cache.mdx +200 -0
  22. package/docs/{QUICK_START_TEST_RECORDING.md → v7/_drafts/quick-start-test-recording.mdx} +3 -3
  23. package/docs/v7/_drafts/sdk-logging.mdx +222 -0
  24. package/docs/v7/_drafts/sdk-migration.mdx +474 -0
  25. package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
  26. package/docs/v7/{guides → _drafts}/self-hosting.mdx +1 -1
  27. package/docs/v7/{guides → _drafts}/troubleshooting.mdx +2 -2
  28. package/docs/v7/{guides → _drafts}/vitest-plugin.mdx +4 -4
  29. package/docs/v7/api/{ai.mdx → act.mdx} +24 -24
  30. package/docs/v7/api/client.mdx +1 -1
  31. package/docs/v7/api/dashcam.mdx +2 -2
  32. package/docs/v7/api/elements.mdx +143 -41
  33. package/docs/v7/api/find.mdx +258 -0
  34. package/docs/v7/api/type.mdx +51 -7
  35. package/docs/v7/features/ai-native.mdx +427 -0
  36. package/docs/v7/features/easy-to-write.mdx +351 -0
  37. package/docs/v7/features/enterprise.mdx +540 -0
  38. package/docs/v7/features/fast.mdx +424 -0
  39. package/docs/v7/features/observable.mdx +623 -0
  40. package/docs/v7/features/powerful.mdx +531 -0
  41. package/docs/v7/features/scalable.mdx +417 -0
  42. package/docs/v7/features/stable.mdx +514 -0
  43. package/docs/v7/getting-started/configuration.mdx +1 -1
  44. package/docs/v7/getting-started/generating-tests.mdx +525 -0
  45. package/docs/v7/getting-started/installation.mdx +486 -0
  46. package/docs/v7/getting-started/quickstart.mdx +51 -5
  47. package/docs/v7/getting-started/running-and-debugging.mdx +511 -0
  48. package/docs/v7/getting-started/setting-up-in-ci.mdx +612 -0
  49. package/docs/v7/getting-started/writing-tests.mdx +535 -0
  50. package/docs/v7/overview/what-is-testdriver.mdx +398 -0
  51. package/docs/v7/playwright.mdx +3 -3
  52. package/docs/v7/presets/chrome.mdx +16 -0
  53. package/docs/v7/presets/electron.mdx +18 -0
  54. package/docs/v7/presets/vscode.mdx +19 -0
  55. package/examples/run-tests-with-recording.sh +70 -0
  56. package/examples/screenshot-example.js +63 -0
  57. package/examples/sdk-awesome-logs-demo.js +177 -0
  58. package/examples/sdk-cache-thresholds.js +96 -0
  59. package/examples/sdk-element-properties.js +155 -0
  60. package/examples/sdk-simple-example.js +65 -0
  61. package/examples/test-recording-example.test.js +166 -0
  62. package/interfaces/cli/commands/init.js +358 -0
  63. package/interfaces/vitest-plugin.mjs +214 -10
  64. package/{src → lib}/core/Dashcam.js +41 -4
  65. package/{src → lib}/vitest/hooks.mjs +118 -100
  66. package/lib/vitest/setup.mjs +44 -0
  67. package/package.json +9 -10
  68. package/sdk.d.ts +15 -2
  69. package/sdk.js +70 -18
  70. package/{self-hosted.yml → setup/aws/self-hosted.yml} +1 -1
  71. package/{testdriver/acceptance-sdk → test/manual}/test-console-logs.test.mjs +1 -1
  72. package/test/manual/test-find-api.js +73 -0
  73. package/test/manual/test-init.sh +54 -0
  74. package/test/manual/test-prompt-cache.js +96 -0
  75. package/test/manual/test-provision-auth.mjs +22 -0
  76. package/test/manual/test-sandbox-render.js +28 -0
  77. package/test/manual/test-sdk-methods.js +15 -0
  78. package/test/manual/test-sdk-refactor.js +53 -0
  79. package/test/manual/test-stack-trace.mjs +57 -0
  80. package/test/testdriver/assert.test.mjs +41 -0
  81. package/{testdriver/acceptance-sdk → test/testdriver}/auto-cache-key-demo.test.mjs +1 -1
  82. package/{testdriver/acceptance-sdk → test/testdriver}/drag-and-drop.test.mjs +1 -1
  83. package/{testdriver/acceptance-sdk → test/testdriver}/element-not-found.test.mjs +1 -1
  84. package/{testdriver/acceptance-sdk → test/testdriver}/exec-js.test.mjs +1 -1
  85. package/{testdriver/acceptance-sdk → test/testdriver}/exec-output.test.mjs +3 -3
  86. package/{testdriver/acceptance-sdk → test/testdriver}/exec-pwsh.test.mjs +3 -3
  87. package/{testdriver/acceptance-sdk → test/testdriver}/focus-window.test.mjs +1 -1
  88. package/{testdriver/acceptance-sdk → test/testdriver}/formatted-logging.test.mjs +1 -1
  89. package/{testdriver/acceptance-sdk → test/testdriver}/hover-image.test.mjs +1 -1
  90. package/{testdriver/acceptance-sdk → test/testdriver}/hover-text-with-description.test.mjs +1 -1
  91. package/{testdriver/acceptance-sdk → test/testdriver}/hover-text.test.mjs +1 -1
  92. package/{testdriver/acceptance-sdk → test/testdriver}/match-image.test.mjs +1 -1
  93. package/{testdriver/acceptance-sdk → test/testdriver}/press-keys.test.mjs +1 -1
  94. package/{testdriver/acceptance-sdk → test/testdriver}/prompt.test.mjs +2 -2
  95. package/{testdriver/acceptance-sdk → test/testdriver}/scroll-keyboard.test.mjs +1 -1
  96. package/{testdriver/acceptance-sdk → test/testdriver}/scroll-until-image.test.mjs +1 -1
  97. package/{testdriver/acceptance-sdk → test/testdriver}/scroll-until-text.test.mjs +1 -1
  98. package/{testdriver/acceptance-sdk → test/testdriver}/scroll.test.mjs +1 -1
  99. package/{src/vitest/lifecycle.mjs → test/testdriver/setup/lifecycleHelpers.mjs} +84 -99
  100. package/test/testdriver/setup/testHelpers.mjs +653 -0
  101. package/{testdriver/acceptance-sdk → test/testdriver}/type.test.mjs +1 -1
  102. package/vitest.config.mjs +11 -57
  103. package/.github/dependabot.yml +0 -11
  104. package/.github/workflows/acceptance-linux.yml +0 -75
  105. package/.github/workflows/acceptance-sdk-tests.yml +0 -133
  106. package/.github/workflows/acceptance-tests.yml +0 -130
  107. package/.github/workflows/lint.yml +0 -27
  108. package/.github/workflows/publish-canary.yml +0 -40
  109. package/.github/workflows/publish-latest.yml +0 -61
  110. package/.github/workflows/test-install.yml +0 -29
  111. package/.vscode/extensions.json +0 -3
  112. package/.vscode/launch.json +0 -22
  113. package/.vscode/settings.json +0 -14
  114. package/AGENTS.md +0 -550
  115. package/CODEOWNERS +0 -2
  116. package/_testdriver/acceptance/assert.yaml +0 -7
  117. package/_testdriver/acceptance/dashcam.yaml +0 -9
  118. package/_testdriver/acceptance/drag-and-drop.yaml +0 -49
  119. package/_testdriver/acceptance/embed.yaml +0 -9
  120. package/_testdriver/acceptance/exec-js.yaml +0 -29
  121. package/_testdriver/acceptance/exec-output.yaml +0 -43
  122. package/_testdriver/acceptance/exec-shell.yaml +0 -40
  123. package/_testdriver/acceptance/focus-window.yaml +0 -16
  124. package/_testdriver/acceptance/hover-image.yaml +0 -18
  125. package/_testdriver/acceptance/hover-text-with-description.yaml +0 -29
  126. package/_testdriver/acceptance/hover-text.yaml +0 -14
  127. package/_testdriver/acceptance/if-else.yaml +0 -31
  128. package/_testdriver/acceptance/match-image.yaml +0 -15
  129. package/_testdriver/acceptance/press-keys.yaml +0 -35
  130. package/_testdriver/acceptance/prompt.yaml +0 -11
  131. package/_testdriver/acceptance/remember.yaml +0 -27
  132. package/_testdriver/acceptance/screenshots/cart.png +0 -0
  133. package/_testdriver/acceptance/scroll-keyboard.yaml +0 -34
  134. package/_testdriver/acceptance/scroll-until-image.yaml +0 -26
  135. package/_testdriver/acceptance/scroll-until-text.yaml +0 -20
  136. package/_testdriver/acceptance/scroll.yaml +0 -33
  137. package/_testdriver/acceptance/snippets/login.yaml +0 -29
  138. package/_testdriver/acceptance/snippets/match-cart.yaml +0 -8
  139. package/_testdriver/acceptance/type.yaml +0 -29
  140. package/_testdriver/behavior/failure.yaml +0 -7
  141. package/_testdriver/behavior/hover-text.yaml +0 -13
  142. package/_testdriver/behavior/lifecycle/postrun.yaml +0 -10
  143. package/_testdriver/behavior/lifecycle/prerun.yaml +0 -8
  144. package/_testdriver/behavior/lifecycle/provision.yaml +0 -8
  145. package/_testdriver/behavior/secrets.yaml +0 -7
  146. package/_testdriver/edge-cases/dashcam-chrome.yaml +0 -8
  147. package/_testdriver/edge-cases/exec-pwsh-multiline.yaml +0 -10
  148. package/_testdriver/edge-cases/js-exception.yaml +0 -8
  149. package/_testdriver/edge-cases/js-promise.yaml +0 -19
  150. package/_testdriver/edge-cases/lifecycle/postrun.yaml +0 -10
  151. package/_testdriver/edge-cases/prompt-in-middle.yaml +0 -23
  152. package/_testdriver/edge-cases/prompt-nested.yaml +0 -7
  153. package/_testdriver/edge-cases/success-test.yaml +0 -9
  154. package/_testdriver/examples/android/example.yaml +0 -12
  155. package/_testdriver/examples/android/lifecycle/postrun.yaml +0 -11
  156. package/_testdriver/examples/android/lifecycle/provision.yaml +0 -47
  157. package/_testdriver/examples/android/readme.md +0 -7
  158. package/_testdriver/examples/chrome-extension/lifecycle/provision.yaml +0 -74
  159. package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
  160. package/_testdriver/examples/desktop/lifecycle/provision.yaml +0 -64
  161. package/_testdriver/examples/vscode-extension/lifecycle/provision.yaml +0 -73
  162. package/_testdriver/examples/web/lifecycle/postrun.yaml +0 -7
  163. package/_testdriver/examples/web/lifecycle/prerun.yaml +0 -22
  164. package/_testdriver/lifecycle/postrun.yaml +0 -8
  165. package/_testdriver/lifecycle/prerun.yaml +0 -15
  166. package/_testdriver/lifecycle/provision.yaml +0 -25
  167. package/docs/v7/guides/ci-cd/azure.mdx +0 -587
  168. package/docs/v7/guides/ci-cd/circleci.mdx +0 -523
  169. package/docs/v7/guides/ci-cd/github-actions.mdx +0 -457
  170. package/docs/v7/guides/ci-cd/gitlab.mdx +0 -498
  171. package/docs/v7/guides/ci-cd/jenkins.mdx +0 -664
  172. package/docs/v7/guides/ci-cd/travis.mdx +0 -438
  173. package/scripts/view-test-results.mjs +0 -96
  174. package/src/vitest/extended.mjs +0 -108
  175. package/src/vitest/index.mjs +0 -64
  176. package/src/vitest/utils.mjs +0 -150
  177. package/styles/.vale-config/2-MDX.ini +0 -5
  178. package/styles/Microsoft/AMPM.yml +0 -9
  179. package/styles/Microsoft/Accessibility.yml +0 -30
  180. package/styles/Microsoft/Acronyms.yml +0 -64
  181. package/styles/Microsoft/Adverbs.yml +0 -272
  182. package/styles/Microsoft/Auto.yml +0 -11
  183. package/styles/Microsoft/Avoid.yml +0 -14
  184. package/styles/Microsoft/Contractions.yml +0 -50
  185. package/styles/Microsoft/Dashes.yml +0 -13
  186. package/styles/Microsoft/DateFormat.yml +0 -8
  187. package/styles/Microsoft/DateNumbers.yml +0 -40
  188. package/styles/Microsoft/DateOrder.yml +0 -8
  189. package/styles/Microsoft/Ellipses.yml +0 -9
  190. package/styles/Microsoft/FirstPerson.yml +0 -16
  191. package/styles/Microsoft/Foreign.yml +0 -13
  192. package/styles/Microsoft/Gender.yml +0 -8
  193. package/styles/Microsoft/GenderBias.yml +0 -42
  194. package/styles/Microsoft/GeneralURL.yml +0 -11
  195. package/styles/Microsoft/HeadingAcronyms.yml +0 -7
  196. package/styles/Microsoft/HeadingColons.yml +0 -8
  197. package/styles/Microsoft/HeadingPunctuation.yml +0 -13
  198. package/styles/Microsoft/Headings.yml +0 -28
  199. package/styles/Microsoft/Hyphens.yml +0 -14
  200. package/styles/Microsoft/Negative.yml +0 -13
  201. package/styles/Microsoft/Ordinal.yml +0 -13
  202. package/styles/Microsoft/OxfordComma.yml +0 -8
  203. package/styles/Microsoft/Passive.yml +0 -183
  204. package/styles/Microsoft/Percentages.yml +0 -7
  205. package/styles/Microsoft/Plurals.yml +0 -7
  206. package/styles/Microsoft/Quotes.yml +0 -7
  207. package/styles/Microsoft/RangeTime.yml +0 -13
  208. package/styles/Microsoft/Semicolon.yml +0 -8
  209. package/styles/Microsoft/SentenceLength.yml +0 -6
  210. package/styles/Microsoft/Spacing.yml +0 -8
  211. package/styles/Microsoft/Suspended.yml +0 -7
  212. package/styles/Microsoft/Terms.yml +0 -42
  213. package/styles/Microsoft/URLFormat.yml +0 -9
  214. package/styles/Microsoft/Units.yml +0 -16
  215. package/styles/Microsoft/Vocab.yml +0 -25
  216. package/styles/Microsoft/We.yml +0 -11
  217. package/styles/Microsoft/Wordiness.yml +0 -127
  218. package/styles/Microsoft/meta.json +0 -4
  219. package/styles/alex/Ablist.yml +0 -274
  220. package/styles/alex/Condescending.yml +0 -16
  221. package/styles/alex/Gendered.yml +0 -110
  222. package/styles/alex/LGBTQ.yml +0 -55
  223. package/styles/alex/OCD.yml +0 -10
  224. package/styles/alex/Press.yml +0 -12
  225. package/styles/alex/ProfanityLikely.yml +0 -1289
  226. package/styles/alex/ProfanityMaybe.yml +0 -282
  227. package/styles/alex/ProfanityUnlikely.yml +0 -251
  228. package/styles/alex/README.md +0 -27
  229. package/styles/alex/Race.yml +0 -85
  230. package/styles/alex/Suicide.yml +0 -26
  231. package/styles/alex/meta.json +0 -4
  232. package/styles/config/vocabularies/Docs/accept.txt +0 -47
  233. package/styles/config/vocabularies/Docs/reject.txt +0 -4
  234. package/styles/proselint/Airlinese.yml +0 -8
  235. package/styles/proselint/AnimalLabels.yml +0 -48
  236. package/styles/proselint/Annotations.yml +0 -9
  237. package/styles/proselint/Apologizing.yml +0 -8
  238. package/styles/proselint/Archaisms.yml +0 -52
  239. package/styles/proselint/But.yml +0 -8
  240. package/styles/proselint/Cliches.yml +0 -782
  241. package/styles/proselint/CorporateSpeak.yml +0 -30
  242. package/styles/proselint/Currency.yml +0 -5
  243. package/styles/proselint/Cursing.yml +0 -15
  244. package/styles/proselint/DateCase.yml +0 -7
  245. package/styles/proselint/DateMidnight.yml +0 -7
  246. package/styles/proselint/DateRedundancy.yml +0 -10
  247. package/styles/proselint/DateSpacing.yml +0 -7
  248. package/styles/proselint/DenizenLabels.yml +0 -52
  249. package/styles/proselint/Diacritical.yml +0 -95
  250. package/styles/proselint/GenderBias.yml +0 -45
  251. package/styles/proselint/GroupTerms.yml +0 -39
  252. package/styles/proselint/Hedging.yml +0 -8
  253. package/styles/proselint/Hyperbole.yml +0 -6
  254. package/styles/proselint/Jargon.yml +0 -11
  255. package/styles/proselint/LGBTOffensive.yml +0 -13
  256. package/styles/proselint/LGBTTerms.yml +0 -15
  257. package/styles/proselint/Malapropisms.yml +0 -8
  258. package/styles/proselint/Needless.yml +0 -358
  259. package/styles/proselint/Nonwords.yml +0 -38
  260. package/styles/proselint/Oxymorons.yml +0 -22
  261. package/styles/proselint/P-Value.yml +0 -6
  262. package/styles/proselint/RASSyndrome.yml +0 -30
  263. package/styles/proselint/README.md +0 -12
  264. package/styles/proselint/Skunked.yml +0 -13
  265. package/styles/proselint/Spelling.yml +0 -17
  266. package/styles/proselint/Typography.yml +0 -11
  267. package/styles/proselint/Uncomparables.yml +0 -50
  268. package/styles/proselint/Very.yml +0 -6
  269. package/styles/proselint/meta.json +0 -15
  270. package/styles/write-good/Cliches.yml +0 -702
  271. package/styles/write-good/E-Prime.yml +0 -32
  272. package/styles/write-good/Illusions.yml +0 -11
  273. package/styles/write-good/Passive.yml +0 -183
  274. package/styles/write-good/README.md +0 -27
  275. package/styles/write-good/So.yml +0 -5
  276. package/styles/write-good/ThereIs.yml +0 -6
  277. package/styles/write-good/TooWordy.yml +0 -221
  278. package/styles/write-good/Weasel.yml +0 -29
  279. package/styles/write-good/meta.json +0 -4
  280. package/test/dashcam.test.js +0 -137
  281. package/test/mcp-example-test.yaml +0 -27
  282. package/test/test_parser.js +0 -47
  283. package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +0 -61
  284. package/testdriver/acceptance-sdk/README.md +0 -128
  285. package/testdriver/acceptance-sdk/TEST_REPORTING.md +0 -245
  286. package/testdriver/acceptance-sdk/assert.test.mjs +0 -26
  287. package/testdriver/acceptance-sdk/hooks-example.test.mjs +0 -38
  288. package/testdriver/acceptance-sdk/presets-example.test.mjs +0 -87
  289. package/testdriver/acceptance-sdk/setup/testHelpers.mjs +0 -420
  290. package/testdriver/acceptance-sdk/sully-ai.test.mjs +0 -234
  291. package/testdriver/acceptance-sdk/type-checking-demo.js +0 -49
  292. package/vale.ini +0 -18
  293. package/vitest.config.example.js +0 -19
  294. package/vitest.config.mjs.bak +0 -44
  295. /package/docs/{ARCHITECTURE.md → v7/_drafts/architecture.mdx} +0 -0
  296. /package/docs/{AWESOME_LOGS_QUICK_REF.md → v7/_drafts/awesome-logs-quick-ref.mdx} +0 -0
  297. /package/docs/v7/{guides → _drafts}/best-practices.mdx +0 -0
  298. /package/docs/v7/{guides → _drafts}/caching-ai.mdx +0 -0
  299. /package/docs/v7/{guides → _drafts}/caching.mdx +0 -0
  300. /package/docs/{MIGRATION.md → v7/_drafts/cli-to-sdk-migration.mdx} +0 -0
  301. /package/{CONTRIBUTING.md → docs/v7/_drafts/contributing.mdx} +0 -0
  302. /package/docs/v7/{progressive-apis/CORE.md → _drafts/core.mdx} +0 -0
  303. /package/docs/v7/{guides → _drafts}/debugging.mdx +0 -0
  304. /package/docs/v7/{guides → _drafts}/faq.mdx +0 -0
  305. /package/docs/v7/{progressive-apis/HOOKS.md → _drafts/hooks.mdx} +0 -0
  306. /package/docs/v7/{guides → _drafts}/migration.mdx +0 -0
  307. /package/docs/v7/{guides → _drafts}/performance.mdx +0 -0
  308. /package/docs/{PRESETS.md → v7/_drafts/presets.mdx} +0 -0
  309. /package/docs/v7/{progressive-apis/PROGRESSIVE_DISCLOSURE.md → _drafts/progressive-disclosure.mdx} +0 -0
  310. /package/docs/v7/{progressive-apis/PROVISION.md → _drafts/provision.mdx} +0 -0
  311. /package/docs/{SDK_AWESOME_LOGS.md → v7/_drafts/sdk-awesome-logs.mdx} +0 -0
  312. /package/docs/{sdk-browser-rendering.md → v7/_drafts/sdk-browser-rendering.mdx} +0 -0
  313. /package/docs/{TEST_RECORDING.md → v7/_drafts/test-recording.mdx} +0 -0
  314. /package/docs/v7/{guides → _drafts}/vitest.mdx +0 -0
  315. /package/docs/v7/{README.md → overview/readme.mdx} +0 -0
  316. /package/{src → lib}/core/index.d.ts +0 -0
  317. /package/{src → lib}/core/index.js +0 -0
  318. /package/{src → lib}/presets/index.mjs +0 -0
  319. /package/{src → lib}/vitest/hooks.d.ts +0 -0
  320. /package/{debug-locate-response.js → test/manual/debug-locate-response.js} +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}/chrome-extension.test.mjs +0 -0
  324. /package/{testdriver/acceptance-sdk → test/testdriver}/setup/globalTeardown.mjs +0 -0
  325. /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>
@@ -12,7 +12,7 @@ Type text or numbers into the currently focused input field with optional delay
12
12
  ## Syntax
13
13
 
14
14
  ```javascript
15
- await testdriver.type(text, delay)
15
+ await testdriver.type(text, options)
16
16
  ```
17
17
 
18
18
  ## Parameters
@@ -21,8 +21,18 @@ await testdriver.type(text, delay)
21
21
  Text to type (can be a string or number)
22
22
  </ParamField>
23
23
 
24
- <ParamField path="delay" type="number" default="250">
25
- Delay between keystrokes in milliseconds
24
+ <ParamField path="options" type="object | number">
25
+ Typing options (or legacy delay number)
26
+
27
+ <Expandable title="properties">
28
+ <ParamField path="delay" type="number" default={250}>
29
+ Delay between keystrokes in milliseconds
30
+ </ParamField>
31
+
32
+ <ParamField path="secret" type="boolean" default={false}>
33
+ If `true`, treats text as sensitive data (won't be logged or stored in debug info/dashcam)
34
+ </ParamField>
35
+ </Expandable>
26
36
  </ParamField>
27
37
 
28
38
  ## Returns
@@ -40,10 +50,44 @@ await testdriver.type('hello@example.com');
40
50
  // Type numbers
41
51
  await testdriver.type(12345);
42
52
 
43
- // Type with custom delay
53
+ // Type with custom delay (legacy syntax)
44
54
  await testdriver.type('slow typing', 500); // 500ms between each character
55
+
56
+ // Type with options object
57
+ await testdriver.type('text', { delay: 500 });
58
+ ```
59
+
60
+ ### Password/Secret Handling
61
+
62
+ ```javascript
63
+ // ✅ SECURE - Mark as secret to prevent logging
64
+ const passwordField = await testdriver.find('password input');
65
+ await passwordField.click();
66
+ await testdriver.type('MySecureP@ssw0rd', { secret: true });
67
+ // Password NOT logged in dashcam or debug output
68
+
69
+ // ❌ INSECURE - Password will be logged
70
+ await testdriver.type('MySecureP@ssw0rd');
71
+ // Password appears in logs, dashcam replay, and debug info
72
+
73
+ // Use secret for any sensitive data
74
+ await testdriver.find('api key input').click();
75
+ await testdriver.type('sk-1234567890abcdef', { secret: true });
76
+
77
+ await testdriver.find('credit card input').click();
78
+ await testdriver.type('4111111111111111', { secret: true });
45
79
  ```
46
80
 
81
+ <Warning>
82
+ **Always use `secret: true` for passwords and sensitive data!**
83
+
84
+ Without this option, typed text appears in:
85
+ - Dashcam video replays
86
+ - TestDriver logs
87
+ - Debug screenshots
88
+ - Error messages
89
+ </Warning>
90
+
47
91
  ### Form Filling
48
92
 
49
93
  ```javascript
@@ -56,9 +100,9 @@ await testdriver.type('user@example.com');
56
100
  await testdriver.pressKeys(['tab']);
57
101
  await testdriver.type('John Doe');
58
102
 
59
- // Type password
103
+ // Type password securely
60
104
  await testdriver.pressKeys(['tab']);
61
- await testdriver.type('MySecureP@ssw0rd');
105
+ await testdriver.type('MySecureP@ssw0rd', { secret: true });
62
106
  ```
63
107
 
64
108
  ### Clearing and Replacing Text
@@ -135,7 +179,7 @@ await testdriver.type('new search query');
135
179
  await testdriver.type('testuser@example.com');
136
180
 
137
181
  await testdriver.pressKeys(['tab']);
138
- await testdriver.type('MyP@ssword123');
182
+ await testdriver.type('MyP@ssword123', { secret: true });
139
183
 
140
184
  await testdriver.pressKeys(['enter']);
141
185
  ```