@testdriverai/agent 7.8.0-canary.10

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 (528) hide show
  1. package/.claude/settings.local.json +7 -0
  2. package/.env.example +4 -0
  3. package/.prettierignore +4 -0
  4. package/.prettierrc +1 -0
  5. package/CHANGELOG.md +953 -0
  6. package/README.md +81 -0
  7. package/agent/events.js +135 -0
  8. package/agent/index.js +2450 -0
  9. package/agent/interface.js +35 -0
  10. package/agent/lib/analytics.js +22 -0
  11. package/agent/lib/censorship.js +75 -0
  12. package/agent/lib/commander.js +246 -0
  13. package/agent/lib/commands.js +1684 -0
  14. package/agent/lib/config.js +60 -0
  15. package/agent/lib/generator.js +91 -0
  16. package/agent/lib/http.js +144 -0
  17. package/agent/lib/logger.js +56 -0
  18. package/agent/lib/outputs.js +29 -0
  19. package/agent/lib/parser.js +209 -0
  20. package/agent/lib/redraw.js +386 -0
  21. package/agent/lib/resources/cursor-2.png +0 -0
  22. package/agent/lib/sandbox.js +1104 -0
  23. package/agent/lib/sdk.js +633 -0
  24. package/agent/lib/session.js +25 -0
  25. package/agent/lib/source-mapper.js +342 -0
  26. package/agent/lib/subimage/index.js +77 -0
  27. package/agent/lib/subimage/opencv.js +69 -0
  28. package/agent/lib/system.js +204 -0
  29. package/agent/lib/theme.js +14 -0
  30. package/agent/lib/valid-version.js +21 -0
  31. package/agent/lib/validation.js +169 -0
  32. package/ai/.claude-plugin/plugin.json +9 -0
  33. package/ai/agents/testdriver.md +638 -0
  34. package/ai/skills/testdriver-ai/SKILL.md +204 -0
  35. package/ai/skills/testdriver-assert/SKILL.md +315 -0
  36. package/ai/skills/testdriver-aws-setup/SKILL.md +448 -0
  37. package/ai/skills/testdriver-cache/SKILL.md +221 -0
  38. package/ai/skills/testdriver-caching/SKILL.md +124 -0
  39. package/ai/skills/testdriver-captcha/SKILL.md +158 -0
  40. package/ai/skills/testdriver-ci-cd/SKILL.md +602 -0
  41. package/ai/skills/testdriver-click/SKILL.md +286 -0
  42. package/ai/skills/testdriver-client/SKILL.md +477 -0
  43. package/ai/skills/testdriver-cloud/SKILL.md +119 -0
  44. package/ai/skills/testdriver-customizing-devices/SKILL.md +319 -0
  45. package/ai/skills/testdriver-dashcam/SKILL.md +418 -0
  46. package/ai/skills/testdriver-debugging-with-screenshots/SKILL.md +401 -0
  47. package/ai/skills/testdriver-device-config/SKILL.md +317 -0
  48. package/ai/skills/testdriver-double-click/SKILL.md +102 -0
  49. package/ai/skills/testdriver-elements/SKILL.md +605 -0
  50. package/ai/skills/testdriver-enterprise/SKILL.md +114 -0
  51. package/ai/skills/testdriver-errors/SKILL.md +246 -0
  52. package/ai/skills/testdriver-events/SKILL.md +356 -0
  53. package/ai/skills/testdriver-examples/SKILL.md +7 -0
  54. package/ai/skills/testdriver-exec/SKILL.md +317 -0
  55. package/ai/skills/testdriver-find/SKILL.md +829 -0
  56. package/ai/skills/testdriver-focus-application/SKILL.md +293 -0
  57. package/ai/skills/testdriver-generating-tests/SKILL.md +36 -0
  58. package/ai/skills/testdriver-hover/SKILL.md +278 -0
  59. package/ai/skills/testdriver-locating-elements/SKILL.md +71 -0
  60. package/ai/skills/testdriver-making-assertions/SKILL.md +32 -0
  61. package/ai/skills/testdriver-mcp/SKILL.md +7 -0
  62. package/ai/skills/testdriver-mcp-workflow/SKILL.md +410 -0
  63. package/ai/skills/testdriver-mouse-down/SKILL.md +161 -0
  64. package/ai/skills/testdriver-mouse-up/SKILL.md +164 -0
  65. package/ai/skills/testdriver-parse/SKILL.md +236 -0
  66. package/ai/skills/testdriver-performing-actions/SKILL.md +54 -0
  67. package/ai/skills/testdriver-press-keys/SKILL.md +348 -0
  68. package/ai/skills/testdriver-provision/SKILL.md +331 -0
  69. package/ai/skills/testdriver-quickstart/SKILL.md +144 -0
  70. package/ai/skills/testdriver-redraw/SKILL.md +214 -0
  71. package/ai/skills/testdriver-reusable-code/SKILL.md +249 -0
  72. package/ai/skills/testdriver-right-click/SKILL.md +123 -0
  73. package/ai/skills/testdriver-running-tests/SKILL.md +185 -0
  74. package/ai/skills/testdriver-screenshot/SKILL.md +248 -0
  75. package/ai/skills/testdriver-screenshots/SKILL.md +184 -0
  76. package/ai/skills/testdriver-scroll/SKILL.md +335 -0
  77. package/ai/skills/testdriver-secrets/SKILL.md +115 -0
  78. package/ai/skills/testdriver-self-hosted/SKILL.md +65 -0
  79. package/ai/skills/testdriver-test-writer/SKILL.md +448 -0
  80. package/ai/skills/testdriver-testdriver/SKILL.md +628 -0
  81. package/ai/skills/testdriver-testdriver-mechanic/SKILL.md +165 -0
  82. package/ai/skills/testdriver-type/SKILL.md +357 -0
  83. package/ai/skills/testdriver-variables/SKILL.md +111 -0
  84. package/ai/skills/testdriver-wait/SKILL.md +50 -0
  85. package/ai/skills/testdriver-waiting-for-elements/SKILL.md +90 -0
  86. package/ai/skills/testdriver-what-is-testdriver/SKILL.md +54 -0
  87. package/bin/testdriverai.js +22 -0
  88. package/debugger/bg.png +0 -0
  89. package/debugger/icon.png +0 -0
  90. package/debugger/index.html +469 -0
  91. package/debugger/td.png +0 -0
  92. package/debugger/tray-buffered.png +0 -0
  93. package/debugger/tray.png +0 -0
  94. package/docs/GITHUB_COMMENTS.md +330 -0
  95. package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +167 -0
  96. package/docs/QUICK-START-GITHUB-COMMENTS.md +84 -0
  97. package/docs/TEST-GITHUB-COMMENTS.md +129 -0
  98. package/docs/_data/examples-manifest.json +177 -0
  99. package/docs/_data/examples-manifest.schema.json +41 -0
  100. package/docs/_scripts/extract-example-urls.js +165 -0
  101. package/docs/_scripts/generate-examples.js +560 -0
  102. package/docs/_scripts/generate-skills.js +154 -0
  103. package/docs/_scripts/link-replacer.js +164 -0
  104. package/docs/_scripts/upload-docs-to-openai.js +284 -0
  105. package/docs/changelog.mdx +161 -0
  106. package/docs/claude-mcp-plugin.mdx +160 -0
  107. package/docs/docs.json +442 -0
  108. package/docs/github-integration-setup.md +266 -0
  109. package/docs/guide/best-practices-polling.mdx +174 -0
  110. package/docs/images/content/account/newprojectsettings.png +0 -0
  111. package/docs/images/content/account/projectpage.png +0 -0
  112. package/docs/images/content/account/projectreplays.png +0 -0
  113. package/docs/images/content/account/team-manage.png +0 -0
  114. package/docs/images/content/account/teampage.png +0 -0
  115. package/docs/images/content/extension/cursor.svg +1 -0
  116. package/docs/images/content/extension/vscode.svg +57 -0
  117. package/docs/images/content/extension/windsurf.svg +3 -0
  118. package/docs/images/content/parse/output.png +0 -0
  119. package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
  120. package/docs/images/content/side-by-side.png +0 -0
  121. package/docs/images/content/vscode/ide-full.png +0 -0
  122. package/docs/images/content/vscode/running.png +0 -0
  123. package/docs/images/content/vscode/v7-chat.png +0 -0
  124. package/docs/images/content/vscode/v7-choose-agent.png +0 -0
  125. package/docs/images/content/vscode/v7-full.png +0 -0
  126. package/docs/images/content/vscode/v7-onboarding.png +0 -0
  127. package/docs/images/content/vscode/vscode-2-assert.png +0 -0
  128. package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
  129. package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
  130. package/docs/images/content/vscode/vscode-file-creation.png +0 -0
  131. package/docs/images/content/vscode/vscode-install.png +0 -0
  132. package/docs/images/content/vscode/vscode-overview.png +0 -0
  133. package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
  134. package/docs/images/content/vscode/vscode-stopchat.png +0 -0
  135. package/docs/images/content/vscode/vscode-stoptest.png +0 -0
  136. package/docs/images/content/vscode/vscode-tdservice.png +0 -0
  137. package/docs/images/content/vscode/vscode-test-output.png +0 -0
  138. package/docs/images/content/vscode/vscode-testhistory.png +0 -0
  139. package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
  140. package/docs/images/content/vscode/vscode-testpane.png +0 -0
  141. package/docs/images/template/dark.png +0 -0
  142. package/docs/images/template/icon.png +0 -0
  143. package/docs/images/template/light.png +0 -0
  144. package/docs/snippets/calendar-link.mdx +4 -0
  145. package/docs/snippets/gitignore-warning.mdx +7 -0
  146. package/docs/snippets/lifecycle-warning.mdx +6 -0
  147. package/docs/snippets/test-prereqs.mdx +12 -0
  148. package/docs/snippets/tests/assert-replay.mdx +7 -0
  149. package/docs/snippets/tests/assert-yaml.mdx +8 -0
  150. package/docs/snippets/tests/exec-js-replay.mdx +7 -0
  151. package/docs/snippets/tests/exec-js-yaml.mdx +32 -0
  152. package/docs/snippets/tests/exec-shell-replay.mdx +7 -0
  153. package/docs/snippets/tests/exec-shell-yaml.mdx +15 -0
  154. package/docs/snippets/tests/hover-image-replay.mdx +7 -0
  155. package/docs/snippets/tests/hover-image-yaml.mdx +17 -0
  156. package/docs/snippets/tests/hover-text-replay.mdx +7 -0
  157. package/docs/snippets/tests/hover-text-with-description-replay.mdx +7 -0
  158. package/docs/snippets/tests/hover-text-with-description-yaml.mdx +24 -0
  159. package/docs/snippets/tests/hover-text-yaml.mdx +14 -0
  160. package/docs/snippets/tests/match-image-replay.mdx +7 -0
  161. package/docs/snippets/tests/match-image-yaml.mdx +17 -0
  162. package/docs/snippets/tests/press-keys-replay.mdx +7 -0
  163. package/docs/snippets/tests/press-keys-yaml.mdx +36 -0
  164. package/docs/snippets/tests/remember-replay.mdx +7 -0
  165. package/docs/snippets/tests/remember-yaml.mdx +28 -0
  166. package/docs/snippets/tests/scroll-replay.mdx +7 -0
  167. package/docs/snippets/tests/scroll-until-image-replay.mdx +7 -0
  168. package/docs/snippets/tests/scroll-until-image-yaml.mdx +14 -0
  169. package/docs/snippets/tests/scroll-until-text-replay.mdx +7 -0
  170. package/docs/snippets/tests/scroll-until-text-yaml.mdx +17 -0
  171. package/docs/snippets/tests/scroll-yaml.mdx +30 -0
  172. package/docs/snippets/tests/type-repeated-replay.mdx +7 -0
  173. package/docs/snippets/tests/type-repeated-yaml.mdx +22 -0
  174. package/docs/snippets/tests/type-replay.mdx +7 -0
  175. package/docs/snippets/tests/type-yaml.mdx +28 -0
  176. package/docs/snippets/tests/wait-for-image-replay.mdx +7 -0
  177. package/docs/snippets/tests/wait-for-image-yaml.mdx +18 -0
  178. package/docs/snippets/tests/wait-for-text-replay.mdx +7 -0
  179. package/docs/snippets/tests/wait-for-text-yaml.mdx +18 -0
  180. package/docs/snippets/tests/wait-replay.mdx +7 -0
  181. package/docs/snippets/tests/wait-yaml.mdx +13 -0
  182. package/docs/styles.css +65 -0
  183. package/docs/v6/account/dashboard.mdx +16 -0
  184. package/docs/v6/account/enterprise.mdx +110 -0
  185. package/docs/v6/account/pricing.mdx +33 -0
  186. package/docs/v6/account/projects.mdx +33 -0
  187. package/docs/v6/account/team.mdx +35 -0
  188. package/docs/v6/action/ami.mdx +109 -0
  189. package/docs/v6/action/performance.mdx +105 -0
  190. package/docs/v6/action/secrets.mdx +93 -0
  191. package/docs/v6/apps/chrome-extensions.mdx +48 -0
  192. package/docs/v6/apps/desktop-apps.mdx +93 -0
  193. package/docs/v6/apps/mobile-apps.mdx +26 -0
  194. package/docs/v6/apps/static-websites.mdx +54 -0
  195. package/docs/v6/apps/tauri-apps.mdx +361 -0
  196. package/docs/v6/bugs/jira.mdx +232 -0
  197. package/docs/v6/cli/overview.mdx +66 -0
  198. package/docs/v6/commands/assert.mdx +45 -0
  199. package/docs/v6/commands/exec.mdx +276 -0
  200. package/docs/v6/commands/focus-application.mdx +44 -0
  201. package/docs/v6/commands/hover-image.mdx +69 -0
  202. package/docs/v6/commands/hover-text.mdx +47 -0
  203. package/docs/v6/commands/if.mdx +53 -0
  204. package/docs/v6/commands/match-image.mdx +67 -0
  205. package/docs/v6/commands/press-keys.mdx +87 -0
  206. package/docs/v6/commands/remember.mdx +49 -0
  207. package/docs/v6/commands/run.mdx +44 -0
  208. package/docs/v6/commands/scroll-until-image.mdx +66 -0
  209. package/docs/v6/commands/scroll-until-text.mdx +60 -0
  210. package/docs/v6/commands/scroll.mdx +69 -0
  211. package/docs/v6/commands/type.mdx +45 -0
  212. package/docs/v6/commands/wait-for-image.mdx +54 -0
  213. package/docs/v6/commands/wait-for-text.mdx +48 -0
  214. package/docs/v6/commands/wait.mdx +45 -0
  215. package/docs/v6/exporting/junit.mdx +218 -0
  216. package/docs/v6/exporting/playwright.mdx +197 -0
  217. package/docs/v6/features/auto-healing.mdx +144 -0
  218. package/docs/v6/features/generation.mdx +116 -0
  219. package/docs/v6/features/parallel-testing.mdx +151 -0
  220. package/docs/v6/features/reusable-snippets.mdx +131 -0
  221. package/docs/v6/features/selectorless.mdx +80 -0
  222. package/docs/v6/features/visual-assertions.mdx +139 -0
  223. package/docs/v6/getting-started/ci.mdx +146 -0
  224. package/docs/v6/getting-started/cli.mdx +91 -0
  225. package/docs/v6/getting-started/editing.mdx +100 -0
  226. package/docs/v6/getting-started/playwright.mdx +342 -0
  227. package/docs/v6/getting-started/running.mdx +48 -0
  228. package/docs/v6/getting-started/self-hosting.mdx +408 -0
  229. package/docs/v6/getting-started/vscode.mdx +88 -0
  230. package/docs/v6/guide/assertions.mdx +189 -0
  231. package/docs/v6/guide/authentication.mdx +136 -0
  232. package/docs/v6/guide/code.mdx +65 -0
  233. package/docs/v6/guide/dashcam.mdx +118 -0
  234. package/docs/v6/guide/environment-variables.mdx +26 -0
  235. package/docs/v6/guide/lifecycle.mdx +242 -0
  236. package/docs/v6/guide/locating.mdx +141 -0
  237. package/docs/v6/guide/protips.mdx +43 -0
  238. package/docs/v6/guide/variables.mdx +143 -0
  239. package/docs/v6/guide/waiting.mdx +130 -0
  240. package/docs/v6/importing/csv.mdx +196 -0
  241. package/docs/v6/importing/gherkin.mdx +143 -0
  242. package/docs/v6/importing/jira.mdx +164 -0
  243. package/docs/v6/importing/testrail.mdx +162 -0
  244. package/docs/v6/integrations/electron.mdx +146 -0
  245. package/docs/v6/integrations/netlify.mdx +100 -0
  246. package/docs/v6/integrations/vercel.mdx +125 -0
  247. package/docs/v6/interactive/explore.mdx +99 -0
  248. package/docs/v6/interactive/run.mdx +52 -0
  249. package/docs/v6/interactive/save.mdx +63 -0
  250. package/docs/v6/overview/comparison.mdx +101 -0
  251. package/docs/v6/overview/faq.mdx +162 -0
  252. package/docs/v6/overview/performance.mdx +52 -0
  253. package/docs/v6/overview/quickstart.mdx +137 -0
  254. package/docs/v6/overview/what-is-testdriver.mdx +85 -0
  255. package/docs/v6/scenarios/ai-chatbot.mdx +28 -0
  256. package/docs/v6/scenarios/cookie-banner.mdx +32 -0
  257. package/docs/v6/scenarios/file-upload.mdx +33 -0
  258. package/docs/v6/scenarios/form-filling.mdx +32 -0
  259. package/docs/v6/scenarios/log-in.mdx +75 -0
  260. package/docs/v6/scenarios/pdf-generation.mdx +25 -0
  261. package/docs/v6/scenarios/spell-check.mdx +22 -0
  262. package/docs/v6/security/action.mdx +84 -0
  263. package/docs/v6/security/agent.mdx +73 -0
  264. package/docs/v6/security/platform.mdx +77 -0
  265. package/docs/v6/tutorials/advanced-test.mdx +81 -0
  266. package/docs/v6/tutorials/basic-test.mdx +45 -0
  267. package/docs/v7/_drafts/agents.mdx +843 -0
  268. package/docs/v7/_drafts/architecture.mdx +399 -0
  269. package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
  270. package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +100 -0
  271. package/docs/v7/_drafts/best-practices.mdx +486 -0
  272. package/docs/v7/_drafts/caching-ai.mdx +215 -0
  273. package/docs/v7/_drafts/caching-selectors.mdx +424 -0
  274. package/docs/v7/_drafts/caching.mdx +366 -0
  275. package/docs/v7/_drafts/cli-to-sdk-migration.mdx +425 -0
  276. package/docs/v7/_drafts/commands/assert.mdx +45 -0
  277. package/docs/v7/_drafts/commands/exec.mdx +276 -0
  278. package/docs/v7/_drafts/commands/focus-application.mdx +44 -0
  279. package/docs/v7/_drafts/commands/hover-image.mdx +69 -0
  280. package/docs/v7/_drafts/commands/hover-text.mdx +47 -0
  281. package/docs/v7/_drafts/commands/if.mdx +53 -0
  282. package/docs/v7/_drafts/commands/match-image.mdx +67 -0
  283. package/docs/v7/_drafts/commands/press-keys.mdx +87 -0
  284. package/docs/v7/_drafts/commands/remember.mdx +49 -0
  285. package/docs/v7/_drafts/commands/run.mdx +44 -0
  286. package/docs/v7/_drafts/commands/scroll-until-image.mdx +66 -0
  287. package/docs/v7/_drafts/commands/scroll-until-text.mdx +60 -0
  288. package/docs/v7/_drafts/commands/scroll.mdx +69 -0
  289. package/docs/v7/_drafts/commands/type.mdx +45 -0
  290. package/docs/v7/_drafts/commands/wait-for-image.mdx +54 -0
  291. package/docs/v7/_drafts/commands/wait-for-text.mdx +48 -0
  292. package/docs/v7/_drafts/commands/wait.mdx +45 -0
  293. package/docs/v7/_drafts/configuration.mdx +378 -0
  294. package/docs/v7/_drafts/contributing.mdx +174 -0
  295. package/docs/v7/_drafts/core.mdx +458 -0
  296. package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
  297. package/docs/v7/_drafts/debugging.mdx +349 -0
  298. package/docs/v7/_drafts/error-handling.mdx +501 -0
  299. package/docs/v7/_drafts/faq.mdx +393 -0
  300. package/docs/v7/_drafts/hooks.mdx +360 -0
  301. package/docs/v7/_drafts/init-command.mdx +95 -0
  302. package/docs/v7/_drafts/installation.mdx +420 -0
  303. package/docs/v7/_drafts/migration.mdx +562 -0
  304. package/docs/v7/_drafts/observable.mdx +604 -0
  305. package/docs/v7/_drafts/playwright.mdx +342 -0
  306. package/docs/v7/_drafts/plugin-migration.mdx +220 -0
  307. package/docs/v7/_drafts/powerful.mdx +419 -0
  308. package/docs/v7/_drafts/presets.mdx +210 -0
  309. package/docs/v7/_drafts/progressive-disclosure.mdx +230 -0
  310. package/docs/v7/_drafts/prompt-cache.mdx +200 -0
  311. package/docs/v7/_drafts/provision.mdx +390 -0
  312. package/docs/v7/_drafts/quick-start-test-recording.mdx +214 -0
  313. package/docs/v7/_drafts/readme.mdx +135 -0
  314. package/docs/v7/_drafts/reports.mdx +414 -0
  315. package/docs/v7/_drafts/scalable.mdx +763 -0
  316. package/docs/v7/_drafts/screenshot.mdx +155 -0
  317. package/docs/v7/_drafts/sdk-awesome-logs.mdx +468 -0
  318. package/docs/v7/_drafts/sdk-browser-rendering.mdx +167 -0
  319. package/docs/v7/_drafts/sdk-migration.mdx +474 -0
  320. package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
  321. package/docs/v7/_drafts/self-hosting.mdx +369 -0
  322. package/docs/v7/_drafts/test-recording.mdx +382 -0
  323. package/docs/v7/_drafts/troubleshooting.mdx +526 -0
  324. package/docs/v7/_drafts/vitest-plugin.mdx +477 -0
  325. package/docs/v7/_drafts/vitest.mdx +535 -0
  326. package/docs/v7/_drafts/writing-tests.mdx +25 -0
  327. package/docs/v7/ai.mdx +205 -0
  328. package/docs/v7/assert.mdx +316 -0
  329. package/docs/v7/aws-setup.mdx +449 -0
  330. package/docs/v7/cache.mdx +223 -0
  331. package/docs/v7/caching.mdx +128 -0
  332. package/docs/v7/captcha.mdx +159 -0
  333. package/docs/v7/ci-cd.mdx +603 -0
  334. package/docs/v7/click.mdx +287 -0
  335. package/docs/v7/client.mdx +478 -0
  336. package/docs/v7/copilot/auto-healing.mdx +265 -0
  337. package/docs/v7/copilot/creating-tests.mdx +156 -0
  338. package/docs/v7/copilot/github.mdx +143 -0
  339. package/docs/v7/copilot/running-tests.mdx +149 -0
  340. package/docs/v7/copilot/setup.mdx +143 -0
  341. package/docs/v7/customizing-devices.mdx +319 -0
  342. package/docs/v7/dashcam.mdx +419 -0
  343. package/docs/v7/debugging-with-screenshots.mdx +402 -0
  344. package/docs/v7/device-config.mdx +317 -0
  345. package/docs/v7/double-click.mdx +102 -0
  346. package/docs/v7/elements.mdx +606 -0
  347. package/docs/v7/enterprise.mdx +9 -0
  348. package/docs/v7/errors.mdx +248 -0
  349. package/docs/v7/events.mdx +358 -0
  350. package/docs/v7/examples/ai.mdx +72 -0
  351. package/docs/v7/examples/assert.mdx +72 -0
  352. package/docs/v7/examples/captcha-api.mdx +92 -0
  353. package/docs/v7/examples/chrome-extension.mdx +132 -0
  354. package/docs/v7/examples/drag-and-drop.mdx +100 -0
  355. package/docs/v7/examples/element-not-found.mdx +67 -0
  356. package/docs/v7/examples/exec-output.mdx +85 -0
  357. package/docs/v7/examples/exec-pwsh.mdx +83 -0
  358. package/docs/v7/examples/focus-window.mdx +62 -0
  359. package/docs/v7/examples/hover-image.mdx +94 -0
  360. package/docs/v7/examples/hover-text.mdx +69 -0
  361. package/docs/v7/examples/installer.mdx +91 -0
  362. package/docs/v7/examples/launch-vscode-linux.mdx +101 -0
  363. package/docs/v7/examples/match-image.mdx +96 -0
  364. package/docs/v7/examples/press-keys.mdx +92 -0
  365. package/docs/v7/examples/scroll-keyboard.mdx +79 -0
  366. package/docs/v7/examples/scroll-until-image.mdx +81 -0
  367. package/docs/v7/examples/scroll-until-text.mdx +109 -0
  368. package/docs/v7/examples/scroll.mdx +81 -0
  369. package/docs/v7/examples/type.mdx +92 -0
  370. package/docs/v7/examples/windows-installer.mdx +89 -0
  371. package/docs/v7/exec.mdx +318 -0
  372. package/docs/v7/find.mdx +830 -0
  373. package/docs/v7/focus-application.mdx +294 -0
  374. package/docs/v7/generating-tests.mdx +36 -0
  375. package/docs/v7/hosted.mdx +158 -0
  376. package/docs/v7/hover.mdx +279 -0
  377. package/docs/v7/locating-elements.mdx +71 -0
  378. package/docs/v7/making-assertions.mdx +32 -0
  379. package/docs/v7/mcp.mdx +9 -0
  380. package/docs/v7/mouse-down.mdx +161 -0
  381. package/docs/v7/mouse-up.mdx +164 -0
  382. package/docs/v7/parse.mdx +237 -0
  383. package/docs/v7/performing-actions.mdx +54 -0
  384. package/docs/v7/press-keys.mdx +349 -0
  385. package/docs/v7/provision.mdx +333 -0
  386. package/docs/v7/quickstart.mdx +173 -0
  387. package/docs/v7/redraw.mdx +216 -0
  388. package/docs/v7/reusable-code.mdx +249 -0
  389. package/docs/v7/right-click.mdx +123 -0
  390. package/docs/v7/running-tests.mdx +185 -0
  391. package/docs/v7/screenshot.mdx +249 -0
  392. package/docs/v7/screenshots.mdx +186 -0
  393. package/docs/v7/scroll.mdx +336 -0
  394. package/docs/v7/secrets.mdx +115 -0
  395. package/docs/v7/self-hosted.mdx +149 -0
  396. package/docs/v7/type.mdx +358 -0
  397. package/docs/v7/variables.mdx +111 -0
  398. package/docs/v7/wait.mdx +52 -0
  399. package/docs/v7/waiting-for-elements.mdx +90 -0
  400. package/docs/v7/what-is-testdriver.mdx +54 -0
  401. package/eslint.config.js +67 -0
  402. package/examples/ai.test.mjs +31 -0
  403. package/examples/assert.test.mjs +47 -0
  404. package/examples/chrome-extension.test.mjs +97 -0
  405. package/examples/config.mjs +5 -0
  406. package/examples/element-not-found.test.mjs +27 -0
  407. package/examples/exec-output.test.mjs +60 -0
  408. package/examples/exec-pwsh.test.mjs +58 -0
  409. package/examples/findall-coffee-icons.test.mjs +42 -0
  410. package/examples/focus-window.test.mjs +37 -0
  411. package/examples/formatted-logging.test.mjs +27 -0
  412. package/examples/hover-image.test.mjs +53 -0
  413. package/examples/hover-text-with-description.test.mjs +57 -0
  414. package/examples/hover-text.test.mjs +28 -0
  415. package/examples/installer.test.mjs +50 -0
  416. package/examples/launch-vscode-linux.test.mjs +55 -0
  417. package/examples/match-image.test.mjs +55 -0
  418. package/examples/parse.test.mjs +19 -0
  419. package/examples/press-keys.test.mjs +44 -0
  420. package/examples/prompt.test.mjs +34 -0
  421. package/examples/scroll-keyboard.test.mjs +38 -0
  422. package/examples/scroll-until-image.test.mjs +40 -0
  423. package/examples/scroll.test.mjs +42 -0
  424. package/examples/type.test.mjs +46 -0
  425. package/examples/windows-installer.test.mjs +54 -0
  426. package/index.js +2 -0
  427. package/interfaces/cli/commands/init.js +438 -0
  428. package/interfaces/cli/commands/setup.js +382 -0
  429. package/interfaces/cli/lib/base.js +285 -0
  430. package/interfaces/cli.js +20 -0
  431. package/interfaces/junit-reporter.js +290 -0
  432. package/interfaces/logger.js +388 -0
  433. package/interfaces/readline.js +234 -0
  434. package/interfaces/shared-test-state.mjs +64 -0
  435. package/interfaces/vitest-plugin.d.ts +115 -0
  436. package/interfaces/vitest-plugin.mjs +1698 -0
  437. package/lib/captcha/solver.js +358 -0
  438. package/lib/core/Dashcam.js +533 -0
  439. package/lib/core/index.d.ts +172 -0
  440. package/lib/core/index.js +12 -0
  441. package/lib/environments.json +18 -0
  442. package/lib/github-comment-formatter.js +263 -0
  443. package/lib/github-comment.mjs +452 -0
  444. package/lib/init-project.js +575 -0
  445. package/lib/presets/index.mjs +331 -0
  446. package/lib/resolve-channel.js +46 -0
  447. package/lib/sentry.js +417 -0
  448. package/lib/vitest/hooks.d.ts +57 -0
  449. package/lib/vitest/hooks.mjs +674 -0
  450. package/lib/vitest/setup-aws.mjs +247 -0
  451. package/lib/vitest/setup-self-hosted.mjs +151 -0
  452. package/lib/vitest/setup.mjs +46 -0
  453. package/manual/captcha-api.test.mjs +51 -0
  454. package/manual/drag-and-drop.test.mjs +59 -0
  455. package/manual/flake-diffthreshold-001.test.mjs +9 -0
  456. package/manual/flake-diffthreshold-01.test.mjs +9 -0
  457. package/manual/flake-diffthreshold-05.test.mjs +9 -0
  458. package/manual/flake-noredraw-cache.test.mjs +9 -0
  459. package/manual/flake-noredraw-nocache.test.mjs +9 -0
  460. package/manual/flake-redraw-cache.test.mjs +9 -0
  461. package/manual/flake-redraw-nocache.test.mjs +9 -0
  462. package/manual/flake-rocket-match.test.mjs +30 -0
  463. package/manual/flake-shared.mjs +51 -0
  464. package/manual/no-provision.test.mjs +31 -0
  465. package/manual/packer-hover-image.test.mjs +176 -0
  466. package/manual/scroll-until-text.test.mjs +68 -0
  467. package/manual/test-init-command.js +223 -0
  468. package/mcp-server/README.md +322 -0
  469. package/mcp-server/dist/codegen.d.ts +9 -0
  470. package/mcp-server/dist/codegen.js +165 -0
  471. package/mcp-server/dist/mcp-app.html +114 -0
  472. package/mcp-server/dist/package.json +1 -0
  473. package/mcp-server/dist/provision-types.d.ts +290 -0
  474. package/mcp-server/dist/provision-types.js +174 -0
  475. package/mcp-server/dist/server.d.ts +6 -0
  476. package/mcp-server/dist/server.mjs +1925 -0
  477. package/mcp-server/dist/session.d.ts +85 -0
  478. package/mcp-server/dist/session.js +152 -0
  479. package/mcp-server/mcp-app.html +28 -0
  480. package/mcp-server/mcp-config.example.json +19 -0
  481. package/mcp-server/package-lock.json +4027 -0
  482. package/mcp-server/package.json +31 -0
  483. package/mcp-server/src/codegen.ts +189 -0
  484. package/mcp-server/src/mcp-app.css +360 -0
  485. package/mcp-server/src/mcp-app.ts +547 -0
  486. package/mcp-server/src/provision-types.ts +209 -0
  487. package/mcp-server/src/server.ts +2391 -0
  488. package/mcp-server/src/session.ts +194 -0
  489. package/mcp-server/tsconfig.json +16 -0
  490. package/mcp-server/vite.config.ts +23 -0
  491. package/package.json +158 -0
  492. package/schema.json +1046 -0
  493. package/scripts/generate-skills.js +94 -0
  494. package/sdk-log-formatter.js +1157 -0
  495. package/sdk.d.ts +1486 -0
  496. package/sdk.js +4336 -0
  497. package/setup/aws/cloudformation.yaml +463 -0
  498. package/setup/aws/disable-defender.sh +42 -0
  499. package/setup/aws/install-dev-runner.sh +79 -0
  500. package/setup/aws/spawn-runner.sh +289 -0
  501. package/test/captcha-solver.test.mjs +152 -0
  502. package/test/chrome-remote-debugging.test.mjs +66 -0
  503. package/test/duckduckgo/experiment.test.mjs +28 -0
  504. package/test/duckduckgo/setup.test.mjs +29 -0
  505. package/test/manual/debug-locate-response.js +82 -0
  506. package/test/manual/reconnect-provision.test.mjs +49 -0
  507. package/test/manual/test-console-logs.test.mjs +42 -0
  508. package/test/manual/test-find-api.js +73 -0
  509. package/test/manual/test-init.sh +54 -0
  510. package/test/manual/test-prompt-cache.js +97 -0
  511. package/test/manual/test-provision-auth.mjs +22 -0
  512. package/test/manual/test-sandbox-render.js +29 -0
  513. package/test/manual/test-sdk-methods.js +15 -0
  514. package/test/manual/test-sdk-refactor.js +53 -0
  515. package/test/manual/test-stack-trace.mjs +57 -0
  516. package/test/manual/verify-element-api.js +89 -0
  517. package/test/manual/verify-types.js +0 -0
  518. package/test/manual-unawaited-promise.test.mjs +31 -0
  519. package/vitest.config.mjs +58 -0
  520. package/vitest.runner.config.mjs +33 -0
  521. package/vscode-extension/.vscodeignore +12 -0
  522. package/vscode-extension/README.md +94 -0
  523. package/vscode-extension/media/icon.png +0 -0
  524. package/vscode-extension/package-lock.json +4126 -0
  525. package/vscode-extension/package.json +86 -0
  526. package/vscode-extension/src/extension.ts +829 -0
  527. package/vscode-extension/testdriverai-0.1.0.vsix +0 -0
  528. package/vscode-extension/tsconfig.json +16 -0
@@ -0,0 +1,829 @@
1
+ import * as crypto from 'crypto';
2
+ import * as fs from 'fs';
3
+ import * as http from 'http';
4
+ import * as os from 'os';
5
+ import * as path from 'path';
6
+ import * as vscode from 'vscode';
7
+ import WebSocket from 'ws';
8
+
9
+ // Store active debugger panels by session ID
10
+ const debuggerPanels: Map<string, vscode.WebviewPanel> = new Map();
11
+ const websocketConnections: Map<string, WebSocket> = new Map();
12
+ let processedSessions: Set<string> = new Set(); // Track sessions we've already opened
13
+
14
+ // File watchers for .testdriver/.previews/ in each workspace folder
15
+ const previewWatchers: Map<string, vscode.FileSystemWatcher> = new Map();
16
+
17
+ // Local HTTP server for receiving session notifications from SDK
18
+ let httpServer: http.Server | undefined;
19
+ let serverPort: number | undefined;
20
+
21
+ // Path to the TestDriver directory (used for IPC between SDK and extension)
22
+ const SESSION_DIR = path.join(os.homedir(), '.testdriver');
23
+ const INSTANCES_DIR = path.join(SESSION_DIR, 'ide-instances');
24
+
25
+ // Generate a unique instance ID for this VS Code window
26
+ const instanceId = crypto.randomUUID();
27
+
28
+ interface SessionData {
29
+ sessionId?: string; // Unique identifier for this test session
30
+ debuggerUrl: string;
31
+ resolution: [number, number];
32
+ testFile?: string;
33
+ os?: string;
34
+ timestamp: number;
35
+ }
36
+
37
+ // Instance registration data written to disk for SDK discovery
38
+ interface InstanceRegistration {
39
+ instanceId: string;
40
+ port: number;
41
+ workspacePaths: string[];
42
+ pid: number;
43
+ timestamp: number;
44
+ }
45
+
46
+ export function activate(context: vscode.ExtensionContext) {
47
+ console.log('TestDriver.ai extension is now active');
48
+
49
+ // Ensure directories exist
50
+ if (!fs.existsSync(SESSION_DIR)) {
51
+ fs.mkdirSync(SESSION_DIR, { recursive: true });
52
+ }
53
+ if (!fs.existsSync(INSTANCES_DIR)) {
54
+ fs.mkdirSync(INSTANCES_DIR, { recursive: true });
55
+ }
56
+
57
+ // Register commands
58
+ const openDebuggerCommand = vscode.commands.registerCommand(
59
+ 'testdriverai.openDebugger',
60
+ () => openDebuggerPanel(context)
61
+ );
62
+
63
+ const closeDebuggerCommand = vscode.commands.registerCommand(
64
+ 'testdriverai.closeDebugger',
65
+ () => closeAllDebuggerPanels()
66
+ );
67
+
68
+ const installMcpCommand = vscode.commands.registerCommand(
69
+ 'testdriverai.installMcp',
70
+ () => installMcpServer()
71
+ );
72
+
73
+ context.subscriptions.push(openDebuggerCommand, closeDebuggerCommand, installMcpCommand);
74
+
75
+ // Start local HTTP server for receiving session notifications
76
+ startHttpServer(context);
77
+
78
+ // Set up file watchers for .testdriver/.previews/ folders
79
+ setupPreviewWatchers(context);
80
+
81
+ // Listen for workspace folder changes to update watchers
82
+ context.subscriptions.push(
83
+ vscode.workspace.onDidChangeWorkspaceFolders(() => {
84
+ setupPreviewWatchers(context);
85
+ })
86
+ );
87
+
88
+ // Auto-install MCP on first activation
89
+ autoInstallMcp();
90
+ }
91
+
92
+ // Start HTTP server to receive session notifications from SDK
93
+ function startHttpServer(context: vscode.ExtensionContext) {
94
+ httpServer = http.createServer((req, res) => {
95
+ // Enable CORS
96
+ res.setHeader('Access-Control-Allow-Origin', '*');
97
+ res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
98
+ res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
99
+
100
+ if (req.method === 'OPTIONS') {
101
+ res.writeHead(200);
102
+ res.end();
103
+ return;
104
+ }
105
+
106
+ if (req.method === 'POST' && req.url === '/session') {
107
+ let body = '';
108
+ req.on('data', chunk => {
109
+ body += chunk.toString();
110
+ });
111
+ req.on('end', () => {
112
+ try {
113
+ const sessionData: SessionData = JSON.parse(body);
114
+ handleSessionNotification(context, sessionData);
115
+ res.writeHead(200, { 'Content-Type': 'application/json' });
116
+ res.end(JSON.stringify({ success: true }));
117
+ } catch (error) {
118
+ console.error('Error parsing session data:', error);
119
+ res.writeHead(400, { 'Content-Type': 'application/json' });
120
+ res.end(JSON.stringify({ error: 'Invalid JSON' }));
121
+ }
122
+ });
123
+ } else if (req.method === 'GET' && req.url === '/health') {
124
+ res.writeHead(200, { 'Content-Type': 'application/json' });
125
+ res.end(JSON.stringify({ status: 'ok', instanceId }));
126
+ } else {
127
+ res.writeHead(404);
128
+ res.end();
129
+ }
130
+ });
131
+
132
+ // Listen on a random available port
133
+ httpServer.listen(0, '127.0.0.1', () => {
134
+ const address = httpServer!.address();
135
+ if (address && typeof address === 'object') {
136
+ serverPort = address.port;
137
+ console.log(`TestDriver extension server listening on port ${serverPort}`);
138
+
139
+ // Register this instance so SDK can discover it
140
+ registerInstance();
141
+ }
142
+ });
143
+
144
+ httpServer.on('error', (error) => {
145
+ console.error('HTTP server error:', error);
146
+ });
147
+ }
148
+
149
+ // Register this VS Code instance for SDK discovery
150
+ function registerInstance() {
151
+ if (!serverPort) {
152
+ return;
153
+ }
154
+
155
+ const workspaceFolders = vscode.workspace.workspaceFolders;
156
+ const workspacePaths = workspaceFolders
157
+ ? workspaceFolders.map(f => f.uri.fsPath)
158
+ : [];
159
+
160
+ const registration: InstanceRegistration = {
161
+ instanceId,
162
+ port: serverPort,
163
+ workspacePaths,
164
+ pid: process.pid,
165
+ timestamp: Date.now()
166
+ };
167
+
168
+ const registrationFile = path.join(INSTANCES_DIR, `${instanceId}.json`);
169
+
170
+ try {
171
+ fs.writeFileSync(registrationFile, JSON.stringify(registration, null, 2));
172
+ console.log(`Registered VS Code instance: ${registrationFile}`);
173
+ } catch (error) {
174
+ console.error('Failed to register instance:', error);
175
+ }
176
+ }
177
+
178
+ // Unregister this instance on deactivation
179
+ function unregisterInstance() {
180
+ const registrationFile = path.join(INSTANCES_DIR, `${instanceId}.json`);
181
+ try {
182
+ if (fs.existsSync(registrationFile)) {
183
+ fs.unlinkSync(registrationFile);
184
+ }
185
+ } catch {
186
+ // Ignore cleanup errors
187
+ }
188
+ }
189
+
190
+ // Handle incoming session notification from SDK
191
+ function handleSessionNotification(context: vscode.ExtensionContext, sessionData: SessionData) {
192
+ // Generate session ID if not present
193
+ if (!sessionData.sessionId) {
194
+ sessionData.sessionId = `session-${Date.now()}`;
195
+ }
196
+
197
+ const config = vscode.workspace.getConfiguration('testdriverai');
198
+ const autoOpen = config.get<boolean>('autoOpenPreview', true);
199
+
200
+ if (autoOpen && !processedSessions.has(sessionData.sessionId)) {
201
+ processedSessions.add(sessionData.sessionId);
202
+ openDebuggerPanel(context, sessionData);
203
+ }
204
+ }
205
+
206
+ // Set up file watchers for .testdriver/.previews/ in each workspace folder
207
+ function setupPreviewWatchers(context: vscode.ExtensionContext) {
208
+ console.log('[TestDriver] Setting up preview watchers...');
209
+
210
+ // Dispose existing watchers
211
+ for (const [_, watcher] of previewWatchers) {
212
+ watcher.dispose();
213
+ }
214
+ previewWatchers.clear();
215
+
216
+ const workspaceFolders = vscode.workspace.workspaceFolders;
217
+ if (!workspaceFolders) {
218
+ console.log('[TestDriver] No workspace folders found');
219
+ return;
220
+ }
221
+
222
+ console.log(`[TestDriver] Found ${workspaceFolders.length} workspace folders`);
223
+
224
+ for (const folder of workspaceFolders) {
225
+ const previewsDir = path.join(folder.uri.fsPath, '.testdriver', '.previews');
226
+ console.log(`[TestDriver] Watching: ${previewsDir}`);
227
+
228
+ // Ensure the previews directory exists
229
+ if (!fs.existsSync(previewsDir)) {
230
+ try {
231
+ fs.mkdirSync(previewsDir, { recursive: true });
232
+ console.log(`[TestDriver] Created directory: ${previewsDir}`);
233
+ } catch (error) {
234
+ console.error(`Failed to create previews directory: ${error}`);
235
+ continue;
236
+ }
237
+ }
238
+
239
+ // Create a file watcher for this workspace's previews folder
240
+ const pattern = new vscode.RelativePattern(folder, '.testdriver/.previews/*.json');
241
+ const watcher = vscode.workspace.createFileSystemWatcher(pattern);
242
+
243
+ // Handle new preview files
244
+ watcher.onDidCreate((uri) => {
245
+ console.log(`[TestDriver] File created: ${uri.fsPath}`);
246
+ handlePreviewFile(context, uri);
247
+ });
248
+
249
+ // Also handle file changes (in case file is created empty then written)
250
+ watcher.onDidChange((uri) => {
251
+ console.log(`[TestDriver] File changed: ${uri.fsPath}`);
252
+ handlePreviewFile(context, uri);
253
+ });
254
+
255
+ previewWatchers.set(folder.uri.fsPath, watcher);
256
+ context.subscriptions.push(watcher);
257
+
258
+ // Clean up any stale preview files from previous sessions (don't open them)
259
+ cleanupStalePreviewFiles(previewsDir);
260
+ }
261
+ }
262
+
263
+ // Clean up stale preview files from previous sessions (don't open them)
264
+ function cleanupStalePreviewFiles(previewsDir: string) {
265
+ try {
266
+ const files = fs.readdirSync(previewsDir);
267
+ for (const file of files) {
268
+ if (file.endsWith('.json')) {
269
+ const filePath = path.join(previewsDir, file);
270
+ try {
271
+ fs.unlinkSync(filePath);
272
+ console.log(`[TestDriver] Cleaned up stale preview file: ${filePath}`);
273
+ } catch {
274
+ // Ignore deletion errors
275
+ }
276
+ }
277
+ }
278
+ } catch {
279
+ // Directory might not exist yet, that's fine
280
+ }
281
+ }
282
+
283
+ // Handle a preview file being created or changed
284
+ function handlePreviewFile(context: vscode.ExtensionContext, uri: vscode.Uri) {
285
+ try {
286
+ const content = fs.readFileSync(uri.fsPath, 'utf-8');
287
+ if (!content.trim()) {
288
+ // File is empty, wait for content
289
+ return;
290
+ }
291
+
292
+ const sessionData: SessionData = JSON.parse(content);
293
+
294
+ // Generate session ID if not present
295
+ if (!sessionData.sessionId) {
296
+ sessionData.sessionId = path.basename(uri.fsPath, '.json');
297
+ }
298
+
299
+ // Check if we've already processed this session
300
+ if (processedSessions.has(sessionData.sessionId)) {
301
+ return;
302
+ }
303
+
304
+ // Use the existing session notification handler
305
+ handleSessionNotification(context, sessionData);
306
+
307
+ // Delete the preview file after processing
308
+ try {
309
+ fs.unlinkSync(uri.fsPath);
310
+ } catch (error) {
311
+ console.error(`Failed to delete preview file: ${error}`);
312
+ }
313
+ } catch (error) {
314
+ console.error(`Error processing preview file ${uri.fsPath}:`, error);
315
+ }
316
+ }
317
+
318
+ // Helper to get test file name from path (just the filename, not full path)
319
+ function getTestFileName(testFile?: string): string {
320
+ if (!testFile) {
321
+ return 'TestDriver';
322
+ }
323
+ // Handle both forward and backslashes
324
+ return testFile.split('/').pop()?.split('\\').pop() || 'TestDriver';
325
+ }
326
+
327
+ // Format the panel title to match debugger.html: [status] filename
328
+ function formatPanelTitle(status: string, testFile?: string): string {
329
+ const fileName = getTestFileName(testFile);
330
+ return `[${status}] ${fileName}`;
331
+ }
332
+
333
+ function openDebuggerPanel(context: vscode.ExtensionContext, sessionData?: SessionData) {
334
+ // Generate or use existing session ID
335
+ const sessionId = sessionData?.sessionId || `manual-${Date.now()}`;
336
+
337
+ // Check if we already have a panel for this session
338
+ const existingPanel = debuggerPanels.get(sessionId);
339
+ if (existingPanel) {
340
+ existingPanel.reveal(vscode.ViewColumn.Active);
341
+ // Update content if we have new session data
342
+ if (sessionData) {
343
+ updateDebuggerContent(existingPanel, sessionData, context, sessionId);
344
+ }
345
+ return;
346
+ }
347
+
348
+ // Determine the initial title
349
+ const initialTitle = sessionData
350
+ ? formatPanelTitle('Loading', sessionData.testFile)
351
+ : 'TestDriver Live Preview';
352
+
353
+ // Create a new webview panel for this session
354
+ const panel = vscode.window.createWebviewPanel(
355
+ 'testdriverDebugger',
356
+ initialTitle,
357
+ vscode.ViewColumn.Beside, // Open beside current editor to show multiple
358
+ {
359
+ enableScripts: true,
360
+ retainContextWhenHidden: true,
361
+ localResourceRoots: [
362
+ vscode.Uri.file(path.join(context.extensionPath, 'media')),
363
+ vscode.Uri.file(path.join(__dirname, '..', '..', 'debugger'))
364
+ ]
365
+ }
366
+ );
367
+
368
+ // Store the panel
369
+ debuggerPanels.set(sessionId, panel);
370
+
371
+ // Set the webview icon
372
+ panel.iconPath = {
373
+ light: vscode.Uri.file(path.join(context.extensionPath, 'media', 'icon.png')),
374
+ dark: vscode.Uri.file(path.join(context.extensionPath, 'media', 'icon.png'))
375
+ };
376
+
377
+ // Handle panel disposal
378
+ panel.onDidDispose(() => {
379
+ debuggerPanels.delete(sessionId);
380
+ disconnectWebSocket(sessionId);
381
+ processedSessions.delete(sessionId);
382
+ }, null, context.subscriptions);
383
+
384
+ // Update content
385
+ if (sessionData) {
386
+ updateDebuggerContent(panel, sessionData, context, sessionId);
387
+ } else {
388
+ // Show waiting state
389
+ panel.webview.html = getWaitingHtml();
390
+ }
391
+ }
392
+
393
+ function updateDebuggerContent(panel: vscode.WebviewPanel, sessionData: SessionData, context: vscode.ExtensionContext, sessionId: string) {
394
+ // Connect to the WebSocket server for live updates
395
+ connectToWebSocket(sessionData.debuggerUrl, panel, sessionId, sessionData.testFile);
396
+
397
+ // Build the data parameter for the debugger
398
+ const data = {
399
+ resolution: sessionData.resolution,
400
+ url: extractVncUrl(sessionData.debuggerUrl),
401
+ token: 'V3b8wG9',
402
+ testFile: sessionData.testFile || null,
403
+ os: sessionData.os || 'linux'
404
+ };
405
+
406
+ const encodedData = Buffer.from(JSON.stringify(data)).toString('base64');
407
+
408
+ // Update the panel title to show it's running
409
+ panel.title = formatPanelTitle('Running', sessionData.testFile);
410
+
411
+ // Update the webview content with the debugger
412
+ panel.webview.html = getDebuggerHtml(sessionData.debuggerUrl, encodedData, panel.webview, context);
413
+ }
414
+
415
+ function extractVncUrl(debuggerUrl: string): string {
416
+ try {
417
+ const url = new URL(debuggerUrl);
418
+ const dataParam = url.searchParams.get('data');
419
+ if (dataParam) {
420
+ const data = JSON.parse(Buffer.from(dataParam, 'base64').toString());
421
+ return data.url || '';
422
+ }
423
+ } catch (error) {
424
+ console.error('Error extracting VNC URL:', error);
425
+ }
426
+ return '';
427
+ }
428
+
429
+ function connectToWebSocket(debuggerUrl: string, panel: vscode.WebviewPanel, sessionId: string, testFile?: string) {
430
+ // Disconnect existing connection for this session
431
+ disconnectWebSocket(sessionId);
432
+
433
+ try {
434
+ const url = new URL(debuggerUrl);
435
+ const wsUrl = `ws://${url.host}`;
436
+
437
+ const ws = new WebSocket(wsUrl);
438
+ websocketConnections.set(sessionId, ws);
439
+
440
+ ws.on('open', () => {
441
+ console.log(`Connected to TestDriver debugger WebSocket for session: ${sessionId}`);
442
+ });
443
+
444
+ ws.on('message', (data: Buffer) => {
445
+ try {
446
+ const message = JSON.parse(data.toString());
447
+ // Forward events to the webview
448
+ panel.webview.postMessage(message);
449
+
450
+ // Update panel title based on test events (matching debugger.html behavior)
451
+ if (message.event) {
452
+ switch (message.event) {
453
+ case 'test:start':
454
+ panel.title = formatPanelTitle('Running', testFile);
455
+ break;
456
+ case 'test:stop':
457
+ panel.title = formatPanelTitle('Stopped', testFile);
458
+ break;
459
+ case 'test:success':
460
+ panel.title = formatPanelTitle('Passed', testFile);
461
+ break;
462
+ case 'test:error':
463
+ panel.title = formatPanelTitle('Failed', testFile);
464
+ break;
465
+ case 'error:fatal':
466
+ case 'error:sdk':
467
+ panel.title = formatPanelTitle('Error', testFile);
468
+ break;
469
+ }
470
+ }
471
+ } catch (error) {
472
+ console.error('Error parsing WebSocket message:', error);
473
+ }
474
+ });
475
+
476
+ ws.on('close', () => {
477
+ console.log(`WebSocket connection closed for session: ${sessionId}`);
478
+ // Update panel title to show disconnected/done state
479
+ panel.title = formatPanelTitle('Done', testFile);
480
+ });
481
+
482
+ ws.on('error', (error: Error) => {
483
+ console.error(`WebSocket error for session ${sessionId}:`, error);
484
+ });
485
+ } catch (error) {
486
+ console.error('Error connecting to WebSocket:', error);
487
+ }
488
+ }
489
+
490
+ function disconnectWebSocket(sessionId: string) {
491
+ const ws = websocketConnections.get(sessionId);
492
+ if (ws) {
493
+ ws.close();
494
+ websocketConnections.delete(sessionId);
495
+ }
496
+ }
497
+
498
+ function closeAllDebuggerPanels() {
499
+ // Close all panels
500
+ for (const [sessionId, panel] of debuggerPanels) {
501
+ panel.dispose();
502
+ disconnectWebSocket(sessionId);
503
+ }
504
+ debuggerPanels.clear();
505
+ processedSessions.clear();
506
+ }
507
+
508
+ function getWaitingHtml(): string {
509
+ return `<!DOCTYPE html>
510
+ <html lang="en">
511
+ <head>
512
+ <meta charset="UTF-8">
513
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
514
+ <title>TestDriver Live Preview</title>
515
+ <style>
516
+ body {
517
+ background-color: #1e1e1e;
518
+ color: #cccccc;
519
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
520
+ display: flex;
521
+ flex-direction: column;
522
+ align-items: center;
523
+ justify-content: center;
524
+ height: 100vh;
525
+ margin: 0;
526
+ text-align: center;
527
+ }
528
+ .logo {
529
+ width: 120px;
530
+ height: 120px;
531
+ margin-bottom: 24px;
532
+ animation: pulse 2s ease-in-out infinite;
533
+ }
534
+ h1 {
535
+ font-size: 24px;
536
+ font-weight: 500;
537
+ margin-bottom: 16px;
538
+ }
539
+ p {
540
+ font-size: 14px;
541
+ color: #888;
542
+ max-width: 400px;
543
+ line-height: 1.6;
544
+ }
545
+ code {
546
+ background: #2d2d2d;
547
+ padding: 2px 6px;
548
+ border-radius: 4px;
549
+ font-family: monospace;
550
+ }
551
+ @keyframes pulse {
552
+ 0%, 100% { opacity: 0.5; transform: scale(1); }
553
+ 50% { opacity: 1; transform: scale(1.05); }
554
+ }
555
+ .spinner {
556
+ width: 40px;
557
+ height: 40px;
558
+ border: 3px solid #333;
559
+ border-top-color: #b0cf34;
560
+ border-radius: 50%;
561
+ animation: spin 1s linear infinite;
562
+ margin-bottom: 24px;
563
+ }
564
+ @keyframes spin {
565
+ to { transform: rotate(360deg); }
566
+ }
567
+ </style>
568
+ </head>
569
+ <body>
570
+ <div class="spinner"></div>
571
+ <h1>Waiting for TestDriver...</h1>
572
+ <p>
573
+ Run a test with <code>preview: "ide"</code> to see the live execution here.
574
+ </p>
575
+ <p style="margin-top: 16px;">
576
+ <code>const testdriver = TestDriver(context, { preview: "ide" });</code>
577
+ </p>
578
+ </body>
579
+ </html>`;
580
+ }
581
+
582
+ function getDebuggerHtml(debuggerUrl: string, encodedData: string, webview: vscode.Webview, context: vscode.ExtensionContext): string {
583
+ // We'll embed the debugger in an iframe pointing to the local server
584
+ // The debugger server must be running for this to work
585
+ // Parse the URL properly to handle existing query parameters
586
+ const url = new URL(debuggerUrl);
587
+ url.searchParams.set('data', encodedData);
588
+ const fullUrl = url.toString();
589
+
590
+ return `<!DOCTYPE html>
591
+ <html lang="en">
592
+ <head>
593
+ <meta charset="UTF-8">
594
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
595
+ <meta http-equiv="Content-Security-Policy" content="default-src 'none'; frame-src http: https:; style-src 'unsafe-inline'; script-src 'unsafe-inline';">
596
+ <title>TestDriver Live Preview</title>
597
+ <style>
598
+ html, body {
599
+ margin: 0;
600
+ padding: 0;
601
+ width: 100%;
602
+ height: 100%;
603
+ overflow: hidden;
604
+ background-color: #1e1e1e;
605
+ }
606
+ iframe {
607
+ width: 100%;
608
+ height: 100%;
609
+ border: none;
610
+ }
611
+ .error {
612
+ display: none;
613
+ flex-direction: column;
614
+ align-items: center;
615
+ justify-content: center;
616
+ height: 100%;
617
+ color: #cccccc;
618
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
619
+ text-align: center;
620
+ padding: 20px;
621
+ }
622
+ .error.visible {
623
+ display: flex;
624
+ }
625
+ .error h2 {
626
+ color: #f44336;
627
+ margin-bottom: 16px;
628
+ }
629
+ .error p {
630
+ color: #888;
631
+ max-width: 400px;
632
+ line-height: 1.6;
633
+ }
634
+ </style>
635
+ </head>
636
+ <body>
637
+ <iframe
638
+ id="debugger-frame"
639
+ src="${fullUrl}"
640
+ sandbox="allow-scripts allow-same-origin"
641
+ ></iframe>
642
+ <div class="error" id="error-message">
643
+ <h2>Connection Lost</h2>
644
+ <p>The TestDriver debugger server is no longer running. Start a new test to reconnect.</p>
645
+ </div>
646
+ <script>
647
+ const iframe = document.getElementById('debugger-frame');
648
+ const errorDiv = document.getElementById('error-message');
649
+
650
+ iframe.onerror = function() {
651
+ iframe.style.display = 'none';
652
+ errorDiv.classList.add('visible');
653
+ };
654
+
655
+ // Listen for messages from the extension
656
+ window.addEventListener('message', event => {
657
+ const message = event.data;
658
+ // Forward WebSocket events to the iframe if needed
659
+ if (iframe.contentWindow) {
660
+ iframe.contentWindow.postMessage(message, '*');
661
+ }
662
+ });
663
+ </script>
664
+ </body>
665
+ </html>`;
666
+ }
667
+
668
+ async function installMcpServer() {
669
+ // Get the workspace folder
670
+ const workspaceFolders = vscode.workspace.workspaceFolders;
671
+ if (!workspaceFolders || workspaceFolders.length === 0) {
672
+ vscode.window.showWarningMessage('Please open a folder before installing TestDriver MCP.');
673
+ return;
674
+ }
675
+
676
+ const workspaceRoot = workspaceFolders[0].uri.fsPath;
677
+
678
+ // Check for various MCP config locations
679
+ const mcpConfigPaths = [
680
+ path.join(workspaceRoot, '.vscode', 'mcp.json'),
681
+ path.join(workspaceRoot, '.cursor', 'mcp.json'),
682
+ path.join(os.homedir(), '.vscode', 'mcp.json'),
683
+ path.join(os.homedir(), '.cursor', 'mcp.json')
684
+ ];
685
+
686
+ // Try to find existing config or create in workspace
687
+ let configPath = mcpConfigPaths.find(p => fs.existsSync(p));
688
+
689
+ if (!configPath) {
690
+ // Ask user where to install
691
+ const choice = await vscode.window.showQuickPick(
692
+ [
693
+ { label: 'Workspace (.vscode/mcp.json)', value: mcpConfigPaths[0] },
694
+ { label: 'Workspace (.cursor/mcp.json)', value: mcpConfigPaths[1] },
695
+ { label: 'Global (~/.vscode/mcp.json)', value: mcpConfigPaths[2] },
696
+ { label: 'Global (~/.cursor/mcp.json)', value: mcpConfigPaths[3] }
697
+ ],
698
+ { placeHolder: 'Where would you like to install the TestDriver MCP server?' }
699
+ );
700
+
701
+ if (!choice) {
702
+ return;
703
+ }
704
+
705
+ configPath = choice.value;
706
+ }
707
+
708
+ // Ensure directory exists
709
+ const configDir = path.dirname(configPath);
710
+ if (!fs.existsSync(configDir)) {
711
+ fs.mkdirSync(configDir, { recursive: true });
712
+ }
713
+
714
+ // Read existing config or create new
715
+ let config: { mcpServers?: Record<string, unknown> } = {};
716
+ if (fs.existsSync(configPath)) {
717
+ try {
718
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
719
+ } catch (error) {
720
+ vscode.window.showErrorMessage(`Error reading MCP config: ${error}`);
721
+ return;
722
+ }
723
+ }
724
+
725
+ // Initialize mcpServers if not present
726
+ if (!config.mcpServers) {
727
+ config.mcpServers = {};
728
+ }
729
+
730
+ // Check if TestDriver MCP is already configured
731
+ if (config.mcpServers['testdriver']) {
732
+ const overwrite = await vscode.window.showWarningMessage(
733
+ 'TestDriver MCP is already configured. Overwrite?',
734
+ 'Yes',
735
+ 'No'
736
+ );
737
+ if (overwrite !== 'Yes') {
738
+ return;
739
+ }
740
+ }
741
+
742
+ // Add TestDriver MCP configuration
743
+ // Set TD_PREVIEW=ide so the live preview opens in IDE panel (VSCode, Cursor, etc.)
744
+ config.mcpServers['testdriver'] = {
745
+ command: 'npx',
746
+ args: ['-y', 'testdriverai', 'mcp'],
747
+ env: {
748
+ TD_API_KEY: '${env:TD_API_KEY}',
749
+ TD_PREVIEW: 'ide'
750
+ }
751
+ };
752
+
753
+ // Write config
754
+ try {
755
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
756
+ vscode.window.showInformationMessage(
757
+ `TestDriver MCP installed successfully at ${configPath}. Don't forget to set your TD_API_KEY environment variable.`
758
+ );
759
+ } catch (error) {
760
+ vscode.window.showErrorMessage(`Error writing MCP config: ${error}`);
761
+ }
762
+ }
763
+
764
+ async function autoInstallMcp() {
765
+ // Check if MCP is already configured in common locations
766
+ const mcpConfigPaths = [
767
+ path.join(os.homedir(), '.vscode', 'mcp.json'),
768
+ path.join(os.homedir(), '.cursor', 'mcp.json')
769
+ ];
770
+
771
+ // Check workspace configs if available
772
+ const workspaceFolders = vscode.workspace.workspaceFolders;
773
+ if (workspaceFolders) {
774
+ mcpConfigPaths.unshift(
775
+ path.join(workspaceFolders[0].uri.fsPath, '.vscode', 'mcp.json'),
776
+ path.join(workspaceFolders[0].uri.fsPath, '.cursor', 'mcp.json')
777
+ );
778
+ }
779
+
780
+ // Check if TestDriver MCP is already configured
781
+ for (const configPath of mcpConfigPaths) {
782
+ if (fs.existsSync(configPath)) {
783
+ try {
784
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
785
+ if (config.mcpServers?.['testdriver']) {
786
+ // Already configured
787
+ return;
788
+ }
789
+ } catch {
790
+ // Ignore parse errors
791
+ }
792
+ }
793
+ }
794
+
795
+ // Prompt user to install MCP
796
+ const install = await vscode.window.showInformationMessage(
797
+ 'Would you like to install the TestDriver MCP server for AI-assisted test creation?',
798
+ 'Install',
799
+ 'Not Now',
800
+ 'Never'
801
+ );
802
+
803
+ if (install === 'Install') {
804
+ await installMcpServer();
805
+ } else if (install === 'Never') {
806
+ // Store preference to not ask again
807
+ const config = vscode.workspace.getConfiguration('testdriverai');
808
+ await config.update('mcpPromptDismissed', true, vscode.ConfigurationTarget.Global);
809
+ }
810
+ }
811
+
812
+ export function deactivate() {
813
+ closeAllDebuggerPanels();
814
+
815
+ // Stop HTTP server
816
+ if (httpServer) {
817
+ httpServer.close();
818
+ httpServer = undefined;
819
+ }
820
+
821
+ // Dispose preview file watchers
822
+ for (const [_, watcher] of previewWatchers) {
823
+ watcher.dispose();
824
+ }
825
+ previewWatchers.clear();
826
+
827
+ // Unregister this instance
828
+ unregisterInstance();
829
+ }