testdriverai 6.2.2 → 7.0.0

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 (264) hide show
  1. package/.github/workflows/acceptance-linux.yml +75 -0
  2. package/.github/workflows/acceptance-sdk-tests.yml +133 -0
  3. package/.vscode/settings.json +5 -1
  4. package/MIGRATION.md +389 -0
  5. package/PLUGIN_MIGRATION.md +222 -0
  6. package/PROMPT_CACHE.md +200 -0
  7. package/SDK_LOGGING.md +222 -0
  8. package/SDK_MIGRATION.md +474 -0
  9. package/SDK_README.md +1122 -0
  10. package/{testdriver → _testdriver}/acceptance/drag-and-drop.yaml +2 -2
  11. package/{testdriver → _testdriver}/acceptance/snippets/login.yaml +1 -1
  12. package/_testdriver/examples/desktop/lifecycle/prerun.yaml +0 -0
  13. package/{testdriver → _testdriver}/examples/web/lifecycle/prerun.yaml +6 -1
  14. package/{testdriver → _testdriver}/lifecycle/postrun.yaml +3 -2
  15. package/_testdriver/lifecycle/prerun.yaml +15 -0
  16. package/{testdriver → _testdriver}/lifecycle/provision.yaml +7 -2
  17. package/agent/index.js +258 -68
  18. package/agent/interface.js +15 -0
  19. package/agent/lib/cache.js +142 -0
  20. package/agent/lib/commander.js +1 -39
  21. package/agent/lib/commands.js +143 -188
  22. package/agent/lib/redraw.js +6 -3
  23. package/agent/lib/sandbox.js +19 -5
  24. package/agent/lib/sdk.js +1 -0
  25. package/agent/lib/system.js +0 -3
  26. package/agent/lib/validation.js +1 -7
  27. package/debug-locate-response.js +82 -0
  28. package/debug-screenshot-1763401388589.png +0 -0
  29. package/debugger/index.html +15 -4
  30. package/docs/ARCHITECTURE.md +424 -0
  31. package/docs/AWESOME_LOGS_QUICK_REF.md +100 -0
  32. package/docs/QUICK_START_TEST_RECORDING.md +215 -0
  33. package/docs/SDK_AWESOME_LOGS.md +468 -0
  34. package/docs/TEST_RECORDING.md +388 -0
  35. package/docs/docs.json +232 -152
  36. package/docs/sdk-browser-rendering.md +167 -0
  37. package/docs/v6/getting-started/self-hosting.mdx +407 -0
  38. package/docs/{guide → v6/guide}/dashcam.mdx +1 -1
  39. package/docs/{guide → v6/guide}/environment-variables.mdx +4 -5
  40. package/docs/{guide → v6/guide}/lifecycle.mdx +1 -1
  41. package/docs/v6/overview/comparison.mdx +101 -0
  42. package/docs/v7/README.md +135 -0
  43. package/docs/v7/api/ai.mdx +205 -0
  44. package/docs/v7/api/assert.mdx +285 -0
  45. package/docs/v7/api/assertions.mdx +403 -0
  46. package/docs/v7/api/click.mdx +287 -0
  47. package/docs/v7/api/client.mdx +322 -0
  48. package/docs/v7/api/elements.mdx +479 -0
  49. package/docs/v7/api/exec.mdx +346 -0
  50. package/docs/v7/api/find.mdx +316 -0
  51. package/docs/v7/api/focusApplication.mdx +294 -0
  52. package/docs/v7/api/hover.mdx +279 -0
  53. package/docs/v7/api/pressKeys.mdx +349 -0
  54. package/docs/v7/api/sandbox.mdx +404 -0
  55. package/docs/v7/api/scroll.mdx +300 -0
  56. package/docs/v7/api/type.mdx +314 -0
  57. package/docs/v7/commands/assert.mdx +45 -0
  58. package/docs/v7/commands/exec.mdx +282 -0
  59. package/docs/v7/commands/focus-application.mdx +44 -0
  60. package/docs/v7/commands/hover-image.mdx +69 -0
  61. package/docs/v7/commands/hover-text.mdx +47 -0
  62. package/docs/v7/commands/if.mdx +53 -0
  63. package/docs/v7/commands/match-image.mdx +67 -0
  64. package/docs/v7/commands/press-keys.mdx +87 -0
  65. package/docs/v7/commands/remember.mdx +49 -0
  66. package/docs/v7/commands/run.mdx +44 -0
  67. package/docs/v7/commands/scroll-until-image.mdx +66 -0
  68. package/docs/v7/commands/scroll-until-text.mdx +60 -0
  69. package/docs/v7/commands/scroll.mdx +69 -0
  70. package/docs/v7/commands/type.mdx +45 -0
  71. package/docs/v7/commands/wait-for-image.mdx +54 -0
  72. package/docs/v7/commands/wait-for-text.mdx +48 -0
  73. package/docs/v7/commands/wait.mdx +45 -0
  74. package/docs/v7/getting-started/quickstart.mdx +199 -0
  75. package/docs/v7/guides/migration.mdx +562 -0
  76. package/docs/{getting-started → v7/guides}/self-hosting.mdx +11 -12
  77. package/docs/v7/playwright.mdx +342 -0
  78. package/eslint.config.js +19 -1
  79. package/examples/run-tests-with-recording.sh +70 -0
  80. package/examples/screenshot-example.js +63 -0
  81. package/examples/sdk-awesome-logs-demo.js +177 -0
  82. package/examples/sdk-cache-thresholds.js +96 -0
  83. package/examples/sdk-element-properties.js +155 -0
  84. package/examples/sdk-simple-example.js +65 -0
  85. package/examples/test-recording-example.test.js +166 -0
  86. package/interfaces/cli/lib/base.js +10 -4
  87. package/interfaces/logger.js +2 -1
  88. package/interfaces/shared-test-state.mjs +69 -0
  89. package/interfaces/vitest-plugin.mjs +744 -0
  90. package/mcp-server/AI_GUIDELINES.md +57 -0
  91. package/package.json +18 -5
  92. package/schema.json +8 -29
  93. package/scripts/view-test-results.mjs +96 -0
  94. package/sdk-log-formatter.js +714 -0
  95. package/sdk.d.ts +735 -0
  96. package/sdk.js +1906 -0
  97. package/{.github/workflows/self-hosted.yml → self-hosted.yml} +13 -4
  98. package/setup/aws/cloudformation.yaml +9 -2
  99. package/test/mcp-example-test.yaml +27 -0
  100. package/test-find-api.js +73 -0
  101. package/test-prompt-cache.js +96 -0
  102. package/test-sandbox-render.js +28 -0
  103. package/test-sdk-methods.js +15 -0
  104. package/test-sdk-refactor.js +53 -0
  105. package/test-stack-trace.mjs +57 -0
  106. package/testdriver/acceptance-sdk/QUICK_REFERENCE.md +61 -0
  107. package/testdriver/acceptance-sdk/README.md +128 -0
  108. package/testdriver/acceptance-sdk/TEST_REPORTING.md +245 -0
  109. package/testdriver/acceptance-sdk/assert.test.mjs +44 -0
  110. package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +70 -0
  111. package/testdriver/acceptance-sdk/element-not-found.test.mjs +38 -0
  112. package/testdriver/acceptance-sdk/exec-js.test.mjs +55 -0
  113. package/testdriver/acceptance-sdk/exec-output.test.mjs +71 -0
  114. package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +69 -0
  115. package/testdriver/acceptance-sdk/focus-window.test.mjs +48 -0
  116. package/testdriver/acceptance-sdk/formatted-logging.test.mjs +41 -0
  117. package/testdriver/acceptance-sdk/hover-image.test.mjs +43 -0
  118. package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +50 -0
  119. package/testdriver/acceptance-sdk/hover-text.test.mjs +41 -0
  120. package/testdriver/acceptance-sdk/match-image.test.mjs +48 -0
  121. package/testdriver/acceptance-sdk/press-keys.test.mjs +64 -0
  122. package/testdriver/acceptance-sdk/prompt.test.mjs +45 -0
  123. package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +52 -0
  124. package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +51 -0
  125. package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +42 -0
  126. package/testdriver/acceptance-sdk/scroll.test.mjs +50 -0
  127. package/testdriver/acceptance-sdk/setup/globalTeardown.mjs +11 -0
  128. package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +239 -0
  129. package/testdriver/acceptance-sdk/setup/testHelpers.mjs +648 -0
  130. package/testdriver/acceptance-sdk/setup/vitestSetup.mjs +40 -0
  131. package/testdriver/acceptance-sdk/type-checking-demo.js +49 -0
  132. package/testdriver/acceptance-sdk/type.test.mjs +84 -0
  133. package/verify-element-api.js +89 -0
  134. package/verify-types.js +0 -0
  135. package/vitest.config.example.js +19 -0
  136. package/vitest.config.mjs +65 -0
  137. package/vitest.config.mjs.bak +44 -0
  138. package/.github/workflows/acceptance-v6.yml +0 -169
  139. package/docs/overview/comparison.mdx +0 -82
  140. package/testdriver/lifecycle/prerun.yaml +0 -17
  141. /package/{testdriver/examples/desktop/lifecycle/prerun.yaml → .env.example} +0 -0
  142. /package/{testdriver → _testdriver}/acceptance/assert.yaml +0 -0
  143. /package/{testdriver → _testdriver}/acceptance/dashcam.yaml +0 -0
  144. /package/{testdriver → _testdriver}/acceptance/embed.yaml +0 -0
  145. /package/{testdriver → _testdriver}/acceptance/exec-js.yaml +0 -0
  146. /package/{testdriver → _testdriver}/acceptance/exec-output.yaml +0 -0
  147. /package/{testdriver → _testdriver}/acceptance/exec-shell.yaml +0 -0
  148. /package/{testdriver → _testdriver}/acceptance/focus-window.yaml +0 -0
  149. /package/{testdriver → _testdriver}/acceptance/hover-image.yaml +0 -0
  150. /package/{testdriver → _testdriver}/acceptance/hover-text-with-description.yaml +0 -0
  151. /package/{testdriver → _testdriver}/acceptance/hover-text.yaml +0 -0
  152. /package/{testdriver → _testdriver}/acceptance/if-else.yaml +0 -0
  153. /package/{testdriver → _testdriver}/acceptance/match-image.yaml +0 -0
  154. /package/{testdriver → _testdriver}/acceptance/press-keys.yaml +0 -0
  155. /package/{testdriver → _testdriver}/acceptance/prompt.yaml +0 -0
  156. /package/{testdriver → _testdriver}/acceptance/remember.yaml +0 -0
  157. /package/{testdriver → _testdriver}/acceptance/screenshots/cart.png +0 -0
  158. /package/{testdriver → _testdriver}/acceptance/scroll-keyboard.yaml +0 -0
  159. /package/{testdriver → _testdriver}/acceptance/scroll-until-image.yaml +0 -0
  160. /package/{testdriver → _testdriver}/acceptance/scroll-until-text.yaml +0 -0
  161. /package/{testdriver → _testdriver}/acceptance/scroll.yaml +0 -0
  162. /package/{testdriver → _testdriver}/acceptance/snippets/match-cart.yaml +0 -0
  163. /package/{testdriver → _testdriver}/acceptance/type.yaml +0 -0
  164. /package/{testdriver → _testdriver}/behavior/failure.yaml +0 -0
  165. /package/{testdriver → _testdriver}/behavior/hover-text.yaml +0 -0
  166. /package/{testdriver → _testdriver}/behavior/lifecycle/postrun.yaml +0 -0
  167. /package/{testdriver → _testdriver}/behavior/lifecycle/prerun.yaml +0 -0
  168. /package/{testdriver → _testdriver}/behavior/lifecycle/provision.yaml +0 -0
  169. /package/{testdriver → _testdriver}/behavior/secrets.yaml +0 -0
  170. /package/{testdriver → _testdriver}/edge-cases/dashcam-chrome.yaml +0 -0
  171. /package/{testdriver → _testdriver}/edge-cases/exec-pwsh-multiline.yaml +0 -0
  172. /package/{testdriver → _testdriver}/edge-cases/js-exception.yaml +0 -0
  173. /package/{testdriver → _testdriver}/edge-cases/js-promise.yaml +0 -0
  174. /package/{testdriver → _testdriver}/edge-cases/lifecycle/postrun.yaml +0 -0
  175. /package/{testdriver → _testdriver}/edge-cases/prompt-in-middle.yaml +0 -0
  176. /package/{testdriver → _testdriver}/edge-cases/prompt-nested.yaml +0 -0
  177. /package/{testdriver → _testdriver}/edge-cases/success-test.yaml +0 -0
  178. /package/{testdriver → _testdriver}/examples/android/example.yaml +0 -0
  179. /package/{testdriver → _testdriver}/examples/android/lifecycle/postrun.yaml +0 -0
  180. /package/{testdriver → _testdriver}/examples/android/lifecycle/provision.yaml +0 -0
  181. /package/{testdriver → _testdriver}/examples/android/readme.md +0 -0
  182. /package/{testdriver → _testdriver}/examples/chrome-extension/lifecycle/provision.yaml +0 -0
  183. /package/{testdriver → _testdriver}/examples/desktop/lifecycle/provision.yaml +0 -0
  184. /package/{testdriver → _testdriver}/examples/vscode-extension/lifecycle/provision.yaml +0 -0
  185. /package/{testdriver → _testdriver}/examples/web/lifecycle/postrun.yaml +0 -0
  186. /package/docs/{account → v6/account}/dashboard.mdx +0 -0
  187. /package/docs/{account → v6/account}/enterprise.mdx +0 -0
  188. /package/docs/{account → v6/account}/pricing.mdx +0 -0
  189. /package/docs/{account → v6/account}/projects.mdx +0 -0
  190. /package/docs/{account → v6/account}/team.mdx +0 -0
  191. /package/docs/{action → v6/action}/ami.mdx +0 -0
  192. /package/docs/{action → v6/action}/performance.mdx +0 -0
  193. /package/docs/{action → v6/action}/secrets.mdx +0 -0
  194. /package/docs/{apps → v6/apps}/chrome-extensions.mdx +0 -0
  195. /package/docs/{apps → v6/apps}/desktop-apps.mdx +0 -0
  196. /package/docs/{apps → v6/apps}/mobile-apps.mdx +0 -0
  197. /package/docs/{apps → v6/apps}/static-websites.mdx +0 -0
  198. /package/docs/{apps → v6/apps}/tauri-apps.mdx +0 -0
  199. /package/docs/{bugs → v6/bugs}/jira.mdx +0 -0
  200. /package/docs/{cli → v6/cli}/overview.mdx +0 -0
  201. /package/docs/{commands → v6/commands}/assert.mdx +0 -0
  202. /package/docs/{commands → v6/commands}/exec.mdx +0 -0
  203. /package/docs/{commands → v6/commands}/focus-application.mdx +0 -0
  204. /package/docs/{commands → v6/commands}/hover-image.mdx +0 -0
  205. /package/docs/{commands → v6/commands}/hover-text.mdx +0 -0
  206. /package/docs/{commands → v6/commands}/if.mdx +0 -0
  207. /package/docs/{commands → v6/commands}/match-image.mdx +0 -0
  208. /package/docs/{commands → v6/commands}/press-keys.mdx +0 -0
  209. /package/docs/{commands → v6/commands}/remember.mdx +0 -0
  210. /package/docs/{commands → v6/commands}/run.mdx +0 -0
  211. /package/docs/{commands → v6/commands}/scroll-until-image.mdx +0 -0
  212. /package/docs/{commands → v6/commands}/scroll-until-text.mdx +0 -0
  213. /package/docs/{commands → v6/commands}/scroll.mdx +0 -0
  214. /package/docs/{commands → v6/commands}/type.mdx +0 -0
  215. /package/docs/{commands → v6/commands}/wait-for-image.mdx +0 -0
  216. /package/docs/{commands → v6/commands}/wait-for-text.mdx +0 -0
  217. /package/docs/{commands → v6/commands}/wait.mdx +0 -0
  218. /package/docs/{exporting → v6/exporting}/junit.mdx +0 -0
  219. /package/docs/{exporting → v6/exporting}/playwright.mdx +0 -0
  220. /package/docs/{features → v6/features}/auto-healing.mdx +0 -0
  221. /package/docs/{features → v6/features}/generation.mdx +0 -0
  222. /package/docs/{features → v6/features}/parallel-testing.mdx +0 -0
  223. /package/docs/{features → v6/features}/reusable-snippets.mdx +0 -0
  224. /package/docs/{features → v6/features}/selectorless.mdx +0 -0
  225. /package/docs/{features → v6/features}/visual-assertions.mdx +0 -0
  226. /package/docs/{getting-started → v6/getting-started}/ci.mdx +0 -0
  227. /package/docs/{getting-started → v6/getting-started}/cli.mdx +0 -0
  228. /package/docs/{getting-started → v6/getting-started}/editing.mdx +0 -0
  229. /package/docs/{getting-started → v6/getting-started}/playwright.mdx +0 -0
  230. /package/docs/{getting-started → v6/getting-started}/running.mdx +0 -0
  231. /package/docs/{getting-started → v6/getting-started}/vscode.mdx +0 -0
  232. /package/docs/{guide → v6/guide}/assertions.mdx +0 -0
  233. /package/docs/{guide → v6/guide}/authentication.mdx +0 -0
  234. /package/docs/{guide → v6/guide}/code.mdx +0 -0
  235. /package/docs/{guide → v6/guide}/locating.mdx +0 -0
  236. /package/docs/{guide → v6/guide}/protips.mdx +0 -0
  237. /package/docs/{guide → v6/guide}/variables.mdx +0 -0
  238. /package/docs/{guide → v6/guide}/waiting.mdx +0 -0
  239. /package/docs/{importing → v6/importing}/csv.mdx +0 -0
  240. /package/docs/{importing → v6/importing}/gherkin.mdx +0 -0
  241. /package/docs/{importing → v6/importing}/jira.mdx +0 -0
  242. /package/docs/{importing → v6/importing}/testrail.mdx +0 -0
  243. /package/docs/{integrations → v6/integrations}/electron.mdx +0 -0
  244. /package/docs/{integrations → v6/integrations}/netlify.mdx +0 -0
  245. /package/docs/{integrations → v6/integrations}/vercel.mdx +0 -0
  246. /package/docs/{interactive → v6/interactive}/explore.mdx +0 -0
  247. /package/docs/{interactive → v6/interactive}/run.mdx +0 -0
  248. /package/docs/{interactive → v6/interactive}/save.mdx +0 -0
  249. /package/docs/{overview → v6/overview}/faq.mdx +0 -0
  250. /package/docs/{overview → v6/overview}/performance.mdx +0 -0
  251. /package/docs/{overview → v6/overview}/quickstart.mdx +0 -0
  252. /package/docs/{overview → v6/overview}/what-is-testdriver.mdx +0 -0
  253. /package/docs/{scenarios → v6/scenarios}/ai-chatbot.mdx +0 -0
  254. /package/docs/{scenarios → v6/scenarios}/cookie-banner.mdx +0 -0
  255. /package/docs/{scenarios → v6/scenarios}/file-upload.mdx +0 -0
  256. /package/docs/{scenarios → v6/scenarios}/form-filling.mdx +0 -0
  257. /package/docs/{scenarios → v6/scenarios}/log-in.mdx +0 -0
  258. /package/docs/{scenarios → v6/scenarios}/pdf-generation.mdx +0 -0
  259. /package/docs/{scenarios → v6/scenarios}/spell-check.mdx +0 -0
  260. /package/docs/{security → v6/security}/action.mdx +0 -0
  261. /package/docs/{security → v6/security}/agent.mdx +0 -0
  262. /package/docs/{security → v6/security}/platform.mdx +0 -0
  263. /package/docs/{tutorials → v6/tutorials}/advanced-test.mdx +0 -0
  264. /package/docs/{tutorials → v6/tutorials}/basic-test.mdx +0 -0
package/SDK_README.md ADDED
@@ -0,0 +1,1122 @@
1
+ # TestDriver SDK
2
+
3
+ The TestDriver SDK provides programmatic access to TestDriver's AI-powered testing capabilities. Use it to automate UI testing for web and desktop applications with natural language commands.
4
+
5
+ ## ✨ New: AWESOME Logs with Great DX!
6
+
7
+ Your SDK now has **beautiful, emoji-rich logging** that makes test output a joy to read! 🎨
8
+
9
+ ```
10
+ [2.34s] 🔍 Found "submit button" · 📍 (682, 189) · ⏱️ 167ms · ⚡ cached
11
+ [2.51s] 👆 Click "submit button"
12
+ [2.89s] ⌨️ Type → hello world
13
+ [3.12s] ✅ Assert "page correct" · ✓ PASSED · ⏱️ 45ms
14
+ ```
15
+
16
+ **Features:**
17
+
18
+ - 🎨 Rich emojis for all actions (find, click, type, scroll, etc.)
19
+ - ⚡ Cache hit/miss indicators
20
+ - ⏱️ Color-coded performance timing (green < 100ms, yellow < 500ms, red > 500ms)
21
+ - 📍 Coordinate display for found elements
22
+ - 📊 Beautiful progress bars and summaries
23
+
24
+ See [docs/AWESOME_LOGS_QUICK_REF.md](./docs/AWESOME_LOGS_QUICK_REF.md) for quick reference or [docs/SDK_AWESOME_LOGS.md](./docs/SDK_AWESOME_LOGS.md) for complete documentation.
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install testdriverai
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ```javascript
35
+ const TestDriver = require("testdriverai");
36
+
37
+ async function runTest() {
38
+ // Initialize SDK with your API key
39
+ const client = new TestDriver(process.env.TD_API_KEY);
40
+
41
+ // Authenticate and connect to a sandbox
42
+ await client.auth();
43
+ await client.connect();
44
+
45
+ // Use the new find() API
46
+ await client.focusApplication("Google Chrome");
47
+
48
+ const searchBox = await client.find("search box").find();
49
+ await searchBox.click();
50
+ await client.type("testdriver.ai");
51
+ await client.pressKeys(["enter"]);
52
+
53
+ // Poll for element to appear
54
+ let result = client.find("TestDriver heading");
55
+ while (!result.found()) {
56
+ result = await result.find();
57
+ await new Promise((resolve) => setTimeout(resolve, 500));
58
+ }
59
+
60
+ // Clean up
61
+ await client.disconnect();
62
+ }
63
+
64
+ runTest();
65
+ ```
66
+
67
+ ## New Element Finding API ✨
68
+
69
+ We've introduced a new `find()` API that provides better control over element finding and interaction. See [SDK_MIGRATION.md](./SDK_MIGRATION.md) for full migration guide.
70
+
71
+ ### Basic Usage
72
+
73
+ ```javascript
74
+ // Find and click an element
75
+ const button = await client.find(
76
+ "the sign in button, black button below password",
77
+ );
78
+ await button.click();
79
+
80
+ // Check if element exists
81
+ if (button.found()) {
82
+ console.log("Button coordinates:", button.getCoordinates());
83
+ }
84
+ ```
85
+
86
+ ### Polling Pattern
87
+
88
+ ```javascript
89
+ // Wait for element to appear
90
+ let element;
91
+ while (!element?.found()) {
92
+ console.log("waiting for element...");
93
+ element = await client.find("login button");
94
+ if (!element.found()) {
95
+ await new Promise((resolve) => setTimeout(resolve, 1000));
96
+ }
97
+ }
98
+ await element.click();
99
+ ```
100
+
101
+ ### Different Actions
102
+
103
+ ```javascript
104
+ const menu = await client.find("File menu").find();
105
+ await menu.hover();
106
+ await menu.rightClick();
107
+ await menu.doubleClick();
108
+
109
+ // Or use the generic click() method with action parameter
110
+ await menu.click("right-click");
111
+ ```
112
+
113
+ ### Drag and Drop
114
+
115
+ ```javascript
116
+ const source = await client.find("draggable item");
117
+ await source.mouseDown();
118
+
119
+ const target = await client.find("drop zone");
120
+ await target.mouseUp();
121
+ ```
122
+
123
+ ## API Reference
124
+
125
+ ### Element Class
126
+
127
+ The `Element` class represents an element found on screen and provides methods for interacting with it.
128
+
129
+ #### Creating Elements
130
+
131
+ ##### `client.find(description)`
132
+
133
+ Creates a new Element instance and immediately attempts to locate it.
134
+
135
+ **Parameters:**
136
+
137
+ - `description` (string): Natural language description of the element
138
+
139
+ **Returns:** `Promise<Element>` - Element instance (already located)
140
+
141
+ **Example:**
142
+
143
+ ```javascript
144
+ const button = await client.find("the sign in button");
145
+ // Element is automatically located
146
+ if (button.found()) {
147
+ await button.click();
148
+ }
149
+ ```
150
+
151
+ #### Element Methods
152
+
153
+ ##### `element.find([newDescription])`
154
+
155
+ Locates (or relocates) the element on screen.
156
+
157
+ **Parameters:**
158
+
159
+ - `newDescription` (string, optional): New description to search for
160
+
161
+ **Returns:** `Promise<Element>` - The same Element instance
162
+
163
+ **Example:**
164
+
165
+ ```javascript
166
+ // Re-find the same element
167
+ await element.find();
168
+
169
+ // Find with a new description
170
+ await element.find("submit button");
171
+ ```
172
+
173
+ ##### `element.found()`
174
+
175
+ Check if the element was successfully located.
176
+
177
+ **Returns:** `boolean` - true if element was found
178
+
179
+ **Example:**
180
+
181
+ ```javascript
182
+ if (element.found()) {
183
+ console.log("Element located!");
184
+ }
185
+ ```
186
+
187
+ ##### `element.click([action])`
188
+
189
+ Click on the element.
190
+
191
+ **Parameters:**
192
+
193
+ - `action` (string, optional): Click type - `'click'`, `'right-click'`, `'double-click'`, `'mouseDown'`, `'mouseUp'` (default: `'click'`)
194
+
195
+ **Returns:** `Promise<void>`
196
+
197
+ **Example:**
198
+
199
+ ```javascript
200
+ await element.click();
201
+ await element.click("right-click");
202
+ ```
203
+
204
+ ##### `element.hover()`
205
+
206
+ Hover the mouse over the element.
207
+
208
+ **Returns:** `Promise<void>`
209
+
210
+ **Example:**
211
+
212
+ ```javascript
213
+ await element.hover();
214
+ ```
215
+
216
+ ##### `element.doubleClick()`
217
+
218
+ Double-click on the element. Convenience method for `element.click('double-click')`.
219
+
220
+ **Returns:** `Promise<void>`
221
+
222
+ **Example:**
223
+
224
+ ```javascript
225
+ await element.doubleClick();
226
+ ```
227
+
228
+ ##### `element.rightClick()`
229
+
230
+ Right-click on the element. Convenience method for `element.click('right-click')`.
231
+
232
+ **Returns:** `Promise<void>`
233
+
234
+ **Example:**
235
+
236
+ ```javascript
237
+ await element.rightClick();
238
+ ```
239
+
240
+ ##### `element.mouseDown()`
241
+
242
+ Press the mouse button down on the element (useful for drag operations).
243
+
244
+ **Returns:** `Promise<void>`
245
+
246
+ **Example:**
247
+
248
+ ```javascript
249
+ const draggable = await client.find("item to drag");
250
+ await draggable.mouseDown();
251
+ ```
252
+
253
+ ##### `element.mouseUp()`
254
+
255
+ Release the mouse button on the element (useful for drag operations).
256
+
257
+ **Returns:** `Promise<void>`
258
+
259
+ **Example:**
260
+
261
+ ```javascript
262
+ const dropZone = await client.find("drop target");
263
+ await dropZone.mouseUp();
264
+ ```
265
+
266
+ ##### `element.getCoordinates()`
267
+
268
+ Get the screen coordinates of the element.
269
+
270
+ **Returns:** `{x, y, centerX, centerY} | null` - Coordinates object or null if not found
271
+
272
+ **Example:**
273
+
274
+ ```javascript
275
+ const coords = element.getCoordinates();
276
+ if (coords) {
277
+ console.log(`Position: ${coords.x}, ${coords.y}`);
278
+ console.log(`Center: ${coords.centerX}, ${coords.centerY}`);
279
+ }
280
+ ```
281
+
282
+ ##### `element.getResponse()`
283
+
284
+ Get the full API response data from the locate operation.
285
+
286
+ **Returns:** `Object | null` - Full response with all available data
287
+
288
+ **Example:**
289
+
290
+ ```javascript
291
+ const response = element.getResponse();
292
+ console.log("Full response:", response);
293
+ ```
294
+
295
+ #### Element Properties
296
+
297
+ Elements expose many read-only properties from the API response:
298
+
299
+ ##### Coordinate Properties
300
+
301
+ - `element.x` - X coordinate (top-left corner) or null
302
+ - `element.y` - Y coordinate (top-left corner) or null
303
+ - `element.centerX` - X coordinate of element center or null
304
+ - `element.centerY` - Y coordinate of element center or null
305
+
306
+ ##### Dimension Properties
307
+
308
+ - `element.width` - Width of the element or null
309
+ - `element.height` - Height of the element or null
310
+ - `element.boundingBox` - Bounding box object or null
311
+
312
+ ##### Match Quality Properties
313
+
314
+ - `element.confidence` - Confidence score (0-1) or null
315
+ - `element.screenshot` - Base64 encoded screenshot or null
316
+ - `element.text` - Text content of the element or null
317
+ - `element.label` - Label/aria-label of the element or null
318
+
319
+ **Example:**
320
+
321
+ ```javascript
322
+ const button = await client.find("login button");
323
+
324
+ if (button.found()) {
325
+ console.log({
326
+ position: { x: button.x, y: button.y },
327
+ center: { x: button.centerX, y: button.centerY },
328
+ size: { width: button.width, height: button.height },
329
+ confidence: button.confidence,
330
+ text: button.text,
331
+ label: button.label,
332
+ });
333
+
334
+ // Save screenshot for debugging
335
+ if (button.screenshot) {
336
+ require("fs").writeFileSync(
337
+ "element.png",
338
+ Buffer.from(button.screenshot, "base64"),
339
+ );
340
+ }
341
+
342
+ // Conditional actions based on properties
343
+ if (button.confidence > 0.8) {
344
+ await button.click();
345
+ } else {
346
+ console.log("Low confidence, skipping click");
347
+ }
348
+ }
349
+ ```
350
+
351
+ For more examples, see `examples/sdk-element-properties.js`.
352
+
353
+ ### Initialization
354
+
355
+ #### `new TestDriver(apiKey, options)`
356
+
357
+ Creates a new TestDriver SDK instance.
358
+
359
+ **Parameters:**
360
+
361
+ - `apiKey` (string): Your TestDriver API key
362
+ - `options` (object, optional):
363
+ - `apiRoot` (string): API endpoint (default: 'https://v6.testdriver.ai')
364
+ - `resolution` (string): Sandbox resolution (default: '1366x768')
365
+ - `analytics` (boolean): Enable analytics (default: true)
366
+ - `logging` (boolean): Enable console logging output (default: true)
367
+ - `environment` (object): Additional environment variables
368
+
369
+ **Example:**
370
+
371
+ ```javascript
372
+ const client = new TestDriver(process.env.TD_API_KEY, {
373
+ resolution: "1920x1080",
374
+ analytics: false,
375
+ logging: true, // See detailed logs
376
+ });
377
+ ```
378
+
379
+ ### Connection Methods
380
+
381
+ #### `auth()`
382
+
383
+ Authenticates with the TestDriver API.
384
+
385
+ **Returns:** `Promise<string>` - Authentication token
386
+
387
+ **Example:**
388
+
389
+ ```javascript
390
+ await client.auth();
391
+ ```
392
+
393
+ #### `connect(options)`
394
+
395
+ Connects to a sandbox environment.
396
+
397
+ **Parameters:**
398
+
399
+ - `options` (object, optional):
400
+ - `sandboxId` (string): Reconnect to existing sandbox
401
+ - `newSandbox` (boolean): Force creation of new sandbox
402
+ - `ip` (string): Direct IP connection
403
+ - `sandboxAmi` (string): Custom AMI for sandbox
404
+ - `sandboxInstance` (string): Instance type
405
+ - `headless` (boolean): Disable browser window rendering (default: false)
406
+
407
+ **Returns:** `Promise<Object>` - Sandbox instance details
408
+
409
+ **Examples:**
410
+
411
+ ```javascript
412
+ // Create new sandbox (opens browser window by default)
413
+ await client.connect({ newSandbox: true });
414
+
415
+ // Create sandbox without opening browser window
416
+ await client.connect({ newSandbox: true, headless: true });
417
+
418
+ // Reconnect to existing sandbox
419
+ await client.connect({ sandboxId: "i-1234567890abcdef0" });
420
+
421
+ // Direct IP connection
422
+ await client.connect({ ip: "192.168.1.100" });
423
+ ```
424
+
425
+ **Note:** By default, the SDK will automatically open a browser window showing the live sandbox environment, similar to the CLI behavior. This allows you to watch test execution in real-time. Set `headless: true` to disable this feature.
426
+
427
+ #### `disconnect()`
428
+
429
+ Disconnects from the sandbox.
430
+
431
+ **Returns:** `Promise<void>`
432
+
433
+ ### Text Interaction Methods
434
+
435
+ #### `hoverText(text, description, action, method, timeout)`
436
+
437
+ Finds and hovers over text on screen.
438
+
439
+ **Parameters:**
440
+
441
+ - `text` (string): Text to find
442
+ - `description` (string, optional): Additional context
443
+ - `action` (string): Action type (default: 'click')
444
+ - `method` (string): Match method - 'turbo', 'leven', or 'dice' (default: 'turbo')
445
+ - `timeout` (number): Timeout in ms (default: 5000)
446
+
447
+ **Returns:** `Promise<Object>` - Match result with coordinates
448
+
449
+ **Example:**
450
+
451
+ ```javascript
452
+ const result = await client.hoverText("Submit", "the submit button");
453
+ console.log(result); // { x: 150, y: 200, ... }
454
+ ```
455
+
456
+ #### `type(text, delay)`
457
+
458
+ Types text with optional delay between keystrokes.
459
+
460
+ **Parameters:**
461
+
462
+ - `text` (string): Text to type
463
+ - `delay` (number): Delay in ms between keystrokes (default: 250)
464
+
465
+ **Example:**
466
+
467
+ ```javascript
468
+ await client.type("hello@example.com", 100);
469
+ ```
470
+
471
+ #### `waitForText(text, timeout, method, invert)`
472
+
473
+ Waits for text to appear on screen.
474
+
475
+ **Parameters:**
476
+
477
+ - `text` (string): Text to wait for
478
+ - `timeout` (number): Timeout in ms (default: 5000)
479
+ - `method` (string): Match method (default: 'turbo')
480
+ - `invert` (boolean): Wait for text to disappear (default: false)
481
+
482
+ **Example:**
483
+
484
+ ```javascript
485
+ await client.waitForText("Success!", 10000);
486
+ ```
487
+
488
+ #### `scrollUntilText(text, direction, maxDistance, textMatchMethod, method, invert)`
489
+
490
+ Scrolls until text is found.
491
+
492
+ **Parameters:**
493
+
494
+ - `text` (string): Text to find
495
+ - `direction` (string): 'up' or 'down' (default: 'down')
496
+ - `maxDistance` (number): Max pixels to scroll (default: 10000)
497
+ - `textMatchMethod` (string): Text matching method (default: 'turbo')
498
+ - `method` (string): Scroll method - 'mouse' or 'keyboard' (default: 'keyboard')
499
+ - `invert` (boolean): Invert match (default: false)
500
+
501
+ **Example:**
502
+
503
+ ```javascript
504
+ await client.scrollUntilText("Terms of Service", "down", 5000);
505
+ ```
506
+
507
+ ### Image Interaction Methods
508
+
509
+ #### `hoverImage(description, action)`
510
+
511
+ Finds and hovers over an image matching the description.
512
+
513
+ **Parameters:**
514
+
515
+ - `description` (string): Description of the image
516
+ - `action` (string): Action type (default: 'click')
517
+
518
+ **Returns:** `Promise<Object>` - Match result
519
+
520
+ **Example:**
521
+
522
+ ```javascript
523
+ await client.hoverImage("the red submit button");
524
+ ```
525
+
526
+ #### `matchImage(imagePath, action, invert)`
527
+
528
+ Finds and interacts with an image using template matching.
529
+
530
+ **Parameters:**
531
+
532
+ - `imagePath` (string): Path to template image
533
+ - `action` (string): 'click' or 'hover' (default: 'click')
534
+ - `invert` (boolean): Invert match (default: false)
535
+
536
+ **Example:**
537
+
538
+ ```javascript
539
+ await client.matchImage("./templates/login-button.png", "click");
540
+ ```
541
+
542
+ #### `waitForImage(description, timeout, invert)`
543
+
544
+ Waits for an image to appear on screen.
545
+
546
+ **Parameters:**
547
+
548
+ - `description` (string): Description of the image
549
+ - `timeout` (number): Timeout in ms (default: 10000)
550
+ - `invert` (boolean): Wait for image to disappear (default: false)
551
+
552
+ **Example:**
553
+
554
+ ```javascript
555
+ await client.waitForImage("loading spinner", 5000, true); // Wait for spinner to disappear
556
+ ```
557
+
558
+ #### `scrollUntilImage(description, direction, maxDistance, method, path, invert)`
559
+
560
+ Scrolls until an image is found.
561
+
562
+ **Parameters:**
563
+
564
+ - `description` (string): Description of image (use either this or path)
565
+ - `direction` (string): 'up' or 'down' (default: 'down')
566
+ - `maxDistance` (number): Max pixels to scroll (default: 10000)
567
+ - `method` (string): Scroll method (default: 'keyboard')
568
+ - `path` (string): Path to template image
569
+ - `invert` (boolean): Invert match (default: false)
570
+
571
+ **Example:**
572
+
573
+ ```javascript
574
+ await client.scrollUntilImage("footer logo", "down", 10000);
575
+ ```
576
+
577
+ ### Mouse & Keyboard Methods
578
+
579
+ #### `click(x, y, action)`
580
+
581
+ Clicks at specific coordinates.
582
+
583
+ **Parameters:**
584
+
585
+ - `x` (number): X coordinate
586
+ - `y` (number): Y coordinate
587
+ - `action` (string): Click type - 'click', 'right-click', 'double-click', 'middle-click', 'drag-start', 'drag-end' (default: 'click')
588
+
589
+ **Example:**
590
+
591
+ ```javascript
592
+ await client.click(500, 300, "double-click");
593
+ ```
594
+
595
+ #### `hover(x, y)`
596
+
597
+ Moves mouse to coordinates.
598
+
599
+ **Parameters:**
600
+
601
+ - `x` (number): X coordinate
602
+ - `y` (number): Y coordinate
603
+
604
+ **Example:**
605
+
606
+ ```javascript
607
+ await client.hover(200, 150);
608
+ ```
609
+
610
+ #### `pressKeys(keys)`
611
+
612
+ Presses keyboard keys (supports combinations).
613
+
614
+ **Parameters:**
615
+
616
+ - `keys` (Array<string>): Array of keys to press
617
+
618
+ **Example:**
619
+
620
+ ```javascript
621
+ // Single key
622
+ await client.pressKeys(["enter"]);
623
+
624
+ // Key combination
625
+ await client.pressKeys(["ctrl", "c"]);
626
+
627
+ // Multiple keys in sequence
628
+ await client.pressKeys(["tab", "tab", "enter"]);
629
+ ```
630
+
631
+ #### `scroll(direction, amount, method)`
632
+
633
+ Scrolls the page.
634
+
635
+ **Parameters:**
636
+
637
+ - `direction` (string): 'up' or 'down' (default: 'down')
638
+ - `amount` (number): Pixels to scroll (default: 300)
639
+ - `method` (string): 'mouse' or 'keyboard' (default: 'mouse')
640
+
641
+ **Example:**
642
+
643
+ ```javascript
644
+ await client.scroll("down", 500, "mouse");
645
+ ```
646
+
647
+ ### Application Control
648
+
649
+ #### `focusApplication(name)`
650
+
651
+ Focuses an application by name.
652
+
653
+ **Parameters:**
654
+
655
+ - `name` (string): Application name
656
+
657
+ **Example:**
658
+
659
+ ```javascript
660
+ await client.focusApplication("Google Chrome");
661
+ ```
662
+
663
+ ### AI-Powered Methods
664
+
665
+ #### `assert(assertion, async, invert)`
666
+
667
+ Makes an AI-powered assertion about the screen state.
668
+
669
+ **Parameters:**
670
+
671
+ - `assertion` (string): Natural language assertion
672
+ - `async` (boolean): Run asynchronously (default: false)
673
+ - `invert` (boolean): Invert the assertion (default: false)
674
+
675
+ **Example:**
676
+
677
+ ```javascript
678
+ await client.assert("The login form is visible");
679
+ await client.assert("The page is showing an error message");
680
+ ```
681
+
682
+ #### `remember(description)`
683
+
684
+ Extracts and remembers information from the screen.
685
+
686
+ **Parameters:**
687
+
688
+ - `description` (string): What to remember
689
+
690
+ **Returns:** `Promise<string>` - Extracted information
691
+
692
+ **Example:**
693
+
694
+ ```javascript
695
+ const email = await client.remember(
696
+ "What is the user email shown on the profile page?",
697
+ );
698
+ console.log(email); // "user@example.com"
699
+ ```
700
+
701
+ ### Code Execution
702
+
703
+ #### `exec(language, code, timeout, silent)`
704
+
705
+ Executes code in the sandbox.
706
+
707
+ **Parameters:**
708
+
709
+ - `language` (string): 'js' or 'pwsh'
710
+ - `code` (string): Code to execute
711
+ - `timeout` (number): Timeout in ms
712
+ - `silent` (boolean): Suppress output (default: false)
713
+
714
+ **Returns:** `Promise<string>` - Execution result
715
+
716
+ **Example:**
717
+
718
+ ```javascript
719
+ // JavaScript
720
+ const result = await client.exec(
721
+ "js",
722
+ `
723
+ result = { timestamp: Date.now(), platform: process.platform };
724
+ `,
725
+ 5000,
726
+ );
727
+
728
+ // PowerShell
729
+ const output = await client.exec(
730
+ "pwsh",
731
+ "Get-Process | Select-Object -First 5",
732
+ 10000,
733
+ );
734
+ ```
735
+
736
+ ### Utility Methods
737
+
738
+ #### `screenshot([scale], [silent], [mouse])`
739
+
740
+ Captures a screenshot of the current screen in the sandbox.
741
+
742
+ **Parameters:**
743
+
744
+ - `scale` (number, optional): Scale factor for the screenshot (default: 1 = original size)
745
+ - `silent` (boolean, optional): Whether to suppress logging (default: false)
746
+ - `mouse` (boolean, optional): Whether to include mouse cursor (default: false)
747
+
748
+ **Returns:** `Promise<string>` - Base64 encoded PNG screenshot
749
+
750
+ **Example:**
751
+
752
+ ```javascript
753
+ // Capture a screenshot
754
+ const screenshot = await client.screenshot();
755
+
756
+ // Save to file
757
+ const fs = require("fs");
758
+ fs.writeFileSync("screenshot.png", Buffer.from(screenshot, "base64"));
759
+
760
+ // Capture with mouse cursor visible
761
+ const screenshotWithMouse = await client.screenshot(1, false, true);
762
+ fs.writeFileSync(
763
+ "screenshot-with-mouse.png",
764
+ Buffer.from(screenshotWithMouse, "base64"),
765
+ );
766
+ ```
767
+
768
+ #### `wait(timeout)`
769
+
770
+ Waits for specified time.
771
+
772
+ **Parameters:**
773
+
774
+ - `timeout` (number): Time in ms (default: 3000)
775
+
776
+ **Example:**
777
+
778
+ ```javascript
779
+ await client.wait(2000); // Wait 2 seconds
780
+ ```
781
+
782
+ #### `getInstance()`
783
+
784
+ Gets the current sandbox instance details.
785
+
786
+ **Returns:** `Object|null` - Sandbox instance
787
+
788
+ #### `getSessionId()`
789
+
790
+ Gets the current session ID.
791
+
792
+ **Returns:** `string|null` - Session ID
793
+
794
+ #### `setLogging(enabled)`
795
+
796
+ Enable or disable console logging output.
797
+
798
+ **Parameters:**
799
+
800
+ - `enabled` (boolean): Whether to enable logging
801
+
802
+ **Example:**
803
+
804
+ ```javascript
805
+ // Disable logging
806
+ client.setLogging(false);
807
+
808
+ // Enable logging
809
+ client.setLogging(true);
810
+ ```
811
+
812
+ #### `getEmitter()`
813
+
814
+ Gets the event emitter for custom event handling.
815
+
816
+ **Returns:** `EventEmitter2` - Event emitter instance
817
+
818
+ **Example:**
819
+
820
+ ```javascript
821
+ const emitter = client.getEmitter();
822
+
823
+ // Listen to all log events
824
+ emitter.on("log:*", (message) => {
825
+ console.log("Log:", message);
826
+ });
827
+
828
+ // Listen to error events
829
+ emitter.on("error:*", (data) => {
830
+ console.error("Error:", data);
831
+ });
832
+
833
+ // Listen to command events
834
+ emitter.on("command:start", (data) => {
835
+ console.log("Command started:", data.command);
836
+ });
837
+
838
+ emitter.on("command:success", (data) => {
839
+ console.log(
840
+ "Command succeeded:",
841
+ data.command,
842
+ "Duration:",
843
+ data.duration,
844
+ "ms",
845
+ );
846
+ });
847
+ ```
848
+
849
+ ## Events
850
+
851
+ The SDK emits various events that you can listen to for detailed execution information:
852
+
853
+ ### Log Events
854
+
855
+ - `log:log` - General log messages
856
+ - `log:warn` - Warning messages
857
+ - `log:debug` - Debug messages
858
+ - `log:narration` - Narration text (e.g., "thinking...")
859
+ - `log:markdown:start` - Markdown streaming started
860
+ - `log:markdown:chunk` - Markdown chunk received
861
+ - `log:markdown:end` - Markdown streaming ended
862
+ - `log:markdown` - Static markdown content
863
+
864
+ ### Command Events
865
+
866
+ - `command:start` - Command execution started
867
+ - `command:success` - Command completed successfully
868
+ - `command:error` - Command failed
869
+
870
+ ### Error Events
871
+
872
+ - `error:fatal` - Fatal error occurred
873
+ - `error:general` - General error
874
+ - `error:sdk` - SDK-related error
875
+ - `error:sandbox` - Sandbox-related error
876
+
877
+ ### Sandbox Events
878
+
879
+ - `sandbox:connected` - Connected to sandbox
880
+ - `sandbox:authenticated` - Authenticated with sandbox
881
+ - `sandbox:error` - Sandbox error
882
+ - `sandbox:disconnected` - Disconnected from sandbox
883
+
884
+ ### Other Events
885
+
886
+ - `status` - Status update
887
+ - `mouse-click` - Mouse click occurred
888
+ - `mouse-move` - Mouse moved
889
+ - `matches:show` - Match results available
890
+
891
+ **Example: Custom Event Handling**
892
+
893
+ ```javascript
894
+ const TestDriver = require("testdriverai");
895
+
896
+ const client = new TestDriver(process.env.TD_API_KEY, {
897
+ logging: false, // Disable default logging
898
+ });
899
+
900
+ const emitter = client.getEmitter();
901
+
902
+ // Custom logging
903
+ emitter.on("log:*", (message) => {
904
+ const timestamp = new Date().toISOString();
905
+ console.log(`[${timestamp}] ${message}`);
906
+ });
907
+
908
+ // Track command performance
909
+ const commandTimes = {};
910
+ emitter.on("command:start", (data) => {
911
+ commandTimes[data.command] = Date.now();
912
+ });
913
+
914
+ emitter.on("command:success", (data) => {
915
+ const duration = Date.now() - commandTimes[data.command];
916
+ console.log(`✓ ${data.command} completed in ${duration}ms`);
917
+ });
918
+
919
+ emitter.on("command:error", (data) => {
920
+ console.error(`✗ ${data.command} failed: ${data.error}`);
921
+ });
922
+
923
+ await client.auth();
924
+ await client.connect();
925
+ await client.hoverText("Submit");
926
+ ```
927
+
928
+ ## Complete Example
929
+
930
+ ```javascript
931
+ const TestDriver = require("testdriverai");
932
+
933
+ async function testLoginFlow() {
934
+ const client = new TestDriver(process.env.TD_API_KEY);
935
+
936
+ try {
937
+ // Setup
938
+ await client.auth();
939
+ await client.connect({ newSandbox: true });
940
+
941
+ // Open browser and navigate
942
+ await client.focusApplication("Google Chrome");
943
+ await client.wait(1000);
944
+
945
+ // Type URL and navigate
946
+ await client.type("https://example.com/login");
947
+ await client.pressKeys(["enter"]);
948
+ await client.waitForText("Login", 5000);
949
+
950
+ // Fill login form
951
+ await client.hoverText("Email");
952
+ await client.type("test@example.com");
953
+ await client.pressKeys(["tab"]);
954
+ await client.type("password123");
955
+
956
+ // Submit form
957
+ await client.hoverText("Sign In");
958
+ await client.pressKeys(["enter"]);
959
+
960
+ // Verify login
961
+ await client.waitForText("Dashboard", 10000);
962
+ await client.assert("User is logged in successfully");
963
+
964
+ // Get user info
965
+ const username = await client.remember(
966
+ "What is the username displayed in the header?",
967
+ );
968
+ console.log("Logged in as:", username);
969
+ } catch (error) {
970
+ console.error("Test failed:", error);
971
+ throw error;
972
+ } finally {
973
+ await client.disconnect();
974
+ }
975
+ }
976
+
977
+ testLoginFlow();
978
+ ```
979
+
980
+ ## Environment Variables
981
+
982
+ - `TD_API_KEY`: Your TestDriver API key (required)
983
+ - `TD_API_ROOT`: API endpoint (optional, default: https://v6.testdriver.ai)
984
+ - `TD_RESOLUTION`: Sandbox resolution (optional, default: 1366x768)
985
+ - `TD_ANALYTICS`: Enable analytics (optional, default: true)
986
+ - `VERBOSE` / `DEBUG` / `TD_DEBUG`: Enable verbose debug output including cache information
987
+
988
+ ## Configuration Options
989
+
990
+ ### Cache Thresholds
991
+
992
+ Configure cache sensitivity for element finding operations. Lower thresholds require higher similarity for cache hits.
993
+
994
+ **Global Configuration:**
995
+
996
+ ```javascript
997
+ const client = new TestDriver(process.env.TD_API_KEY, {
998
+ cacheThreshold: {
999
+ find: 0.03, // 3% difference = 97% similarity required (stricter)
1000
+ findAll: 0.05, // 5% difference = 95% similarity required (default)
1001
+ },
1002
+ });
1003
+ ```
1004
+
1005
+ **Disable Cache Globally:**
1006
+
1007
+ ```javascript
1008
+ // Force all find operations to regenerate (never use cache)
1009
+ const client = new TestDriver(process.env.TD_API_KEY, {
1010
+ cache: false,
1011
+ });
1012
+ ```
1013
+
1014
+ **Per-Command Configuration:**
1015
+
1016
+ ```javascript
1017
+ // Override cache threshold for a specific find
1018
+ const element = await client.find("login button", 0.01); // 99% similarity required
1019
+
1020
+ // Override cache threshold for a specific findAll
1021
+ const items = await client.findAll("list items", 0.1); // 90% similarity required
1022
+
1023
+ // Disable cache for a specific find (always regenerate)
1024
+ const element = await client.find("login button", -1);
1025
+ ```
1026
+
1027
+ **Cache Threshold Values:**
1028
+
1029
+ - `0.01` - Very strict (99% similarity required)
1030
+ - `0.03` - Strict (97% similarity required)
1031
+ - `0.05` - Default (95% similarity required)
1032
+ - `0.10` - Relaxed (90% similarity required)
1033
+
1034
+ ### Debugging Cache Behavior
1035
+
1036
+ Enable verbose output to see cache hit/miss information:
1037
+
1038
+ ```bash
1039
+ VERBOSE=true node your-test.js
1040
+ ```
1041
+
1042
+ Debug output includes:
1043
+
1044
+ - Cache hit/miss status
1045
+ - Cache strategy used (image/text)
1046
+ - Similarity scores for cache matches
1047
+ - Response times
1048
+ - Debug images with element highlights
1049
+
1050
+ Example debug output:
1051
+
1052
+ ```
1053
+ 🔍 Element Found:
1054
+ Description: login button
1055
+ Coordinates: (523, 345)
1056
+ Duration: 1234ms
1057
+ Cache Hit: ✅ YES
1058
+ Cache Strategy: text
1059
+ Similarity: 98.50%
1060
+ Confidence: 95.20%
1061
+ Debug Image: /tmp/testdriver-debug/element-found-1234567890.png
1062
+ ```
1063
+
1064
+ ## Error Handling
1065
+
1066
+ The SDK throws errors when operations fail. Always wrap your code in try-catch blocks:
1067
+
1068
+ ```javascript
1069
+ try {
1070
+ await client.hoverText("Submit");
1071
+ } catch (error) {
1072
+ console.error("Failed to find Submit button:", error.message);
1073
+ // Handle error appropriately
1074
+ }
1075
+ ```
1076
+
1077
+ ## Best Practices
1078
+
1079
+ 1. **Always authenticate and connect before running commands**
1080
+
1081
+ ```javascript
1082
+ await client.auth();
1083
+ await client.connect();
1084
+ ```
1085
+
1086
+ 2. **Use appropriate timeouts for slow operations**
1087
+
1088
+ ```javascript
1089
+ await client.waitForText("Loading...", 30000); // 30 second timeout
1090
+ ```
1091
+
1092
+ 3. **Clean up after tests**
1093
+
1094
+ ```javascript
1095
+ try {
1096
+ // Your test code
1097
+ } finally {
1098
+ await client.disconnect();
1099
+ }
1100
+ ```
1101
+
1102
+ 4. **Use natural language assertions for validation**
1103
+
1104
+ ```javascript
1105
+ await client.assert("The form was submitted successfully");
1106
+ ```
1107
+
1108
+ 5. **Add waits between actions to let UI settle**
1109
+ ```javascript
1110
+ await client.click(100, 200);
1111
+ await client.wait(1000); // Let UI respond
1112
+ ```
1113
+
1114
+ ## Support
1115
+
1116
+ - Documentation: https://docs.testdriver.ai
1117
+ - Discord: https://discord.com/invite/cWDFW8DzPm
1118
+ - GitHub: https://github.com/testdriverai/cli
1119
+
1120
+ ## License
1121
+
1122
+ ISC