testdriverai 7.2.64 → 7.2.65

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 (360) hide show
  1. package/agent/index.js +4 -3
  2. package/agent/interface.js +11 -251
  3. package/agent/lib/debugger-server.js +2 -1
  4. package/agent/lib/logger.js +56 -0
  5. package/agent/lib/sandbox.js +6 -7
  6. package/ai/agents/test-writer.md +457 -0
  7. package/{docs/v7/ai.mdx → ai/skills/testdriver:ai/SKILL.md} +3 -5
  8. package/{docs/v7/assert.mdx → ai/skills/testdriver:assert/SKILL.md} +3 -4
  9. package/{docs/v7/aws-setup.mdx → ai/skills/testdriver:aws-setup/SKILL.md} +3 -4
  10. package/{docs/v7/caching.mdx → ai/skills/testdriver:caching/SKILL.md} +3 -7
  11. package/{docs/v7/captcha.mdx → ai/skills/testdriver:captcha/SKILL.md} +4 -5
  12. package/{docs/v7/ci-cd.mdx → ai/skills/testdriver:ci-cd/SKILL.md} +3 -4
  13. package/{docs/v7/click.mdx → ai/skills/testdriver:click/SKILL.md} +3 -4
  14. package/{docs/v7/client.mdx → ai/skills/testdriver:client/SKILL.md} +11 -5
  15. package/{docs/v7/cloud.mdx → ai/skills/testdriver:cloud/SKILL.md} +3 -4
  16. package/{docs/v7/customizing-devices.mdx → ai/skills/testdriver:customizing-devices/SKILL.md} +36 -26
  17. package/{docs/v7/dashcam.mdx → ai/skills/testdriver:dashcam/SKILL.md} +3 -4
  18. package/{docs/v7/device-config.mdx → ai/skills/testdriver:device-config/SKILL.md} +3 -3
  19. package/{docs/v7/double-click.mdx → ai/skills/testdriver:double-click/SKILL.md} +3 -3
  20. package/{docs/v7/elements.mdx → ai/skills/testdriver:elements/SKILL.md} +3 -4
  21. package/{docs/v7/enterprise.mdx → ai/skills/testdriver:enterprise/SKILL.md} +3 -5
  22. package/ai/skills/testdriver:examples/SKILL.md +7 -0
  23. package/{docs/v7/exec.mdx → ai/skills/testdriver:exec/SKILL.md} +3 -4
  24. package/{docs/v7/find.mdx → ai/skills/testdriver:find/SKILL.md} +81 -4
  25. package/{docs/v7/focus-application.mdx → ai/skills/testdriver:focus-application/SKILL.md} +3 -4
  26. package/{docs/v7/generating-tests.mdx → ai/skills/testdriver:generating-tests/SKILL.md} +3 -3
  27. package/{docs/v7/hover.mdx → ai/skills/testdriver:hover/SKILL.md} +3 -4
  28. package/{docs/v7/locating-elements.mdx → ai/skills/testdriver:locating-elements/SKILL.md} +3 -3
  29. package/{docs/v7/making-assertions.mdx → ai/skills/testdriver:making-assertions/SKILL.md} +3 -3
  30. package/ai/skills/testdriver:mcp-workflow/SKILL.md +410 -0
  31. package/{docs/v7/mouse-down.mdx → ai/skills/testdriver:mouse-down/SKILL.md} +3 -3
  32. package/{docs/v7/mouse-up.mdx → ai/skills/testdriver:mouse-up/SKILL.md} +3 -3
  33. package/{docs/v7/performing-actions.mdx → ai/skills/testdriver:performing-actions/SKILL.md} +3 -3
  34. package/{docs/v7/press-keys.mdx → ai/skills/testdriver:press-keys/SKILL.md} +3 -4
  35. package/{docs/v7/quickstart.mdx → ai/skills/testdriver:quickstart/SKILL.md} +3 -4
  36. package/{docs/v7/reusable-code.mdx → ai/skills/testdriver:reusable-code/SKILL.md} +3 -3
  37. package/{docs/v7/right-click.mdx → ai/skills/testdriver:right-click/SKILL.md} +3 -3
  38. package/{docs/v7/running-tests.mdx → ai/skills/testdriver:running-tests/SKILL.md} +3 -3
  39. package/{docs/v7/screenshot.mdx → ai/skills/testdriver:screenshot/SKILL.md} +3 -4
  40. package/{docs/v7/scroll.mdx → ai/skills/testdriver:scroll/SKILL.md} +3 -4
  41. package/{docs/v7/secrets.mdx → ai/skills/testdriver:secrets/SKILL.md} +3 -3
  42. package/{docs/v7/self-hosted.mdx → ai/skills/testdriver:self-hosted/SKILL.md} +3 -4
  43. package/ai/skills/testdriver:testdriver/SKILL.md +31 -0
  44. package/{docs/v7/type.mdx → ai/skills/testdriver:type/SKILL.md} +3 -4
  45. package/{docs/v7/variables.mdx → ai/skills/testdriver:variables/SKILL.md} +3 -3
  46. package/{docs/v7/waiting-for-elements.mdx → ai/skills/testdriver:waiting-for-elements/SKILL.md} +3 -3
  47. package/{docs/v7/what-is-testdriver.mdx → ai/skills/testdriver:what-is-testdriver/SKILL.md} +3 -3
  48. package/interfaces/cli/commands/init.js +278 -1
  49. package/interfaces/cli/commands/setup.js +382 -0
  50. package/interfaces/vitest-plugin.mjs +190 -122
  51. package/lib/sentry.js +4 -3
  52. package/lib/vitest/hooks.mjs +70 -16
  53. package/package.json +29 -9
  54. package/sdk.d.ts +29 -2
  55. package/sdk.js +1 -0
  56. package/.env.example +0 -4
  57. package/.github/workflows/acceptance-linux-scheduled.yaml +0 -45
  58. package/.github/workflows/acceptance-windows-scheduled.yaml +0 -54
  59. package/.github/workflows/acceptance.yaml +0 -87
  60. package/.github/workflows/publish.yaml +0 -68
  61. package/.github/workflows/test-init.yml +0 -145
  62. package/.github/workflows/testdriver.yml +0 -170
  63. package/.github/workflows/windows-self-hosted.yaml +0 -82
  64. package/.prettierignore +0 -4
  65. package/.prettierrc +0 -1
  66. package/CHANGELOG.md +0 -34
  67. package/agents.md +0 -455
  68. package/debugger/bg.png +0 -0
  69. package/debugger/icon.png +0 -0
  70. package/debugger/index.html +0 -797
  71. package/debugger/td.png +0 -0
  72. package/debugger/tray-buffered.png +0 -0
  73. package/debugger/tray.png +0 -0
  74. package/docs/GITHUB_COMMENTS.md +0 -330
  75. package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +0 -167
  76. package/docs/QUICK-START-GITHUB-COMMENTS.md +0 -84
  77. package/docs/TEST-GITHUB-COMMENTS.md +0 -129
  78. package/docs/_scripts/link-replacer.js +0 -164
  79. package/docs/_scripts/upload-docs-to-openai.js +0 -284
  80. package/docs/docs.json +0 -393
  81. package/docs/github-integration-setup.md +0 -266
  82. package/docs/guide/best-practices-polling.mdx +0 -154
  83. package/docs/images/content/account/newprojectsettings.png +0 -0
  84. package/docs/images/content/account/projectpage.png +0 -0
  85. package/docs/images/content/account/projectreplays.png +0 -0
  86. package/docs/images/content/account/team-manage.png +0 -0
  87. package/docs/images/content/account/teampage.png +0 -0
  88. package/docs/images/content/extension/cursor.svg +0 -1
  89. package/docs/images/content/extension/vscode.svg +0 -57
  90. package/docs/images/content/extension/windsurf.svg +0 -3
  91. package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
  92. package/docs/images/content/side-by-side.png +0 -0
  93. package/docs/images/content/vscode/ide-full.png +0 -0
  94. package/docs/images/content/vscode/running.png +0 -0
  95. package/docs/images/content/vscode/vscode-2-assert.png +0 -0
  96. package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
  97. package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
  98. package/docs/images/content/vscode/vscode-file-creation.png +0 -0
  99. package/docs/images/content/vscode/vscode-install.png +0 -0
  100. package/docs/images/content/vscode/vscode-overview.png +0 -0
  101. package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
  102. package/docs/images/content/vscode/vscode-stopchat.png +0 -0
  103. package/docs/images/content/vscode/vscode-stoptest.png +0 -0
  104. package/docs/images/content/vscode/vscode-tdservice.png +0 -0
  105. package/docs/images/content/vscode/vscode-test-output.png +0 -0
  106. package/docs/images/content/vscode/vscode-testhistory.png +0 -0
  107. package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
  108. package/docs/images/content/vscode/vscode-testpane.png +0 -0
  109. package/docs/images/template/dark.png +0 -0
  110. package/docs/images/template/icon.png +0 -0
  111. package/docs/images/template/light.png +0 -0
  112. package/docs/snippets/calendar-link.mdx +0 -4
  113. package/docs/snippets/gitignore-warning.mdx +0 -7
  114. package/docs/snippets/lifecycle-warning.mdx +0 -6
  115. package/docs/snippets/test-prereqs.mdx +0 -12
  116. package/docs/snippets/tests/assert-replay.mdx +0 -7
  117. package/docs/snippets/tests/assert-yaml.mdx +0 -8
  118. package/docs/snippets/tests/exec-js-replay.mdx +0 -7
  119. package/docs/snippets/tests/exec-js-yaml.mdx +0 -32
  120. package/docs/snippets/tests/exec-shell-replay.mdx +0 -7
  121. package/docs/snippets/tests/exec-shell-yaml.mdx +0 -15
  122. package/docs/snippets/tests/hover-image-replay.mdx +0 -7
  123. package/docs/snippets/tests/hover-image-yaml.mdx +0 -17
  124. package/docs/snippets/tests/hover-text-replay.mdx +0 -7
  125. package/docs/snippets/tests/hover-text-with-description-replay.mdx +0 -7
  126. package/docs/snippets/tests/hover-text-with-description-yaml.mdx +0 -24
  127. package/docs/snippets/tests/hover-text-yaml.mdx +0 -14
  128. package/docs/snippets/tests/match-image-replay.mdx +0 -7
  129. package/docs/snippets/tests/match-image-yaml.mdx +0 -17
  130. package/docs/snippets/tests/press-keys-replay.mdx +0 -7
  131. package/docs/snippets/tests/press-keys-yaml.mdx +0 -36
  132. package/docs/snippets/tests/remember-replay.mdx +0 -7
  133. package/docs/snippets/tests/remember-yaml.mdx +0 -28
  134. package/docs/snippets/tests/scroll-replay.mdx +0 -7
  135. package/docs/snippets/tests/scroll-until-image-replay.mdx +0 -7
  136. package/docs/snippets/tests/scroll-until-image-yaml.mdx +0 -14
  137. package/docs/snippets/tests/scroll-until-text-replay.mdx +0 -7
  138. package/docs/snippets/tests/scroll-until-text-yaml.mdx +0 -17
  139. package/docs/snippets/tests/scroll-yaml.mdx +0 -30
  140. package/docs/snippets/tests/type-repeated-replay.mdx +0 -7
  141. package/docs/snippets/tests/type-repeated-yaml.mdx +0 -22
  142. package/docs/snippets/tests/type-replay.mdx +0 -7
  143. package/docs/snippets/tests/type-yaml.mdx +0 -28
  144. package/docs/snippets/tests/wait-for-image-replay.mdx +0 -7
  145. package/docs/snippets/tests/wait-for-image-yaml.mdx +0 -18
  146. package/docs/snippets/tests/wait-for-text-replay.mdx +0 -7
  147. package/docs/snippets/tests/wait-for-text-yaml.mdx +0 -18
  148. package/docs/snippets/tests/wait-replay.mdx +0 -7
  149. package/docs/snippets/tests/wait-yaml.mdx +0 -13
  150. package/docs/styles.css +0 -65
  151. package/docs/v6/account/dashboard.mdx +0 -16
  152. package/docs/v6/account/enterprise.mdx +0 -110
  153. package/docs/v6/account/pricing.mdx +0 -33
  154. package/docs/v6/account/projects.mdx +0 -33
  155. package/docs/v6/account/team.mdx +0 -35
  156. package/docs/v6/action/ami.mdx +0 -109
  157. package/docs/v6/action/performance.mdx +0 -105
  158. package/docs/v6/action/secrets.mdx +0 -93
  159. package/docs/v6/apps/chrome-extensions.mdx +0 -48
  160. package/docs/v6/apps/desktop-apps.mdx +0 -93
  161. package/docs/v6/apps/mobile-apps.mdx +0 -26
  162. package/docs/v6/apps/static-websites.mdx +0 -54
  163. package/docs/v6/apps/tauri-apps.mdx +0 -361
  164. package/docs/v6/bugs/jira.mdx +0 -232
  165. package/docs/v6/cli/overview.mdx +0 -66
  166. package/docs/v6/commands/assert.mdx +0 -45
  167. package/docs/v6/commands/exec.mdx +0 -282
  168. package/docs/v6/commands/focus-application.mdx +0 -44
  169. package/docs/v6/commands/hover-image.mdx +0 -69
  170. package/docs/v6/commands/hover-text.mdx +0 -47
  171. package/docs/v6/commands/if.mdx +0 -53
  172. package/docs/v6/commands/match-image.mdx +0 -67
  173. package/docs/v6/commands/press-keys.mdx +0 -87
  174. package/docs/v6/commands/remember.mdx +0 -49
  175. package/docs/v6/commands/run.mdx +0 -44
  176. package/docs/v6/commands/scroll-until-image.mdx +0 -66
  177. package/docs/v6/commands/scroll-until-text.mdx +0 -60
  178. package/docs/v6/commands/scroll.mdx +0 -69
  179. package/docs/v6/commands/type.mdx +0 -45
  180. package/docs/v6/commands/wait-for-image.mdx +0 -54
  181. package/docs/v6/commands/wait-for-text.mdx +0 -48
  182. package/docs/v6/commands/wait.mdx +0 -45
  183. package/docs/v6/exporting/junit.mdx +0 -218
  184. package/docs/v6/exporting/playwright.mdx +0 -197
  185. package/docs/v6/features/auto-healing.mdx +0 -144
  186. package/docs/v6/features/generation.mdx +0 -116
  187. package/docs/v6/features/parallel-testing.mdx +0 -151
  188. package/docs/v6/features/reusable-snippets.mdx +0 -131
  189. package/docs/v6/features/selectorless.mdx +0 -80
  190. package/docs/v6/features/visual-assertions.mdx +0 -139
  191. package/docs/v6/getting-started/ci.mdx +0 -146
  192. package/docs/v6/getting-started/cli.mdx +0 -91
  193. package/docs/v6/getting-started/editing.mdx +0 -100
  194. package/docs/v6/getting-started/playwright.mdx +0 -342
  195. package/docs/v6/getting-started/running.mdx +0 -48
  196. package/docs/v6/getting-started/self-hosting.mdx +0 -408
  197. package/docs/v6/getting-started/vscode.mdx +0 -89
  198. package/docs/v6/guide/assertions.mdx +0 -189
  199. package/docs/v6/guide/authentication.mdx +0 -136
  200. package/docs/v6/guide/code.mdx +0 -65
  201. package/docs/v6/guide/dashcam.mdx +0 -118
  202. package/docs/v6/guide/environment-variables.mdx +0 -26
  203. package/docs/v6/guide/lifecycle.mdx +0 -242
  204. package/docs/v6/guide/locating.mdx +0 -141
  205. package/docs/v6/guide/protips.mdx +0 -43
  206. package/docs/v6/guide/variables.mdx +0 -143
  207. package/docs/v6/guide/waiting.mdx +0 -130
  208. package/docs/v6/importing/csv.mdx +0 -196
  209. package/docs/v6/importing/gherkin.mdx +0 -143
  210. package/docs/v6/importing/jira.mdx +0 -164
  211. package/docs/v6/importing/testrail.mdx +0 -162
  212. package/docs/v6/integrations/electron.mdx +0 -146
  213. package/docs/v6/integrations/netlify.mdx +0 -100
  214. package/docs/v6/integrations/vercel.mdx +0 -125
  215. package/docs/v6/interactive/explore.mdx +0 -99
  216. package/docs/v6/interactive/run.mdx +0 -52
  217. package/docs/v6/interactive/save.mdx +0 -63
  218. package/docs/v6/overview/comparison.mdx +0 -101
  219. package/docs/v6/overview/faq.mdx +0 -162
  220. package/docs/v6/overview/performance.mdx +0 -52
  221. package/docs/v6/overview/quickstart.mdx +0 -137
  222. package/docs/v6/overview/what-is-testdriver.mdx +0 -85
  223. package/docs/v6/scenarios/ai-chatbot.mdx +0 -28
  224. package/docs/v6/scenarios/cookie-banner.mdx +0 -32
  225. package/docs/v6/scenarios/file-upload.mdx +0 -33
  226. package/docs/v6/scenarios/form-filling.mdx +0 -32
  227. package/docs/v6/scenarios/log-in.mdx +0 -75
  228. package/docs/v6/scenarios/pdf-generation.mdx +0 -25
  229. package/docs/v6/scenarios/spell-check.mdx +0 -22
  230. package/docs/v6/security/action.mdx +0 -84
  231. package/docs/v6/security/agent.mdx +0 -73
  232. package/docs/v6/security/platform.mdx +0 -77
  233. package/docs/v6/tutorials/advanced-test.mdx +0 -81
  234. package/docs/v6/tutorials/basic-test.mdx +0 -45
  235. package/docs/v7/_drafts/agents.mdx +0 -852
  236. package/docs/v7/_drafts/architecture.mdx +0 -399
  237. package/docs/v7/_drafts/auto-cache-key.mdx +0 -167
  238. package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +0 -100
  239. package/docs/v7/_drafts/best-practices.mdx +0 -486
  240. package/docs/v7/_drafts/caching-ai.mdx +0 -215
  241. package/docs/v7/_drafts/caching-selectors.mdx +0 -424
  242. package/docs/v7/_drafts/caching.mdx +0 -366
  243. package/docs/v7/_drafts/cli-to-sdk-migration.mdx +0 -425
  244. package/docs/v7/_drafts/commands/assert.mdx +0 -45
  245. package/docs/v7/_drafts/commands/exec.mdx +0 -282
  246. package/docs/v7/_drafts/commands/focus-application.mdx +0 -44
  247. package/docs/v7/_drafts/commands/hover-image.mdx +0 -69
  248. package/docs/v7/_drafts/commands/hover-text.mdx +0 -47
  249. package/docs/v7/_drafts/commands/if.mdx +0 -53
  250. package/docs/v7/_drafts/commands/match-image.mdx +0 -67
  251. package/docs/v7/_drafts/commands/press-keys.mdx +0 -87
  252. package/docs/v7/_drafts/commands/remember.mdx +0 -49
  253. package/docs/v7/_drafts/commands/run.mdx +0 -44
  254. package/docs/v7/_drafts/commands/scroll-until-image.mdx +0 -66
  255. package/docs/v7/_drafts/commands/scroll-until-text.mdx +0 -60
  256. package/docs/v7/_drafts/commands/scroll.mdx +0 -69
  257. package/docs/v7/_drafts/commands/type.mdx +0 -45
  258. package/docs/v7/_drafts/commands/wait-for-image.mdx +0 -54
  259. package/docs/v7/_drafts/commands/wait-for-text.mdx +0 -48
  260. package/docs/v7/_drafts/commands/wait.mdx +0 -45
  261. package/docs/v7/_drafts/configuration.mdx +0 -378
  262. package/docs/v7/_drafts/contributing.mdx +0 -174
  263. package/docs/v7/_drafts/core.mdx +0 -458
  264. package/docs/v7/_drafts/dashcam-title-feature.mdx +0 -89
  265. package/docs/v7/_drafts/debugging.mdx +0 -349
  266. package/docs/v7/_drafts/error-handling.mdx +0 -501
  267. package/docs/v7/_drafts/faq.mdx +0 -393
  268. package/docs/v7/_drafts/hooks.mdx +0 -360
  269. package/docs/v7/_drafts/init-command.mdx +0 -95
  270. package/docs/v7/_drafts/installation.mdx +0 -420
  271. package/docs/v7/_drafts/migration.mdx +0 -562
  272. package/docs/v7/_drafts/observable.mdx +0 -604
  273. package/docs/v7/_drafts/playwright.mdx +0 -342
  274. package/docs/v7/_drafts/plugin-migration.mdx +0 -220
  275. package/docs/v7/_drafts/powerful.mdx +0 -419
  276. package/docs/v7/_drafts/presets.mdx +0 -210
  277. package/docs/v7/_drafts/progressive-disclosure.mdx +0 -230
  278. package/docs/v7/_drafts/prompt-cache.mdx +0 -200
  279. package/docs/v7/_drafts/provision.mdx +0 -390
  280. package/docs/v7/_drafts/quick-start-test-recording.mdx +0 -214
  281. package/docs/v7/_drafts/readme.mdx +0 -135
  282. package/docs/v7/_drafts/reports.mdx +0 -414
  283. package/docs/v7/_drafts/scalable.mdx +0 -754
  284. package/docs/v7/_drafts/screenshot.mdx +0 -155
  285. package/docs/v7/_drafts/sdk-awesome-logs.mdx +0 -468
  286. package/docs/v7/_drafts/sdk-browser-rendering.mdx +0 -167
  287. package/docs/v7/_drafts/sdk-migration.mdx +0 -474
  288. package/docs/v7/_drafts/sdk-v7-complete.mdx +0 -345
  289. package/docs/v7/_drafts/self-hosting.mdx +0 -369
  290. package/docs/v7/_drafts/test-recording.mdx +0 -382
  291. package/docs/v7/_drafts/troubleshooting.mdx +0 -526
  292. package/docs/v7/_drafts/vitest-plugin.mdx +0 -477
  293. package/docs/v7/_drafts/vitest.mdx +0 -535
  294. package/docs/v7/_drafts/writing-tests.mdx +0 -25
  295. package/docs/v7/examples.mdx +0 -5
  296. package/eslint.config.js +0 -67
  297. package/examples/ai.test.mjs +0 -30
  298. package/examples/assert.test.mjs +0 -46
  299. package/examples/captcha-api.test.mjs +0 -50
  300. package/examples/chrome-extension.test.mjs +0 -94
  301. package/examples/drag-and-drop.test.mjs +0 -58
  302. package/examples/element-not-found.test.mjs +0 -26
  303. package/examples/exec-output.test.mjs +0 -59
  304. package/examples/exec-pwsh.test.mjs +0 -57
  305. package/examples/focus-window.test.mjs +0 -36
  306. package/examples/formatted-logging.test.mjs +0 -26
  307. package/examples/hover-image.test.mjs +0 -52
  308. package/examples/hover-text-with-description.test.mjs +0 -56
  309. package/examples/hover-text.test.mjs +0 -27
  310. package/examples/installer.test.mjs +0 -49
  311. package/examples/launch-vscode-linux.test.mjs +0 -54
  312. package/examples/match-image.test.mjs +0 -54
  313. package/examples/no-provision.test.mjs +0 -23
  314. package/examples/press-keys.test.mjs +0 -50
  315. package/examples/prompt.test.mjs +0 -33
  316. package/examples/scroll-keyboard.test.mjs +0 -37
  317. package/examples/scroll-until-image.test.mjs +0 -39
  318. package/examples/scroll-until-text.test.mjs +0 -67
  319. package/examples/scroll.test.mjs +0 -41
  320. package/examples/type.test.mjs +0 -45
  321. package/examples/windows-installer.test.mjs +0 -53
  322. package/interfaces/cli/commands/edit.js +0 -3
  323. package/interfaces/cli/commands/generate.js +0 -3
  324. package/interfaces/cli/commands/run.js +0 -3
  325. package/interfaces/cli/utils/factory.js +0 -71
  326. package/jsconfig.json +0 -26
  327. package/manual/test-init-command.js +0 -223
  328. package/sdk-log-formatter.js +0 -930
  329. package/setup/aws/cloudformation.yaml +0 -470
  330. package/setup/aws/spawn-runner.sh +0 -190
  331. package/test/api-resilience.test.mjs +0 -0
  332. package/test/captcha-solver.test.mjs +0 -70
  333. package/test/chrome-remote-debugging.test.mjs +0 -66
  334. package/test/manual/debug-locate-response.js +0 -82
  335. package/test/manual/reconnect-provision.test.mjs +0 -49
  336. package/test/manual/test-console-logs.test.mjs +0 -42
  337. package/test/manual/test-find-api.js +0 -73
  338. package/test/manual/test-init.sh +0 -54
  339. package/test/manual/test-prompt-cache.js +0 -96
  340. package/test/manual/test-provision-auth.mjs +0 -22
  341. package/test/manual/test-sandbox-render.js +0 -28
  342. package/test/manual/test-sdk-methods.js +0 -15
  343. package/test/manual/test-sdk-refactor.js +0 -53
  344. package/test/manual/test-stack-trace.mjs +0 -57
  345. package/test/manual/verify-element-api.js +0 -89
  346. package/test/manual/verify-types.js +0 -0
  347. package/test/manual-unawaited-promise.test.mjs +0 -31
  348. package/testdriver-plugin/skills/actions/SKILL.md +0 -93
  349. package/testdriver-plugin/skills/assertions/SKILL.md +0 -77
  350. package/testdriver-plugin/skills/caching/SKILL.md +0 -66
  351. package/testdriver-plugin/skills/creating-tests/SKILL.md +0 -104
  352. package/testdriver-plugin/skills/finding-elements/SKILL.md +0 -77
  353. package/testdriver-plugin/skills/github-actions/SKILL.md +0 -100
  354. package/testdriver-plugin/skills/running-tests/SKILL.md +0 -77
  355. package/testdriver-plugin/skills/secrets/SKILL.md +0 -87
  356. package/testdriver-plugin/skills/self-hosting/SKILL.md +0 -89
  357. package/testdriver-plugin/skills/setup/SKILL.md +0 -76
  358. package/testdriver-plugin/skills/variables/SKILL.md +0 -88
  359. package/testdriver-plugin/skills/waiting/SKILL.md +0 -72
  360. package/vitest.config.mjs +0 -29
@@ -19,21 +19,21 @@ const MINIMUM_VITEST_VERSION = 4;
19
19
  */
20
20
  function checkVitestVersion() {
21
21
  try {
22
- const vitestPkg = require('vitest/package.json');
22
+ const vitestPkg = require("vitest/package.json");
23
23
  const version = vitestPkg.version;
24
- const major = parseInt(version.split('.')[0], 10);
25
-
24
+ const major = parseInt(version.split(".")[0], 10);
25
+
26
26
  if (major < MINIMUM_VITEST_VERSION) {
27
27
  throw new Error(
28
28
  `TestDriver requires Vitest >= ${MINIMUM_VITEST_VERSION}.0.0, but found ${version}. ` +
29
- `Please upgrade Vitest: npm install vitest@latest`
29
+ `Please upgrade Vitest: npm install vitest@latest`,
30
30
  );
31
31
  }
32
32
  } catch (err) {
33
- if (err.code === 'MODULE_NOT_FOUND') {
33
+ if (err.code === "MODULE_NOT_FOUND") {
34
34
  throw new Error(
35
- 'TestDriver requires Vitest to be installed. ' +
36
- 'Please install it: npm install vitest@latest'
35
+ "TestDriver requires Vitest to be installed. " +
36
+ "Please install it: npm install vitest@latest",
37
37
  );
38
38
  }
39
39
  throw err;
@@ -50,7 +50,8 @@ checkVitestVersion();
50
50
  * Set TD_LOG_LEVEL=debug for verbose output
51
51
  */
52
52
  const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
53
- const currentLogLevel = LOG_LEVELS[process.env.TD_LOG_LEVEL?.toLowerCase()] ?? LOG_LEVELS.info;
53
+ const currentLogLevel =
54
+ LOG_LEVELS[process.env.TD_LOG_LEVEL?.toLowerCase()] ?? LOG_LEVELS.info;
54
55
 
55
56
  const logger = {
56
57
  debug: (...args) => {
@@ -187,7 +188,7 @@ export function getPluginState() {
187
188
  export async function authenticateWithApiKey(apiKey, apiRoot) {
188
189
  if (!apiKey) {
189
190
  const error = new Error(
190
- "TD_API_KEY is not configured. Get your API key at https://console.testdriver.ai/team"
191
+ "TD_API_KEY is not configured. Get your API key at https://console.testdriver.ai/team",
191
192
  );
192
193
  error.code = "MISSING_API_KEY";
193
194
  error.isAuthError = true;
@@ -213,7 +214,7 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
213
214
  // Network-level error (fetch failed entirely)
214
215
  const networkError = new Error(
215
216
  `Unable to reach TestDriver API at ${apiRoot}. ` +
216
- "Check your internet connection and try again."
217
+ "Check your internet connection and try again.",
217
218
  );
218
219
  networkError.code = "NETWORK_ERROR";
219
220
  networkError.isNetworkError = true;
@@ -233,8 +234,8 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
233
234
  if (response.status === 401) {
234
235
  const authError = new Error(
235
236
  data.message ||
236
- "Invalid API key. Please check your TD_API_KEY and try again. " +
237
- "Get your API key at https://console.testdriver.ai/team"
237
+ "Invalid API key. Please check your TD_API_KEY and try again. " +
238
+ "Get your API key at https://console.testdriver.ai/team",
238
239
  );
239
240
  authError.code = data.error || "INVALID_API_KEY";
240
241
  authError.isAuthError = true;
@@ -245,7 +246,7 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
245
246
  if (response.status >= 500) {
246
247
  const serverError = new Error(
247
248
  data.message ||
248
- `TestDriver API is currently unavailable (HTTP ${response.status}). Please try again later.`
249
+ `TestDriver API is currently unavailable (HTTP ${response.status}). Please try again later.`,
249
250
  );
250
251
  serverError.code = data.error || "API_UNAVAILABLE";
251
252
  serverError.isServerError = true;
@@ -255,7 +256,7 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
255
256
  // Rate limiting (429)
256
257
  if (response.status === 429) {
257
258
  const rateLimitError = new Error(
258
- "Too many requests to TestDriver API. Please wait a moment and try again."
259
+ "Too many requests to TestDriver API. Please wait a moment and try again.",
259
260
  );
260
261
  rateLimitError.code = "RATE_LIMITED";
261
262
  rateLimitError.isRateLimitError = true;
@@ -265,7 +266,7 @@ export async function authenticateWithApiKey(apiKey, apiRoot) {
265
266
  // Other HTTP errors
266
267
  throw new Error(
267
268
  `Authentication failed: ${response.status} ${response.statusText}` +
268
- (data.message ? ` - ${data.message}` : "")
269
+ (data.message ? ` - ${data.message}` : ""),
269
270
  );
270
271
  }
271
272
 
@@ -324,17 +325,17 @@ export async function recordTestCaseDirect(token, apiRoot, testCaseData) {
324
325
  }
325
326
 
326
327
  // Import TestDriverSDK using require to avoid esbuild transformation issues
327
- const TestDriverSDK = require('../sdk.js');
328
+ const TestDriverSDK = require("../sdk.js");
328
329
 
329
330
  /**
330
331
  * Create a TestDriver client for use in beforeAll/beforeEach hooks
331
332
  * This is for the shared instance pattern where one driver is used across multiple tests
332
- *
333
+ *
333
334
  * @param {object} options - TestDriver options
334
335
  * @param {string} [options.apiKey] - TestDriver API key (defaults to process.env.TD_API_KEY)
335
336
  * @param {boolean} [options.headless] - Run sandbox in headless mode
336
337
  * @returns {Promise<TestDriver>} Connected TestDriver client instance
337
- *
338
+ *
338
339
  * @example
339
340
  * let testdriver;
340
341
  * beforeAll(async () => {
@@ -344,45 +345,46 @@ const TestDriverSDK = require('../sdk.js');
344
345
  */
345
346
  export async function createTestDriver(options = {}) {
346
347
  // Get global plugin options if available
347
- const pluginOptions = globalThis.__testdriverPlugin?.state?.testDriverOptions || {};
348
-
348
+ const pluginOptions =
349
+ globalThis.__testdriverPlugin?.state?.testDriverOptions || {};
350
+
349
351
  // Merge options: plugin global options < test-specific options
350
352
  const mergedOptions = { ...pluginOptions, ...options };
351
-
353
+
352
354
  // Support TD_OS environment variable for specifying target OS (linux, mac, windows)
353
355
  // Priority: test options > plugin options > environment variable > default (linux)
354
356
  if (!mergedOptions.os && process.env.TD_OS) {
355
357
  mergedOptions.os = process.env.TD_OS;
356
358
  }
357
-
359
+
358
360
  // Extract TestDriver-specific options
359
361
  const apiKey = mergedOptions.apiKey || process.env.TD_API_KEY;
360
-
362
+
361
363
  // Build config for TestDriverSDK constructor
362
364
  const config = { ...mergedOptions };
363
365
  delete config.apiKey;
364
-
366
+
365
367
  // Use TD_API_ROOT from environment if not provided in config
366
368
  if (!config.apiRoot && process.env.TD_API_ROOT) {
367
369
  config.apiRoot = process.env.TD_API_ROOT;
368
370
  }
369
-
371
+
370
372
  const testdriver = new TestDriverSDK(apiKey, config);
371
-
373
+
372
374
  // Connect to sandbox
373
375
  await testdriver.auth();
374
376
  await testdriver.connect();
375
-
377
+
376
378
  return testdriver;
377
379
  }
378
380
 
379
381
  /**
380
382
  * Register a test with a shared TestDriver instance
381
383
  * Call this at the start of each test to associate the test context with the driver
382
- *
384
+ *
383
385
  * @param {TestDriver} testdriver - TestDriver client instance from createTestDriver
384
386
  * @param {object} context - Vitest test context (from async (context) => {})
385
- *
387
+ *
386
388
  * @example
387
389
  * it("step01: verify login", async (context) => {
388
390
  * registerTest(testdriver, context);
@@ -391,12 +393,14 @@ export async function createTestDriver(options = {}) {
391
393
  */
392
394
  export function registerTest(testdriver, context) {
393
395
  if (!testdriver) {
394
- throw new Error('registerTest() requires a TestDriver instance');
396
+ throw new Error("registerTest() requires a TestDriver instance");
395
397
  }
396
398
  if (!context || !context.task) {
397
- throw new Error('registerTest() requires Vitest context. Pass the context parameter from your test function.');
399
+ throw new Error(
400
+ "registerTest() requires Vitest context. Pass the context parameter from your test function.",
401
+ );
398
402
  }
399
-
403
+
400
404
  testdriver.__vitestContext = context.task;
401
405
  logger.debug(`Registered test: ${context.task.name}`);
402
406
  }
@@ -404,9 +408,9 @@ export function registerTest(testdriver, context) {
404
408
  /**
405
409
  * Clean up a TestDriver client created with createTestDriver
406
410
  * Call this in afterAll to properly disconnect and stop recordings
407
- *
411
+ *
408
412
  * @param {TestDriver} testdriver - TestDriver client instance
409
- *
413
+ *
410
414
  * @example
411
415
  * afterAll(async () => {
412
416
  * await cleanupTestDriver(testdriver);
@@ -416,31 +420,44 @@ export async function cleanupTestDriver(testdriver) {
416
420
  if (!testdriver) {
417
421
  return;
418
422
  }
419
-
423
+
420
424
  try {
421
425
  // Stop dashcam if it was started
422
426
  if (testdriver._dashcam && testdriver._dashcam.recording) {
423
427
  try {
424
428
  const dashcamUrl = await testdriver.dashcam.stop();
425
- console.log('🎥 Dashcam URL:', dashcamUrl);
426
-
429
+ const debugMode =
430
+ process.env.VERBOSE || process.env.DEBUG || process.env.TD_DEBUG;
431
+ if (debugMode) {
432
+ console.log("🎥 Dashcam URL:", dashcamUrl);
433
+ }
434
+
427
435
  // Register dashcam URL in memory for the reporter
428
436
  if (dashcamUrl && globalThis.__testdriverPlugin?.registerDashcamUrl) {
429
- const testId = testdriver.__vitestContext?.id || 'unknown';
430
- const platform = testdriver.os || 'linux';
431
- globalThis.__testdriverPlugin.registerDashcamUrl(testId, dashcamUrl, platform);
437
+ const testId = testdriver.__vitestContext?.id || "unknown";
438
+ const platform = testdriver.os || "linux";
439
+ globalThis.__testdriverPlugin.registerDashcamUrl(
440
+ testId,
441
+ dashcamUrl,
442
+ platform,
443
+ );
432
444
  }
433
445
  } catch (error) {
434
- console.error('❌ Failed to stop dashcam:', error.message);
435
- if (error.name === 'NotFoundError' || error.responseData?.error === 'NotFoundError') {
436
- console.log(' ℹ️ Sandbox session already terminated - dashcam stop skipped');
446
+ console.error("❌ Failed to stop dashcam:", error.message);
447
+ if (
448
+ error.name === "NotFoundError" ||
449
+ error.responseData?.error === "NotFoundError"
450
+ ) {
451
+ console.log(
452
+ " ℹ️ Sandbox session already terminated - dashcam stop skipped",
453
+ );
437
454
  }
438
455
  }
439
456
  }
440
-
457
+
441
458
  await testdriver.disconnect();
442
459
  } catch (error) {
443
- console.error('Error disconnecting client:', error);
460
+ console.error("Error disconnecting client:", error);
444
461
  }
445
462
  }
446
463
 
@@ -452,7 +469,7 @@ async function handleProcessExit() {
452
469
  logger.debug("testRun:", !!pluginState.testRun);
453
470
  logger.debug("testRunId:", pluginState.testRunId);
454
471
  logger.debug("testRunCompleted:", pluginState.testRunCompleted);
455
-
472
+
456
473
  if (!pluginState.testRun || !pluginState.testRunId) {
457
474
  logger.debug("No test run to cancel - skipping cleanup");
458
475
  return;
@@ -517,20 +534,22 @@ function registerExitHandlers() {
517
534
  }
518
535
  isExiting = true;
519
536
  isCancelling = true; // Mark that we're cancelling
520
-
537
+
521
538
  // Temporarily override process.exit to prevent Vitest from exiting before we're done
522
539
  const originalExit = process.exit;
523
540
  let exitCalled = false;
524
541
  let exitCode = 130;
525
-
542
+
526
543
  process.exit = (code) => {
527
544
  if (!exitCalled) {
528
545
  exitCalled = true;
529
546
  exitCode = code ?? 130;
530
- logger.debug(`process.exit(${exitCode}) called, waiting for cleanup...`);
547
+ logger.debug(
548
+ `process.exit(${exitCode}) called, waiting for cleanup...`,
549
+ );
531
550
  }
532
551
  };
533
-
552
+
534
553
  handleProcessExit()
535
554
  .then(() => {
536
555
  logger.debug("Cleanup completed successfully");
@@ -552,14 +571,14 @@ function registerExitHandlers() {
552
571
  if (isExiting) return;
553
572
  isExiting = true;
554
573
  isCancelling = true;
555
-
574
+
556
575
  const originalExit = process.exit;
557
576
  let exitCode = 143;
558
-
577
+
559
578
  process.exit = (code) => {
560
579
  exitCode = code ?? 143;
561
580
  };
562
-
581
+
563
582
  handleProcessExit()
564
583
  .then(() => {
565
584
  logger.debug("Cleanup completed successfully");
@@ -573,7 +592,6 @@ function registerExitHandlers() {
573
592
  process.exit(exitCode);
574
593
  });
575
594
  });
576
-
577
595
  }
578
596
 
579
597
  /**
@@ -584,7 +602,9 @@ export default function testDriverPlugin(options = {}) {
584
602
  // Store options but don't read env vars yet - they may not be loaded
585
603
  // Environment variables will be read in onInit after setupFiles run
586
604
  pluginState.apiRoot =
587
- options.apiRoot || process.env.TD_API_ROOT || "https://testdriver-api.onrender.com";
605
+ options.apiRoot ||
606
+ process.env.TD_API_ROOT ||
607
+ "https://testdriver-api.onrender.com";
588
608
  pluginState.ciProvider = detectCI();
589
609
  pluginState.gitInfo = getGitInfo();
590
610
 
@@ -607,10 +627,10 @@ export default function testDriverPlugin(options = {}) {
607
627
 
608
628
  // Create reporter instance
609
629
  const reporter = new TestDriverReporter(options);
610
-
630
+
611
631
  // Add name property for Vitest
612
- reporter.name = 'testdriver';
613
-
632
+ reporter.name = "testdriver";
633
+
614
634
  return reporter;
615
635
  }
616
636
 
@@ -621,7 +641,10 @@ export default function testDriverPlugin(options = {}) {
621
641
  class TestDriverReporter {
622
642
  constructor(options = {}) {
623
643
  this.options = options;
624
- logger.debug("Reporter created with options:", { hasApiKey: !!options.apiKey, hasApiRoot: !!options.apiRoot });
644
+ logger.debug("Reporter created with options:", {
645
+ hasApiKey: !!options.apiKey,
646
+ hasApiRoot: !!options.apiRoot,
647
+ });
625
648
  }
626
649
 
627
650
  async onInit(ctx) {
@@ -634,7 +657,10 @@ class TestDriverReporter {
634
657
 
635
658
  // NOW read the API key and API root (after setupFiles have run, including dotenv/config)
636
659
  pluginState.apiKey = this.options.apiKey || process.env.TD_API_KEY;
637
- pluginState.apiRoot = this.options.apiRoot || process.env.TD_API_ROOT || "https://testdriver-api.onrender.com";
660
+ pluginState.apiRoot =
661
+ this.options.apiRoot ||
662
+ process.env.TD_API_ROOT ||
663
+ "https://testdriver-api.onrender.com";
638
664
  logger.debug("API key from options:", !!this.options.apiKey);
639
665
  logger.debug("API key from env (at onInit):", !!process.env.TD_API_KEY);
640
666
  logger.debug("API root from options:", this.options.apiRoot);
@@ -654,7 +680,12 @@ class TestDriverReporter {
654
680
  // Check if we should enable the reporter
655
681
  if (!pluginState.apiKey) {
656
682
  logger.warn("No API key provided, skipping test recording");
657
- logger.debug("API key sources - options:", !!this.options.apiKey, "env:", !!process.env.TD_API_KEY);
683
+ logger.debug(
684
+ "API key sources - options:",
685
+ !!this.options.apiKey,
686
+ "env:",
687
+ !!process.env.TD_API_KEY,
688
+ );
658
689
  return;
659
690
  }
660
691
 
@@ -705,7 +736,6 @@ class TestDriverReporter {
705
736
  apiRoot: pluginState.apiRoot,
706
737
  startTime: pluginState.startTime,
707
738
  });
708
-
709
739
  } catch (error) {
710
740
  logger.error("Failed to initialize:", error.message);
711
741
  pluginState.apiKey = null;
@@ -723,7 +753,9 @@ class TestDriverReporter {
723
753
 
724
754
  // If we're cancelling due to SIGINT/SIGTERM, skip - handleProcessExit will handle it
725
755
  if (isCancelling) {
726
- logger.debug("Cancellation in progress via signal handler, skipping onTestRunEnd");
756
+ logger.debug(
757
+ "Cancellation in progress via signal handler, skipping onTestRunEnd",
758
+ );
727
759
  return;
728
760
  }
729
761
 
@@ -734,12 +766,16 @@ class TestDriverReporter {
734
766
  }
735
767
 
736
768
  if (!pluginState.apiKey) {
737
- logger.warn("Skipping completion - no API key (was it cleared after init failure?)");
769
+ logger.warn(
770
+ "Skipping completion - no API key (was it cleared after init failure?)",
771
+ );
738
772
  return;
739
773
  }
740
774
 
741
775
  if (!pluginState.testRun) {
742
- logger.warn("Skipping completion - no test run created (check initialization logs)");
776
+ logger.warn(
777
+ "Skipping completion - no test run created (check initialization logs)",
778
+ );
743
779
  return;
744
780
  }
745
781
 
@@ -765,7 +801,9 @@ class TestDriverReporter {
765
801
  }
766
802
 
767
803
  // Complete test run via API
768
- logger.debug(`Completing test run ${pluginState.testRunId} with status: ${status}`);
804
+ logger.debug(
805
+ `Completing test run ${pluginState.testRunId} with status: ${status}`,
806
+ );
769
807
 
770
808
  const completeData = {
771
809
  runId: pluginState.testRunId,
@@ -779,17 +817,23 @@ class TestDriverReporter {
779
817
 
780
818
  // Update platform if detected from test results
781
819
  const platform = getPlatform();
782
- logger.debug(`Platform detection result: ${platform}, detectedPlatform in state: ${pluginState.detectedPlatform}`);
820
+ logger.debug(
821
+ `Platform detection result: ${platform}, detectedPlatform in state: ${pluginState.detectedPlatform}`,
822
+ );
783
823
  if (platform) {
784
824
  completeData.platform = platform;
785
825
  logger.debug(`Updating test run with platform: ${platform}`);
786
826
  } else {
787
- logger.warn(`No platform detected, test run will keep default platform`);
827
+ logger.warn(
828
+ `No platform detected, test run will keep default platform`,
829
+ );
788
830
  }
789
831
 
790
832
  // Wait for any pending operations (shouldn't be any, but just in case)
791
833
  if (pluginState.pendingTestCaseRecords.size > 0) {
792
- logger.debug(`Waiting for ${pluginState.pendingTestCaseRecords.size} pending operations...`);
834
+ logger.debug(
835
+ `Waiting for ${pluginState.pendingTestCaseRecords.size} pending operations...`,
836
+ );
793
837
  await Promise.all(Array.from(pluginState.pendingTestCaseRecords));
794
838
  }
795
839
 
@@ -811,12 +855,14 @@ class TestDriverReporter {
811
855
  logger.info(`View test run: ${testRunUrl}`);
812
856
  // Output in a parseable format for CI
813
857
  console.log(`TESTDRIVER_RUN_URL=${testRunUrl}`);
814
-
858
+
815
859
  // Post GitHub comment if in CI environment
816
860
  await postGitHubCommentIfEnabled(testRunUrl, stats, completeData);
817
861
  }
818
862
 
819
- logger.info(`Test run completed: ${stats.passedTests}/${stats.totalTests} passed`);
863
+ logger.info(
864
+ `Test run completed: ${stats.passedTests}/${stats.totalTests} passed`,
865
+ );
820
866
  } catch (error) {
821
867
  logger.error("Failed to complete test run:", error.message);
822
868
  logger.debug("Error stack:", error.stack);
@@ -848,8 +894,10 @@ class TestDriverReporter {
848
894
  // Calculate duration from tracked start time
849
895
  const testCase = pluginState.testCases.get(test.id);
850
896
  const duration = testCase ? Date.now() - testCase.startTime : 0;
851
-
852
- logger.debug(`Calculated duration: ${duration}ms (startTime: ${testCase?.startTime}, now: ${Date.now()})`);
897
+
898
+ logger.debug(
899
+ `Calculated duration: ${duration}ms (startTime: ${testCase?.startTime}, now: ${Date.now()})`,
900
+ );
853
901
 
854
902
  // Read test metadata from Vitest's task.meta (set in test hooks)
855
903
  const meta = test.meta();
@@ -874,9 +922,10 @@ class TestDriverReporter {
874
922
  test.suite?.file?.name ||
875
923
  test.location?.file ||
876
924
  "unknown";
877
- testFile = pluginState.projectRoot && absolutePath !== "unknown"
878
- ? path.relative(pluginState.projectRoot, absolutePath)
879
- : absolutePath;
925
+ testFile =
926
+ pluginState.projectRoot && absolutePath !== "unknown"
927
+ ? path.relative(pluginState.projectRoot, absolutePath)
928
+ : absolutePath;
880
929
  logger.debug(`Resolved testFile from fallback: ${testFile}`);
881
930
  }
882
931
 
@@ -890,7 +939,9 @@ class TestDriverReporter {
890
939
  const token = process.env.TD_TEST_RUN_TOKEN;
891
940
 
892
941
  if (!testRunId || !token) {
893
- logger.warn(`Test run not initialized, skipping test case recording for: ${test.name}`);
942
+ logger.warn(
943
+ `Test run not initialized, skipping test case recording for: ${test.name}`,
944
+ );
894
945
  return;
895
946
  }
896
947
 
@@ -938,7 +989,9 @@ class TestDriverReporter {
938
989
  if (errorMessage) testCaseData.errorMessage = errorMessage;
939
990
  if (errorStack) testCaseData.errorStack = errorStack;
940
991
 
941
- logger.debug(`Recording test case: ${test.name} (${status}) with testFile: ${testFile}, testOrder: ${testOrder}, duration: ${duration}ms, replay: ${dashcamUrl ? "yes" : "no"}`);
992
+ logger.debug(
993
+ `Recording test case: ${test.name} (${status}) with testFile: ${testFile}, testOrder: ${testOrder}, duration: ${duration}ms, replay: ${dashcamUrl ? "yes" : "no"}`,
994
+ );
942
995
 
943
996
  const testCaseResponse = await recordTestCaseDirect(
944
997
  token,
@@ -955,8 +1008,10 @@ class TestDriverReporter {
955
1008
  id: testCaseDbId,
956
1009
  });
957
1010
 
958
- console.log('');
959
- console.log(`🔗 Test Report: ${getConsoleUrl(pluginState.apiRoot)}/runs/${testRunDbId}/${testCaseDbId}`);
1011
+ console.log("");
1012
+ console.log(
1013
+ `🔗 Test Report: ${getConsoleUrl(pluginState.apiRoot)}/runs/${testRunDbId}/${testCaseDbId}`,
1014
+ );
960
1015
  } catch (error) {
961
1016
  logger.error("Failed to report test case:", error.message);
962
1017
  }
@@ -970,24 +1025,23 @@ class TestDriverReporter {
970
1025
  /**
971
1026
  * Maps an API root URL to its corresponding web console URL.
972
1027
  * The API and web console are served from different domains/ports.
973
- *
1028
+ *
974
1029
  * @param {string} apiRoot - The API root URL (e.g., https://testdriver-api.onrender.com)
975
1030
  * @returns {string} The corresponding web console URL
976
1031
  */
977
1032
  function getConsoleUrl(apiRoot) {
1033
+ if (!apiRoot) return "https://console.testdriver.ai";
978
1034
 
979
- if (!apiRoot) return 'https://console.testdriver.ai';
980
-
981
1035
  // Production: API on render.com -> Console on testdriver.ai
982
- if (apiRoot.includes('testdriver-api.onrender.com')) {
983
- return 'https://console.testdriver.ai';
1036
+ if (apiRoot.includes("testdriver-api.onrender.com")) {
1037
+ return "https://console.testdriver.ai";
984
1038
  }
985
-
1039
+
986
1040
  // Local development: API on localhost:1337 -> Web on localhost:3001
987
- if (apiRoot.includes('ngrok.io')) {
1041
+ if (apiRoot.includes("ngrok.io")) {
988
1042
  return `http://localhost:3001`;
989
1043
  }
990
-
1044
+
991
1045
  // Ngrok or other tunnels: assume same host, different path structure
992
1046
  // For ngrok, the API and web might be on same domain or user needs to configure
993
1047
  // Return as-is since we can't reliably determine the mapping
@@ -1005,14 +1059,18 @@ function getSuiteName() {
1005
1059
  function getPlatform() {
1006
1060
  // First try to get platform from SDK client detected during test execution
1007
1061
  if (pluginState.detectedPlatform) {
1008
- logger.debug(`Using platform from SDK client: ${pluginState.detectedPlatform}`);
1062
+ logger.debug(
1063
+ `Using platform from SDK client: ${pluginState.detectedPlatform}`,
1064
+ );
1009
1065
  return pluginState.detectedPlatform;
1010
1066
  }
1011
1067
 
1012
1068
  // Try to get platform from dashcam URLs (registered during test cleanup)
1013
1069
  for (const [, data] of pluginState.dashcamUrls) {
1014
1070
  if (data.platform) {
1015
- logger.debug(`Using platform from dashcam URL registration: ${data.platform}`);
1071
+ logger.debug(
1072
+ `Using platform from dashcam URL registration: ${data.platform}`,
1073
+ );
1016
1074
  return data.platform;
1017
1075
  }
1018
1076
  }
@@ -1083,7 +1141,7 @@ function getGitInfo() {
1083
1141
  try {
1084
1142
  info.commit = execSync("git rev-parse HEAD", {
1085
1143
  encoding: "utf8",
1086
- stdio: ["pipe", "pipe", "ignore"]
1144
+ stdio: ["pipe", "pipe", "ignore"],
1087
1145
  }).trim();
1088
1146
  logger.debug("Git commit from local:", info.commit);
1089
1147
  } catch (e) {
@@ -1095,7 +1153,7 @@ function getGitInfo() {
1095
1153
  try {
1096
1154
  info.branch = execSync("git rev-parse --abbrev-ref HEAD", {
1097
1155
  encoding: "utf8",
1098
- stdio: ["pipe", "pipe", "ignore"]
1156
+ stdio: ["pipe", "pipe", "ignore"],
1099
1157
  }).trim();
1100
1158
  logger.debug("Git branch from local:", info.branch);
1101
1159
  } catch (e) {
@@ -1107,7 +1165,7 @@ function getGitInfo() {
1107
1165
  try {
1108
1166
  info.author = execSync("git config user.name", {
1109
1167
  encoding: "utf8",
1110
- stdio: ["pipe", "pipe", "ignore"]
1168
+ stdio: ["pipe", "pipe", "ignore"],
1111
1169
  }).trim();
1112
1170
  logger.debug("Git author from local:", info.author);
1113
1171
  } catch (e) {
@@ -1119,7 +1177,7 @@ function getGitInfo() {
1119
1177
  try {
1120
1178
  const remoteUrl = execSync("git config --get remote.origin.url", {
1121
1179
  encoding: "utf8",
1122
- stdio: ["pipe", "pipe", "ignore"]
1180
+ stdio: ["pipe", "pipe", "ignore"],
1123
1181
  }).trim();
1124
1182
 
1125
1183
  // Extract repo from git URL (supports both SSH and HTTPS)
@@ -1153,42 +1211,46 @@ function getGitInfo() {
1153
1211
  async function postGitHubCommentIfEnabled(testRunUrl, stats, completeData) {
1154
1212
  try {
1155
1213
  // Check if GitHub comments are explicitly disabled
1156
- if (process.env.TESTDRIVER_SKIP_GITHUB_COMMENT === 'true') {
1157
- logger.debug('GitHub comments disabled via TESTDRIVER_SKIP_GITHUB_COMMENT');
1214
+ if (process.env.TESTDRIVER_SKIP_GITHUB_COMMENT === "true") {
1215
+ logger.debug(
1216
+ "GitHub comments disabled via TESTDRIVER_SKIP_GITHUB_COMMENT",
1217
+ );
1158
1218
  return;
1159
1219
  }
1160
-
1220
+
1161
1221
  // Check if GitHub comment posting is enabled
1162
1222
  const githubToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
1163
1223
  const prNumber = process.env.GITHUB_PR_NUMBER;
1164
1224
  const commitSha = process.env.GITHUB_SHA || pluginState.gitInfo.commit;
1165
-
1225
+
1166
1226
  // Only post if we have a token and either a PR number or commit SHA
1167
1227
  if (!githubToken) {
1168
- logger.debug('GitHub token not found, skipping comment posting');
1228
+ logger.debug("GitHub token not found, skipping comment posting");
1169
1229
  return;
1170
1230
  }
1171
-
1231
+
1172
1232
  if (!prNumber && !commitSha) {
1173
- logger.debug('Neither PR number nor commit SHA found, skipping comment posting');
1233
+ logger.debug(
1234
+ "Neither PR number nor commit SHA found, skipping comment posting",
1235
+ );
1174
1236
  return;
1175
1237
  }
1176
-
1238
+
1177
1239
  // Extract owner/repo from git info
1178
1240
  const repo = pluginState.gitInfo.repo;
1179
1241
  if (!repo) {
1180
- logger.warn('Repository info not available, skipping GitHub comment');
1242
+ logger.warn("Repository info not available, skipping GitHub comment");
1181
1243
  return;
1182
1244
  }
1183
-
1184
- const [owner, repoName] = repo.split('/');
1245
+
1246
+ const [owner, repoName] = repo.split("/");
1185
1247
  if (!owner || !repoName) {
1186
- logger.warn('Invalid repository format, expected owner/repo');
1248
+ logger.warn("Invalid repository format, expected owner/repo");
1187
1249
  return;
1188
1250
  }
1189
-
1190
- logger.debug('Preparing GitHub comment...');
1191
-
1251
+
1252
+ logger.debug("Preparing GitHub comment...");
1253
+
1192
1254
  // Prepare test run data for comment
1193
1255
  const testRunData = {
1194
1256
  runId: pluginState.testRunId,
@@ -1199,16 +1261,19 @@ async function postGitHubCommentIfEnabled(testRunUrl, stats, completeData) {
1199
1261
  skippedTests: stats.skippedTests,
1200
1262
  duration: completeData.duration,
1201
1263
  testRunUrl,
1202
- platform: completeData.platform || pluginState.detectedPlatform || 'unknown',
1203
- branch: pluginState.gitInfo.branch || 'unknown',
1204
- commit: commitSha || 'unknown',
1264
+ platform:
1265
+ completeData.platform || pluginState.detectedPlatform || "unknown",
1266
+ branch: pluginState.gitInfo.branch || "unknown",
1267
+ commit: commitSha || "unknown",
1205
1268
  };
1206
-
1269
+
1207
1270
  // Use recorded test cases from pluginState
1208
1271
  const testCases = pluginState.recordedTestCases || [];
1209
-
1210
- logger.info(`Posting GitHub comment with ${testCases.length} test cases...`);
1211
-
1272
+
1273
+ logger.info(
1274
+ `Posting GitHub comment with ${testCases.length} test cases...`,
1275
+ );
1276
+
1212
1277
  // Post or update GitHub comment
1213
1278
  const githubOptions = {
1214
1279
  token: githubToken,
@@ -1217,14 +1282,17 @@ async function postGitHubCommentIfEnabled(testRunUrl, stats, completeData) {
1217
1282
  prNumber: prNumber ? parseInt(prNumber, 10) : undefined,
1218
1283
  commitSha: commitSha,
1219
1284
  };
1220
-
1221
- const comment = await postOrUpdateTestResults(testRunData, testCases, githubOptions);
1285
+
1286
+ const comment = await postOrUpdateTestResults(
1287
+ testRunData,
1288
+ testCases,
1289
+ githubOptions,
1290
+ );
1222
1291
  logger.info(`✅ GitHub comment posted: ${comment.html_url}`);
1223
1292
  console.log(`\n🔗 GitHub Comment: ${comment.html_url}\n`);
1224
-
1225
1293
  } catch (error) {
1226
- logger.warn('Failed to post GitHub comment:', error.message);
1227
- logger.debug('GitHub comment error stack:', error.stack);
1294
+ logger.warn("Failed to post GitHub comment:", error.message);
1295
+ logger.debug("GitHub comment error stack:", error.stack);
1228
1296
  }
1229
1297
  }
1230
1298
 
@@ -1286,7 +1354,7 @@ async function createTestRun(data) {
1286
1354
  async function completeTestRun(data) {
1287
1355
  const url = `${pluginState.apiRoot}/api/v1/testdriver/test-run-complete`;
1288
1356
  logger.debug(`completeTestRun: POSTing to ${url}`);
1289
-
1357
+
1290
1358
  try {
1291
1359
  const response = await withTimeout(
1292
1360
  fetch(url, {