@testdriverai/mcp 7.10.0-canary.0 → 7.10.1-test

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 (196) hide show
  1. package/.claude-plugin/marketplace.json +17 -0
  2. package/agent/index.js +116 -71
  3. package/agent/interface.js +13 -1
  4. package/agent/lib/commands.js +35 -15
  5. package/agent/lib/provision-commands.js +92 -11
  6. package/agent/lib/sandbox.js +263 -30
  7. package/agent/lib/sdk.js +16 -12
  8. package/agent/lib/system.js +107 -31
  9. package/ai/.claude-plugin/plugin.json +89 -4
  10. package/ai/agents/testdriver.md +4 -8
  11. package/ai/skills/testdriver-assert/SKILL.md +0 -1
  12. package/ai/skills/testdriver-aws-setup/SKILL.md +1 -1
  13. package/ai/skills/testdriver-caching/SKILL.md +13 -6
  14. package/ai/skills/testdriver-ci-cd/SKILL.md +57 -14
  15. package/ai/skills/testdriver-client/SKILL.md +10 -6
  16. package/ai/skills/testdriver-customizing-devices/SKILL.md +7 -5
  17. package/ai/skills/testdriver-dashcam/SKILL.md +36 -3
  18. package/ai/skills/testdriver-debugging-with-screenshots/SKILL.md +28 -4
  19. package/ai/skills/testdriver-device-config/SKILL.md +5 -5
  20. package/ai/skills/testdriver-find/SKILL.md +50 -11
  21. package/ai/skills/testdriver-generating-tests/SKILL.md +163 -8
  22. package/ai/skills/testdriver-hosted/SKILL.md +1 -1
  23. package/ai/skills/testdriver-machine-setup/SKILL.md +331 -0
  24. package/ai/skills/testdriver-making-assertions/SKILL.md +78 -2
  25. package/ai/skills/testdriver-mcp-workflow/SKILL.md +2 -2
  26. package/ai/skills/testdriver-parse/SKILL.md +1 -1
  27. package/ai/skills/testdriver-performing-actions/SKILL.md +97 -3
  28. package/ai/skills/testdriver-provision/SKILL.md +0 -20
  29. package/ai/skills/testdriver-quickstart/SKILL.md +299 -21
  30. package/ai/skills/testdriver-reusable-code/SKILL.md +3 -3
  31. package/ai/skills/testdriver-running-tests/SKILL.md +1 -1
  32. package/ai/skills/testdriver-screenshot/SKILL.md +5 -5
  33. package/ai/skills/testdriver-scroll/SKILL.md +30 -160
  34. package/ai/skills/testdriver-test-writer/SKILL.md +3 -5
  35. package/ai/skills/testdriver-testdriver/SKILL.md +3 -7
  36. package/ai/skills/testdriver-wait/SKILL.md +1 -1
  37. package/{docs/v7/ai.mdx → ai/skills/testdriver:ai/SKILL.md} +3 -4
  38. package/ai/skills/testdriver:assert/SKILL.md +315 -0
  39. package/ai/skills/testdriver:aws-setup/SKILL.md +448 -0
  40. package/ai/skills/testdriver:cache/SKILL.md +221 -0
  41. package/ai/skills/testdriver:caching/SKILL.md +124 -0
  42. package/ai/skills/testdriver:captcha/SKILL.md +158 -0
  43. package/ai/skills/testdriver:ci-cd/SKILL.md +659 -0
  44. package/ai/skills/testdriver:click/SKILL.md +286 -0
  45. package/ai/skills/testdriver:client/SKILL.md +477 -0
  46. package/ai/skills/testdriver:customizing-devices/SKILL.md +319 -0
  47. package/ai/skills/testdriver:dashcam/SKILL.md +451 -0
  48. package/ai/skills/testdriver:debugging-with-screenshots/SKILL.md +415 -0
  49. package/{docs/v7/device-config.mdx → ai/skills/testdriver:device-config/SKILL.md} +8 -8
  50. package/ai/skills/testdriver:double-click/SKILL.md +102 -0
  51. package/ai/skills/testdriver:elements/SKILL.md +605 -0
  52. package/ai/skills/testdriver:enterprise/SKILL.md +7 -0
  53. package/ai/skills/testdriver:errors/SKILL.md +246 -0
  54. package/ai/skills/testdriver:events/SKILL.md +356 -0
  55. package/ai/skills/testdriver:exec/SKILL.md +317 -0
  56. package/ai/skills/testdriver:find/SKILL.md +860 -0
  57. package/ai/skills/testdriver:focus-application/SKILL.md +293 -0
  58. package/ai/skills/testdriver:generating-tests/SKILL.md +25 -0
  59. package/ai/skills/testdriver:hosted/SKILL.md +156 -0
  60. package/ai/skills/testdriver:hover/SKILL.md +278 -0
  61. package/{docs/v7/locating-elements.mdx → ai/skills/testdriver:locating-elements/SKILL.md} +3 -3
  62. package/ai/skills/testdriver:making-assertions/SKILL.md +32 -0
  63. package/ai/skills/testdriver:mcp/SKILL.md +7 -0
  64. package/ai/skills/testdriver:mouse-down/SKILL.md +161 -0
  65. package/ai/skills/testdriver:mouse-up/SKILL.md +164 -0
  66. package/ai/skills/testdriver:parse/SKILL.md +236 -0
  67. package/ai/skills/testdriver:performing-actions/SKILL.md +53 -0
  68. package/ai/skills/testdriver:press-keys/SKILL.md +348 -0
  69. package/ai/skills/testdriver:provision/SKILL.md +331 -0
  70. package/ai/skills/testdriver:quickstart/SKILL.md +172 -0
  71. package/ai/skills/testdriver:redraw/SKILL.md +214 -0
  72. package/ai/skills/testdriver:reusable-code/SKILL.md +249 -0
  73. package/ai/skills/testdriver:right-click/SKILL.md +123 -0
  74. package/{docs/v7/running-tests.mdx → ai/skills/testdriver:running-tests/SKILL.md} +4 -4
  75. package/ai/skills/testdriver:screenshot/SKILL.md +248 -0
  76. package/ai/skills/testdriver:screenshots/SKILL.md +184 -0
  77. package/ai/skills/testdriver:scroll/SKILL.md +205 -0
  78. package/ai/skills/testdriver:secrets/SKILL.md +115 -0
  79. package/ai/skills/testdriver:self-hosted/SKILL.md +147 -0
  80. package/ai/skills/testdriver:test-results-json/SKILL.md +257 -0
  81. package/ai/skills/testdriver:testdriver/SKILL.md +624 -0
  82. package/ai/skills/testdriver:type/SKILL.md +357 -0
  83. package/ai/skills/testdriver:variables/SKILL.md +111 -0
  84. package/ai/skills/testdriver:wait/SKILL.md +50 -0
  85. package/{docs/v7/waiting-for-elements.mdx → ai/skills/testdriver:waiting-for-elements/SKILL.md} +3 -3
  86. package/ai/skills/testdriver:what-is-testdriver/SKILL.md +54 -0
  87. package/debugger/index.html +12 -0
  88. package/docs/_data/examples-manifest.json +54 -46
  89. package/docs/_data/examples-manifest.schema.json +12 -2
  90. package/docs/_scripts/extract-example-urls.js +21 -3
  91. package/docs/_scripts/generate-examples.js +64 -16
  92. package/docs/changelog.mdx +29 -2
  93. package/docs/claude-mcp-plugin.mdx +65 -18
  94. package/docs/docs.json +16 -33
  95. package/docs/snippets/tests/scroll-yaml.mdx +2 -2
  96. package/docs/v6/commands/scroll-until-image.mdx +1 -1
  97. package/docs/v6/commands/scroll-until-text.mdx +2 -2
  98. package/docs/v6/commands/scroll.mdx +2 -2
  99. package/docs/v7/_drafts/agents.mdx +0 -4
  100. package/docs/v7/_drafts/best-practices.mdx +0 -12
  101. package/docs/v7/_drafts/commands/scroll-until-image.mdx +1 -1
  102. package/docs/v7/_drafts/commands/scroll-until-text.mdx +2 -2
  103. package/docs/v7/_drafts/commands/scroll.mdx +1 -1
  104. package/docs/v7/_drafts/configuration.mdx +0 -12
  105. package/docs/v7/_drafts/faq.mdx +2 -10
  106. package/docs/v7/_drafts/readme.mdx +0 -1
  107. package/docs/v7/_drafts/troubleshooting.mdx +1 -4
  108. package/docs/v7/assert.mdx +0 -1
  109. package/docs/v7/aws-setup.mdx +1 -1
  110. package/docs/v7/caching.mdx +14 -10
  111. package/docs/v7/ci-cd.mdx +98 -14
  112. package/docs/v7/client.mdx +10 -6
  113. package/docs/v7/copilot/auto-healing.mdx +167 -18
  114. package/docs/v7/copilot/running-tests.mdx +915 -54
  115. package/docs/v7/customizing-devices.mdx +7 -5
  116. package/docs/v7/dashcam.mdx +36 -3
  117. package/docs/v7/debugging-with-screenshots.mdx +31 -7
  118. package/docs/v7/examples/ai.mdx +13 -8
  119. package/docs/v7/examples/assert.mdx +1 -1
  120. package/docs/v7/examples/chrome-extension.mdx +30 -13
  121. package/docs/v7/examples/element-not-found.mdx +1 -1
  122. package/docs/v7/examples/findall-coffee-icons.mdx +3 -2
  123. package/docs/v7/examples/formatted-logging.mdx +74 -0
  124. package/docs/v7/examples/hover-image.mdx +1 -1
  125. package/docs/v7/examples/hover-text-with-description.mdx +2 -2
  126. package/docs/v7/examples/hover-text.mdx +1 -1
  127. package/docs/v7/examples/installer.mdx +1 -1
  128. package/docs/v7/examples/launch-vscode-linux.mdx +27 -27
  129. package/docs/v7/examples/match-image.mdx +1 -1
  130. package/docs/v7/examples/parse.mdx +3 -2
  131. package/docs/v7/examples/press-keys.mdx +2 -2
  132. package/docs/v7/examples/scroll-keyboard.mdx +2 -2
  133. package/docs/v7/examples/scroll-until-text.mdx +1 -1
  134. package/docs/v7/examples/scroll.mdx +1 -1
  135. package/docs/v7/examples/type.mdx +1 -1
  136. package/docs/v7/find.mdx +50 -11
  137. package/docs/v7/generating-tests.mdx +166 -10
  138. package/docs/v7/hosted.mdx +1 -1
  139. package/docs/v7/making-assertions.mdx +81 -4
  140. package/docs/v7/parse.mdx +1 -1
  141. package/docs/v7/performing-actions.mdx +100 -5
  142. package/docs/v7/provision.mdx +0 -20
  143. package/docs/v7/quickstart.mdx +299 -21
  144. package/docs/v7/reusable-code.mdx +3 -3
  145. package/docs/v7/screenshot.mdx +5 -5
  146. package/docs/v7/scroll.mdx +30 -160
  147. package/docs/v7/wait.mdx +1 -1
  148. package/examples/chrome-extension.test.mjs +29 -12
  149. package/examples/config.mjs +1 -1
  150. package/examples/launch-vscode-linux.test.mjs +25 -25
  151. package/interfaces/cli/commands/init.js +84 -2
  152. package/interfaces/vitest-plugin.mjs +51 -61
  153. package/lib/core/Dashcam.js +43 -2
  154. package/lib/core/index.d.ts +13 -1
  155. package/lib/github-comment.mjs +11 -5
  156. package/lib/init-project.js +98 -44
  157. package/lib/install-clients.js +470 -0
  158. package/lib/provision.js +749 -0
  159. package/lib/resolve-channel.js +2 -2
  160. package/lib/sentry.js +15 -2
  161. package/lib/vitest/hooks.mjs +44 -34
  162. package/mcp-server/README.md +32 -0
  163. package/mcp-server/dist/core/actions.d.ts +176 -0
  164. package/mcp-server/dist/core/actions.js +753 -0
  165. package/mcp-server/dist/env-utils.d.ts +45 -0
  166. package/mcp-server/dist/env-utils.js +63 -0
  167. package/mcp-server/dist/provision-types.d.ts +6 -2
  168. package/mcp-server/dist/provision-types.js +3 -1
  169. package/mcp-server/dist/server.mjs +449 -751
  170. package/mcp-server/package-lock.json +384 -1
  171. package/mcp-server/package.json +5 -2
  172. package/mcp-server/src/core/actions.ts +912 -0
  173. package/mcp-server/src/env-utils.test.ts +82 -0
  174. package/mcp-server/src/env-utils.ts +77 -0
  175. package/mcp-server/src/provision-types.ts +4 -1
  176. package/mcp-server/src/server.ts +533 -954
  177. package/mcp-server/tsconfig.json +1 -1
  178. package/mcp-server/vitest.config.ts +7 -0
  179. package/package.json +15 -3
  180. package/sdk.d.ts +11 -9
  181. package/sdk.js +116 -746
  182. package/setup/aws/spawn-runner.sh +24 -3
  183. package/docs/v7/_drafts/caching-ai.mdx +0 -215
  184. package/docs/v7/_drafts/caching.mdx +0 -366
  185. package/docs/v7/_drafts/prompt-cache.mdx +0 -200
  186. package/docs/v7/copilot/creating-tests.mdx +0 -156
  187. package/docs/v7/copilot/github.mdx +0 -143
  188. package/docs/v7/copilot/setup.mdx +0 -143
  189. package/docs/v7/examples/exec-output.mdx +0 -84
  190. package/docs/v7/examples/exec-pwsh.mdx +0 -82
  191. package/docs/v7/examples/focus-window.mdx +0 -61
  192. package/docs/v7/examples/scroll-until-image.mdx +0 -82
  193. package/docs/v7/examples/windows-installer.mdx +0 -95
  194. package/examples/formatted-logging.test.mjs +0 -27
  195. package/examples/match-image.test.mjs +0 -55
  196. package/manual/exec-stream-logs.test.mjs +0 -25
@@ -1,42 +1,781 @@
1
1
  ---
2
- title: "Running Tests"
3
- sidebarTitle: "Running Tests"
4
- description: "Re-run tests with Vitest and use GUI mode for visual debugging"
2
+ title: "Run"
3
+ sidebarTitle: "Run"
4
+ description: "Set up your environment and run TestDriver tests locally and in CI"
5
5
  icon: "play"
6
6
  ---
7
7
 
8
- After creating tests with the TestDriver agent, you can re-run them without starting a new MCP session. Tests are saved as standard Vitest files that run independently.
8
+ Run the tests you've explored and learned, anywhere. TestDriver tests are plain [Vitest](https://vitest.dev) files, so they run the same way on your machine and in CI — across web, desktop, and extensions on real VMs. This page walks through getting set up, configuring **where** and **how** your tests run (the device and app under test, and the sandbox machine), and actually running them locally and in CI.
9
9
 
10
- ## Running from VS Code (GUI Mode)
10
+ If you haven't written tests yet, start with [Explore](/v7/generating-tests) to generate your first tests and [Learn](/v7/caching) to make them fast and reliable.
11
11
 
12
- For a visual testing experience, use the **Vitest extension**:
12
+ ## Setup
13
+
14
+ TestDriver integrates with AI coding assistants through the VS Code extension and MCP server. The same MCP server works with GitHub Copilot, Cursor, Claude Desktop, and any other MCP-capable assistant. This section walks you through the complete setup.
15
+
16
+ ### Prerequisites
17
+
18
+ Before you begin, you'll need:
19
+
20
+ - **An MCP-capable AI assistant** — Such as GitHub Copilot, Cursor, or Claude Desktop. For Copilot, a [free tier](https://github.com/features/copilot/plans) is available.
21
+ - **TestDriver Account** — Create a free account at [console.testdriver.ai](https://console.testdriver.ai/team) to get your API key. 60 free device minutes, no credit card required.
22
+ - **VS Code** — The TestDriver extension provides the best experience with live preview and integrated test running.
23
+
24
+ ### Setup Steps
13
25
 
14
26
  <Steps>
15
- <Step title="Open Test Explorer">
16
- Click the **beaker icon** in the VS Code sidebar to open the Test Explorer. This shows all your test files and test cases.
27
+ <Step title="Install the TestDriver Extension">
28
+ <Card
29
+ horizontal
30
+ title="Install TestDriver for VS Code"
31
+ arrow
32
+ href="vscode:extension/testdriver.testdriver"
33
+ icon="/images/content/extension/vscode.svg"
34
+ ></Card>
35
+
36
+ The extension provides:
37
+ - One-click sign-in and project initialization
38
+ - Live preview panel for watching tests execute
39
+ - MCP server configuration
17
40
  </Step>
18
41
 
19
- <Step title="Run Tests">
20
- Click the **play button** next to any test file or individual test to run it. You can also:
21
- - Run all tests with the "Run All" button
22
- - Debug tests with the "Debug" button
42
+ <Step title="Sign Into TestDriver">
43
+ Sign in to connect your account and API key.
44
+
45
+ 1. Open the command palette (`Cmd+Shift+P` or `Ctrl+Shift+P`)
46
+ 2. Run **TestDriver: Login**
47
+ 3. Your browser will open to the TestDriver sign-in page
48
+ 4. Sign in (or create an account)
49
+ 5. You'll be redirected back to VS Code, now signed in
50
+
51
+ <Tip>
52
+ The extension automatically saves your API key to VS Code's secure storage and your workspace `.env` file.
53
+ </Tip>
23
54
  </Step>
24
55
 
25
- <Step title="View Results">
26
- Test results appear inline:
27
- - ✅ Green checkmark for passing tests
28
- - Red X for failing tests
29
- - Click on a failing test to see error details
56
+ <Step title="Initialize Your Project">
57
+ Set up TestDriver in your project with a single command.
58
+
59
+ 1. Open the command palette (`Cmd+Shift+P` or `Ctrl+Shift+P`)
60
+ 2. Run **TestDriver: Init Project**
61
+
62
+ This command:
63
+ - Creates a `package.json` with TestDriver and Vitest dependencies
64
+ - Generates a `vitest.config.mjs` with proper timeout settings
65
+ - Creates example test files in `tests/`
66
+ - Sets up `.env` with your API key
67
+ - Creates the TestDriver agent file at `.github/agents/testdriver.agent.md`
68
+ - Configures the MCP server
69
+
70
+ <Note>
71
+ If you already have a `package.json`, the command will add the necessary dependencies to it.
72
+ </Note>
73
+ </Step>
74
+
75
+ <Step title="Start the MCP Server">
76
+ The MCP server enables your AI assistant to control TestDriver sandboxes.
77
+
78
+ After initialization, the MCP configuration is created at `.vscode/mcp.json`:
79
+
80
+ ```json .vscode/mcp.json
81
+ {
82
+ "servers": {
83
+ "testdriver": {
84
+ "command": "npx",
85
+ "args": ["-p", "testdriverai", "testdriverai-mcp"],
86
+ "env": {
87
+ "TD_PREVIEW": "ide",
88
+ "TD_API_KEY": "your-api-key"
89
+ }
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ **To start the MCP server:**
96
+
97
+ 1. Open the command palette (`Cmd+Shift+P` or `Ctrl+Shift+P`)
98
+ 2. Run **MCP: List Servers**
99
+ 3. Click on the **testdriver** server
100
+ 4. Select **Start Server**
101
+
102
+ You can also click the MCP icon in the status bar to manage servers.
103
+
104
+ <Tip>
105
+ See the [VS Code MCP documentation](https://code.visualstudio.com/docs/copilot/chat/mcp-servers) for more details on managing MCP servers.
106
+ </Tip>
107
+
108
+ <Warning>
109
+ Make sure your API key is set. The extension uses the key from your sign-in, but you can also set it via the `TD_API_KEY` environment variable.
110
+ </Warning>
111
+ </Step>
112
+
113
+ <Step title="Install Vitest Extension (Recommended)">
114
+ For the best experience running tests, install the Vitest extension:
115
+
116
+ <Card
117
+ horizontal
118
+ title="Vitest Extension"
119
+ arrow
120
+ href="vscode:extension/vitest.explorer"
121
+ icon="flask-vial"
122
+ >
123
+ Run tests with GUI mode from the Test Explorer
124
+ </Card>
125
+
126
+ After installation, you'll see a beaker icon in the sidebar for accessing the Test Explorer.
30
127
  </Step>
31
128
  </Steps>
32
129
 
130
+ ### Verify Your Setup
131
+
132
+ To verify everything is configured correctly:
133
+
134
+ 1. Open the command palette and run **TestDriver: Check Status**
135
+ 2. You should see:
136
+ - ✅ Signed in
137
+ - ✅ MCP server configured
138
+ - ✅ Project initialized
139
+
140
+ ### The Agent File
141
+
142
+ During initialization, TestDriver creates an agent file at `.github/agents/testdriver.agent.md`. This file tells your AI assistant how to use TestDriver's MCP tools.
143
+
144
+ The agent has access to tools like:
145
+ - `session_start` — Launch a sandbox with Chrome or other apps
146
+ - `find` / `click` / `type` — Interact with elements on screen
147
+ - `assert` — Verify conditions using AI vision
148
+ - `screenshot` — Capture the current screen state
149
+
150
+ ## Configuring the Device
151
+
152
+ Provision methods are the starting point for most tests. They launch applications in your sandbox and prepare the environment for testing — a browser, a desktop app, a Chrome extension, or VS Code.
153
+
154
+ ### Chrome Browser
155
+
156
+ The most common starting point for web testing. Launches Chrome browser and navigates to a URL.
157
+
158
+ ```javascript
159
+ await testdriver.provision.chrome({
160
+ url: 'https://example.com',
161
+ });
162
+ ```
163
+
164
+ #### Options
165
+
166
+ | Option | Type | Default | Description |
167
+ |--------|------|---------|-------------|
168
+ | `url` | string | `'http://testdriver-sandbox.vercel.app/'` | URL to navigate to |
169
+ | `maximized` | boolean | `true` | Start browser maximized |
170
+ | `guest` | boolean | `false` | Use guest mode (no profile) |
171
+
172
+ #### Example: Basic Web Test
173
+
174
+ ```javascript
175
+ import { describe, expect, it } from "vitest";
176
+ import { TestDriver } from "testdriverai/vitest/hooks";
177
+
178
+ describe("Login Flow", () => {
179
+ it("should log in successfully", async (context) => {
180
+ const testdriver = TestDriver(context);
181
+
182
+ await testdriver.provision.chrome({
183
+ url: 'https://myapp.com/login',
184
+ });
185
+
186
+ await testdriver.find("Email input").click();
187
+ await testdriver.type("user@example.com");
188
+
189
+ await testdriver.find("Password input").click();
190
+ await testdriver.type("password123");
191
+
192
+ await testdriver.find("Sign In button").click();
193
+
194
+ const result = await testdriver.assert("the dashboard is visible");
195
+ expect(result).toBeTruthy();
196
+ });
197
+ });
198
+ ```
199
+
200
+ <Info>
201
+ `provision.chrome()` automatically starts Dashcam recording and waits for Chrome to be ready before returning.
202
+ </Info>
203
+
204
+ ### Chrome Extensions
205
+
206
+ Launch Chrome with a custom extension loaded. Supports both local extensions and Chrome Web Store extensions.
207
+
208
+ #### Load from Local Path
209
+
210
+ Clone or create an extension locally, then load it:
211
+
212
+ ```javascript
213
+ // First, get the extension onto the sandbox
214
+ await testdriver.exec(
215
+ 'sh',
216
+ 'git clone https://github.com/user/my-extension.git /tmp/my-extension',
217
+ 60000
218
+ );
219
+
220
+ // Launch Chrome with the extension
221
+ await testdriver.provision.chromeExtension({
222
+ extensionPath: '/tmp/my-extension',
223
+ url: 'https://example.com'
224
+ });
225
+ ```
226
+
227
+ #### Load from Chrome Web Store
228
+
229
+ Load any published extension by its Chrome Web Store ID:
230
+
231
+ ```javascript
232
+ await testdriver.provision.chromeExtension({
233
+ extensionId: 'cjpalhdlnbpafiamejdnhcphjbkeiagm', // uBlock Origin
234
+ url: 'https://example.com'
235
+ });
236
+ ```
237
+
238
+ <Tip>
239
+ Find the extension ID in the Chrome Web Store URL. For example, `https://chrome.google.com/webstore/detail/ublock-origin/cjpalhdlnbpafiamejdnhcphjbkeiagm` → ID is `cjpalhdlnbpafiamejdnhcphjbkeiagm`
240
+ </Tip>
241
+
242
+ #### Options
243
+
244
+ | Option | Type | Default | Description |
245
+ |--------|------|---------|-------------|
246
+ | `extensionPath` | string | - | Local path to unpacked extension directory |
247
+ | `extensionId` | string | - | Chrome Web Store extension ID |
248
+ | `url` | string | - | URL to navigate to after launch |
249
+ | `maximized` | boolean | `true` | Start browser maximized |
250
+
33
251
  <Warning>
34
- VS Code's Test Explorer only shows output for **failing tests**. To see output from passing tests (including screenshots and console logs), run tests from the terminal instead.
252
+ You must provide either `extensionPath` or `extensionId`, but not both.
253
+ </Warning>
254
+
255
+ #### Example: Testing a Chrome Extension
256
+
257
+ ```javascript
258
+ import { describe, expect, it } from "vitest";
259
+ import { TestDriver } from "testdriverai/vitest/hooks";
260
+
261
+ describe("Chrome Extension Test", () => {
262
+ it("should load and interact with extension", async (context) => {
263
+ const testdriver = TestDriver(context);
264
+
265
+ // Clone extension from GitHub
266
+ await testdriver.exec(
267
+ 'sh',
268
+ 'git clone https://github.com/user/my-extension.git /tmp/my-extension',
269
+ 60000,
270
+ true
271
+ );
272
+
273
+ // Launch Chrome with extension loaded
274
+ await testdriver.provision.chromeExtension({
275
+ extensionPath: '/tmp/my-extension',
276
+ url: 'https://testdriver.ai'
277
+ });
278
+
279
+ // Click extensions puzzle icon
280
+ const extensionsButton = await testdriver.find("puzzle-shaped icon in Chrome toolbar");
281
+ await extensionsButton.click();
282
+
283
+ // Interact with your extension
284
+ const myExtension = await testdriver.find("My Extension in the dropdown");
285
+ await myExtension.click();
286
+
287
+ const result = await testdriver.assert("extension popup is visible");
288
+ expect(result).toBeTruthy();
289
+ });
290
+ });
291
+ ```
292
+
293
+ ### Desktop Apps
294
+
295
+ Download and install desktop applications. Supports `.deb`, `.rpm`, `.msi`, `.exe`, `.AppImage`, `.dmg`, `.pkg`, and shell scripts.
296
+
297
+ ```javascript
298
+ const filePath = await testdriver.provision.installer({
299
+ url: 'https://example.com/app.deb',
300
+ appName: 'MyApp', // Focus this app after install
301
+ launch: true, // Auto-launch after install
302
+ });
303
+ ```
304
+
305
+ #### Options
306
+
307
+ | Option | Type | Default | Description |
308
+ |--------|------|---------|-------------|
309
+ | `url` | string | **required** | URL to download the installer from |
310
+ | `filename` | string | auto-detected | Filename to save as |
311
+ | `appName` | string | - | Application name to focus after install |
312
+ | `launch` | boolean | `true` | Launch the app after installation |
313
+
314
+ #### Supported File Types
315
+
316
+ | Extension | OS | Install Method |
317
+ |-----------|-----|----------------|
318
+ | `.deb` | Linux | `dpkg -i` + `apt-get install -f` |
319
+ | `.rpm` | Linux | `rpm -i` |
320
+ | `.AppImage` | Linux | `chmod +x` |
321
+ | `.sh` | Linux | `chmod +x` + execute |
322
+ | `.msi` | Windows | `msiexec /i /quiet` |
323
+ | `.exe` | Windows | Silent install (`/S`) |
324
+ | `.dmg` | macOS | Mount + copy to Applications |
325
+ | `.pkg` | macOS | `installer -pkg` |
326
+
327
+ #### Example: Install and Test a Desktop App
328
+
329
+ ```javascript
330
+ import { describe, expect, it } from "vitest";
331
+ import { TestDriver } from "testdriverai/vitest/hooks";
332
+
333
+ describe("Desktop App Test", () => {
334
+ it("should install and launch app", async (context) => {
335
+ const testdriver = TestDriver(context);
336
+
337
+ // Download and install
338
+ const installerPath = await testdriver.provision.installer({
339
+ url: 'https://github.com/sharkdp/bat/releases/download/v0.24.0/bat_0.24.0_amd64.deb',
340
+ });
341
+
342
+ // Verify installation
343
+ const output = await testdriver.exec('sh', 'bat --version', 5000);
344
+ expect(output).toContain('bat');
345
+ });
346
+ });
347
+ ```
348
+
349
+ #### Example: Windows Installer
350
+
351
+ ```javascript
352
+ import { describe, expect, it } from "vitest";
353
+ import { TestDriver } from "testdriverai/vitest/hooks";
354
+
355
+ describe("Windows App Test", () => {
356
+ it("should install on Windows", async (context) => {
357
+ const testdriver = TestDriver(context, {
358
+ os: 'windows'
359
+ });
360
+
361
+ // Download MSI installer
362
+ const installerPath = await testdriver.provision.installer({
363
+ url: 'https://example.com/app.msi',
364
+ launch: false, // Don't auto-launch
365
+ });
366
+
367
+ // Custom installation if needed
368
+ await testdriver.exec(
369
+ 'pwsh',
370
+ `Start-Process msiexec.exe -ArgumentList "/i", "${installerPath}", "/qn" -Wait`,
371
+ 120000
372
+ );
373
+
374
+ // Verify installation
375
+ const result = await testdriver.assert("application is installed");
376
+ expect(result).toBeTruthy();
377
+ });
378
+ });
379
+ ```
380
+
381
+ #### Manual Installation
382
+
383
+ Set `launch: false` to download without auto-installing:
384
+
385
+ ```javascript
386
+ const filePath = await testdriver.provision.installer({
387
+ url: 'https://example.com/custom-script.sh',
388
+ launch: false,
389
+ });
390
+
391
+ // Run custom install commands
392
+ await testdriver.exec('sh', `chmod +x "${filePath}"`, 5000);
393
+ await testdriver.exec('sh', `"${filePath}" --custom-flag`, 60000);
394
+ ```
395
+
396
+ ### VS Code
397
+
398
+ Launch Visual Studio Code with optional workspace and extensions.
399
+
400
+ ```javascript
401
+ await testdriver.provision.vscode({
402
+ workspace: '/home/testdriver/my-project',
403
+ extensions: ['ms-python.python', 'esbenp.prettier-vscode'],
404
+ });
405
+ ```
406
+
407
+ #### Options
408
+
409
+ | Option | Type | Default | Description |
410
+ |--------|------|---------|-------------|
411
+ | `workspace` | string | - | Workspace folder to open |
412
+ | `extensions` | string[] | `[]` | Extensions to install (by ID) |
413
+
414
+ #### Example: VS Code Extension Test
415
+
416
+ ```javascript
417
+ import { describe, expect, it } from "vitest";
418
+ import { TestDriver } from "testdriverai/vitest/hooks";
419
+
420
+ describe("VS Code Test", () => {
421
+ it("should open workspace with extensions", async (context) => {
422
+ const testdriver = TestDriver(context);
423
+
424
+ // Create a test project
425
+ await testdriver.exec('sh', 'mkdir -p /tmp/test-project && echo "print(1)" > /tmp/test-project/test.py', 10000);
426
+
427
+ // Launch VS Code
428
+ await testdriver.provision.vscode({
429
+ workspace: '/tmp/test-project',
430
+ extensions: ['ms-python.python'],
431
+ });
432
+
433
+ // Verify VS Code is ready
434
+ const result = await testdriver.assert("VS Code is open with the project");
435
+ expect(result).toBeTruthy();
436
+
437
+ // Open the Python file
438
+ await testdriver.find("test.py in the explorer").click();
439
+ });
440
+ });
441
+ ```
442
+
443
+ ### Choosing the Right Provision Method
444
+
445
+ | Use Case | Method |
446
+ |----------|--------|
447
+ | Testing a website | `provision.chrome` |
448
+ | Testing a Chrome extension | `provision.chromeExtension` |
449
+ | Testing a desktop app (needs installation) | `provision.installer` |
450
+ | Testing VS Code or VS Code extensions | `provision.vscode` |
451
+
452
+ <Tip>
453
+ All provision methods automatically start Dashcam recording and wait for the application to be ready before returning. You don't need to call `dashcam.start()` manually.
454
+ </Tip>
455
+
456
+ ## Configuring the Machine
457
+
458
+ TestDriver provisions a fresh cloud VM for every test by default. This section covers how to configure Linux and Windows machines, reduce startup time by keeping machines alive between runs, use provision scripts for repeatable setup, and install custom software on the fly.
459
+
460
+ ### Linux Machines
461
+
462
+ Linux is the default operating system. No extra configuration is required.
463
+
464
+ ```javascript
465
+ import { describe, expect, it } from "vitest";
466
+ import { TestDriver } from "testdriverai/vitest/hooks";
467
+
468
+ describe("My Test", () => {
469
+ it("runs on Linux", async (context) => {
470
+ const testdriver = TestDriver(context);
471
+
472
+ await testdriver.provision.chrome({ url: "https://example.com" });
473
+
474
+ const result = await testdriver.assert("the page loaded successfully");
475
+ expect(result).toBeTruthy();
476
+ });
477
+ });
478
+ ```
479
+
480
+ #### Common Linux Options
481
+
482
+ | Option | Type | Default | Description |
483
+ |--------|------|---------|-------------|
484
+ | `os` | string | `"linux"` | Operating system |
485
+ | `resolution` | string | `"1366x768"` | Screen resolution (Enterprise only) |
486
+ | `e2bTemplateId` | string | — | Custom E2B template ID (see [Self-Hosted](/v7/self-hosted)) |
487
+ | `keepAlive` | number | `60000` | Ms to keep VM alive after disconnect |
488
+ | `reconnect` | boolean | `false` | Reconnect to last used sandbox |
489
+
490
+ ```javascript
491
+ const testdriver = TestDriver(context, {
492
+ os: "linux",
493
+ resolution: "1920x1080",
494
+ keepAlive: 5 * 60 * 1000, // keep alive 5 minutes
495
+ });
496
+ ```
497
+
498
+ ### Windows Machines
499
+
500
+ Set `os: "windows"` to provision a Windows VM instead. Everything else works the same way.
501
+
502
+ ```javascript
503
+ const testdriver = TestDriver(context, {
504
+ os: "windows",
505
+ });
506
+
507
+ await testdriver.provision.chrome({ url: "https://example.com" });
508
+ ```
509
+
510
+ Windows sandboxes use EC2 instances and take longer to boot than Linux (E2B) sandboxes — typically 1–3 minutes for a cold start. See [Keeping Machines Alive](#keeping-machines-alive-between-runs) below to avoid this cost on repeated runs.
511
+
512
+ #### Common Windows Options
513
+
514
+ | Option | Type | Default | Description |
515
+ |--------|------|---------|-------------|
516
+ | `os` | string | — | Set to `"windows"` |
517
+ | `resolution` | string | `"1366x768"` | Screen resolution (Enterprise only) |
518
+ | `sandboxAmi` | string | — | Custom AMI ID (self-hosted) |
519
+ | `sandboxInstance` | string | — | EC2 instance type (self-hosted) |
520
+ | `keepAlive` | number | `60000` | Ms to keep VM alive after disconnect |
521
+ | `reconnect` | boolean | `false` | Reconnect to last used sandbox |
522
+
523
+ ```javascript
524
+ const testdriver = TestDriver(context, {
525
+ os: "windows",
526
+ resolution: "1920x1080",
527
+ keepAlive: 10 * 60 * 1000, // keep alive 10 minutes
528
+ });
529
+ ```
530
+
531
+ ### Keeping Machines Alive Between Runs
532
+
533
+ Windows (and Linux) cold starts can be expensive if you're iterating quickly. Use `keepAlive` + `reconnect` to reuse the same VM across multiple test runs.
534
+
535
+ #### How it works
536
+
537
+ Every time the SDK successfully connects to a sandbox, it records the sandbox id in `.testdriver/last-sandbox` inside your project directory. The next test that opts in with `reconnect: true` reads that file and reattaches automatically — no manual id tracking required.
538
+
539
+ Provision calls (`testdriver.provision.chrome(...)`, `vscode(...)`, etc.) are **skipped** when reconnecting, because the application is already running inside the sandbox from the previous run.
540
+
541
+ <Note>
542
+ `.testdriver/last-sandbox` is already covered by the default TestDriver `.gitignore`. Don't commit it.
543
+ </Note>
544
+
545
+ #### Step 1 — Start the machine with a long `keepAlive`
546
+
547
+ ```javascript
548
+ // first.test.mjs
549
+ const testdriver = TestDriver(context, {
550
+ os: "windows",
551
+ keepAlive: 30 * 60 * 1000, // keep alive 30 minutes after this test ends
552
+ });
553
+
554
+ await testdriver.provision.chrome({ url: "https://example.com" });
555
+ // ... your test steps
556
+ ```
557
+
558
+ When this test finishes, the sandbox stays running for 30 minutes instead of being terminated immediately.
559
+
560
+ #### Step 2 — Reattach automatically with `reconnect: true`
561
+
562
+ ```javascript
563
+ // second.test.mjs
564
+ const testdriver = TestDriver(context, {
565
+ os: "windows",
566
+ reconnect: true, // ← reads .testdriver/last-sandbox
567
+ keepAlive: 30 * 60 * 1000,
568
+ });
569
+
570
+ // No provision call — Chrome is already open from the previous run.
571
+ await testdriver.find("Sign In button").click();
572
+ ```
573
+
574
+ #### Step 2 (alternative) — Reattach to an explicit id
575
+
576
+ If you need to pin to a specific sandbox (CI matrix, multiple chains in parallel, etc.) pass the id directly:
577
+
578
+ ```javascript
579
+ await testdriver.connect({ sandboxId: "sandbox-abc123" });
580
+ ```
581
+
582
+ When reattaching to a sandbox:
583
+ - You reuse a specific running machine directly
584
+ - You continue from the app state created in the earlier run
585
+ - You must run within the previous test's `keepAlive` window
586
+
587
+ <Tip>
588
+ Use `testdriver.getLastSandboxId()` to read the recorded sandbox id (and optional metadata) for scripting purposes.
589
+ </Tip>
590
+
591
+ #### Chaining describe blocks within one test file
592
+
593
+ A common pattern is to break a long flow into focused `describe` blocks that share one sandbox — the first block provisions and signs in, later blocks reconnect and continue:
594
+
595
+ ```javascript
596
+ import { describe, expect, it } from "vitest";
597
+ import { TestDriver } from "testdriverai/vitest/hooks";
598
+
599
+ const KEEP_ALIVE_MS = 5 * 60 * 1000;
600
+
601
+ describe("step 1 — log in", () => {
602
+ it("signs in and lands on the dashboard", async (context) => {
603
+ const testdriver = TestDriver(context, { keepAlive: KEEP_ALIVE_MS });
604
+ await testdriver.provision.chrome({ url: "https://example.com/login" });
605
+ await testdriver.find("username input").click();
606
+ await testdriver.type("standard_user");
607
+ await testdriver.pressKeys(["tab"]);
608
+ await testdriver.type("secret_sauce", { secret: true });
609
+ await testdriver.pressKeys(["enter"]);
610
+ expect(await testdriver.assert("the dashboard is visible")).toBeTruthy();
611
+ });
612
+ });
613
+
614
+ describe("step 2 — add to cart", () => {
615
+ it("reuses the logged-in sandbox", async (context) => {
616
+ const testdriver = TestDriver(context, {
617
+ reconnect: true, // ← skip provisioning, reattach
618
+ keepAlive: KEEP_ALIVE_MS,
619
+ });
620
+ await testdriver.find("Add to cart").click();
621
+ await testdriver.find("cart icon").click();
622
+ expect(await testdriver.assert("the cart has an item")).toBeTruthy();
623
+ });
624
+ });
625
+
626
+ describe("step 3 — check out", () => {
627
+ it("continues from the cart state", async (context) => {
628
+ const testdriver = TestDriver(context, { reconnect: true, keepAlive: 30_000 });
629
+ await testdriver.find("Checkout").click();
630
+ expect(await testdriver.assert("the checkout form is visible")).toBeTruthy();
631
+ });
632
+ });
633
+ ```
634
+
635
+ A runnable copy of this pattern lives at [`examples/reconnect-sequential.test.mjs`](https://github.com/testdriverai/mono/blob/main/sdk/examples/reconnect-sequential.test.mjs).
636
+
637
+ <Warning>
638
+ Vitest runs **test files** in parallel by default. Within a single file, `describe`/`it` blocks run in source order, so reconnect chaining works as written. To chain across multiple files, run them sequentially (e.g. `vitest run --sequence.concurrent=false` or place them in a single project pool with workers set to 1).
639
+ </Warning>
640
+
641
+ #### How `keepAlive` works
642
+
643
+ `keepAlive` is a duration in milliseconds. After the SDK disconnects, the server keeps the VM running for that long before terminating it. The default is `60000` (1 minute). Note: `keepAlive: 0` currently falls back to the default disconnect grace period rather than terminating immediately, so use a positive duration when you want to control the grace window explicitly.
644
+
645
+ ```javascript
646
+ const testdriver = TestDriver(context, {
647
+ keepAlive: 0, // currently uses the default 1 minute grace period
648
+ // keepAlive: 60000, // default — 1 minute
649
+ // keepAlive: 600000, // 10 minutes
650
+ // keepAlive: 3600000, // 1 hour
651
+ });
652
+ ```
653
+
654
+ <Warning>
655
+ Machines kept alive beyond your test session continue to consume credits. Always set a `keepAlive` value appropriate for your workflow.
35
656
  </Warning>
36
657
 
37
- ## Running from Terminal
658
+ ### Using Provision Scripts
659
+
660
+ Provision scripts let you run arbitrary setup steps before your test starts — downloading fixtures, seeding a database, configuring environment variables, and more. Use `testdriver.exec()` to run shell or PowerShell commands directly in the sandbox.
661
+
662
+ <Card
663
+ title="exec() Reference"
664
+ icon="terminal"
665
+ href="/v7/exec"
666
+ >
667
+ Full reference for running shell and PowerShell commands in the sandbox.
668
+ </Card>
669
+
670
+ #### Linux setup script
38
671
 
39
- Use Vitest to run your tests from the command line to see full output:
672
+ ```javascript
673
+ await testdriver.provision.chrome({ url: "https://myapp.com" });
674
+
675
+ // Run a setup script from your repo
676
+ await testdriver.exec("sh", `
677
+ curl -s https://myapp.com/api/reset-test-db -X POST
678
+ echo "Test DB reset"
679
+ `, 30000);
680
+ ```
681
+
682
+ #### Windows setup script (PowerShell)
683
+
684
+ ```javascript
685
+ await testdriver.provision.chrome({ url: "https://myapp.com" });
686
+
687
+ await testdriver.exec("pwsh", `
688
+ $env:API_URL = "https://staging.myapp.com"
689
+ Write-Host "Environment configured"
690
+ `, 15000);
691
+ ```
692
+
693
+ #### Clone a repo and run a script
694
+
695
+ ```javascript
696
+ await testdriver.exec("sh", `
697
+ git clone https://github.com/myorg/test-fixtures.git /tmp/fixtures
698
+ bash /tmp/fixtures/seed.sh
699
+ `, 120000);
700
+ ```
701
+
702
+ ### Installing Custom Software
703
+
704
+ You can install software at the start of a test using `exec()`. This works for any package available via `apt`, `brew`, `choco`, `winget`, npm, pip, or direct download.
705
+
706
+ #### Linux — apt packages
707
+
708
+ ```javascript
709
+ await testdriver.exec("sh", `
710
+ sudo apt-get update -qq
711
+ sudo apt-get install -y ffmpeg imagemagick
712
+ `, 120000);
713
+ ```
714
+
715
+ #### Linux — Node.js tools
716
+
717
+ ```javascript
718
+ await testdriver.exec("sh", "npm install -g @playwright/test", 60000);
719
+ ```
720
+
721
+ #### Windows — winget
722
+
723
+ ```javascript
724
+ await testdriver.exec("pwsh", `
725
+ winget install --id=7zip.7zip -e --silent
726
+ `, 120000);
727
+ ```
728
+
729
+ #### Windows — Chocolatey
730
+
731
+ ```javascript
732
+ await testdriver.exec("pwsh", `
733
+ choco install googlechrome --yes --no-progress
734
+ `, 180000);
735
+ ```
736
+
737
+ #### Download and run an installer
738
+
739
+ ```javascript
740
+ // Linux
741
+ await testdriver.exec("sh", `
742
+ curl -L https://example.com/installer.sh -o /tmp/installer.sh
743
+ chmod +x /tmp/installer.sh
744
+ /tmp/installer.sh --silent
745
+ `, 300000);
746
+
747
+ // Windows
748
+ await testdriver.exec("pwsh", `
749
+ Invoke-WebRequest -Uri "https://example.com/installer.exe" -OutFile "$env:TEMP\\installer.exe"
750
+ Start-Process "$env:TEMP\\installer.exe" -ArgumentList "/S" -Wait
751
+ `, 300000);
752
+ ```
753
+
754
+ <Note>
755
+ Installing software at test start adds to your test duration. For software you use in every test, consider preloading it into a custom VM image via the Enterprise self-hosted plan.
756
+ </Note>
757
+
758
+ #### Want Software Pre-Installed on Every Machine?
759
+
760
+ Installing packages at runtime works well for occasional or lightweight dependencies. But if you're installing the same 5-minute setup on every test run, you're wasting time and credits.
761
+
762
+ With the **Self-Hosted Enterprise plan** you get access to our golden VM base image and Packer scripts, so you can bake your applications, dependencies, and configuration directly into a custom AMI. Tests spin up with everything already installed — zero setup time.
763
+
764
+ <Card
765
+ title="Self-Hosted Enterprise"
766
+ icon="server"
767
+ href="/v7/self-hosted"
768
+ >
769
+ Preload software, configure custom hardware, and run unlimited tests with a flat license fee. Our team assists with deployment and setup.
770
+ </Card>
771
+
772
+ ## Running Tests
773
+
774
+ After creating tests with the TestDriver agent, you can re-run them without starting a new MCP session. Tests are saved as standard Vitest files that run independently — the same way on your machine and in CI.
775
+
776
+ ### Running from Terminal
777
+
778
+ TestDriver works with [Vitest's](https://vitest.dev) test runner. Use Vitest to run your tests from the command line to see full output:
40
779
 
41
780
  ```bash
42
781
  # Run all tests
@@ -49,55 +788,76 @@ vitest run tests/login.test.mjs
49
788
  vitest
50
789
  ```
51
790
 
52
- See the [running tests guide](/v7/running-tests) for more CLI options and configuration.
791
+ <Info>
792
+ Install Vitest globally for best results: `npm install vitest -g`
793
+ </Info>
53
794
 
54
- ## Iterating on Tests
795
+ Vitest automatically discovers files matching patterns like `*.test.js`, `*.test.mjs`, or `*.spec.js`.
55
796
 
56
- When tests fail or need updates, you have two options:
797
+ #### Common CLI options
798
+
799
+ ```bash
800
+ # Run multiple specific files
801
+ vitest run login.test.mjs checkout.test.mjs
57
802
 
58
- ### Option 1: Ask the MCP Agent (Recommended)
803
+ # Run every test in a folder
804
+ vitest run tests/e2e/
59
805
 
60
- For discovering updated element descriptions or debugging failures, chat with the TestDriver agent:
806
+ # Filter tests by name (supports regex)
807
+ vitest run --grep "login"
61
808
 
809
+ # Generate a coverage report
810
+ vitest run --coverage
62
811
  ```
63
- The login test is failing because the form layout changed.
64
- Update tests/login.test.mjs to work with the new design.
812
+
813
+ <Info>
814
+ Coverage requires the `@vitest/coverage-v8` package. Install it with `npm install -D @vitest/coverage-v8`.
815
+ </Info>
816
+
817
+ #### Vitest UI
818
+
819
+ For interactive debugging, launch the web-based UI (starts in watch mode):
820
+
821
+ ```bash
822
+ vitest --ui
65
823
  ```
66
824
 
67
- The agent will:
68
- 1. Start a new session
69
- 2. Navigate to the page
70
- 3. Analyze the current state
71
- 4. Update the test code
825
+ Open http://localhost:51204 to browse your test tree, see pass/fail states and timing, view inline console output, and re-run individual tests.
72
826
 
73
- This is the best approach when:
74
- - Element text or layout has changed
75
- - You need to see what's currently on screen
76
- - The failure reason isn't obvious from the error message
827
+ <Tip>
828
+ Combine with `--open` to open the UI in your browser automatically: `vitest --ui --open`
829
+ </Tip>
77
830
 
78
- ### Option 2: Edit the Code Directly
831
+ ### Running from VS Code (GUI Mode)
79
832
 
80
- For simple changes, edit the test files directly:
833
+ For a visual testing experience, use the **Vitest extension**:
81
834
 
82
- ```javascript
83
- // Change the element description
84
- const button = await testdriver.find("Submit Order button");
835
+ <Steps>
836
+ <Step title="Open Test Explorer">
837
+ Click the **beaker icon** in the VS Code sidebar to open the Test Explorer. This shows all your test files and test cases.
838
+ </Step>
85
839
 
86
- // Add a wait
87
- await testdriver.wait(2000);
840
+ <Step title="Run Tests">
841
+ Click the **play button** next to any test file or individual test to run it. You can also:
842
+ - Run all tests with the "Run All" button
843
+ - Debug tests with the "Debug" button
844
+ </Step>
88
845
 
89
- // Update assertions
90
- const result = await testdriver.assert("order confirmation shows order ID");
91
- ```
846
+ <Step title="View Results">
847
+ Test results appear inline:
848
+ - ✅ Green checkmark for passing tests
849
+ - ❌ Red X for failing tests
850
+ - Click on a failing test to see error details
851
+ </Step>
852
+ </Steps>
92
853
 
93
- This is faster for:
94
- - Updating text strings
95
- - Adjusting timeouts
96
- - Fixing typos
854
+ <Warning>
855
+ VS Code's Test Explorer only shows output for **failing tests**. To see output from passing tests (including screenshots and console logs), run tests from the terminal instead.
856
+ </Warning>
97
857
 
98
- ## Test Configuration
858
+ ### Test Configuration
99
859
 
100
- ### Timeouts
860
+ #### Timeouts
101
861
 
102
862
  TestDriver tests require longer timeouts than typical unit tests. Your `vitest.config.mjs` should have:
103
863
 
@@ -112,7 +872,7 @@ export default defineConfig({
112
872
  });
113
873
  ```
114
874
 
115
- ### Environment Variables
875
+ #### Environment Variables
116
876
 
117
877
  Tests use the `TD_API_KEY` environment variable. Set it in your `.env` file:
118
878
 
@@ -126,7 +886,50 @@ Or pass it when running tests:
126
886
  TD_API_KEY=your-key vitest run
127
887
  ```
128
888
 
129
- ## Viewing Test Reports
889
+ ### Parallel Execution
890
+
891
+ TestDriver runs each test in its own cloud sandbox, enabling true parallel execution. Run your entire test suite in minutes instead of hours.
892
+
893
+ Your TestDriver plan includes a set number of **license slots** that determine how many tests can run simultaneously. Each running test occupies one slot — when the test completes and the sandbox is destroyed, the slot is immediately freed for the next test.
894
+
895
+ Set `maxConcurrency` in your Vitest config to match your license slot limit:
896
+
897
+ ```javascript vitest.config.mjs
898
+ import { defineConfig } from 'vitest/config';
899
+ import TestDriver from 'testdriverai/vitest';
900
+
901
+ export default defineConfig({
902
+ test: {
903
+ testTimeout: 900000,
904
+ hookTimeout: 900000,
905
+ maxConcurrency: 5, // Match your license slot limit
906
+ reporters: ['default', TestDriver()],
907
+ setupFiles: ['testdriverai/vitest/setup'],
908
+ },
909
+ });
910
+ ```
911
+
912
+ You can also cap concurrency from the CLI:
913
+
914
+ ```bash
915
+ vitest run --maxConcurrency=5
916
+ ```
917
+
918
+ <Warning>
919
+ Setting `maxConcurrency` higher than your license slots will cause tests to fail when slots are exhausted. Always match this value to your plan's limit.
920
+ </Warning>
921
+
922
+ | Test Suite | Sequential (1 slot) | Parallel (5 slots) | Parallel (10 slots) |
923
+ |------------|--------------------|--------------------|---------------------|
924
+ | 10 tests @ 2min each | 20 min | 4 min | 2 min |
925
+ | 50 tests @ 2min each | 100 min | 20 min | 10 min |
926
+ | 100 tests @ 2min each | 200 min | 40 min | 20 min |
927
+
928
+ <Info>
929
+ View your available slots at [console.testdriver.ai](https://console.testdriver.ai). Upgrade anytime to increase parallelization.
930
+ </Info>
931
+
932
+ ### Viewing Test Reports
130
933
 
131
934
  After each test run, TestDriver provides a link to the full test report:
132
935
 
@@ -147,3 +950,61 @@ The report includes:
147
950
  >
148
951
  Access all your test runs and recordings in the TestDriver console
149
952
  </Card>
953
+
954
+ ### Iterating on Tests
955
+
956
+ When tests fail or need updates, you have two options:
957
+
958
+ #### Option 1: Ask the AI Assistant (Recommended)
959
+
960
+ For discovering updated element descriptions or debugging failures, chat with the TestDriver agent through your AI assistant:
961
+
962
+ ```
963
+ The login test is failing because the form layout changed.
964
+ Update tests/login.test.mjs to work with the new design.
965
+ ```
966
+
967
+ The agent will:
968
+ 1. Start a new session
969
+ 2. Navigate to the page
970
+ 3. Analyze the current state
971
+ 4. Update the test code
972
+
973
+ This is the best approach when:
974
+ - Element text or layout has changed
975
+ - You need to see what's currently on screen
976
+ - The failure reason isn't obvious from the error message
977
+
978
+ #### Option 2: Edit the Code Directly
979
+
980
+ For simple changes, edit the test files directly:
981
+
982
+ ```javascript
983
+ // Change the element description
984
+ const button = await testdriver.find("Submit Order button");
985
+
986
+ // Add a wait
987
+ await testdriver.wait(2000);
988
+
989
+ // Update assertions
990
+ const result = await testdriver.assert("order confirmation shows order ID");
991
+ ```
992
+
993
+ This is faster for:
994
+ - Updating text strings
995
+ - Adjusting timeouts
996
+ - Fixing typos
997
+
998
+ <Tip>
999
+ When a test fails because the UI shifted, let TestDriver fix it for you automatically. See [Prevent](/v7/copilot/auto-healing) for auto-healing, and [Debug](/v7/debugging-with-screenshots) for inspecting screenshots when a run goes wrong.
1000
+ </Tip>
1001
+
1002
+ ## Next
1003
+
1004
+ <Card
1005
+ title="Validate"
1006
+ icon="circle-check"
1007
+ href="/v7/making-assertions"
1008
+ >
1009
+ Now that your tests run anywhere, learn how to make strong assertions that catch real bugs.
1010
+ </Card>