testdriverai 7.2.63 → 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 (362) hide show
  1. package/agent/index.js +77 -59
  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 +79 -29
  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/core/Dashcam.js +22 -15
  52. package/lib/sentry.js +4 -3
  53. package/lib/vitest/hooks.mjs +70 -16
  54. package/lib/vitest/setup-aws.mjs +0 -10
  55. package/package.json +29 -9
  56. package/sdk.d.ts +32 -5
  57. package/sdk.js +10 -9
  58. package/.env.example +0 -4
  59. package/.github/workflows/acceptance-linux-scheduled.yaml +0 -45
  60. package/.github/workflows/acceptance-windows-scheduled.yaml +0 -54
  61. package/.github/workflows/acceptance.yaml +0 -87
  62. package/.github/workflows/publish.yaml +0 -68
  63. package/.github/workflows/test-init.yml +0 -145
  64. package/.github/workflows/testdriver.yml +0 -170
  65. package/.github/workflows/windows-self-hosted.yaml +0 -82
  66. package/.prettierignore +0 -4
  67. package/.prettierrc +0 -1
  68. package/CHANGELOG.md +0 -30
  69. package/agents.md +0 -455
  70. package/debugger/bg.png +0 -0
  71. package/debugger/icon.png +0 -0
  72. package/debugger/index.html +0 -797
  73. package/debugger/td.png +0 -0
  74. package/debugger/tray-buffered.png +0 -0
  75. package/debugger/tray.png +0 -0
  76. package/docs/GITHUB_COMMENTS.md +0 -330
  77. package/docs/GITHUB_COMMENTS_ANNOUNCEMENT.md +0 -167
  78. package/docs/QUICK-START-GITHUB-COMMENTS.md +0 -84
  79. package/docs/TEST-GITHUB-COMMENTS.md +0 -129
  80. package/docs/_scripts/link-replacer.js +0 -164
  81. package/docs/_scripts/upload-docs-to-openai.js +0 -284
  82. package/docs/docs.json +0 -393
  83. package/docs/github-integration-setup.md +0 -266
  84. package/docs/guide/best-practices-polling.mdx +0 -154
  85. package/docs/images/content/account/newprojectsettings.png +0 -0
  86. package/docs/images/content/account/projectpage.png +0 -0
  87. package/docs/images/content/account/projectreplays.png +0 -0
  88. package/docs/images/content/account/team-manage.png +0 -0
  89. package/docs/images/content/account/teampage.png +0 -0
  90. package/docs/images/content/extension/cursor.svg +0 -1
  91. package/docs/images/content/extension/vscode.svg +0 -57
  92. package/docs/images/content/extension/windsurf.svg +0 -3
  93. package/docs/images/content/self-hosted/launchtemplateid.png +0 -0
  94. package/docs/images/content/side-by-side.png +0 -0
  95. package/docs/images/content/vscode/ide-full.png +0 -0
  96. package/docs/images/content/vscode/running.png +0 -0
  97. package/docs/images/content/vscode/vscode-2-assert.png +0 -0
  98. package/docs/images/content/vscode/vscode-agent-preview.png +0 -0
  99. package/docs/images/content/vscode/vscode-copilot-ask.png +0 -0
  100. package/docs/images/content/vscode/vscode-file-creation.png +0 -0
  101. package/docs/images/content/vscode/vscode-install.png +0 -0
  102. package/docs/images/content/vscode/vscode-overview.png +0 -0
  103. package/docs/images/content/vscode/vscode-setup-walkthrough.png +0 -0
  104. package/docs/images/content/vscode/vscode-stopchat.png +0 -0
  105. package/docs/images/content/vscode/vscode-stoptest.png +0 -0
  106. package/docs/images/content/vscode/vscode-tdservice.png +0 -0
  107. package/docs/images/content/vscode/vscode-test-output.png +0 -0
  108. package/docs/images/content/vscode/vscode-testhistory.png +0 -0
  109. package/docs/images/content/vscode/vscode-testpane-runtests.png +0 -0
  110. package/docs/images/content/vscode/vscode-testpane.png +0 -0
  111. package/docs/images/template/dark.png +0 -0
  112. package/docs/images/template/icon.png +0 -0
  113. package/docs/images/template/light.png +0 -0
  114. package/docs/snippets/calendar-link.mdx +0 -4
  115. package/docs/snippets/gitignore-warning.mdx +0 -7
  116. package/docs/snippets/lifecycle-warning.mdx +0 -6
  117. package/docs/snippets/test-prereqs.mdx +0 -12
  118. package/docs/snippets/tests/assert-replay.mdx +0 -7
  119. package/docs/snippets/tests/assert-yaml.mdx +0 -8
  120. package/docs/snippets/tests/exec-js-replay.mdx +0 -7
  121. package/docs/snippets/tests/exec-js-yaml.mdx +0 -32
  122. package/docs/snippets/tests/exec-shell-replay.mdx +0 -7
  123. package/docs/snippets/tests/exec-shell-yaml.mdx +0 -15
  124. package/docs/snippets/tests/hover-image-replay.mdx +0 -7
  125. package/docs/snippets/tests/hover-image-yaml.mdx +0 -17
  126. package/docs/snippets/tests/hover-text-replay.mdx +0 -7
  127. package/docs/snippets/tests/hover-text-with-description-replay.mdx +0 -7
  128. package/docs/snippets/tests/hover-text-with-description-yaml.mdx +0 -24
  129. package/docs/snippets/tests/hover-text-yaml.mdx +0 -14
  130. package/docs/snippets/tests/match-image-replay.mdx +0 -7
  131. package/docs/snippets/tests/match-image-yaml.mdx +0 -17
  132. package/docs/snippets/tests/press-keys-replay.mdx +0 -7
  133. package/docs/snippets/tests/press-keys-yaml.mdx +0 -36
  134. package/docs/snippets/tests/remember-replay.mdx +0 -7
  135. package/docs/snippets/tests/remember-yaml.mdx +0 -28
  136. package/docs/snippets/tests/scroll-replay.mdx +0 -7
  137. package/docs/snippets/tests/scroll-until-image-replay.mdx +0 -7
  138. package/docs/snippets/tests/scroll-until-image-yaml.mdx +0 -14
  139. package/docs/snippets/tests/scroll-until-text-replay.mdx +0 -7
  140. package/docs/snippets/tests/scroll-until-text-yaml.mdx +0 -17
  141. package/docs/snippets/tests/scroll-yaml.mdx +0 -30
  142. package/docs/snippets/tests/type-repeated-replay.mdx +0 -7
  143. package/docs/snippets/tests/type-repeated-yaml.mdx +0 -22
  144. package/docs/snippets/tests/type-replay.mdx +0 -7
  145. package/docs/snippets/tests/type-yaml.mdx +0 -28
  146. package/docs/snippets/tests/wait-for-image-replay.mdx +0 -7
  147. package/docs/snippets/tests/wait-for-image-yaml.mdx +0 -18
  148. package/docs/snippets/tests/wait-for-text-replay.mdx +0 -7
  149. package/docs/snippets/tests/wait-for-text-yaml.mdx +0 -18
  150. package/docs/snippets/tests/wait-replay.mdx +0 -7
  151. package/docs/snippets/tests/wait-yaml.mdx +0 -13
  152. package/docs/styles.css +0 -65
  153. package/docs/v6/account/dashboard.mdx +0 -16
  154. package/docs/v6/account/enterprise.mdx +0 -110
  155. package/docs/v6/account/pricing.mdx +0 -33
  156. package/docs/v6/account/projects.mdx +0 -33
  157. package/docs/v6/account/team.mdx +0 -35
  158. package/docs/v6/action/ami.mdx +0 -109
  159. package/docs/v6/action/performance.mdx +0 -105
  160. package/docs/v6/action/secrets.mdx +0 -93
  161. package/docs/v6/apps/chrome-extensions.mdx +0 -48
  162. package/docs/v6/apps/desktop-apps.mdx +0 -93
  163. package/docs/v6/apps/mobile-apps.mdx +0 -26
  164. package/docs/v6/apps/static-websites.mdx +0 -54
  165. package/docs/v6/apps/tauri-apps.mdx +0 -361
  166. package/docs/v6/bugs/jira.mdx +0 -232
  167. package/docs/v6/cli/overview.mdx +0 -66
  168. package/docs/v6/commands/assert.mdx +0 -45
  169. package/docs/v6/commands/exec.mdx +0 -282
  170. package/docs/v6/commands/focus-application.mdx +0 -44
  171. package/docs/v6/commands/hover-image.mdx +0 -69
  172. package/docs/v6/commands/hover-text.mdx +0 -47
  173. package/docs/v6/commands/if.mdx +0 -53
  174. package/docs/v6/commands/match-image.mdx +0 -67
  175. package/docs/v6/commands/press-keys.mdx +0 -87
  176. package/docs/v6/commands/remember.mdx +0 -49
  177. package/docs/v6/commands/run.mdx +0 -44
  178. package/docs/v6/commands/scroll-until-image.mdx +0 -66
  179. package/docs/v6/commands/scroll-until-text.mdx +0 -60
  180. package/docs/v6/commands/scroll.mdx +0 -69
  181. package/docs/v6/commands/type.mdx +0 -45
  182. package/docs/v6/commands/wait-for-image.mdx +0 -54
  183. package/docs/v6/commands/wait-for-text.mdx +0 -48
  184. package/docs/v6/commands/wait.mdx +0 -45
  185. package/docs/v6/exporting/junit.mdx +0 -218
  186. package/docs/v6/exporting/playwright.mdx +0 -197
  187. package/docs/v6/features/auto-healing.mdx +0 -144
  188. package/docs/v6/features/generation.mdx +0 -116
  189. package/docs/v6/features/parallel-testing.mdx +0 -151
  190. package/docs/v6/features/reusable-snippets.mdx +0 -131
  191. package/docs/v6/features/selectorless.mdx +0 -80
  192. package/docs/v6/features/visual-assertions.mdx +0 -139
  193. package/docs/v6/getting-started/ci.mdx +0 -146
  194. package/docs/v6/getting-started/cli.mdx +0 -91
  195. package/docs/v6/getting-started/editing.mdx +0 -100
  196. package/docs/v6/getting-started/playwright.mdx +0 -342
  197. package/docs/v6/getting-started/running.mdx +0 -48
  198. package/docs/v6/getting-started/self-hosting.mdx +0 -408
  199. package/docs/v6/getting-started/vscode.mdx +0 -89
  200. package/docs/v6/guide/assertions.mdx +0 -189
  201. package/docs/v6/guide/authentication.mdx +0 -136
  202. package/docs/v6/guide/code.mdx +0 -65
  203. package/docs/v6/guide/dashcam.mdx +0 -118
  204. package/docs/v6/guide/environment-variables.mdx +0 -26
  205. package/docs/v6/guide/lifecycle.mdx +0 -242
  206. package/docs/v6/guide/locating.mdx +0 -141
  207. package/docs/v6/guide/protips.mdx +0 -43
  208. package/docs/v6/guide/variables.mdx +0 -143
  209. package/docs/v6/guide/waiting.mdx +0 -130
  210. package/docs/v6/importing/csv.mdx +0 -196
  211. package/docs/v6/importing/gherkin.mdx +0 -143
  212. package/docs/v6/importing/jira.mdx +0 -164
  213. package/docs/v6/importing/testrail.mdx +0 -162
  214. package/docs/v6/integrations/electron.mdx +0 -146
  215. package/docs/v6/integrations/netlify.mdx +0 -100
  216. package/docs/v6/integrations/vercel.mdx +0 -125
  217. package/docs/v6/interactive/explore.mdx +0 -99
  218. package/docs/v6/interactive/run.mdx +0 -52
  219. package/docs/v6/interactive/save.mdx +0 -63
  220. package/docs/v6/overview/comparison.mdx +0 -101
  221. package/docs/v6/overview/faq.mdx +0 -162
  222. package/docs/v6/overview/performance.mdx +0 -52
  223. package/docs/v6/overview/quickstart.mdx +0 -137
  224. package/docs/v6/overview/what-is-testdriver.mdx +0 -85
  225. package/docs/v6/scenarios/ai-chatbot.mdx +0 -28
  226. package/docs/v6/scenarios/cookie-banner.mdx +0 -32
  227. package/docs/v6/scenarios/file-upload.mdx +0 -33
  228. package/docs/v6/scenarios/form-filling.mdx +0 -32
  229. package/docs/v6/scenarios/log-in.mdx +0 -75
  230. package/docs/v6/scenarios/pdf-generation.mdx +0 -25
  231. package/docs/v6/scenarios/spell-check.mdx +0 -22
  232. package/docs/v6/security/action.mdx +0 -84
  233. package/docs/v6/security/agent.mdx +0 -73
  234. package/docs/v6/security/platform.mdx +0 -77
  235. package/docs/v6/tutorials/advanced-test.mdx +0 -81
  236. package/docs/v6/tutorials/basic-test.mdx +0 -45
  237. package/docs/v7/_drafts/agents.mdx +0 -852
  238. package/docs/v7/_drafts/architecture.mdx +0 -399
  239. package/docs/v7/_drafts/auto-cache-key.mdx +0 -167
  240. package/docs/v7/_drafts/awesome-logs-quick-ref.mdx +0 -100
  241. package/docs/v7/_drafts/best-practices.mdx +0 -486
  242. package/docs/v7/_drafts/caching-ai.mdx +0 -215
  243. package/docs/v7/_drafts/caching-selectors.mdx +0 -424
  244. package/docs/v7/_drafts/caching.mdx +0 -366
  245. package/docs/v7/_drafts/cli-to-sdk-migration.mdx +0 -425
  246. package/docs/v7/_drafts/commands/assert.mdx +0 -45
  247. package/docs/v7/_drafts/commands/exec.mdx +0 -282
  248. package/docs/v7/_drafts/commands/focus-application.mdx +0 -44
  249. package/docs/v7/_drafts/commands/hover-image.mdx +0 -69
  250. package/docs/v7/_drafts/commands/hover-text.mdx +0 -47
  251. package/docs/v7/_drafts/commands/if.mdx +0 -53
  252. package/docs/v7/_drafts/commands/match-image.mdx +0 -67
  253. package/docs/v7/_drafts/commands/press-keys.mdx +0 -87
  254. package/docs/v7/_drafts/commands/remember.mdx +0 -49
  255. package/docs/v7/_drafts/commands/run.mdx +0 -44
  256. package/docs/v7/_drafts/commands/scroll-until-image.mdx +0 -66
  257. package/docs/v7/_drafts/commands/scroll-until-text.mdx +0 -60
  258. package/docs/v7/_drafts/commands/scroll.mdx +0 -69
  259. package/docs/v7/_drafts/commands/type.mdx +0 -45
  260. package/docs/v7/_drafts/commands/wait-for-image.mdx +0 -54
  261. package/docs/v7/_drafts/commands/wait-for-text.mdx +0 -48
  262. package/docs/v7/_drafts/commands/wait.mdx +0 -45
  263. package/docs/v7/_drafts/configuration.mdx +0 -378
  264. package/docs/v7/_drafts/contributing.mdx +0 -174
  265. package/docs/v7/_drafts/core.mdx +0 -458
  266. package/docs/v7/_drafts/dashcam-title-feature.mdx +0 -89
  267. package/docs/v7/_drafts/debugging.mdx +0 -349
  268. package/docs/v7/_drafts/error-handling.mdx +0 -501
  269. package/docs/v7/_drafts/faq.mdx +0 -393
  270. package/docs/v7/_drafts/hooks.mdx +0 -360
  271. package/docs/v7/_drafts/init-command.mdx +0 -95
  272. package/docs/v7/_drafts/installation.mdx +0 -420
  273. package/docs/v7/_drafts/migration.mdx +0 -562
  274. package/docs/v7/_drafts/observable.mdx +0 -604
  275. package/docs/v7/_drafts/playwright.mdx +0 -342
  276. package/docs/v7/_drafts/plugin-migration.mdx +0 -220
  277. package/docs/v7/_drafts/powerful.mdx +0 -419
  278. package/docs/v7/_drafts/presets.mdx +0 -210
  279. package/docs/v7/_drafts/progressive-disclosure.mdx +0 -230
  280. package/docs/v7/_drafts/prompt-cache.mdx +0 -200
  281. package/docs/v7/_drafts/provision.mdx +0 -390
  282. package/docs/v7/_drafts/quick-start-test-recording.mdx +0 -214
  283. package/docs/v7/_drafts/readme.mdx +0 -135
  284. package/docs/v7/_drafts/reports.mdx +0 -414
  285. package/docs/v7/_drafts/scalable.mdx +0 -754
  286. package/docs/v7/_drafts/screenshot.mdx +0 -155
  287. package/docs/v7/_drafts/sdk-awesome-logs.mdx +0 -468
  288. package/docs/v7/_drafts/sdk-browser-rendering.mdx +0 -167
  289. package/docs/v7/_drafts/sdk-migration.mdx +0 -474
  290. package/docs/v7/_drafts/sdk-v7-complete.mdx +0 -345
  291. package/docs/v7/_drafts/self-hosting.mdx +0 -369
  292. package/docs/v7/_drafts/test-recording.mdx +0 -382
  293. package/docs/v7/_drafts/troubleshooting.mdx +0 -526
  294. package/docs/v7/_drafts/vitest-plugin.mdx +0 -477
  295. package/docs/v7/_drafts/vitest.mdx +0 -535
  296. package/docs/v7/_drafts/writing-tests.mdx +0 -25
  297. package/docs/v7/examples.mdx +0 -5
  298. package/eslint.config.js +0 -67
  299. package/examples/ai.test.mjs +0 -30
  300. package/examples/assert.test.mjs +0 -46
  301. package/examples/captcha-api.test.mjs +0 -50
  302. package/examples/chrome-extension.test.mjs +0 -94
  303. package/examples/drag-and-drop.test.mjs +0 -58
  304. package/examples/element-not-found.test.mjs +0 -26
  305. package/examples/exec-output.test.mjs +0 -59
  306. package/examples/exec-pwsh.test.mjs +0 -57
  307. package/examples/focus-window.test.mjs +0 -36
  308. package/examples/formatted-logging.test.mjs +0 -26
  309. package/examples/hover-image.test.mjs +0 -52
  310. package/examples/hover-text-with-description.test.mjs +0 -56
  311. package/examples/hover-text.test.mjs +0 -27
  312. package/examples/installer.test.mjs +0 -49
  313. package/examples/launch-vscode-linux.test.mjs +0 -54
  314. package/examples/match-image.test.mjs +0 -54
  315. package/examples/no-provision.test.mjs +0 -23
  316. package/examples/press-keys.test.mjs +0 -50
  317. package/examples/prompt.test.mjs +0 -33
  318. package/examples/scroll-keyboard.test.mjs +0 -37
  319. package/examples/scroll-until-image.test.mjs +0 -39
  320. package/examples/scroll-until-text.test.mjs +0 -67
  321. package/examples/scroll.test.mjs +0 -41
  322. package/examples/type.test.mjs +0 -45
  323. package/examples/windows-installer.test.mjs +0 -53
  324. package/interfaces/cli/commands/edit.js +0 -3
  325. package/interfaces/cli/commands/generate.js +0 -3
  326. package/interfaces/cli/commands/run.js +0 -3
  327. package/interfaces/cli/utils/factory.js +0 -71
  328. package/jsconfig.json +0 -26
  329. package/manual/test-init-command.js +0 -223
  330. package/sdk-log-formatter.js +0 -930
  331. package/setup/aws/cloudformation.yaml +0 -470
  332. package/setup/aws/spawn-runner.sh +0 -190
  333. package/test/api-resilience.test.mjs +0 -0
  334. package/test/captcha-solver.test.mjs +0 -70
  335. package/test/chrome-remote-debugging.test.mjs +0 -66
  336. package/test/manual/debug-locate-response.js +0 -82
  337. package/test/manual/reconnect-provision.test.mjs +0 -49
  338. package/test/manual/test-console-logs.test.mjs +0 -42
  339. package/test/manual/test-find-api.js +0 -73
  340. package/test/manual/test-init.sh +0 -54
  341. package/test/manual/test-prompt-cache.js +0 -96
  342. package/test/manual/test-provision-auth.mjs +0 -22
  343. package/test/manual/test-sandbox-render.js +0 -28
  344. package/test/manual/test-sdk-methods.js +0 -15
  345. package/test/manual/test-sdk-refactor.js +0 -53
  346. package/test/manual/test-stack-trace.mjs +0 -57
  347. package/test/manual/verify-element-api.js +0 -89
  348. package/test/manual/verify-types.js +0 -0
  349. package/test/manual-unawaited-promise.test.mjs +0 -31
  350. package/testdriver-plugin/skills/actions/SKILL.md +0 -93
  351. package/testdriver-plugin/skills/assertions/SKILL.md +0 -77
  352. package/testdriver-plugin/skills/caching/SKILL.md +0 -66
  353. package/testdriver-plugin/skills/creating-tests/SKILL.md +0 -104
  354. package/testdriver-plugin/skills/finding-elements/SKILL.md +0 -77
  355. package/testdriver-plugin/skills/github-actions/SKILL.md +0 -100
  356. package/testdriver-plugin/skills/running-tests/SKILL.md +0 -77
  357. package/testdriver-plugin/skills/secrets/SKILL.md +0 -87
  358. package/testdriver-plugin/skills/self-hosting/SKILL.md +0 -89
  359. package/testdriver-plugin/skills/setup/SKILL.md +0 -76
  360. package/testdriver-plugin/skills/variables/SKILL.md +0 -88
  361. package/testdriver-plugin/skills/waiting/SKILL.md +0 -72
  362. package/vitest.config.mjs +0 -29
@@ -1,930 +0,0 @@
1
- const chalk = require("chalk");
2
-
3
- /**
4
- * AWESOME Log formatter for TestDriver SDK 🎨
5
- * Provides beautiful, emoji-rich formatting with great DX for logs sent to dashcam
6
- * ANSI codes are preserved through the log pipeline: SDK → sandbox → /tmp/testdriver.log → dashcam
7
- *
8
- * Now with full UTF-8 and emoji support! 🚀
9
- */
10
-
11
- // Duration threshold configurations for different contexts
12
- const DURATION_THRESHOLDS = {
13
- default: { fast: 3000, medium: 10000 },
14
- action: { fast: 100, medium: 500 },
15
- redraw: { fast: 5000, medium: 10000 },
16
- quickAction: { fast: 50, medium: 200 },
17
- test: { fast: 1000, medium: 5000 },
18
- };
19
-
20
- class SDKLogFormatter {
21
- constructor(options = {}) {
22
- this.testContext = {
23
- currentTest: null,
24
- currentFile: null,
25
- startTime: null,
26
- };
27
- this.eventCount = 0;
28
- this.useColors = options.colors !== false;
29
- this.useEmojis = options.emojis !== false;
30
- }
31
-
32
- /**
33
- * Set the current test context from Vitest
34
- * @param {Object} context - Test context with file, test name, etc.
35
- */
36
- setTestContext(context) {
37
- if (context.file) this.testContext.currentFile = context.file;
38
- if (context.test) this.testContext.currentTest = context.test;
39
- if (context.startTime) this.testContext.startTime = context.startTime;
40
- }
41
-
42
- /**
43
- * Get elapsed time since test start
44
- * @returns {string} Formatted elapsed time
45
- */
46
- getElapsedTime() {
47
- if (!this.testContext.startTime) return "";
48
- const elapsed = Date.now() - this.testContext.startTime;
49
- const seconds = (elapsed / 1000).toFixed(2);
50
- return `[${seconds}s]`;
51
- }
52
-
53
- /**
54
- * Add elapsed time to parts array if available
55
- * @param {Array} parts - Array to push time string to
56
- * @param {boolean} dim - Whether to dim the time string
57
- */
58
- addTimestamp(parts, dim = true) {
59
- const timeStr = this.getElapsedTime();
60
- if (timeStr) {
61
- parts.push(dim ? chalk.dim(timeStr) : timeStr);
62
- }
63
- }
64
-
65
- /**
66
- * Get color function based on duration and thresholds
67
- * @param {number} durationMs - Duration in milliseconds
68
- * @param {string} thresholdKey - Key from DURATION_THRESHOLDS
69
- * @returns {Function} Chalk color function
70
- */
71
- getDurationColor(durationMs, thresholdKey = "default") {
72
- const thresholds = DURATION_THRESHOLDS[thresholdKey] || DURATION_THRESHOLDS.default;
73
- if (durationMs < thresholds.fast) return chalk.green;
74
- if (durationMs < thresholds.medium) return chalk.yellow;
75
- return chalk.red;
76
- }
77
-
78
- /**
79
- * Format duration with appropriate color
80
- * @param {number|string} duration - Duration in ms
81
- * @param {string} thresholdKey - Key from DURATION_THRESHOLDS
82
- * @param {boolean} showSeconds - Show as seconds (true) or raw (false)
83
- * @returns {string} Formatted duration string
84
- */
85
- formatDurationColored(duration, thresholdKey = "default", showSeconds = true) {
86
- const durationMs = parseInt(duration);
87
- const color = this.getDurationColor(durationMs, thresholdKey);
88
- const display = showSeconds ? `(${(durationMs / 1000).toFixed(1)}s)` : `(${duration})`;
89
- return color(display);
90
- }
91
-
92
- /**
93
- * Join metadata parts with separator
94
- * @param {Array} metaParts - Array of metadata strings
95
- * @returns {string} Joined metadata string with separators
96
- */
97
- joinMetaParts(metaParts) {
98
- if (metaParts.length === 0) return "";
99
- return chalk.dim("·") + " " + metaParts.join(chalk.dim(" · "));
100
- }
101
-
102
- /**
103
- * Create an indented result line prefix (for child results)
104
- * @returns {string} Indented arrow prefix
105
- */
106
- getResultPrefix() {
107
- return " " + chalk.dim("→");
108
- }
109
-
110
- /**
111
- * Format a nested action result line (scrolled, clicked, typed, pressed keys, etc.)
112
- * @param {string} message - The action message (e.g., "scrolled down 300px", "pressed keys: tab")
113
- * @param {number} durationMs - Duration in milliseconds
114
- * @returns {string} Formatted nested result line
115
- */
116
- formatNestedAction(message, durationMs) {
117
- return this.getResultPrefix() + " " + chalk.dim(message) + " " + this.formatDurationColored(durationMs);
118
- }
119
-
120
- /**
121
- * Format a redraw/idle wait completion line
122
- * @param {number} durationMs - Duration in milliseconds
123
- * @returns {string} Formatted redraw complete line
124
- */
125
- formatRedrawComplete(durationMs) {
126
- return this.formatNestedAction("flake protection", durationMs);
127
- }
128
-
129
- /**
130
- * Format a scroll action result
131
- * @param {string} direction - Scroll direction (up, down, left, right)
132
- * @param {number} amount - Scroll amount in pixels
133
- * @param {number} durationMs - Duration in milliseconds
134
- * @returns {string} Formatted scroll result line
135
- */
136
- formatScrollResult(direction, amount, durationMs) {
137
- return this.formatNestedAction(`scrolled ${direction} ${amount}px`, durationMs);
138
- }
139
-
140
- /**
141
- * Format a click action result
142
- * @param {string} button - Button type (left, right, middle)
143
- * @param {number} x - X coordinate
144
- * @param {number} y - Y coordinate
145
- * @param {number} durationMs - Duration in milliseconds
146
- * @returns {string} Formatted click result line
147
- */
148
- formatClickResult(button, x, y, durationMs) {
149
- return this.formatNestedAction(`click ${button} clicking at ${x}, ${y}`, durationMs);
150
- }
151
-
152
- /**
153
- * Format a type action result
154
- * @param {string} text - Text that was typed (or "****" for secrets)
155
- * @param {boolean} isSecret - Whether the text is a secret
156
- * @param {number} durationMs - Duration in milliseconds
157
- * @returns {string} Formatted type result line
158
- */
159
- formatTypeResult(text, isSecret, durationMs) {
160
- const displayText = isSecret ? "secret ****" : `"${text}"`;
161
- return this.formatNestedAction(`typed ${displayText}`, durationMs);
162
- }
163
-
164
- /**
165
- * Format a press keys action result
166
- * @param {string} keysDisplay - Keys that were pressed (comma-separated)
167
- * @param {number} durationMs - Duration in milliseconds
168
- * @returns {string} Formatted press keys result line
169
- */
170
- formatPressKeysResult(keysDisplay, durationMs) {
171
- return this.formatNestedAction(`pressed keys: ${keysDisplay}`, durationMs);
172
- }
173
-
174
- /**
175
- * Format a nested code display line (for exec commands)
176
- * @param {string} codeDisplay - The code to display
177
- * @returns {string} Formatted code line
178
- */
179
- formatCodeLine(codeDisplay) {
180
- return this.getResultPrefix() + " " + chalk.dim(codeDisplay);
181
- }
182
-
183
- /**
184
- * Format an exec complete result
185
- * @param {number} exitCode - The exit code
186
- * @param {number} durationMs - Duration in milliseconds
187
- * @returns {string} Formatted exec result line
188
- */
189
- formatExecComplete(exitCode, durationMs) {
190
- const statusText = exitCode !== 0
191
- ? `failed (exit code ${exitCode})`
192
- : `complete (exit code 0)`;
193
- const statusColor = exitCode !== 0 ? chalk.red : chalk.green;
194
-
195
- return this.formatResultLine(
196
- statusText,
197
- statusColor,
198
- { duration: durationMs },
199
- "action"
200
- );
201
- }
202
-
203
- /**
204
- * Format a log message in Vitest style
205
- * @param {string} type - Log type (info, success, error, action, debug)
206
- * @param {string} message - The message to format
207
- * @param {Object} meta - Additional metadata
208
- * @returns {string} Formatted log message
209
- */
210
- format(type, message, meta = {}) {
211
- this.eventCount++;
212
-
213
- const parts = [];
214
-
215
- // Add timestamp/elapsed time
216
- this.addTimestamp(parts, false);
217
-
218
- // Add type prefix with color
219
- const prefix = this.getPrefix(type);
220
- if (prefix) parts.push(prefix);
221
-
222
- // Add message
223
- parts.push(this.formatMessage(type, message));
224
-
225
- // Add metadata if present
226
- if (meta.duration) {
227
- parts.push(chalk.dim(`(${meta.duration})`));
228
- }
229
-
230
- return parts.join(" ");
231
- }
232
-
233
- /**
234
- * Get prefix for log type with AWESOME colors and emojis 🎨
235
- * @param {string} type - Log type
236
- * @returns {string} Colored prefix with emoji
237
- */
238
- getPrefix(type) {
239
- if (!this.useEmojis) {
240
- // Fallback to simple symbols without emojis
241
- const simplePrefixes = {
242
- info: chalk.blue("ℹ"),
243
- success: chalk.green("✓"),
244
- error: chalk.red("✖"),
245
- action: chalk.cyan("→"),
246
- debug: chalk.gray("⚙"),
247
- find: chalk.magenta("⌕"),
248
- click: chalk.cyan("▸"),
249
- type: chalk.yellow("⌨"),
250
- assert: chalk.green("✓"),
251
- scroll: chalk.blue("↕"),
252
- hover: chalk.cyan("→"),
253
- wait: chalk.yellow("⏱"),
254
- connect: chalk.green("⚡"),
255
- disconnect: chalk.red("⏹"),
256
- };
257
- return simplePrefixes[type] || chalk.gray("•");
258
- }
259
-
260
- const prefixes = {
261
- // Core actions - hand gestures
262
- info: chalk.blue("ℹ️"),
263
- success: chalk.green("✅"),
264
- error: chalk.red("❌"),
265
- warning: chalk.yellow("⚠️"),
266
-
267
- // Finding elements
268
- find: chalk.magenta("🔍"),
269
- findAll: chalk.magenta("🔎"),
270
-
271
- // Mouse actions
272
- click: chalk.cyan("👆"),
273
- doubleClick: chalk.cyan("👆👆"),
274
- rightClick: chalk.cyan("🖱️"),
275
- hover: chalk.cyan("👉"),
276
- drag: chalk.cyan("✊"),
277
-
278
- // Keyboard actions
279
- type: chalk.yellow("⌨️ "),
280
- pressKeys: chalk.yellow("🎹"),
281
-
282
- // Navigation
283
- scroll: chalk.blue("📜"),
284
- scrollUp: chalk.blue("⬆️"),
285
- scrollDown: chalk.blue("⬇️"),
286
- navigate: chalk.blue("🧭"),
287
-
288
- // Validation
289
- assert: chalk.green("✅"),
290
- verify: chalk.green("🔍"),
291
- extract: chalk.blue("🧠"),
292
-
293
- // System
294
- connect: chalk.green("🔌"),
295
- disconnect: chalk.red("🔌"),
296
- screenshot: chalk.blue("📸"),
297
- wait: chalk.yellow("⏳"),
298
-
299
- // Focus & Windows
300
- focusApplication: chalk.cyan("🎯"),
301
-
302
- // Cache
303
- cacheHit: chalk.yellow("⚡"),
304
- cacheMiss: chalk.gray("💤"),
305
-
306
- // Debug
307
- debug: chalk.gray("🔧"),
308
-
309
- // Default
310
- action: chalk.cyan("▶️ "),
311
- };
312
- return prefixes[type] || chalk.gray("•");
313
- }
314
-
315
- /**
316
- * Format the message content with appropriate styling
317
- * @param {string} type - Log type
318
- * @param {string} message - Raw message
319
- * @returns {string} Formatted message
320
- */
321
- formatMessage(type, message) {
322
- if (!this.useColors) return message;
323
-
324
- const formatters = {
325
- success: (msg) => chalk.green(msg),
326
- error: (msg) => chalk.red(msg),
327
- debug: (msg) => chalk.dim(msg),
328
- };
329
-
330
- return formatters[type] ? formatters[type](message) : message;
331
- }
332
-
333
- /**
334
- * Format a "finding" style message (when search starts) 🔍
335
- * @param {string} prefixType - Prefix type for getPrefix
336
- * @param {string} label - Action label (e.g., "Finding", "Finding All", "Asserting")
337
- * @param {string} description - Element/assertion description
338
- * @returns {string} Formatted message
339
- */
340
- formatFindingStyle(prefixType, label, description) {
341
- const parts = [];
342
- this.addTimestamp(parts);
343
- parts.push(this.getPrefix(prefixType));
344
- parts.push(chalk.bold.cyan(label));
345
- parts.push(chalk.cyan(`"${description}"`));
346
- return parts.join(" ");
347
- }
348
-
349
- /**
350
- * Format an element finding message (when search starts) 🔍
351
- * @param {string} description - Element description
352
- * @returns {string} Formatted message
353
- */
354
- formatElementFinding(description) {
355
- return this.formatFindingStyle("find", "Finding", description);
356
- }
357
-
358
- /**
359
- * Build common metadata parts for result messages
360
- * @param {Object} meta - Metadata object
361
- * @param {string} thresholdKey - Duration threshold key
362
- * @returns {Array} Array of formatted metadata strings
363
- */
364
- buildResultMetaParts(meta, thresholdKey = "default") {
365
- const metaParts = [];
366
-
367
- if (meta.x !== undefined && meta.y !== undefined) {
368
- metaParts.push(chalk.dim.gray(`📍 (${meta.x}, ${meta.y})`));
369
- }
370
- if (meta.selectorId && meta.consoleUrl) {
371
- const cacheUrl = `${meta.consoleUrl}/cache/${meta.selectorId}`;
372
- metaParts.push(chalk.blue.underline(cacheUrl));
373
- }
374
- if (meta.error) {
375
- metaParts.push(chalk.dim.red(meta.error));
376
- }
377
- if (meta.cacheHit) {
378
- metaParts.push(chalk.bold.yellow("⚡ cached"));
379
- }
380
- // Duration always last
381
- if (meta.duration) {
382
- metaParts.push(this.formatDurationColored(meta.duration, thresholdKey));
383
- }
384
-
385
- return metaParts;
386
- }
387
-
388
- /**
389
- * Format a result line (indented child result)
390
- * @param {string} statusText - Status text (e.g., "found", "not found")
391
- * @param {Function} statusColor - Chalk color function for status
392
- * @param {Object} meta - Metadata object
393
- * @param {string} thresholdKey - Duration threshold key
394
- * @returns {string} Formatted result line
395
- */
396
- formatResultLine(statusText, statusColor, meta = {}, thresholdKey = "default") {
397
- const parts = [];
398
- this.addTimestamp(parts);
399
- parts.push(this.getResultPrefix());
400
- parts.push(statusColor(statusText));
401
-
402
- const metaParts = this.buildResultMetaParts(meta, thresholdKey);
403
- if (metaParts.length > 0) {
404
- parts.push(this.joinMetaParts(metaParts));
405
- }
406
-
407
- return parts.join(" ");
408
- }
409
-
410
- /**
411
- * Format an element found message with AWESOME styling 🎯
412
- * @param {string} description - Element description
413
- * @param {Object} meta - Element metadata (coordinates, duration, cache hit)
414
- * @returns {string} Formatted message
415
- */
416
- formatElementFound(description, meta = {}) {
417
- return this.formatResultLine("found", chalk.green, meta);
418
- }
419
-
420
- /**
421
- * Format an element not found message with styling ❌
422
- * @param {string} description - Element description
423
- * @param {Object} meta - Metadata (duration, error)
424
- * @returns {string} Formatted message
425
- */
426
- formatElementNotFound(description, meta = {}) {
427
- return this.formatResultLine("not found", chalk.red, meta);
428
- }
429
-
430
- /**
431
- * Format a finding all message (when search starts) 🔎
432
- * @param {string} description - Element description
433
- * @returns {string} Formatted message
434
- */
435
- formatElementsFinding(description) {
436
- return this.formatFindingStyle("findAll", "Finding All", description);
437
- }
438
-
439
- /**
440
- * Format a found all message with AWESOME styling 🎯
441
- * @param {string} description - Element description
442
- * @param {number} count - Number of elements found
443
- * @param {Object} meta - Metadata (duration, cache hit)
444
- * @returns {string} Formatted message
445
- */
446
- formatElementsFound(description, count, meta = {}) {
447
- const parts = [];
448
- this.addTimestamp(parts);
449
- parts.push(this.getResultPrefix());
450
- parts.push(chalk.green(`found ${count} elements`));
451
-
452
- const metaParts = [];
453
- if (meta.cacheHit) {
454
- metaParts.push(chalk.bold.yellow("⚡ cached"));
455
- }
456
- if (meta.duration) {
457
- metaParts.push(this.formatDurationColored(meta.duration));
458
- }
459
-
460
- if (metaParts.length > 0) {
461
- parts.push(this.joinMetaParts(metaParts));
462
- }
463
-
464
- return parts.join(" ");
465
- }
466
-
467
- /**
468
- * Format an asserting message (when assertion starts) ✓
469
- * @param {string} assertion - What is being asserted
470
- * @returns {string} Formatted message
471
- */
472
- formatAsserting(assertion) {
473
- return this.formatFindingStyle("assert", "Asserting", assertion);
474
- }
475
- /**
476
- * Format the assertion result as a subtask line
477
- * @param {boolean} passed - Whether assertion passed
478
- * @param {string} response - The AI response message
479
- * @param {number} durationMs - Duration in milliseconds
480
- * @returns {string} Formatted result line
481
- */
482
- formatAssertResult(passed, response, durationMs) {
483
- const parts = [];
484
- this.addTimestamp(parts);
485
- parts.push(this.getResultPrefix());
486
-
487
- if (passed) {
488
- parts.push(chalk.green("passed"));
489
- } else {
490
- parts.push(chalk.red("failed"));
491
- }
492
-
493
- // Add the response message (trimmed)
494
- if (response) {
495
- const trimmedResponse = response.trim().split('\n')[0]; // First line only
496
- parts.push(chalk.dim(trimmedResponse));
497
- }
498
-
499
- // Add duration
500
- if (durationMs) {
501
- parts.push(this.formatDurationColored(durationMs, "action"));
502
- }
503
-
504
- return parts.join(" ");
505
- }
506
-
507
- // Action color mapping (shared between formatAction and formatActionComplete)
508
- static ACTION_COLORS = {
509
- click: chalk.bold.cyan,
510
- hover: chalk.bold.blue,
511
- type: chalk.bold.yellow,
512
- scroll: chalk.bold.magenta,
513
- assert: chalk.bold.green,
514
- wait: chalk.bold.yellow,
515
- };
516
-
517
- /**
518
- * Build action message parts (shared logic for formatAction and formatActionComplete)
519
- * @param {string} action - Action type
520
- * @param {string} description - Description or target
521
- * @returns {Array} Array of formatted parts
522
- */
523
- buildActionParts(action, description) {
524
- const parts = [];
525
- this.addTimestamp(parts);
526
-
527
- const actionKey = action.toLowerCase().replace(/\s+/g, "");
528
- parts.push(this.getPrefix(actionKey));
529
-
530
- const actionText = action.charAt(0).toUpperCase() + action.slice(1).toLowerCase();
531
- const colorFn = SDKLogFormatter.ACTION_COLORS[actionKey] || chalk.bold.white;
532
- parts.push(colorFn(actionText));
533
-
534
- if (description) {
535
- parts.push(chalk.cyan(`"${description}"`));
536
- }
537
-
538
- return { parts, actionKey };
539
- }
540
-
541
- /**
542
- * Format an action message with AWESOME emojis! 🎬
543
- * @param {string} action - Action type
544
- * @param {string} description - Description or target
545
- * @param {Object} meta - Action metadata
546
- * @returns {string} Formatted message
547
- */
548
- formatAction(action, description, meta = {}) {
549
- const { parts } = this.buildActionParts(action, description);
550
-
551
- const metaParts = [];
552
- if (meta.text) {
553
- metaParts.push(chalk.gray(`→ ${chalk.white(meta.text)}`));
554
- }
555
- if (meta.duration) {
556
- metaParts.push(chalk.dim(`⏱️ ${this.formatDurationColored(meta.duration, "quickAction", false)}`));
557
- }
558
-
559
- if (metaParts.length > 0) {
560
- parts.push(this.joinMetaParts(metaParts));
561
- }
562
-
563
- return parts.join(" ");
564
- }
565
-
566
- /**
567
- * Format an action complete message with separate action and redraw durations 🎬
568
- * @param {string} action - Action type
569
- * @param {string} description - Description or target
570
- * @param {Object} meta - Action metadata
571
- * @param {number} meta.actionDuration - Duration of the action itself in ms
572
- * @param {number} meta.redrawDuration - Duration of the redraw wait in ms
573
- * @param {boolean} meta.cacheHit - Whether cache was hit
574
- * @returns {string} Formatted message
575
- */
576
- formatActionComplete(action, description, meta = {}) {
577
- const { parts } = this.buildActionParts(action, description);
578
-
579
- const metaParts = [];
580
-
581
- if (meta.actionDuration !== undefined) {
582
- const durationMs = parseInt(meta.actionDuration);
583
- const durationSec = (durationMs / 1000).toFixed(1) + 's';
584
- const color = this.getDurationColor(durationMs, "action");
585
- metaParts.push(chalk.dim(`⚡ ${color(durationSec)}`));
586
- }
587
-
588
- if (meta.redrawDuration !== undefined) {
589
- const durationMs = parseInt(meta.redrawDuration);
590
- const durationSec = (durationMs / 1000).toFixed(1) + 's';
591
- const color = this.getDurationColor(durationMs, "redraw");
592
- metaParts.push(chalk.dim(`🔄 ${color(durationSec)}`));
593
- }
594
-
595
- if (meta.cacheHit) {
596
- metaParts.push(chalk.bold.yellow("⚡ cached"));
597
- }
598
-
599
- if (metaParts.length > 0) {
600
- parts.push(this.joinMetaParts(metaParts));
601
- }
602
-
603
- return parts.join(" ");
604
- }
605
-
606
- /**
607
- * Format an assertion message with beautiful status indicators 🎯
608
- * @param {string} assertion - What is being asserted
609
- * @param {boolean} passed - Whether assertion passed
610
- * @param {Object} meta - Assertion metadata
611
- * @returns {string} Formatted message
612
- */
613
- formatAssertion(assertion, passed, meta = {}) {
614
- const parts = [];
615
- this.addTimestamp(parts);
616
-
617
- if (passed) {
618
- parts.push(this.getPrefix("success"));
619
- parts.push(chalk.bold.green("Assert"));
620
- parts.push(chalk.cyan(`"${assertion}"`));
621
- parts.push(chalk.dim("·"));
622
- parts.push(chalk.bold.green("✓ PASSED"));
623
- } else {
624
- parts.push(this.getPrefix("error"));
625
- parts.push(chalk.bold.red("Assert"));
626
- parts.push(chalk.cyan(`"${assertion}"`));
627
- parts.push(chalk.dim("·"));
628
- parts.push(chalk.bold.red("✗ FAILED"));
629
- }
630
-
631
- if (meta.duration) {
632
- parts.push(chalk.dim("·"));
633
- parts.push(chalk.dim(`⏱️ ${this.formatDurationColored(meta.duration, "action", false)}`));
634
- }
635
-
636
- return parts.join(" ");
637
- }
638
-
639
- /**
640
- * Format an error message with clear visual indicators 🚨
641
- * @param {string} message - Error message
642
- * @param {Error} error - Error object
643
- * @returns {string} Formatted error
644
- */
645
- formatError(message, error) {
646
- const parts = [];
647
- this.addTimestamp(parts);
648
- parts.push(this.getPrefix("error"));
649
- parts.push(chalk.red.bold(message));
650
-
651
- if (error && error.message) {
652
- parts.push(chalk.dim("→"));
653
- parts.push(chalk.red(error.message));
654
- }
655
-
656
- return parts.join(" ");
657
- }
658
-
659
- /**
660
- * Format a connection/disconnection message 🔌
661
- * @param {string} type - 'connect' or 'disconnect'
662
- * @param {Object} meta - Connection metadata
663
- * @returns {string} Formatted message
664
- */
665
- formatConnection(type, meta = {}) {
666
- const parts = [];
667
- this.addTimestamp(parts);
668
- parts.push(this.getPrefix(type));
669
-
670
- if (type === "connect") {
671
- parts.push(chalk.bold.green("Connected"));
672
- if (meta.sandboxId) {
673
- parts.push(chalk.dim("·"));
674
- parts.push(chalk.cyan(`Sandbox: ${meta.sandboxId}`));
675
- }
676
- if (meta.os) {
677
- parts.push(chalk.dim("·"));
678
- parts.push(chalk.gray(`OS: ${meta.os}`));
679
- }
680
- } else {
681
- parts.push(chalk.bold.yellow("Disconnected"));
682
- }
683
-
684
- return parts.join(" ");
685
- }
686
-
687
- /**
688
- * Format a screenshot message 📸
689
- * @param {Object} meta - Screenshot metadata
690
- * @returns {string} Formatted message
691
- */
692
- formatScreenshot(meta = {}) {
693
- const parts = [];
694
- this.addTimestamp(parts);
695
- parts.push(this.getPrefix("screenshot"));
696
- parts.push(chalk.bold.blue("Screenshot"));
697
-
698
- if (meta.path) {
699
- parts.push(chalk.dim("·"));
700
- parts.push(chalk.cyan(meta.path));
701
- }
702
-
703
- if (meta.size) {
704
- parts.push(chalk.dim("·"));
705
- parts.push(chalk.gray(`${meta.size}`));
706
- }
707
-
708
- return parts.join(" ");
709
- }
710
-
711
- /**
712
- * Format a cache status message ⚡
713
- * @param {boolean} hit - Whether it was a cache hit
714
- * @param {Object} meta - Cache metadata
715
- * @returns {string} Formatted message
716
- */
717
- formatCacheStatus(hit, meta = {}) {
718
- const parts = [];
719
- parts.push(this.getPrefix(hit ? "cacheHit" : "cacheMiss"));
720
-
721
- if (hit) {
722
- parts.push(chalk.bold.yellow("Cache HIT"));
723
- if (meta.similarity !== undefined) {
724
- const similarity = (meta.similarity * 100).toFixed(1);
725
- parts.push(chalk.dim("·"));
726
- parts.push(chalk.green(`${similarity}% similar`));
727
- }
728
- } else {
729
- parts.push(chalk.dim.gray("Cache MISS"));
730
- }
731
-
732
- if (meta.strategy) {
733
- parts.push(chalk.dim("·"));
734
- parts.push(chalk.gray(meta.strategy));
735
- }
736
-
737
- return parts.join(" ");
738
- }
739
-
740
- /**
741
- * Create a beautiful section header with box drawing 📦
742
- * @param {string} title - Section title
743
- * @param {string} emoji - Optional emoji to prefix
744
- * @returns {string} Formatted header
745
- */
746
- formatHeader(title, emoji = "✨") {
747
- const width = Math.min(60, Math.max(title.length + 4, 40));
748
- const topLine = chalk.dim("╭" + "─".repeat(width - 2) + "╮");
749
- const titleLine =
750
- `${chalk.dim("│")} ${emoji} ${chalk.bold.white(title)}`.padEnd(width + 20) + chalk.dim("│");
751
- const bottomLine = chalk.dim("╰" + "─".repeat(width - 2) + "╯");
752
- return `\n${topLine}\n${titleLine}\n${bottomLine}\n`;
753
- }
754
-
755
- /**
756
- * Format a simple divider
757
- * @param {string} char - Character to use for divider
758
- * @returns {string} Formatted divider
759
- */
760
- formatDivider(char = "─") {
761
- return chalk.dim(char.repeat(60));
762
- }
763
-
764
- /**
765
- * Format a beautiful summary line with stats 📊
766
- * @param {Object} stats - Test statistics
767
- * @returns {string} Formatted summary
768
- */
769
- formatSummary(stats) {
770
- const parts = [];
771
-
772
- if (stats.passed > 0) {
773
- parts.push(chalk.bold.green(`✓ ${stats.passed} passed`));
774
- }
775
- if (stats.failed > 0) {
776
- parts.push(chalk.bold.red(`✗ ${stats.failed} failed`));
777
- }
778
- if (stats.skipped > 0) {
779
- parts.push(chalk.yellow(`⊘ ${stats.skipped} skipped`));
780
- }
781
- if (stats.total > 0) {
782
- parts.push(chalk.dim(`${stats.total} total`));
783
- }
784
- if (stats.duration) {
785
- parts.push(chalk.dim(`⏱️ ${stats.duration}`));
786
- }
787
-
788
- const divider = this.formatDivider();
789
- const separator = chalk.dim(" │ ");
790
- return `\n${divider}\n${parts.join(separator)}\n${divider}\n`;
791
- }
792
-
793
- /**
794
- * Format a progress indicator 📈
795
- * @param {number} current - Current step
796
- * @param {number} total - Total steps
797
- * @param {string} message - Progress message
798
- * @returns {string} Formatted progress
799
- */
800
- formatProgress(current, total, message = "") {
801
- const percentage = Math.round((current / total) * 100);
802
- const barWidth = 20;
803
- const filled = Math.round((current / total) * barWidth);
804
- const empty = barWidth - filled;
805
-
806
- const bar = chalk.green("█".repeat(filled)) + chalk.dim("░".repeat(empty));
807
- const stats = chalk.dim(`${current}/${total}`);
808
-
809
- const parts = [
810
- chalk.bold("Progress"),
811
- bar,
812
- chalk.cyan(`${percentage}%`),
813
- stats,
814
- ];
815
-
816
- if (message) {
817
- parts.push(chalk.dim("·"));
818
- parts.push(chalk.gray(message));
819
- }
820
-
821
- return parts.join(" ");
822
- }
823
-
824
- /**
825
- * Format a waiting/loading message ⏳
826
- * @param {string} message - What we're waiting for
827
- * @param {number} elapsed - Elapsed time in ms
828
- * @returns {string} Formatted waiting message
829
- */
830
- formatWaiting(message, elapsed) {
831
- const parts = [];
832
- parts.push(this.getPrefix("wait"));
833
- parts.push(chalk.bold.yellow("Waiting"));
834
- parts.push(chalk.cyan(message));
835
-
836
- if (elapsed) {
837
- const seconds = (elapsed / 1000).toFixed(1);
838
- parts.push(chalk.dim("·"));
839
- parts.push(chalk.gray(`${seconds}s`));
840
- }
841
-
842
- return parts.join(" ");
843
- }
844
-
845
- /**
846
- * Format test start message 🚀
847
- * @param {string} testName - Name of the test
848
- * @returns {string} Formatted test start
849
- */
850
- formatTestStart(testName) {
851
- return `\n${chalk.bold.cyan("▶️ Running:")} ${chalk.white(testName)}\n`;
852
- }
853
-
854
- /**
855
- * Format test end message with result 🏁
856
- * @param {string} testName - Name of the test
857
- * @param {boolean} passed - Whether test passed
858
- * @param {number} duration - Test duration in ms
859
- * @returns {string} Formatted test end
860
- */
861
- formatTestEnd(testName, passed, duration) {
862
- const parts = [];
863
-
864
- if (passed) {
865
- parts.push(chalk.bold.green("✅ PASSED"));
866
- } else {
867
- parts.push(chalk.bold.red("❌ FAILED"));
868
- }
869
-
870
- parts.push(chalk.white(testName));
871
-
872
- if (duration) {
873
- const seconds = (duration / 1000).toFixed(2);
874
- const color = this.getDurationColor(duration, "test");
875
- parts.push(chalk.dim("·"));
876
- parts.push(color(`${seconds}s`));
877
- }
878
-
879
- return `\n${parts.join(" ")}\n`;
880
- }
881
-
882
- /**
883
- * Format ai() start message - provides visual scope boundary
884
- * @param {string} task - The task being executed
885
- * @returns {string} Formatted ai start message
886
- */
887
- formatAIStart(task) {
888
- const parts = [];
889
- this.addTimestamp(parts);
890
- parts.push(this.getPrefix("action"));
891
- parts.push(chalk.bold.cyan("AI"));
892
- parts.push(chalk.cyan(`"${task}"`));
893
- return parts.join(" ");
894
- }
895
-
896
- /**
897
- * Format ai() completion message - provides visual scope boundary
898
- * @param {number} durationMs - Duration in milliseconds
899
- * @param {boolean} success - Whether the ai completed successfully
900
- * @param {string} [error] - Error message if failed
901
- * @returns {string} Formatted ai complete message
902
- */
903
- formatAIComplete(durationMs, success, error = null) {
904
- const parts = [];
905
- this.addTimestamp(parts);
906
- parts.push(this.getResultPrefix());
907
-
908
- if (success) {
909
- parts.push(chalk.green("complete"));
910
- } else {
911
- parts.push(chalk.red("failed"));
912
- if (error) {
913
- parts.push(chalk.dim("·"));
914
- parts.push(chalk.red(error));
915
- }
916
- }
917
-
918
- parts.push(this.formatDurationColored(durationMs, "default"));
919
-
920
- return parts.join(" ");
921
- }
922
- }
923
-
924
- // Export singleton instance
925
- const formatter = new SDKLogFormatter();
926
-
927
- module.exports = {
928
- SDKLogFormatter,
929
- formatter,
930
- };