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,167 @@
1
+ # Auto-Generated Cache Keys from File Hash
2
+
3
+ ## Overview
4
+
5
+ When you create a TestDriver instance without providing an explicit `cacheKey`, the SDK will automatically generate one based on the SHA-256 hash of the calling file. This provides automatic cache invalidation when your test file changes, while enabling cache hits for identical test runs.
6
+
7
+ ## How It Works
8
+
9
+ 1. **Stack Trace Analysis**: When `TestDriver()` is called, the SDK analyzes the call stack to find the caller file
10
+ 2. **File Hashing**: The content of the caller file is hashed using SHA-256
11
+ 3. **Cache Key Generation**: The first 16 characters of the hash are used as the cache key
12
+ 4. **Automatic Updates**: When the test file is modified, the hash changes, automatically invalidating the cache
13
+
14
+ ## Benefits
15
+
16
+ - ✅ **No Manual Cache Management**: Cache keys are automatically generated and updated
17
+ - ✅ **File-Scoped Caching**: All tests in the same file share the same cache
18
+ - ✅ **Automatic Invalidation**: Cache is invalidated when the test file changes
19
+ - ✅ **Explicit Override**: You can still provide a manual `cacheKey` if needed
20
+
21
+ ## Usage Examples
22
+
23
+ ### Automatic Cache Key (Recommended)
24
+
25
+ ```javascript
26
+ import { TestDriver } from 'testdriverai/vitest/hooks';
27
+
28
+ test('login test', async (context) => {
29
+ // No cacheKey provided - will be auto-generated from this file's hash
30
+ const testdriver = TestDriver(context, {
31
+ headless: true
32
+ });
33
+
34
+ // Cache key is automatically set based on this file
35
+ console.log(testdriver.options.cacheKey); // e.g., "4cae7be040f293b9"
36
+
37
+ const button = await testdriver.find('login button');
38
+ // Subsequent calls in this test file will use the same cache
39
+ });
40
+ ```
41
+
42
+ ### Explicit Cache Key (Override)
43
+
44
+ ```javascript
45
+ import { TestDriver } from 'testdriverai/vitest/hooks';
46
+
47
+ test('login test', async (context) => {
48
+ // Explicit cacheKey provided - auto-generation is skipped
49
+ const testdriver = TestDriver(context, {
50
+ headless: true,
51
+ cacheKey: 'my-custom-key-v1'
52
+ });
53
+
54
+ console.log(testdriver.options.cacheKey); // "my-custom-key-v1"
55
+
56
+ const button = await testdriver.find('login button');
57
+ });
58
+ ```
59
+
60
+ ## Cache Behavior
61
+
62
+ ### With Auto-Generated Key
63
+
64
+ 1. **First Run**: Creates cache entries with key = hash of test file
65
+ 2. **Subsequent Runs** (file unchanged): Cache hits
66
+ 3. **After File Modification**: New hash = new cache key = cache miss
67
+
68
+ ### Cache Hit Example
69
+
70
+ ```javascript
71
+ // File: login.test.mjs (hash: 4cae7be040f293b9)
72
+
73
+ const testdriver = TestDriver(context);
74
+ // Auto-generated cacheKey: "4cae7be040f293b9"
75
+
76
+ const button1 = await testdriver.find('login button'); // Cache MISS (first time)
77
+ const button2 = await testdriver.find('login button'); // Cache HIT (same file, same test run)
78
+ ```
79
+
80
+ After modifying the file (adding a comment, changing test logic, etc.):
81
+
82
+ ```javascript
83
+ // File: login.test.mjs (hash: 7f3d9a2b1c5e8f6a) <- changed!
84
+
85
+ const testdriver = TestDriver(context);
86
+ // Auto-generated cacheKey: "7f3d9a2b1c5e8f6a" <- different from before
87
+
88
+ const button1 = await testdriver.find('login button'); // Cache MISS (new hash)
89
+ ```
90
+
91
+ ## Debug Mode
92
+
93
+ To see the auto-generated cache key in debug logs:
94
+
95
+ ```bash
96
+ # Set environment variable
97
+ export TD_DEBUG=1
98
+
99
+ # Or in your test
100
+ process.env.TD_DEBUG = '1';
101
+ ```
102
+
103
+ With debug mode enabled, you'll see:
104
+ ```
105
+ 🔍 find() threshold: 0.05 (cache ENABLED, cacheKey: 4cae7be040f293b9 (auto-generated from file hash))
106
+ ```
107
+
108
+ ## Implementation Details
109
+
110
+ ### Stack Trace Filtering
111
+
112
+ The auto-generation skips the following in the stack trace to find the actual test file:
113
+ - `sdk.js` (TestDriver SDK)
114
+ - `hooks.mjs` / `hooks.js` (Vitest hooks)
115
+ - `node_modules` (dependencies)
116
+ - `node:internal` (Node.js internals)
117
+
118
+ ### File Path Handling
119
+
120
+ The implementation handles both:
121
+ - Regular file paths: `/Users/you/project/test.mjs`
122
+ - File URL format: `file:///Users/you/project/test.mjs`
123
+
124
+ ### Hash Format
125
+
126
+ - Algorithm: SHA-256
127
+ - Output: First 16 hexadecimal characters (e.g., `4cae7be040f293b9`)
128
+ - Collision probability: Effectively zero for practical purposes
129
+
130
+ ## When to Use Manual Cache Keys
131
+
132
+ Consider using manual `cacheKey` values when:
133
+
134
+ 1. **Cross-File Caching**: You want to share cache across multiple test files
135
+ 2. **Version-Based Caching**: You want explicit control over cache invalidation
136
+ 3. **CI/CD Integration**: You want to tie caching to build numbers or git commits
137
+
138
+ Example:
139
+
140
+ ```javascript
141
+ const cacheKey = \`test-suite-\${process.env.GIT_COMMIT}\`;
142
+ const testdriver = TestDriver(context, { cacheKey });
143
+ ```
144
+
145
+ ## Migration from Manual Keys
146
+
147
+ If you currently use manual cache keys:
148
+
149
+ ### Before
150
+ ```javascript
151
+ const testdriver = TestDriver(context, {
152
+ cacheKey: 'login-test-v1'
153
+ });
154
+ ```
155
+
156
+ ### After (automatic)
157
+ ```javascript
158
+ // Just remove the cacheKey - it will be auto-generated!
159
+ const testdriver = TestDriver(context);
160
+ ```
161
+
162
+ The cache will automatically invalidate when the test file changes, which is usually the desired behavior.
163
+
164
+ ## See Also
165
+
166
+ - [SDK_README.md](./SDK_README.md) - Cache configuration options
167
+ - [CACHE_ARCHITECTURE.md](../api/CACHE_ARCHITECTURE.md) - Cache system architecture
@@ -0,0 +1,486 @@
1
+ ---
2
+ title: "Best Practices"
3
+ description: "Patterns and practices for reliable tests"
4
+ icon: "star"
5
+ ---
6
+
7
+ ## Test Structure
8
+
9
+ ### Use beforeAll/afterAll
10
+
11
+ Create one sandbox per test suite:
12
+
13
+ ```javascript
14
+ describe('Login Flow', () => {
15
+ let testdriver;
16
+
17
+ beforeAll(async () => {
18
+ const client = await TestDriver.create({
19
+ apiKey: process.env.TD_API_KEY,
20
+ os: 'linux'
21
+ });
22
+ await client.auth();
23
+ await client.connect({ newSandbox: true });
24
+ testdriver = client;
25
+ });
26
+
27
+ afterAll(async () => {
28
+ await testdriver?.disconnect();
29
+ });
30
+
31
+ it('logs in successfully', async () => {
32
+ // Test code
33
+ });
34
+
35
+ it('shows error on invalid credentials', async () => {
36
+ // Test code
37
+ });
38
+ });
39
+ ```
40
+
41
+ ### Use Presets
42
+
43
+ Presets handle lifecycle automatically:
44
+
45
+ ```javascript
46
+ // ✅ Good - automatic lifecycle
47
+ test('my test', async (context) => {
48
+ const { testdriver } = await chrome(context, { url });
49
+ // Test code
50
+ });
51
+
52
+ // ❌ Avoid - manual lifecycle
53
+ test('my test', async () => {
54
+ const client = new TestDriver(...);
55
+ await client.auth();
56
+ await client.connect();
57
+ // Test code
58
+ await client.disconnect();
59
+ });
60
+ ```
61
+
62
+ ## Element Finding
63
+
64
+ ### Be Specific
65
+
66
+ ```javascript
67
+ // ❌ Too vague
68
+ await testdriver.find('button');
69
+
70
+ // ✅ Specific
71
+ await testdriver.find('blue submit button at bottom of login form');
72
+
73
+ // ✅ Include visual context
74
+ await testdriver.find('red delete button next to user John Doe');
75
+
76
+ // ✅ Use nearby text
77
+ await testdriver.find('button below "Confirm your email" text');
78
+ ```
79
+
80
+ ### Always Check found()
81
+
82
+ ```javascript
83
+ // ❌ Assumes element exists
84
+ const button = await testdriver.find('submit button');
85
+ await button.click();
86
+
87
+ // ✅ Verifies element exists
88
+ const button = await testdriver.find('submit button');
89
+ if (!button.found()) {
90
+ throw new Error('Submit button not found');
91
+ }
92
+ await button.click();
93
+
94
+ // ✅ Or use assertion
95
+ const button = await testdriver.find('submit button');
96
+ expect(button.found()).toBe(true);
97
+ await button.click();
98
+ ```
99
+
100
+ ### Poll for Dynamic Elements
101
+
102
+ ```javascript
103
+ // ✅ Wait for element to appear
104
+ async function waitFor(testdriver, description, timeout = 30000) {
105
+ const start = Date.now();
106
+ while (Date.now() - start < timeout) {
107
+ const element = await testdriver.find(description);
108
+ if (element.found()) return element;
109
+ await new Promise(r => setTimeout(r, 1000));
110
+ }
111
+ throw new Error(`Element not found: ${description}`);
112
+ }
113
+
114
+ const button = await waitFor(testdriver, 'submit button');
115
+ await button.click();
116
+ ```
117
+
118
+ ## Actions
119
+
120
+ ### Use Descriptive Prompts
121
+
122
+ ```javascript
123
+ // ❌ Generic
124
+ await testdriver.ai('login');
125
+
126
+ // ✅ Specific steps
127
+ await testdriver.ai('click the username field and type user@example.com');
128
+ await testdriver.ai('click the password field and type password123');
129
+ await testdriver.ai('click the blue submit button');
130
+ ```
131
+
132
+ ### Chain Actions
133
+
134
+ ```javascript
135
+ // ✅ Find once, use multiple times
136
+ const input = await testdriver.find('username field');
137
+ await input.click();
138
+ await testdriver.type('user@example.com');
139
+ await testdriver.pressKeys(['Tab']);
140
+
141
+ // ✅ Or chain directly
142
+ await testdriver.find('username field').then(el => el.click());
143
+ ```
144
+
145
+ ### Use Keyboard Shortcuts
146
+
147
+ ```javascript
148
+ // ✅ Faster than UI navigation
149
+ await testdriver.pressKeys(['ctrl', 'a']); // Select all
150
+ await testdriver.pressKeys(['ctrl', 'c']); // Copy
151
+ await testdriver.pressKeys(['ctrl', 'v']); // Paste
152
+ await testdriver.pressKeys(['escape']); // Close dialog
153
+ ```
154
+
155
+ ## Assertions
156
+
157
+ ### Verify Key States
158
+
159
+ ```javascript
160
+ // ✅ Assert at checkpoints
161
+ await testdriver.assert('login page is visible');
162
+ await testdriver.find('username').then(el => el.click());
163
+ await testdriver.type('user@example.com');
164
+ await testdriver.find('password').then(el => el.click());
165
+ await testdriver.type('password123');
166
+ await testdriver.find('submit').then(el => el.click());
167
+ await testdriver.assert('dashboard is visible');
168
+ await testdriver.assert('welcome message shows username');
169
+ ```
170
+
171
+ ### Use Vitest Assertions
172
+
173
+ ```javascript
174
+ // ✅ Combine TestDriver and Vitest
175
+ const element = await testdriver.find('error message');
176
+ expect(element.found()).toBe(true);
177
+ expect(element.text).toContain('Invalid credentials');
178
+
179
+ const result = await testdriver.assert('dashboard loaded');
180
+ expect(result).toBe(true);
181
+ ```
182
+
183
+ ## Performance
184
+
185
+ ### Reuse Sandboxes
186
+
187
+ ```javascript
188
+ // ✅ One sandbox per suite
189
+ beforeAll(async () => {
190
+ testdriver = await TestDriver.create(...);
191
+ });
192
+
193
+ afterAll(async () => {
194
+ await testdriver.disconnect();
195
+ });
196
+
197
+ // ❌ One sandbox per test (slow!)
198
+ beforeEach(async () => {
199
+ testdriver = await TestDriver.create(...);
200
+ });
201
+ ```
202
+
203
+ ### Use Caching
204
+
205
+ ```javascript
206
+ // ✅ Enable caching for repeated elements
207
+ await testdriver.find('submit button'); // AI call
208
+ await testdriver.find('submit button'); // Cache hit (fast!)
209
+
210
+ // ✅ Use consistent prompts
211
+ const buttonDesc = 'blue submit button at bottom';
212
+ await testdriver.find(buttonDesc); // Cache hit on reuse
213
+ ```
214
+
215
+ ### Parallel Execution
216
+
217
+ ```javascript
218
+ // vitest.config.mjs
219
+ export default defineConfig({
220
+ test: {
221
+ pool: 'forks',
222
+ maxConcurrency: 5, // Run 5 tests in parallel
223
+ fileParallelism: true
224
+ }
225
+ });
226
+ ```
227
+
228
+ ## Error Handling
229
+
230
+ ### Graceful Failures
231
+
232
+ ```javascript
233
+ test('handles missing elements', async (context) => {
234
+ const { testdriver } = await chrome(context, { url });
235
+
236
+ const optionalButton = await testdriver.find('optional newsletter button');
237
+
238
+ if (optionalButton.found()) {
239
+ await optionalButton.click();
240
+ } else {
241
+ console.log('Newsletter button not present, skipping');
242
+ }
243
+
244
+ // Continue with required elements
245
+ const required = await testdriver.find('continue button');
246
+ expect(required.found()).toBe(true);
247
+ await required.click();
248
+ });
249
+ ```
250
+
251
+ ### Try-Catch for exec()
252
+
253
+ ```javascript
254
+ // ✅ Handle command failures
255
+ try {
256
+ await testdriver.exec('sh', 'risky-command', 30000, false);
257
+ } catch (error) {
258
+ console.log('Command failed, using fallback');
259
+ await testdriver.exec('sh', 'fallback-command', 30000, false);
260
+ }
261
+ ```
262
+
263
+ ### Cleanup in Finally
264
+
265
+ ```javascript
266
+ let testdriver;
267
+
268
+ try {
269
+ testdriver = await TestDriver.create(...);
270
+ await testdriver.auth();
271
+ await testdriver.connect();
272
+
273
+ // Test code
274
+
275
+ } catch (error) {
276
+ console.error('Test failed:', error);
277
+ throw error;
278
+ } finally {
279
+ await testdriver?.disconnect();
280
+ }
281
+ ```
282
+
283
+ ## Code Organization
284
+
285
+ ### Extract Common Patterns
286
+
287
+ ```javascript
288
+ // helpers.mjs
289
+ export async function login(testdriver, email, password) {
290
+ await testdriver.find('username field').then(el => el.click());
291
+ await testdriver.type(email);
292
+ await testdriver.find('password field').then(el => el.click());
293
+ await testdriver.type(password);
294
+ await testdriver.find('submit button').then(el => el.click());
295
+ await testdriver.assert('dashboard is visible');
296
+ }
297
+
298
+ // test file
299
+ import { login } from './helpers.mjs';
300
+
301
+ test('user can access settings', async (context) => {
302
+ const { testdriver } = await chrome(context, { url });
303
+ await login(testdriver, 'user@example.com', 'password123');
304
+ await testdriver.find('settings').then(el => el.click());
305
+ });
306
+ ```
307
+
308
+ ### Use Page Objects
309
+
310
+ ```javascript
311
+ // pages/LoginPage.mjs
312
+ export class LoginPage {
313
+ constructor(testdriver) {
314
+ this.testdriver = testdriver;
315
+ }
316
+
317
+ async login(email, password) {
318
+ await this.testdriver.find('username field').then(el => el.click());
319
+ await this.testdriver.type(email);
320
+ await this.testdriver.find('password field').then(el => el.click());
321
+ await this.testdriver.type(password);
322
+ await this.testdriver.find('submit button').then(el => el.click());
323
+ }
324
+
325
+ async assertVisible() {
326
+ const result = await this.testdriver.assert('login page is visible');
327
+ expect(result).toBe(true);
328
+ }
329
+ }
330
+
331
+ // test file
332
+ import { LoginPage } from './pages/LoginPage.mjs';
333
+
334
+ test('login flow', async (context) => {
335
+ const { testdriver } = await chrome(context, { url });
336
+ const loginPage = new LoginPage(testdriver);
337
+
338
+ await loginPage.assertVisible();
339
+ await loginPage.login('user@example.com', 'password123');
340
+ });
341
+ ```
342
+
343
+ ## Environment Management
344
+
345
+ ### Use Environment Variables
346
+
347
+ ```javascript
348
+ // ✅ Good - configurable
349
+ const testdriver = await TestDriver.create({
350
+ apiKey: process.env.TD_API_KEY,
351
+ os: process.env.TD_OS || 'linux',
352
+ resolution: process.env.TD_RESOLUTION || '1920x1080'
353
+ });
354
+
355
+ // ❌ Bad - hardcoded
356
+ const testdriver = await TestDriver.create({
357
+ apiKey: 'td_1234567890',
358
+ os: 'linux'
359
+ });
360
+ ```
361
+
362
+ ### Separate Test Data
363
+
364
+ ```javascript
365
+ // test-data.json
366
+ {
367
+ "validUser": {
368
+ "email": "user@example.com",
369
+ "password": "password123"
370
+ },
371
+ "adminUser": {
372
+ "email": "admin@example.com",
373
+ "password": "admin123"
374
+ }
375
+ }
376
+
377
+ // test file
378
+ import testData from './test-data.json';
379
+
380
+ test('login as user', async (context) => {
381
+ const { testdriver } = await chrome(context, { url });
382
+ await login(testdriver, testData.validUser.email, testData.validUser.password);
383
+ });
384
+ ```
385
+
386
+ ## Documentation
387
+
388
+ ### Comment Complex Logic
389
+
390
+ ```javascript
391
+ // ✅ Explain why, not what
392
+ // Wait for animation to complete before interacting
393
+ await new Promise(r => setTimeout(r, 1000));
394
+
395
+ // Navigate to nested menu item since direct click doesn't work
396
+ await testdriver.find('menu button').then(el => el.hover());
397
+ await testdriver.find('submenu item').then(el => el.click());
398
+ ```
399
+
400
+ ### Name Tests Clearly
401
+
402
+ ```javascript
403
+ // ✅ Clear intent
404
+ test('user sees error when submitting form with invalid email', async () => {});
405
+ test('admin can delete user accounts from settings page', async () => {});
406
+
407
+ // ❌ Vague
408
+ test('form validation', async () => {});
409
+ test('user deletion', async () => {});
410
+ ```
411
+
412
+ ## Anti-Patterns
413
+
414
+ ### Don't Use Hardcoded Delays
415
+
416
+ ```javascript
417
+ // ❌ Brittle - might be too short or too long
418
+ await new Promise(r => setTimeout(r, 5000));
419
+
420
+ // ✅ Poll for condition
421
+ const element = await waitFor(testdriver, 'success message');
422
+ ```
423
+
424
+ ### Don't Ignore Errors
425
+
426
+ ```javascript
427
+ // ❌ Silently fails
428
+ try {
429
+ await testdriver.find('button').then(el => el.click());
430
+ } catch (error) {
431
+ // Ignore
432
+ }
433
+
434
+ // ✅ Handle gracefully
435
+ const button = await testdriver.find('button');
436
+ if (!button.found()) {
437
+ console.warn('Optional button not found, skipping');
438
+ } else {
439
+ await button.click();
440
+ }
441
+ ```
442
+
443
+ ### Don't Test Implementation Details
444
+
445
+ ```javascript
446
+ // ❌ Too specific to implementation
447
+ await testdriver.find('div with class submit-btn-container').then(el => el.click());
448
+
449
+ // ✅ User-facing behavior
450
+ await testdriver.find('submit button').then(el => el.click());
451
+ ```
452
+
453
+ ## Checklist
454
+
455
+ Before committing tests:
456
+
457
+ - ✅ Tests use `beforeAll`/`afterAll` for sandbox lifecycle
458
+ - ✅ All elements checked with `.found()` before use
459
+ - ✅ Descriptive element descriptions with visual context
460
+ - ✅ Key assertions at important checkpoints
461
+ - ✅ No hardcoded delays (use polling instead)
462
+ - ✅ API keys in environment variables
463
+ - ✅ Test names clearly describe intent
464
+ - ✅ Common patterns extracted to helpers
465
+ - ✅ Error handling for optional elements
466
+ - ✅ Cleanup in `finally` blocks
467
+
468
+ ## See Also
469
+
470
+ <CardGroup cols={2}>
471
+ <Card title="Debugging" icon="bug" href="/v7/guides/debugging">
472
+ Debug failing tests
473
+ </Card>
474
+
475
+ <Card title="Configuration" icon="gear" href="/v7/getting-started/configuration">
476
+ Configure TestDriver
477
+ </Card>
478
+
479
+ <Card title="Caching" icon="bolt" href="/v7/guides/caching-ai">
480
+ Optimize with caching
481
+ </Card>
482
+
483
+ <Card title="Examples" icon="code" href="/v7/presets/chrome">
484
+ See working examples
485
+ </Card>
486
+ </CardGroup>