testdriverai 7.0.0 → 7.1.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 (112) hide show
  1. package/AGENTS.md +550 -0
  2. package/CODEOWNERS +0 -1
  3. package/README.md +126 -0
  4. package/agent/index.js +43 -18
  5. package/agent/lib/commands.js +794 -135
  6. package/agent/lib/redraw.js +124 -39
  7. package/agent/lib/sandbox.js +10 -1
  8. package/agent/lib/sdk.js +21 -0
  9. package/docs/MIGRATION.md +425 -0
  10. package/docs/PRESETS.md +210 -0
  11. package/docs/docs.json +91 -37
  12. package/docs/guide/best-practices-polling.mdx +154 -0
  13. package/docs/v7/api/dashcam.mdx +497 -0
  14. package/docs/v7/api/doubleClick.mdx +102 -0
  15. package/docs/v7/api/mouseDown.mdx +161 -0
  16. package/docs/v7/api/mouseUp.mdx +164 -0
  17. package/docs/v7/api/rightClick.mdx +123 -0
  18. package/docs/v7/getting-started/configuration.mdx +380 -0
  19. package/docs/v7/getting-started/quickstart.mdx +273 -140
  20. package/docs/v7/guides/best-practices.mdx +486 -0
  21. package/docs/v7/guides/caching-ai.mdx +215 -0
  22. package/docs/v7/guides/caching-selectors.mdx +292 -0
  23. package/docs/v7/guides/caching.mdx +366 -0
  24. package/docs/v7/guides/ci-cd/azure.mdx +587 -0
  25. package/docs/v7/guides/ci-cd/circleci.mdx +523 -0
  26. package/docs/v7/guides/ci-cd/github-actions.mdx +457 -0
  27. package/docs/v7/guides/ci-cd/gitlab.mdx +498 -0
  28. package/docs/v7/guides/ci-cd/jenkins.mdx +664 -0
  29. package/docs/v7/guides/ci-cd/travis.mdx +438 -0
  30. package/docs/v7/guides/debugging.mdx +349 -0
  31. package/docs/v7/guides/faq.mdx +393 -0
  32. package/docs/v7/guides/performance.mdx +517 -0
  33. package/docs/v7/guides/troubleshooting.mdx +526 -0
  34. package/docs/v7/guides/vitest-plugin.mdx +477 -0
  35. package/docs/v7/guides/vitest.mdx +535 -0
  36. package/docs/v7/platforms/linux.mdx +308 -0
  37. package/docs/v7/platforms/macos.mdx +433 -0
  38. package/docs/v7/platforms/windows.mdx +430 -0
  39. package/docs/v7/presets/chrome-extension.mdx +223 -0
  40. package/docs/v7/presets/chrome.mdx +287 -0
  41. package/docs/v7/presets/electron.mdx +435 -0
  42. package/docs/v7/presets/vscode.mdx +398 -0
  43. package/docs/v7/presets/webapp.mdx +396 -0
  44. package/docs/v7/progressive-apis/CORE.md +459 -0
  45. package/docs/v7/progressive-apis/HOOKS.md +360 -0
  46. package/docs/v7/progressive-apis/PROGRESSIVE_DISCLOSURE.md +230 -0
  47. package/docs/v7/progressive-apis/PROVISION.md +266 -0
  48. package/interfaces/vitest-plugin.mjs +186 -100
  49. package/package.json +12 -1
  50. package/sdk.d.ts +335 -42
  51. package/sdk.js +756 -95
  52. package/src/core/Dashcam.js +469 -0
  53. package/src/core/index.d.ts +150 -0
  54. package/src/core/index.js +12 -0
  55. package/src/presets/index.mjs +331 -0
  56. package/src/vitest/extended.mjs +108 -0
  57. package/src/vitest/hooks.d.ts +119 -0
  58. package/src/vitest/hooks.mjs +298 -0
  59. package/src/vitest/index.mjs +64 -0
  60. package/src/vitest/lifecycle.mjs +277 -0
  61. package/src/vitest/utils.mjs +150 -0
  62. package/test/dashcam.test.js +137 -0
  63. package/testdriver/acceptance-sdk/assert.test.mjs +13 -31
  64. package/testdriver/acceptance-sdk/auto-cache-key-demo.test.mjs +56 -0
  65. package/testdriver/acceptance-sdk/chrome-extension.test.mjs +89 -0
  66. package/testdriver/acceptance-sdk/drag-and-drop.test.mjs +7 -19
  67. package/testdriver/acceptance-sdk/element-not-found.test.mjs +6 -19
  68. package/testdriver/acceptance-sdk/exec-js.test.mjs +6 -18
  69. package/testdriver/acceptance-sdk/exec-output.test.mjs +8 -20
  70. package/testdriver/acceptance-sdk/exec-pwsh.test.mjs +13 -25
  71. package/testdriver/acceptance-sdk/focus-window.test.mjs +8 -20
  72. package/testdriver/acceptance-sdk/formatted-logging.test.mjs +5 -20
  73. package/testdriver/acceptance-sdk/hooks-example.test.mjs +38 -0
  74. package/testdriver/acceptance-sdk/hover-image.test.mjs +10 -19
  75. package/testdriver/acceptance-sdk/hover-text-with-description.test.mjs +7 -19
  76. package/testdriver/acceptance-sdk/hover-text.test.mjs +5 -19
  77. package/testdriver/acceptance-sdk/match-image.test.mjs +7 -19
  78. package/testdriver/acceptance-sdk/presets-example.test.mjs +87 -0
  79. package/testdriver/acceptance-sdk/press-keys.test.mjs +5 -19
  80. package/testdriver/acceptance-sdk/prompt.test.mjs +6 -18
  81. package/testdriver/acceptance-sdk/scroll-keyboard.test.mjs +6 -20
  82. package/testdriver/acceptance-sdk/scroll-until-image.test.mjs +6 -18
  83. package/testdriver/acceptance-sdk/scroll-until-text.test.mjs +9 -23
  84. package/testdriver/acceptance-sdk/scroll.test.mjs +12 -21
  85. package/testdriver/acceptance-sdk/setup/testHelpers.mjs +124 -352
  86. package/testdriver/acceptance-sdk/sully-ai.test.mjs +234 -0
  87. package/testdriver/acceptance-sdk/test-console-logs.test.mjs +42 -0
  88. package/testdriver/acceptance-sdk/type.test.mjs +19 -58
  89. package/vitest.config.mjs +1 -0
  90. package/.vscode/mcp.json +0 -9
  91. package/MIGRATION.md +0 -389
  92. package/PLUGIN_MIGRATION.md +0 -222
  93. package/PROMPT_CACHE.md +0 -200
  94. package/SDK_LOGGING.md +0 -222
  95. package/SDK_MIGRATION.md +0 -474
  96. package/SDK_README.md +0 -1122
  97. package/debug-screenshot-1763401388589.png +0 -0
  98. package/examples/run-tests-with-recording.sh +0 -70
  99. package/examples/screenshot-example.js +0 -63
  100. package/examples/sdk-awesome-logs-demo.js +0 -177
  101. package/examples/sdk-cache-thresholds.js +0 -96
  102. package/examples/sdk-element-properties.js +0 -155
  103. package/examples/sdk-simple-example.js +0 -65
  104. package/examples/test-recording-example.test.js +0 -166
  105. package/mcp-server/AI_GUIDELINES.md +0 -57
  106. package/test-find-api.js +0 -73
  107. package/test-prompt-cache.js +0 -96
  108. package/test-sandbox-render.js +0 -28
  109. package/test-sdk-methods.js +0 -15
  110. package/test-sdk-refactor.js +0 -53
  111. package/test-stack-trace.mjs +0 -57
  112. package/testdriver/acceptance-sdk/setup/lifecycleHelpers.mjs +0 -239
@@ -0,0 +1,497 @@
1
+ ---
2
+ title: "Dashcam"
3
+ sidebarTitle: "Dashcam"
4
+ description: "Record test execution with video and logs"
5
+ icon: "video"
6
+ ---
7
+
8
+ ## Overview
9
+
10
+ Dashcam provides automatic video recording and log aggregation for your tests. It captures screen recordings, application logs, and test execution details that can be reviewed later.
11
+
12
+ ## Basic Usage
13
+
14
+ ### With Presets
15
+
16
+ Most presets automatically include Dashcam:
17
+
18
+ ```javascript
19
+ import { test } from 'vitest';
20
+ import { chrome } from 'testdriverai/presets';
21
+
22
+ test('my test', async (context) => {
23
+ const { testdriver, dashcam } = await chrome(context, {
24
+ url: 'https://example.com'
25
+ });
26
+
27
+ // Test executes with recording automatically
28
+ await testdriver.find('login button').then(el => el.click());
29
+
30
+ // Dashcam URL available after test
31
+ console.log('Replay:', dashcam.url);
32
+ });
33
+ ```
34
+
35
+ ### Manual Setup
36
+
37
+ For more control, create a Dashcam instance directly:
38
+
39
+ ```javascript
40
+ import TestDriver from 'testdriverai';
41
+ import Dashcam from 'testdriverai/src/core/Dashcam.js';
42
+
43
+ const client = await TestDriver.create({ os: 'linux' });
44
+ const dashcam = new Dashcam(client, {
45
+ apiKey: process.env.DASHCAM_API_KEY
46
+ });
47
+
48
+ await dashcam.auth();
49
+ await dashcam.start();
50
+
51
+ // Run your tests
52
+
53
+ const url = await dashcam.stop();
54
+ console.log('Replay URL:', url);
55
+ ```
56
+
57
+ ## Constructor
58
+
59
+ Create a new Dashcam instance:
60
+
61
+ ```javascript
62
+ new Dashcam(client, options)
63
+ ```
64
+
65
+ ### Parameters
66
+
67
+ <ParamField path="client" type="TestDriver" required>
68
+ TestDriver client instance
69
+ </ParamField>
70
+
71
+ <ParamField path="options" type="object">
72
+ Configuration options
73
+
74
+ <Expandable title="options properties">
75
+ <ParamField path="apiKey" type="string" default="4e93d8bf-3886-4d26-a144-116c4063522d">
76
+ Dashcam API key for authentication
77
+ </ParamField>
78
+
79
+ <ParamField path="autoStart" type="boolean" default={false}>
80
+ Automatically start recording after authentication
81
+ </ParamField>
82
+
83
+ <ParamField path="logs" type="array" default={[]}>
84
+ Log configurations to add automatically
85
+ </ParamField>
86
+ </Expandable>
87
+ </ParamField>
88
+
89
+ ## Methods
90
+
91
+ ### auth()
92
+
93
+ Authenticate with Dashcam service:
94
+
95
+ ```javascript
96
+ await dashcam.auth(apiKey)
97
+ ```
98
+
99
+ <ParamField path="apiKey" type="string" optional>
100
+ Override the API key set in constructor
101
+ </ParamField>
102
+
103
+ **Returns:** `Promise<void>`
104
+
105
+ **Example:**
106
+ ```javascript
107
+ await dashcam.auth('your-api-key');
108
+ ```
109
+
110
+ ### start()
111
+
112
+ Start recording:
113
+
114
+ ```javascript
115
+ await dashcam.start()
116
+ ```
117
+
118
+ **Returns:** `Promise<void>`
119
+
120
+ **Example:**
121
+ ```javascript
122
+ await dashcam.start();
123
+ console.log('Recording started');
124
+ ```
125
+
126
+ ### stop()
127
+
128
+ Stop recording and retrieve replay URL:
129
+
130
+ ```javascript
131
+ await dashcam.stop()
132
+ ```
133
+
134
+ **Returns:** `Promise<string|null>` - Replay URL if available
135
+
136
+ **Example:**
137
+ ```javascript
138
+ const url = await dashcam.stop();
139
+ if (url) {
140
+ console.log('Watch replay:', url);
141
+ } else {
142
+ console.log('No replay URL available');
143
+ }
144
+ ```
145
+
146
+ ### addFileLog()
147
+
148
+ Track a log file in the recording:
149
+
150
+ ```javascript
151
+ await dashcam.addFileLog(path, name)
152
+ ```
153
+
154
+ <ParamField path="path" type="string" required>
155
+ Path to the log file
156
+ </ParamField>
157
+
158
+ <ParamField path="name" type="string" required>
159
+ Display name for the log in Dashcam
160
+ </ParamField>
161
+
162
+ **Returns:** `Promise<void>`
163
+
164
+ **Example:**
165
+ ```javascript
166
+ // Linux/Mac
167
+ await dashcam.addFileLog('/tmp/app.log', 'Application Log');
168
+
169
+ // Windows
170
+ await dashcam.addFileLog('C:\\logs\\app.log', 'Application Log');
171
+ ```
172
+
173
+ ### addApplicationLog()
174
+
175
+ Track application-specific logs:
176
+
177
+ ```javascript
178
+ await dashcam.addApplicationLog(application, name)
179
+ ```
180
+
181
+ <ParamField path="application" type="string" required>
182
+ Application name to track
183
+ </ParamField>
184
+
185
+ <ParamField path="name" type="string" required>
186
+ Display name for the log
187
+ </ParamField>
188
+
189
+ **Returns:** `Promise<void>`
190
+
191
+ **Example:**
192
+ ```javascript
193
+ await dashcam.addApplicationLog('Google Chrome', 'Browser Logs');
194
+ ```
195
+
196
+ ### addLog()
197
+
198
+ Generic method to add any type of log:
199
+
200
+ ```javascript
201
+ await dashcam.addLog(config)
202
+ ```
203
+
204
+ <ParamField path="config" type="object" required>
205
+ Log configuration
206
+
207
+ <Expandable title="config properties">
208
+ <ParamField path="name" type="string" required>
209
+ Display name for the log
210
+ </ParamField>
211
+
212
+ <ParamField path="type" type="string" required>
213
+ Log type: `'file'`, `'stdout'`, or `'application'`
214
+ </ParamField>
215
+
216
+ <ParamField path="path" type="string">
217
+ File path (required for type='file')
218
+ </ParamField>
219
+
220
+ <ParamField path="application" type="string">
221
+ Application name (required for type='application')
222
+ </ParamField>
223
+ </Expandable>
224
+ </ParamField>
225
+
226
+ **Returns:** `Promise<void>`
227
+
228
+ **Example:**
229
+ ```javascript
230
+ await dashcam.addLog({
231
+ name: 'Test Output',
232
+ type: 'file',
233
+ path: '/tmp/test.log'
234
+ });
235
+
236
+ await dashcam.addLog({
237
+ name: 'Chrome Logs',
238
+ type: 'application',
239
+ application: 'Google Chrome'
240
+ });
241
+ ```
242
+
243
+ ### isRecording()
244
+
245
+ Check if currently recording:
246
+
247
+ ```javascript
248
+ await dashcam.isRecording()
249
+ ```
250
+
251
+ **Returns:** `Promise<boolean>` - True if recording is active
252
+
253
+ **Example:**
254
+ ```javascript
255
+ if (await dashcam.isRecording()) {
256
+ console.log('Recording in progress');
257
+ }
258
+ ```
259
+
260
+ ## Properties
261
+
262
+ ### recording
263
+
264
+ Current recording state:
265
+
266
+ ```javascript
267
+ dashcam.recording // boolean
268
+ ```
269
+
270
+ ### apiKey
271
+
272
+ Configured API key:
273
+
274
+ ```javascript
275
+ dashcam.apiKey // string
276
+ ```
277
+
278
+ ### client
279
+
280
+ Associated TestDriver client:
281
+
282
+ ```javascript
283
+ dashcam.client // TestDriver instance
284
+ ```
285
+
286
+ ## Complete Examples
287
+
288
+ ### Basic Recording
289
+
290
+ ```javascript
291
+ import { test } from 'vitest';
292
+ import TestDriver from 'testdriverai';
293
+ import Dashcam from 'testdriverai/src/core/Dashcam.js';
294
+
295
+ test('record test execution', async () => {
296
+ const client = await TestDriver.create({ os: 'linux' });
297
+ const dashcam = new Dashcam(client);
298
+
299
+ await dashcam.auth();
300
+ await dashcam.start();
301
+
302
+ // Run your test
303
+ await client.find('button').then(el => el.click());
304
+
305
+ const url = await dashcam.stop();
306
+ console.log('Replay:', url);
307
+
308
+ await client.cleanup();
309
+ });
310
+ ```
311
+
312
+ ### With Log Tracking
313
+
314
+ ```javascript
315
+ test('record with logs', async () => {
316
+ const client = await TestDriver.create({ os: 'linux' });
317
+ const dashcam = new Dashcam(client);
318
+
319
+ await dashcam.auth();
320
+
321
+ // Add log files before starting
322
+ await dashcam.addFileLog('/tmp/testdriver.log', 'TestDriver Log');
323
+ await dashcam.addFileLog('/tmp/app.log', 'Application Log');
324
+
325
+ await dashcam.start();
326
+
327
+ // Test execution
328
+ await client.find('login button').then(el => el.click());
329
+
330
+ const url = await dashcam.stop();
331
+ console.log('Replay with logs:', url);
332
+
333
+ await client.cleanup();
334
+ });
335
+ ```
336
+
337
+ ### Auto-start Configuration
338
+
339
+ ```javascript
340
+ test('auto-start recording', async () => {
341
+ const client = await TestDriver.create({ os: 'linux' });
342
+ const dashcam = new Dashcam(client, {
343
+ autoStart: true,
344
+ logs: [
345
+ {
346
+ name: 'App Log',
347
+ type: 'file',
348
+ path: '/tmp/app.log'
349
+ }
350
+ ]
351
+ });
352
+
353
+ await dashcam.auth(); // Automatically starts recording
354
+
355
+ // Test execution
356
+ await client.find('submit button').then(el => el.click());
357
+
358
+ const url = await dashcam.stop();
359
+ console.log('Replay:', url);
360
+
361
+ await client.cleanup();
362
+ });
363
+ ```
364
+
365
+ ### Using with Presets
366
+
367
+ ```javascript
368
+ import { chrome } from 'testdriverai/presets';
369
+
370
+ test('preset with dashcam', async (context) => {
371
+ const { testdriver, dashcam } = await chrome(context, {
372
+ url: 'https://example.com',
373
+ dashcam: true // Enabled by default
374
+ });
375
+
376
+ // Test runs with automatic recording
377
+ await testdriver.find('button').then(el => el.click());
378
+
379
+ // URL automatically available
380
+ console.log('Replay:', dashcam.url);
381
+ });
382
+ ```
383
+
384
+ ### Disabling Dashcam in Presets
385
+
386
+ ```javascript
387
+ test('without dashcam', async (context) => {
388
+ const { testdriver } = await chrome(context, {
389
+ url: 'https://example.com',
390
+ dashcam: false // Disable recording
391
+ });
392
+
393
+ // Test runs without recording (faster)
394
+ await testdriver.find('button').then(el => el.click());
395
+ });
396
+ ```
397
+
398
+ ## Platform Differences
399
+
400
+ ### Windows
401
+
402
+ On Windows, Dashcam uses PowerShell commands and installs via npm:
403
+
404
+ ```javascript
405
+ // Windows-specific paths
406
+ await dashcam.addFileLog(
407
+ 'C:\\Users\\testdriver\\Documents\\testdriver.log',
408
+ 'TestDriver Log'
409
+ );
410
+ ```
411
+
412
+ ### Linux/Mac
413
+
414
+ On Linux/Mac, Dashcam uses shell commands:
415
+
416
+ ```javascript
417
+ // Unix-specific paths
418
+ await dashcam.addFileLog('/tmp/testdriver.log', 'TestDriver Log');
419
+ ```
420
+
421
+ ## Troubleshooting
422
+
423
+ ### No Replay URL Returned
424
+
425
+ If `stop()` returns `null`:
426
+
427
+ 1. Check that recording was started with `start()`
428
+ 2. Verify authentication succeeded
429
+ 3. Ensure dashcam CLI is installed in the environment
430
+ 4. Check console output for error messages
431
+
432
+ ### Recording Not Starting
433
+
434
+ If recording doesn't start:
435
+
436
+ 1. Verify API key is correct
437
+ 2. Check `auth()` completed successfully
438
+ 3. Ensure dashcam CLI is installed globally
439
+ 4. Check `isRecording()` to verify state
440
+
441
+ ### Logs Not Appearing
442
+
443
+ If logs aren't visible in replay:
444
+
445
+ 1. Add logs before calling `start()`
446
+ 2. Verify log file paths are correct
447
+ 3. Ensure log files exist and are accessible
448
+ 4. Check file permissions
449
+
450
+ ## Best Practices
451
+
452
+ <AccordionGroup>
453
+ <Accordion title="Always authenticate before starting">
454
+ ```javascript
455
+ await dashcam.auth();
456
+ await dashcam.start();
457
+ ```
458
+ </Accordion>
459
+
460
+ <Accordion title="Add logs before starting recording">
461
+ ```javascript
462
+ await dashcam.addFileLog('/tmp/app.log', 'App Log');
463
+ await dashcam.start();
464
+ ```
465
+ </Accordion>
466
+
467
+ <Accordion title="Always stop recording">
468
+ Use try/finally to ensure recording stops:
469
+
470
+ ```javascript
471
+ try {
472
+ await dashcam.start();
473
+ // Test code
474
+ } finally {
475
+ await dashcam.stop();
476
+ }
477
+ ```
478
+ </Accordion>
479
+
480
+ <Accordion title="Check for replay URL">
481
+ ```javascript
482
+ const url = await dashcam.stop();
483
+ if (url) {
484
+ console.log('Replay:', url);
485
+ } else {
486
+ console.warn('No replay URL available');
487
+ }
488
+ ```
489
+ </Accordion>
490
+ </AccordionGroup>
491
+
492
+ ## See Also
493
+
494
+ - [Chrome Preset](/v7/presets/chrome) - Automatic Dashcam setup for web apps
495
+ - [VS Code Extensions](/v7/presets/vscode) - Dashcam with VS Code testing
496
+ - [Desktop Apps](/v7/presets/electron) - Dashcam with Electron apps
497
+ - [Lifecycle Helpers](/v7/guides/lifecycle) - Prerun/postrun with Dashcam
@@ -0,0 +1,102 @@
1
+ ---
2
+ title: "doubleClick"
3
+ description: "Perform a double-click action on an element or at specific coordinates"
4
+ icon: "hand-pointer"
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ The `doubleClick()` method performs a double-click action on an element. You can either call it on an [`Element`](/v7/core-concepts/elements) instance or use it directly with a selector.
10
+
11
+ ## Syntax
12
+
13
+ ```javascript
14
+ // Double-click on an element
15
+ await element.doubleClick();
16
+
17
+ // Double-click using a selector
18
+ await ai.doubleClick('selector');
19
+ ```
20
+
21
+ ## Parameters
22
+
23
+ When called on an `Element`, no parameters are required.
24
+
25
+ When called directly on the AI client:
26
+
27
+ | Parameter | Type | Description |
28
+ |-----------|------|-------------|
29
+ | `selector` | `string` | The selector describing the element to double-click |
30
+
31
+ ## Returns
32
+
33
+ Returns a `Promise<void>` that resolves when the double-click action completes.
34
+
35
+ ## Examples
36
+
37
+ ### Double-Click on Found Element
38
+
39
+ ```javascript
40
+ const fileItem = await ai.find('README.md file');
41
+ await fileItem.doubleClick();
42
+ ```
43
+
44
+ ### Direct Double-Click with Selector
45
+
46
+ ```javascript
47
+ await ai.doubleClick('README.md in the file list');
48
+ ```
49
+
50
+ ### Opening Files in VS Code
51
+
52
+ ```javascript
53
+ import { test } from 'vitest';
54
+ import { vscode } from '@testdriver/sdk';
55
+
56
+ test('opens a file by double-clicking', async () => {
57
+ const { ai } = await vscode();
58
+
59
+ // Double-click to open a file in the explorer
60
+ await ai.doubleClick('package.json in the file explorer');
61
+
62
+ // Verify the file opened
63
+ const editor = await ai.find('text editor showing package.json');
64
+ expect(editor).toBeTruthy();
65
+ });
66
+ ```
67
+
68
+ ### Opening Folders in File Manager
69
+
70
+ ```javascript
71
+ import { test } from 'vitest';
72
+ import { chrome } from '@testdriver/sdk';
73
+
74
+ test('navigates folders in Google Drive', async () => {
75
+ const { ai } = await chrome('https://drive.google.com');
76
+
77
+ // Double-click to open a folder
78
+ await ai.doubleClick('Documents folder');
79
+
80
+ // Wait for folder to open
81
+ await ai.find('breadcrumb showing Documents');
82
+ });
83
+ ```
84
+
85
+ ### Selecting Text with Double-Click
86
+
87
+ ```javascript
88
+ // Double-click to select a word
89
+ await ai.doubleClick('word "TestDriver" in the paragraph');
90
+
91
+ // Verify selection
92
+ const selectedText = await ai.exec('window.getSelection().toString()');
93
+ expect(selectedText).toBe('TestDriver');
94
+ ```
95
+
96
+ ## Related Methods
97
+
98
+ - [`click()`](/v7/api/click) - Single click on an element
99
+ - [`rightClick()`](/v7/api/rightClick) - Right-click to open context menu
100
+ - [`mouseDown()`](/v7/api/mouseDown) - Press mouse button without releasing
101
+ - [`mouseUp()`](/v7/api/mouseUp) - Release mouse button
102
+ - [`hover()`](/v7/api/hover) - Move mouse over element without clicking