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
@@ -1,10 +1,19 @@
1
1
  name: AWS
2
-
3
2
  on:
4
3
  workflow_dispatch:
5
4
  push:
5
+ branches:
6
+ - main
6
7
  paths-ignore:
7
8
  - "docs/**"
9
+ pull_request:
10
+ branches:
11
+ - main
12
+ types:
13
+ - ready_for_review
14
+ pull_request_review:
15
+ types:
16
+ - submitted
8
17
 
9
18
  jobs:
10
19
  gather:
@@ -54,6 +63,7 @@ jobs:
54
63
  - name: Setup AWS Instance
55
64
  id: aws-setup
56
65
  run: |
66
+ chmod +x ./setup/aws/spawn-runner.sh
57
67
  OUTPUT=$(./setup/aws/spawn-runner.sh | tee /dev/stderr) # Capture and display output
58
68
  echo "$OUTPUT"
59
69
  PUBLIC_IP=$(echo "$OUTPUT" | grep "PUBLIC_IP=" | cut -d'=' -f2)
@@ -68,9 +78,8 @@ jobs:
68
78
  AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
69
79
  AWS_REGION: us-east-2
70
80
  AWS_LAUNCH_TEMPLATE_ID: lt-00d02f31cfc602f27
71
- AMI_ID: ami-085f872ca0cd80fed
72
- RESOLUTION_WIDTH: 1920
73
- RESOLUTION_HEIGHT: 1080
81
+ AMI_ID: ami-055cd47506a2f39bb
82
+ RESOLUTION: 1920x1080
74
83
  - name: Run TestDriver
75
84
  run: node bin/testdriverai.js run testdriver/acceptance/${{ matrix.test }} --ip="${{ steps.aws-setup.outputs.public-ip }}" --junit=out.xml
76
85
  env:
@@ -168,8 +168,15 @@ Resources:
168
168
  IpProtocol: tcp,
169
169
  FromPort: 8765,
170
170
  ToPort: 8765,
171
- CidrIp: !Ref AllowedIngressCidr,
172
- Description: "pyautogui-cli WebSockets",
171
+ CidrIp: 35.171.123.200/32,
172
+ Description: "pyautogui-cli WebSockets - Static IP 1",
173
+ }
174
+ - {
175
+ IpProtocol: tcp,
176
+ FromPort: 8765,
177
+ ToPort: 8765,
178
+ CidrIp: 52.201.199.222/32,
179
+ Description: "pyautogui-cli WebSockets - Static IP 2",
173
180
  }
174
181
  - {
175
182
  IpProtocol: tcp,
@@ -0,0 +1,469 @@
1
+ /**
2
+ * Dashcam Class
3
+ * Manages Dashcam CLI recording lifecycle
4
+ *
5
+ * Provides a clean interface for:
6
+ * - Authentication
7
+ * - Log tracking
8
+ * - Starting/stopping recordings
9
+ * - Retrieving replay URLs
10
+ */
11
+
12
+ class Dashcam {
13
+ /**
14
+ * Create a Dashcam instance
15
+ * @param {Object} client - TestDriver client instance
16
+ * @param {Object} options - Configuration options
17
+ * @param {string} [options.apiKey] - Dashcam API key
18
+ * @param {boolean} [options.autoStart=false] - Auto-start recording
19
+ * @param {Array} [options.logs=[]] - Log configurations to add
20
+ */
21
+ constructor(client, options = {}) {
22
+ if (!client) {
23
+ throw new Error('Dashcam requires a TestDriver client instance');
24
+ }
25
+
26
+ this.client = client;
27
+ // Use provided apiKey, or client's apiKey, or fallback to a default
28
+ this.apiKey = options.apiKey || client.apiKey || client.config?.TD_API_KEY || '4e93d8bf-3886-4d26-a144-116c4063522d';
29
+ this.autoStart = options.autoStart ?? false;
30
+ this.logs = options.logs || [];
31
+ this.recording = false;
32
+ this._authenticated = false;
33
+ this.startTime = null; // Track when dashcam recording started
34
+ }
35
+
36
+ /**
37
+ * Get shell type based on client OS
38
+ * @private
39
+ */
40
+ _getShell() {
41
+ return this.client.os === 'windows' ? 'pwsh' : 'sh';
42
+ }
43
+
44
+ /**
45
+ * Get TD_API_ROOT from client config
46
+ * @private
47
+ */
48
+ _getApiRoot() {
49
+ return this.client.config?.TD_API_ROOT || 'http://localhost:1337';
50
+ }
51
+
52
+ /**
53
+ * Get dashcam executable path
54
+ * @private
55
+ */
56
+ async _getDashcamPath() {
57
+ const shell = this._getShell();
58
+ const npmPrefix = await this.client.exec(shell, 'npm prefix -g', 40000, true);
59
+
60
+ if (this.client.os === 'windows') {
61
+ return npmPrefix.trim() + '\\dashcam.cmd';
62
+ }
63
+ return npmPrefix.trim() + '/bin/dashcam';
64
+ }
65
+
66
+ /**
67
+ * Authenticate dashcam with API key
68
+ * @param {string} [apiKey] - Override API key
69
+ * @returns {Promise<void>}
70
+ */
71
+ async auth(apiKey) {
72
+ const key = apiKey || this.apiKey;
73
+ const shell = this._getShell();
74
+ const apiRoot = this._getApiRoot();
75
+
76
+ if (this.client.os === 'windows') {
77
+ // Debug session info
78
+ const debug = await this.client.exec(shell, 'query session', 40000, true);
79
+ this._log('debug', 'Debug version output:', debug);
80
+
81
+ // Uninstall and clear cache for fresh install
82
+ await this.client.exec(shell, 'npm uninstall dashcam -g', 40000, true);
83
+ await this.client.exec(shell, 'npm cache clean --force', 40000, true);
84
+
85
+ // Install dashcam with TD_API_ROOT environment variable
86
+ const installOutput = await this.client.exec(
87
+ shell,
88
+ `$env:TD_API_ROOT="${apiRoot}"; npm install dashcam@beta -g`,
89
+ 120000,
90
+ true
91
+ );
92
+ this._log('debug', 'Install dashcam output:', installOutput);
93
+
94
+ // Verify version
95
+ const latestVersion = await this.client.exec(
96
+ shell,
97
+ 'npm view dashcam@beta version',
98
+ 40000,
99
+ true
100
+ );
101
+ this._log('debug', 'Latest beta version available:', latestVersion);
102
+
103
+ const dashcamPath = await this._getDashcamPath();
104
+ this._log('debug', 'Dashcam executable path:', dashcamPath);
105
+
106
+ const installedVersion = await this.client.exec(
107
+ shell,
108
+ 'npm ls dashcam -g',
109
+ 40000,
110
+ true
111
+ );
112
+ this._log('debug', 'Installed dashcam version:', installedVersion);
113
+
114
+ // Test version command
115
+ const versionTest = await this.client.exec(
116
+ shell,
117
+ `& "${dashcamPath}" version`,
118
+ 40000,
119
+ true
120
+ );
121
+ this._log('debug', 'Dashcam version test:', versionTest);
122
+
123
+ // Verify installation
124
+ if (!installedVersion) {
125
+ this._log('error', 'Dashcam version command returned null/empty');
126
+ this._log('debug', 'Install output was:', installOutput);
127
+ } else if (!installedVersion.includes('1.3.')) {
128
+ this._log('warn', 'Dashcam version may be outdated. Expected 1.3.x, got:', installedVersion);
129
+ } else {
130
+ this._log('debug', 'Dashcam version verified:', installedVersion);
131
+ }
132
+
133
+ // Authenticate with TD_API_ROOT
134
+ const authOutput = await this.client.exec(
135
+ shell,
136
+ `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" auth ${key}`,
137
+ 120000,
138
+ true
139
+ );
140
+ this._log('debug', 'Auth output:', authOutput);
141
+ } else {
142
+ // Linux/Mac authentication with TD_API_ROOT
143
+ const authOutput = await this.client.exec(
144
+ shell,
145
+ `TD_API_ROOT="${apiRoot}" dashcam auth ${key}`,
146
+ 120000,
147
+ true
148
+ );
149
+ this._log('debug', 'Auth output:', authOutput);
150
+ }
151
+
152
+ this._authenticated = true;
153
+ }
154
+
155
+ /**
156
+ * Add file log tracking
157
+ * @param {string} path - Path to log file
158
+ * @param {string} name - Display name
159
+ * @returns {Promise<void>}
160
+ */
161
+ async addFileLog(path, name) {
162
+ const shell = this._getShell();
163
+ const apiRoot = this._getApiRoot();
164
+
165
+ if (this.client.os === 'windows') {
166
+ // Create log file if it doesn't exist
167
+ const createFileOutput = await this.client.exec(
168
+ shell,
169
+ `New-Item -ItemType File -Path "${path}" -Force`,
170
+ 10000,
171
+ true
172
+ );
173
+ this._log('debug', 'Create log file output:', createFileOutput);
174
+
175
+ const dashcamPath = await this._getDashcamPath();
176
+ const addLogOutput = await this.client.exec(
177
+ shell,
178
+ `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" logs --add --type=file --file="${path}" --name="${name}"`,
179
+ 120000,
180
+ true
181
+ );
182
+ this._log('debug', 'Add log tracking output:', addLogOutput);
183
+ } else {
184
+ // Create log file
185
+ await this.client.exec(shell, `touch ${path}`, 10000, true);
186
+
187
+ // Add log tracking with TD_API_ROOT
188
+ const addLogOutput = await this.client.exec(
189
+ shell,
190
+ `TD_API_ROOT="${apiRoot}" dashcam logs --add --type=file --file="${path}" --name="${name}"`,
191
+ 10000,
192
+ true
193
+ );
194
+ this._log('debug', 'Add log tracking output:', addLogOutput);
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Add application log tracking
200
+ * @param {string} application - Application name
201
+ * @param {string} name - Display name
202
+ * @returns {Promise<void>}
203
+ */
204
+ async addApplicationLog(application, name) {
205
+ const shell = this._getShell();
206
+ const dashcamPath = await this._getDashcamPath();
207
+ const apiRoot = this._getApiRoot();
208
+
209
+ if (this.client.os === 'windows') {
210
+ const addLogOutput = await this.client.exec(
211
+ shell,
212
+ `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" logs --add --type=application --application="${application}" --name="${name}"`,
213
+ 120000,
214
+ true
215
+ );
216
+ this._log('debug', 'Add application log tracking output:', addLogOutput);
217
+ } else {
218
+ const addLogOutput = await this.client.exec(
219
+ shell,
220
+ `TD_API_ROOT="${apiRoot}" dashcam logs --add --type=application --application="${application}" --name="${name}"`,
221
+ 10000,
222
+ true
223
+ );
224
+ this._log('debug', 'Add application log tracking output:', addLogOutput);
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Add web log tracking
230
+ * @param {string} pattern - URL pattern to match (e.g., "*example.com*")
231
+ * @param {string} name - Display name
232
+ * @returns {Promise<void>}
233
+ */
234
+ async addWebLog(pattern, name) {
235
+ const shell = this._getShell();
236
+ const dashcamPath = await this._getDashcamPath();
237
+ const apiRoot = this._getApiRoot();
238
+
239
+ if (this.client.os === 'windows') {
240
+ const addLogOutput = await this.client.exec(
241
+ shell,
242
+ `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" logs --add --type=web --pattern="${pattern}" --name="${name}"`,
243
+ 120000,
244
+ true
245
+ );
246
+ this._log('debug', 'Add web log tracking output:', addLogOutput);
247
+ } else {
248
+ const addLogOutput = await this.client.exec(
249
+ shell,
250
+ `TD_API_ROOT="${apiRoot}" dashcam logs --add --type=web --pattern="${pattern}" --name="${name}"`,
251
+ 10000,
252
+ true
253
+ );
254
+ this._log('debug', 'Add web log tracking output:', addLogOutput);
255
+ }
256
+ }
257
+
258
+ /**
259
+ * Start dashcam recording
260
+ * @returns {Promise<void>}
261
+ */
262
+ async start() {
263
+ if (this.recording) {
264
+ this._log('warn', 'Dashcam already recording');
265
+ return;
266
+ }
267
+
268
+ // Auto-authenticate if not already done
269
+ if (!this._authenticated) {
270
+ this._log('info', 'Auto-authenticating dashcam...');
271
+ await this.auth();
272
+ }
273
+
274
+ const shell = this._getShell();
275
+ const apiRoot = this._getApiRoot();
276
+
277
+ if (this.client.os === 'windows') {
278
+ this._log('info', 'Starting dashcam recording on Windows...');
279
+
280
+ const dashcamPath = await this._getDashcamPath();
281
+ this._log('debug', 'Dashcam path:', dashcamPath);
282
+
283
+ // Verify dashcam exists
284
+ const dashcamExists = await this.client.exec(
285
+ shell,
286
+ `Test-Path "${dashcamPath}"`,
287
+ 10000,
288
+ true
289
+ );
290
+ this._log('debug', 'Dashcam.cmd exists:', dashcamExists);
291
+
292
+ // Start dashcam record and redirect output with TD_API_ROOT
293
+ const outputFile = 'C:\\Users\\testdriver\\.dashcam-cli\\dashcam-start.log';
294
+ const startScript = `
295
+ try {
296
+ $env:TD_API_ROOT="${apiRoot}"
297
+ $process = Start-Process "cmd.exe" -ArgumentList "/c", "${dashcamPath} record > ${outputFile} 2>&1" -PassThru
298
+ Write-Output "Process started with PID: $($process.Id)"
299
+ Start-Sleep -Seconds 2
300
+ if ($process.HasExited) {
301
+ Write-Output "Process has already exited with code: $($process.ExitCode)"
302
+ } else {
303
+ Write-Output "Process is still running"
304
+ }
305
+ } catch {
306
+ Write-Output "ERROR: $_"
307
+ }
308
+ `;
309
+
310
+ const startOutput = await this.client.exec(shell, startScript, 10000, true);
311
+ this._log('debug', 'Start-Process output:', startOutput);
312
+
313
+ // Wait and check output
314
+ await new Promise(resolve => setTimeout(resolve, 2000));
315
+ const dashcamOutput = await this.client.exec(
316
+ shell,
317
+ `Get-Content "${outputFile}" -ErrorAction SilentlyContinue`,
318
+ 10000,
319
+ true
320
+ );
321
+ this._log('debug', 'Dashcam record output:', dashcamOutput);
322
+
323
+ // Give process time to initialize
324
+ await new Promise(resolve => setTimeout(resolve, 5000));
325
+
326
+ this._log('info', 'Dashcam recording started');
327
+ } else {
328
+ // Linux/Mac with TD_API_ROOT
329
+ this._log('info', 'Starting dashcam recording on Linux/Mac...');
330
+ await this.client.exec(shell, `TD_API_ROOT="${apiRoot}" dashcam record >/dev/null 2>&1 &`);
331
+ this._log('info', 'Dashcam recording started');
332
+ }
333
+
334
+ this.recording = true;
335
+ this.startTime = Date.now(); // Record the timestamp when dashcam started
336
+
337
+ // Update the session with dashcam start time for interaction timestamp synchronization
338
+ if (this.client && this.client.agent && this.client.agent.session) {
339
+ try {
340
+ const apiRoot = this.apiRoot || process.env.TD_API_ROOT || 'https://app.testdriver.ai';
341
+ const response = await fetch(`${apiRoot}/api/v7.0.0/testdriver/session/${this.client.agent.session}/update-dashcam-time`, {
342
+ method: 'POST',
343
+ headers: {
344
+ 'Content-Type': 'application/json',
345
+ 'Authorization': `Bearer ${this.apiKey}`
346
+ },
347
+ body: JSON.stringify({ dashcamStartTime: this.startTime })
348
+ });
349
+
350
+ if (response.ok) {
351
+ this._log('info', `Updated session ${this.client.agent.session} with dashcam start time: ${this.startTime}`);
352
+ } else {
353
+ this._log('warn', 'Failed to update session with dashcam start time:', response.statusText);
354
+ }
355
+ } catch (err) {
356
+ this._log('warn', 'Error updating session with dashcam start time:', err.message);
357
+ }
358
+ }
359
+ }
360
+
361
+ /**
362
+ * Stop dashcam recording and retrieve replay URL
363
+ * @returns {Promise<string|null>} Replay URL if available
364
+ */
365
+ async stop() {
366
+ if (!this.recording) {
367
+ // Internal log only - don't spam user console
368
+ this._log('warn', 'Dashcam not recording');
369
+ return null;
370
+ }
371
+
372
+ this._log('info', 'Stopping dashcam and retrieving URL...');
373
+ const shell = this._getShell();
374
+ const apiRoot = this._getApiRoot();
375
+ let output;
376
+
377
+ if (this.client.os === 'windows') {
378
+ this._log('info', 'Stopping dashcam process on Windows...');
379
+
380
+ const dashcamPath = await this._getDashcamPath();
381
+
382
+ // Stop and get output with TD_API_ROOT
383
+ output = await this.client.exec(shell, `$env:TD_API_ROOT="${apiRoot}"; & "${dashcamPath}" stop`, 120000);
384
+ this._log('debug', 'Dashcam stop command output:', output);
385
+ } else {
386
+ // Linux/Mac with TD_API_ROOT
387
+ const dashcamPath = await this._getDashcamPath();
388
+ output = await this.client.exec(shell, `TD_API_ROOT="${apiRoot}" "${dashcamPath}" stop`, 60000, false);
389
+ this._log('debug', 'Dashcam command output:', output);
390
+ }
391
+
392
+ this.recording = false;
393
+
394
+ // Extract URL from output
395
+ if (output) {
396
+ // Look for replay URL with optional query parameters (most specific)
397
+ // Matches: http://localhost:3001/replay/abc123?share=xyz or https://app.dashcam.io/replay/abc123
398
+ const replayUrlMatch = output.match(/https?:\/\/[^\s"',}]+\/replay\/[^\s"',}]+/);
399
+ if (replayUrlMatch) {
400
+ let url = replayUrlMatch[0];
401
+ // Remove trailing punctuation but keep query params
402
+ url = url.replace(/[.,;:!\)\]]+$/, '').trim();
403
+ this._log('info', 'Found dashcam URL:', url);
404
+ return url;
405
+ }
406
+
407
+ // Fallback: any dashcam.io or testdriver.ai URL
408
+ const dashcamUrlMatch = output.match(/https?:\/\/(?:app\.)?(?:dashcam\.io|testdriver\.ai)[^\s"',}]+/);
409
+ if (dashcamUrlMatch) {
410
+ let url = dashcamUrlMatch[0];
411
+ url = url.replace(/[.,;:!\?\)\]]+$/, '').trim();
412
+ this._log('info', 'Found dashcam URL:', url);
413
+ return url;
414
+ }
415
+
416
+ this._log('warn', 'No replay URL found in dashcam output');
417
+ } else {
418
+ this._log('warn', 'Dashcam command returned no output');
419
+ }
420
+
421
+ return null;
422
+ }
423
+
424
+ /**
425
+ * Internal logging - writes to testdriver log file but not user console
426
+ * @private
427
+ */
428
+ _log(level, ...args) {
429
+ const message = args.map(arg =>
430
+ typeof arg === 'object' ? JSON.stringify(arg, null, 2) : String(arg)
431
+ ).join(' ');
432
+
433
+ const timestamp = new Date().toISOString();
434
+ const logLine = `[${timestamp}] [DASHCAM:${level.toUpperCase()}] ${message}`;
435
+
436
+ // Send to sandbox log file via output command (same as console interceptor)
437
+ if (this.client?.sandbox?.instanceSocketConnected) {
438
+ try {
439
+ this.client.sandbox.send({
440
+ type: "output",
441
+ output: Buffer.from(logLine, "utf8").toString("base64"),
442
+ });
443
+ } catch {
444
+ // Silently fail
445
+ }
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Check if currently recording
451
+ * @returns {Promise<boolean>}
452
+ */
453
+ async isRecording() {
454
+ return this.recording;
455
+ }
456
+
457
+ /**
458
+ * Get milliseconds elapsed since dashcam started recording
459
+ * @returns {number|null} Milliseconds since start, or null if not recording
460
+ */
461
+ getElapsedTime() {
462
+ if (!this.recording || !this.startTime) {
463
+ return null;
464
+ }
465
+ return Date.now() - this.startTime;
466
+ }
467
+ }
468
+
469
+ module.exports = Dashcam;
@@ -0,0 +1,150 @@
1
+ /**
2
+ * TypeScript definitions for TestDriver Core Module
3
+ * @module testdriverai/core
4
+ */
5
+
6
+ export class Dashcam {
7
+ /**
8
+ * Create a new Dashcam instance
9
+ * @param client - TestDriver client instance
10
+ * @param options - Dashcam options
11
+ */
12
+ constructor(client: any, options?: DashcamOptions);
13
+
14
+ /**
15
+ * Authenticate with Dashcam CLI
16
+ * @param apiKey - Dashcam API key (optional, uses DASHCAM_API_KEY env var if not provided)
17
+ * @returns Promise that resolves when authenticated
18
+ */
19
+ auth(apiKey?: string): Promise<void>;
20
+
21
+ /**
22
+ * Add a log entry to Dashcam
23
+ * @param config - Log configuration
24
+ */
25
+ addLog(config: LogConfig): Promise<void>;
26
+
27
+ /**
28
+ * Add a file log to Dashcam
29
+ * @param path - Path to file to log
30
+ * @param name - Name/description for the log entry
31
+ */
32
+ addFileLog(path: string, name: string): Promise<void>;
33
+
34
+ /**
35
+ * Add an application log to Dashcam
36
+ * @param application - Application name to track
37
+ * @param name - Name/description for the log entry
38
+ */
39
+ addApplicationLog(application: string, name: string): Promise<void>;
40
+
41
+ /**
42
+ * Start recording
43
+ * @returns Promise that resolves when recording starts
44
+ */
45
+ start(): Promise<void>;
46
+
47
+ /**
48
+ * Stop recording and get replay URL
49
+ * @returns Promise that resolves to the replay URL (or null if not recording)
50
+ */
51
+ stop(): Promise<string | null>;
52
+
53
+ /**
54
+ * Check if currently recording
55
+ * @returns true if recording, false otherwise
56
+ */
57
+ isRecording(): boolean;
58
+ }
59
+
60
+ export interface DashcamOptions {
61
+ /**
62
+ * Dashcam API key (defaults to DASHCAM_API_KEY env var)
63
+ */
64
+ apiKey?: string;
65
+ }
66
+
67
+ export interface LogConfig {
68
+ /**
69
+ * Type of log entry
70
+ */
71
+ type: 'file' | 'application';
72
+
73
+ /**
74
+ * Path to file (for file logs)
75
+ */
76
+ path?: string;
77
+
78
+ /**
79
+ * Application name (for application logs)
80
+ */
81
+ application?: string;
82
+
83
+ /**
84
+ * Name/description for the log entry
85
+ */
86
+ name: string;
87
+ }
88
+
89
+ /**
90
+ * TestDriver SDK class
91
+ * Re-exported from main module for convenience
92
+ */
93
+ export class TestDriver {
94
+ constructor(apiKey: string, options?: TestDriverOptions);
95
+
96
+ auth(): Promise<void>;
97
+ connect(options?: ConnectOptions): Promise<any>;
98
+ disconnect(): Promise<void>;
99
+
100
+ find(query: string): Promise<any>;
101
+ findAll(query: string): Promise<any[]>;
102
+ click(target: string): Promise<void>;
103
+ type(target: string, text: string): Promise<void>;
104
+ exec(shell: string, command: string, timeout?: number, ignoreError?: boolean): Promise<string>;
105
+ focusApplication(appName: string): Promise<void>;
106
+
107
+ // Add other TestDriver methods as needed
108
+ }
109
+
110
+ export interface TestDriverOptions {
111
+ /**
112
+ * API endpoint URL
113
+ */
114
+ apiRoot?: string;
115
+
116
+ /**
117
+ * Target OS: 'linux', 'mac', or 'windows'
118
+ */
119
+ os?: 'linux' | 'mac' | 'windows';
120
+
121
+ /**
122
+ * Create new sandbox
123
+ */
124
+ newSandbox?: boolean;
125
+
126
+ /**
127
+ * Screen resolution
128
+ */
129
+ resolution?: string;
130
+
131
+ /**
132
+ * Enable analytics
133
+ */
134
+ analytics?: boolean;
135
+
136
+ /**
137
+ * Cache thresholds for find operations
138
+ */
139
+ cacheThresholds?: {
140
+ find?: number;
141
+ findAll?: number;
142
+ };
143
+ }
144
+
145
+ export interface ConnectOptions {
146
+ /**
147
+ * Create new sandbox instance
148
+ */
149
+ new?: boolean;
150
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Core SDK Exports
3
+ * Main exports for the TestDriver SDK
4
+ */
5
+
6
+ const TestDriver = require('../../sdk.js');
7
+ const Dashcam = require('./Dashcam.js');
8
+
9
+ module.exports = {
10
+ TestDriver,
11
+ Dashcam,
12
+ };