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
@@ -0,0 +1,331 @@
1
+ /**
2
+ * TestDriver Presets
3
+ *
4
+ * DEPRECATED: These presets are maintained for backwards compatibility.
5
+ * New code should use the simpler API:
6
+ *
7
+ * @example
8
+ * import { TestDriver } from 'testdriverai/vitest/hooks';
9
+ *
10
+ * test('my test', async (context) => {
11
+ * const testdriver = TestDriver(context, { headless: true });
12
+ *
13
+ * await testdriver.connected();
14
+ * await testdriver.dashcam.start();
15
+ * await testdriver.provision.chrome({ url: 'https://example.com' });
16
+ * await testdriver.find('Login').click();
17
+ * await testdriver.dashcam.stop();
18
+ * });
19
+ */
20
+
21
+ import { TestDriver } from '../vitest/hooks.mjs';
22
+
23
+ /**
24
+ * Chrome Browser Preset (DEPRECATED)
25
+ * Use testdriver.provision.chrome() instead
26
+ */
27
+ export async function chrome(context, options = {}) {
28
+ console.warn('[chrome preset] DEPRECATED: Use TestDriver() + testdriver.provision.chrome() instead');
29
+
30
+ const {
31
+ url = 'http://testdriver-sandbox.vercel.app/',
32
+ dashcam: enableDashcam = false,
33
+ ...testDriverOptions
34
+ } = options;
35
+
36
+ const testdriver = TestDriver(context, testDriverOptions);
37
+
38
+ // Wait for connection to complete if autoConnect was enabled
39
+ if (testdriver.__connectionPromise) {
40
+ await testdriver.__connectionPromise;
41
+ }
42
+
43
+ if (enableDashcam) {
44
+ await testdriver.dashcam.start();
45
+ }
46
+
47
+ await testdriver.provision.chrome({ url });
48
+
49
+ return {
50
+ testdriver: testdriver,
51
+ dashcam: enableDashcam ? testdriver.dashcam : null,
52
+ };
53
+ }
54
+
55
+ /**
56
+ * VS Code Preset
57
+ * Automatically sets up VS Code with TestDriver and Dashcam
58
+ *
59
+ * @param {object} context - Vitest test context
60
+ * @param {object} options - Preset options (accepts all useTestDriver options)
61
+ * @param {string} [options.workspace] - Workspace/folder to open
62
+ * @param {string[]} [options.extensions=[]] - Extensions to install
63
+ * @param {boolean} [options.dashcam=true] - Enable Dashcam recording
64
+ * @param {boolean} [options.newSandbox=true] - Create new sandbox
65
+ * @param {string} [options.os='linux'] - Target OS (linux/mac/windows)
66
+ * @param {string} [options.apiKey] - TestDriver API key
67
+ * @param {string} [options.apiRoot] - API endpoint
68
+ * @param {boolean} [options.autoConnect=true] - Automatically connect to sandbox
69
+ * @returns {Promise<{testdriver: TestDriver, vscode: TestDriver, dashcam: Dashcam}>}
70
+ *
71
+ * @example
72
+ * test('extension test', async (context) => {
73
+ * const { vscode } = await vscode(context, {
74
+ * workspace: '/tmp/test-project',
75
+ * extensions: ['ms-python.python']
76
+ * });
77
+ *
78
+ * await vscode.find('File menu').click();
79
+ * await vscode.find('New File').click();
80
+ * });
81
+ */
82
+ export async function vscode(context, options = {}) {
83
+ // Extract vscode-specific options
84
+ const {
85
+ workspace = null,
86
+ dashcam: enableDashcam = true,
87
+ extensions = [],
88
+ // All other options are passed directly to useTestDriver
89
+ ...testDriverOptions
90
+ } = options;
91
+
92
+ // Set up TestDriver client - all options pass through directly
93
+ const client = TestDriver(context, testDriverOptions);
94
+
95
+ // Wait for client to connect (if autoConnect was enabled)
96
+ if (client.__connectionPromise) {
97
+ await client.__connectionPromise;
98
+ }
99
+
100
+ // Set up Dashcam if enabled (dashcam is built into TestDriver)
101
+ if (enableDashcam) {
102
+ await client.dashcam.start();
103
+ }
104
+
105
+ // Install extensions if provided
106
+ for (const extension of extensions) {
107
+ const os = testDriverOptions.os || 'linux';
108
+ const shell = os === 'windows' ? 'pwsh' : 'sh';
109
+ await client.exec(
110
+ shell,
111
+ `code --install-extension ${extension}`,
112
+ 60000,
113
+ true
114
+ );
115
+ }
116
+
117
+ // Launch VS Code
118
+ const os = testDriverOptions.os || 'linux';
119
+ const shell = os === 'windows' ? 'pwsh' : 'sh';
120
+ const workspaceArg = workspace ? `"${workspace}"` : '';
121
+
122
+ if (os === 'windows') {
123
+ await client.exec(
124
+ shell,
125
+ `Start-Process code -ArgumentList ${workspaceArg}`,
126
+ 30000
127
+ );
128
+ } else {
129
+ await client.exec(
130
+ shell,
131
+ `code ${workspaceArg} >/dev/null 2>&1 &`,
132
+ 30000
133
+ );
134
+ }
135
+
136
+ // Wait for VS Code to be ready
137
+ await client.focusApplication('Visual Studio Code');
138
+
139
+ return {
140
+ testdriver: client,
141
+ vscode: client, // Alias for semantic clarity
142
+ dashcam: enableDashcam ? client.dashcam : null,
143
+ };
144
+ }
145
+
146
+ /**
147
+ * Create a custom preset
148
+ * Builder function for creating your own presets
149
+ *
150
+ * @param {object} config - Preset configuration
151
+ * @param {Function} config.setup - Setup function (async)
152
+ * @param {string} config.name - Preset name
153
+ * @param {object} config.defaults - Default options (accepts all useTestDriver options)
154
+ * @returns {Function} Preset function
155
+ *
156
+ * @example
157
+ * const myElectronPreset = createPreset({
158
+ * name: 'Electron App',
159
+ * defaults: { os: 'linux', dashcam: true },
160
+ * async setup(context, client, dashcam, options) {
161
+ * await client.exec('sh', `electron ${options.appPath} &`, 30000);
162
+ * await client.focusApplication('Electron');
163
+ * return { app: client };
164
+ * }
165
+ * });
166
+ *
167
+ * // Use your custom preset
168
+ * test('my test', async (context) => {
169
+ * const { app } = await myElectronPreset(context, {
170
+ * appPath: './dist',
171
+ * newSandbox: false // All useTestDriver options work!
172
+ * });
173
+ * });
174
+ */
175
+ export function createPreset(config) {
176
+ const { setup, name = 'Custom Preset', defaults = {} } = config;
177
+
178
+ if (typeof setup !== 'function') {
179
+ throw new Error(`Preset "${name}" requires a setup function`);
180
+ }
181
+
182
+ return async function preset(context, options = {}) {
183
+ const finalOptions = { ...defaults, ...options };
184
+ const {
185
+ dashcam: enableDashcam = true,
186
+ // All other options are passed directly to TestDriver
187
+ ...testDriverOptions
188
+ } = finalOptions;
189
+
190
+ // Set up TestDriver client - all options pass through directly
191
+ const client = TestDriver(context, testDriverOptions);
192
+
193
+ // Wait for client to connect (if autoConnect was enabled)
194
+ if (client.__connectionPromise) {
195
+ await client.__connectionPromise;
196
+ }
197
+
198
+ // Set up Dashcam if enabled (dashcam is built into TestDriver)
199
+ if (enableDashcam) {
200
+ await client.dashcam.start();
201
+ }
202
+
203
+ // Call user's setup function
204
+ const result = await setup(context, client, enableDashcam ? client.dashcam : null, finalOptions);
205
+
206
+ // Ensure we return testdriver and dashcam
207
+ return {
208
+ testdriver: client,
209
+ dashcam: enableDashcam ? client.dashcam : null,
210
+ ...result,
211
+ };
212
+ };
213
+ }
214
+
215
+ /**
216
+ * Run Electron App
217
+ * Automatically sets up an Electron application with TestDriver
218
+ *
219
+ * @param {object} context - Vitest test context
220
+ * @param {object} options - Preset options (accepts all useTestDriver options)
221
+ * @param {string} options.appPath - Path to Electron app (required)
222
+ * @param {string[]} [options.args=[]] - Additional electron args
223
+ * @param {boolean} [options.dashcam=true] - Enable Dashcam recording
224
+ * @param {boolean} [options.newSandbox=true] - Create new sandbox
225
+ * @param {string} [options.os='linux'] - Target OS (linux/mac/windows)
226
+ * @param {string} [options.apiKey] - TestDriver API key
227
+ * @param {string} [options.apiRoot] - API endpoint
228
+ * @param {boolean} [options.autoConnect=true] - Automatically connect to sandbox
229
+ * @returns {Promise<{testdriver: TestDriver, app: TestDriver, dashcam: Dashcam}>}
230
+ */
231
+ export const electron = createPreset({
232
+ name: 'Electron App',
233
+ defaults: { dashcam: true, args: [] },
234
+ async setup(context, client, dashcam, options) {
235
+ const { appPath, args = [], os = 'linux' } = options;
236
+
237
+ if (!appPath) {
238
+ throw new Error('electron preset requires appPath option');
239
+ }
240
+
241
+ const shell = os === 'windows' ? 'pwsh' : 'sh';
242
+ const argsString = args.join(' ');
243
+
244
+ if (os === 'windows') {
245
+ await client.exec(
246
+ shell,
247
+ `Start-Process electron -ArgumentList "${appPath}", ${argsString}`,
248
+ 30000
249
+ );
250
+ } else {
251
+ await client.exec(
252
+ shell,
253
+ `electron "${appPath}" ${argsString} >/dev/null 2>&1 &`,
254
+ 30000
255
+ );
256
+ }
257
+
258
+ await client.focusApplication('Electron');
259
+
260
+ return {
261
+ testdriver: client,
262
+ app: client,
263
+ };
264
+ },
265
+ });
266
+
267
+ /**
268
+ * Run Web App (generic browser)
269
+ * Simplified preset for any web application
270
+ *
271
+ * @param {object} context - Vitest test context
272
+ * @param {object} options - Preset options (accepts all useTestDriver options)
273
+ * @param {string} options.url - URL to navigate to (required)
274
+ * @param {string} [options.browser='chrome'] - Browser to use: 'chrome', 'firefox', 'edge'
275
+ * @param {boolean} [options.newSandbox=true] - Create new sandbox
276
+ * @param {string} [options.os='linux'] - Target OS (linux/mac/windows)
277
+ * @param {boolean} [options.dashcam=true] - Enable Dashcam recording
278
+ * @returns {Promise<{testdriver: TestDriver, dashcam: Dashcam}>}
279
+ */
280
+ export async function webApp(context, options = {}) {
281
+ const { browser = 'chrome', ...restOptions } = options;
282
+
283
+ // Currently only Chrome is implemented
284
+ // All options are automatically forwarded to the browser preset
285
+ if (browser === 'chrome') {
286
+ return chrome(context, restOptions);
287
+ }
288
+
289
+ throw new Error(`Browser "${browser}" not yet implemented. Use 'chrome' for now.`);
290
+ }
291
+
292
+ /**
293
+ * Provision application preset
294
+ * Main entry point for provisioning any application preset
295
+ *
296
+ * @param {string} app - Application type: 'chrome', 'vscode', 'electron', 'webapp'
297
+ * @param {object} options - Preset options (varies by app type)
298
+ * @param {object} context - Vitest test context
299
+ * @returns {Promise<{testdriver: TestDriver, dashcam: Dashcam, ...}>}
300
+ *
301
+ * @example
302
+ * test('my test', async (context) => {
303
+ * const { testdriver } = await provision('chrome', {
304
+ * url: 'https://example.com'
305
+ * }, context);
306
+ *
307
+ * await testdriver.find('button').click();
308
+ * });
309
+ */
310
+ export async function provision(app, options = {}, context) {
311
+ const presets = {
312
+ chrome,
313
+ vscode,
314
+ electron,
315
+ webapp: webApp,
316
+ };
317
+
318
+ const preset = presets[app.toLowerCase()];
319
+
320
+ if (!preset) {
321
+ throw new Error(`Unknown app type "${app}". Available: ${Object.keys(presets).join(', ')}`);
322
+ }
323
+
324
+ return preset(context, options);
325
+ }
326
+
327
+ // Export aliases for backwards compatibility
328
+ export const chromePreset = chrome;
329
+ export const vscodePreset = vscode;
330
+ export const electronPreset = electron;
331
+ export const webAppPreset = webApp;
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Extended Vitest test functions for TestDriver
3
+ *
4
+ * Provides custom test modifiers:
5
+ * - it.once() - Only runs once per sandbox session (skipped on reconnect)
6
+ *
7
+ * The reconnection state is determined by the TestDriver SDK after connect().
8
+ *
9
+ * @example
10
+ * import { describe, it, beforeAll, expect } from 'testdriverai/vitest';
11
+ * import TestDriver from 'testdriverai';
12
+ *
13
+ * describe('My Test', () => {
14
+ * let testdriver;
15
+ *
16
+ * beforeAll(async () => {
17
+ * testdriver = new TestDriver(process.env.TD_API_KEY);
18
+ * await testdriver.connect();
19
+ *
20
+ * // Store globally so it.once() can access it
21
+ * globalThis.__testdriver = testdriver;
22
+ * });
23
+ *
24
+ * // Only runs once per sandbox session (skipped on reconnect)
25
+ * it.once('launch the application', async () => {
26
+ * await testdriver.exec('sh', 'google-chrome https://example.com', 5000);
27
+ * });
28
+ *
29
+ * // Always runs
30
+ * it('click the button', async () => {
31
+ * await testdriver.find('Button').click();
32
+ * });
33
+ * });
34
+ */
35
+
36
+ import { afterAll, beforeAll, expect, describe as vitestDescribe, it as vitestIt, test as vitestTest } from 'vitest';
37
+
38
+ /**
39
+ * Get the TestDriver instance from global state
40
+ * @returns {Object|null} TestDriver instance or null
41
+ */
42
+ function getTestDriver() {
43
+ return globalThis.__testdriver || null;
44
+ }
45
+
46
+ /**
47
+ * Check if we're reconnected to an existing sandbox
48
+ * Uses the SDK's isReconnected property set after connect()
49
+ * @returns {boolean} true if reconnected to existing sandbox
50
+ */
51
+ function isReconnected() {
52
+ const testdriver = getTestDriver();
53
+ if (!testdriver) {
54
+ // No testdriver yet - assume new sandbox (setup should run)
55
+ return false;
56
+ }
57
+ return testdriver.isReconnected === true;
58
+ }
59
+
60
+ /**
61
+ * Extended test function with .once() modifier
62
+ */
63
+ function createExtendedIt(baseIt) {
64
+ const extended = function(name, fn, timeout) {
65
+ return baseIt(name, fn, timeout);
66
+ };
67
+
68
+ // Copy all properties from base it
69
+ Object.assign(extended, baseIt);
70
+
71
+ /**
72
+ * it.once() - Only runs once per sandbox session, skipped on reconnect
73
+ * Use for provisioning, app launch, initial navigation
74
+ *
75
+ * The test checks testdriver.isReconnected which is set by the SDK
76
+ * after connect() based on whether it reconnected to an existing
77
+ * sandbox or created a new one.
78
+ */
79
+ extended.once = function(name, fn, timeout) {
80
+ return baseIt(name, async (...args) => {
81
+ if (isReconnected()) {
82
+ console.log(`⏭️ Skipping (already run in this sandbox): ${name}`);
83
+ return;
84
+ }
85
+ return fn(...args);
86
+ }, timeout);
87
+ };
88
+
89
+ // Preserve skip, only, todo, etc.
90
+ if (baseIt.skip) extended.skip = baseIt.skip;
91
+ if (baseIt.only) extended.only = baseIt.only;
92
+ if (baseIt.todo) extended.todo = baseIt.todo;
93
+ if (baseIt.concurrent) extended.concurrent = baseIt.concurrent;
94
+ if (baseIt.sequential) extended.sequential = baseIt.sequential;
95
+
96
+ return extended;
97
+ }
98
+
99
+ // Create extended test functions
100
+ export const it = createExtendedIt(vitestIt);
101
+ export const test = createExtendedIt(vitestTest);
102
+
103
+ // Re-export other vitest functions unchanged
104
+ export { afterAll, beforeAll, vitestDescribe as describe, expect };
105
+
106
+ // Also export utility for manual checking
107
+ export { getTestDriver, isReconnected };
108
+
@@ -0,0 +1,119 @@
1
+ /**
2
+ * TypeScript definitions for TestDriver Vitest Hooks
3
+ * @module testdriverai/vitest/hooks
4
+ */
5
+
6
+ import { Dashcam, DashcamOptions, TestDriver, TestDriverOptions } from '../core/index';
7
+
8
+ /**
9
+ * Vitest test context (from test function parameter)
10
+ */
11
+ export interface VitestContext {
12
+ /**
13
+ * Current test task
14
+ */
15
+ task: any;
16
+
17
+ /**
18
+ * Register cleanup handler
19
+ */
20
+ onTestFinished?: (fn: () => void | Promise<void>) => void;
21
+ }
22
+
23
+ /**
24
+ * Options for useTestDriver hook
25
+ */
26
+ export interface UseTestDriverOptions extends TestDriverOptions {
27
+ /**
28
+ * Automatically connect to sandbox (default: true)
29
+ */
30
+ autoConnect?: boolean;
31
+
32
+ /**
33
+ * Create new sandbox (default: true)
34
+ */
35
+ new?: boolean;
36
+ }
37
+
38
+ /**
39
+ * Options for useDashcam hook
40
+ */
41
+ export interface UseDashcamOptions extends DashcamOptions {
42
+ /**
43
+ * Automatically authenticate (default: true)
44
+ */
45
+ autoAuth?: boolean;
46
+
47
+ /**
48
+ * Automatically start recording (default: false)
49
+ */
50
+ autoStart?: boolean;
51
+
52
+ /**
53
+ * Automatically stop recording at test end (default: false)
54
+ */
55
+ autoStop?: boolean;
56
+ }
57
+
58
+ /**
59
+ * Use TestDriver client in a test
60
+ * Creates and manages TestDriver instance for the current test
61
+ *
62
+ * @param context - Vitest test context (from async (context) => {})
63
+ * @param options - TestDriver options
64
+ * @returns TestDriver client instance
65
+ *
66
+ * @example
67
+ * test('my test', async (context) => {
68
+ * const client = useTestDriver(context, { os: 'linux' });
69
+ * await client.find('Login button').click();
70
+ * });
71
+ */
72
+ export function useTestDriver(context: VitestContext, options?: UseTestDriverOptions): TestDriver;
73
+
74
+ /**
75
+ * Use Dashcam in a test
76
+ * Creates and manages Dashcam instance for the current test
77
+ *
78
+ * @param context - Vitest test context
79
+ * @param client - TestDriver client instance (from useTestDriver)
80
+ * @param options - Dashcam options
81
+ * @returns Dashcam instance
82
+ *
83
+ * @example
84
+ * test('my test', async (context) => {
85
+ * const client = useTestDriver(context);
86
+ * const dashcam = useDashcam(context, client, {
87
+ * autoStart: true,
88
+ * autoStop: true
89
+ * });
90
+ *
91
+ * await client.find('button').click();
92
+ * });
93
+ */
94
+ export function useDashcam(context: VitestContext, client: TestDriver, options?: UseDashcamOptions): Dashcam;
95
+
96
+ /**
97
+ * Use TestDriver with Dashcam in one call
98
+ * Combined hook for the simplest usage pattern
99
+ *
100
+ * @param context - Vitest test context
101
+ * @param options - Combined options for TestDriver and Dashcam
102
+ * @returns Object with client and dashcam instances
103
+ *
104
+ * @example
105
+ * test('my test', async (context) => {
106
+ * const { client, dashcam } = useTestDriverWithDashcam(context, {
107
+ * os: 'linux'
108
+ * });
109
+ *
110
+ * await client.find('button').click();
111
+ * });
112
+ */
113
+ export function useTestDriverWithDashcam(
114
+ context: VitestContext,
115
+ options?: UseTestDriverOptions & UseDashcamOptions
116
+ ): {
117
+ client: TestDriver;
118
+ dashcam: Dashcam;
119
+ };