testdriverai 7.2.20 → 7.2.22

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.
@@ -1,517 +0,0 @@
1
- ---
2
- title: "Performance Optimization"
3
- description: "Speed up your TestDriver tests"
4
- icon: "gauge"
5
- ---
6
-
7
- ## Overview
8
-
9
- TestDriver tests run in cloud sandboxes, which adds latency. This guide shows you how to minimize overhead and maximize test speed.
10
-
11
- ## Baseline Performance
12
-
13
- Typical test execution:
14
-
15
- | Phase | Time | What's Happening |
16
- |-------|------|------------------|
17
- | Sandbox startup | 20-60s | First test creates VM |
18
- | Sandbox reuse | 0s | Subsequent tests reuse VM |
19
- | Page load | 1-5s | Browser navigates to URL |
20
- | Element finding | 0.5-3s | AI locates element |
21
- | Element finding (cached) | 0.1-0.5s | Cache hit |
22
- | Command execution | 0.1-1s | Click, type, etc. |
23
- | AI command | 2-5s | Natural language parsing |
24
- | AI command (cached) | 0.1-0.5s | Cache hit |
25
-
26
- **First test**: 60-90s
27
- **Subsequent tests**: 5-30s
28
- **Optimized tests**: 2-10s
29
-
30
- ## Optimization Strategies
31
-
32
- ### 1. Reuse Sandboxes
33
-
34
- The biggest performance win is reusing sandboxes across tests.
35
-
36
- #### ❌ Slow - Create new sandbox per test
37
-
38
- ```javascript
39
- test('test 1', async () => {
40
- const testdriver = await TestDriver.create({
41
- apiKey: process.env.TD_API_KEY
42
- });
43
- // Test logic
44
- await testdriver.cleanup();
45
- });
46
-
47
- test('test 2', async () => {
48
- const testdriver = await TestDriver.create({
49
- apiKey: process.env.TD_API_KEY
50
- });
51
- // Test logic
52
- await testdriver.cleanup();
53
- });
54
- ```
55
-
56
- **Time**: 60s + 60s = 120s
57
-
58
- #### ✅ Fast - Reuse sandbox with context
59
-
60
- ```javascript
61
- import { chrome } from '../setup/lifecycleHelpers.mjs';
62
-
63
- test('test 1', async (context) => {
64
- const { testdriver } = await chrome(context, { url: 'https://app.com' });
65
- // Test logic
66
- });
67
-
68
- test('test 2', async (context) => {
69
- const { testdriver } = await chrome(context, { url: 'https://app.com' });
70
- // Test logic
71
- });
72
- ```
73
-
74
- **Time**: 60s + 5s = 65s (54% faster)
75
-
76
- ### 2. Enable Caching
77
-
78
- TestDriver has two cache layers:
79
-
80
- #### AI Prompt Cache
81
-
82
- Caches AI-generated commands locally:
83
-
84
- ```yaml
85
- # testdriver.yaml
86
- cache:
87
- ai: true # Enable prompt caching
88
- ```
89
-
90
- **Impact**: 2-5s → 0.1-0.5s per AI command
91
-
92
- #### Selector Cache
93
-
94
- Caches element locations on server:
95
-
96
- ```yaml
97
- # testdriver.yaml
98
- cache:
99
- selectors: true
100
- selectorThreshold: 0.95 # 95% similarity required
101
- ```
102
-
103
- **Impact**: 0.5-3s → 0.1-0.5s per element find
104
-
105
- **Combined**: Tests run 3-5x faster on subsequent runs.
106
-
107
- ### 3. Parallel Test Execution
108
-
109
- Run multiple tests simultaneously:
110
-
111
- ```javascript
112
- // vitest.config.mjs
113
- export default defineConfig({
114
- test: {
115
- maxConcurrency: 5, // Run 5 tests at once
116
- fileParallelism: true
117
- }
118
- });
119
- ```
120
-
121
- **Impact**: 5 tests in 300s → 5 tests in 90s (70% faster)
122
-
123
- **Tradeoffs**:
124
- - Uses more test minutes
125
- - May hit API rate limits
126
- - Requires adequate plan limits
127
-
128
- ### 4. Reduce AI Commands
129
-
130
- AI commands are slower than direct commands.
131
-
132
- #### ❌ Slow - AI for everything
133
-
134
- ```javascript
135
- await testdriver.ai('click the login button');
136
- await testdriver.ai('type email@example.com');
137
- await testdriver.ai('type mypassword');
138
- await testdriver.ai('press enter');
139
- ```
140
-
141
- **Time**: ~15s
142
-
143
- #### ✅ Fast - AI only for finding
144
-
145
- ```javascript
146
- await testdriver.find('email field').then(el => el.click());
147
- await testdriver.type('email@example.com');
148
-
149
- await testdriver.find('password field').then(el => el.click());
150
- await testdriver.type('mypassword');
151
-
152
- await testdriver.find('login button').then(el => el.click());
153
- ```
154
-
155
- **Time**: ~5s (66% faster)
156
-
157
- ### 5. Batch Operations
158
-
159
- Minimize round trips to sandbox:
160
-
161
- #### ❌ Slow - Sequential operations
162
-
163
- ```javascript
164
- await testdriver.find('first name').then(el => el.click());
165
- await testdriver.type('John');
166
-
167
- await testdriver.find('last name').then(el => el.click());
168
- await testdriver.type('Doe');
169
-
170
- await testdriver.find('email').then(el => el.click());
171
- await testdriver.type('john@example.com');
172
- ```
173
-
174
- **Time**: 6 round trips
175
-
176
- #### ✅ Fast - Batch with keyboard navigation
177
-
178
- ```javascript
179
- await testdriver.find('first name').then(el => el.click());
180
- await testdriver.type('John');
181
-
182
- await testdriver.pressKeys(['tab']);
183
- await testdriver.type('Doe');
184
-
185
- await testdriver.pressKeys(['tab']);
186
- await testdriver.type('john@example.com');
187
- ```
188
-
189
- **Time**: 4 round trips (33% faster)
190
-
191
- ### 6. Smart Waiting
192
-
193
- Avoid unnecessary delays:
194
-
195
- #### ❌ Slow - Fixed delays
196
-
197
- ```javascript
198
- await testdriver.find('button').then(el => el.click());
199
- await new Promise(r => setTimeout(r, 5000)); // Always wait 5s
200
- await testdriver.find('success message');
201
- ```
202
-
203
- #### ✅ Fast - Poll until found
204
-
205
- ```javascript
206
- await testdriver.find('button').then(el => el.click());
207
-
208
- // Poll for success message
209
- let element;
210
- for (let i = 0; i < 30; i++) {
211
- element = await testdriver.find('success message');
212
- if (element.found()) break;
213
- await new Promise(r => setTimeout(r, 500));
214
- }
215
- ```
216
-
217
- #### ✅ Better - Use assertions
218
-
219
- ```javascript
220
- await testdriver.find('button').then(el => el.click());
221
- await testdriver.assert('success message appeared'); // Waits intelligently
222
- ```
223
-
224
- ### 7. Optimize Element Descriptions
225
-
226
- More specific = faster finding:
227
-
228
- #### ❌ Slow - Vague description
229
-
230
- ```javascript
231
- await testdriver.find('button'); // Many buttons to check
232
- ```
233
-
234
- **Time**: 2-3s (checks all buttons)
235
-
236
- #### ✅ Fast - Specific description
237
-
238
- ```javascript
239
- await testdriver.find('blue submit button at bottom right');
240
- ```
241
-
242
- **Time**: 0.5-1s (narrows search area)
243
-
244
- ### 8. Preload Resources
245
-
246
- Speed up page load:
247
-
248
- #### ❌ Slow - Load on demand
249
-
250
- ```javascript
251
- test('test 1', async (context) => {
252
- const { testdriver } = await chrome(context, { url: 'https://app.com' });
253
- // First test loads everything
254
- });
255
- ```
256
-
257
- #### ✅ Fast - Preload in beforeAll
258
-
259
- ```javascript
260
- import { beforeAll, test } from 'vitest';
261
-
262
- beforeAll(async (context) => {
263
- const { testdriver } = await chrome(context, { url: 'https://app.com' });
264
- // Preload app, login, navigate to test area
265
- await testdriver.find('email').then(el => el.click());
266
- await testdriver.type(process.env.TEST_EMAIL);
267
- await testdriver.find('password').then(el => el.click());
268
- await testdriver.type(process.env.TEST_PASSWORD);
269
- await testdriver.find('login').then(el => el.click());
270
- }, 120000);
271
- ```
272
-
273
- ### 9. Clean Cache Strategically
274
-
275
- Don't clear cache unnecessarily:
276
-
277
- #### ❌ Slow - Clear cache every run
278
-
279
- ```bash
280
- # package.json
281
- {
282
- "scripts": {
283
- "test": "rm -rf .testdriver/.cache && vitest"
284
- }
285
- }
286
- ```
287
-
288
- #### ✅ Fast - Clear cache when needed
289
-
290
- ```bash
291
- # Only clear when UI changes
292
- npm run test:clean # Separate script
293
-
294
- # Normal runs use cache
295
- npm test
296
- ```
297
-
298
- ### 10. Monitor Performance
299
-
300
- Track test execution time:
301
-
302
- ```javascript
303
- import { test } from 'vitest';
304
-
305
- test('slow test detector', async (context) => {
306
- const start = Date.now();
307
-
308
- // Test logic
309
- const { testdriver } = await chrome(context, { url });
310
- await testdriver.find('button').then(el => el.click());
311
-
312
- const duration = Date.now() - start;
313
- console.log(`Test took ${duration}ms`);
314
-
315
- if (duration > 30000) {
316
- console.warn('⚠️ Test exceeded 30s threshold');
317
- }
318
- });
319
- ```
320
-
321
- ## Configuration Tuning
322
-
323
- ### Timeout Settings
324
-
325
- Balance reliability vs speed:
326
-
327
- ```javascript
328
- // vitest.config.mjs
329
- export default defineConfig({
330
- test: {
331
- testTimeout: 60000, // Default: 60s
332
- hookTimeout: 30000 // Setup/teardown: 30s
333
- }
334
- });
335
- ```
336
-
337
- For fast, stable tests:
338
- ```javascript
339
- testTimeout: 30000 // 30s
340
- ```
341
-
342
- For slower, complex tests:
343
- ```javascript
344
- testTimeout: 120000 // 2 minutes
345
- ```
346
-
347
- ### Verbosity Level
348
-
349
- Lower verbosity = less overhead:
350
-
351
- ```javascript
352
- const testdriver = await TestDriver.create({
353
- apiKey: process.env.TD_API_KEY,
354
- verbosity: 0 // 0=silent, 1=normal, 2=debug
355
- });
356
- ```
357
-
358
- **Impact**: Small (~100ms per test), but adds up.
359
-
360
- ### Resolution
361
-
362
- Lower resolution = faster screenshots:
363
-
364
- ```javascript
365
- const testdriver = await TestDriver.create({
366
- apiKey: process.env.TD_API_KEY,
367
- resolution: '1280x720' // Default: 1920x1080
368
- });
369
- ```
370
-
371
- **Impact**: 10-20% faster element finding.
372
-
373
- ## Advanced Techniques
374
-
375
- ### Connection Pooling
376
-
377
- Reuse connections across test suites:
378
-
379
- ```javascript
380
- // setup/pool.mjs
381
- const clients = new Map();
382
-
383
- export async function getClient(context) {
384
- const key = `${context.task.file.name}`;
385
-
386
- if (!clients.has(key)) {
387
- clients.set(key, await TestDriver.create({
388
- apiKey: process.env.TD_API_KEY
389
- }));
390
- }
391
-
392
- return clients.get(key);
393
- }
394
-
395
- export async function cleanup() {
396
- for (const client of clients.values()) {
397
- await client.cleanup();
398
- }
399
- clients.clear();
400
- }
401
- ```
402
-
403
- ### Lazy Loading
404
-
405
- Only load what you need:
406
-
407
- ```javascript
408
- // ❌ Load everything upfront
409
- import { chrome, firefox, electron } from './setup/lifecycleHelpers.mjs';
410
-
411
- // ✅ Import only what you use
412
- import { chrome } from './setup/lifecycleHelpers.mjs';
413
- ```
414
-
415
- ### Snapshot Testing
416
-
417
- Compare screenshots instead of re-running:
418
-
419
- ```javascript
420
- test('visual regression', async (context) => {
421
- const { testdriver } = await chrome(context, { url });
422
-
423
- const element = await testdriver.find('hero section');
424
-
425
- // First run: saves screenshot
426
- // Subsequent runs: compares screenshot
427
- expect(element.screenshot).toMatchImageSnapshot();
428
- });
429
- ```
430
-
431
- ## Profiling
432
-
433
- ### Find Slow Tests
434
-
435
- ```bash
436
- # Run with timing report
437
- npm test -- --reporter=verbose
438
-
439
- # Output shows duration per test
440
- ✓ fast test (1.2s)
441
- ✓ medium test (5.4s)
442
- ✗ slow test (45.2s)
443
- ```
444
-
445
- ### Measure Operations
446
-
447
- ```javascript
448
- async function timedOperation(name, fn) {
449
- const start = Date.now();
450
- const result = await fn();
451
- console.log(`${name}: ${Date.now() - start}ms`);
452
- return result;
453
- }
454
-
455
- test('profiled test', async (context) => {
456
- const { testdriver } = await timedOperation('Setup', async () => {
457
- return await chrome(context, { url });
458
- });
459
-
460
- await timedOperation('Find button', async () => {
461
- return await testdriver.find('button');
462
- });
463
-
464
- await timedOperation('Click button', async () => {
465
- const el = await testdriver.find('button');
466
- return await el.click();
467
- });
468
- });
469
- ```
470
-
471
- ### Identify Bottlenecks
472
-
473
- Common slow operations:
474
-
475
- 1. **Sandbox creation** (20-60s) - Reuse sandboxes
476
- 2. **Page navigation** (1-5s) - Minimize navigation
477
- 3. **AI commands** (2-5s) - Use direct commands
478
- 4. **Element finding** (0.5-3s) - Enable caching
479
- 5. **Fixed delays** (varies) - Replace with smart waiting
480
-
481
- ## Production Checklist
482
-
483
- - [ ] Reuse sandboxes via context
484
- - [ ] Enable AI and selector caching
485
- - [ ] Use parallel execution (maxConcurrency: 5)
486
- - [ ] Minimize AI commands
487
- - [ ] Batch operations
488
- - [ ] Use smart waiting (assertions)
489
- - [ ] Specific element descriptions
490
- - [ ] Appropriate timeout settings
491
- - [ ] Monitor test duration
492
- - [ ] Profile slow tests
493
-
494
- Expected performance:
495
- - **First run**: 60-90s for suite
496
- - **Cached runs**: 10-30s for suite
497
- - **Per test**: 2-10s (cached)
498
-
499
- ## See Also
500
-
501
- <CardGroup cols={2}>
502
- <Card title="Caching (AI)" icon="brain" href="/v7/guides/caching-ai">
503
- AI prompt caching
504
- </Card>
505
-
506
- <Card title="Caching (Selectors)" icon="bullseye" href="/v7/guides/caching-selectors">
507
- Selector caching
508
- </Card>
509
-
510
- <Card title="Best Practices" icon="star" href="/v7/guides/best-practices">
511
- Testing patterns
512
- </Card>
513
-
514
- <Card title="CI/CD Integration" icon="arrows-spin" href="/v7/guides/ci-cd">
515
- Optimize CI pipelines
516
- </Card>
517
- </CardGroup>