testdriverai 7.2.2 → 7.2.3
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.
- package/docs/docs.json +2 -11
- package/docs/v7/_drafts/provision.mdx +251 -188
- package/docs/v7/getting-started/quickstart.mdx +9 -16
- package/lib/vitest/hooks.mjs +11 -7
- package/package.json +1 -1
- package/sdk.js +168 -38
- package/test/testdriver/installer.test.mjs +47 -0
- package/test/testdriver/launch-vscode-linux.test.mjs +55 -0
- package/vitest.config.mjs +1 -1
package/docs/docs.json
CHANGED
|
@@ -192,15 +192,8 @@
|
|
|
192
192
|
"groups": [
|
|
193
193
|
{
|
|
194
194
|
"group": "Getting Started",
|
|
195
|
-
"icon": "rocket",
|
|
196
|
-
"pages": [
|
|
197
|
-
"/v7/getting-started/quickstart"
|
|
198
|
-
]
|
|
199
|
-
},
|
|
200
|
-
{
|
|
201
|
-
"group": "Guides",
|
|
202
|
-
"icon": "book",
|
|
203
195
|
"pages": [
|
|
196
|
+
"/v7/getting-started/quickstart",
|
|
204
197
|
"/v7/getting-started/writing-tests",
|
|
205
198
|
"/v7/getting-started/running-tests",
|
|
206
199
|
"/v7/getting-started/debugging-tests",
|
|
@@ -209,7 +202,6 @@
|
|
|
209
202
|
},
|
|
210
203
|
{
|
|
211
204
|
"group": "Examples",
|
|
212
|
-
"icon": "code",
|
|
213
205
|
"pages": [
|
|
214
206
|
"/v7/presets/chrome",
|
|
215
207
|
"/v7/presets/chrome-extension",
|
|
@@ -218,8 +210,7 @@
|
|
|
218
210
|
]
|
|
219
211
|
},
|
|
220
212
|
{
|
|
221
|
-
"group": "
|
|
222
|
-
"icon": "layer-group",
|
|
213
|
+
"group": "Guides",
|
|
223
214
|
"pages": [
|
|
224
215
|
{
|
|
225
216
|
"group": "Selectorless Testing",
|
|
@@ -1,266 +1,329 @@
|
|
|
1
1
|
# Provision API
|
|
2
2
|
|
|
3
|
-
The `provision
|
|
3
|
+
The `provision` object provides easy setup methods for common applications. These methods automatically handle TestDriver initialization, Dashcam recording, and application launching.
|
|
4
4
|
|
|
5
5
|
## Quick Start
|
|
6
6
|
|
|
7
7
|
```javascript
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { TestDriver } from 'testdriverai/lib/vitest/hooks.mjs';
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
describe('My Test Suite', () => {
|
|
12
|
+
it('should test Chrome browser', async (context) => {
|
|
13
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
14
|
+
|
|
15
|
+
await testdriver.provision.chrome({
|
|
16
|
+
url: 'https://example.com'
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
await testdriver.find('Login button').click();
|
|
20
|
+
});
|
|
17
21
|
});
|
|
18
22
|
```
|
|
19
23
|
|
|
20
|
-
##
|
|
21
|
-
|
|
22
|
-
```typescript
|
|
23
|
-
provision(appType, options, context)
|
|
24
|
-
```
|
|
24
|
+
## Available Methods
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
- `
|
|
28
|
-
- `
|
|
29
|
-
- `
|
|
26
|
+
- `provision.chrome()` - Launch Chrome browser with URL
|
|
27
|
+
- `provision.vscode()` - Launch VS Code with optional extensions
|
|
28
|
+
- `provision.installer()` - Download and install applications
|
|
29
|
+
- `provision.electron()` - Launch Electron applications
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
- `testdriver` - TestDriver instance ready to use
|
|
33
|
-
- `dashcam` - Dashcam instance (if enabled)
|
|
34
|
-
- Additional app-specific properties (like `vscode`, `app`)
|
|
31
|
+
---
|
|
35
32
|
|
|
36
|
-
##
|
|
33
|
+
## provision.chrome()
|
|
37
34
|
|
|
38
|
-
|
|
35
|
+
Launch Google Chrome with automatic Dashcam recording.
|
|
39
36
|
|
|
40
37
|
```javascript
|
|
41
|
-
|
|
38
|
+
await testdriver.provision.chrome({
|
|
42
39
|
url: 'https://myapp.com',
|
|
43
|
-
maximized: true,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
os: 'linux' // Target OS (default: 'linux')
|
|
47
|
-
}, context);
|
|
48
|
-
|
|
49
|
-
await testdriver.find('username').type('user@example.com');
|
|
50
|
-
await testdriver.find('Login').click();
|
|
40
|
+
maximized: true,
|
|
41
|
+
extensionPath: '/path/to/extension'
|
|
42
|
+
});
|
|
51
43
|
```
|
|
52
44
|
|
|
53
45
|
**Options:**
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
46
|
+
| Option | Type | Default | Description |
|
|
47
|
+
|--------|------|---------|-------------|
|
|
48
|
+
| `url` | string | `'http://testdriver-sandbox.vercel.app/'` | URL to navigate to |
|
|
49
|
+
| `maximized` | boolean | `true` | Start browser maximized |
|
|
50
|
+
| `extensionPath` | string | - | Path to Chrome extension to load |
|
|
59
51
|
|
|
60
|
-
**
|
|
61
|
-
- `testdriver` - TestDriver instance
|
|
62
|
-
- `dashcam` - Dashcam instance (if enabled)
|
|
63
|
-
|
|
64
|
-
### VS Code
|
|
52
|
+
**Example:**
|
|
65
53
|
|
|
66
54
|
```javascript
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
await
|
|
75
|
-
await
|
|
55
|
+
it('should login to my app', async (context) => {
|
|
56
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
57
|
+
|
|
58
|
+
await testdriver.provision.chrome({
|
|
59
|
+
url: 'https://myapp.com/login'
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
await testdriver.find('email input').type('user@example.com');
|
|
63
|
+
await testdriver.find('password input').type('password123');
|
|
64
|
+
await testdriver.find('Login button').click();
|
|
65
|
+
|
|
66
|
+
const result = await testdriver.assert('Dashboard is visible');
|
|
67
|
+
expect(result).toBeTruthy();
|
|
68
|
+
});
|
|
76
69
|
```
|
|
77
70
|
|
|
78
|
-
|
|
79
|
-
- `workspace` - Workspace/folder path to open (optional)
|
|
80
|
-
- `extensions` - Array of extension IDs to install (optional)
|
|
81
|
-
- `dashcam` - Enable Dashcam recording (default: `true`)
|
|
82
|
-
- `os` - Target OS (default: `'linux'`)
|
|
71
|
+
---
|
|
83
72
|
|
|
84
|
-
|
|
85
|
-
- `testdriver` - TestDriver instance
|
|
86
|
-
- `vscode` - Alias for testdriver (semantic clarity)
|
|
87
|
-
- `dashcam` - Dashcam instance (if enabled)
|
|
73
|
+
## provision.vscode()
|
|
88
74
|
|
|
89
|
-
|
|
75
|
+
Launch Visual Studio Code with optional extension installation. Automatically starts Dashcam recording.
|
|
90
76
|
|
|
91
77
|
```javascript
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
os: 'linux'
|
|
97
|
-
}, context);
|
|
98
|
-
|
|
99
|
-
await app.find('main window').click();
|
|
78
|
+
await testdriver.provision.vscode({
|
|
79
|
+
workspace: '/path/to/project',
|
|
80
|
+
extensions: ['esbenp.prettier-vscode', 'ms-python.python']
|
|
81
|
+
});
|
|
100
82
|
```
|
|
101
83
|
|
|
102
84
|
**Options:**
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
85
|
+
| Option | Type | Default | Description |
|
|
86
|
+
|--------|------|---------|-------------|
|
|
87
|
+
| `workspace` | string | - | Workspace/folder path to open |
|
|
88
|
+
| `extensions` | string[] | `[]` | VS Code extension IDs to install |
|
|
107
89
|
|
|
108
|
-
**
|
|
109
|
-
- `testdriver` - TestDriver instance
|
|
110
|
-
- `app` - Alias for testdriver (semantic clarity)
|
|
111
|
-
- `dashcam` - Dashcam instance (if enabled)
|
|
112
|
-
|
|
113
|
-
### Web App
|
|
114
|
-
|
|
115
|
-
Generic wrapper for web applications (currently uses Chrome):
|
|
90
|
+
**Example - Basic Launch:**
|
|
116
91
|
|
|
117
92
|
```javascript
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
93
|
+
it('should launch VS Code', async (context) => {
|
|
94
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
95
|
+
|
|
96
|
+
await testdriver.provision.vscode();
|
|
97
|
+
|
|
98
|
+
const result = await testdriver.assert(
|
|
99
|
+
'Visual Studio Code window is visible on screen'
|
|
100
|
+
);
|
|
101
|
+
expect(result).toBeTruthy();
|
|
102
|
+
});
|
|
122
103
|
```
|
|
123
104
|
|
|
124
|
-
|
|
105
|
+
**Example - Install Extensions:**
|
|
125
106
|
|
|
126
107
|
```javascript
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const { testdriver, dashcam } = await provision('chrome', {
|
|
134
|
-
url: 'https://myapp.com/login',
|
|
135
|
-
maximized: true
|
|
136
|
-
}, context);
|
|
137
|
-
|
|
138
|
-
// Interact with the application
|
|
139
|
-
await testdriver.find('email input').type('user@example.com');
|
|
140
|
-
await testdriver.find('password input').type('password123');
|
|
141
|
-
await testdriver.find('Login button').click();
|
|
142
|
-
|
|
143
|
-
// Verify results
|
|
144
|
-
const result = await testdriver.assert('Welcome message is visible');
|
|
145
|
-
expect(result).toBeTruthy();
|
|
146
|
-
|
|
147
|
-
// Dashcam automatically stops and saves replay at test end
|
|
148
|
-
// No cleanup needed - handled automatically!
|
|
108
|
+
it('should install and use a VS Code extension', async (context) => {
|
|
109
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
110
|
+
|
|
111
|
+
// Launch VS Code with extensions installed
|
|
112
|
+
await testdriver.provision.vscode({
|
|
113
|
+
extensions: ['esbenp.prettier-vscode']
|
|
149
114
|
});
|
|
115
|
+
|
|
116
|
+
// Open the extensions panel
|
|
117
|
+
await testdriver.pressKeys(['ctrl', 'shift', 'x']);
|
|
118
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
119
|
+
|
|
120
|
+
const result = await testdriver.assert(
|
|
121
|
+
'Prettier extension is visible in the extensions panel'
|
|
122
|
+
);
|
|
123
|
+
expect(result).toBeTruthy();
|
|
150
124
|
});
|
|
151
125
|
```
|
|
152
126
|
|
|
153
|
-
|
|
127
|
+
---
|
|
154
128
|
|
|
155
|
-
|
|
129
|
+
## provision.installer()
|
|
156
130
|
|
|
157
|
-
|
|
158
|
-
2. **Sets up Dashcam** - Authenticates and starts recording (if enabled)
|
|
159
|
-
3. **Launches application** - Opens the specified app with your configuration
|
|
160
|
-
4. **Focuses window** - Ensures the app is ready for interaction
|
|
161
|
-
5. **Returns ready-to-use instances** - Everything is set up and ready
|
|
131
|
+
Download and install applications from a URL. Automatically detects file type and runs the appropriate install command.
|
|
162
132
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
133
|
+
```javascript
|
|
134
|
+
const filePath = await testdriver.provision.installer({
|
|
135
|
+
url: 'https://example.com/app.deb',
|
|
136
|
+
appName: 'MyApp'
|
|
137
|
+
});
|
|
138
|
+
```
|
|
169
139
|
|
|
170
|
-
|
|
140
|
+
**Options:**
|
|
141
|
+
| Option | Type | Default | Description |
|
|
142
|
+
|--------|------|---------|-------------|
|
|
143
|
+
| `url` | string | **required** | URL to download the installer from |
|
|
144
|
+
| `filename` | string | auto-detected | Filename to save as |
|
|
145
|
+
| `appName` | string | - | Application name to focus after install |
|
|
146
|
+
| `launch` | boolean | `true` | Whether to launch/focus the app after installation |
|
|
147
|
+
|
|
148
|
+
**Returns:** `Promise<string>` - Path to the downloaded file
|
|
149
|
+
|
|
150
|
+
**Supported File Types:**
|
|
151
|
+
|
|
152
|
+
| Platform | Extensions | Install Command |
|
|
153
|
+
|----------|------------|-----------------|
|
|
154
|
+
| Linux | `.deb` | `sudo dpkg -i && apt-get install -f -y` |
|
|
155
|
+
| Linux | `.rpm` | `sudo rpm -i` |
|
|
156
|
+
| Linux | `.appimage` | `chmod +x` |
|
|
157
|
+
| Linux | `.sh` | `chmod +x && execute` |
|
|
158
|
+
| Windows | `.msi` | `msiexec /i /quiet /norestart` |
|
|
159
|
+
| Windows | `.exe` | `Start-Process /S` |
|
|
160
|
+
| macOS | `.dmg` | `hdiutil attach && cp to /Applications` |
|
|
161
|
+
| macOS | `.pkg` | `installer -pkg -target /` |
|
|
162
|
+
|
|
163
|
+
**Example - Install .deb Package:**
|
|
171
164
|
|
|
172
165
|
```javascript
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
166
|
+
it('should install a .deb package', async (context) => {
|
|
167
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
168
|
+
|
|
169
|
+
const filePath = await testdriver.provision.installer({
|
|
170
|
+
url: 'https://github.com/sharkdp/bat/releases/download/v0.24.0/bat_0.24.0_amd64.deb'
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Verify installation
|
|
174
|
+
await testdriver.exec('sh', 'bat --version', 10000);
|
|
175
|
+
});
|
|
176
|
+
```
|
|
180
177
|
|
|
181
|
-
|
|
182
|
-
await dashcam.auth();
|
|
183
|
-
await dashcam.start();
|
|
178
|
+
**Example - Download Only (No Auto-Launch):**
|
|
184
179
|
|
|
185
|
-
|
|
186
|
-
|
|
180
|
+
```javascript
|
|
181
|
+
it('should download a script', async (context) => {
|
|
182
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
183
|
+
|
|
184
|
+
const filePath = await testdriver.provision.installer({
|
|
185
|
+
url: 'https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh',
|
|
186
|
+
launch: false
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Run custom post-download commands
|
|
190
|
+
await testdriver.exec('sh', `source "${filePath}"`, 30000);
|
|
191
|
+
});
|
|
192
|
+
```
|
|
187
193
|
|
|
188
|
-
|
|
194
|
+
**Example - Custom Post-Install:**
|
|
189
195
|
|
|
190
|
-
|
|
191
|
-
|
|
196
|
+
```javascript
|
|
197
|
+
it('should run AppImage with custom flags', async (context) => {
|
|
198
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
199
|
+
|
|
200
|
+
const filePath = await testdriver.provision.installer({
|
|
201
|
+
url: 'https://example.com/app.AppImage',
|
|
202
|
+
launch: false
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Run with custom arguments
|
|
206
|
+
await testdriver.exec('sh', `"${filePath}" --no-sandbox &`, 10000);
|
|
207
|
+
|
|
208
|
+
await new Promise(resolve => setTimeout(resolve, 5000));
|
|
209
|
+
|
|
210
|
+
const result = await testdriver.assert('App window is visible');
|
|
211
|
+
expect(result).toBeTruthy();
|
|
212
|
+
});
|
|
192
213
|
```
|
|
193
214
|
|
|
194
|
-
|
|
215
|
+
---
|
|
195
216
|
|
|
196
|
-
##
|
|
217
|
+
## provision.electron()
|
|
197
218
|
|
|
198
|
-
|
|
219
|
+
Launch an Electron application.
|
|
199
220
|
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
const { testdriver } = await provision('chrome', {
|
|
205
|
-
url: 'https://example.com',
|
|
206
|
-
maximized: true,
|
|
207
|
-
os: 'linux' // ✅ Type-safe: only 'linux' | 'mac' | 'windows'
|
|
208
|
-
}, context);
|
|
209
|
-
|
|
210
|
-
// ✅ Full autocomplete for testdriver methods
|
|
211
|
-
await testdriver.find('button').click();
|
|
221
|
+
```javascript
|
|
222
|
+
await testdriver.provision.electron({
|
|
223
|
+
appPath: '/path/to/app',
|
|
224
|
+
args: ['--enable-logging']
|
|
212
225
|
});
|
|
213
226
|
```
|
|
214
227
|
|
|
215
|
-
|
|
228
|
+
**Options:**
|
|
229
|
+
| Option | Type | Default | Description |
|
|
230
|
+
|--------|------|---------|-------------|
|
|
231
|
+
| `appPath` | string | **required** | Path to Electron application |
|
|
232
|
+
| `args` | string[] | `[]` | Additional command-line arguments |
|
|
216
233
|
|
|
217
|
-
|
|
234
|
+
**Example:**
|
|
218
235
|
|
|
219
236
|
```javascript
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
workspace: '/path/to/project'
|
|
237
|
+
it('should launch Electron app', async (context) => {
|
|
238
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
239
|
+
|
|
240
|
+
await testdriver.provision.electron({
|
|
241
|
+
appPath: '/path/to/my-electron-app',
|
|
242
|
+
args: ['--enable-logging']
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
await testdriver.find('main window').click();
|
|
230
246
|
});
|
|
231
247
|
```
|
|
232
248
|
|
|
233
|
-
|
|
249
|
+
---
|
|
234
250
|
|
|
235
|
-
##
|
|
251
|
+
## How Provision Methods Work
|
|
252
|
+
|
|
253
|
+
When you call a provision method:
|
|
236
254
|
|
|
237
|
-
1. **
|
|
238
|
-
2. **
|
|
239
|
-
3. **
|
|
240
|
-
4. **
|
|
241
|
-
5. **
|
|
255
|
+
1. **Waits for connection** - Calls `ready()` to ensure sandbox is connected
|
|
256
|
+
2. **Sets up Dashcam** - Creates log file and adds to Dashcam monitoring
|
|
257
|
+
3. **Starts recording** - Automatically starts Dashcam if not already recording
|
|
258
|
+
4. **Launches application** - Opens the specified app with your configuration
|
|
259
|
+
5. **Focuses window** - Ensures the app is ready for interaction
|
|
242
260
|
|
|
243
|
-
|
|
261
|
+
At test end:
|
|
262
|
+
- Dashcam automatically stops and saves replay URL
|
|
263
|
+
- TestDriver automatically disconnects
|
|
264
|
+
- All cleanup is handled for you
|
|
265
|
+
|
|
266
|
+
## Complete Example
|
|
244
267
|
|
|
245
268
|
```javascript
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
269
|
+
import { describe, it, expect } from 'vitest';
|
|
270
|
+
import { TestDriver } from 'testdriverai/lib/vitest/hooks.mjs';
|
|
271
|
+
|
|
272
|
+
describe('Application Testing', () => {
|
|
273
|
+
it('should test Chrome login flow', async (context) => {
|
|
274
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
275
|
+
|
|
276
|
+
await testdriver.provision.chrome({
|
|
277
|
+
url: 'https://myapp.com/login'
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
await testdriver.find('email').type('user@example.com');
|
|
281
|
+
await testdriver.find('password').type('password123');
|
|
282
|
+
await testdriver.find('Login').click();
|
|
251
283
|
|
|
252
|
-
await testdriver.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
284
|
+
const result = await testdriver.assert('Welcome message is visible');
|
|
285
|
+
expect(result).toBeTruthy();
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
it('should test VS Code extension', async (context) => {
|
|
289
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
290
|
+
|
|
291
|
+
await testdriver.provision.vscode({
|
|
292
|
+
extensions: ['ms-python.python']
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
await testdriver.pressKeys(['ctrl', 'shift', 'p']);
|
|
296
|
+
await testdriver.type('Python: Select Interpreter');
|
|
297
|
+
await testdriver.pressKeys(['enter']);
|
|
298
|
+
|
|
299
|
+
const result = await testdriver.assert('Python interpreter selector is visible');
|
|
300
|
+
expect(result).toBeTruthy();
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
it('should install and test CLI tool', async (context) => {
|
|
304
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
305
|
+
|
|
306
|
+
await testdriver.provision.installer({
|
|
307
|
+
url: 'https://github.com/sharkdp/bat/releases/download/v0.24.0/bat_0.24.0_amd64.deb'
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Verify the tool works
|
|
311
|
+
await testdriver.exec('sh', 'bat --help', 10000);
|
|
312
|
+
});
|
|
258
313
|
});
|
|
259
314
|
```
|
|
260
315
|
|
|
316
|
+
## Best Practices
|
|
317
|
+
|
|
318
|
+
1. **Use `newSandbox: true`** - Each test gets a clean environment
|
|
319
|
+
2. **Enable Dashcam** - Great for debugging test failures (enabled by default)
|
|
320
|
+
3. **Check assertions** - Always verify the expected state after actions
|
|
321
|
+
4. **Use appropriate provision method** - Match the method to your test target
|
|
322
|
+
5. **Handle async properly** - All provision methods return Promises
|
|
323
|
+
|
|
261
324
|
## See Also
|
|
262
325
|
|
|
263
|
-
- [Hooks API](./
|
|
264
|
-
- [
|
|
265
|
-
- [
|
|
266
|
-
- [
|
|
326
|
+
- [Hooks API](./hooks.mdx) - TestDriver initialization
|
|
327
|
+
- [Find API](../api/find.mdx) - Element finding
|
|
328
|
+
- [Exec API](../api/exec.mdx) - Running shell commands
|
|
329
|
+
- [Assert API](../api/assert.mdx) - AI-powered assertions
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
---
|
|
2
2
|
title: "Quick Start"
|
|
3
3
|
sidebarTitle: "Quickstart"
|
|
4
|
-
description: "
|
|
4
|
+
description: "Run your first computer-use test in minutes."
|
|
5
5
|
icon: "rocket"
|
|
6
6
|
mode: "wide"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
TestDriver makes it easy to write automated computer-use tests for web browsers, desktop apps, and more.
|
|
9
|
+
TestDriver makes it easy to write automated computer-use tests for web browsers, desktop apps, and more. Follow the directions below to run your first TestDriver test.
|
|
10
|
+
|
|
11
|
+
<Tip><a href="https://discord.com/invite/cWDFW8DzPm" target="_blank" rel="noreferrer">Join our Discord</a> if you have any questions or need help getting started!</Tip>
|
|
12
|
+
|
|
13
|
+
## Get Started in 3 Steps
|
|
10
14
|
|
|
11
15
|
<Steps>
|
|
12
16
|
<Step title="Create a TestDriver Account">
|
|
@@ -20,7 +24,7 @@ TestDriver makes it easy to write automated computer-use tests for web browsers,
|
|
|
20
24
|
arrow
|
|
21
25
|
horizontal
|
|
22
26
|
>
|
|
23
|
-
|
|
27
|
+
No credit-card required!
|
|
24
28
|
</Card>
|
|
25
29
|
|
|
26
30
|
</Step>
|
|
@@ -41,10 +45,10 @@ TestDriver makes it easy to write automated computer-use tests for web browsers,
|
|
|
41
45
|
TestDriver uses Vitest as the test runner. To run your test, use:
|
|
42
46
|
|
|
43
47
|
```bash
|
|
44
|
-
|
|
48
|
+
vitest run
|
|
45
49
|
```
|
|
46
50
|
|
|
47
|
-
This will spawn a sandbox, launch Chrome,
|
|
51
|
+
This will spawn a sandbox, launch Chrome, and run the example test!
|
|
48
52
|
|
|
49
53
|
</Step>
|
|
50
54
|
</Steps>
|
|
@@ -84,14 +88,3 @@ TestDriver makes it easy to write automated computer-use tests for web browsers,
|
|
|
84
88
|
Choose your complexity level
|
|
85
89
|
</Card>
|
|
86
90
|
</CardGroup>
|
|
87
|
-
|
|
88
|
-
## Why TestDriver v7?
|
|
89
|
-
|
|
90
|
-
The v7 SDK offers advantages over YAML-based testing:
|
|
91
|
-
|
|
92
|
-
- ✅ **Type Safety** - Full TypeScript support with IntelliSense
|
|
93
|
-
- ✅ **Programmatic Control** - Use variables, loops, and functions
|
|
94
|
-
- ✅ **Better Debugging** - Stack traces point to your actual code
|
|
95
|
-
- ✅ **Automatic Lifecycle** - Presets handle setup and cleanup
|
|
96
|
-
- ✅ **Framework Integration** - Works with Vitest, Jest, Mocha, etc.
|
|
97
|
-
- ✅ **Video Replays** - Automatic Dashcam recording included
|
package/lib/vitest/hooks.mjs
CHANGED
|
@@ -15,11 +15,11 @@
|
|
|
15
15
|
* });
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
+
import chalk from 'chalk';
|
|
18
19
|
import fs from 'fs';
|
|
19
20
|
import os from 'os';
|
|
20
21
|
import path from 'path';
|
|
21
22
|
import { vi } from 'vitest';
|
|
22
|
-
import chalk from 'chalk';
|
|
23
23
|
import TestDriverSDK from '../../sdk.js';
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -78,26 +78,30 @@ function setupConsoleSpy(client, taskId) {
|
|
|
78
78
|
}
|
|
79
79
|
};
|
|
80
80
|
|
|
81
|
+
// Store original console methods before spying
|
|
82
|
+
const originalLog = console.log.bind(console);
|
|
83
|
+
const originalError = console.error.bind(console);
|
|
84
|
+
const originalWarn = console.warn.bind(console);
|
|
85
|
+
const originalInfo = console.info.bind(console);
|
|
86
|
+
|
|
81
87
|
// Create spies for each console method
|
|
82
88
|
const logSpy = vi.spyOn(console, 'log').mockImplementation((...args) => {
|
|
83
|
-
// Call
|
|
84
|
-
logSpy.mock.calls; // Track calls
|
|
85
|
-
process.stdout.write(args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ') + '\n');
|
|
89
|
+
originalLog(...args); // Call original (Vitest will capture this)
|
|
86
90
|
forwardToSandbox(args);
|
|
87
91
|
});
|
|
88
92
|
|
|
89
93
|
const errorSpy = vi.spyOn(console, 'error').mockImplementation((...args) => {
|
|
90
|
-
|
|
94
|
+
originalError(...args);
|
|
91
95
|
forwardToSandbox(args);
|
|
92
96
|
});
|
|
93
97
|
|
|
94
98
|
const warnSpy = vi.spyOn(console, 'warn').mockImplementation((...args) => {
|
|
95
|
-
|
|
99
|
+
originalWarn(...args);
|
|
96
100
|
forwardToSandbox(args);
|
|
97
101
|
});
|
|
98
102
|
|
|
99
103
|
const infoSpy = vi.spyOn(console, 'info').mockImplementation((...args) => {
|
|
100
|
-
|
|
104
|
+
originalInfo(...args);
|
|
101
105
|
forwardToSandbox(args);
|
|
102
106
|
});
|
|
103
107
|
|
package/package.json
CHANGED
package/sdk.js
CHANGED
|
@@ -1702,26 +1702,49 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
|
|
|
1702
1702
|
* @returns {Promise<void>}
|
|
1703
1703
|
*/
|
|
1704
1704
|
vscode: async (options = {}) => {
|
|
1705
|
-
|
|
1705
|
+
// Automatically wait for connection to be ready
|
|
1706
|
+
await this.ready();
|
|
1706
1707
|
|
|
1707
1708
|
const {
|
|
1708
1709
|
workspace = null,
|
|
1709
1710
|
extensions = [],
|
|
1710
1711
|
} = options;
|
|
1711
1712
|
|
|
1713
|
+
const shell = this.os === 'windows' ? 'pwsh' : 'sh';
|
|
1714
|
+
|
|
1715
|
+
// If dashcam is available, set up file logging
|
|
1716
|
+
if (this._dashcam) {
|
|
1717
|
+
// Create the log file on the remote machine
|
|
1718
|
+
const logPath = this.os === "windows"
|
|
1719
|
+
? "C:\\Users\\testdriver\\Documents\\testdriver.log"
|
|
1720
|
+
: "/tmp/testdriver.log";
|
|
1721
|
+
|
|
1722
|
+
const createLogCmd = this.os === "windows"
|
|
1723
|
+
? `New-Item -ItemType File -Path "${logPath}" -Force | Out-Null`
|
|
1724
|
+
: `touch ${logPath}`;
|
|
1725
|
+
|
|
1726
|
+
await this.exec(shell, createLogCmd, 10000, true);
|
|
1727
|
+
await this._dashcam.addFileLog(logPath, "TestDriver Log");
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
// Automatically start dashcam if not already recording
|
|
1731
|
+
if (!this._dashcam || !this._dashcam.recording) {
|
|
1732
|
+
await this.dashcam.start();
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1712
1735
|
// Install extensions if provided
|
|
1713
1736
|
for (const extension of extensions) {
|
|
1714
|
-
|
|
1737
|
+
console.log(`[provision.vscode] Installing extension: ${extension}`);
|
|
1715
1738
|
await this.exec(
|
|
1716
1739
|
shell,
|
|
1717
|
-
`code --install-extension ${extension}`,
|
|
1718
|
-
|
|
1740
|
+
`code --install-extension ${extension} --force`,
|
|
1741
|
+
120000,
|
|
1719
1742
|
true
|
|
1720
1743
|
);
|
|
1744
|
+
console.log(`[provision.vscode] ✅ Extension installed: ${extension}`);
|
|
1721
1745
|
}
|
|
1722
1746
|
|
|
1723
1747
|
// Launch VS Code
|
|
1724
|
-
const shell = this.os === 'windows' ? 'pwsh' : 'sh';
|
|
1725
1748
|
const workspaceArg = workspace ? `"${workspace}"` : '';
|
|
1726
1749
|
|
|
1727
1750
|
if (this.os === 'windows') {
|
|
@@ -1738,10 +1761,148 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
|
|
|
1738
1761
|
);
|
|
1739
1762
|
}
|
|
1740
1763
|
|
|
1764
|
+
// Wait for VS Code to start up
|
|
1765
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
1766
|
+
|
|
1741
1767
|
// Wait for VS Code to be ready
|
|
1742
1768
|
await this.focusApplication('Visual Studio Code');
|
|
1743
1769
|
},
|
|
1744
1770
|
|
|
1771
|
+
/**
|
|
1772
|
+
* Download and install an application
|
|
1773
|
+
* @param {Object} options - Installer options
|
|
1774
|
+
* @param {string} options.url - URL to download the installer from
|
|
1775
|
+
* @param {string} [options.filename] - Filename to save as (auto-detected from URL if not provided)
|
|
1776
|
+
* @param {string} [options.appName] - Application name to focus after install
|
|
1777
|
+
* @param {boolean} [options.launch=true] - Whether to launch the app after installation
|
|
1778
|
+
* @returns {Promise<string>} Path to the downloaded file
|
|
1779
|
+
* @example
|
|
1780
|
+
* // Install a .deb package on Linux (auto-detected)
|
|
1781
|
+
* await testdriver.provision.installer({
|
|
1782
|
+
* url: 'https://example.com/app.deb',
|
|
1783
|
+
* appName: 'MyApp'
|
|
1784
|
+
* });
|
|
1785
|
+
*
|
|
1786
|
+
* @example
|
|
1787
|
+
* // Download and run custom commands
|
|
1788
|
+
* const filePath = await testdriver.provision.installer({
|
|
1789
|
+
* url: 'https://example.com/app.AppImage',
|
|
1790
|
+
* launch: false
|
|
1791
|
+
* });
|
|
1792
|
+
* await testdriver.exec('sh', `chmod +x "${filePath}" && "${filePath}" &`, 10000);
|
|
1793
|
+
*/
|
|
1794
|
+
installer: async (options = {}) => {
|
|
1795
|
+
// Automatically wait for connection to be ready
|
|
1796
|
+
await this.ready();
|
|
1797
|
+
|
|
1798
|
+
const {
|
|
1799
|
+
url,
|
|
1800
|
+
filename,
|
|
1801
|
+
appName,
|
|
1802
|
+
launch = true,
|
|
1803
|
+
} = options;
|
|
1804
|
+
|
|
1805
|
+
if (!url) {
|
|
1806
|
+
throw new Error('[provision.installer] url is required');
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
const shell = this.os === 'windows' ? 'pwsh' : 'sh';
|
|
1810
|
+
|
|
1811
|
+
// If dashcam is available, set up file logging
|
|
1812
|
+
if (this._dashcam) {
|
|
1813
|
+
const logPath = this.os === "windows"
|
|
1814
|
+
? "C:\\Users\\testdriver\\Documents\\testdriver.log"
|
|
1815
|
+
: "/tmp/testdriver.log";
|
|
1816
|
+
|
|
1817
|
+
const createLogCmd = this.os === "windows"
|
|
1818
|
+
? `New-Item -ItemType File -Path "${logPath}" -Force | Out-Null`
|
|
1819
|
+
: `touch ${logPath}`;
|
|
1820
|
+
|
|
1821
|
+
await this.exec(shell, createLogCmd, 10000, true);
|
|
1822
|
+
await this._dashcam.addFileLog(logPath, "TestDriver Log");
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
// Automatically start dashcam if not already recording
|
|
1826
|
+
if (!this._dashcam || !this._dashcam.recording) {
|
|
1827
|
+
await this.dashcam.start();
|
|
1828
|
+
}
|
|
1829
|
+
|
|
1830
|
+
// Determine filename from URL if not provided
|
|
1831
|
+
const urlObj = new URL(url);
|
|
1832
|
+
const detectedFilename = filename || urlObj.pathname.split('/').pop() || 'installer';
|
|
1833
|
+
|
|
1834
|
+
// Determine download directory and full path
|
|
1835
|
+
const downloadDir = this.os === 'windows'
|
|
1836
|
+
? 'C:\\Users\\testdriver\\Downloads'
|
|
1837
|
+
: '/tmp';
|
|
1838
|
+
const filePath = this.os === 'windows'
|
|
1839
|
+
? `${downloadDir}\\${detectedFilename}`
|
|
1840
|
+
: `${downloadDir}/${detectedFilename}`;
|
|
1841
|
+
|
|
1842
|
+
console.log(`[provision.installer] Downloading ${url}...`);
|
|
1843
|
+
|
|
1844
|
+
// Download the file
|
|
1845
|
+
if (this.os === 'windows') {
|
|
1846
|
+
await this.exec(
|
|
1847
|
+
shell,
|
|
1848
|
+
`Invoke-WebRequest -Uri "${url}" -OutFile "${filePath}"`,
|
|
1849
|
+
300000, // 5 min timeout for download
|
|
1850
|
+
true
|
|
1851
|
+
);
|
|
1852
|
+
} else {
|
|
1853
|
+
await this.exec(
|
|
1854
|
+
shell,
|
|
1855
|
+
`curl -L -o "${filePath}" "${url}"`,
|
|
1856
|
+
300000,
|
|
1857
|
+
true
|
|
1858
|
+
);
|
|
1859
|
+
}
|
|
1860
|
+
|
|
1861
|
+
console.log(`[provision.installer] ✅ Downloaded to ${filePath}`);
|
|
1862
|
+
|
|
1863
|
+
// Auto-detect install command based on file extension
|
|
1864
|
+
const ext = detectedFilename.split('.').pop()?.toLowerCase();
|
|
1865
|
+
let installCommand = null;
|
|
1866
|
+
|
|
1867
|
+
if (this.os === 'windows') {
|
|
1868
|
+
if (ext === 'msi') {
|
|
1869
|
+
installCommand = `Start-Process msiexec -ArgumentList '/i', '"${filePath}"', '/quiet', '/norestart' -Wait`;
|
|
1870
|
+
} else if (ext === 'exe') {
|
|
1871
|
+
installCommand = `Start-Process "${filePath}" -ArgumentList '/S' -Wait`;
|
|
1872
|
+
}
|
|
1873
|
+
} else if (this.os === 'linux') {
|
|
1874
|
+
if (ext === 'deb') {
|
|
1875
|
+
installCommand = `sudo dpkg -i "${filePath}" && sudo apt-get install -f -y`;
|
|
1876
|
+
} else if (ext === 'rpm') {
|
|
1877
|
+
installCommand = `sudo rpm -i "${filePath}"`;
|
|
1878
|
+
} else if (ext === 'appimage') {
|
|
1879
|
+
installCommand = `chmod +x "${filePath}"`;
|
|
1880
|
+
} else if (ext === 'sh') {
|
|
1881
|
+
installCommand = `chmod +x "${filePath}" && "${filePath}"`;
|
|
1882
|
+
}
|
|
1883
|
+
} else if (this.os === 'darwin') {
|
|
1884
|
+
if (ext === 'dmg') {
|
|
1885
|
+
installCommand = `hdiutil attach "${filePath}" -mountpoint /Volumes/installer && cp -R /Volumes/installer/*.app /Applications/ && hdiutil detach /Volumes/installer`;
|
|
1886
|
+
} else if (ext === 'pkg') {
|
|
1887
|
+
installCommand = `sudo installer -pkg "${filePath}" -target /`;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
|
|
1891
|
+
if (installCommand) {
|
|
1892
|
+
console.log(`[provision.installer] Installing...`);
|
|
1893
|
+
await this.exec(shell, installCommand, 300000, true);
|
|
1894
|
+
console.log(`[provision.installer] ✅ Installation complete`);
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
// Launch and focus the app if appName is provided and launch is true
|
|
1898
|
+
if (appName && launch) {
|
|
1899
|
+
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
1900
|
+
await this.focusApplication(appName);
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1903
|
+
return filePath;
|
|
1904
|
+
},
|
|
1905
|
+
|
|
1745
1906
|
/**
|
|
1746
1907
|
* Launch Electron app
|
|
1747
1908
|
* @param {Object} options - Electron launch options
|
|
@@ -2606,6 +2767,8 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
|
|
|
2606
2767
|
createMarkdownLogger(this.emitter);
|
|
2607
2768
|
|
|
2608
2769
|
// Set up basic event logging
|
|
2770
|
+
// Note: We only console.log here - the console spy in vitest/hooks.mjs
|
|
2771
|
+
// handles forwarding to sandbox. This prevents duplicate output to server.
|
|
2609
2772
|
this.emitter.on("log:**", (message) => {
|
|
2610
2773
|
const event = this.emitter.event;
|
|
2611
2774
|
if (event === events.log.debug && !debugMode) return;
|
|
@@ -2614,9 +2777,6 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
|
|
|
2614
2777
|
? `[${this.testContext}] ${message}`
|
|
2615
2778
|
: message;
|
|
2616
2779
|
console.log(prefixedMessage);
|
|
2617
|
-
|
|
2618
|
-
// Also forward to sandbox for dashcam
|
|
2619
|
-
this._forwardLogToSandbox(prefixedMessage);
|
|
2620
2780
|
}
|
|
2621
2781
|
});
|
|
2622
2782
|
|
|
@@ -2675,36 +2835,6 @@ with zipfile.ZipFile(io.BytesIO(zip_data)) as zf:
|
|
|
2675
2835
|
});
|
|
2676
2836
|
}
|
|
2677
2837
|
|
|
2678
|
-
/**
|
|
2679
|
-
* Forward log message to sandbox for debugger display
|
|
2680
|
-
* @private
|
|
2681
|
-
* @param {string} message - Log message to forward
|
|
2682
|
-
*/
|
|
2683
|
-
_forwardLogToSandbox(message) {
|
|
2684
|
-
try {
|
|
2685
|
-
// Only forward if sandbox is connected
|
|
2686
|
-
if (this.sandbox && this.sandbox.instanceSocketConnected) {
|
|
2687
|
-
// Don't send objects as they cause base64 encoding errors
|
|
2688
|
-
if (typeof message === "object") {
|
|
2689
|
-
return;
|
|
2690
|
-
}
|
|
2691
|
-
|
|
2692
|
-
// Add test context prefix if available
|
|
2693
|
-
const prefixedMessage = this.testContext
|
|
2694
|
-
? `[${this.testContext}] ${message}`
|
|
2695
|
-
: message;
|
|
2696
|
-
|
|
2697
|
-
this.sandbox.send({
|
|
2698
|
-
type: "output",
|
|
2699
|
-
output: Buffer.from(prefixedMessage).toString("base64"),
|
|
2700
|
-
});
|
|
2701
|
-
}
|
|
2702
|
-
} catch {
|
|
2703
|
-
// Silently fail to avoid breaking the log flow
|
|
2704
|
-
// console.error("Error forwarding log to sandbox:", error);
|
|
2705
|
-
}
|
|
2706
|
-
}
|
|
2707
|
-
|
|
2708
2838
|
/**
|
|
2709
2839
|
* Open URL in default browser
|
|
2710
2840
|
* @private
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestDriver SDK - Installer Test (Vitest)
|
|
3
|
+
* Tests the provision.installer() method for downloading and installing apps
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import { TestDriver } from "../../lib/vitest/hooks.mjs";
|
|
8
|
+
|
|
9
|
+
describe("Provision Installer", () => {
|
|
10
|
+
it(
|
|
11
|
+
"should download and install a .deb package on Linux",
|
|
12
|
+
async (context) => {
|
|
13
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
14
|
+
|
|
15
|
+
// Install bat (a cat clone with syntax highlighting) using provision.installer
|
|
16
|
+
const filePath = await testdriver.provision.installer({
|
|
17
|
+
url: 'https://github.com/sharkdp/bat/releases/download/v0.24.0/bat_0.24.0_amd64.deb',
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
// Verify the file was downloaded
|
|
21
|
+
expect(filePath).toContain('bat');
|
|
22
|
+
|
|
23
|
+
// Verify bat was installed by running it
|
|
24
|
+
await testdriver.exec('sh', 'bat --version', 10000);
|
|
25
|
+
},
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
it(
|
|
29
|
+
"should download a shell script and verify it exists",
|
|
30
|
+
async (context) => {
|
|
31
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
32
|
+
|
|
33
|
+
// Download a shell script (nvm installer)
|
|
34
|
+
const filePath = await testdriver.provision.installer({
|
|
35
|
+
url: 'https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh',
|
|
36
|
+
launch: false, // Don't auto-run the script
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
// Verify the file was downloaded
|
|
40
|
+
expect(filePath).toContain('install.sh');
|
|
41
|
+
|
|
42
|
+
// Verify the file is executable
|
|
43
|
+
const result = await testdriver.exec('sh', `ls -la "${filePath}"`, 10000);
|
|
44
|
+
expect(result).toBeTruthy();
|
|
45
|
+
},
|
|
46
|
+
);
|
|
47
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TestDriver SDK - Launch VS Code on Linux Test (Vitest)
|
|
3
|
+
* Tests launching Visual Studio Code on Debian/Ubuntu using provision.vscode()
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, expect, it } from "vitest";
|
|
7
|
+
import { TestDriver } from "../../lib/vitest/hooks.mjs";
|
|
8
|
+
|
|
9
|
+
describe("Launch VS Code on Linux", () => {
|
|
10
|
+
it(
|
|
11
|
+
"should launch VS Code on Debian/Ubuntu",
|
|
12
|
+
async (context) => {
|
|
13
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
14
|
+
|
|
15
|
+
// provision.vscode() automatically calls ready() and starts dashcam
|
|
16
|
+
await testdriver.provision.vscode();
|
|
17
|
+
|
|
18
|
+
// Assert that VS Code is running
|
|
19
|
+
const result = await testdriver.assert(
|
|
20
|
+
"Visual Studio Code window is visible on screen",
|
|
21
|
+
);
|
|
22
|
+
expect(result).toBeTruthy();
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
it(
|
|
27
|
+
"should install and use a VS Code extension",
|
|
28
|
+
async (context) => {
|
|
29
|
+
const testdriver = TestDriver(context, { newSandbox: true });
|
|
30
|
+
|
|
31
|
+
// Launch VS Code with the Prettier extension installed
|
|
32
|
+
await testdriver.provision.vscode({
|
|
33
|
+
extensions: ["esbenp.prettier-vscode"],
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Assert that VS Code is running
|
|
37
|
+
const vsCodeVisible = await testdriver.assert(
|
|
38
|
+
"Visual Studio Code window is visible on screen",
|
|
39
|
+
);
|
|
40
|
+
expect(vsCodeVisible).toBeTruthy();
|
|
41
|
+
|
|
42
|
+
// Open the extensions panel to verify Prettier is installed
|
|
43
|
+
await testdriver.pressKeys(["ctrl", "shift", "x"]);
|
|
44
|
+
|
|
45
|
+
// Wait for extensions panel to open
|
|
46
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
47
|
+
|
|
48
|
+
// Assert that Prettier extension is visible in the installed extensions
|
|
49
|
+
const prettierVisible = await testdriver.assert(
|
|
50
|
+
"Prettier extension is visible in the extensions panel or sidebar",
|
|
51
|
+
);
|
|
52
|
+
expect(prettierVisible).toBeTruthy();
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
});
|