testdriverai 7.2.3 → 7.2.10

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 (142) hide show
  1. package/.github/workflows/publish.yaml +15 -7
  2. package/.github/workflows/testdriver.yml +163 -0
  3. package/.testdriver/last-sandbox +7 -0
  4. package/agent/events.js +1 -0
  5. package/agent/index.js +99 -163
  6. package/agent/lib/sandbox.js +11 -1
  7. package/agents.md +393 -0
  8. package/bin/testdriverai.js +8 -0
  9. package/debug/01-table-initial.png +0 -0
  10. package/debug/02-after-ai-explore.png +0 -0
  11. package/debug/02-after-scroll.png +0 -0
  12. package/debugger/index.html +37 -0
  13. package/docs/docs.json +93 -125
  14. package/docs/v7/_drafts/architecture.mdx +1 -26
  15. package/docs/v7/_drafts/caching.mdx +2 -2
  16. package/docs/v7/{getting-started → _drafts}/installation.mdx +0 -66
  17. package/docs/v7/{features/coverage.mdx → _drafts/powerful.mdx} +1 -90
  18. package/docs/v7/_drafts/quick-start-test-recording.mdx +0 -1
  19. package/docs/v7/{features → _drafts}/scalable.mdx +126 -4
  20. package/docs/v7/_drafts/screenshot.mdx +155 -0
  21. package/docs/v7/_drafts/test-recording.mdx +0 -6
  22. package/docs/v7/_drafts/writing-tests.mdx +25 -0
  23. package/docs/v7/{api/act.mdx → ai.mdx} +28 -27
  24. package/docs/v7/{api/assert.mdx → assert.mdx} +3 -3
  25. package/docs/v7/aws-setup.mdx +338 -0
  26. package/docs/v7/caching.mdx +128 -0
  27. package/docs/v7/ci-cd.mdx +605 -0
  28. package/docs/v7/{api/click.mdx → click.mdx} +4 -4
  29. package/docs/v7/cloud.mdx +120 -0
  30. package/docs/v7/customizing-devices.mdx +129 -0
  31. package/docs/v7/{api/doubleClick.mdx → double-click.mdx} +5 -5
  32. package/docs/v7/enterprise.mdx +135 -0
  33. package/docs/v7/examples.mdx +5 -0
  34. package/docs/v7/{api/exec.mdx → exec.mdx} +3 -3
  35. package/docs/v7/{api/find.mdx → find.mdx} +17 -21
  36. package/docs/v7/{api/focusApplication.mdx → focus-application.mdx} +3 -3
  37. package/docs/v7/generating-tests.mdx +32 -0
  38. package/docs/v7/{api/hover.mdx → hover.mdx} +3 -3
  39. package/docs/v7/locating-elements.mdx +71 -0
  40. package/docs/v7/making-assertions.mdx +32 -0
  41. package/docs/v7/{api/mouseDown.mdx → mouse-down.mdx} +7 -7
  42. package/docs/v7/{api/mouseUp.mdx → mouse-up.mdx} +8 -8
  43. package/docs/v7/performing-actions.mdx +51 -0
  44. package/docs/v7/{api/pressKeys.mdx → press-keys.mdx} +3 -3
  45. package/docs/v7/quickstart.mdx +162 -0
  46. package/docs/v7/reusable-code.mdx +240 -0
  47. package/docs/v7/{api/rightClick.mdx → right-click.mdx} +5 -5
  48. package/docs/v7/running-tests.mdx +181 -0
  49. package/docs/v7/{api/scroll.mdx → scroll.mdx} +3 -3
  50. package/docs/v7/secrets.mdx +115 -0
  51. package/docs/v7/self-hosted.mdx +66 -0
  52. package/docs/v7/{api/type.mdx → type.mdx} +3 -3
  53. package/docs/v7/variables.mdx +111 -0
  54. package/docs/v7/waiting-for-elements.mdx +66 -0
  55. package/docs/v7/what-is-testdriver.mdx +54 -0
  56. package/interfaces/cli/commands/init.js +33 -19
  57. package/interfaces/cli/lib/base.js +24 -0
  58. package/interfaces/cli.js +8 -1
  59. package/interfaces/logger.js +8 -3
  60. package/interfaces/vitest-plugin.mjs +16 -71
  61. package/lib/sentry.js +343 -0
  62. package/lib/vitest/hooks.mjs +81 -81
  63. package/package.json +4 -3
  64. package/sdk-log-formatter.js +41 -0
  65. package/sdk.d.ts +22 -9
  66. package/sdk.js +344 -100
  67. package/test/manual/reconnect-provision.test.mjs +49 -0
  68. package/test/manual/reconnect-signin.test.mjs +41 -0
  69. package/test/testdriver/act.test.mjs +30 -0
  70. package/test/testdriver/ai.test.mjs +30 -0
  71. package/test/testdriver/assert.test.mjs +1 -1
  72. package/test/testdriver/hover-text.test.mjs +1 -1
  73. package/test/testdriver/setup/testHelpers.mjs +8 -119
  74. package/test/testdriver/windows-installer.test.mjs +61 -0
  75. package/tests/example.test.js +33 -0
  76. package/tests/login.js +28 -0
  77. package/tests/table-sort-enrollments.test.mjs +72 -0
  78. package/tests/table-sort-experiment.test.mjs +42 -0
  79. package/tests/table-sort-setup.test.mjs +59 -0
  80. package/vitest.config.mjs +3 -1
  81. package/agent/lib/cache.js +0 -142
  82. package/docs/v7/api/assertions.mdx +0 -403
  83. package/docs/v7/features/ai-native.mdx +0 -413
  84. package/docs/v7/features/application-logs.mdx +0 -353
  85. package/docs/v7/features/browser-logs.mdx +0 -414
  86. package/docs/v7/features/cache-management.mdx +0 -402
  87. package/docs/v7/features/continuous-testing.mdx +0 -346
  88. package/docs/v7/features/data-driven-testing.mdx +0 -441
  89. package/docs/v7/features/easy-to-write.mdx +0 -280
  90. package/docs/v7/features/enterprise.mdx +0 -656
  91. package/docs/v7/features/fast.mdx +0 -406
  92. package/docs/v7/features/managed-sandboxes.mdx +0 -384
  93. package/docs/v7/features/network-monitoring.mdx +0 -568
  94. package/docs/v7/features/parallel-execution.mdx +0 -381
  95. package/docs/v7/features/powerful.mdx +0 -531
  96. package/docs/v7/features/sandbox-customization.mdx +0 -229
  97. package/docs/v7/features/stable.mdx +0 -473
  98. package/docs/v7/features/system-performance.mdx +0 -616
  99. package/docs/v7/features/test-analytics.mdx +0 -373
  100. package/docs/v7/features/test-cases.mdx +0 -393
  101. package/docs/v7/features/test-replays.mdx +0 -408
  102. package/docs/v7/features/test-reports.mdx +0 -308
  103. package/docs/v7/getting-started/debugging-tests.mdx +0 -382
  104. package/docs/v7/getting-started/quickstart.mdx +0 -90
  105. package/docs/v7/getting-started/running-tests.mdx +0 -173
  106. package/docs/v7/getting-started/setting-up-in-ci.mdx +0 -612
  107. package/docs/v7/getting-started/writing-tests.mdx +0 -534
  108. package/docs/v7/overview/what-is-testdriver.mdx +0 -386
  109. package/docs/v7/presets/chrome-extension.mdx +0 -248
  110. package/docs/v7/presets/chrome.mdx +0 -300
  111. package/docs/v7/presets/electron.mdx +0 -460
  112. package/docs/v7/presets/vscode.mdx +0 -417
  113. package/docs/v7/presets/webapp.mdx +0 -393
  114. /package/docs/v7/{commands → _drafts/commands}/assert.mdx +0 -0
  115. /package/docs/v7/{commands → _drafts/commands}/exec.mdx +0 -0
  116. /package/docs/v7/{commands → _drafts/commands}/focus-application.mdx +0 -0
  117. /package/docs/v7/{commands → _drafts/commands}/hover-image.mdx +0 -0
  118. /package/docs/v7/{commands → _drafts/commands}/hover-text.mdx +0 -0
  119. /package/docs/v7/{commands → _drafts/commands}/if.mdx +0 -0
  120. /package/docs/v7/{commands → _drafts/commands}/match-image.mdx +0 -0
  121. /package/docs/v7/{commands → _drafts/commands}/press-keys.mdx +0 -0
  122. /package/docs/v7/{commands → _drafts/commands}/remember.mdx +0 -0
  123. /package/docs/v7/{commands → _drafts/commands}/run.mdx +0 -0
  124. /package/docs/v7/{commands → _drafts/commands}/scroll-until-image.mdx +0 -0
  125. /package/docs/v7/{commands → _drafts/commands}/scroll-until-text.mdx +0 -0
  126. /package/docs/v7/{commands → _drafts/commands}/scroll.mdx +0 -0
  127. /package/docs/v7/{commands → _drafts/commands}/type.mdx +0 -0
  128. /package/docs/v7/{commands → _drafts/commands}/wait-for-image.mdx +0 -0
  129. /package/docs/v7/{commands → _drafts/commands}/wait-for-text.mdx +0 -0
  130. /package/docs/v7/{commands → _drafts/commands}/wait.mdx +0 -0
  131. /package/docs/v7/{getting-started → _drafts}/configuration.mdx +0 -0
  132. /package/docs/v7/{features → _drafts}/observable.mdx +0 -0
  133. /package/docs/v7/{platforms → _drafts/platforms}/linux.mdx +0 -0
  134. /package/docs/v7/{platforms → _drafts/platforms}/macos.mdx +0 -0
  135. /package/docs/v7/{platforms → _drafts/platforms}/windows.mdx +0 -0
  136. /package/docs/v7/{playwright.mdx → _drafts/playwright.mdx} +0 -0
  137. /package/docs/v7/{overview → _drafts}/readme.mdx +0 -0
  138. /package/docs/v7/{features → _drafts}/reports.mdx +0 -0
  139. /package/docs/v7/{api/client.mdx → client.mdx} +0 -0
  140. /package/docs/v7/{api/dashcam.mdx → dashcam.mdx} +0 -0
  141. /package/docs/v7/{api/elements.mdx → elements.mdx} +0 -0
  142. /package/docs/v7/{api/sandbox.mdx → sandbox.mdx} +0 -0
@@ -0,0 +1,155 @@
1
+ ---
2
+ title: "screenshot()"
3
+ sidebarTitle: "screenshot"
4
+ description: "Capture a screenshot of the current screen"
5
+ icon: "camera"
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ Capture a screenshot of the current sandbox screen. Returns a base64 encoded PNG, or saves directly to a file and returns the filepath.
11
+
12
+ ## Syntax
13
+
14
+ ```javascript
15
+ // Options object (recommended)
16
+ await testdriver.screenshot(options)
17
+
18
+ // Legacy positional arguments
19
+ await testdriver.screenshot(scale, silent, mouse)
20
+ ```
21
+
22
+ ## Parameters
23
+
24
+ <ParamField path="options" type="object">
25
+ Options object for screenshot capture
26
+ </ParamField>
27
+
28
+ <ParamField path="options.scale" type="number" default="1">
29
+ Scale factor for the screenshot (1 = original size, 0.5 = half size, etc.)
30
+ </ParamField>
31
+
32
+ <ParamField path="options.silent" type="boolean" default="false">
33
+ Whether to suppress logging output
34
+ </ParamField>
35
+
36
+ <ParamField path="options.mouse" type="boolean" default="false">
37
+ Whether to include the mouse cursor in the screenshot
38
+ </ParamField>
39
+
40
+ <ParamField path="options.path" type="string">
41
+ File path to save the screenshot. If provided, the screenshot is saved to this file and the filepath is returned instead of base64 data. Directories are created automatically if they don't exist.
42
+ </ParamField>
43
+
44
+ ## Returns
45
+
46
+ `Promise<string>` - Base64 encoded PNG screenshot, or the filepath if `options.path` was provided.
47
+
48
+ ## Examples
49
+
50
+ ### Basic Screenshot (Base64)
51
+
52
+ ```javascript
53
+ // Capture a screenshot and get base64 data
54
+ const screenshot = await testdriver.screenshot();
55
+
56
+ // Write to file manually
57
+ import fs from 'fs';
58
+ fs.writeFileSync('screenshot.png', Buffer.from(screenshot, 'base64'));
59
+ ```
60
+
61
+ ### Save Directly to File
62
+
63
+ ```javascript
64
+ // Save screenshot directly to a file (returns filepath)
65
+ const filepath = await testdriver.screenshot({ path: './screenshots/test.png' });
66
+ console.log(`Screenshot saved to: ${filepath}`);
67
+ ```
68
+
69
+ ### With Mouse Cursor
70
+
71
+ ```javascript
72
+ // Include mouse cursor in screenshot
73
+ const screenshot = await testdriver.screenshot({ mouse: true });
74
+
75
+ // Or save to file with mouse cursor
76
+ const filepath = await testdriver.screenshot({
77
+ path: './debug/with-cursor.png',
78
+ mouse: true
79
+ });
80
+ ```
81
+
82
+ ### Scaled Screenshot
83
+
84
+ ```javascript
85
+ // Capture at half size (useful for reducing file size)
86
+ const screenshot = await testdriver.screenshot({ scale: 0.5 });
87
+
88
+ // Capture at double size
89
+ const filepath = await testdriver.screenshot({
90
+ path: './hires.png',
91
+ scale: 2
92
+ });
93
+ ```
94
+
95
+ ### Silent Mode
96
+
97
+ ```javascript
98
+ // Capture without logging output
99
+ const screenshot = await testdriver.screenshot({ silent: true });
100
+ ```
101
+
102
+ ### Legacy Usage
103
+
104
+ ```javascript
105
+ // Legacy positional arguments still work
106
+ const screenshot = await testdriver.screenshot(1, false, true);
107
+ // Equivalent to: screenshot({ scale: 1, silent: false, mouse: true })
108
+ ```
109
+
110
+ ## Use Cases
111
+
112
+ ### Debugging Test Failures
113
+
114
+ ```javascript
115
+ it('should complete checkout', async (context) => {
116
+ const testdriver = TestDriver(context, { newSandbox: true });
117
+ await testdriver.provision.chrome({ url: 'https://shop.example.com' });
118
+
119
+ try {
120
+ await testdriver.find('Checkout button').click();
121
+ await testdriver.assert('Order confirmation is visible');
122
+ } catch (error) {
123
+ // Save screenshot on failure for debugging
124
+ await testdriver.screenshot({ path: './failures/checkout-failed.png' });
125
+ throw error;
126
+ }
127
+ });
128
+ ```
129
+
130
+ ### Visual Documentation
131
+
132
+ ```javascript
133
+ // Capture screenshots at each step for documentation
134
+ await testdriver.screenshot({ path: './docs/step1-login.png' });
135
+ await testdriver.find('Login button').click();
136
+ await testdriver.screenshot({ path: './docs/step2-dashboard.png' });
137
+ ```
138
+
139
+ ### Comparing States
140
+
141
+ ```javascript
142
+ // Capture before and after states
143
+ const before = await testdriver.screenshot();
144
+ await testdriver.find('Toggle dark mode').click();
145
+ const after = await testdriver.screenshot();
146
+
147
+ // Both are base64 - can be compared or stored
148
+ ```
149
+
150
+ ## Notes
151
+
152
+ - Screenshots are captured from the sandbox's virtual display
153
+ - The base64 string does not include a data URI prefix (raw base64)
154
+ - When using `path`, directories are created automatically if they don't exist
155
+ - File format is always PNG regardless of the file extension used
@@ -376,12 +376,6 @@ const run2 = await client.createTestRun({
376
376
  - Associated dashcam replay
377
377
  - Timing and duration
378
378
 
379
- ### TdSandbox
380
- - VM/sandbox lifecycle tracking
381
- - Platform and OS information
382
- - Dashcam integration status
383
- - Cost and usage metrics
384
-
385
379
  ### Replay
386
380
  - Dashcam recordings
387
381
  - Linked to test runs and cases
@@ -0,0 +1,25 @@
1
+ ---
2
+ title: "Writing Tests"
3
+ description: "Learn how to write effective TestDriver tests with natural language"
4
+ icon: "pencil"
5
+ ---
6
+
7
+ Learn how to write effective TestDriver tests. This section covers everything from generating tests with AI to making assertions.
8
+
9
+ <CardGroup cols={2}>
10
+ <Card title="Generating Tests" icon="wand-magic-sparkles" href="./generating-tests">
11
+ Use AI coding agents and exploration mode to generate tests
12
+ </Card>
13
+ <Card title="Locating Elements" icon="crosshairs" href="./locating-elements">
14
+ Find UI elements using natural language descriptions
15
+ </Card>
16
+ <Card title="Waiting for Elements" icon="clock" href="./waiting-for-elements">
17
+ Handle async operations and prevent flaky tests
18
+ </Card>
19
+ <Card title="Performing Actions" icon="computer-mouse" href="./performing-actions">
20
+ Click, type, hover, scroll and more
21
+ </Card>
22
+ <Card title="Making Assertions" icon="check-double" href="./making-assertions">
23
+ Verify application state with AI-powered assertions
24
+ </Card>
25
+ </CardGroup>
@@ -1,18 +1,19 @@
1
1
  ---
2
- title: "act()"
3
- sidebarTitle: "act"
2
+ title: "ai()"
3
+ sidebarTitle: "ai"
4
4
  description: "Execute natural language tasks using AI"
5
5
  icon: "wand-magic-sparkles"
6
+ tag: beta
6
7
  ---
7
8
 
8
9
  ## Overview
9
10
 
10
- The `act()` method allows you to execute complex tasks using natural language descriptions. TestDriver's AI will figure out the steps needed to accomplish the task.
11
+ The `ai()` method allows you to execute complex tasks using natural language descriptions. TestDriver's AI will figure out the steps needed to accomplish the task.
11
12
 
12
13
  ## Syntax
13
14
 
14
15
  ```javascript
15
- await testdriver.act(task, options)
16
+ await testdriver.ai(task, options)
16
17
  ```
17
18
 
18
19
  ## Parameters
@@ -41,20 +42,20 @@ await testdriver.act(task, options)
41
42
 
42
43
  ```javascript
43
44
  // Simple task execution
44
- await testdriver.act('Click the submit button');
45
+ await testdriver.ai('Click the submit button');
45
46
 
46
47
  // Complex multi-step task
47
- await testdriver.act('Fill out the contact form and submit it');
48
+ await testdriver.ai('Fill out the contact form and submit it');
48
49
 
49
50
  // Navigation task
50
- await testdriver.act('Go to the settings page and enable notifications');
51
+ await testdriver.ai('Go to the settings page and enable notifications');
51
52
  ```
52
53
 
53
54
  ### With Validation
54
55
 
55
56
  ```javascript
56
57
  // AI will verify the task completed successfully
57
- const result = await testdriver.act('Complete the checkout process', {
58
+ const result = await testdriver.ai('Complete the checkout process', {
58
59
  validateAndLoop: true
59
60
  });
60
61
 
@@ -65,10 +66,10 @@ console.log('Task result:', result);
65
66
 
66
67
  ```javascript
67
68
  // The AI will break down complex tasks
68
- await testdriver.act('Search for "laptop", add the first result to cart, and proceed to checkout');
69
+ await testdriver.ai('Search for "laptop", add the first result to cart, and proceed to checkout');
69
70
 
70
71
  // UI exploration
71
- await testdriver.act('Find and click all menu items to explore the application');
72
+ await testdriver.ai('Find and click all menu items to explore the application');
72
73
  ```
73
74
 
74
75
  ## Use Cases
@@ -78,8 +79,8 @@ await testdriver.act('Find and click all menu items to explore the application')
78
79
  Use AI to explore unfamiliar applications:
79
80
 
80
81
  ```javascript
81
- await testdriver.act('Explore the main navigation menu');
82
- await testdriver.act('Try to find the user profile settings');
82
+ await testdriver.ai('Explore the main navigation menu');
83
+ await testdriver.ai('Try to find the user profile settings');
83
84
  ```
84
85
  </Accordion>
85
86
 
@@ -87,8 +88,8 @@ await testdriver.act('Find and click all menu items to explore the application')
87
88
  Let AI handle multi-step processes:
88
89
 
89
90
  ```javascript
90
- await testdriver.act('Complete the multi-step registration form');
91
- await testdriver.act('Configure all the advanced settings to default values');
91
+ await testdriver.ai('Complete the multi-step registration form');
92
+ await testdriver.ai('Configure all the advanced settings to default values');
92
93
  ```
93
94
  </Accordion>
94
95
 
@@ -96,8 +97,8 @@ await testdriver.act('Find and click all menu items to explore the application')
96
97
  When exact element locations aren't critical:
97
98
 
98
99
  ```javascript
99
- await testdriver.act('Close any popup dialogs');
100
- await testdriver.act('Accept the cookie consent if it appears');
100
+ await testdriver.ai('Close any popup dialogs');
101
+ await testdriver.ai('Accept the cookie consent if it appears');
101
102
  ```
102
103
  </Accordion>
103
104
  </AccordionGroup>
@@ -109,13 +110,13 @@ await testdriver.act('Find and click all menu items to explore the application')
109
110
 
110
111
  ```javascript
111
112
  // ✅ Good
112
- await testdriver.act('Add the first product to the shopping cart');
113
+ await testdriver.ai('Add the first product to the shopping cart');
113
114
 
114
115
  // ❌ Too vague
115
- await testdriver.act('do something');
116
+ await testdriver.ai('do something');
116
117
 
117
118
  // ❌ Too specific (defeats the purpose)
118
- await testdriver.act('click at coordinates 500, 300');
119
+ await testdriver.ai('click at coordinates 500, 300');
119
120
  ```
120
121
  </Check>
121
122
 
@@ -124,7 +125,7 @@ await testdriver.act('Find and click all menu items to explore the application')
124
125
 
125
126
  ```javascript
126
127
  // Use AI for setup
127
- await testdriver.act('Navigate to the login page');
128
+ await testdriver.ai('Navigate to the login page');
128
129
 
129
130
  // Use explicit methods for critical steps
130
131
  const usernameField = await testdriver.find('username input');
@@ -141,7 +142,7 @@ await testdriver.act('Find and click all menu items to explore the application')
141
142
 
142
143
  ## When to Use AI vs Explicit Methods
143
144
 
144
- ### Use `act()` when:
145
+ ### Use `ai()` when:
145
146
  - Exploring unfamiliar applications
146
147
  - Handling optional UI elements (popups, cookies, etc.)
147
148
  - Prototyping tests quickly
@@ -177,8 +178,8 @@ describe('E-commerce Flow with AI', () => {
177
178
  await testdriver.focusApplication('Google Chrome');
178
179
 
179
180
  // Use AI for navigation and exploration
180
- await testdriver.act('Browse to the electronics section');
181
- await testdriver.act('Find and add a laptop to the cart');
181
+ await testdriver.ai('Browse to the electronics section');
182
+ await testdriver.ai('Find and add a laptop to the cart');
182
183
 
183
184
  // Use explicit methods for critical steps
184
185
  const cartIcon = await testdriver.find('shopping cart icon');
@@ -188,7 +189,7 @@ describe('E-commerce Flow with AI', () => {
188
189
  console.log('Cart total:', total);
189
190
 
190
191
  // Use AI for checkout flow
191
- await testdriver.act('Proceed to checkout and fill in shipping details', {
192
+ await testdriver.ai('Proceed to checkout and fill in shipping details', {
192
193
  validateAndLoop: true
193
194
  });
194
195
 
@@ -200,6 +201,6 @@ describe('E-commerce Flow with AI', () => {
200
201
 
201
202
  ## Related Methods
202
203
 
203
- - [`find()`](/v7/api/find) - Locate specific elements
204
- - [`assert()`](/v7/api/assert) - Make assertions
205
- - [`extract()`](/v7/api/extract) - Extract information
204
+ - [`find()`](/v7/find) - Locate specific elements
205
+ - [`assert()`](/v7/assert) - Make assertions
206
+ - [`extract()`](/v7/extract) - Extract information
@@ -280,6 +280,6 @@ describe('Assertions', () => {
280
280
 
281
281
  ## Related Methods
282
282
 
283
- - [`extract()`](/v7/api/extract) - Extract information for detailed assertions
284
- - [`find()`](/v7/api/find) - Locate elements to verify
285
- - [`ai()`](/v7/api/ai) - Complex AI-driven tasks
283
+ - [`extract()`](/v7/extract) - Extract information for detailed assertions
284
+ - [`find()`](/v7/find) - Locate elements to verify
285
+ - [`ai()`](/v7/ai) - Complex AI-driven tasks
@@ -0,0 +1,338 @@
1
+ ---
2
+ title: "AWS Setup Guide"
3
+ sidebarTitle: "AWS Setup"
4
+ description: "Deploy TestDriver on your AWS infrastructure using CloudFormation"
5
+ icon: "aws"
6
+ ---
7
+
8
+ This guide walks you through setting up self-hosted TestDriver instances on AWS. By the end, you'll have a complete infrastructure that can spawn test environments on-demand and integrate with your CI/CD pipelines.
9
+
10
+ ```mermaid
11
+ graph LR
12
+ A[Vitest] <--> B[api.testdriver.ai]
13
+ B <--> C[Your AWS EC2 Instance]
14
+ ```
15
+
16
+ ## Overview
17
+
18
+ The setup process involves three main steps:
19
+
20
+ 1. **CloudFormation Infrastructure** — Deploy our template to create VPC, security groups, IAM roles, and launch templates
21
+ 2. **On-Demand Instance Spawning** — Use `spawn-runner.sh` to launch TestDriver instances when you need to run tests
22
+ 3. **CI/CD Integration** — Automate the lifecycle in GitHub Actions: spawn instance → run tests → terminate instance
23
+
24
+ ## Prerequisites
25
+
26
+ Before you begin, ensure you have:
27
+
28
+ - AWS account with CloudFormation permissions
29
+ - [AWS CLI](https://aws.amazon.com/cli/) installed and configured (`aws configure`)
30
+ - Access to the TestDriver AMI — [Contact us](https://form.typeform.com/to/UECf9rDx?typeform-source=testdriver.ai) with your AWS region
31
+ - A GitHub repository for your tests
32
+
33
+ <Tip>
34
+ The TestDriver Golden Image AMI ID is `ami-086b5b4b86d78987c`. Contact us to get access in your preferred AWS region.
35
+ </Tip>
36
+
37
+ ## Step 1: Deploy CloudFormation Stack
38
+
39
+ Our CloudFormation template creates all the AWS infrastructure you need:
40
+
41
+ - Dedicated VPC with public subnet
42
+ - Security group with required port access
43
+ - IAM roles and instance profiles
44
+ - EC2 launch template for instance creation
45
+
46
+ Download the template from the [TestDriver CLI repository](https://github.com/testdriverai/testdriverai/tree/main/setup/aws/cloudformation.yaml), then deploy:
47
+
48
+ ```bash
49
+ aws cloudformation deploy \
50
+ --template-file setup/aws/cloudformation.yaml \
51
+ --stack-name testdriver-infrastructure \
52
+ --parameter-overrides \
53
+ ProjectTag=testdriver \
54
+ AllowedIngressCidr=0.0.0.0/0 \
55
+ InstanceType=c5.xlarge \
56
+ CreateKeyPair=true \
57
+ --capabilities CAPABILITY_IAM
58
+ ```
59
+
60
+ <Warning>
61
+ **Security**: Replace `AllowedIngressCidr=0.0.0.0/0` with your specific IP ranges to restrict VPC access.
62
+ </Warning>
63
+
64
+ ### Get Your Launch Template ID
65
+
66
+ After deployment completes, retrieve the launch template ID:
67
+
68
+ ```bash
69
+ aws cloudformation describe-stacks \
70
+ --stack-name testdriver-infrastructure \
71
+ --query 'Stacks[0].Outputs[?OutputKey==`LaunchTemplateId`].OutputValue' \
72
+ --output text
73
+ ```
74
+
75
+ <Tip>
76
+ **Save this ID** — you'll need it for spawning instances and CI configuration.
77
+ </Tip>
78
+
79
+ ## Step 2: Spawn Test Instances
80
+
81
+ Use the `spawn-runner.sh` script to launch instances on-demand. This script:
82
+
83
+ - Launches a new EC2 instance using your launch template
84
+ - Waits for the instance to be ready
85
+ - Completes the TestDriver handshake
86
+ - Returns instance details for test execution
87
+
88
+ ```bash
89
+ # Set environment variables
90
+ export AWS_REGION=us-east-2
91
+ export AMI_ID=ami-086b5b4b86d78987c
92
+ export AWS_LAUNCH_TEMPLATE_ID=lt-your-template-id
93
+ export RESOLUTION=1440x900 # Optional, default is 1440x900
94
+
95
+ # Spawn an instance
96
+ ./setup/aws/spawn-runner.sh
97
+ ```
98
+
99
+ Output:
100
+ ```
101
+ PUBLIC_IP=1.2.3.4
102
+ INSTANCE_ID=i-1234567890abcdef0
103
+ AWS_REGION=us-east-2
104
+ ```
105
+
106
+ ### Run Tests Against Your Instance
107
+
108
+ With Vitest, specify the instance IP using the `TD_VM` environment variable:
109
+
110
+ ```bash
111
+ TD_API_KEY=your-api-key TD_VM=1.2.3.4 npx vitest run
112
+ ```
113
+
114
+ ### Terminate Instances
115
+
116
+ Always terminate instances after tests complete to avoid unnecessary costs:
117
+
118
+ ```bash
119
+ aws ec2 terminate-instances \
120
+ --instance-ids i-1234567890abcdef0 \
121
+ --region us-east-2
122
+ ```
123
+
124
+ <Note>
125
+ Instances are tagged with `Name=TestDriverRunner` and your `ProjectTag` for easy identification in the AWS console.
126
+ </Note>
127
+
128
+ ## Step 3: GitHub Actions Integration
129
+
130
+ Automate the full lifecycle in your CI/CD pipeline. This workflow spawns an instance, runs tests, and terminates the instance:
131
+
132
+ ```yaml .github/workflows/test.yml
133
+ name: TestDriver Self-Hosted
134
+
135
+ on:
136
+ push:
137
+ branches: [main]
138
+ pull_request:
139
+
140
+ # Prevent concurrent runs from competing for resources
141
+ concurrency:
142
+ group: ${{ github.workflow }}-${{ github.ref }}
143
+ cancel-in-progress: true
144
+
145
+ jobs:
146
+ test:
147
+ runs-on: ubuntu-latest
148
+ steps:
149
+ - name: Checkout repository
150
+ uses: actions/checkout@v4
151
+
152
+ - name: Setup Node.js
153
+ uses: actions/setup-node@v4
154
+ with:
155
+ node-version: '20'
156
+
157
+ - name: Install dependencies
158
+ run: npm install
159
+
160
+ - name: Setup AWS Instance
161
+ id: aws-setup
162
+ run: |
163
+ OUTPUT=$(./setup/aws/spawn-runner.sh | tee /dev/stderr)
164
+ PUBLIC_IP=$(echo "$OUTPUT" | grep "PUBLIC_IP=" | cut -d'=' -f2)
165
+ INSTANCE_ID=$(echo "$OUTPUT" | grep "INSTANCE_ID=" | cut -d'=' -f2)
166
+ echo "public-ip=$PUBLIC_IP" >> $GITHUB_OUTPUT
167
+ echo "instance-id=$INSTANCE_ID" >> $GITHUB_OUTPUT
168
+ env:
169
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
170
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
171
+ AWS_REGION: ${{ secrets.AWS_REGION }}
172
+ AWS_LAUNCH_TEMPLATE_ID: ${{ secrets.AWS_LAUNCH_TEMPLATE_ID }}
173
+ AMI_ID: ${{ secrets.AMI_ID }}
174
+
175
+ - name: Run Tests
176
+ run: npx vitest run
177
+ env:
178
+ TD_API_KEY: ${{ secrets.TD_API_KEY }}
179
+ TD_VM: ${{ steps.aws-setup.outputs.public-ip }}
180
+
181
+ - name: Shutdown AWS Instance
182
+ if: always()
183
+ run: |
184
+ aws ec2 terminate-instances \
185
+ --region ${{ secrets.AWS_REGION }} \
186
+ --instance-ids ${{ steps.aws-setup.outputs.instance-id }}
187
+ env:
188
+ AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
189
+ AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
190
+ AWS_REGION: ${{ secrets.AWS_REGION }}
191
+ ```
192
+
193
+ ### Required GitHub Secrets
194
+
195
+ | Secret | Description | Example |
196
+ |--------|-------------|---------|
197
+ | `AWS_ACCESS_KEY_ID` | AWS access key | `AKIAIOSFODNN7EXAMPLE` |
198
+ | `AWS_SECRET_ACCESS_KEY` | AWS secret key | `wJalrXUtnFEMI/K7MDENG...` |
199
+ | `AWS_REGION` | AWS region | `us-east-2` |
200
+ | `AWS_LAUNCH_TEMPLATE_ID` | From CloudFormation output | `lt-07c53ce8349b958d1` |
201
+ | `AMI_ID` | TestDriver AMI ID | `ami-086b5b4b86d78987c` |
202
+ | `TD_API_KEY` | Your TestDriver API key | From [console.testdriver.ai](https://console.testdriver.ai) |
203
+
204
+ ## AMI Customization
205
+
206
+ The TestDriver Golden Image comes pre-configured with:
207
+
208
+ - Windows Server with desktop environment
209
+ - VNC + web server for remote access
210
+ - Python, Node.js, Git
211
+ - TestDriver agent and dependencies
212
+
213
+ ### Creating a Custom AMI
214
+
215
+ You can customize the AMI to include additional software or configurations:
216
+
217
+ <Steps>
218
+ <Step title="Connect via RDP">
219
+ Use the default credentials:
220
+ - **Username**: `testdriver`
221
+ - **Password**: `changemeABC123`
222
+ </Step>
223
+
224
+ <Step title="Change the Password">
225
+ **Critical**: Run the password rotation script immediately:
226
+ ```powershell
227
+ C:\testdriver\RotateLocalPasswords.ps1
228
+ ```
229
+ Save the new password securely.
230
+ </Step>
231
+
232
+ <Step title="Install Your Software">
233
+ Install any additional dependencies, configure settings, or modify the environment as needed.
234
+ </Step>
235
+
236
+ <Step title="Create New AMI">
237
+ Use the AWS console or CLI to create an AMI from your modified instance. Update your workflow to use the new AMI ID.
238
+ </Step>
239
+ </Steps>
240
+
241
+ <Warning>
242
+ **Security**: Never use the default password in production. Always rotate passwords before creating custom AMIs.
243
+ </Warning>
244
+
245
+ ### Changing Screen Resolution
246
+
247
+ Set resolution via environment variable when spawning:
248
+
249
+ ```bash
250
+ export RESOLUTION=1920x1080
251
+ ./setup/aws/spawn-runner.sh
252
+ ```
253
+
254
+ Or in a lifecycle provision script:
255
+
256
+ ```yaml lifecycle/provision.yaml
257
+ version: 7.0.0
258
+ steps:
259
+ - prompt: set screen resolution
260
+ commands:
261
+ - command: exec
262
+ lang: pwsh
263
+ code: |
264
+ C:\testdriver\SetResolution.ps1 -Width 1920 -Height 1080
265
+ ```
266
+
267
+ ## Security Best Practices
268
+
269
+ ### Network Security
270
+
271
+ - **Restrict CIDR blocks** — Only allow access from known IP ranges
272
+ - **Use VPC endpoints** — For private AWS service communication
273
+ - **Enable VPC Flow Logs** — For network monitoring
274
+
275
+ ### AWS Authentication
276
+
277
+ Use OIDC instead of long-term credentials for GitHub Actions:
278
+
279
+ ```yaml
280
+ permissions:
281
+ id-token: write
282
+ contents: read
283
+
284
+ steps:
285
+ - name: Configure AWS credentials
286
+ uses: aws-actions/configure-aws-credentials@v4
287
+ with:
288
+ role-to-assume: arn:aws:iam::123456789012:role/GitHubActionsRole
289
+ aws-region: us-east-2
290
+ ```
291
+
292
+ See [GitHub's OIDC documentation](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) for setup instructions.
293
+
294
+ ### Instance Security
295
+
296
+ - **Terminate immediately** after tests complete
297
+ - **Monitor costs** with AWS billing alerts
298
+ - **Use least-privilege IAM roles**
299
+ - **Enable CloudTrail** for audit logging
300
+
301
+ ## Troubleshooting
302
+
303
+ ### Instance Not Responding
304
+
305
+ - **Check security groups** — Verify required ports are open (RDP 3389, VNC 5900, TestDriver ports)
306
+ - **Verify status checks** — Ensure instance has passed AWS status checks
307
+ - **Check AMI compatibility** — Some instance types don't support certain AMIs
308
+
309
+ ### Connection Timeouts
310
+
311
+ - Verify network connectivity from CI runner to instance
312
+ - Check VPC routing and internet gateway configuration
313
+ - Confirm instance is in the correct subnet
314
+
315
+ ### AWS CLI Errors
316
+
317
+ - Validate credentials and permissions
318
+ - Check service quotas and limits
319
+ - Verify region consistency across all resources
320
+
321
+ ## Getting Help
322
+
323
+ <CardGroup cols={2}>
324
+ <Card
325
+ title="Contact Support"
326
+ icon="envelope"
327
+ href="https://calendly.com/d/cq23-qyn-3v6/testdriver-ai-demo"
328
+ >
329
+ Get help with self-hosted setup and configuration
330
+ </Card>
331
+ <Card
332
+ title="Self-Hosted Overview"
333
+ icon="server"
334
+ href="/v7/self-hosted"
335
+ >
336
+ Learn more about self-hosted benefits and pricing
337
+ </Card>
338
+ </CardGroup>