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
@@ -0,0 +1,852 @@
1
+ # TestDriver AI SDK - Agent Guide
2
+
3
+ This guide is designed for AI agents to understand how to use the TestDriver SDK for automated testing.
4
+
5
+ ## Table of Contents
6
+ - [Quick Setup](#quick-setup)
7
+ - [Authentication](#authentication)
8
+ - [Provision Methods](#provision-methods)
9
+ - [Core API Methods](#core-api-methods)
10
+ - [Reconnection and Debugging](#reconnection-and-debugging)
11
+ - [Best Practices](#best-practices)
12
+
13
+ ---
14
+
15
+ ## Quick Setup
16
+
17
+ ### 1. Install Dependencies
18
+
19
+ ```bash
20
+ npm install --save-dev testdriverai vitest
21
+ ```
22
+
23
+ ### 2. Configure Vitest Project
24
+
25
+ Create or update `vitest.config.mjs`:
26
+
27
+ ```javascript
28
+ import { defineConfig } from 'vitest/config';
29
+
30
+ export default defineConfig({
31
+ test: {
32
+ testTimeout: 120000, // 2 minutes (TestDriver tests can take longer)
33
+ hookTimeout: 120000,
34
+ },
35
+ });
36
+ ```
37
+
38
+ ### 3. Create Test File
39
+
40
+ Create a test file (e.g., `test.test.js`):
41
+
42
+ ```javascript
43
+ import { test } from 'vitest';
44
+ import { chrome } from 'testdriverai/presets';
45
+
46
+ test('my first test', async (context) => {
47
+ const { testdriver } = await chrome(context, {
48
+ url: 'https://example.com',
49
+ apiKey: process.env.TD_API_KEY
50
+ });
51
+
52
+ await testdriver.find('More information link').click();
53
+ await testdriver.assert('IANA page is visible');
54
+ });
55
+ ```
56
+
57
+ ---
58
+
59
+ ## Authentication
60
+
61
+ ### Getting an API Key
62
+
63
+ 1. Go to [console.testdriver.ai](https://console.testdriver.ai)
64
+ 2. Sign up or log in
65
+ 3. Navigate to your account settings
66
+ 4. Generate a new API key (format: `tdai-1234567890abcdef`)
67
+
68
+ ### Using API Keys
69
+
70
+ **Recommended: Environment Variables**
71
+
72
+ Create `.env` file (add to `.gitignore`):
73
+ ```bash
74
+ TD_API_KEY=tdai-1234567890abcdef
75
+ ```
76
+
77
+ Then use in tests:
78
+ ```javascript
79
+ const { testdriver } = await chrome(context, {
80
+ url: 'https://example.com'
81
+ // apiKey automatically read from process.env.TD_API_KEY
82
+ });
83
+ ```
84
+
85
+ **Alternative: Direct in Code (Not Recommended)**
86
+ ```javascript
87
+ const { testdriver } = await chrome(context, {
88
+ url: 'https://example.com',
89
+ apiKey: 'tdai-1234567890abcdef' // DON'T commit to version control!
90
+ });
91
+ ```
92
+
93
+ ---
94
+
95
+ ## Provision Methods
96
+
97
+ TestDriver provides presets to automatically provision different application types.
98
+
99
+ ### chrome() - Web Applications
100
+
101
+ Automatically launches Chrome browser and navigates to a URL.
102
+
103
+ ```javascript
104
+ import { chrome } from 'testdriverai/presets';
105
+
106
+ test('web app test', async (context) => {
107
+ const { testdriver, dashcam } = await chrome(context, {
108
+ url: 'https://myapp.com',
109
+ maximized: true, // Start maximized (default: true)
110
+ guest: true, // Use incognito mode (default: true)
111
+ dashcam: true, // Enable recording (default: true)
112
+ os: 'linux', // OS: 'linux', 'mac', 'windows' (default: 'linux')
113
+ apiKey: process.env.TD_API_KEY
114
+ });
115
+
116
+ // Your test code
117
+ });
118
+ ```
119
+
120
+ ### vscode() - VS Code Extensions
121
+
122
+ Automatically launches VS Code with specified workspace and extensions.
123
+
124
+ ```javascript
125
+ import { vscode } from 'testdriverai/presets';
126
+
127
+ test('vscode extension test', async (context) => {
128
+ const { testdriver, dashcam } = await vscode(context, {
129
+ workspace: '/tmp/test-project',
130
+ extensions: ['ms-python.python', 'dbaeumer.vscode-eslint'],
131
+ dashcam: true,
132
+ os: 'linux',
133
+ apiKey: process.env.TD_API_KEY
134
+ });
135
+
136
+ // Your test code
137
+ });
138
+ ```
139
+
140
+ ### electron() - Desktop Applications
141
+
142
+ Automatically launches Electron applications.
143
+
144
+ ```javascript
145
+ import { electron } from 'testdriverai/presets';
146
+
147
+ test('electron app test', async (context) => {
148
+ const { app, dashcam } = await electron(context, {
149
+ appPath: './dist/my-app',
150
+ args: ['--debug'], // Additional CLI args (optional)
151
+ dashcam: true,
152
+ os: 'linux',
153
+ apiKey: process.env.TD_API_KEY
154
+ });
155
+
156
+ // Your test code (app is alias for testdriver)
157
+ });
158
+ ```
159
+
160
+ ### Advanced: TestDriver() Hook + provision
161
+
162
+ For more control, use the direct API (recommended for v7.1+):
163
+
164
+ ```javascript
165
+ import { TestDriver } from 'testdriverai/vitest/hooks';
166
+
167
+ test('my test', async (context) => {
168
+ const testdriver = TestDriver(context, {
169
+ apiKey: process.env.TD_API_KEY
170
+ });
171
+
172
+ // Provision Chrome
173
+ await testdriver.provision.chrome({ url: 'https://example.com' });
174
+
175
+ // Or provision VS Code
176
+ await testdriver.provision.vscode({
177
+ workspace: '/tmp/project',
178
+ extensions: ['ms-python.python']
179
+ });
180
+
181
+ // Or provision Electron
182
+ await testdriver.provision.electron({ appPath: './dist/app' });
183
+
184
+ // Your test code
185
+ });
186
+ ```
187
+
188
+ ---
189
+
190
+ ## Core API Methods
191
+
192
+ All methods use AI-powered natural language descriptions.
193
+
194
+ ### find(description) - Locate Elements
195
+
196
+ Find a single element using natural language.
197
+
198
+ ```javascript
199
+ // Basic finding
200
+ const button = await testdriver.find('submit button');
201
+ const input = await testdriver.find('email input field');
202
+ const link = await testdriver.find('Contact Us link');
203
+
204
+ // With context
205
+ const field = await testdriver.find('username input in the login form');
206
+ const deleteBtn = await testdriver.find('delete button in the top right corner');
207
+
208
+ // Chainable syntax (recommended)
209
+ await testdriver.find('submit button').click();
210
+ await testdriver.find('email input').type('user@example.com');
211
+ ```
212
+
213
+ **Returns:** `Element` object with properties:
214
+ - `coordinates` - Position `{x, y, centerX, centerY}`
215
+ - `text` - Text content (if available)
216
+ - `confidence` - AI confidence score
217
+ - `screenshot` - Base64 screenshot (if available)
218
+
219
+ ### findAll(description) - Find Multiple Elements
220
+
221
+ Find all matching elements.
222
+
223
+ ```javascript
224
+ const items = await testdriver.findAll('product card');
225
+ console.log(`Found ${items.length} products`);
226
+
227
+ // Interact with each
228
+ for (const item of items) {
229
+ await item.click();
230
+ // Do something
231
+ }
232
+ ```
233
+
234
+ **Returns:** `Array<Element>`
235
+
236
+ ### click(x?, y?, action?) - Click Elements or Coordinates
237
+
238
+ ```javascript
239
+ // Element click (chainable)
240
+ await testdriver.find('Login button').click();
241
+
242
+ // Coordinate click
243
+ await testdriver.click(500, 300);
244
+
245
+ // Different click types
246
+ await element.click(); // Regular click
247
+ await element.click('double-click'); // Double-click
248
+ await element.click('right-click'); // Right-click
249
+ await element.click('mouseDown'); // Press and hold
250
+ await element.click('mouseUp'); // Release
251
+
252
+ // Or use dedicated methods
253
+ await element.doubleClick();
254
+ await element.rightClick();
255
+ await element.mouseDown();
256
+ await element.mouseUp();
257
+ ```
258
+
259
+ ### type(text, options) - Type Text
260
+
261
+ Type text into the currently focused input.
262
+
263
+ ```javascript
264
+ // Basic typing
265
+ await testdriver.find('email input').click();
266
+ await testdriver.type('user@example.com');
267
+
268
+ // Type with delay between keystrokes
269
+ await testdriver.type('slow typing', { delay: 500 }); // 500ms between chars
270
+
271
+ // ⚠️ IMPORTANT: Always use secret: true for passwords and sensitive data
272
+ await testdriver.find('password input').click();
273
+ await testdriver.type('MySecureP@ssw0rd', { secret: true });
274
+ // This prevents logging in dashcam, debug info, and console
275
+
276
+ // Type numbers
277
+ await testdriver.type(12345);
278
+ ```
279
+
280
+ **Options:**
281
+ - `delay` (number) - Delay between keystrokes in ms (default: 250)
282
+ - `secret` (boolean) - Mark as sensitive data, won't be logged (default: false)
283
+
284
+ ### hover(x?, y?) - Hover Over Elements
285
+
286
+ ```javascript
287
+ // Element hover
288
+ const menu = await testdriver.find('Products menu');
289
+ await menu.hover();
290
+ await testdriver.find('Laptops submenu item').click();
291
+
292
+ // Coordinate hover
293
+ await testdriver.hover(500, 300);
294
+ ```
295
+
296
+ ### assert(assertion) - AI-Powered Assertions
297
+
298
+ Verify screen state using natural language.
299
+
300
+ ```javascript
301
+ // Verify elements
302
+ await testdriver.assert('the login page is displayed');
303
+ await testdriver.assert('submit button is visible');
304
+
305
+ // Verify content
306
+ await testdriver.assert('the page title is "Welcome"');
307
+ await testdriver.assert('success message says "Account created"');
308
+
309
+ // Verify state
310
+ await testdriver.assert('the checkbox is checked');
311
+ await testdriver.assert('the form is empty');
312
+
313
+ // Verify visual appearance
314
+ await testdriver.assert('the button is blue');
315
+ await testdriver.assert('the loading spinner is displayed');
316
+ ```
317
+
318
+ **Returns:** `Promise<boolean>` - `true` if passes, throws error if fails
319
+
320
+ ### pressKeys(keys) - Keyboard Shortcuts
321
+
322
+ Press one or more keys simultaneously.
323
+
324
+ ```javascript
325
+ // Navigation
326
+ await testdriver.pressKeys(['tab']);
327
+ await testdriver.pressKeys(['shift', 'tab']);
328
+ await testdriver.pressKeys(['enter']);
329
+
330
+ // Keyboard shortcuts
331
+ await testdriver.pressKeys(['ctrl', 'c']); // Copy
332
+ await testdriver.pressKeys(['ctrl', 'v']); // Paste
333
+ await testdriver.pressKeys(['ctrl', 's']); // Save
334
+ await testdriver.pressKeys(['ctrl', 'a']); // Select all
335
+
336
+ // Arrow keys
337
+ await testdriver.pressKeys(['up']);
338
+ await testdriver.pressKeys(['down']);
339
+ await testdriver.pressKeys(['left']);
340
+ await testdriver.pressKeys(['right']);
341
+
342
+ // Special keys
343
+ await testdriver.pressKeys(['escape']);
344
+ await testdriver.pressKeys(['backspace']);
345
+ await testdriver.pressKeys(['delete']);
346
+ await testdriver.pressKeys(['home']);
347
+ await testdriver.pressKeys(['end']);
348
+ ```
349
+
350
+ ### scroll(direction, amount, method) - Scroll Pages
351
+
352
+ ```javascript
353
+ // Scroll down (default)
354
+ await testdriver.scroll();
355
+ await testdriver.scroll('down', 500);
356
+
357
+ // Scroll up
358
+ await testdriver.scroll('up', 300);
359
+
360
+ // Horizontal scrolling
361
+ await testdriver.scroll('right', 300);
362
+ await testdriver.scroll('left', 300);
363
+
364
+ // Scroll methods
365
+ await testdriver.scroll('down', 300, 'mouse'); // Mouse wheel (smooth)
366
+ await testdriver.scroll('down', 300, 'keyboard'); // Page Down key
367
+
368
+ // Scroll until text appears
369
+ await testdriver.scrollUntilText('Contact Us');
370
+ await testdriver.scrollUntilText('Footer', 'down', 5000);
371
+ ```
372
+
373
+ ### exec(language, code, timeout, silent) - Execute Code
374
+
375
+ Execute JavaScript or PowerShell in the sandbox.
376
+
377
+ ```javascript
378
+ // JavaScript execution (in browser)
379
+ const title = await testdriver.exec('js', 'document.title', 5000);
380
+
381
+ await testdriver.exec('js', `
382
+ document.querySelector('#username').value = 'testuser';
383
+ `, 5000);
384
+
385
+ // Get element text
386
+ const text = await testdriver.exec('js', `
387
+ document.querySelector('.notification').textContent
388
+ `, 5000);
389
+
390
+ // PowerShell execution (Windows sandbox)
391
+ await testdriver.exec('pwsh', 'npm install -g http-server', 30000);
392
+ await testdriver.exec('pwsh', 'Start-Process notepad.exe', 5000);
393
+ ```
394
+
395
+ ### focusApplication(appName) - Switch Applications
396
+
397
+ Focus a different application window.
398
+
399
+ ```javascript
400
+ await testdriver.focusApplication('Chrome');
401
+ await testdriver.focusApplication('VS Code');
402
+ await testdriver.focusApplication('Notepad');
403
+ ```
404
+
405
+ ---
406
+
407
+ ## Reconnection and Debugging
408
+
409
+ ### Reconnecting to a Sandbox
410
+
411
+ If a test fails or you need to try different selectors, you can reconnect to the same sandbox instance.
412
+
413
+ **Automatic Sandbox Tracking:** TestDriver automatically saves the last sandbox ID to `.testdriver/last-sandbox` in your project directory. You can use this to reconnect without manually tracking the ID.
414
+
415
+ **Manual Tracking:** You can also explicitly save the `sandboxId` from the initial connection:
416
+
417
+ ```javascript
418
+ import { test } from 'vitest';
419
+ import { TestDriver } from 'testdriverai';
420
+
421
+ test('initial attempt', async () => {
422
+ const testdriver = new TestDriver({
423
+ apiKey: process.env.TD_API_KEY
424
+ });
425
+
426
+ const instance = await testdriver.connect();
427
+ console.log('Sandbox ID:', instance.instanceId);
428
+ // Output: Sandbox ID: i-0abc123def456789
429
+
430
+ // Your test code that might fail
431
+ try {
432
+ await testdriver.find('difficult selector').click();
433
+ } catch (error) {
434
+ console.error('Failed:', error);
435
+ // Don't disconnect yet - keep sandbox alive for debugging
436
+ }
437
+ });
438
+
439
+ test('reconnect to same sandbox', async () => {
440
+ const testdriver = new TestDriver({
441
+ apiKey: process.env.TD_API_KEY
442
+ });
443
+
444
+ // Option 1: Read from saved file
445
+ const lastSandboxId = testdriver.getLastSandboxId();
446
+
447
+ // Option 2: Use the ID from previous test (manual tracking)
448
+ // const sandboxId = 'i-0abc123def456789';
449
+
450
+ // Reconnect using the sandbox ID
451
+ await testdriver.connect({
452
+ sandboxId: lastSandboxId, // or use manually saved ID
453
+ newSandbox: false
454
+ });
455
+
456
+ // Try different selector on the same sandbox state
457
+ try {
458
+ await testdriver.find('alternative selector description').click();
459
+ } catch (error) {
460
+ console.error('Alternative also failed:', error);
461
+ }
462
+
463
+ await testdriver.disconnect();
464
+ });
465
+ ```
466
+
467
+ ### Debugging Failed Finds
468
+
469
+ When `find()` fails, TestDriver provides detailed debug information:
470
+
471
+ ```javascript
472
+ try {
473
+ await testdriver.find('non-existent button').click();
474
+ } catch (error) {
475
+ // error.name === 'ElementNotFoundError'
476
+ console.log('Element not found:', error.message);
477
+ console.log('Similarity score:', error.similarity); // How close the match was
478
+ console.log('Debug screenshot:', error.debugScreenshot); // Base64 image
479
+ console.log('Cache info:', error.cacheInfo); // Cache diagnostics
480
+
481
+ // Try alternative description
482
+ await testdriver.find('button with different description').click();
483
+ }
484
+ ```
485
+
486
+ ### Keep Sandbox Alive for Multiple Attempts
487
+
488
+ ```javascript
489
+ test('iterative debugging', async (context) => {
490
+ const testdriver = new TestDriver({
491
+ apiKey: process.env.TD_API_KEY
492
+ });
493
+
494
+ const instance = await testdriver.connect();
495
+ const sandboxId = instance.instanceId;
496
+
497
+ // Try multiple selectors
498
+ const selectors = [
499
+ 'submit button',
500
+ 'blue submit button',
501
+ 'submit button in bottom right',
502
+ 'button with "Submit" text'
503
+ ];
504
+
505
+ for (const selector of selectors) {
506
+ try {
507
+ console.log(`Trying: ${selector}`);
508
+ const element = await testdriver.find(selector);
509
+ console.log(`✓ Found with: ${selector}`);
510
+ await element.click();
511
+ break; // Success!
512
+ } catch (error) {
513
+ console.log(`✗ Failed with: ${selector}`);
514
+ console.log(` Similarity: ${error.similarity}`);
515
+ }
516
+ }
517
+
518
+ // Clean up
519
+ await testdriver.disconnect();
520
+ });
521
+ ```
522
+
523
+ ### Using Vitest Hooks for Reconnection
524
+
525
+ ```javascript
526
+ import { test, beforeAll, afterAll } from 'vitest';
527
+ import { TestDriver } from 'testdriverai';
528
+
529
+ let testdriver;
530
+ let sandboxId;
531
+
532
+ beforeAll(async () => {
533
+ testdriver = new TestDriver({
534
+ apiKey: process.env.TD_API_KEY
535
+ });
536
+ const instance = await testdriver.connect();
537
+ sandboxId = instance.instanceId;
538
+ console.log('Sandbox started:', sandboxId);
539
+ });
540
+
541
+ afterAll(async () => {
542
+ await testdriver.disconnect();
543
+ });
544
+
545
+ test('first attempt', async () => {
546
+ await testdriver.find('selector v1').click();
547
+ });
548
+
549
+ test('second attempt - same sandbox', async () => {
550
+ // Continues in same sandbox session
551
+ await testdriver.find('selector v2').click();
552
+ });
553
+ ```
554
+
555
+ ---
556
+
557
+ ## Best Practices
558
+
559
+ ### 1. Use Descriptive Selectors
560
+
561
+ ```javascript
562
+ // ❌ Too vague
563
+ await testdriver.find('button');
564
+
565
+ // ✅ Specific
566
+ await testdriver.find('blue submit button below the login form');
567
+ ```
568
+
569
+ ### 2. Always Use secret: true for Sensitive Data
570
+
571
+ ```javascript
572
+ // ❌ Password will be logged
573
+ await testdriver.type('MyPassword123');
574
+
575
+ // ✅ Protected from logging
576
+ await testdriver.type('MyPassword123', { secret: true });
577
+ ```
578
+
579
+ ### 3. Prefer Chainable Syntax
580
+
581
+ ```javascript
582
+ // ❌ More verbose
583
+ const button = await testdriver.find('submit button');
584
+ await button.click();
585
+
586
+ // ✅ Cleaner
587
+ await testdriver.find('submit button').click();
588
+ ```
589
+
590
+ ### 4. Use Environment Variables for API Keys
591
+
592
+ ```javascript
593
+ // ❌ Hardcoded (security risk)
594
+ apiKey: 'tdai-1234567890abcdef'
595
+
596
+ // ✅ From environment
597
+ apiKey: process.env.TD_API_KEY
598
+ ```
599
+
600
+ ### 5. Set Appropriate Timeouts
601
+
602
+ ```javascript
603
+ // vitest.config.mjs
604
+ export default defineConfig({
605
+ test: {
606
+ testTimeout: 120000, // 2 minutes for TestDriver tests
607
+ hookTimeout: 120000,
608
+ },
609
+ });
610
+ ```
611
+
612
+ ### 6. Handle Errors Gracefully
613
+
614
+ ```javascript
615
+ try {
616
+ await testdriver.find('optional element').click();
617
+ } catch (error) {
618
+ console.log('Element not found, continuing...');
619
+ // Try alternative path
620
+ }
621
+ ```
622
+
623
+ ### 7. Use Assertions for Verification
624
+
625
+ ```javascript
626
+ // After action, verify result
627
+ await testdriver.find('submit button').click();
628
+ await testdriver.assert('success message is displayed');
629
+ ```
630
+
631
+ ### 8. Clean Up Resources
632
+
633
+ ```javascript
634
+ // Always disconnect when done
635
+ test('my test', async (context) => {
636
+ const { testdriver } = await chrome(context, { url: 'https://example.com' });
637
+
638
+ try {
639
+ // Your test code
640
+ } finally {
641
+ await testdriver.disconnect();
642
+ }
643
+ });
644
+
645
+ // Or use presets which auto-cleanup
646
+ ```
647
+
648
+ ### 9. Save Sandbox IDs for Debugging
649
+
650
+ ```javascript
651
+ const instance = await testdriver.connect();
652
+ console.log('Sandbox ID for debugging:', instance.instanceId);
653
+ // Save this ID to reconnect later if test fails
654
+ ```
655
+
656
+ ### 10. Use findAll() for Lists
657
+
658
+ ```javascript
659
+ // ❌ Finding one at a time
660
+ const item1 = await testdriver.find('first product card');
661
+ const item2 = await testdriver.find('second product card');
662
+
663
+ // ✅ Find all at once
664
+ const items = await testdriver.findAll('product card');
665
+ for (const item of items) {
666
+ await item.click();
667
+ }
668
+ ```
669
+
670
+ ---
671
+
672
+ ## Common Patterns
673
+
674
+ ### Login Flow
675
+
676
+ ```javascript
677
+ test('user login', async (context) => {
678
+ const { testdriver } = await chrome(context, {
679
+ url: 'https://myapp.com/login'
680
+ });
681
+
682
+ await testdriver.find('email input').type('user@example.com');
683
+ await testdriver.find('password input').type('SecurePass123', { secret: true });
684
+ await testdriver.find('Login button').click();
685
+
686
+ await testdriver.assert('Welcome message is visible');
687
+ });
688
+ ```
689
+
690
+ ### Form Filling
691
+
692
+ ```javascript
693
+ test('contact form', async (context) => {
694
+ const { testdriver } = await chrome(context, {
695
+ url: 'https://example.com/contact'
696
+ });
697
+
698
+ await testdriver.find('name input').type('John Doe');
699
+ await testdriver.find('email input').type('john@example.com');
700
+ await testdriver.find('message textarea').type('Hello, this is a test message.');
701
+ await testdriver.find('submit button').click();
702
+
703
+ await testdriver.assert('Thank you message appears');
704
+ });
705
+ ```
706
+
707
+ ### Navigation
708
+
709
+ ```javascript
710
+ test('multi-page navigation', async (context) => {
711
+ const { testdriver } = await chrome(context, {
712
+ url: 'https://example.com'
713
+ });
714
+
715
+ await testdriver.find('About link').click();
716
+ await testdriver.assert('About page heading is visible');
717
+
718
+ await testdriver.find('Contact link').click();
719
+ await testdriver.assert('Contact form is displayed');
720
+ });
721
+ ```
722
+
723
+ ### Working with Dropdowns
724
+
725
+ ```javascript
726
+ test('dropdown selection', async (context) => {
727
+ const { testdriver } = await chrome(context, {
728
+ url: 'https://example.com/form'
729
+ });
730
+
731
+ await testdriver.find('country dropdown').click();
732
+ await testdriver.find('United States option').click();
733
+
734
+ await testdriver.assert('United States is selected');
735
+ });
736
+ ```
737
+
738
+ ---
739
+
740
+ ## Environment Variables
741
+
742
+ TestDriver supports these environment variables:
743
+
744
+ - `TD_API_KEY` - Your TestDriver API key (recommended)
745
+ - `TD_NO_CACHE` - Set to `"true"` to disable caching
746
+ - `DASHCAM_API_KEY` - Dashcam API key (usually same as TD_API_KEY)
747
+ - `TESTDRIVER_SANDBOX_ID` - Reuse an existing sandbox instance (see Sandbox Management below)
748
+
749
+ ---
750
+
751
+ ## Sandbox Management
752
+
753
+ TestDriver allows you to create long-running sandbox instances that can be reused across multiple test runs.
754
+
755
+ ### Creating a Sandbox
756
+
757
+ Use the CLI to spawn a new sandbox:
758
+
759
+ ```bash
760
+ # Spawn a Linux sandbox (default)
761
+ testdriver sandbox spawn
762
+
763
+ # Spawn with a 2-hour lifetime (in milliseconds)
764
+ testdriver sandbox spawn --timeout 7200000
765
+
766
+ # Spawn a Windows sandbox
767
+ testdriver sandbox spawn --os windows
768
+
769
+ # Spawn with custom instance type (requires permission)
770
+ testdriver sandbox spawn --instance-type c5.xlarge
771
+ ```
772
+
773
+ The command will output the instance ID and instructions for using it.
774
+
775
+ ### Listing Sandboxes
776
+
777
+ View all your team's sandboxes:
778
+
779
+ ```bash
780
+ # List all sandboxes
781
+ testdriver sandbox list
782
+
783
+ # List only ready sandboxes
784
+ testdriver sandbox list --status ready
785
+ ```
786
+
787
+ ### Using a Sandbox in Tests
788
+
789
+ Set the `TESTDRIVER_SANDBOX_ID` environment variable to connect to an existing sandbox:
790
+
791
+ ```bash
792
+ export TESTDRIVER_SANDBOX_ID=i-0abc123def456789
793
+ vitest
794
+ ```
795
+
796
+ This will:
797
+ - Skip creating a new sandbox
798
+ - Connect to the specified sandbox instance
799
+ - Track which tests run on that sandbox
800
+ - Keep the sandbox alive after tests finish
801
+
802
+ ### Stopping a Sandbox
803
+
804
+ When you're done with a sandbox, stop it to avoid unnecessary costs:
805
+
806
+ ```bash
807
+ testdriver sandbox stop i-0abc123def456789
808
+ ```
809
+
810
+ This will:
811
+ - Terminate the EC2 instance
812
+ - Calculate the total runtime and cost
813
+ - Send billing data to your team
814
+
815
+ ### Sandbox Benefits
816
+
817
+ Using explicit sandboxes provides:
818
+ - **Faster test runs** - No sandbox creation time
819
+ - **Clear cost tracking** - Lifetime-based billing
820
+ - **Better debugging** - Keep sandbox alive to investigate failures
821
+ - **Team visibility** - See all active sandboxes in dashboard
822
+
823
+ ---
824
+
825
+ ## Resources
826
+
827
+ - **Documentation:** [docs.testdriver.ai](https://docs.testdriver.ai)
828
+ - **Dashboard:** [console.testdriver.ai](https://console.testdriver.ai)
829
+ - **API Reference:** Full method documentation in `/docs/v7/api/`
830
+ - **Examples:** See `/examples/` directory in the SDK package
831
+
832
+ ---
833
+
834
+ ## Quick Reference
835
+
836
+ | Method | Purpose | Example |
837
+ |--------|---------|---------|
838
+ | `find(desc)` | Locate single element | `await testdriver.find('submit button')` |
839
+ | `findAll(desc)` | Find all matching elements | `await testdriver.findAll('list item')` |
840
+ | `click()` | Click element/coordinates | `await element.click()` |
841
+ | `type(text, opts)` | Type text | `await testdriver.type('text', { secret: true })` |
842
+ | `hover()` | Hover over element | `await element.hover()` |
843
+ | `assert(text)` | Verify state | `await testdriver.assert('button is visible')` |
844
+ | `pressKeys(keys)` | Keyboard shortcuts | `await testdriver.pressKeys(['ctrl', 'c'])` |
845
+ | `scroll(dir, amt)` | Scroll page | `await testdriver.scroll('down', 500)` |
846
+ | `exec(lang, code)` | Execute code | `await testdriver.exec('js', 'document.title', 5000)` |
847
+ | `connect(opts)` | Connect to sandbox | `await testdriver.connect({ sandboxId: 'i-123' })` |
848
+ | `disconnect()` | Close connection | `await testdriver.disconnect()` |
849
+
850
+ ---
851
+
852
+ **Last Updated:** December 2, 2025