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.
Files changed (78) hide show
  1. package/.github/workflows/acceptance.yaml +81 -0
  2. package/.github/workflows/publish.yaml +44 -0
  3. package/.github/workflows/test-init.yml +145 -0
  4. package/agent/index.js +18 -19
  5. package/agent/lib/commander.js +2 -2
  6. package/agent/lib/commands.js +324 -124
  7. package/agent/lib/redraw.js +99 -39
  8. package/agent/lib/sandbox.js +98 -6
  9. package/agent/lib/sdk.js +25 -0
  10. package/agent/lib/system.js +2 -1
  11. package/agent/lib/validation.js +6 -6
  12. package/docs/docs.json +211 -101
  13. package/docs/snippets/tests/type-repeated-replay.mdx +1 -1
  14. package/docs/v7/_drafts/caching-selectors.mdx +24 -0
  15. package/docs/v7/_drafts/migration.mdx +3 -3
  16. package/docs/v7/api/act.mdx +2 -2
  17. package/docs/v7/api/assert.mdx +2 -2
  18. package/docs/v7/api/assertions.mdx +21 -21
  19. package/docs/v7/api/elements.mdx +78 -0
  20. package/docs/v7/api/find.mdx +38 -0
  21. package/docs/v7/api/focusApplication.mdx +2 -2
  22. package/docs/v7/api/hover.mdx +2 -2
  23. package/docs/v7/features/ai-native.mdx +57 -71
  24. package/docs/v7/features/application-logs.mdx +353 -0
  25. package/docs/v7/features/browser-logs.mdx +414 -0
  26. package/docs/v7/features/cache-management.mdx +402 -0
  27. package/docs/v7/features/continuous-testing.mdx +346 -0
  28. package/docs/v7/features/coverage.mdx +508 -0
  29. package/docs/v7/features/data-driven-testing.mdx +441 -0
  30. package/docs/v7/features/easy-to-write.mdx +2 -73
  31. package/docs/v7/features/enterprise.mdx +155 -39
  32. package/docs/v7/features/fast.mdx +63 -81
  33. package/docs/v7/features/managed-sandboxes.mdx +384 -0
  34. package/docs/v7/features/network-monitoring.mdx +568 -0
  35. package/docs/v7/features/observable.mdx +3 -22
  36. package/docs/v7/features/parallel-execution.mdx +381 -0
  37. package/docs/v7/features/powerful.mdx +1 -1
  38. package/docs/v7/features/reports.mdx +414 -0
  39. package/docs/v7/features/sandbox-customization.mdx +229 -0
  40. package/docs/v7/features/scalable.mdx +217 -2
  41. package/docs/v7/features/stable.mdx +106 -147
  42. package/docs/v7/features/system-performance.mdx +616 -0
  43. package/docs/v7/features/test-analytics.mdx +373 -0
  44. package/docs/v7/features/test-cases.mdx +393 -0
  45. package/docs/v7/features/test-replays.mdx +408 -0
  46. package/docs/v7/features/test-reports.mdx +308 -0
  47. package/docs/v7/getting-started/{running-and-debugging.mdx → debugging-tests.mdx} +12 -142
  48. package/docs/v7/getting-started/quickstart.mdx +22 -305
  49. package/docs/v7/getting-started/running-tests.mdx +173 -0
  50. package/docs/v7/overview/readme.mdx +1 -1
  51. package/docs/v7/overview/what-is-testdriver.mdx +2 -14
  52. package/docs/v7/presets/chrome-extension.mdx +147 -122
  53. package/interfaces/cli/commands/init.js +78 -20
  54. package/interfaces/cli/lib/base.js +3 -2
  55. package/interfaces/logger.js +0 -2
  56. package/interfaces/shared-test-state.mjs +0 -5
  57. package/interfaces/vitest-plugin.mjs +69 -42
  58. package/lib/core/Dashcam.js +65 -66
  59. package/lib/vitest/hooks.mjs +42 -50
  60. package/manual/test-init-command.js +223 -0
  61. package/package.json +2 -2
  62. package/schema.json +5 -5
  63. package/sdk-log-formatter.js +351 -176
  64. package/sdk.d.ts +8 -8
  65. package/sdk.js +436 -121
  66. package/setup/aws/cloudformation.yaml +2 -2
  67. package/setup/aws/self-hosted.yml +1 -1
  68. package/test/testdriver/chrome-extension.test.mjs +55 -72
  69. package/test/testdriver/element-not-found.test.mjs +2 -1
  70. package/test/testdriver/hover-image.test.mjs +1 -1
  71. package/test/testdriver/hover-text-with-description.test.mjs +0 -3
  72. package/test/testdriver/scroll-until-text.test.mjs +10 -6
  73. package/test/testdriver/setup/lifecycleHelpers.mjs +19 -24
  74. package/test/testdriver/setup/testHelpers.mjs +18 -23
  75. package/vitest.config.mjs +3 -3
  76. package/.github/workflows/linux-tests.yml +0 -28
  77. package/docs/v7/getting-started/generating-tests.mdx +0 -525
  78. 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": "willow",
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 (YAML)",
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
- "version": "v7 (JS Beta)",
195
- "groups": [
196
- {
197
- "group": "Overview",
198
- "icon": "circle-info",
199
- "pages": [
200
- "/v7/overview/what-is-testdriver",
201
- {
202
- "group": "Features",
203
- "icon": "star",
204
- "pages": [
205
- "/v7/features/easy-to-write",
206
- "/v7/features/fast",
207
- "/v7/features/stable",
208
- "/v7/features/scalable",
209
- "/v7/features/ai-native",
210
- "/v7/features/powerful",
211
- "/v7/features/observable",
212
- "/v7/features/enterprise"
213
- ]
214
- }
215
- ]
216
- },
217
- {
218
- "group": "Getting Started",
219
- "icon": "rocket",
220
- "pages": [
221
- "/v7/getting-started/installation",
222
- "/v7/getting-started/writing-tests",
223
- "/v7/getting-started/generating-tests",
224
- "/v7/getting-started/running-and-debugging",
225
- "/v7/getting-started/setting-up-in-ci"
226
- ]
227
- },
228
- {
229
- "group": "Examples",
230
- "icon": "code",
231
- "pages": [
232
- "/v7/presets/chrome",
233
- "/v7/presets/chrome-extension",
234
- "/v7/presets/vscode",
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"
257
- ]
258
- },
259
- {
260
- "group": "API Reference",
261
- "icon": "book",
262
- "pages": [
263
- "/v7/api/client",
264
- "/v7/api/elements",
265
- "/v7/api/sandbox",
266
- "/v7/api/dashcam"
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": {
@@ -1,4 +1,4 @@
1
- <div className="replay-block">
1
+ st<div className="replay-block">
2
2
  <iframe
3
3
  src="https://app.dashcam.io/replay/683f9c23dc453b7caa0e0d0c?share=gIH46G6NMy3tV8V0KssxA&embed=true&timestamp=90000&playbackRate=5"
4
4
  width="1000"
@@ -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
- ### 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
 
@@ -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.remember('the cart total amount');
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
- - [`remember()`](/v7/api/remember) - Extract information
205
+ - [`extract()`](/v7/api/extract) - Extract information
@@ -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.remember('the success message text');
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
- - [`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
@@ -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
- // Remember product details
268
- const productName = await testdriver.remember('the name of the product just added');
269
- const price = await testdriver.remember('the price of the product');
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.remember('the order number');
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 `remember()` to extract values for detailed comparisons:
380
+ Use `extract()` to extract values for detailed comparisons:
381
381
 
382
382
  ```javascript
383
- const actualTotal = await testdriver.remember('the cart total');
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.remember('the success message text');
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
  ```
@@ -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: