testdriverai 7.0.0 → 7.1.1

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 (324) hide show
  1. package/.env.example +2 -0
  2. package/.github/workflows/linux-tests.yml +28 -0
  3. package/README.md +126 -0
  4. package/agent/index.js +7 -9
  5. package/agent/interface.js +13 -2
  6. package/agent/lib/commands.js +795 -136
  7. package/agent/lib/redraw.js +124 -39
  8. package/agent/lib/sandbox.js +40 -3
  9. package/agent/lib/sdk.js +21 -0
  10. package/agent/lib/valid-version.js +2 -2
  11. package/debugger/index.html +1 -1
  12. package/docs/docs.json +86 -71
  13. package/docs/guide/best-practices-polling.mdx +154 -0
  14. package/docs/v6/getting-started/self-hosting.mdx +3 -2
  15. package/docs/v7/_drafts/agents.mdx +852 -0
  16. package/docs/v7/_drafts/auto-cache-key.mdx +167 -0
  17. package/docs/v7/_drafts/best-practices.mdx +486 -0
  18. package/docs/v7/_drafts/caching-ai.mdx +215 -0
  19. package/docs/v7/_drafts/caching-selectors.mdx +400 -0
  20. package/docs/v7/_drafts/caching.mdx +366 -0
  21. package/docs/v7/_drafts/cli-to-sdk-migration.mdx +425 -0
  22. package/docs/v7/_drafts/core.mdx +459 -0
  23. package/docs/v7/_drafts/dashcam-title-feature.mdx +89 -0
  24. package/docs/v7/_drafts/debugging.mdx +349 -0
  25. package/docs/v7/_drafts/error-handling.mdx +501 -0
  26. package/docs/v7/_drafts/faq.mdx +393 -0
  27. package/docs/v7/_drafts/hooks.mdx +360 -0
  28. package/docs/v7/_drafts/implementation-plan.mdx +994 -0
  29. package/docs/v7/_drafts/init-command.mdx +95 -0
  30. package/docs/v7/_drafts/optimal-sdk-design.mdx +1348 -0
  31. package/docs/v7/_drafts/performance.mdx +517 -0
  32. package/docs/v7/_drafts/presets.mdx +210 -0
  33. package/docs/v7/_drafts/progressive-disclosure.mdx +230 -0
  34. package/docs/v7/_drafts/provision.mdx +266 -0
  35. package/docs/{QUICK_START_TEST_RECORDING.md → v7/_drafts/quick-start-test-recording.mdx} +3 -3
  36. package/docs/v7/_drafts/sdk-v7-complete.mdx +345 -0
  37. package/docs/v7/{guides → _drafts}/self-hosting.mdx +1 -1
  38. package/docs/v7/_drafts/troubleshooting.mdx +526 -0
  39. package/docs/v7/_drafts/vitest-plugin.mdx +477 -0
  40. package/docs/v7/_drafts/vitest.mdx +535 -0
  41. package/docs/v7/api/{ai.mdx → act.mdx} +24 -24
  42. package/docs/v7/api/client.mdx +1 -1
  43. package/docs/v7/api/dashcam.mdx +497 -0
  44. package/docs/v7/api/doubleClick.mdx +102 -0
  45. package/docs/v7/api/elements.mdx +143 -41
  46. package/docs/v7/api/find.mdx +258 -0
  47. package/docs/v7/api/mouseDown.mdx +161 -0
  48. package/docs/v7/api/mouseUp.mdx +164 -0
  49. package/docs/v7/api/rightClick.mdx +123 -0
  50. package/docs/v7/api/type.mdx +51 -7
  51. package/docs/v7/features/ai-native.mdx +427 -0
  52. package/docs/v7/features/easy-to-write.mdx +351 -0
  53. package/docs/v7/features/enterprise.mdx +540 -0
  54. package/docs/v7/features/fast.mdx +424 -0
  55. package/docs/v7/features/observable.mdx +623 -0
  56. package/docs/v7/features/powerful.mdx +531 -0
  57. package/docs/v7/features/scalable.mdx +417 -0
  58. package/docs/v7/features/stable.mdx +514 -0
  59. package/docs/v7/getting-started/configuration.mdx +380 -0
  60. package/docs/v7/getting-started/generating-tests.mdx +525 -0
  61. package/docs/v7/getting-started/installation.mdx +486 -0
  62. package/docs/v7/getting-started/quickstart.mdx +320 -141
  63. package/docs/v7/getting-started/running-and-debugging.mdx +511 -0
  64. package/docs/v7/getting-started/setting-up-in-ci.mdx +612 -0
  65. package/docs/v7/getting-started/writing-tests.mdx +535 -0
  66. package/docs/v7/overview/what-is-testdriver.mdx +398 -0
  67. package/docs/v7/platforms/linux.mdx +308 -0
  68. package/docs/v7/platforms/macos.mdx +433 -0
  69. package/docs/v7/platforms/windows.mdx +430 -0
  70. package/docs/v7/playwright.mdx +3 -3
  71. package/docs/v7/presets/chrome-extension.mdx +223 -0
  72. package/docs/v7/presets/chrome.mdx +303 -0
  73. package/docs/v7/presets/electron.mdx +453 -0
  74. package/docs/v7/presets/vscode.mdx +417 -0
  75. package/docs/v7/presets/webapp.mdx +396 -0
  76. package/examples/run-tests-with-recording.sh +2 -2
  77. package/interfaces/cli/commands/init.js +358 -0
  78. package/interfaces/vitest-plugin.mjs +393 -103
  79. package/lib/core/Dashcam.js +506 -0
  80. package/lib/core/index.d.ts +150 -0
  81. package/lib/core/index.js +12 -0
  82. package/lib/presets/index.mjs +331 -0
  83. package/lib/vitest/hooks.d.ts +119 -0
  84. package/lib/vitest/hooks.mjs +316 -0
  85. package/lib/vitest/setup.mjs +44 -0
  86. package/package.json +13 -3
  87. package/sdk.d.ts +350 -44
  88. package/sdk.js +818 -105
  89. package/{self-hosted.yml → setup/aws/self-hosted.yml} +1 -1
  90. package/test/manual/test-console-logs.test.mjs +42 -0
  91. package/test/manual/test-init.sh +54 -0
  92. package/test/manual/test-provision-auth.mjs +22 -0
  93. package/test/testdriver/assert.test.mjs +41 -0
  94. package/test/testdriver/auto-cache-key-demo.test.mjs +56 -0
  95. package/test/testdriver/chrome-extension.test.mjs +89 -0
  96. package/{testdriver/acceptance-sdk → test/testdriver}/drag-and-drop.test.mjs +7 -19
  97. package/{testdriver/acceptance-sdk → test/testdriver}/element-not-found.test.mjs +6 -19
  98. package/{testdriver/acceptance-sdk → test/testdriver}/exec-js.test.mjs +6 -18
  99. package/{testdriver/acceptance-sdk → test/testdriver}/exec-output.test.mjs +9 -21
  100. package/{testdriver/acceptance-sdk → test/testdriver}/exec-pwsh.test.mjs +14 -26
  101. package/{testdriver/acceptance-sdk → test/testdriver}/focus-window.test.mjs +8 -20
  102. package/{testdriver/acceptance-sdk → test/testdriver}/formatted-logging.test.mjs +5 -20
  103. package/{testdriver/acceptance-sdk → test/testdriver}/hover-image.test.mjs +10 -19
  104. package/{testdriver/acceptance-sdk → test/testdriver}/hover-text-with-description.test.mjs +7 -19
  105. package/{testdriver/acceptance-sdk → test/testdriver}/hover-text.test.mjs +5 -19
  106. package/{testdriver/acceptance-sdk → test/testdriver}/match-image.test.mjs +7 -19
  107. package/{testdriver/acceptance-sdk → test/testdriver}/press-keys.test.mjs +5 -19
  108. package/{testdriver/acceptance-sdk → test/testdriver}/prompt.test.mjs +7 -19
  109. package/{testdriver/acceptance-sdk → test/testdriver}/scroll-keyboard.test.mjs +6 -20
  110. package/{testdriver/acceptance-sdk → test/testdriver}/scroll-until-image.test.mjs +6 -18
  111. package/test/testdriver/scroll-until-text.test.mjs +28 -0
  112. package/{testdriver/acceptance-sdk → test/testdriver}/scroll.test.mjs +12 -21
  113. package/test/testdriver/setup/lifecycleHelpers.mjs +262 -0
  114. package/{testdriver/acceptance-sdk → test/testdriver}/setup/testHelpers.mjs +25 -20
  115. package/test/testdriver/type.test.mjs +45 -0
  116. package/vitest.config.mjs +11 -56
  117. package/.github/dependabot.yml +0 -11
  118. package/.github/workflows/acceptance-linux.yml +0 -75
  119. package/.github/workflows/acceptance-sdk-tests.yml +0 -133
  120. package/.github/workflows/acceptance-tests.yml +0 -130
  121. package/.github/workflows/lint.yml +0 -27
  122. package/.github/workflows/publish-canary.yml +0 -40
  123. package/.github/workflows/publish-latest.yml +0 -61
  124. package/.github/workflows/test-install.yml +0 -29
  125. package/.vscode/extensions.json +0 -3
  126. package/.vscode/launch.json +0 -22
  127. package/.vscode/mcp.json +0 -9
  128. package/.vscode/settings.json +0 -14
  129. package/CODEOWNERS +0 -3
  130. package/MIGRATION.md +0 -389
  131. package/SDK_README.md +0 -1122
  132. package/_testdriver/acceptance/assert.yaml +0 -7
  133. package/_testdriver/acceptance/dashcam.yaml +0 -9
  134. package/_testdriver/acceptance/drag-and-drop.yaml +0 -49
  135. package/_testdriver/acceptance/embed.yaml +0 -9
  136. package/_testdriver/acceptance/exec-js.yaml +0 -29
  137. package/_testdriver/acceptance/exec-output.yaml +0 -43
  138. package/_testdriver/acceptance/exec-shell.yaml +0 -40
  139. package/_testdriver/acceptance/focus-window.yaml +0 -16
  140. package/_testdriver/acceptance/hover-image.yaml +0 -18
  141. package/_testdriver/acceptance/hover-text-with-description.yaml +0 -29
  142. package/_testdriver/acceptance/hover-text.yaml +0 -14
  143. package/_testdriver/acceptance/if-else.yaml +0 -31
  144. package/_testdriver/acceptance/match-image.yaml +0 -15
  145. package/_testdriver/acceptance/press-keys.yaml +0 -35
  146. package/_testdriver/acceptance/prompt.yaml +0 -11
  147. package/_testdriver/acceptance/remember.yaml +0 -27
  148. package/_testdriver/acceptance/screenshots/cart.png +0 -0
  149. package/_testdriver/acceptance/scroll-keyboard.yaml +0 -34
  150. package/_testdriver/acceptance/scroll-until-image.yaml +0 -26
  151. package/_testdriver/acceptance/scroll-until-text.yaml +0 -20
  152. package/_testdriver/acceptance/scroll.yaml +0 -33
  153. package/_testdriver/acceptance/snippets/login.yaml +0 -29
  154. package/_testdriver/acceptance/snippets/match-cart.yaml +0 -8
  155. package/_testdriver/acceptance/type.yaml +0 -29
  156. package/_testdriver/behavior/failure.yaml +0 -7
  157. package/_testdriver/behavior/hover-text.yaml +0 -13
  158. package/_testdriver/behavior/lifecycle/postrun.yaml +0 -10
  159. package/_testdriver/behavior/lifecycle/prerun.yaml +0 -8
  160. package/_testdriver/behavior/lifecycle/provision.yaml +0 -8
  161. package/_testdriver/behavior/secrets.yaml +0 -7
  162. package/_testdriver/edge-cases/dashcam-chrome.yaml +0 -8
  163. package/_testdriver/edge-cases/exec-pwsh-multiline.yaml +0 -10
  164. package/_testdriver/edge-cases/js-exception.yaml +0 -8
  165. package/_testdriver/edge-cases/js-promise.yaml +0 -19
  166. package/_testdriver/edge-cases/lifecycle/postrun.yaml +0 -10
  167. package/_testdriver/edge-cases/prompt-in-middle.yaml +0 -23
  168. package/_testdriver/edge-cases/prompt-nested.yaml +0 -7
  169. package/_testdriver/edge-cases/success-test.yaml +0 -9
  170. package/_testdriver/examples/android/example.yaml +0 -12
  171. package/_testdriver/examples/android/lifecycle/postrun.yaml +0 -11
  172. package/_testdriver/examples/android/lifecycle/provision.yaml +0 -47
  173. package/_testdriver/examples/android/readme.md +0 -7
  174. package/_testdriver/examples/chrome-extension/lifecycle/provision.yaml +0 -74
  175. package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
  176. package/_testdriver/examples/desktop/lifecycle/provision.yaml +0 -64
  177. package/_testdriver/examples/vscode-extension/lifecycle/provision.yaml +0 -73
  178. package/_testdriver/examples/web/lifecycle/postrun.yaml +0 -7
  179. package/_testdriver/examples/web/lifecycle/prerun.yaml +0 -22
  180. package/_testdriver/lifecycle/postrun.yaml +0 -8
  181. package/_testdriver/lifecycle/prerun.yaml +0 -15
  182. package/_testdriver/lifecycle/provision.yaml +0 -25
  183. package/debug-screenshot-1763401388589.png +0 -0
  184. package/mcp-server/AI_GUIDELINES.md +0 -57
  185. package/scripts/view-test-results.mjs +0 -96
  186. package/styles/.vale-config/2-MDX.ini +0 -5
  187. package/styles/Microsoft/AMPM.yml +0 -9
  188. package/styles/Microsoft/Accessibility.yml +0 -30
  189. package/styles/Microsoft/Acronyms.yml +0 -64
  190. package/styles/Microsoft/Adverbs.yml +0 -272
  191. package/styles/Microsoft/Auto.yml +0 -11
  192. package/styles/Microsoft/Avoid.yml +0 -14
  193. package/styles/Microsoft/Contractions.yml +0 -50
  194. package/styles/Microsoft/Dashes.yml +0 -13
  195. package/styles/Microsoft/DateFormat.yml +0 -8
  196. package/styles/Microsoft/DateNumbers.yml +0 -40
  197. package/styles/Microsoft/DateOrder.yml +0 -8
  198. package/styles/Microsoft/Ellipses.yml +0 -9
  199. package/styles/Microsoft/FirstPerson.yml +0 -16
  200. package/styles/Microsoft/Foreign.yml +0 -13
  201. package/styles/Microsoft/Gender.yml +0 -8
  202. package/styles/Microsoft/GenderBias.yml +0 -42
  203. package/styles/Microsoft/GeneralURL.yml +0 -11
  204. package/styles/Microsoft/HeadingAcronyms.yml +0 -7
  205. package/styles/Microsoft/HeadingColons.yml +0 -8
  206. package/styles/Microsoft/HeadingPunctuation.yml +0 -13
  207. package/styles/Microsoft/Headings.yml +0 -28
  208. package/styles/Microsoft/Hyphens.yml +0 -14
  209. package/styles/Microsoft/Negative.yml +0 -13
  210. package/styles/Microsoft/Ordinal.yml +0 -13
  211. package/styles/Microsoft/OxfordComma.yml +0 -8
  212. package/styles/Microsoft/Passive.yml +0 -183
  213. package/styles/Microsoft/Percentages.yml +0 -7
  214. package/styles/Microsoft/Plurals.yml +0 -7
  215. package/styles/Microsoft/Quotes.yml +0 -7
  216. package/styles/Microsoft/RangeTime.yml +0 -13
  217. package/styles/Microsoft/Semicolon.yml +0 -8
  218. package/styles/Microsoft/SentenceLength.yml +0 -6
  219. package/styles/Microsoft/Spacing.yml +0 -8
  220. package/styles/Microsoft/Suspended.yml +0 -7
  221. package/styles/Microsoft/Terms.yml +0 -42
  222. package/styles/Microsoft/URLFormat.yml +0 -9
  223. package/styles/Microsoft/Units.yml +0 -16
  224. package/styles/Microsoft/Vocab.yml +0 -25
  225. package/styles/Microsoft/We.yml +0 -11
  226. package/styles/Microsoft/Wordiness.yml +0 -127
  227. package/styles/Microsoft/meta.json +0 -4
  228. package/styles/alex/Ablist.yml +0 -274
  229. package/styles/alex/Condescending.yml +0 -16
  230. package/styles/alex/Gendered.yml +0 -110
  231. package/styles/alex/LGBTQ.yml +0 -55
  232. package/styles/alex/OCD.yml +0 -10
  233. package/styles/alex/Press.yml +0 -12
  234. package/styles/alex/ProfanityLikely.yml +0 -1289
  235. package/styles/alex/ProfanityMaybe.yml +0 -282
  236. package/styles/alex/ProfanityUnlikely.yml +0 -251
  237. package/styles/alex/README.md +0 -27
  238. package/styles/alex/Race.yml +0 -85
  239. package/styles/alex/Suicide.yml +0 -26
  240. package/styles/alex/meta.json +0 -4
  241. package/styles/config/vocabularies/Docs/accept.txt +0 -47
  242. package/styles/config/vocabularies/Docs/reject.txt +0 -4
  243. package/styles/proselint/Airlinese.yml +0 -8
  244. package/styles/proselint/AnimalLabels.yml +0 -48
  245. package/styles/proselint/Annotations.yml +0 -9
  246. package/styles/proselint/Apologizing.yml +0 -8
  247. package/styles/proselint/Archaisms.yml +0 -52
  248. package/styles/proselint/But.yml +0 -8
  249. package/styles/proselint/Cliches.yml +0 -782
  250. package/styles/proselint/CorporateSpeak.yml +0 -30
  251. package/styles/proselint/Currency.yml +0 -5
  252. package/styles/proselint/Cursing.yml +0 -15
  253. package/styles/proselint/DateCase.yml +0 -7
  254. package/styles/proselint/DateMidnight.yml +0 -7
  255. package/styles/proselint/DateRedundancy.yml +0 -10
  256. package/styles/proselint/DateSpacing.yml +0 -7
  257. package/styles/proselint/DenizenLabels.yml +0 -52
  258. package/styles/proselint/Diacritical.yml +0 -95
  259. package/styles/proselint/GenderBias.yml +0 -45
  260. package/styles/proselint/GroupTerms.yml +0 -39
  261. package/styles/proselint/Hedging.yml +0 -8
  262. package/styles/proselint/Hyperbole.yml +0 -6
  263. package/styles/proselint/Jargon.yml +0 -11
  264. package/styles/proselint/LGBTOffensive.yml +0 -13
  265. package/styles/proselint/LGBTTerms.yml +0 -15
  266. package/styles/proselint/Malapropisms.yml +0 -8
  267. package/styles/proselint/Needless.yml +0 -358
  268. package/styles/proselint/Nonwords.yml +0 -38
  269. package/styles/proselint/Oxymorons.yml +0 -22
  270. package/styles/proselint/P-Value.yml +0 -6
  271. package/styles/proselint/RASSyndrome.yml +0 -30
  272. package/styles/proselint/README.md +0 -12
  273. package/styles/proselint/Skunked.yml +0 -13
  274. package/styles/proselint/Spelling.yml +0 -17
  275. package/styles/proselint/Typography.yml +0 -11
  276. package/styles/proselint/Uncomparables.yml +0 -50
  277. package/styles/proselint/Very.yml +0 -6
  278. package/styles/proselint/meta.json +0 -15
  279. package/styles/write-good/Cliches.yml +0 -702
  280. package/styles/write-good/E-Prime.yml +0 -32
  281. package/styles/write-good/Illusions.yml +0 -11
  282. package/styles/write-good/Passive.yml +0 -183
  283. package/styles/write-good/README.md +0 -27
  284. package/styles/write-good/So.yml +0 -5
  285. package/styles/write-good/ThereIs.yml +0 -6
  286. package/styles/write-good/TooWordy.yml +0 -221
  287. package/styles/write-good/Weasel.yml +0 -29
  288. package/styles/write-good/meta.json +0 -4
  289. package/test/mcp-example-test.yaml +0 -27
  290. package/test/test_parser.js +0 -47
  291. package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +0 -61
  292. package/testdriver/acceptance-sdk/README.md +0 -128
  293. package/testdriver/acceptance-sdk/TEST_REPORTING.md +0 -245
  294. package/testdriver/acceptance-sdk/assert.test.mjs +0 -44
  295. package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +0 -42
  296. package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +0 -239
  297. package/testdriver/acceptance-sdk/type-checking-demo.js +0 -49
  298. package/testdriver/acceptance-sdk/type.test.mjs +0 -84
  299. package/vale.ini +0 -18
  300. package/vitest.config.example.js +0 -19
  301. package/vitest.config.mjs.bak +0 -44
  302. /package/docs/{ARCHITECTURE.md → v7/_drafts/architecture.mdx} +0 -0
  303. /package/docs/{AWESOME_LOGS_QUICK_REF.md → v7/_drafts/awesome-logs-quick-ref.mdx} +0 -0
  304. /package/{CONTRIBUTING.md → docs/v7/_drafts/contributing.mdx} +0 -0
  305. /package/docs/v7/{guides → _drafts}/migration.mdx +0 -0
  306. /package/{PLUGIN_MIGRATION.md → docs/v7/_drafts/plugin-migration.mdx} +0 -0
  307. /package/{PROMPT_CACHE.md → docs/v7/_drafts/prompt-cache.mdx} +0 -0
  308. /package/docs/{SDK_AWESOME_LOGS.md → v7/_drafts/sdk-awesome-logs.mdx} +0 -0
  309. /package/docs/{sdk-browser-rendering.md → v7/_drafts/sdk-browser-rendering.mdx} +0 -0
  310. /package/{SDK_LOGGING.md → docs/v7/_drafts/sdk-logging.mdx} +0 -0
  311. /package/{SDK_MIGRATION.md → docs/v7/_drafts/sdk-migration.mdx} +0 -0
  312. /package/docs/{TEST_RECORDING.md → v7/_drafts/test-recording.mdx} +0 -0
  313. /package/docs/v7/{README.md → overview/readme.mdx} +0 -0
  314. /package/{debug-locate-response.js → test/manual/debug-locate-response.js} +0 -0
  315. /package/{test-find-api.js → test/manual/test-find-api.js} +0 -0
  316. /package/{test-prompt-cache.js → test/manual/test-prompt-cache.js} +0 -0
  317. /package/{test-sandbox-render.js → test/manual/test-sandbox-render.js} +0 -0
  318. /package/{test-sdk-methods.js → test/manual/test-sdk-methods.js} +0 -0
  319. /package/{test-sdk-refactor.js → test/manual/test-sdk-refactor.js} +0 -0
  320. /package/{test-stack-trace.mjs → test/manual/test-stack-trace.mjs} +0 -0
  321. /package/{verify-element-api.js → test/manual/verify-element-api.js} +0 -0
  322. /package/{verify-types.js → test/manual/verify-types.js} +0 -0
  323. /package/{testdriver/acceptance-sdk → test/testdriver}/setup/globalTeardown.mjs +0 -0
  324. /package/{testdriver/acceptance-sdk → test/testdriver}/setup/vitestSetup.mjs +0 -0
@@ -0,0 +1,358 @@
1
+ const BaseCommand = require("../lib/base.js");
2
+ const { createCommandDefinitions } = require("../../../agent/interface.js");
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+ const chalk = require("chalk");
6
+ const { execSync } = require("child_process");
7
+ const readline = require("readline");
8
+
9
+ /**
10
+ * Init command - scaffolds Vitest SDK example tests for TestDriver
11
+ */
12
+ class InitCommand extends BaseCommand {
13
+ async run() {
14
+ await this.parse(InitCommand);
15
+
16
+ console.log(chalk.cyan("\n🚀 Initializing TestDriver project...\n"));
17
+
18
+ await this.setupPackageJson();
19
+ await this.createVitestExample();
20
+ await this.createGitHubWorkflow();
21
+ await this.createGitignore();
22
+ await this.installDependencies();
23
+ await this.promptForApiKey();
24
+
25
+ console.log(chalk.green("\n✅ Project initialized successfully!\n"));
26
+ this.printNextSteps();
27
+ }
28
+
29
+ /**
30
+ * Prompt user for API key and save to .env
31
+ */
32
+ async promptForApiKey() {
33
+ const envPath = path.join(process.cwd(), ".env");
34
+
35
+ // Check if .env already exists with TD_API_KEY
36
+ if (fs.existsSync(envPath)) {
37
+ const envContent = fs.readFileSync(envPath, "utf8");
38
+ if (envContent.includes("TD_API_KEY=")) {
39
+ console.log(chalk.gray("\n API key already configured in .env, skipping...\n"));
40
+ return;
41
+ }
42
+ }
43
+
44
+ console.log(chalk.cyan(" Setting up your TestDriver API key...\n"));
45
+ console.log(chalk.gray(" Get your API key from: https://console.testdriver.ai/team"));
46
+
47
+ // Ask if user wants to open the browser
48
+ const shouldOpen = await this.askYesNo(" Open API keys page in browser? (Y/n): ");
49
+ if (shouldOpen) {
50
+ try {
51
+ // Dynamic import for ES module
52
+ const open = (await import("open")).default;
53
+ await open("https://console.testdriver.ai/team");
54
+ console.log(chalk.gray(" Opening browser...\n"));
55
+ } catch (error) {
56
+ console.log(chalk.yellow(" ⚠️ Could not open browser automatically\n"));
57
+ }
58
+ }
59
+
60
+ // Prompt for API key with hidden input
61
+ const apiKey = await this.promptHidden(" Enter your API key (input will be hidden): ");
62
+
63
+ if (apiKey && apiKey.trim()) {
64
+ // Save to .env
65
+ const envContent = fs.existsSync(envPath)
66
+ ? fs.readFileSync(envPath, "utf8") + "\n"
67
+ : "";
68
+
69
+ fs.writeFileSync(envPath, envContent + `TD_API_KEY=${apiKey.trim()}\n`);
70
+ console.log(chalk.green("\n ✓ API key saved to .env\n"));
71
+ } else {
72
+ console.log(chalk.yellow("\n ⚠️ No API key entered. You can add it later to .env:\n"));
73
+ console.log(chalk.gray(" TD_API_KEY=your_api_key\n"));
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Prompt for hidden input (like password)
79
+ */
80
+ async promptHidden(question) {
81
+ return new Promise((resolve) => {
82
+ const rl = readline.createInterface({
83
+ input: process.stdin,
84
+ output: process.stdout,
85
+ });
86
+
87
+ // Mute output to hide the input
88
+ const stdin = process.stdin;
89
+ const muted = {
90
+ write: () => {},
91
+ };
92
+
93
+ rl.question(question, (answer) => {
94
+ rl.close();
95
+ stdin.removeListener("data", muted.write);
96
+ console.log(""); // New line after hidden input
97
+ resolve(answer);
98
+ });
99
+
100
+ // Mute stdin to hide input
101
+ stdin.on("data", (char) => {
102
+ // Don't write to output (hides the input)
103
+ });
104
+ });
105
+ }
106
+
107
+ /**
108
+ * Ask a yes/no question
109
+ */
110
+ async askYesNo(question) {
111
+ return new Promise((resolve) => {
112
+ const rl = readline.createInterface({
113
+ input: process.stdin,
114
+ output: process.stdout,
115
+ });
116
+
117
+ rl.question(question, (answer) => {
118
+ rl.close();
119
+ const normalized = answer.toLowerCase().trim();
120
+ resolve(normalized === "" || normalized === "y" || normalized === "yes");
121
+ });
122
+ });
123
+ }
124
+
125
+ /**
126
+ * Setup package.json if it doesn't exist
127
+ */
128
+ async setupPackageJson() {
129
+ const packageJsonPath = path.join(process.cwd(), "package.json");
130
+
131
+ if (!fs.existsSync(packageJsonPath)) {
132
+ console.log(chalk.gray(" Creating package.json..."));
133
+
134
+ const packageJson = {
135
+ name: path.basename(process.cwd()),
136
+ version: "1.0.0",
137
+ description: "TestDriver.ai test suite",
138
+ type: "module",
139
+ scripts: {
140
+ test: "vitest run",
141
+ "test:watch": "vitest",
142
+ "test:ui": "vitest --ui"
143
+ },
144
+ keywords: ["testdriver", "testing", "e2e"],
145
+ author: "",
146
+ license: "ISC"
147
+ };
148
+
149
+ fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + "\n");
150
+ console.log(chalk.green(` Created package.json`));
151
+ } else {
152
+ console.log(chalk.gray(" package.json already exists, skipping..."));
153
+ }
154
+ }
155
+
156
+ /**
157
+ * Create a Vitest SDK example
158
+ */
159
+ async createVitestExample() {
160
+ const testDir = path.join(process.cwd(), "tests");
161
+ const testFile = path.join(testDir, "example.test.js");
162
+ const configFile = path.join(process.cwd(), "vitest.config.js");
163
+
164
+ // Create test directory if it doesn't exist
165
+ if (!fs.existsSync(testDir)) {
166
+ fs.mkdirSync(testDir, { recursive: true });
167
+ console.log(chalk.gray(` Created directory: ${testDir}`));
168
+ }
169
+
170
+ // Create example Vitest test
171
+ const vitestContent = `import { test, expect } from 'vitest';
172
+ import { chrome } from 'testdriverai/presets';
173
+
174
+ test('should navigate to example.com and find elements', async (context) => {
175
+ // The chrome preset handles connection, browser launch, and cleanup automatically
176
+ const { testdriver } = await chrome(context, {
177
+ url: 'https://example.com'
178
+ // apiKey automatically read from process.env.TD_API_KEY via .env file
179
+ });
180
+
181
+ // Find and verify elements
182
+ const heading = await testdriver.find('heading that says Example Domain');
183
+ expect(heading.found()).toBe(true);
184
+
185
+ const link = await testdriver.find('More information link');
186
+ expect(link.found()).toBe(true);
187
+ });
188
+ `;
189
+
190
+ fs.writeFileSync(testFile, vitestContent);
191
+ console.log(chalk.green(` Created test file: ${testFile}`));
192
+
193
+ // Create vitest config if it doesn't exist
194
+ if (!fs.existsSync(configFile)) {
195
+ const configContent = `import { defineConfig } from 'vitest/config';
196
+ import TestDriver from 'testdriverai/vitest';
197
+ import dotenv from 'dotenv';
198
+
199
+ // Load environment variables from .env file
200
+ dotenv.config();
201
+
202
+ export default defineConfig({
203
+ plugins: [TestDriver()],
204
+ test: {
205
+ testTimeout: 120000,
206
+ hookTimeout: 120000,
207
+ },
208
+ });
209
+ `;
210
+
211
+ fs.writeFileSync(configFile, configContent);
212
+ console.log(chalk.green(` Created config file: ${configFile}`));
213
+ }
214
+
215
+ }
216
+
217
+ /**
218
+ * Create or update .gitignore to include .env
219
+ */
220
+ async createGitignore() {
221
+ const gitignorePath = path.join(process.cwd(), ".gitignore");
222
+
223
+ let gitignoreContent = "";
224
+ if (fs.existsSync(gitignorePath)) {
225
+ gitignoreContent = fs.readFileSync(gitignorePath, "utf8");
226
+
227
+ // Check if .env is already in .gitignore
228
+ if (gitignoreContent.includes(".env")) {
229
+ console.log(chalk.gray(" .env already in .gitignore, skipping..."));
230
+ return;
231
+ }
232
+ }
233
+
234
+ // Add common ignores including .env
235
+ const ignoresToAdd = [
236
+ "",
237
+ "# TestDriver.ai",
238
+ ".env",
239
+ "node_modules/",
240
+ "test-results/",
241
+ "*.log",
242
+ ];
243
+
244
+ const newContent = gitignoreContent.trim()
245
+ ? gitignoreContent + "\n" + ignoresToAdd.join("\n") + "\n"
246
+ : ignoresToAdd.join("\n") + "\n";
247
+
248
+ fs.writeFileSync(gitignorePath, newContent);
249
+ console.log(chalk.green(" Updated .gitignore"));
250
+ }
251
+
252
+ /**
253
+ * Create GitHub Actions workflow
254
+ */
255
+ async createGitHubWorkflow() {
256
+ const workflowDir = path.join(process.cwd(), ".github", "workflows");
257
+ const workflowFile = path.join(workflowDir, "testdriver.yml");
258
+
259
+ // Create .github/workflows directory if it doesn't exist
260
+ if (!fs.existsSync(workflowDir)) {
261
+ fs.mkdirSync(workflowDir, { recursive: true });
262
+ console.log(chalk.gray(` Created directory: ${workflowDir}`));
263
+ }
264
+
265
+ if (!fs.existsSync(workflowFile)) {
266
+ const workflowContent = `name: TestDriver.ai Tests
267
+
268
+ on:
269
+ push:
270
+ branches: [ main, master ]
271
+ pull_request:
272
+ branches: [ main, master ]
273
+
274
+ jobs:
275
+ test:
276
+ runs-on: ubuntu-latest
277
+
278
+ steps:
279
+ - uses: actions/checkout@v4
280
+
281
+ - name: Setup Node.js
282
+ uses: actions/setup-node@v4
283
+ with:
284
+ node-version: '20'
285
+ cache: 'npm'
286
+
287
+ - name: Install dependencies
288
+ run: npm ci
289
+
290
+ - name: Run TestDriver.ai tests
291
+ env:
292
+ TD_API_KEY: \${{ secrets.TD_API_KEY }}
293
+ run: npm test
294
+
295
+ - name: Upload test results
296
+ if: always()
297
+ uses: actions/upload-artifact@v4
298
+ with:
299
+ name: test-results
300
+ path: test-results/
301
+ retention-days: 30
302
+ `;
303
+
304
+ fs.writeFileSync(workflowFile, workflowContent);
305
+ console.log(chalk.green(` Created GitHub workflow: ${workflowFile}`));
306
+ } else {
307
+ console.log(chalk.gray(" GitHub workflow already exists, skipping..."));
308
+ }
309
+ }
310
+
311
+ /**
312
+ * Install dependencies
313
+ */
314
+ async installDependencies() {
315
+ console.log(chalk.cyan("\n Installing dependencies...\n"));
316
+
317
+ try {
318
+ execSync("npm install -D vitest testdriverai && npm install dotenv", {
319
+ cwd: process.cwd(),
320
+ stdio: "inherit"
321
+ });
322
+ console.log(chalk.green("\n Dependencies installed successfully!"));
323
+ } catch (error) {
324
+ console.log(
325
+ chalk.yellow(
326
+ "\n⚠️ Failed to install dependencies automatically. Please run:",
327
+ ),
328
+ );
329
+ console.log(chalk.gray(" npm install -D vitest testdriverai"));
330
+ console.log(chalk.gray(" npm install dotenv\n"));
331
+ }
332
+ }
333
+
334
+ /**
335
+ * Print next steps
336
+ */
337
+ printNextSteps() {
338
+ console.log(chalk.cyan("Next steps:\n"));
339
+ console.log(" 1. Run your tests:");
340
+ console.log(chalk.gray(" npm test\n"));
341
+ console.log(" 2. For CI/CD, add TD_API_KEY to your GitHub repository secrets");
342
+ console.log(chalk.gray(" Settings → Secrets → Actions → New repository secret\n"));
343
+ console.log(
344
+ chalk.cyan("Learn more at https://docs.testdriver.ai/getting-started\n"),
345
+ );
346
+ }
347
+ }
348
+
349
+ // Get command definition from interface.js
350
+ const tempAgent = { workingDir: process.cwd() };
351
+ const definitions = createCommandDefinitions(tempAgent);
352
+ const commandDef = definitions["init"];
353
+
354
+ InitCommand.description = commandDef?.description || "";
355
+ InitCommand.args = commandDef?.args || {};
356
+ InitCommand.flags = commandDef?.flags || {};
357
+
358
+ module.exports = InitCommand;