testdriverai 6.2.2 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/.github/workflows/acceptance-linux.yml +75 -0
  2. package/.github/workflows/acceptance-sdk-tests.yml +133 -0
  3. package/.vscode/settings.json +5 -1
  4. package/AGENTS.md +550 -0
  5. package/CODEOWNERS +0 -1
  6. package/README.md +126 -0
  7. package/{testdriver → _testdriver}/acceptance/drag-and-drop.yaml +2 -2
  8. package/{testdriver → _testdriver}/acceptance/snippets/login.yaml +1 -1
  9. package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
  10. package/{testdriver → _testdriver}/examples/web/lifecycle/prerun.yaml +6 -1
  11. package/{testdriver → _testdriver}/lifecycle/postrun.yaml +3 -2
  12. package/_testdriver/lifecycle/prerun.yaml +15 -0
  13. package/{testdriver → _testdriver}/lifecycle/provision.yaml +7 -2
  14. package/agent/index.js +300 -85
  15. package/agent/interface.js +15 -0
  16. package/agent/lib/cache.js +142 -0
  17. package/agent/lib/commander.js +1 -39
  18. package/agent/lib/commands.js +910 -296
  19. package/agent/lib/redraw.js +129 -41
  20. package/agent/lib/sandbox.js +29 -6
  21. package/agent/lib/sdk.js +22 -0
  22. package/agent/lib/system.js +0 -3
  23. package/agent/lib/validation.js +1 -7
  24. package/debug-locate-response.js +82 -0
  25. package/debugger/index.html +15 -4
  26. package/docs/ARCHITECTURE.md +424 -0
  27. package/docs/AWESOME_LOGS_QUICK_REF.md +100 -0
  28. package/docs/MIGRATION.md +425 -0
  29. package/docs/PRESETS.md +210 -0
  30. package/docs/QUICK_START_TEST_RECORDING.md +215 -0
  31. package/docs/SDK_AWESOME_LOGS.md +468 -0
  32. package/docs/TEST_RECORDING.md +388 -0
  33. package/docs/docs.json +286 -152
  34. package/docs/guide/best-practices-polling.mdx +154 -0
  35. package/docs/sdk-browser-rendering.md +167 -0
  36. package/docs/v6/getting-started/self-hosting.mdx +407 -0
  37. package/docs/{guide → v6/guide}/dashcam.mdx +1 -1
  38. package/docs/{guide → v6/guide}/environment-variables.mdx +4 -5
  39. package/docs/{guide → v6/guide}/lifecycle.mdx +1 -1
  40. package/docs/v6/overview/comparison.mdx +101 -0
  41. package/docs/v7/README.md +135 -0
  42. package/docs/v7/api/ai.mdx +205 -0
  43. package/docs/v7/api/assert.mdx +285 -0
  44. package/docs/v7/api/assertions.mdx +403 -0
  45. package/docs/v7/api/click.mdx +287 -0
  46. package/docs/v7/api/client.mdx +322 -0
  47. package/docs/v7/api/dashcam.mdx +497 -0
  48. package/docs/v7/api/doubleClick.mdx +102 -0
  49. package/docs/v7/api/elements.mdx +479 -0
  50. package/docs/v7/api/exec.mdx +346 -0
  51. package/docs/v7/api/find.mdx +316 -0
  52. package/docs/v7/api/focusApplication.mdx +294 -0
  53. package/docs/v7/api/hover.mdx +279 -0
  54. package/docs/v7/api/mouseDown.mdx +161 -0
  55. package/docs/v7/api/mouseUp.mdx +164 -0
  56. package/docs/v7/api/pressKeys.mdx +349 -0
  57. package/docs/v7/api/rightClick.mdx +123 -0
  58. package/docs/v7/api/sandbox.mdx +404 -0
  59. package/docs/v7/api/scroll.mdx +300 -0
  60. package/docs/v7/api/type.mdx +314 -0
  61. package/docs/v7/commands/assert.mdx +45 -0
  62. package/docs/v7/commands/exec.mdx +282 -0
  63. package/docs/v7/commands/focus-application.mdx +44 -0
  64. package/docs/v7/commands/hover-image.mdx +69 -0
  65. package/docs/v7/commands/hover-text.mdx +47 -0
  66. package/docs/v7/commands/if.mdx +53 -0
  67. package/docs/v7/commands/match-image.mdx +67 -0
  68. package/docs/v7/commands/press-keys.mdx +87 -0
  69. package/docs/v7/commands/remember.mdx +49 -0
  70. package/docs/v7/commands/run.mdx +44 -0
  71. package/docs/v7/commands/scroll-until-image.mdx +66 -0
  72. package/docs/v7/commands/scroll-until-text.mdx +60 -0
  73. package/docs/v7/commands/scroll.mdx +69 -0
  74. package/docs/v7/commands/type.mdx +45 -0
  75. package/docs/v7/commands/wait-for-image.mdx +54 -0
  76. package/docs/v7/commands/wait-for-text.mdx +48 -0
  77. package/docs/v7/commands/wait.mdx +45 -0
  78. package/docs/v7/getting-started/configuration.mdx +380 -0
  79. package/docs/v7/getting-started/quickstart.mdx +332 -0
  80. package/docs/v7/guides/best-practices.mdx +486 -0
  81. package/docs/v7/guides/caching-ai.mdx +215 -0
  82. package/docs/v7/guides/caching-selectors.mdx +292 -0
  83. package/docs/v7/guides/caching.mdx +366 -0
  84. package/docs/v7/guides/ci-cd/azure.mdx +587 -0
  85. package/docs/v7/guides/ci-cd/circleci.mdx +523 -0
  86. package/docs/v7/guides/ci-cd/github-actions.mdx +457 -0
  87. package/docs/v7/guides/ci-cd/gitlab.mdx +498 -0
  88. package/docs/v7/guides/ci-cd/jenkins.mdx +664 -0
  89. package/docs/v7/guides/ci-cd/travis.mdx +438 -0
  90. package/docs/v7/guides/debugging.mdx +349 -0
  91. package/docs/v7/guides/faq.mdx +393 -0
  92. package/docs/v7/guides/migration.mdx +562 -0
  93. package/docs/v7/guides/performance.mdx +517 -0
  94. package/docs/{getting-started → v7/guides}/self-hosting.mdx +11 -12
  95. package/docs/v7/guides/troubleshooting.mdx +526 -0
  96. package/docs/v7/guides/vitest-plugin.mdx +477 -0
  97. package/docs/v7/guides/vitest.mdx +535 -0
  98. package/docs/v7/platforms/linux.mdx +308 -0
  99. package/docs/v7/platforms/macos.mdx +433 -0
  100. package/docs/v7/platforms/windows.mdx +430 -0
  101. package/docs/v7/playwright.mdx +342 -0
  102. package/docs/v7/presets/chrome-extension.mdx +223 -0
  103. package/docs/v7/presets/chrome.mdx +287 -0
  104. package/docs/v7/presets/electron.mdx +435 -0
  105. package/docs/v7/presets/vscode.mdx +398 -0
  106. package/docs/v7/presets/webapp.mdx +396 -0
  107. package/docs/v7/progressive-apis/CORE.md +459 -0
  108. package/docs/v7/progressive-apis/HOOKS.md +360 -0
  109. package/docs/v7/progressive-apis/PROGRESSIVE_DISCLOSURE.md +230 -0
  110. package/docs/v7/progressive-apis/PROVISION.md +266 -0
  111. package/eslint.config.js +19 -1
  112. package/interfaces/cli/lib/base.js +10 -4
  113. package/interfaces/logger.js +2 -1
  114. package/interfaces/shared-test-state.mjs +69 -0
  115. package/interfaces/vitest-plugin.mjs +830 -0
  116. package/package.json +29 -5
  117. package/schema.json +8 -29
  118. package/scripts/view-test-results.mjs +96 -0
  119. package/sdk-log-formatter.js +714 -0
  120. package/sdk.d.ts +1028 -0
  121. package/sdk.js +2567 -0
  122. package/{.github/workflows/self-hosted.yml → self-hosted.yml} +13 -4
  123. package/setup/aws/cloudformation.yaml +9 -2
  124. package/src/core/Dashcam.js +469 -0
  125. package/src/core/index.d.ts +150 -0
  126. package/src/core/index.js +12 -0
  127. package/src/presets/index.mjs +331 -0
  128. package/src/vitest/extended.mjs +108 -0
  129. package/src/vitest/hooks.d.ts +119 -0
  130. package/src/vitest/hooks.mjs +298 -0
  131. package/src/vitest/index.mjs +64 -0
  132. package/src/vitest/lifecycle.mjs +277 -0
  133. package/src/vitest/utils.mjs +150 -0
  134. package/test/dashcam.test.js +137 -0
  135. package/test/mcp-example-test.yaml +27 -0
  136. package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +61 -0
  137. package/testdriver/acceptance-sdk/README.md +128 -0
  138. package/testdriver/acceptance-sdk/TEST_REPORTING.md +245 -0
  139. package/testdriver/acceptance-sdk/assert.test.mjs +26 -0
  140. package/testdriver/acceptance-sdk/auto-cache-key-demo.test.mjs +56 -0
  141. package/testdriver/acceptance-sdk/chrome-extension.test.mjs +89 -0
  142. package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +58 -0
  143. package/testdriver/acceptance-sdk/element-not-found.test.mjs +25 -0
  144. package/testdriver/acceptance-sdk/exec-js.test.mjs +43 -0
  145. package/testdriver/acceptance-sdk/exec-output.test.mjs +59 -0
  146. package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +57 -0
  147. package/testdriver/acceptance-sdk/focus-window.test.mjs +36 -0
  148. package/testdriver/acceptance-sdk/formatted-logging.test.mjs +26 -0
  149. package/testdriver/acceptance-sdk/hooks-example.test.mjs +38 -0
  150. package/testdriver/acceptance-sdk/hover-image.test.mjs +34 -0
  151. package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +38 -0
  152. package/testdriver/acceptance-sdk/hover-text.test.mjs +27 -0
  153. package/testdriver/acceptance-sdk/match-image.test.mjs +36 -0
  154. package/testdriver/acceptance-sdk/presets-example.test.mjs +87 -0
  155. package/testdriver/acceptance-sdk/press-keys.test.mjs +50 -0
  156. package/testdriver/acceptance-sdk/prompt.test.mjs +33 -0
  157. package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +38 -0
  158. package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +39 -0
  159. package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +28 -0
  160. package/testdriver/acceptance-sdk/scroll.test.mjs +41 -0
  161. package/testdriver/acceptance-sdk/setup/globalTeardown.mjs +11 -0
  162. package/testdriver/acceptance-sdk/setup/testHelpers.mjs +420 -0
  163. package/testdriver/acceptance-sdk/setup/vitestSetup.mjs +40 -0
  164. package/testdriver/acceptance-sdk/sully-ai.test.mjs +234 -0
  165. package/testdriver/acceptance-sdk/test-console-logs.test.mjs +42 -0
  166. package/testdriver/acceptance-sdk/type-checking-demo.js +49 -0
  167. package/testdriver/acceptance-sdk/type.test.mjs +45 -0
  168. package/verify-element-api.js +89 -0
  169. package/verify-types.js +0 -0
  170. package/vitest.config.example.js +19 -0
  171. package/vitest.config.mjs +66 -0
  172. package/vitest.config.mjs.bak +44 -0
  173. package/.github/workflows/acceptance-v6.yml +0 -169
  174. package/.vscode/mcp.json +0 -9
  175. package/docs/overview/comparison.mdx +0 -82
  176. package/testdriver/lifecycle/prerun.yaml +0 -17
  177. /package/{testdriver/examples/desktop/lifecycle/prerun.yaml → .env.example} +0 -0
  178. /package/{testdriver → _testdriver}/acceptance/assert.yaml +0 -0
  179. /package/{testdriver → _testdriver}/acceptance/dashcam.yaml +0 -0
  180. /package/{testdriver → _testdriver}/acceptance/embed.yaml +0 -0
  181. /package/{testdriver → _testdriver}/acceptance/exec-js.yaml +0 -0
  182. /package/{testdriver → _testdriver}/acceptance/exec-output.yaml +0 -0
  183. /package/{testdriver → _testdriver}/acceptance/exec-shell.yaml +0 -0
  184. /package/{testdriver → _testdriver}/acceptance/focus-window.yaml +0 -0
  185. /package/{testdriver → _testdriver}/acceptance/hover-image.yaml +0 -0
  186. /package/{testdriver → _testdriver}/acceptance/hover-text-with-description.yaml +0 -0
  187. /package/{testdriver → _testdriver}/acceptance/hover-text.yaml +0 -0
  188. /package/{testdriver → _testdriver}/acceptance/if-else.yaml +0 -0
  189. /package/{testdriver → _testdriver}/acceptance/match-image.yaml +0 -0
  190. /package/{testdriver → _testdriver}/acceptance/press-keys.yaml +0 -0
  191. /package/{testdriver → _testdriver}/acceptance/prompt.yaml +0 -0
  192. /package/{testdriver → _testdriver}/acceptance/remember.yaml +0 -0
  193. /package/{testdriver → _testdriver}/acceptance/screenshots/cart.png +0 -0
  194. /package/{testdriver → _testdriver}/acceptance/scroll-keyboard.yaml +0 -0
  195. /package/{testdriver → _testdriver}/acceptance/scroll-until-image.yaml +0 -0
  196. /package/{testdriver → _testdriver}/acceptance/scroll-until-text.yaml +0 -0
  197. /package/{testdriver → _testdriver}/acceptance/scroll.yaml +0 -0
  198. /package/{testdriver → _testdriver}/acceptance/snippets/match-cart.yaml +0 -0
  199. /package/{testdriver → _testdriver}/acceptance/type.yaml +0 -0
  200. /package/{testdriver → _testdriver}/behavior/failure.yaml +0 -0
  201. /package/{testdriver → _testdriver}/behavior/hover-text.yaml +0 -0
  202. /package/{testdriver → _testdriver}/behavior/lifecycle/postrun.yaml +0 -0
  203. /package/{testdriver → _testdriver}/behavior/lifecycle/prerun.yaml +0 -0
  204. /package/{testdriver → _testdriver}/behavior/lifecycle/provision.yaml +0 -0
  205. /package/{testdriver → _testdriver}/behavior/secrets.yaml +0 -0
  206. /package/{testdriver → _testdriver}/edge-cases/dashcam-chrome.yaml +0 -0
  207. /package/{testdriver → _testdriver}/edge-cases/exec-pwsh-multiline.yaml +0 -0
  208. /package/{testdriver → _testdriver}/edge-cases/js-exception.yaml +0 -0
  209. /package/{testdriver → _testdriver}/edge-cases/js-promise.yaml +0 -0
  210. /package/{testdriver → _testdriver}/edge-cases/lifecycle/postrun.yaml +0 -0
  211. /package/{testdriver → _testdriver}/edge-cases/prompt-in-middle.yaml +0 -0
  212. /package/{testdriver → _testdriver}/edge-cases/prompt-nested.yaml +0 -0
  213. /package/{testdriver → _testdriver}/edge-cases/success-test.yaml +0 -0
  214. /package/{testdriver → _testdriver}/examples/android/example.yaml +0 -0
  215. /package/{testdriver → _testdriver}/examples/android/lifecycle/postrun.yaml +0 -0
  216. /package/{testdriver → _testdriver}/examples/android/lifecycle/provision.yaml +0 -0
  217. /package/{testdriver → _testdriver}/examples/android/readme.md +0 -0
  218. /package/{testdriver → _testdriver}/examples/chrome-extension/lifecycle/provision.yaml +0 -0
  219. /package/{testdriver → _testdriver}/examples/desktop/lifecycle/provision.yaml +0 -0
  220. /package/{testdriver → _testdriver}/examples/vscode-extension/lifecycle/provision.yaml +0 -0
  221. /package/{testdriver → _testdriver}/examples/web/lifecycle/postrun.yaml +0 -0
  222. /package/docs/{account → v6/account}/dashboard.mdx +0 -0
  223. /package/docs/{account → v6/account}/enterprise.mdx +0 -0
  224. /package/docs/{account → v6/account}/pricing.mdx +0 -0
  225. /package/docs/{account → v6/account}/projects.mdx +0 -0
  226. /package/docs/{account → v6/account}/team.mdx +0 -0
  227. /package/docs/{action → v6/action}/ami.mdx +0 -0
  228. /package/docs/{action → v6/action}/performance.mdx +0 -0
  229. /package/docs/{action → v6/action}/secrets.mdx +0 -0
  230. /package/docs/{apps → v6/apps}/chrome-extensions.mdx +0 -0
  231. /package/docs/{apps → v6/apps}/desktop-apps.mdx +0 -0
  232. /package/docs/{apps → v6/apps}/mobile-apps.mdx +0 -0
  233. /package/docs/{apps → v6/apps}/static-websites.mdx +0 -0
  234. /package/docs/{apps → v6/apps}/tauri-apps.mdx +0 -0
  235. /package/docs/{bugs → v6/bugs}/jira.mdx +0 -0
  236. /package/docs/{cli → v6/cli}/overview.mdx +0 -0
  237. /package/docs/{commands → v6/commands}/assert.mdx +0 -0
  238. /package/docs/{commands → v6/commands}/exec.mdx +0 -0
  239. /package/docs/{commands → v6/commands}/focus-application.mdx +0 -0
  240. /package/docs/{commands → v6/commands}/hover-image.mdx +0 -0
  241. /package/docs/{commands → v6/commands}/hover-text.mdx +0 -0
  242. /package/docs/{commands → v6/commands}/if.mdx +0 -0
  243. /package/docs/{commands → v6/commands}/match-image.mdx +0 -0
  244. /package/docs/{commands → v6/commands}/press-keys.mdx +0 -0
  245. /package/docs/{commands → v6/commands}/remember.mdx +0 -0
  246. /package/docs/{commands → v6/commands}/run.mdx +0 -0
  247. /package/docs/{commands → v6/commands}/scroll-until-image.mdx +0 -0
  248. /package/docs/{commands → v6/commands}/scroll-until-text.mdx +0 -0
  249. /package/docs/{commands → v6/commands}/scroll.mdx +0 -0
  250. /package/docs/{commands → v6/commands}/type.mdx +0 -0
  251. /package/docs/{commands → v6/commands}/wait-for-image.mdx +0 -0
  252. /package/docs/{commands → v6/commands}/wait-for-text.mdx +0 -0
  253. /package/docs/{commands → v6/commands}/wait.mdx +0 -0
  254. /package/docs/{exporting → v6/exporting}/junit.mdx +0 -0
  255. /package/docs/{exporting → v6/exporting}/playwright.mdx +0 -0
  256. /package/docs/{features → v6/features}/auto-healing.mdx +0 -0
  257. /package/docs/{features → v6/features}/generation.mdx +0 -0
  258. /package/docs/{features → v6/features}/parallel-testing.mdx +0 -0
  259. /package/docs/{features → v6/features}/reusable-snippets.mdx +0 -0
  260. /package/docs/{features → v6/features}/selectorless.mdx +0 -0
  261. /package/docs/{features → v6/features}/visual-assertions.mdx +0 -0
  262. /package/docs/{getting-started → v6/getting-started}/ci.mdx +0 -0
  263. /package/docs/{getting-started → v6/getting-started}/cli.mdx +0 -0
  264. /package/docs/{getting-started → v6/getting-started}/editing.mdx +0 -0
  265. /package/docs/{getting-started → v6/getting-started}/playwright.mdx +0 -0
  266. /package/docs/{getting-started → v6/getting-started}/running.mdx +0 -0
  267. /package/docs/{getting-started → v6/getting-started}/vscode.mdx +0 -0
  268. /package/docs/{guide → v6/guide}/assertions.mdx +0 -0
  269. /package/docs/{guide → v6/guide}/authentication.mdx +0 -0
  270. /package/docs/{guide → v6/guide}/code.mdx +0 -0
  271. /package/docs/{guide → v6/guide}/locating.mdx +0 -0
  272. /package/docs/{guide → v6/guide}/protips.mdx +0 -0
  273. /package/docs/{guide → v6/guide}/variables.mdx +0 -0
  274. /package/docs/{guide → v6/guide}/waiting.mdx +0 -0
  275. /package/docs/{importing → v6/importing}/csv.mdx +0 -0
  276. /package/docs/{importing → v6/importing}/gherkin.mdx +0 -0
  277. /package/docs/{importing → v6/importing}/jira.mdx +0 -0
  278. /package/docs/{importing → v6/importing}/testrail.mdx +0 -0
  279. /package/docs/{integrations → v6/integrations}/electron.mdx +0 -0
  280. /package/docs/{integrations → v6/integrations}/netlify.mdx +0 -0
  281. /package/docs/{integrations → v6/integrations}/vercel.mdx +0 -0
  282. /package/docs/{interactive → v6/interactive}/explore.mdx +0 -0
  283. /package/docs/{interactive → v6/interactive}/run.mdx +0 -0
  284. /package/docs/{interactive → v6/interactive}/save.mdx +0 -0
  285. /package/docs/{overview → v6/overview}/faq.mdx +0 -0
  286. /package/docs/{overview → v6/overview}/performance.mdx +0 -0
  287. /package/docs/{overview → v6/overview}/quickstart.mdx +0 -0
  288. /package/docs/{overview → v6/overview}/what-is-testdriver.mdx +0 -0
  289. /package/docs/{scenarios → v6/scenarios}/ai-chatbot.mdx +0 -0
  290. /package/docs/{scenarios → v6/scenarios}/cookie-banner.mdx +0 -0
  291. /package/docs/{scenarios → v6/scenarios}/file-upload.mdx +0 -0
  292. /package/docs/{scenarios → v6/scenarios}/form-filling.mdx +0 -0
  293. /package/docs/{scenarios → v6/scenarios}/log-in.mdx +0 -0
  294. /package/docs/{scenarios → v6/scenarios}/pdf-generation.mdx +0 -0
  295. /package/docs/{scenarios → v6/scenarios}/spell-check.mdx +0 -0
  296. /package/docs/{security → v6/security}/action.mdx +0 -0
  297. /package/docs/{security → v6/security}/agent.mdx +0 -0
  298. /package/docs/{security → v6/security}/platform.mdx +0 -0
  299. /package/docs/{tutorials → v6/tutorials}/advanced-test.mdx +0 -0
  300. /package/docs/{tutorials → v6/tutorials}/basic-test.mdx +0 -0
package/AGENTS.md ADDED
@@ -0,0 +1,550 @@
1
+ # TestDriver Agent Instructions
2
+
3
+ This guide teaches AI agents how to write modular, iteratively-debuggable Vitest tests using the TestDriver SDK.
4
+
5
+ ## Core Principles
6
+
7
+ ### 1. One Action Per Step
8
+
9
+ Each `it()` block performs exactly **ONE state-changing action** plus optional assertions.
10
+
11
+ **State-changing actions:**
12
+ - `click()` - clicking on elements
13
+ - `type()` - typing text
14
+ - `pressKeys()` - keyboard shortcuts
15
+ - `scroll()` - scrolling the page
16
+
17
+ **NOT state-changing (can combine with actions):**
18
+ - `find()` - locating elements
19
+ - `assert()` - verifying state
20
+ - `exists()` - checking element presence
21
+ - `screenshot()` - capturing screen
22
+
23
+ ```javascript
24
+ // ✅ CORRECT: One action per step
25
+ it("step01: click the login button", async () => {
26
+ const button = await testdriver.find("Login button");
27
+ await button.click();
28
+ // Optional: verify the action worked
29
+ const form = await testdriver.find("Login form");
30
+ expect(form.exists()).toBe(true);
31
+ });
32
+
33
+ it("step02: type username", async () => {
34
+ await testdriver.type("testuser");
35
+ await testdriver.assert("username field contains 'testuser'");
36
+ });
37
+
38
+ // ❌ WRONG: Multiple actions in one step
39
+ it("login flow", async () => {
40
+ await testdriver.find("Login").click();
41
+ await testdriver.type("user"); // second action!
42
+ await testdriver.type("password"); // third action!
43
+ await testdriver.find("Submit").click(); // fourth action!
44
+ });
45
+ ```
46
+
47
+ ### 2. Step Naming Convention
48
+
49
+ Use zero-padded step numbers for proper sorting and easy `--testNamePattern` filtering:
50
+
51
+ ```javascript
52
+ it("step01: open application", async () => { ... });
53
+ it("step02: click login button", async () => { ... });
54
+ it("step03: enter username", async () => { ... });
55
+ it("step04: enter password", async () => { ... });
56
+ it("step05: submit form", async () => { ... });
57
+ it("step06: verify dashboard loaded", async () => { ... });
58
+ ```
59
+
60
+ ### 3. All Steps in One File
61
+
62
+ Write all steps for a test flow in the same file. This allows:
63
+ - Running the entire flow: `vitest path/to/test.test.js`
64
+ - Debugging a single step: `vitest --testNamePattern "step03"`
65
+
66
+ ### 4. Optional Assertions
67
+
68
+ Assertions verify the action worked. Use them when appropriate:
69
+
70
+ ```javascript
71
+ // After clicking - verify navigation or state change
72
+ it("step01: click submit button", async () => {
73
+ await testdriver.find("Submit button").click();
74
+ await testdriver.assert("form was submitted successfully");
75
+ });
76
+
77
+ // After typing - verify text appeared (optional, typing is deterministic)
78
+ it("step02: type search query", async () => {
79
+ await testdriver.type("search term");
80
+ // assertion optional for typing
81
+ });
82
+
83
+ // Use exists() for element presence
84
+ it("step03: verify modal appeared", async () => {
85
+ const modal = await testdriver.find("Confirmation modal");
86
+ expect(modal.exists()).toBe(true);
87
+ });
88
+ ```
89
+
90
+ ## Test Structure
91
+
92
+ ### Basic Template
93
+
94
+ ```javascript
95
+ import { describe, it, expect } from "vitest";
96
+ import { TestDriver } from "testdriverai/vitest";
97
+
98
+ describe("Feature Name", () => {
99
+ it("step01: provision and first action", async (context) => {
100
+ const testdriver = TestDriver(context);
101
+ await testdriver.provision.chrome({ url: 'https://example.com' });
102
+
103
+ // First action + optional assertion
104
+ });
105
+
106
+ it("step02: second action", async (context) => {
107
+ const testdriver = TestDriver(context);
108
+
109
+ // action + optional assertion
110
+ });
111
+ });
112
+ ```
113
+
114
+ **Key Points:**
115
+ - Each test gets its own `testdriver` via `TestDriver(context)`
116
+ - Call `provision.chrome({ url })` in the first step to launch the browser
117
+ - Subsequent steps don't need to provision - the sandbox persists
118
+
119
+ ## Shared Helpers
120
+
121
+ ### Reusing Common Flows
122
+
123
+ Before writing steps for common flows (login, navigation, setup), check if a helper already exists.
124
+
125
+ **Helper Location:** `testdriver/helpers/`
126
+
127
+ ### Creating a Helper
128
+
129
+ ```javascript
130
+ // testdriver/helpers/login.js
131
+ export async function login(testdriver, username = "testuser", password = "password") {
132
+ const loginButton = await testdriver.find("Login button");
133
+ await loginButton.click();
134
+
135
+ await testdriver.type(username);
136
+ await testdriver.pressKeys(["Tab"]);
137
+ await testdriver.type(password);
138
+
139
+ const submitButton = await testdriver.find("Submit button");
140
+ await submitButton.click();
141
+
142
+ await testdriver.assert("user is logged in");
143
+ }
144
+ ```
145
+
146
+ ### Using a Helper
147
+
148
+ ```javascript
149
+ import { describe, it, expect } from "vitest";
150
+ import { TestDriver } from "testdriverai/vitest";
151
+ import { login } from "./helpers/login.js";
152
+
153
+ describe("Dashboard Tests", () => {
154
+ it("step01: provision and login", async (context) => {
155
+ const testdriver = TestDriver(context);
156
+ await testdriver.provision.chrome({ url: 'https://example.com' });
157
+ await login(testdriver);
158
+ });
159
+
160
+ it("step02: navigate to settings", async (context) => {
161
+ const testdriver = TestDriver(context);
162
+ const settingsLink = await testdriver.find("Settings link in sidebar");
163
+ await settingsLink.click();
164
+ });
165
+
166
+ // ... more steps
167
+ });
168
+ ```
169
+
170
+ ### When to Create Helpers
171
+
172
+ Create a helper when:
173
+ - Flow is used in multiple tests (login, logout, navigation)
174
+ - Flow has 3+ steps that are always done together
175
+ - Flow requires specific credentials or configuration
176
+
177
+ ## Iterative Development Workflow
178
+
179
+ **CRITICAL: Write and run tests ONE STEP AT A TIME.** Do not write the entire test file upfront. Build it incrementally.
180
+
181
+ ### The Loop
182
+
183
+ ```
184
+ ┌─────────────────────────────────────────────────────────┐
185
+ │ 1. Write ONE step │
186
+ │ 2. Run that step: vitest --testNamePattern "stepNN" │
187
+ │ 3. Did it pass? │
188
+ │ ├─ YES → Go to step 1, write next step │
189
+ │ └─ NO → Fix the step, go to step 2 │
190
+ │ 4. When all steps pass, run entire file │
191
+ └─────────────────────────────────────────────────────────┘
192
+ ```
193
+
194
+ ### Detailed Workflow
195
+
196
+ #### Phase 1: Setup the Test File
197
+
198
+ Create the file with the describe block and NO steps yet:
199
+
200
+ ```javascript
201
+ import { describe, it, expect } from "vitest";
202
+ import { TestDriver } from "testdriverai/vitest";
203
+
204
+ describe("My Feature Test", () => {
205
+ // Steps will be added one at a time below
206
+ });
207
+ ```
208
+
209
+ #### Phase 2: Write Step 1
210
+
211
+ Add the first step with provisioning:
212
+
213
+ ```javascript
214
+ it("step01: open app and click login", async (context) => {
215
+ const testdriver = TestDriver(context);
216
+ await testdriver.provision.chrome({ url: 'https://example.com/login' });
217
+
218
+ const button = await testdriver.find("Login button in the header");
219
+ await button.click();
220
+ });
221
+ ```
222
+
223
+ #### Phase 3: Run Step 1
224
+
225
+ ```bash
226
+ vitest --testNamePattern "step01" path/to/test.test.js
227
+ ```
228
+
229
+ **If it passes:** Move to Phase 4.
230
+
231
+ **If it fails:**
232
+ - Check the error message
233
+ - Take a screenshot to see actual screen state
234
+ - Common fixes:
235
+ - Improve the `find()` description (be more specific)
236
+ - The element might not be visible yet (add scroll or wait)
237
+ - Wrong element clicked (check coordinates)
238
+ - Edit the step and re-run the same command
239
+
240
+ #### Phase 4: Write Step 2
241
+
242
+ Only after step 1 passes, add step 2:
243
+
244
+ ```javascript
245
+ it("step02: type username", async (context) => {
246
+ const testdriver = TestDriver(context);
247
+ await testdriver.type("testuser");
248
+ });
249
+ ```
250
+
251
+ #### Phase 5: Run Step 2
252
+
253
+ ```bash
254
+ vitest --testNamePattern "step02" path/to/test.test.js
255
+ ```
256
+
257
+ The sandbox persists from step 1, so the app is already in the right state.
258
+
259
+ **If it passes:** Continue to step 3.
260
+
261
+ **If it fails:** Fix and re-run step 2 only.
262
+
263
+ #### Phase 6: Repeat
264
+
265
+ Continue this pattern:
266
+ 1. Write step N
267
+ 2. Run step N
268
+ 3. Fix until passing
269
+ 4. Write step N+1
270
+
271
+ #### Phase 7: Run Full Test
272
+
273
+ When all steps pass individually:
274
+
275
+ ```bash
276
+ vitest path/to/test.test.js
277
+ ```
278
+
279
+ This runs all steps in sequence to verify the complete flow.
280
+
281
+ ### Why One Step at a Time?
282
+
283
+ 1. **Faster debugging** - If step 5 fails, you only re-run step 5, not steps 1-4
284
+ 2. **Sandbox reuse** - The sandbox persists, so previous steps don't need to re-run
285
+ 3. **Immediate feedback** - You see if each action works before moving on
286
+ 4. **Easier fixes** - Smaller changes are easier to debug
287
+
288
+ ### Example: Building a Test Incrementally
289
+
290
+ **Goal:** Test user login
291
+
292
+ **Iteration 1:** Write and run step01
293
+ ```javascript
294
+ it("step01: provision and click login", async (context) => {
295
+ const testdriver = TestDriver(context);
296
+ await testdriver.provision.chrome({ url: 'https://example.com' });
297
+
298
+ const button = await testdriver.find("Login button");
299
+ await button.click();
300
+ });
301
+ ```
302
+ ```bash
303
+ vitest --testNamePattern "step01" login.test.js
304
+ # ✅ Passed
305
+ ```
306
+
307
+ **Iteration 2:** Write and run step02
308
+ ```javascript
309
+ it("step02: type username", async (context) => {
310
+ const testdriver = TestDriver(context);
311
+ await testdriver.type("admin");
312
+ });
313
+ ```
314
+ ```bash
315
+ vitest --testNamePattern "step02" login.test.js
316
+ # ❌ Failed - typing into wrong field
317
+ ```
318
+
319
+ **Iteration 2b:** Fix step02 - need to click the field first
320
+ ```javascript
321
+ it("step02: click username field", async (context) => {
322
+ const testdriver = TestDriver(context);
323
+ const field = await testdriver.find("Username input field");
324
+ await field.click();
325
+ });
326
+ ```
327
+ ```bash
328
+ vitest --testNamePattern "step02" login.test.js
329
+ # ✅ Passed
330
+ ```
331
+
332
+ **Iteration 3:** Write and run step03
333
+ ```javascript
334
+ it("step03: type username", async (context) => {
335
+ const testdriver = TestDriver(context);
336
+ await testdriver.type("admin");
337
+ });
338
+ ```
339
+ ```bash
340
+ vitest --testNamePattern "step03" login.test.js
341
+ # ✅ Passed
342
+ ```
343
+
344
+ Continue until complete...
345
+
346
+ ## Sandbox Persistence
347
+
348
+ TestDriver automatically persists sandbox connections:
349
+
350
+ - **Project-local:** `.testdriver-sandbox.json` in current directory
351
+ - **Timeout:** 10 minutes of inactivity
352
+ - **Auto-reconnect:** Automatically reuses recent sandbox on next run
353
+
354
+ During development:
355
+ - Don't call `disconnect()` in `afterAll`
356
+ - Sandbox stays alive between test runs
357
+ - If sandbox expires, a new one is created automatically
358
+
359
+ ### One-Time Setup Steps
360
+
361
+ Use `it.once()` for steps that should only run once per sandbox session (app launch, provisioning):
362
+
363
+ ```javascript
364
+ import { describe, it, beforeAll, expect } from "testdriverai/vitest";
365
+ import TestDriver from "testdriverai";
366
+
367
+ describe("My Test", () => {
368
+ let testdriver;
369
+
370
+ beforeAll(async () => {
371
+ testdriver = new TestDriver(process.env.TD_API_KEY);
372
+ await testdriver.connect();
373
+
374
+ // Store globally so it.once() can check isReconnected
375
+ globalThis.__testdriver = testdriver;
376
+ });
377
+
378
+ // Only runs once per sandbox session - skipped on reconnect
379
+ it.once("launch the application", async () => {
380
+ await testdriver.exec("sh", "google-chrome https://example.com", 5000);
381
+ await testdriver.assert("application is loaded");
382
+ });
383
+
384
+ // Always runs
385
+ it("click the login button", async () => {
386
+ const button = await testdriver.find("Login button");
387
+ await button.click();
388
+ });
389
+
390
+ // Always runs
391
+ it("type username", async () => {
392
+ await testdriver.type("testuser");
393
+ });
394
+ });
395
+ ```
396
+
397
+ **When to use `it.once()`:**
398
+ - Launching the application
399
+ - Navigating to initial URL
400
+ - One-time provisioning steps
401
+ - Any step that sets up state that persists in the sandbox
402
+
403
+ **Regular `it()` steps:**
404
+ - All subsequent actions
405
+ - Steps you're actively developing/debugging
406
+
407
+ This lets you run the full file and one-time steps are automatically skipped on reconnect:
408
+ ```bash
409
+ # First run: setup runs, then all steps
410
+ vitest path/to/test.test.js
411
+
412
+ # Second run (within 10 min): setup skipped, runs from first regular step
413
+ vitest path/to/test.test.js
414
+ ```
415
+
416
+ **How it works:**
417
+ - The SDK sets `testdriver.isReconnected = true` when it reconnects to an existing sandbox
418
+ - `it.once()` checks `globalThis.__testdriver.isReconnected` to decide whether to skip
419
+
420
+ ## SDK Quick Reference
421
+
422
+ ### Finding Elements
423
+
424
+ ```javascript
425
+ // Find returns an Element with coordinates
426
+ const element = await testdriver.find("description of element");
427
+
428
+ // Chain directly
429
+ await testdriver.find("Submit button").click();
430
+
431
+ // Check if found
432
+ if (element.exists()) { ... }
433
+
434
+ // Get coordinates
435
+ const { x, y, centerX, centerY } = element.getCoordinates();
436
+ ```
437
+
438
+ ### Actions
439
+
440
+ ```javascript
441
+ // Click (on found element)
442
+ await element.click();
443
+ await element.click("double-click");
444
+ await element.click("right-click");
445
+
446
+ // Type text (into focused field)
447
+ await testdriver.type("text to type");
448
+ await testdriver.type("text", 100); // with delay between keys
449
+
450
+ // Keyboard shortcuts
451
+ await testdriver.pressKeys(["ctrl", "c"]);
452
+ await testdriver.pressKeys(["cmd", "shift", "p"]);
453
+
454
+ // Scroll
455
+ await testdriver.scroll("down", 300);
456
+ await testdriver.scroll("up", 500, "keyboard");
457
+ ```
458
+
459
+ ### Verification
460
+
461
+ ```javascript
462
+ // AI-powered assertion (flexible, visual)
463
+ const passed = await testdriver.assert("the form was submitted successfully");
464
+ expect(passed).toBe(true);
465
+
466
+ // Element existence
467
+ const element = await testdriver.find("Success message");
468
+ expect(element.exists()).toBe(true);
469
+
470
+ // Screenshot for debugging
471
+ const screenshot = await testdriver.screenshot();
472
+ ```
473
+
474
+ ### Remember (Extract Values)
475
+
476
+ ```javascript
477
+ // Extract dynamic values from screen
478
+ const orderNumber = await testdriver.remember("the order confirmation number");
479
+ console.log("Order:", orderNumber);
480
+ ```
481
+
482
+ ## Example: Complete Test File
483
+
484
+ ```javascript
485
+ import { describe, it, expect } from "vitest";
486
+ import { TestDriver } from "testdriverai/vitest";
487
+
488
+ describe("User Registration", () => {
489
+ it("step01: provision and navigate to registration", async (context) => {
490
+ const testdriver = TestDriver(context);
491
+ await testdriver.provision.chrome({ url: 'https://example.com' });
492
+
493
+ const signupLink = await testdriver.find("Sign Up link in navigation");
494
+ await signupLink.click();
495
+ await testdriver.assert("registration form is visible");
496
+ });
497
+
498
+ it("step02: enter email address", async (context) => {
499
+ const testdriver = TestDriver(context);
500
+ const emailField = await testdriver.find("Email input field");
501
+ await emailField.click();
502
+ await testdriver.type("test@example.com");
503
+ });
504
+
505
+ it("step03: enter password", async (context) => {
506
+ const testdriver = TestDriver(context);
507
+ const passwordField = await testdriver.find("Password input field");
508
+ await passwordField.click();
509
+ await testdriver.type("SecurePass123!");
510
+ });
511
+
512
+ it("step04: confirm password", async (context) => {
513
+ const testdriver = TestDriver(context);
514
+ const confirmField = await testdriver.find("Confirm password field");
515
+ await confirmField.click();
516
+ await testdriver.type("SecurePass123!");
517
+ });
518
+
519
+ it("step05: accept terms and conditions", async (context) => {
520
+ const testdriver = TestDriver(context);
521
+ const checkbox = await testdriver.find("Terms and conditions checkbox");
522
+ await checkbox.click();
523
+ });
524
+
525
+ it("step06: submit registration", async (context) => {
526
+ const testdriver = TestDriver(context);
527
+ const submitButton = await testdriver.find("Create Account button");
528
+ await submitButton.click();
529
+ await testdriver.assert("account was created successfully");
530
+ });
531
+
532
+ it("step07: verify welcome message", async (context) => {
533
+ const testdriver = TestDriver(context);
534
+ const welcome = await testdriver.find("Welcome message");
535
+ expect(welcome.exists()).toBe(true);
536
+ });
537
+ });
538
+ ```
539
+
540
+ ## Tips for AI Agents
541
+
542
+ 1. **Write ONE step at a time** - Never write the full test upfront. Write step 1, run it, fix it, then write step 2.
543
+ 2. **Run after each step** - Use `vitest --testNamePattern "stepNN"` to test each step immediately
544
+ 3. **Search before writing** - Check `testdriver/helpers/` for existing flows
545
+ 4. **Descriptive find()** - Include location, color, text: "blue Submit button below the form"
546
+ 5. **One action at a time** - Easier to debug when things fail
547
+ 6. **Assert after important actions** - Especially navigation and form submissions
548
+ 7. **Use step numbers** - Makes `--testNamePattern` filtering easy
549
+ 8. **Don't disconnect during dev** - Let sandbox persist for faster iteration
550
+ 9. **Check screenshots** - When steps fail, look at actual screen state
package/CODEOWNERS CHANGED
@@ -1,3 +1,2 @@
1
1
  # https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#example-of-a-codeowners-file
2
2
  @ianjennings
3
- @ericclemmons
package/README.md CHANGED
@@ -10,6 +10,71 @@ Automate and scale QA with computer-use agents.
10
10
 
11
11
  [Follow the instructions on our docs for more.](https://docs.testdriver.ai/overview/quickstart).
12
12
 
13
+ ## v7 SDK - Progressive Disclosure
14
+
15
+ TestDriver v7 introduces **three levels of API** to match your experience level:
16
+
17
+ ### 🟢 Beginner: Presets (Zero Config)
18
+
19
+ ```javascript
20
+ import { test } from 'vitest';
21
+ import { chromePreset } from 'testdriverai/presets';
22
+
23
+ test('login test', async (context) => {
24
+ const { client } = await chromePreset(context, {
25
+ url: 'https://myapp.com'
26
+ });
27
+
28
+ await client.find('Login button').click();
29
+ });
30
+ ```
31
+
32
+ **Built-in presets:** Chrome, VS Code, Electron, and create your own!
33
+
34
+ ### 🟡 Intermediate: Hooks (Flexible)
35
+
36
+ ```javascript
37
+ import { test } from 'vitest';
38
+ import { useTestDriver, useDashcam } from 'testdriverai/vitest/hooks';
39
+
40
+ test('my test', async (context) => {
41
+ const client = useTestDriver(context, { os: 'linux' });
42
+ const dashcam = useDashcam(context, client, {
43
+ autoStart: true,
44
+ autoStop: true
45
+ });
46
+
47
+ await client.find('button').click();
48
+ });
49
+ ```
50
+
51
+ **Automatic lifecycle management** - no more forgetting cleanup!
52
+
53
+ ### 🔴 Advanced: Core Classes (Full Control)
54
+
55
+ ```javascript
56
+ import { test } from 'vitest';
57
+ import { TestDriver, Dashcam } from 'testdriverai/core';
58
+
59
+ test('my test', async () => {
60
+ const client = new TestDriver(apiKey, { os: 'linux' });
61
+ const dashcam = new Dashcam(client);
62
+
63
+ await client.auth();
64
+ await client.connect();
65
+ await dashcam.start();
66
+
67
+ await client.find('button').click();
68
+
69
+ await dashcam.stop();
70
+ await client.disconnect();
71
+ });
72
+ ```
73
+
74
+ **Full manual control** for advanced scenarios.
75
+
76
+ 📖 **Learn more:** [MIGRATION.md](./docs/MIGRATION.md) | [PRESETS.md](./docs/PRESETS.md) | [HOOKS.md](./docs/HOOKS.md)
77
+
13
78
  # About
14
79
 
15
80
  TestDriver isn't like any test framework you've used before. TestDriver is an OS Agent for QA. TestDriver uses AI vision along with mouse and keyboard emulation to control the entire desktop. It's more like a QA employee than a test framework. This kind of black-box testing has some major advantages:
@@ -186,3 +251,64 @@ You can also set the default test file path using environment variables:
186
251
  export TD_DEFAULT_TEST_FILE="custom/path/test.yaml"
187
252
  node your-script.js
188
253
  ```
254
+
255
+ ## MCP Server for AI Agents
256
+
257
+ TestDriver includes a Model Context Protocol (MCP) server that enables AI agents to **interactively create Vitest test files**.
258
+
259
+ ### How It Works
260
+
261
+ 1. **AI agent connects** to a persistent TestDriver sandbox
262
+ 2. **User describes** what they want to test
263
+ 3. **AI explores** the application using TestDriver commands
264
+ 4. **AI generates** Vitest test code from successful interactions
265
+
266
+ ### Quick Start
267
+
268
+ ```bash
269
+ cd mcp-server
270
+ npm install && npm run build
271
+ npm run deploy # Install to ~/.mcp/testdriver
272
+ ```
273
+
274
+ ### Configuration
275
+
276
+ Add to your MCP client configuration:
277
+
278
+ ```json
279
+ {
280
+ "servers": {
281
+ "testdriverai": {
282
+ "type": "stdio",
283
+ "command": "node",
284
+ "args": ["/path/to/cli/mcp-server/dist/index.js"]
285
+ }
286
+ }
287
+ }
288
+ ```
289
+
290
+ ### Example Workflow
291
+
292
+ ```
293
+ User: "Create a test that logs into the app"
294
+
295
+ AI: [Connects to sandbox with user's API key]
296
+ AI: [Takes screenshot to see login page]
297
+ AI: [Finds username field: await testdriver_find({ description: "username field" })]
298
+ AI: [Clicks and types: await testdriver_type({ text: "test_user" })]
299
+ AI: [Finds password field, enters password]
300
+ AI: [Clicks login button]
301
+ AI: [Asserts login succeeded]
302
+ AI: [Generates Vitest test file from these steps]
303
+ AI: [Saves test/login.test.mjs]
304
+ ```
305
+
306
+ ### Key Features
307
+
308
+ - **Persistent sandbox** - Connection stays alive throughout test creation
309
+ - **Live debugger URL** - User can watch the AI test in real-time
310
+ - **Full SDK access** - All v7 SDK methods available
311
+ - **Code generation** - AI translates interactions into proper Vitest code
312
+
313
+ See [mcp-server/TEST_CREATION_GUIDE.md](mcp-server/TEST_CREATION_GUIDE.md) for the complete guide.
314
+
@@ -37,11 +37,11 @@ steps:
37
37
  - command: hover-text
38
38
  text: New Text Document
39
39
  description: new text document icon in the center of the desktop
40
- action: drag-start
40
+ action: mouseDown
41
41
  - command: hover-text
42
42
  text: Recycle Bin
43
43
  description: recycle bin icon in the top left corner of the desktop
44
- action: drag-end
44
+ action: mouseUp
45
45
 
46
46
  - prompt: '"New Text Document" icon is not on the Desktop'
47
47
  commands:
@@ -1,4 +1,4 @@
1
- version: 5.7.7
1
+ perversion: 5.7.7
2
2
  session: 682f6071811bd5a322c0e6dd
3
3
  steps:
4
4
  - prompt: focus chrome