testdriverai 7.8.0-test.8 → 7.8.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 (54) hide show
  1. package/agent/index.js +6 -5
  2. package/agent/lib/commands.js +3 -2
  3. package/agent/lib/http.js +144 -0
  4. package/agent/lib/sandbox.js +117 -102
  5. package/agent/lib/sdk.js +4 -2
  6. package/agent/lib/system.js +25 -65
  7. package/ai/skills/testdriver-mcp/SKILL.md +7 -0
  8. package/ai/skills/testdriver-running-tests/SKILL.md +1 -1
  9. package/docs/changelog.mdx +148 -8
  10. package/docs/docs.json +44 -37
  11. package/docs/images/content/vscode/v7-chat.png +0 -0
  12. package/docs/images/content/vscode/v7-choose-agent.png +0 -0
  13. package/docs/images/content/vscode/v7-full.png +0 -0
  14. package/docs/images/content/vscode/v7-onboarding.png +0 -0
  15. package/docs/v7/cache.mdx +223 -0
  16. package/docs/v7/copilot/auto-healing.mdx +265 -0
  17. package/docs/v7/copilot/creating-tests.mdx +156 -0
  18. package/docs/v7/copilot/github.mdx +143 -0
  19. package/docs/v7/copilot/running-tests.mdx +149 -0
  20. package/docs/v7/copilot/setup.mdx +143 -0
  21. package/docs/v7/enterprise.mdx +3 -110
  22. package/docs/v7/errors.mdx +248 -0
  23. package/docs/v7/events.mdx +358 -0
  24. package/docs/v7/examples/exec-output.mdx +85 -0
  25. package/docs/v7/examples/exec-pwsh.mdx +83 -0
  26. package/docs/v7/examples/focus-window.mdx +62 -0
  27. package/docs/v7/{cloud.mdx → hosted.mdx} +43 -5
  28. package/docs/v7/mcp.mdx +9 -0
  29. package/docs/v7/provision.mdx +333 -0
  30. package/docs/v7/quickstart.mdx +30 -2
  31. package/docs/v7/redraw.mdx +216 -0
  32. package/docs/v7/running-tests.mdx +1 -1
  33. package/docs/v7/screenshots.mdx +186 -0
  34. package/docs/v7/self-hosted.mdx +127 -44
  35. package/interfaces/logger.js +0 -12
  36. package/interfaces/vitest-plugin.mjs +3 -0
  37. package/lib/core/Dashcam.js +13 -16
  38. package/lib/environments.json +18 -0
  39. package/lib/resolve-channel.js +4 -3
  40. package/{examples → manual}/drag-and-drop.test.mjs +1 -1
  41. package/package.json +3 -3
  42. package/sdk.js +3 -3
  43. package/vitest.config.mjs +20 -32
  44. /package/{examples → manual}/flake-diffthreshold-001.test.mjs +0 -0
  45. /package/{examples → manual}/flake-diffthreshold-01.test.mjs +0 -0
  46. /package/{examples → manual}/flake-diffthreshold-05.test.mjs +0 -0
  47. /package/{examples → manual}/flake-noredraw-cache.test.mjs +0 -0
  48. /package/{examples → manual}/flake-noredraw-nocache.test.mjs +0 -0
  49. /package/{examples → manual}/flake-redraw-cache.test.mjs +0 -0
  50. /package/{examples → manual}/flake-redraw-nocache.test.mjs +0 -0
  51. /package/{examples → manual}/flake-rocket-match.test.mjs +0 -0
  52. /package/{examples → manual}/flake-shared.mjs +0 -0
  53. /package/{examples → manual}/no-provision.test.mjs +0 -0
  54. /package/{examples → manual}/scroll-until-text.test.mjs +0 -0
@@ -0,0 +1,358 @@
1
+ ---
2
+ title: "Events"
3
+ sidebarTitle: "Events"
4
+ description: "Listen to SDK lifecycle events with wildcard support"
5
+ icon: "bolt"
6
+ mode: "wide"
7
+ ---
8
+
9
+ ## Overview
10
+
11
+ TestDriver uses [EventEmitter2](https://github.com/EventEmitter2/EventEmitter2) for its event system. Events use a colon-delimited namespace pattern and support wildcard listeners.
12
+
13
+ Access the emitter through `testdriver.emitter`:
14
+
15
+ ```javascript
16
+ testdriver.emitter.on('command:start', (data) => {
17
+ console.log(`Running: ${data.command}`);
18
+ });
19
+ ```
20
+
21
+ ### Configuration
22
+
23
+ The internal emitter is created with:
24
+
25
+ ```javascript
26
+ new EventEmitter2({
27
+ wildcard: true,
28
+ delimiter: ':',
29
+ maxListeners: 20,
30
+ verboseMemoryLeak: false,
31
+ ignoreErrors: false,
32
+ });
33
+ ```
34
+
35
+ ### Wildcard Listeners
36
+
37
+ Use `*` to match a single level or `**` to match multiple levels:
38
+
39
+ ```javascript
40
+ // Match all log events
41
+ testdriver.emitter.on('log:*', (message) => {
42
+ console.log(message);
43
+ });
44
+
45
+ // Match all events in any namespace
46
+ testdriver.emitter.on('**', (...args) => {
47
+ console.log('Event:', this.event, args);
48
+ });
49
+ ```
50
+
51
+ ## Event Reference
52
+
53
+ ### Command Events
54
+
55
+ Emitted during the execution of SDK commands (`click`, `type`, `find`, etc.).
56
+
57
+ | Event | Payload |
58
+ |---|---|
59
+ | `command:start` | `{ command, depth, data, timestamp, sourcePosition }` |
60
+ | `command:success` | `{ command, depth, data, duration, response, timestamp, sourcePosition }` |
61
+ | `command:error` | `{ command, depth, data, error, duration, timestamp, sourcePosition }` |
62
+ | `command:status` | `{ command, status: "executing", data, depth, timestamp }` |
63
+ | `command:progress` | `{ command, status: "completed", timing, data, depth, timestamp }` |
64
+
65
+ ```javascript
66
+ testdriver.emitter.on('command:start', ({ command, data }) => {
67
+ console.log(`Starting ${command}`, data);
68
+ });
69
+
70
+ testdriver.emitter.on('command:error', ({ command, error, duration }) => {
71
+ console.error(`${command} failed after ${duration}ms: ${error}`);
72
+ });
73
+ ```
74
+
75
+ ### Step Events
76
+
77
+ Emitted for each AI reasoning step within a command.
78
+
79
+ | Event | Payload |
80
+ |---|---|
81
+ | `step:start` | `{ stepIndex, prompt, commandCount, timestamp, sourcePosition }` |
82
+ | `step:success` | `{ stepIndex, prompt, commandCount, duration, timestamp, sourcePosition }` |
83
+ | `step:error` | `{ stepIndex, prompt, error, duration?, timestamp, sourcePosition? }` |
84
+
85
+ ```javascript
86
+ testdriver.emitter.on('step:start', ({ stepIndex, prompt }) => {
87
+ console.log(`Step ${stepIndex}: ${prompt}`);
88
+ });
89
+ ```
90
+
91
+ ### Test Events
92
+
93
+ Emitted when a test file execution starts.
94
+
95
+ | Event | Payload |
96
+ |---|---|
97
+ | `test:start` | `{ filePath, timestamp }` |
98
+ | `test:success` | *Emitted on test completion* |
99
+ | `test:error` | *Emitted on test failure* |
100
+
101
+ ### Log Events
102
+
103
+ Emitted for all log output from the SDK.
104
+
105
+ | Event | Payload |
106
+ |---|---|
107
+ | `log:log` | `(message: string)` — general log message |
108
+ | `log:warn` | `(message: string)` — warning message |
109
+ | `log:debug` | `(message: string)` — debug output (only when `VERBOSE`/`DEBUG`/`TD_DEBUG` env set) |
110
+ | `log:info` | `(message: string)` — informational message |
111
+ | `log:error` | `(message: string)` — error message |
112
+ | `log:narration` | `(message: string, overwrite?: boolean)` — in-place status line |
113
+ | `log:markdown` | `(markdown: string)` — full static markdown content |
114
+ | `log:markdown:start` | `(streamId: string)` — begin streaming markdown |
115
+ | `log:markdown:chunk` | `(streamId: string, chunk: string)` — incremental chunk |
116
+ | `log:markdown:end` | `(streamId: string)` — end streaming markdown |
117
+
118
+ ```javascript
119
+ // Capture all logs
120
+ testdriver.emitter.on('log:*', function (message) {
121
+ console.log(`[${this.event}]`, message);
122
+ });
123
+ ```
124
+
125
+ ### Screen Capture Events
126
+
127
+ Emitted during screenshot capture.
128
+
129
+ | Event | Payload |
130
+ |---|---|
131
+ | `screen-capture:start` | `{ scale, silent, display }` |
132
+ | `screen-capture:end` | `{ scale, silent, display }` |
133
+ | `screen-capture:error` | `{ error, scale, silent, display }` |
134
+
135
+ ### Sandbox Events
136
+
137
+ Emitted for sandbox WebSocket lifecycle and communication.
138
+
139
+ | Event | Payload |
140
+ |---|---|
141
+ | `sandbox:connected` | *No payload* — WebSocket connection established |
142
+ | `sandbox:authenticated` | `{ traceId }` — authentication successful |
143
+ | `sandbox:error` | `(err: Error \| string)` — connection or sandbox error |
144
+ | `sandbox:sent` | `(message: object)` — WebSocket message sent |
145
+ | `sandbox:received` | *No payload* — successful message reply received |
146
+ | `sandbox:progress` | `{ step, message }` — sandbox setup progress |
147
+
148
+ ```javascript
149
+ testdriver.emitter.on('sandbox:connected', () => {
150
+ console.log('Connected to sandbox');
151
+ });
152
+
153
+ testdriver.emitter.on('sandbox:progress', ({ step, message }) => {
154
+ console.log(`Sandbox: [${step}] ${message}`);
155
+ });
156
+ ```
157
+
158
+ ### Redraw Events
159
+
160
+ Emitted during screen stability detection. See [Redraw](/v7/redraw) for more details.
161
+
162
+ | Event | Payload |
163
+ |---|---|
164
+ | `redraw:status` | `{ redraw: { enabled, settled, hasChangedFromInitial, consecutiveFramesStable, diffFromInitial, diffFromLast, text }, network: { enabled, settled, rxBytes, txBytes, text }, timeout: { isTimeout, elapsed, max, text } }` |
165
+ | `redraw:complete` | `{ screenSettled, hasChangedFromInitial, consecutiveFramesStable, networkSettled, isTimeout, timeElapsed }` |
166
+
167
+ ```javascript
168
+ testdriver.emitter.on('redraw:complete', (result) => {
169
+ if (result.isTimeout) {
170
+ console.warn('Redraw timed out after', result.timeElapsed, 'ms');
171
+ }
172
+ });
173
+ ```
174
+
175
+ ### File Events
176
+
177
+ Emitted during file load/save operations in the agent.
178
+
179
+ | Event | Payload |
180
+ |---|---|
181
+ | `file:start` | `{ operation: "load" \| "save" \| "run", filePath, timestamp }` |
182
+ | `file:stop` | `{ operation, filePath, duration, success, sourceMap?, reason?, timestamp }` |
183
+ | `file:load` | `{ filePath, size, timestamp }` |
184
+ | `file:save` | `{ filePath, size, timestamp }` |
185
+ | `file:diff` | `{ filePath, diff: { patches, sourceMaps, summary: { additions, deletions, modifications } }, timestamp }` |
186
+ | `file:error` | `{ operation, filePath, error, duration?, timestamp }` |
187
+
188
+ ### Error Events
189
+
190
+ Emitted for errors at various severity levels.
191
+
192
+ | Event | Payload |
193
+ |---|---|
194
+ | `error:fatal` | `(error: string \| Error)` — terminates the process |
195
+ | `error:general` | `(message: string)` — non-fatal error |
196
+ | `error:sandbox` | `(err: Error \| string)` — sandbox/WebSocket error |
197
+
198
+ ```javascript
199
+ testdriver.emitter.on('error:*', function (err) {
200
+ console.error(`[${this.event}]`, err);
201
+ });
202
+ ```
203
+
204
+ ### SDK Events
205
+
206
+ Emitted for API request lifecycle.
207
+
208
+ | Event | Payload |
209
+ |---|---|
210
+ | `sdk:request` | `{ path }` — outgoing API request |
211
+ | `sdk:response` | `{ path }` — API response received |
212
+ | `sdk:retry` | `{ path, attempt, error, delayMs }` — request retry |
213
+
214
+ ### Other Events
215
+
216
+ | Event | Payload |
217
+ |---|---|
218
+ | `exit` | `(exitCode: number)` — `0` for success, `1` for failure |
219
+ | `status` | `(message: string)` — general status updates |
220
+ | `mouse-click` | `{ x, y, button, click, double }` — mouse click performed |
221
+ | `terminal:stdout` | Terminal stdout output |
222
+ | `terminal:stderr` | Terminal stderr output |
223
+
224
+ ## Practical Examples
225
+
226
+ ### Custom Test Reporter
227
+
228
+ ```javascript
229
+ const results = [];
230
+
231
+ testdriver.emitter.on('command:success', ({ command, duration }) => {
232
+ results.push({ command, duration, status: 'pass' });
233
+ });
234
+
235
+ testdriver.emitter.on('command:error', ({ command, duration, error }) => {
236
+ results.push({ command, duration, status: 'fail', error });
237
+ });
238
+
239
+ // After test completes
240
+ afterAll(() => {
241
+ console.table(results);
242
+ });
243
+ ```
244
+
245
+ ### Progress Monitoring
246
+
247
+ ```javascript
248
+ testdriver.emitter.on('step:start', ({ stepIndex, prompt }) => {
249
+ process.stdout.write(`\r Step ${stepIndex}: ${prompt}`);
250
+ });
251
+
252
+ testdriver.emitter.on('command:progress', ({ command, timing }) => {
253
+ process.stdout.write(`\r ${command} completed in ${timing}ms`);
254
+ });
255
+ ```
256
+
257
+ ### Debug Logging
258
+
259
+ ```javascript
260
+ // Log every event (verbose)
261
+ testdriver.emitter.on('**', function (...args) {
262
+ console.debug(`[EVENT] ${this.event}`, ...args);
263
+ });
264
+ ```
265
+
266
+ ## Types
267
+
268
+ ```typescript
269
+ interface CommandStartEvent {
270
+ command: string;
271
+ depth: number;
272
+ data: Record<string, any>;
273
+ timestamp: number;
274
+ sourcePosition: SourcePosition;
275
+ }
276
+
277
+ interface CommandSuccessEvent {
278
+ command: string;
279
+ depth: number;
280
+ data: Record<string, any>;
281
+ duration: number;
282
+ response: any;
283
+ timestamp: number;
284
+ sourcePosition: SourcePosition;
285
+ }
286
+
287
+ interface CommandErrorEvent {
288
+ command: string;
289
+ depth: number;
290
+ data: Record<string, any>;
291
+ error: string;
292
+ duration: number;
293
+ timestamp: number;
294
+ sourcePosition: SourcePosition;
295
+ }
296
+
297
+ interface StepStartEvent {
298
+ stepIndex: number;
299
+ prompt: string;
300
+ commandCount: number;
301
+ timestamp: number;
302
+ sourcePosition: SourcePosition;
303
+ }
304
+
305
+ interface StepSuccessEvent {
306
+ stepIndex: number;
307
+ prompt: string;
308
+ commandCount: number;
309
+ duration: number;
310
+ timestamp: number;
311
+ sourcePosition: SourcePosition;
312
+ }
313
+
314
+ interface RedrawStatusEvent {
315
+ redraw: {
316
+ enabled: boolean;
317
+ settled: boolean;
318
+ hasChangedFromInitial: boolean;
319
+ consecutiveFramesStable: number;
320
+ diffFromInitial: number;
321
+ diffFromLast: number;
322
+ text: string;
323
+ };
324
+ network: {
325
+ enabled: boolean;
326
+ settled: boolean;
327
+ rxBytes: number;
328
+ txBytes: number;
329
+ text: string;
330
+ };
331
+ timeout: {
332
+ isTimeout: boolean;
333
+ elapsed: number;
334
+ max: number;
335
+ text: string;
336
+ };
337
+ }
338
+
339
+ interface RedrawCompleteEvent {
340
+ screenSettled: boolean;
341
+ hasChangedFromInitial: boolean;
342
+ consecutiveFramesStable: number;
343
+ networkSettled: boolean;
344
+ isTimeout: boolean;
345
+ timeElapsed: number;
346
+ }
347
+
348
+ interface SandboxProgressEvent {
349
+ step: string;
350
+ message: string;
351
+ }
352
+
353
+ interface SourcePosition {
354
+ file: string;
355
+ line: number;
356
+ column: number;
357
+ }
358
+ ```
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: "Exec Output Test Example"
3
+ sidebarTitle: "Exec Output"
4
+ description: "Example test demonstrating how to capture and use output from PowerShell exec commands."
5
+ icon: "terminal"
6
+ mode: "wide"
7
+ ---
8
+
9
+ ## Demo Test Run
10
+
11
+ Watch this test execute in a real sandbox environment:
12
+
13
+ {/* exec-output.test.mjs output */}
14
+ <iframe
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b42fc0ac3cc632a918c/replay"
16
+ width="100%"
17
+ height="390"
18
+ style={{ border: "1px solid #333", borderRadius: "8px" }}
19
+ allow="fullscreen"
20
+ />
21
+
22
+ ## Source Code
23
+
24
+ ```javascript title="exec-output.test.mjs" {22-27}
25
+ /**
26
+ * TestDriver SDK - Exec Output Test (Vitest)
27
+ * Converted from: testdriver/acceptance/exec-output.yaml
28
+ */
29
+
30
+ import { describe, expect, it } from "vitest";
31
+ import { TestDriver } from "../lib/vitest/hooks.mjs";
32
+ import { getDefaults } from "./config.mjs";
33
+
34
+ describe.skip("Exec Output Test", () => {
35
+ it(
36
+ "should set date using PowerShell and navigate to calendar",
37
+ async (context) => {
38
+ const testdriver = TestDriver(context, { ...getDefaults(context), headless: true });
39
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
40
+
41
+ //
42
+ // Generate date in query string format
43
+ const queryString = await testdriver.exec(
44
+ "pwsh",
45
+ `
46
+ $date = (Get-Date).AddMonths(1)
47
+ Write-Output $date.ToString("yyyy-MM-dd")
48
+ `,
49
+ 10000,
50
+ );
51
+
52
+ // Assert that the date is valid
53
+ const dateValidResult = await testdriver.assert(
54
+ `${queryString} is a valid date`,
55
+ );
56
+ expect(dateValidResult).toBeTruthy();
57
+
58
+ // Generate date in display format
59
+ const expectedDate = await testdriver.exec(
60
+ "pwsh",
61
+ `
62
+ $date = (Get-Date).AddMonths(1)
63
+ Write-Output $date.ToString("ddd MMM d yyyy")
64
+ `,
65
+ 10000,
66
+ );
67
+
68
+ // Navigate to calendar with date parameter
69
+ await testdriver.focusApplication("Google Chrome");
70
+ await testdriver.pressKeys(["ctrl", "l"]);
71
+ await testdriver.type(
72
+ `https://teamup.com/ks48cf2135e7e080bc?view=d&date=${queryString}`,
73
+ );
74
+ await testdriver.pressKeys(["enter"]);
75
+
76
+ // Assert that the expected date shows
77
+ await testdriver.focusApplication("Google Chrome");
78
+ const result = await testdriver.assert(
79
+ `the text ${expectedDate} is visible on screen`,
80
+ );
81
+ expect(result).toBeTruthy();
82
+ },
83
+ );
84
+ });
85
+ ```
@@ -0,0 +1,83 @@
1
+ ---
2
+ title: "Exec PowerShell Test Example"
3
+ sidebarTitle: "Exec Pwsh"
4
+ description: "Example test demonstrating how to generate dynamic data using PowerShell and use it in test interactions."
5
+ icon: "terminal"
6
+ mode: "wide"
7
+ ---
8
+
9
+ ## Demo Test Run
10
+
11
+ Watch this test execute in a real sandbox environment:
12
+
13
+ {/* exec-pwsh.test.mjs output */}
14
+ <iframe
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b42565d339e8065f180/replay"
16
+ width="100%"
17
+ height="390"
18
+ style={{ border: "1px solid #333", borderRadius: "8px" }}
19
+ allow="fullscreen"
20
+ />
21
+
22
+ ## Source Code
23
+
24
+ ```javascript title="exec-pwsh.test.mjs" {20-38}
25
+ /**
26
+ * TestDriver SDK - Exec Shell Test (Vitest)
27
+ * Converted from: testdriver/acceptance/exec-shell.yaml
28
+ */
29
+
30
+ import { describe, expect, it } from "vitest";
31
+ import { TestDriver } from "../lib/vitest/hooks.mjs";
32
+ import { getDefaults } from "./config.mjs";
33
+
34
+ describe.skip("Exec PowerShell Test", () => {
35
+ it(
36
+ "should generate random email using PowerShell and enter it",
37
+ async (context) => {
38
+ const testdriver = TestDriver(context, { ...getDefaults(context), headless: true });
39
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
40
+
41
+ //
42
+ // Generate random email using PowerShell
43
+ const randomEmail = await testdriver.exec({
44
+ language: "pwsh",
45
+ code: `
46
+ # Random email generator in PowerShell
47
+
48
+ # Arrays of possible names and domains
49
+ $firstNames = @("john", "jane", "alex", "chris", "sara", "mike", "lisa", "david", "emma", "ryan")
50
+ $lastNames = @("smith", "johnson", "williams", "brown", "jones", "garcia", "miller", "davis", "martin", "lee")
51
+ $domains = @("example.com", "testmail.com", "mailinator.com", "demo.org", "company.net")
52
+
53
+ # Random selection
54
+ $first = Get-Random -InputObject $firstNames
55
+ $last = Get-Random -InputObject $lastNames
56
+ $domain = Get-Random -InputObject $domains
57
+ $number = Get-Random -Minimum 1 -Maximum 1000
58
+
59
+ # Generate the email
60
+ $email = "$first.$last$number@$domain".ToLower()
61
+
62
+ # Output
63
+ Write-Output "$email"
64
+ `,
65
+ timeout: 10000,
66
+ });
67
+
68
+ // Enter the email in username field
69
+ const usernameField = await testdriver.find(
70
+ "Username, input field for username",
71
+ );
72
+ await usernameField.click();
73
+ await testdriver.type(randomEmail);
74
+
75
+ // Assert that the username field shows a valid email address
76
+ const result = await testdriver.assert(
77
+ `the username field contains ${randomEmail} which is a valid email address`,
78
+ );
79
+ expect(result).toBeTruthy();
80
+ },
81
+ );
82
+ });
83
+ ```
@@ -0,0 +1,62 @@
1
+ ---
2
+ title: "Focus Window Test Example"
3
+ sidebarTitle: "Focus Window"
4
+ description: "Example test demonstrating how to switch focus between application windows."
5
+ icon: "window-maximize"
6
+ mode: "wide"
7
+ ---
8
+
9
+ ## Demo Test Run
10
+
11
+ Watch this test execute in a real sandbox environment:
12
+
13
+ {/* focus-window.test.mjs output */}
14
+ <iframe
15
+ src="https://testdriver-api.onrender.com/api/v1/testdriver/testcase/69a62b4549845ced0b71e2b1/replay"
16
+ width="100%"
17
+ height="390"
18
+ style={{ border: "1px solid #333", borderRadius: "8px" }}
19
+ allow="fullscreen"
20
+ />
21
+
22
+ ## Source Code
23
+
24
+ ```javascript title="focus-window.test.mjs" {23-25}
25
+ /**
26
+ * TestDriver SDK - Focus Window Test (Vitest)
27
+ * Converted from: testdriver/acceptance/focus-window.yaml
28
+ */
29
+
30
+ import { describe, expect, it } from "vitest";
31
+ import { TestDriver } from "../lib/vitest/hooks.mjs";
32
+ import { getDefaults } from "./config.mjs";
33
+
34
+ describe("Focus Window Test", () => {
35
+ it.skip(
36
+ "should click Microsoft Edge icon and focus Google Chrome",
37
+ async (context) => {
38
+ const testdriver = TestDriver(context, { ...getDefaults(context), headless: true });
39
+ await testdriver.provision.chrome({ url: 'http://testdriver-sandbox.vercel.app/login' });
40
+
41
+ //
42
+ // Show desktop
43
+ await testdriver.pressKeys(["winleft", "d"]);
44
+
45
+ // Click on the Microsoft Edge icon
46
+ const edgeIcon = await testdriver.find(
47
+ "a blue and green swirl icon on the taskbar representing Microsoft Edge",
48
+ );
49
+ await edgeIcon.click();
50
+
51
+ // Focus Google Chrome
52
+ await testdriver.focusApplication("Google Chrome");
53
+
54
+ // Assert Chrome is focused (implicit through successful focus)
55
+ const result = await testdriver.assert(
56
+ "Google Chrome is the focused application",
57
+ );
58
+ expect(result).toBeTruthy();
59
+ },
60
+ );
61
+ });
62
+ ```
@@ -1,18 +1,56 @@
1
1
  ---
2
- title: "Cloud Plan"
3
- sidebarTitle: "Cloud"
2
+ title: "Hosted"
3
+ sidebarTitle: "Hosted"
4
4
  description: "The fastest way to get started with TestDriver. Just set your API key and start testing."
5
5
  icon: "cloud"
6
+ mode: "wide"
6
7
  ---
7
8
 
8
- Cloud pricing is based on **device-seconds**: the amount of time your tests run on **our infrastructure**.
9
+ Hosted pricing is based on **device-seconds**: the amount of time your tests run on **our infrastructure**.
9
10
 
10
11
  - **Zero Setup** — Start testing immediately. No DevOps required.
11
12
  - **Free Tier** — Get started with a limited preview at no cost.
12
13
  - **Pay As You Go** — Only pay for the device-seconds you use.
13
14
 
15
+ ## Hosted Plans
16
+
17
+ <CardGroup cols={3}>
18
+ <Card title="Free Trial" icon="gift" href="https://docs.testdriver.ai">
19
+ **$0/month**
20
+
21
+ - 1 Concurrent Sandbox
22
+ - 60 Minutes Included
23
+ - 1 Team User
24
+ - Community Support
25
+ </Card>
26
+
27
+ <Card title="Pro" icon="rocket" href="https://console.testdriver.ai/checkout/pro">
28
+ **$20/month**
29
+
30
+ - 2 Concurrent Sandboxes
31
+ - 600 Minutes Included
32
+ - Overage: $0.002/second
33
+ - 1 Team User
34
+ - Test Recordings
35
+ - Community Support
36
+ </Card>
37
+
38
+ <Card title="Team" icon="users" href="https://console.testdriver.ai/checkout/team">
39
+ **$600/month**
40
+
41
+ - 8 Concurrent Sandboxes
42
+ - 10,000 Minutes Included
43
+ - Overage: $0.001/second
44
+ - 5 Team Users
45
+ - Test Recordings
46
+ - Private Support
47
+ - Test Analytics
48
+ - CPU, RAM, & Network Profiles
49
+ </Card>
50
+ </CardGroup>
51
+
14
52
  ## Get Started
15
- Cloud is the default when you follow the Quickstart guide.
53
+ Hosted is the default when you follow the Quickstart guide.
16
54
  <Card
17
55
  title="Try the Quickstart"
18
56
  icon="play"
@@ -102,7 +140,7 @@ To prevent tests from failing due to exceeding your license slot limit, we recom
102
140
 
103
141
  ## When to Consider Self-Hosted
104
142
 
105
- Cloud is perfect for getting started and for teams that want zero infrastructure management. However, you might consider [Self-Hosted](/v7/self-hosted) if you:
143
+ Hosted is perfect for getting started and for teams that want zero infrastructure management. However, you might consider [Self-Hosted](/v7/self-hosted) if you:
106
144
 
107
145
  - Want to escape per-second billing with a flat license fee
108
146
  - Require greater concurrency than offered in Cloud plans
@@ -0,0 +1,9 @@
1
+ ---
2
+ title: "GitHub Copilot Guide"
3
+ sidebarTitle: "GitHub Copilot Guide"
4
+ description: "Execute natural language tasks using AI"
5
+ icon: "github"
6
+ ---
7
+
8
+ ## Overview
9
+