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
@@ -3,29 +3,15 @@
3
3
  * Converted from: testdriver/acceptance/hover-text.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../lib/vitest/hooks.mjs";
12
8
 
13
9
  describe("Hover Text Test", () => {
14
- let testdriver;
10
+ it("should click Sign In and verify error message", async (context) => {
11
+ const testdriver = TestDriver(context, { headless: true, newSandbox: true, cacheKey: 'hover-text-test' });
12
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
13
 
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({ task: context.task });
18
-
19
- await setupTest(testdriver);
20
- });
21
-
22
- afterEach(async (context) => {
23
- await teardownTest(testdriver, { task: context.task });
24
- });
25
-
26
- it("should click Sign In and verify error message", async () => {
27
14
  // Click on Sign In button using new find() API
28
- await testdriver.focusApplication("Google Chrome");
29
15
 
30
16
  const signInButton = await testdriver.find(
31
17
  "Sign In, black button below the password field",
@@ -5,32 +5,20 @@
5
5
 
6
6
  import path, { dirname } from "path";
7
7
  import { fileURLToPath } from "url";
8
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
9
- import {
10
- createTestClient,
11
- performLogin,
12
- setupTest,
13
- teardownTest,
14
- } from "./setup/testHelpers.mjs";
8
+ import { describe, expect, it } from "vitest";
9
+ import { TestDriver } from "../../lib/vitest/hooks.mjs";
10
+ import { performLogin } from "./setup/testHelpers.mjs";
15
11
 
16
12
  // Get the directory of the current module
17
13
  const __filename = fileURLToPath(import.meta.url);
18
14
  const __dirname = dirname(__filename);
19
15
 
20
16
  describe("Match Image Test", () => {
21
- let testdriver;
17
+ it("should match shopping cart image and verify empty cart", async (context) => {
18
+ const testdriver = TestDriver(context, { headless: true });
19
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
22
20
 
23
- beforeEach(async (context) => {
24
- testdriver = createTestClient({ task: context.task });
25
-
26
- await setupTest(testdriver);
27
- });
28
-
29
- afterEach(async (context) => {
30
- await teardownTest(testdriver, { task: context.task });
31
- });
32
-
33
- it("should match shopping cart image and verify empty cart", async () => {
21
+ //
34
22
  // Perform login first
35
23
  await performLogin(testdriver);
36
24
 
@@ -3,28 +3,14 @@
3
3
  * Converted from: testdriver/acceptance/press-keys.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../lib/vitest/hooks.mjs";
12
8
 
13
9
  describe("Press Keys Test", () => {
14
- let testdriver;
10
+ it("should create tabs and navigate using keyboard shortcuts", async (context) => {
11
+ const testdriver = TestDriver(context, { headless: true });
12
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
13
 
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({ task: context.task });
18
-
19
- await setupTest(testdriver);
20
- });
21
-
22
- afterEach(async (context) => {
23
- await teardownTest(testdriver, { task: context.task });
24
- });
25
-
26
- it("should create tabs and navigate using keyboard shortcuts", async () => {
27
- await testdriver.focusApplication("Google Chrome");
28
14
  const signInButton = await testdriver.find(
29
15
  "Sign In, black button below the password field",
30
16
  );
@@ -3,27 +3,15 @@
3
3
  * Converted from: testdriver/acceptance/prompt.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../lib/vitest/hooks.mjs";
12
8
 
13
9
  describe.skip("Prompt Test", () => {
14
- let testdriver;
10
+ it("should execute AI-driven prompts", async (context) => {
11
+ const testdriver = TestDriver(context, { headless: true });
12
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
13
 
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({ task: context.task });
18
-
19
- await setupTest(testdriver);
20
- });
21
-
22
- afterEach(async (context) => {
23
- await teardownTest(testdriver, { task: context.task });
24
- });
25
-
26
- it("should execute AI-driven prompts", async () => {
14
+ //
27
15
  // Note: The SDK doesn't have a direct equivalent to YAML prompts without commands.
28
16
  // This would typically be handled by the AI agent interpreting natural language.
29
17
  // For SDK usage, you need to use explicit commands.
@@ -37,7 +25,7 @@ describe.skip("Prompt Test", () => {
37
25
  // This test is skipped as it requires explicit SDK implementation
38
26
  // You would need to implement these as explicit SDK calls
39
27
 
40
- await testdriver.ai("log in");
28
+ await testdriver.act("log in");
41
29
 
42
30
  const result = await testdriver.assert("the testdriver sandbox is visible");
43
31
  expect(result).toBeTruthy();
@@ -3,29 +3,15 @@
3
3
  * Converted from: testdriver/acceptance/scroll-keyboard.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../lib/vitest/hooks.mjs";
12
8
 
13
9
  describe("Scroll Keyboard Test", () => {
14
- let testdriver;
10
+ it("should navigate to webhamster.com and scroll with keyboard", async (context) => {
11
+ const testdriver = TestDriver(context, { headless: true });
12
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
13
 
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({
18
- task: context.task,
19
- redrawThreshold: 0.5, // Higher threshold for scroll test
20
- });
21
- await setupTest(testdriver);
22
- });
23
-
24
- afterEach(async (context) => {
25
- await teardownTest(testdriver, { task: context.task });
26
- });
27
-
28
- it("should navigate to webhamster.com and scroll with keyboard", async () => {
14
+ //
29
15
  // Navigate to https://www.webhamster.com/
30
16
  await testdriver.focusApplication("Google Chrome");
31
17
  const urlBar = await testdriver.find(
@@ -3,27 +3,15 @@
3
3
  * Converted from: testdriver/acceptance/scroll-until-image.yaml
4
4
  */
5
5
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../lib/vitest/hooks.mjs";
12
8
 
13
9
  describe("Scroll Until Image Test", () => {
14
- let testdriver;
10
+ it("should scroll until brown colored house image appears", async (context) => {
11
+ const testdriver = TestDriver(context, { headless: true });
12
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
13
 
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({ task: context.task });
18
-
19
- await setupTest(testdriver);
20
- });
21
-
22
- afterEach(async (context) => {
23
- await teardownTest(testdriver, { task: context.task });
24
- });
25
-
26
- it("should scroll until brown colored house image appears", async () => {
14
+ //
27
15
  // Navigate to Wikipedia page
28
16
  await testdriver.pressKeys(["ctrl", "l"]);
29
17
  await testdriver.type("https://en.wikipedia.org/wiki/Leonardo_da_Vinci");
@@ -0,0 +1,28 @@
1
+ /**
2
+ * TestDriver SDK - Scroll Until Text Test (Vitest)
3
+ * Converted from: testdriver/acceptance/scroll-until-text.yaml
4
+ */
5
+
6
+ import { describe, expect, it } from "vitest";
7
+ import { TestDriver } from "../../lib/vitest/hooks.mjs";
8
+ import { performLogin } from "./setup/testHelpers.mjs";
9
+
10
+ describe("Scroll Until Text Test", () => {
11
+ it('should scroll until "testdriver socks" appears', async (context) => {
12
+ const testdriver = TestDriver(context, { headless: true });
13
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
14
+
15
+ //
16
+ // Perform login first
17
+ await performLogin(testdriver);
18
+
19
+ // Scroll until text appears
20
+ await testdriver.focusApplication("Google Chrome");
21
+ await testdriver.scrollUntilText({ text: "testdriver socks", direction: "down" });
22
+
23
+ // Assert testdriver socks appears on screen
24
+ await testdriver.focusApplication("Google Chrome");
25
+ const result = await testdriver.assert("TestDriver Socks appears on screen");
26
+ expect(result).toBeTruthy();
27
+ });
28
+ });
@@ -1,33 +1,24 @@
1
1
  /**
2
2
  * TestDriver SDK - Scroll Test (Vitest)
3
3
  * Converted from: testdriver/acceptance/scroll.yaml
4
+ *
5
+ * UPDATED: Now using chrome preset for automatic setup
4
6
  */
5
7
 
6
- import { afterEach, beforeEach, describe, expect, it } from "vitest";
7
- import {
8
- createTestClient,
9
- setupTest,
10
- teardownTest,
11
- } from "./setup/testHelpers.mjs";
8
+ import { describe, expect, it } from "vitest";
9
+ import { TestDriver } from "../../lib/vitest/hooks.mjs";
12
10
 
13
11
  describe("Scroll Test", () => {
14
- let testdriver;
12
+ it("should navigate and scroll down the page", async (context) => {
13
+ const testdriver = TestDriver(context, { headless: true });
14
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
15
15
 
16
- beforeEach(async (context) => {
17
- testdriver = createTestClient({ task: context.task });
16
+ // Give Chrome a moment to fully render the UI
17
+ await new Promise(resolve => setTimeout(resolve, 2000));
18
18
 
19
- await setupTest(testdriver);
20
- });
21
-
22
- afterEach(async (context) => {
23
- await teardownTest(testdriver, { task: context.task });
24
- });
25
-
26
- it("should navigate and scroll down the page", async () => {
27
- // Navigate to webhamster.com
28
- await testdriver.focusApplication("Google Chrome");
19
+ // Navigate to webhamster.com - just look for the domain, not the full path
29
20
  const urlBar = await testdriver.find(
30
- "testdriver-sandbox.vercel.app/login, the URL in the omnibox showing the current page",
21
+ "testdriver-sandbox.vercel.app, the URL in the address bar",
31
22
  );
32
23
  await urlBar.click();
33
24
  await testdriver.pressKeys(["ctrl", "a"]);
@@ -44,7 +35,7 @@ describe("Scroll Test", () => {
44
35
  await testdriver.scroll("down", 1000);
45
36
 
46
37
  // Assert page is scrolled
47
- const result = await testdriver.assert("the page is scrolled down");
38
+ const result = await testdriver.assert("the page is scrolled down, the hamster dance heading is not visible on the page");
48
39
  expect(result).toBeTruthy();
49
40
  });
50
41
  });
@@ -0,0 +1,262 @@
1
+ /**
2
+ * Lifecycle Helpers
3
+ * Shared lifecycle hook functions (prerun, postrun)
4
+ *
5
+ * LEGACY: These helpers are thin wrappers around the new Dashcam class.
6
+ * For new code, prefer using the Dashcam class directly:
7
+ *
8
+ * import { Dashcam } from 'testdriverai/core';
9
+ * const dashcam = new Dashcam(client);
10
+ * await dashcam.auth();
11
+ */
12
+
13
+ import Dashcam from '../../../lib/core/Dashcam.js';
14
+
15
+ // Module-level cache to maintain Dashcam instance state across helper calls
16
+ const dashcamInstances = new WeakMap();
17
+
18
+ /**
19
+ * Get or create Dashcam instance for a client
20
+ * @private
21
+ * @param {TestDriver} client
22
+ * @param {object} options
23
+ * @returns {Dashcam}
24
+ */
25
+ function getDashcam(client, options = {}) {
26
+ if (!dashcamInstances.has(client)) {
27
+ dashcamInstances.set(client, new Dashcam(client, options));
28
+ }
29
+ return dashcamInstances.get(client);
30
+ }
31
+
32
+ /**
33
+ * Authenticate dashcam with API key
34
+ * @deprecated Use `new Dashcam(client)` and `dashcam.auth()` instead
35
+ * @param {TestDriver} client - TestDriver client
36
+ * @param {string} apiKey - Dashcam API key (default: 4e93d8bf-3886-4d26-a144-116c4063522d)
37
+ */
38
+ export async function authDashcam(
39
+ client,
40
+ apiKey = "4e93d8bf-3886-4d26-a144-116c4063522d",
41
+ ) {
42
+ const dashcam = getDashcam(client, { apiKey });
43
+ await dashcam.auth();
44
+ }
45
+
46
+ /**
47
+ * Add log file tracking to dashcam
48
+ * @deprecated Use `new Dashcam(client)` and `dashcam.addFileLog()` instead
49
+ * @param {TestDriver} client - TestDriver client
50
+ * @param {string} logName - Name for the log in dashcam (default: "TestDriver Log")
51
+ */
52
+ export async function addDashcamLog(client, logName = "TestDriver Log") {
53
+ const dashcam = getDashcam(client);
54
+ const logPath = client.os === "windows"
55
+ ? "C:\\Users\\testdriver\\Documents\\testdriver.log"
56
+ : "/tmp/testdriver.log";
57
+
58
+ // Create log file first
59
+ if (client.os === "windows") {
60
+ await client.exec("pwsh", `New-Item -ItemType File -Path "${logPath}" -Force`, 10000, true);
61
+ } else {
62
+ await client.exec("sh", `touch ${logPath}`, 10000, true);
63
+ }
64
+
65
+ await dashcam.addFileLog(logPath, logName);
66
+ }
67
+
68
+ /**
69
+ * Start dashcam recording
70
+ * @deprecated Use `new Dashcam(client)` and `dashcam.start()` instead
71
+ * @param {TestDriver} client - TestDriver client
72
+ */
73
+ export async function startDashcam(client) {
74
+ const dashcam = getDashcam(client);
75
+ await dashcam.start();
76
+ }
77
+
78
+ /**
79
+ * Stop dashcam recording and retrieve URL
80
+ * @deprecated Use `new Dashcam(client)` and `dashcam.stop()` instead
81
+ * @param {TestDriver} client - TestDriver client
82
+ * @returns {Promise<string|null>} Dashcam URL if available
83
+ */
84
+ export async function stopDashcam(client) {
85
+ console.log("🎬 Stopping dashcam and retrieving URL...");
86
+ const dashcam = getDashcam(client);
87
+ const url = await dashcam.stop();
88
+
89
+ if (url) {
90
+ console.log("✅ Found dashcam URL:", url);
91
+ console.log("🎥 Dashcam URL:", url);
92
+ } else {
93
+ console.warn("⚠️ No replay URL found in dashcam output");
94
+ }
95
+
96
+ return url;
97
+ }
98
+
99
+ /**
100
+ * Launch Chrome browser with guest mode
101
+ * @param {TestDriver} client - TestDriver client
102
+ * @param {string} url - URL to open (default: https://testdriver-sandbox.vercel.app/)
103
+ */
104
+ export async function launchChrome(
105
+ client,
106
+ url = "http://testdriver-sandbox.vercel.app/",
107
+ ) {
108
+ const shell = client.os === "windows" ? "pwsh" : "sh";
109
+
110
+ if (client.os === "windows") {
111
+ await client.exec(
112
+ "pwsh",
113
+ `Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "--guest", "${url}"`,
114
+ 30000,
115
+ );
116
+ } else {
117
+ await client.exec(
118
+ shell,
119
+ `google-chrome --start-maximized --disable-fre --no-default-browser-check --no-first-run --guest "${url}" >/dev/null 2>&1 &`,
120
+ 30000,
121
+ );
122
+ }
123
+ }
124
+
125
+ /**
126
+ * Launch Chrome for Testing browser with guest mode
127
+ * @param {TestDriver} client - TestDriver client
128
+ * @param {string} url - URL to open (default: https://testdriver-sandbox.vercel.app/)
129
+ */
130
+ export async function launchChromeForTesting(
131
+ client,
132
+ url = "http://testdriver-sandbox.vercel.app/",
133
+ ) {
134
+ const shell = client.os === "windows" ? "pwsh" : "sh";
135
+
136
+ if (client.os === "windows") {
137
+ // Windows Chrome for Testing path would need to be determined
138
+ // For now, fallback to regular Chrome on Windows
139
+ await client.exec(
140
+ "pwsh",
141
+ `Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "--guest", "${url}"`,
142
+ 30000,
143
+ );
144
+ } else {
145
+ await client.exec(
146
+ shell,
147
+ `chrome-for-testing --start-maximized --disable-fre --no-default-browser-check --no-first-run --guest "${url}" >/dev/null 2>&1 &`,
148
+ 30000,
149
+ );
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Launch Chrome for Testing with a Chrome extension loaded
155
+ * @param {TestDriver} client - TestDriver client
156
+ * @param {string} extensionId - Chrome Web Store extension ID (e.g., "cjpalhdlnbpafiamejdnhcphjbkeiagm" for uBlock Origin)
157
+ * @param {string} url - URL to open (default: https://testdriver-sandbox.vercel.app/)
158
+ * @example
159
+ * // Launch with uBlock Origin extension
160
+ * await launchChromeExtension(client, "cjpalhdlnbpafiamejdnhcphjbkeiagm");
161
+ *
162
+ * // Launch with multiple extensions (comma-separated)
163
+ * await launchChromeExtension(client, "cjpalhdlnbpafiamejdnhcphjbkeiagm,nngceckbapebfimnlniiiahkandclblb");
164
+ */
165
+ export async function launchChromeExtension(
166
+ client,
167
+ extensionId,
168
+ url = "http://testdriver-sandbox.vercel.app/",
169
+ ) {
170
+ const shell = client.os === "windows" ? "pwsh" : "sh";
171
+
172
+ if (client.os === "windows") {
173
+ // Windows Chrome for Testing path would need to be determined
174
+ // For now, fallback to regular Chrome on Windows
175
+ await client.exec(
176
+ "pwsh",
177
+ `Start-Process "C:/Program Files/Google/Chrome/Application/chrome.exe" -ArgumentList "--start-maximized", "--load-extension=${extensionId}", "${url}"`,
178
+ 30000,
179
+ );
180
+ } else {
181
+ await client.exec(
182
+ shell,
183
+ `chrome-for-testing --start-maximized --disable-fre --no-default-browser-check --no-first-run --load-extension=${extensionId} "${url}" >/dev/null 2>&1 &`,
184
+ 30000,
185
+ );
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Wait for page to load by polling for text
191
+ * @param {TestDriver} client - TestDriver client
192
+ * @param {string} text - Text to wait for
193
+ * @param {number} maxAttempts - Maximum number of attempts (default: 60)
194
+ * @param {number} pollInterval - Interval between polls in ms (default: 5000)
195
+ */
196
+ export async function waitForPage(
197
+ client,
198
+ text,
199
+ maxAttempts = 60,
200
+ pollInterval = 5000,
201
+ ) {
202
+ console.log("Waiting for page to load, looking for text:", text);
203
+ let element;
204
+ for (let i = 0; i < maxAttempts; i++) {
205
+ element = await client.find(text);
206
+ if (element.found()) break;
207
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
208
+ }
209
+ }
210
+
211
+ /**
212
+ * Run prerun lifecycle hooks
213
+ * Implements lifecycle/prerun.yaml functionality
214
+ * @param {TestDriver} client - TestDriver client
215
+ */
216
+ export async function runPrerun(client) {
217
+ await authDashcam(client);
218
+ await addDashcamLog(client);
219
+ await startDashcam(client);
220
+ await launchChrome(client);
221
+ await waitForPage(client, "TestDriver.ai Sandbox");
222
+ }
223
+
224
+ /**
225
+ * Run prerun lifecycle hooks with Chrome for Testing
226
+ * Implements lifecycle/prerun.yaml functionality using Chrome for Testing
227
+ * @param {TestDriver} client - TestDriver client
228
+ */
229
+ export async function runPrerunChromeForTesting(client) {
230
+ await authDashcam(client);
231
+ await addDashcamLog(client);
232
+ await startDashcam(client);
233
+ await launchChromeForTesting(client);
234
+ await waitForPage(client, "TestDriver.ai Sandbox");
235
+ }
236
+
237
+ /**
238
+ * Run prerun lifecycle hooks with Chrome extension loaded
239
+ * Implements lifecycle/prerun.yaml functionality with a Chrome extension
240
+ * @param {TestDriver} client - TestDriver client
241
+ * @param {string} extensionId - Chrome Web Store extension ID to load
242
+ * @example
243
+ * // Launch with uBlock Origin extension
244
+ * await runPrerunChromeExtension(client, "cjpalhdlnbpafiamejdnhcphjbkeiagm");
245
+ */
246
+ export async function runPrerunChromeExtension(client, extensionId) {
247
+ await authDashcam(client);
248
+ await addDashcamLog(client);
249
+ await startDashcam(client);
250
+ await launchChromeExtension(client, extensionId);
251
+ await waitForPage(client, "TestDriver.ai Sandbox");
252
+ }
253
+
254
+ /**
255
+ * Run postrun lifecycle hooks
256
+ * Implements lifecycle/postrun.yaml functionality
257
+ * @param {TestDriver} client - TestDriver client
258
+ * @returns {Promise<string|null>} Dashcam URL if available
259
+ */
260
+ export async function runPostrun(client) {
261
+ return await stopDashcam(client);
262
+ }
@@ -11,26 +11,26 @@ import path, { dirname } from "path";
11
11
  import { fileURLToPath } from "url";
12
12
  import TestDriver from "../../../sdk.js";
13
13
  import {
14
- addDashcamLog,
15
- authDashcam,
16
- launchChrome,
17
- runPostrun,
18
- runPrerun,
19
- startDashcam,
20
- stopDashcam,
21
- waitForPage,
14
+ addDashcamLog,
15
+ authDashcam,
16
+ launchChrome,
17
+ runPostrun,
18
+ runPrerun,
19
+ startDashcam,
20
+ stopDashcam,
21
+ waitForPage,
22
22
  } from "./lifecycleHelpers.mjs";
23
23
 
24
24
  // Re-export lifecycle helpers for backward compatibility
25
25
  export {
26
- addDashcamLog,
27
- authDashcam,
28
- launchChrome,
29
- runPostrun,
30
- runPrerun,
31
- startDashcam,
32
- stopDashcam,
33
- waitForPage
26
+ addDashcamLog,
27
+ authDashcam,
28
+ launchChrome,
29
+ runPostrun,
30
+ runPrerun,
31
+ startDashcam,
32
+ stopDashcam,
33
+ waitForPage
34
34
  };
35
35
 
36
36
  // Get the directory of the current module
@@ -264,7 +264,7 @@ export function createTestClient(options = {}) {
264
264
  apiKey: process.env.TD_API_KEY,
265
265
  apiRoot: process.env.TD_API_ROOT || "https://testdriver-api.onrender.com",
266
266
  // headless: false,
267
- // newSandbox: true,
267
+ newSandbox: true,
268
268
  // ip: '18.217.194.23'
269
269
  // ...clientOptions,
270
270
  // cache: false,
@@ -522,7 +522,11 @@ export async function teardownTest(client, options = {}) {
522
522
  }
523
523
 
524
524
  // Note: Duration is calculated by Vitest and passed via result.duration
525
- // We don't need to track start time ourselves
525
+ // We include it in the test result file so the reporter can use it
526
+
527
+ // Get duration from Vitest result
528
+ const result = options.task.result?.();
529
+ const duration = result?.duration || 0;
526
530
 
527
531
  // Write test result with dashcam URL, platform, and metadata
528
532
  const testResult = {
@@ -536,6 +540,7 @@ export async function teardownTest(client, options = {}) {
536
540
  : null,
537
541
  platform: client.os, // Include platform from SDK client (source of truth)
538
542
  timestamp: Date.now(),
543
+ duration: duration, // Include duration from Vitest
539
544
  };
540
545
 
541
546
  fs.writeFileSync(testResultFile, JSON.stringify(testResult, null, 2));
@@ -614,9 +619,9 @@ export async function performLogin(
614
619
  await usernameField.click();
615
620
  await client.type(username);
616
621
 
617
- // Enter password
622
+ // Enter password (marked as secret so it's not logged or stored)
618
623
  await client.pressKeys(["tab"]);
619
- await client.type(password);
624
+ await client.type(password, { secret: true });
620
625
 
621
626
  // Submit form
622
627
  await client.pressKeys(["tab"]);