testdriverai 7.1.3 → 7.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/acceptance.yaml +81 -0
- package/.github/workflows/publish.yaml +44 -0
- package/.github/workflows/test-init.yml +145 -0
- package/agent/index.js +18 -19
- package/agent/lib/commander.js +2 -2
- package/agent/lib/commands.js +324 -124
- package/agent/lib/redraw.js +99 -39
- package/agent/lib/sandbox.js +98 -6
- package/agent/lib/sdk.js +25 -0
- package/agent/lib/system.js +2 -1
- package/agent/lib/validation.js +6 -6
- package/docs/docs.json +211 -101
- package/docs/snippets/tests/type-repeated-replay.mdx +1 -1
- package/docs/v7/_drafts/caching-selectors.mdx +24 -0
- package/docs/v7/_drafts/migration.mdx +3 -3
- package/docs/v7/api/act.mdx +2 -2
- package/docs/v7/api/assert.mdx +2 -2
- package/docs/v7/api/assertions.mdx +21 -21
- package/docs/v7/api/elements.mdx +78 -0
- package/docs/v7/api/find.mdx +38 -0
- package/docs/v7/api/focusApplication.mdx +2 -2
- package/docs/v7/api/hover.mdx +2 -2
- package/docs/v7/features/ai-native.mdx +57 -71
- package/docs/v7/features/application-logs.mdx +353 -0
- package/docs/v7/features/browser-logs.mdx +414 -0
- package/docs/v7/features/cache-management.mdx +402 -0
- package/docs/v7/features/continuous-testing.mdx +346 -0
- package/docs/v7/features/coverage.mdx +508 -0
- package/docs/v7/features/data-driven-testing.mdx +441 -0
- package/docs/v7/features/easy-to-write.mdx +2 -73
- package/docs/v7/features/enterprise.mdx +155 -39
- package/docs/v7/features/fast.mdx +63 -81
- package/docs/v7/features/managed-sandboxes.mdx +384 -0
- package/docs/v7/features/network-monitoring.mdx +568 -0
- package/docs/v7/features/observable.mdx +3 -22
- package/docs/v7/features/parallel-execution.mdx +381 -0
- package/docs/v7/features/powerful.mdx +1 -1
- package/docs/v7/features/reports.mdx +414 -0
- package/docs/v7/features/sandbox-customization.mdx +229 -0
- package/docs/v7/features/scalable.mdx +217 -2
- package/docs/v7/features/stable.mdx +106 -147
- package/docs/v7/features/system-performance.mdx +616 -0
- package/docs/v7/features/test-analytics.mdx +373 -0
- package/docs/v7/features/test-cases.mdx +393 -0
- package/docs/v7/features/test-replays.mdx +408 -0
- package/docs/v7/features/test-reports.mdx +308 -0
- package/docs/v7/getting-started/{running-and-debugging.mdx → debugging-tests.mdx} +12 -142
- package/docs/v7/getting-started/quickstart.mdx +22 -305
- package/docs/v7/getting-started/running-tests.mdx +173 -0
- package/docs/v7/overview/readme.mdx +1 -1
- package/docs/v7/overview/what-is-testdriver.mdx +2 -14
- package/docs/v7/presets/chrome-extension.mdx +147 -122
- package/interfaces/cli/commands/init.js +78 -20
- package/interfaces/cli/lib/base.js +3 -2
- package/interfaces/logger.js +0 -2
- package/interfaces/shared-test-state.mjs +0 -5
- package/interfaces/vitest-plugin.mjs +69 -42
- package/lib/core/Dashcam.js +65 -66
- package/lib/vitest/hooks.mjs +42 -50
- package/manual/test-init-command.js +223 -0
- package/package.json +2 -2
- package/schema.json +5 -5
- package/sdk-log-formatter.js +351 -176
- package/sdk.d.ts +8 -8
- package/sdk.js +436 -121
- package/setup/aws/cloudformation.yaml +2 -2
- package/setup/aws/self-hosted.yml +1 -1
- package/test/testdriver/chrome-extension.test.mjs +55 -72
- package/test/testdriver/element-not-found.test.mjs +2 -1
- package/test/testdriver/hover-image.test.mjs +1 -1
- package/test/testdriver/hover-text-with-description.test.mjs +0 -3
- package/test/testdriver/scroll-until-text.test.mjs +10 -6
- package/test/testdriver/setup/lifecycleHelpers.mjs +19 -24
- package/test/testdriver/setup/testHelpers.mjs +18 -23
- package/vitest.config.mjs +3 -3
- package/.github/workflows/linux-tests.yml +0 -28
- package/docs/v7/getting-started/generating-tests.mdx +0 -525
- package/test/testdriver/auto-cache-key-demo.test.mjs +0 -56
package/docs/docs.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://mintlify.com/docs.json",
|
|
3
|
-
"theme": "
|
|
3
|
+
"theme": "aspen",
|
|
4
4
|
"name": "TestDriver",
|
|
5
5
|
"colors": {
|
|
6
6
|
"primary": "#b3d334",
|
|
@@ -12,12 +12,9 @@
|
|
|
12
12
|
},
|
|
13
13
|
"favicon": "/images/template/icon.png",
|
|
14
14
|
"navigation": {
|
|
15
|
-
"tabs": [
|
|
16
|
-
{
|
|
17
|
-
"tab": "Computer-Use SDK",
|
|
18
15
|
"versions": [
|
|
19
16
|
{
|
|
20
|
-
"version": "v6
|
|
17
|
+
"version": "v6",
|
|
21
18
|
"groups": [
|
|
22
19
|
{
|
|
23
20
|
"group": "Overview",
|
|
@@ -68,24 +65,24 @@
|
|
|
68
65
|
"group": "Apps",
|
|
69
66
|
"icon": "laptop",
|
|
70
67
|
"pages": [
|
|
71
|
-
"apps/static-websites",
|
|
72
|
-
"apps/desktop-apps",
|
|
73
|
-
"apps/chrome-extensions",
|
|
74
|
-
"apps/mobile-apps",
|
|
75
|
-
"apps/tauri-apps"
|
|
68
|
+
"/v6/apps/static-websites",
|
|
69
|
+
"/v6/apps/desktop-apps",
|
|
70
|
+
"/v6/apps/chrome-extensions",
|
|
71
|
+
"/v6/apps/mobile-apps",
|
|
72
|
+
"/v6/apps/tauri-apps"
|
|
76
73
|
]
|
|
77
74
|
},
|
|
78
75
|
{
|
|
79
76
|
"group": "Scenarios",
|
|
80
77
|
"icon": "computer-mouse",
|
|
81
78
|
"pages": [
|
|
82
|
-
"scenarios/ai-chatbot",
|
|
83
|
-
"scenarios/cookie-banner",
|
|
84
|
-
"scenarios/file-upload",
|
|
85
|
-
"scenarios/form-filling",
|
|
86
|
-
"scenarios/log-in",
|
|
87
|
-
"scenarios/pdf-generation",
|
|
88
|
-
"scenarios/spell-check"
|
|
79
|
+
"/v6/scenarios/ai-chatbot",
|
|
80
|
+
"/v6/scenarios/cookie-banner",
|
|
81
|
+
"/v6/scenarios/file-upload",
|
|
82
|
+
"/v6/scenarios/form-filling",
|
|
83
|
+
"/v6/scenarios/log-in",
|
|
84
|
+
"/v6/scenarios/pdf-generation",
|
|
85
|
+
"/v6/scenarios/spell-check"
|
|
89
86
|
]
|
|
90
87
|
},
|
|
91
88
|
{
|
|
@@ -156,7 +153,7 @@
|
|
|
156
153
|
{
|
|
157
154
|
"group": "Reporting",
|
|
158
155
|
"icon": "chart-simple",
|
|
159
|
-
"pages": ["exporting/junit", "/v6/bugs/jira"]
|
|
156
|
+
"pages": ["/v6/exporting/junit", "/v6/bugs/jira"]
|
|
160
157
|
}
|
|
161
158
|
]
|
|
162
159
|
},
|
|
@@ -190,87 +187,140 @@
|
|
|
190
187
|
}
|
|
191
188
|
]
|
|
192
189
|
},
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
190
|
+
{
|
|
191
|
+
"version": "v7",
|
|
192
|
+
"groups": [
|
|
193
|
+
{
|
|
194
|
+
"group": "Getting Started",
|
|
195
|
+
"icon": "rocket",
|
|
196
|
+
"pages": [
|
|
197
|
+
"/v7/getting-started/quickstart"
|
|
198
|
+
]
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
"group": "Guides",
|
|
202
|
+
"icon": "book",
|
|
203
|
+
"pages": [
|
|
204
|
+
"/v7/getting-started/writing-tests",
|
|
205
|
+
"/v7/getting-started/running-tests",
|
|
206
|
+
"/v7/getting-started/debugging-tests",
|
|
207
|
+
"/v7/getting-started/setting-up-in-ci"
|
|
208
|
+
]
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"group": "Examples",
|
|
212
|
+
"icon": "code",
|
|
213
|
+
"pages": [
|
|
214
|
+
"/v7/presets/chrome",
|
|
215
|
+
"/v7/presets/chrome-extension",
|
|
216
|
+
"/v7/presets/vscode",
|
|
217
|
+
"/v7/presets/electron"
|
|
218
|
+
]
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
"group": "Features",
|
|
222
|
+
"icon": "layer-group",
|
|
223
|
+
"pages": [
|
|
224
|
+
{
|
|
225
|
+
"group": "Selectorless Testing",
|
|
226
|
+
"icon": "sparkles",
|
|
227
|
+
"pages": [
|
|
228
|
+
"/v7/features/easy-to-write",
|
|
229
|
+
"/v7/features/coverage",
|
|
230
|
+
"/v7/features/powerful",
|
|
231
|
+
"/v7/features/ai-native"
|
|
232
|
+
]
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
"group": "Sandbox Environments",
|
|
236
|
+
"icon": "cube",
|
|
237
|
+
"pages": [
|
|
238
|
+
"/v7/features/managed-sandboxes",
|
|
239
|
+
"/v7/features/sandbox-customization",
|
|
240
|
+
"/v7/features/enterprise"
|
|
241
|
+
]
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
"group": "Test Observability",
|
|
245
|
+
"icon": "microscope",
|
|
246
|
+
"pages": [
|
|
247
|
+
"/v7/features/application-logs",
|
|
248
|
+
"/v7/features/browser-logs",
|
|
249
|
+
"/v7/features/network-monitoring",
|
|
250
|
+
"/v7/features/system-performance"
|
|
251
|
+
]
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
"group": "Performance & Reliability",
|
|
255
|
+
"icon": "gauge-high",
|
|
256
|
+
"pages": [
|
|
257
|
+
"/v7/features/fast",
|
|
258
|
+
"/v7/features/stable",
|
|
259
|
+
"/v7/features/parallel-execution",
|
|
260
|
+
"/v7/features/cache-management"
|
|
261
|
+
]
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"group": "Testing at Scale",
|
|
265
|
+
"icon": "arrow-up-right-dots",
|
|
266
|
+
"pages": [
|
|
267
|
+
"/v7/features/data-driven-testing",
|
|
268
|
+
"/v7/features/continuous-testing"
|
|
269
|
+
]
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"group": "Reports & Analytics",
|
|
273
|
+
"icon": "chart-line",
|
|
274
|
+
"pages": [
|
|
275
|
+
"/v7/features/test-reports",
|
|
276
|
+
"/v7/features/test-analytics",
|
|
277
|
+
"/v7/features/test-cases",
|
|
278
|
+
"/v7/features/test-replays"
|
|
279
|
+
]
|
|
280
|
+
}
|
|
281
|
+
]
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
"group": "Reference",
|
|
285
|
+
"icon": "book",
|
|
286
|
+
"pages": [
|
|
287
|
+
{
|
|
288
|
+
"group": "Actions",
|
|
289
|
+
"icon": "bolt",
|
|
290
|
+
"pages": [
|
|
291
|
+
"/v7/api/act",
|
|
292
|
+
"/v7/api/assert",
|
|
293
|
+
"/v7/api/assertions",
|
|
294
|
+
"/v7/api/click",
|
|
295
|
+
"/v7/api/doubleClick",
|
|
296
|
+
"/v7/api/exec",
|
|
297
|
+
"/v7/api/find",
|
|
298
|
+
"/v7/api/focusApplication",
|
|
299
|
+
"/v7/api/hover",
|
|
300
|
+
"/v7/api/mouseDown",
|
|
301
|
+
"/v7/api/mouseUp",
|
|
302
|
+
"/v7/api/pressKeys",
|
|
303
|
+
"/v7/api/rightClick",
|
|
304
|
+
"/v7/api/type",
|
|
305
|
+
"/v7/api/scroll"
|
|
306
|
+
]
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
"group": "Client SDK",
|
|
310
|
+
"icon": "code",
|
|
311
|
+
"pages": [
|
|
312
|
+
"/v7/api/client",
|
|
313
|
+
"/v7/api/elements",
|
|
314
|
+
"/v7/api/sandbox",
|
|
315
|
+
"/v7/api/dashcam"
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
}
|
|
322
|
+
|
|
271
323
|
]
|
|
272
|
-
}
|
|
273
|
-
]
|
|
274
324
|
},
|
|
275
325
|
"logo": {
|
|
276
326
|
"light": "/images/template/light.png",
|
|
@@ -307,15 +357,75 @@
|
|
|
307
357
|
"redirects": [
|
|
308
358
|
{
|
|
309
359
|
"source": "/guides/github-actions",
|
|
310
|
-
"destination": "/action/setup"
|
|
360
|
+
"destination": "/v6/action/setup"
|
|
311
361
|
},
|
|
312
362
|
{
|
|
313
363
|
"source": "/reference/test-steps",
|
|
314
|
-
"destination": "/features/selectorless"
|
|
364
|
+
"destination": "/v6/features/selectorless"
|
|
315
365
|
},
|
|
316
366
|
{
|
|
317
367
|
"source": "/guides/debugging-test-runs",
|
|
318
|
-
"destination": "/getting-started/editing"
|
|
368
|
+
"destination": "/v6/getting-started/editing"
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
"source": "/overview/:slug*",
|
|
372
|
+
"destination": "/v6/overview/:slug*"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
"source": "/features/:slug*",
|
|
376
|
+
"destination": "/v6/features/:slug*"
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
"source": "/getting-started/:slug*",
|
|
380
|
+
"destination": "/v6/getting-started/:slug*"
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
"source": "/cli/:slug*",
|
|
384
|
+
"destination": "/v6/cli/:slug*"
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
"source": "/interactive/:slug*",
|
|
388
|
+
"destination": "/v6/interactive/:slug*"
|
|
389
|
+
},
|
|
390
|
+
{
|
|
391
|
+
"source": "/guide/:slug*",
|
|
392
|
+
"destination": "/v6/guide/:slug*"
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
"source": "/action/:slug*",
|
|
396
|
+
"destination": "/v6/action/:slug*"
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
"source": "/account/:slug*",
|
|
400
|
+
"destination": "/v6/account/:slug*"
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"source": "/apps/:slug*",
|
|
404
|
+
"destination": "/v6/apps/:slug*"
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
"source": "/scenarios/:slug*",
|
|
408
|
+
"destination": "/v6/scenarios/:slug*"
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
"source": "/integrations/:slug*",
|
|
412
|
+
"destination": "/v6/integrations/:slug*"
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
"source": "/importing/:slug*",
|
|
416
|
+
"destination": "/v6/importing/:slug*"
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
"source": "/exporting/:slug*",
|
|
420
|
+
"destination": "/v6/exporting/:slug*"
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
"source": "/bugs/:slug*",
|
|
424
|
+
"destination": "/v6/bugs/:slug*"
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
"source": "/commands/:slug*",
|
|
428
|
+
"destination": "/v6/commands/:slug*"
|
|
319
429
|
}
|
|
320
430
|
],
|
|
321
431
|
"integrations": {
|
|
@@ -51,6 +51,30 @@ const button = await testdriver.find('submit button', {
|
|
|
51
51
|
});
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
+
**Use Case - Variables in Test Steps:**
|
|
55
|
+
|
|
56
|
+
Custom cache keys are especially useful when you use variables in your test steps, ensuring consistent caching regardless of the variable values:
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
// Without custom cache key - each variable value creates a new cache entry
|
|
60
|
+
const userName = 'john.doe@example.com';
|
|
61
|
+
await testdriver.find(`input field for ${userName}`); // Cache miss every time
|
|
62
|
+
|
|
63
|
+
// With custom cache key - all values share the same cache
|
|
64
|
+
const userName = 'john.doe@example.com';
|
|
65
|
+
await testdriver.find(`input field for ${userName}`, {
|
|
66
|
+
cacheKey: 'email-input-field' // Same cache key regardless of userName value
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Another example with dynamic data
|
|
70
|
+
const productId = '12345';
|
|
71
|
+
await testdriver.find(`product card for ${productId}`, {
|
|
72
|
+
cacheKey: 'product-card' // Reuses cache for any productId
|
|
73
|
+
});
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
This prevents cache pollution and improves cache hit rates when your prompts include dynamic values.
|
|
77
|
+
|
|
54
78
|
### Global Cache Key
|
|
55
79
|
|
|
56
80
|
Enable caching for all finds in your test:
|
|
@@ -207,16 +207,16 @@ await testdriver.focusApplication('Google Chrome');
|
|
|
207
207
|
```
|
|
208
208
|
</CodeGroup>
|
|
209
209
|
|
|
210
|
-
###
|
|
210
|
+
### Extract
|
|
211
211
|
|
|
212
212
|
<CodeGroup>
|
|
213
213
|
```yaml YAML
|
|
214
|
-
- command:
|
|
214
|
+
- command: extract
|
|
215
215
|
description: the order number
|
|
216
216
|
```
|
|
217
217
|
|
|
218
218
|
```javascript SDK
|
|
219
|
-
const orderNumber = await testdriver.
|
|
219
|
+
const orderNumber = await testdriver.extract('the order number');
|
|
220
220
|
```
|
|
221
221
|
</CodeGroup>
|
|
222
222
|
|
package/docs/v7/api/act.mdx
CHANGED
|
@@ -184,7 +184,7 @@ describe('E-commerce Flow with AI', () => {
|
|
|
184
184
|
const cartIcon = await testdriver.find('shopping cart icon');
|
|
185
185
|
await cartIcon.click();
|
|
186
186
|
|
|
187
|
-
const total = await testdriver.
|
|
187
|
+
const total = await testdriver.extract('the cart total amount');
|
|
188
188
|
console.log('Cart total:', total);
|
|
189
189
|
|
|
190
190
|
// Use AI for checkout flow
|
|
@@ -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
|
-
- [`
|
|
205
|
+
- [`extract()`](/v7/api/extract) - Extract information
|
package/docs/v7/api/assert.mdx
CHANGED
|
@@ -98,7 +98,7 @@ await testdriver.assert('the modal dialog is open');
|
|
|
98
98
|
expect(result).toBeTruthy();
|
|
99
99
|
|
|
100
100
|
// Extract for detailed comparison
|
|
101
|
-
const message = await testdriver.
|
|
101
|
+
const message = await testdriver.extract('the success message text');
|
|
102
102
|
expect(message).toContain('successfully');
|
|
103
103
|
```
|
|
104
104
|
</Check>
|
|
@@ -280,6 +280,6 @@ describe('Assertions', () => {
|
|
|
280
280
|
|
|
281
281
|
## Related Methods
|
|
282
282
|
|
|
283
|
-
- [`
|
|
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
|
-
###
|
|
80
|
+
### extract()
|
|
81
81
|
|
|
82
|
-
Extract
|
|
82
|
+
Extract information from the screen using AI.
|
|
83
83
|
|
|
84
84
|
```javascript
|
|
85
|
-
await testdriver.
|
|
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.
|
|
98
|
-
const total = await testdriver.
|
|
99
|
-
const errorMessage = await testdriver.
|
|
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.
|
|
103
|
-
const orderId = await testdriver.
|
|
104
|
-
const phoneNumber = await testdriver.
|
|
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.
|
|
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
|
-
//
|
|
118
|
-
const confirmationCode = await testdriver.
|
|
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.
|
|
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.
|
|
137
|
+
const status = await testdriver.extract('the order status');
|
|
138
138
|
|
|
139
139
|
if (status.includes('Pending')) {
|
|
140
140
|
// Wait for processing
|
|
@@ -264,9 +264,9 @@ describe('E2E Shopping Flow', () => {
|
|
|
264
264
|
// Verify cart updated
|
|
265
265
|
await testdriver.assert('cart badge shows 1 item');
|
|
266
266
|
|
|
267
|
-
//
|
|
268
|
-
const productName = await testdriver.
|
|
269
|
-
const price = await testdriver.
|
|
267
|
+
// Extract product details
|
|
268
|
+
const productName = await testdriver.extract('the name of the product just added');
|
|
269
|
+
const price = await testdriver.extract('the price of the product');
|
|
270
270
|
|
|
271
271
|
console.log(`Added ${productName} at ${price}`);
|
|
272
272
|
|
|
@@ -302,7 +302,7 @@ describe('E2E Shopping Flow', () => {
|
|
|
302
302
|
await testdriver.assert('success message is shown');
|
|
303
303
|
|
|
304
304
|
// Extract order number
|
|
305
|
-
const orderNumber = await testdriver.
|
|
305
|
+
const orderNumber = await testdriver.extract('the order number');
|
|
306
306
|
console.log('Order placed:', orderNumber);
|
|
307
307
|
expect(orderNumber).toBeTruthy();
|
|
308
308
|
});
|
|
@@ -377,10 +377,10 @@ describe('E2E Shopping Flow', () => {
|
|
|
377
377
|
</Accordion>
|
|
378
378
|
|
|
379
379
|
<Accordion title="Extract before comparing">
|
|
380
|
-
Use `
|
|
380
|
+
Use `extract()` to extract values for detailed comparisons:
|
|
381
381
|
|
|
382
382
|
```javascript
|
|
383
|
-
const actualTotal = await testdriver.
|
|
383
|
+
const actualTotal = await testdriver.extract('the cart total');
|
|
384
384
|
const expectedTotal = '$99.99';
|
|
385
385
|
|
|
386
386
|
expect(actualTotal).toBe(expectedTotal);
|
|
@@ -395,7 +395,7 @@ describe('E2E Shopping Flow', () => {
|
|
|
395
395
|
await testdriver.assert('success message is displayed');
|
|
396
396
|
|
|
397
397
|
// Extract and use traditional assertion for exact values
|
|
398
|
-
const message = await testdriver.
|
|
398
|
+
const message = await testdriver.extract('the success message text');
|
|
399
399
|
expect(message).toContain('successfully');
|
|
400
400
|
expect(message).toMatch(/order #\d+/i);
|
|
401
401
|
```
|
package/docs/v7/api/elements.mdx
CHANGED
|
@@ -418,6 +418,84 @@ if (element.confidence !== null) {
|
|
|
418
418
|
Properties marked with ⚠️ may be `null` depending on what the AI could detect from the screenshot.
|
|
419
419
|
</Note>
|
|
420
420
|
|
|
421
|
+
## JSON Serialization
|
|
422
|
+
|
|
423
|
+
Element objects can be safely serialized using `JSON.stringify()` for logging, debugging, and data storage. Circular references are automatically removed:
|
|
424
|
+
|
|
425
|
+
```javascript
|
|
426
|
+
const element = await testdriver.find('login button');
|
|
427
|
+
|
|
428
|
+
// Safe to stringify - no circular reference errors!
|
|
429
|
+
console.log(JSON.stringify(element, null, 2));
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
**Serialized output includes:**
|
|
433
|
+
|
|
434
|
+
```json
|
|
435
|
+
{
|
|
436
|
+
"description": "login button",
|
|
437
|
+
"coordinates": { "x": 100, "y": 200, "centerX": 150, "centerY": 225 },
|
|
438
|
+
"found": true,
|
|
439
|
+
"threshold": 0.01,
|
|
440
|
+
"x": 100,
|
|
441
|
+
"y": 200,
|
|
442
|
+
"cache": {
|
|
443
|
+
"hit": true,
|
|
444
|
+
"strategy": "pixel-diff",
|
|
445
|
+
"createdAt": "2025-12-09T10:30:00.000Z",
|
|
446
|
+
"diffPercent": 0.0023,
|
|
447
|
+
"imageUrl": "https://cache.testdriver.ai/..."
|
|
448
|
+
},
|
|
449
|
+
"similarity": 0.98,
|
|
450
|
+
"confidence": 0.95,
|
|
451
|
+
"selector": "button#login",
|
|
452
|
+
"aiResponse": "Found the blue login button in the center of the form..."
|
|
453
|
+
}
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
**Serialized properties:**
|
|
457
|
+
|
|
458
|
+
| Property | Type | Description |
|
|
459
|
+
|----------|------|-------------|
|
|
460
|
+
| `description` | string | Element search description |
|
|
461
|
+
| `coordinates` | object | Full coordinate object `{x, y, centerX, centerY}` |
|
|
462
|
+
| `found` | boolean | Whether element was located |
|
|
463
|
+
| `threshold` | number | Cache threshold used for this find |
|
|
464
|
+
| `x`, `y` | number | Top-left coordinates |
|
|
465
|
+
| `cache.hit` | boolean | Whether cache was used |
|
|
466
|
+
| `cache.strategy` | string | Cache strategy (e.g., "pixel-diff") |
|
|
467
|
+
| `cache.createdAt` | string | ISO timestamp when cache was created |
|
|
468
|
+
| `cache.diffPercent` | number | Pixel difference from cached image |
|
|
469
|
+
| `cache.imageUrl` | string | URL to cached screenshot |
|
|
470
|
+
| `similarity` | number | Similarity score (0-1) |
|
|
471
|
+
| `confidence` | number | AI confidence score (0-1) |
|
|
472
|
+
| `selector` | string | CSS/XPath selector if available |
|
|
473
|
+
| `aiResponse` | string | AI's explanation of what it found |
|
|
474
|
+
|
|
475
|
+
**Use cases:**
|
|
476
|
+
|
|
477
|
+
```javascript
|
|
478
|
+
// Debugging element detection
|
|
479
|
+
const element = await testdriver.find('submit button');
|
|
480
|
+
if (!element.found()) {
|
|
481
|
+
console.error('Element not found:', JSON.stringify(element, null, 2));
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// Logging cache performance
|
|
485
|
+
const data = JSON.parse(JSON.stringify(element));
|
|
486
|
+
if (data.cache.hit) {
|
|
487
|
+
console.log(`Cache hit! Diff: ${(data.cache.diffPercent * 100).toFixed(2)}%`);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// Sharing element data across processes
|
|
491
|
+
const elementData = JSON.stringify(element);
|
|
492
|
+
// Send to another process, log to file, etc.
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
<Tip>
|
|
496
|
+
Use JSON serialization when you need to log element data or when debugging why an element wasn't found. The serialized output excludes large binary data (screenshots) and circular references.
|
|
497
|
+
</Tip>
|
|
498
|
+
|
|
421
499
|
## Polling for Elements
|
|
422
500
|
|
|
423
501
|
Use polling to wait for elements that may not be immediately visible:
|