testdriverai 7.1.2 → 7.1.4

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 (38) hide show
  1. package/.github/workflows/test-init.yml +145 -0
  2. package/agent/lib/commander.js +2 -2
  3. package/agent/lib/commands.js +10 -5
  4. package/docs/docs.json +29 -84
  5. package/docs/v7/_drafts/migration.mdx +3 -3
  6. package/docs/v7/api/act.mdx +1 -1
  7. package/docs/v7/api/assert.mdx +1 -1
  8. package/docs/v7/api/assertions.mdx +14 -14
  9. package/docs/v7/getting-started/quickstart.mdx +11 -9
  10. package/docs/v7/getting-started/running-and-debugging.mdx +3 -2
  11. package/docs/v7/getting-started/writing-tests.mdx +4 -5
  12. package/docs/v7/overview/readme.mdx +1 -1
  13. package/docs/v7/presets/chrome.mdx +38 -41
  14. package/docs/v7/presets/electron.mdx +107 -100
  15. package/docs/v7/presets/webapp.mdx +40 -43
  16. package/interfaces/cli/commands/init.js +79 -21
  17. package/interfaces/vitest-plugin.mjs +36 -9
  18. package/lib/vitest/hooks.mjs +5 -1
  19. package/manual/test-init-command.js +223 -0
  20. package/package.json +2 -2
  21. package/schema.json +5 -5
  22. package/sdk-log-formatter.js +1 -1
  23. package/sdk.d.ts +8 -8
  24. package/sdk.js +64 -14
  25. package/test/testdriver/exec-output.test.mjs +1 -1
  26. package/test/testdriver/exec-pwsh.test.mjs +1 -1
  27. package/test/testdriver/focus-window.test.mjs +1 -1
  28. package/test/testdriver/hover-image.test.mjs +1 -1
  29. package/test/testdriver/hover-text-with-description.test.mjs +0 -3
  30. package/test/testdriver/match-image.test.mjs +1 -1
  31. package/test/testdriver/scroll-keyboard.test.mjs +1 -1
  32. package/test/testdriver/scroll-until-image.test.mjs +1 -1
  33. package/test/testdriver/scroll-until-text.test.mjs +18 -1
  34. package/test/testdriver/scroll.test.mjs +1 -1
  35. package/test/testdriver/setup/lifecycleHelpers.mjs +105 -5
  36. package/test/testdriver/setup/testHelpers.mjs +6 -2
  37. package/vitest.config.mjs +7 -2
  38. package/test/testdriver/exec-js.test.mjs +0 -43
@@ -0,0 +1,145 @@
1
+ name: Test Init Command
2
+
3
+ on:
4
+ push:
5
+ branches: [ main, develop ]
6
+ pull_request:
7
+ branches: [ main, develop ]
8
+ workflow_dispatch:
9
+
10
+ jobs:
11
+ test-init:
12
+ runs-on: ubuntu-latest
13
+
14
+ steps:
15
+ - name: Checkout CLI repository
16
+ uses: actions/checkout@v4
17
+
18
+ - name: Setup Node.js
19
+ uses: actions/setup-node@v4
20
+ with:
21
+ node-version: '20'
22
+ cache: 'npm'
23
+
24
+ - name: Install CLI dependencies
25
+ run: npm ci
26
+
27
+ - name: Create test directory
28
+ run: |
29
+ mkdir -p /tmp/test-init-project
30
+ cd /tmp/test-init-project
31
+
32
+ - name: Run init command (skip prompts)
33
+ working-directory: /tmp/test-init-project
34
+ run: |
35
+ # Create .env with API key first to skip the prompt
36
+ echo "TD_API_KEY=${{ secrets.TD_API_KEY }}" > .env
37
+
38
+ # Run init command using the CLI from the repo
39
+ node ${{ github.workspace }}/bin/testdriverai.js init
40
+ env:
41
+ TD_API_KEY: ${{ secrets.TD_API_KEY }}
42
+
43
+ - name: Verify project structure
44
+ working-directory: /tmp/test-init-project
45
+ run: |
46
+ echo "Checking generated files..."
47
+
48
+ # Check for package.json
49
+ if [ ! -f "package.json" ]; then
50
+ echo "❌ package.json not found"
51
+ exit 1
52
+ fi
53
+ echo "✓ package.json exists"
54
+
55
+ # Check for vitest config
56
+ if [ ! -f "vitest.config.js" ]; then
57
+ echo "❌ vitest.config.js not found"
58
+ exit 1
59
+ fi
60
+ echo "✓ vitest.config.js exists"
61
+
62
+ # Check for test file
63
+ if [ ! -f "tests/example.test.js" ]; then
64
+ echo "❌ tests/example.test.js not found"
65
+ exit 1
66
+ fi
67
+ echo "✓ tests/example.test.js exists"
68
+
69
+ # Check for .env file
70
+ if [ ! -f ".env" ]; then
71
+ echo "❌ .env not found"
72
+ exit 1
73
+ fi
74
+ echo "✓ .env exists"
75
+
76
+ # Check for .gitignore
77
+ if [ ! -f ".gitignore" ]; then
78
+ echo "❌ .gitignore not found"
79
+ exit 1
80
+ fi
81
+ echo "✓ .gitignore exists"
82
+
83
+ # Check for GitHub workflow
84
+ if [ ! -f ".github/workflows/testdriver.yml" ]; then
85
+ echo "❌ .github/workflows/testdriver.yml not found"
86
+ exit 1
87
+ fi
88
+ echo "✓ .github/workflows/testdriver.yml exists"
89
+
90
+ - name: Verify vitest config contents
91
+ working-directory: /tmp/test-init-project
92
+ run: |
93
+ echo "Checking vitest.config.js contents..."
94
+
95
+ # Check for TestDriver reporter
96
+ if ! grep -q "TestDriver()" vitest.config.js; then
97
+ echo "❌ TestDriver reporter not found in vitest.config.js"
98
+ cat vitest.config.js
99
+ exit 1
100
+ fi
101
+ echo "✓ TestDriver reporter is configured"
102
+
103
+ # Check for setupFiles
104
+ if ! grep -q "setupFiles.*testdriverai/vitest/setup" vitest.config.js; then
105
+ echo "❌ setupFiles not configured correctly"
106
+ cat vitest.config.js
107
+ exit 1
108
+ fi
109
+ echo "✓ setupFiles is configured"
110
+
111
+ - name: Verify test file contents
112
+ working-directory: /tmp/test-init-project
113
+ run: |
114
+ echo "Checking test file contents..."
115
+
116
+ # Check for .provision usage
117
+ if ! grep -q "\.provision\.chrome" tests/example.test.js; then
118
+ echo "❌ Test does not use .provision.chrome"
119
+ cat tests/example.test.js
120
+ exit 1
121
+ fi
122
+ echo "✓ Test uses .provision.chrome"
123
+
124
+ # Check for TestDriver import
125
+ if ! grep -q "from 'testdriverai/vitest/hooks'" tests/example.test.js; then
126
+ echo "❌ Test does not import from testdriverai/vitest/hooks"
127
+ cat tests/example.test.js
128
+ exit 1
129
+ fi
130
+ echo "✓ Test imports TestDriver from vitest/hooks"
131
+
132
+ - name: Run the generated test
133
+ working-directory: /tmp/test-init-project
134
+ run: npm test
135
+ env:
136
+ TD_API_KEY: ${{ secrets.TD_API_KEY }}
137
+
138
+ - name: Upload test results
139
+ if: always()
140
+ uses: actions/upload-artifact@v4
141
+ with:
142
+ name: test-init-results
143
+ path: /tmp/test-init-project/test-results/
144
+ retention-days: 7
145
+ if-no-files-found: warn
@@ -178,9 +178,9 @@ commands:
178
178
  emitter.emit(events.log.log, generator.jsonToManual(object));
179
179
  response = await commands["focus-application"](object.name);
180
180
  break;
181
- case "remember": {
181
+ case "extract": {
182
182
  emitter.emit(events.log.log, generator.jsonToManual(object));
183
- let value = await commands["remember"](object.description);
183
+ let value = await commands["extract"](object.description);
184
184
  emitter.emit(events.log.log, value);
185
185
  outputsInstance.set(object.output, value);
186
186
  break;
@@ -283,6 +283,11 @@ const createCommands = (
283
283
  * @param {number} [options.redraw.diffThreshold=0.1] - Screen diff threshold percentage
284
284
  */
285
285
  const scroll = async (direction = 'down', options = {}) => {
286
+ // Convert number to object format
287
+ if (typeof options === 'number') {
288
+ options = { amount: options };
289
+ }
290
+
286
291
  let { amount = 300 } = options;
287
292
  const redrawOptions = extractRedrawOptions(options);
288
293
 
@@ -1151,7 +1156,7 @@ const createCommands = (
1151
1156
  ),
1152
1157
  true,
1153
1158
  );
1154
- await scroll({ direction, amount: incrementDistance });
1159
+ await scroll(direction, { amount: incrementDistance });
1155
1160
  scrollDistance = scrollDistance + incrementDistance;
1156
1161
  }
1157
1162
  }
@@ -1231,7 +1236,7 @@ const createCommands = (
1231
1236
  theme.dim(`scrolling ${direction} ${incrementDistance} pixels...`),
1232
1237
  true,
1233
1238
  );
1234
- await scroll({ direction, amount: incrementDistance });
1239
+ await scroll(direction, { amount: incrementDistance });
1235
1240
  scrollDistance = scrollDistance + incrementDistance;
1236
1241
  }
1237
1242
  }
@@ -1271,11 +1276,11 @@ const createCommands = (
1271
1276
  return "The application was focused.";
1272
1277
  },
1273
1278
  /**
1274
- * Extract and remember information from the screen using AI
1279
+ * Extract information from the screen using AI
1275
1280
  * @param {Object|string} options - Options object or description (for backward compatibility)
1276
- * @param {string} options.description - What to remember
1281
+ * @param {string} options.description - What to extract
1277
1282
  */
1278
- "remember": async (...args) => {
1283
+ "extract": async (...args) => {
1279
1284
  const rememberStartTime = Date.now();
1280
1285
  let description;
1281
1286
 
package/docs/docs.json CHANGED
@@ -232,8 +232,28 @@
232
232
  "/v7/presets/chrome",
233
233
  "/v7/presets/chrome-extension",
234
234
  "/v7/presets/vscode",
235
- "/v7/presets/electron",
236
- "/v7/presets/webapp"
235
+ "/v7/presets/electron"
236
+ ]
237
+ },
238
+ {
239
+ "group": "Actions",
240
+ "icon": "bolt",
241
+ "pages": [
242
+ "/v7/api/act",
243
+ "/v7/api/assert",
244
+ "/v7/api/assertions",
245
+ "/v7/api/click",
246
+ "/v7/api/doubleClick",
247
+ "/v7/api/exec",
248
+ "/v7/api/find",
249
+ "/v7/api/focusApplication",
250
+ "/v7/api/hover",
251
+ "/v7/api/mouseDown",
252
+ "/v7/api/mouseUp",
253
+ "/v7/api/pressKeys",
254
+ "/v7/api/rightClick",
255
+ "/v7/api/type",
256
+ "/v7/api/scroll"
237
257
  ]
238
258
  },
239
259
  {
@@ -241,35 +261,8 @@
241
261
  "icon": "book",
242
262
  "pages": [
243
263
  "/v7/api/client",
264
+ "/v7/api/elements",
244
265
  "/v7/api/sandbox",
245
- {
246
- "group": "Interactions",
247
- "icon": "bolt",
248
- "pages": [
249
- "/v7/api/find",
250
- "/v7/api/elements",
251
- "/v7/api/click",
252
- "/v7/api/doubleClick",
253
- "/v7/api/rightClick",
254
- "/v7/api/hover",
255
- "/v7/api/mouseDown",
256
- "/v7/api/mouseUp",
257
- "/v7/api/type",
258
- "/v7/api/pressKeys",
259
- "/v7/api/scroll",
260
- "/v7/api/focusApplication"
261
- ]
262
- },
263
- {
264
- "group": "AI & Assertions",
265
- "icon": "wand-magic-sparkles",
266
- "pages": [
267
- "/v7/api/act",
268
- "/v7/api/assert",
269
- "/v7/api/assertions"
270
- ]
271
- },
272
- "/v7/api/exec",
273
266
  "/v7/api/dashcam"
274
267
  ]
275
268
  }
@@ -313,64 +306,16 @@
313
306
  },
314
307
  "redirects": [
315
308
  {
316
- "source": "/overview/:slug*",
317
- "destination": "/v6/overview/:slug*"
318
- },
319
- {
320
- "source": "/features/:slug*",
321
- "destination": "/v6/features/:slug*"
322
- },
323
- {
324
- "source": "/account/:slug*",
325
- "destination": "/v6/account/:slug*"
326
- },
327
- {
328
- "source": "/cli/:slug*",
329
- "destination": "/v6/cli/:slug*"
330
- },
331
- {
332
- "source": "/interactive/:slug*",
333
- "destination": "/v6/interactive/:slug*"
334
- },
335
- {
336
- "source": "/getting-started/:slug*",
337
- "destination": "/v6/getting-started/:slug*"
338
- },
339
- {
340
- "source": "/apps/:slug*",
341
- "destination": "/v6/apps/:slug*"
342
- },
343
- {
344
- "source": "/scenarios/:slug*",
345
- "destination": "/v6/scenarios/:slug*"
346
- },
347
- {
348
- "source": "/integrations/:slug*",
349
- "destination": "/v6/integrations/:slug*"
350
- },
351
- {
352
- "source": "/guide/:slug*",
353
- "destination": "/v6/guide/:slug*"
354
- },
355
- {
356
- "source": "/action/:slug*",
357
- "destination": "/v6/action/:slug*"
358
- },
359
- {
360
- "source": "/importing/:slug*",
361
- "destination": "/v6/importing/:slug*"
362
- },
363
- {
364
- "source": "/exporting/:slug*",
365
- "destination": "/v6/exporting/:slug*"
309
+ "source": "/guides/github-actions",
310
+ "destination": "/action/setup"
366
311
  },
367
312
  {
368
- "source": "/bugs/:slug*",
369
- "destination": "/v6/bugs/:slug*"
313
+ "source": "/reference/test-steps",
314
+ "destination": "/features/selectorless"
370
315
  },
371
316
  {
372
- "source": "/commands/:slug*",
373
- "destination": "/v6/commands/:slug*"
317
+ "source": "/guides/debugging-test-runs",
318
+ "destination": "/getting-started/editing"
374
319
  }
375
320
  ],
376
321
  "integrations": {
@@ -207,16 +207,16 @@ await testdriver.focusApplication('Google Chrome');
207
207
  ```
208
208
  </CodeGroup>
209
209
 
210
- ### Remember
210
+ ### Extract
211
211
 
212
212
  <CodeGroup>
213
213
  ```yaml YAML
214
- - command: remember
214
+ - command: extract
215
215
  description: the order number
216
216
  ```
217
217
 
218
218
  ```javascript SDK
219
- const orderNumber = await testdriver.remember('the order number');
219
+ const orderNumber = await testdriver.extract('the order number');
220
220
  ```
221
221
  </CodeGroup>
222
222
 
@@ -202,4 +202,4 @@ describe('E-commerce Flow with AI', () => {
202
202
 
203
203
  - [`find()`](/v7/api/find) - Locate specific elements
204
204
  - [`assert()`](/v7/api/assert) - Make assertions
205
- - [`remember()`](/v7/api/remember) - Extract information
205
+ - [`extract()`](/v7/api/extract) - Extract information
@@ -280,6 +280,6 @@ describe('Assertions', () => {
280
280
 
281
281
  ## Related Methods
282
282
 
283
- - [`remember()`](/v7/api/remember) - Extract information for detailed assertions
283
+ - [`extract()`](/v7/api/extract) - Extract information for detailed assertions
284
284
  - [`find()`](/v7/api/find) - Locate elements to verify
285
285
  - [`ai()`](/v7/api/ai) - Complex AI-driven tasks
@@ -77,12 +77,12 @@ await waitForAssertion(testdriver, 'results are displayed', 10000);
77
77
 
78
78
  ## Extracting Information
79
79
 
80
- ### remember()
80
+ ### extract()
81
81
 
82
- Extract and remember information from the screen using AI.
82
+ Extract information from the screen using AI.
83
83
 
84
84
  ```javascript
85
- await testdriver.remember(description)
85
+ await testdriver.extract(description)
86
86
  ```
87
87
 
88
88
  **Parameters:**
@@ -94,17 +94,17 @@ await testdriver.remember(description)
94
94
 
95
95
  ```javascript
96
96
  // Extract text from screen
97
- const password = await testdriver.remember('the password displayed on screen');
98
- const total = await testdriver.remember('the order total amount');
99
- const errorMessage = await testdriver.remember('the error message text');
97
+ const password = await testdriver.extract('the password displayed on screen');
98
+ const total = await testdriver.extract('the order total amount');
99
+ const errorMessage = await testdriver.extract('the error message text');
100
100
 
101
101
  // Extract structured data
102
- const email = await testdriver.remember('the email address in the confirmation');
103
- const orderId = await testdriver.remember('the order ID number');
104
- const phoneNumber = await testdriver.remember('the phone number');
102
+ const email = await testdriver.extract('the email address in the confirmation');
103
+ const orderId = await testdriver.extract('the order ID number');
104
+ const phoneNumber = await testdriver.extract('the phone number');
105
105
 
106
106
  // Use extracted data
107
- const password = await testdriver.remember('the password for standard_user');
107
+ const password = await testdriver.extract('the password for standard_user');
108
108
  const passwordField = await testdriver.find('password input');
109
109
  await passwordField.click();
110
110
  await testdriver.type(password);
@@ -114,8 +114,8 @@ await testdriver.type(password);
114
114
 
115
115
  **Dynamic Content:**
116
116
  ```javascript
117
- // Remember generated values
118
- const confirmationCode = await testdriver.remember('the 6-digit confirmation code');
117
+ // Extract generated values
118
+ const confirmationCode = await testdriver.extract('the 6-digit confirmation code');
119
119
  console.log('Code:', confirmationCode);
120
120
 
121
121
  // Use it later in the test
@@ -125,7 +125,7 @@ await testdriver.type(confirmationCode);
125
125
  **Verification:**
126
126
  ```javascript
127
127
  // Extract and verify
128
- const displayedTotal = await testdriver.remember('the cart total');
128
+ const displayedTotal = await testdriver.extract('the cart total');
129
129
  const expectedTotal = '$99.99';
130
130
 
131
131
  expect(displayedTotal).toBe(expectedTotal);
@@ -134,7 +134,7 @@ expect(displayedTotal).toBe(expectedTotal);
134
134
  **Conditional Logic:**
135
135
  ```javascript
136
136
  // Extract state and make decisions
137
- const status = await testdriver.remember('the order status');
137
+ const status = await testdriver.extract('the order status');
138
138
 
139
139
  if (status.includes('Pending')) {
140
140
  // Wait for processing
@@ -44,14 +44,16 @@ TestDriver v7 lets you write AI-powered tests in JavaScript/TypeScript with full
44
44
  <Tab title="Fast">
45
45
  ```javascript test.test.js
46
46
  import { test } from 'vitest';
47
- import { chrome } from 'testdriverai/presets';
47
+ import { TestDriver } from 'testdriverai/vitest/hooks';
48
48
 
49
49
  test('my first test', async (context) => {
50
- const { testdriver } = await chrome(context, {
51
- url: 'https://example.com',
50
+ const testdriver = TestDriver(context, {
51
+ headless: true,
52
52
  apiKey: 'tdai-1234567890abcdef' // Your API key from console.testdriver.ai
53
53
  });
54
54
 
55
+ await testdriver.provision.chrome({ url: 'https://example.com' });
56
+
55
57
  await testdriver.find('More information link').click();
56
58
  await testdriver.assert('IANA page is visible');
57
59
  });
@@ -73,13 +75,13 @@ TestDriver v7 lets you write AI-powered tests in JavaScript/TypeScript with full
73
75
 
74
76
  ```javascript test.test.js
75
77
  import { test } from 'vitest';
76
- import { chrome } from 'testdriverai/presets';
78
+ import { TestDriver } from 'testdriverai/vitest/hooks';
77
79
 
78
80
  test('my first test', async (context) => {
79
- const { testdriver } = await chrome(context, {
80
- url: 'https://example.com'
81
- // apiKey automatically read from process.env.TD_API_KEY
82
- });
81
+ const testdriver = TestDriver(context, { headless: true });
82
+ // apiKey automatically read from process.env.TD_API_KEY
83
+
84
+ await testdriver.provision.chrome({ url: 'https://example.com' });
83
85
 
84
86
  await testdriver.find('More information link').click();
85
87
  await testdriver.assert('IANA page is visible');
@@ -111,7 +113,7 @@ TestDriver v7 lets you write AI-powered tests in JavaScript/TypeScript with full
111
113
 
112
114
  ## What Just Happened?
113
115
 
114
- The `chrome()` preset automatically:
116
+ The TestDriver hook with `provision.chrome()` automatically:
115
117
  1. ✅ Connected to a TestDriver sandbox
116
118
  2. ✅ Launched Chrome browser
117
119
  3. ✅ Navigated to your URL
@@ -101,11 +101,12 @@ Every test automatically records a video replay:
101
101
  <Step title="Get Replay URL">
102
102
  ```javascript
103
103
  test('my test', async (context) => {
104
- const { testdriver, dashcam } = await chrome(context, { url });
104
+ const testdriver = TestDriver(context, { headless: true });
105
+ await testdriver.provision.chrome({ url });
105
106
 
106
107
  // Your test code
107
108
 
108
- console.log('Replay:', dashcam.url);
109
+ console.log('Replay:', testdriver.dashcam.url);
109
110
  });
110
111
  ```
111
112
 
@@ -12,13 +12,12 @@ Every TestDriver test follows this simple pattern:
12
12
 
13
13
  ```javascript
14
14
  import { test } from 'vitest';
15
- import { chrome } from 'testdriverai/presets';
15
+ import { TestDriver } from 'testdriverai/vitest/hooks';
16
16
 
17
17
  test('descriptive test name', async (context) => {
18
18
  // 1. Setup: Launch and navigate
19
- const { testdriver } = await chrome(context, {
20
- url: 'https://myapp.com'
21
- });
19
+ const testdriver = TestDriver(context, { headless: true });
20
+ await testdriver.provision.chrome({ url: 'https://myapp.com' });
22
21
 
23
22
  // 2. Act: Interact with your application
24
23
  await testdriver.find('email input').type('user@example.com');
@@ -30,7 +29,7 @@ test('descriptive test name', async (context) => {
30
29
  ```
31
30
 
32
31
  <Tip>
33
- The preset handles all setup and cleanup automatically - no need for manual browser management!
32
+ The TestDriver hook handles all setup and cleanup automatically - no need for manual browser management!
34
33
  </Tip>
35
34
 
36
35
  ## Finding Elements
@@ -75,7 +75,7 @@ v7/
75
75
  - Basic assertions
76
76
  - Negative assertions (invert parameter)
77
77
  - Async assertions
78
- - `remember()` - Extract information from screen
78
+ - `extract()` - Extract information from screen
79
79
  - Testing patterns:
80
80
  - Polling assertions
81
81
  - Multi-step validation