@testdriverai/agent 7.11.229-canary → 7.11.232-test

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 (100) hide show
  1. package/docs/_data/examples-manifest.json +16 -16
  2. package/docs/_scripts/generate-examples.js +38 -45
  3. package/docs/agent.mdx +174 -93
  4. package/docs/assert.mdx +73 -269
  5. package/docs/aws-setup.mdx +196 -314
  6. package/docs/bring-your-own-machine.mdx +139 -206
  7. package/docs/cache.mdx +144 -151
  8. package/docs/caching.mdx +99 -89
  9. package/docs/captcha.mdx +126 -111
  10. package/docs/ci-cd.mdx +154 -543
  11. package/docs/click.mdx +74 -229
  12. package/docs/client.mdx +177 -231
  13. package/docs/copilot/auto-healing.mdx +86 -370
  14. package/docs/copilot/running-tests.mdx +121 -930
  15. package/docs/customizing-devices.mdx +74 -251
  16. package/docs/dashcam.mdx +72 -405
  17. package/docs/debugging-with-screenshots.mdx +105 -371
  18. package/docs/docs.json +79 -22
  19. package/docs/double-click.mdx +48 -68
  20. package/docs/errors.mdx +102 -176
  21. package/docs/events.mdx +170 -327
  22. package/docs/examples/{hover-text-with-description.mdx → add-to-cart.mdx} +30 -35
  23. package/docs/examples/assert.mdx +28 -41
  24. package/docs/examples/click-button.mdx +71 -0
  25. package/docs/examples/{hover-image.mdx → click-icon.mdx} +32 -28
  26. package/docs/examples/element-not-found.mdx +27 -26
  27. package/docs/examples/findall-coffee-icons.mdx +28 -32
  28. package/docs/examples/installer.mdx +61 -25
  29. package/docs/examples/launch-vscode-linux.mdx +28 -51
  30. package/docs/examples/navigate-and-scroll.mdx +82 -0
  31. package/docs/examples/parse.mdx +24 -28
  32. package/docs/examples/press-keys.mdx +28 -25
  33. package/docs/examples/scroll.mdx +25 -27
  34. package/docs/examples/type.mdx +26 -21
  35. package/docs/exec.mdx +92 -256
  36. package/docs/extract.mdx +69 -170
  37. package/docs/find.mdx +140 -736
  38. package/docs/focus-application.mdx +69 -246
  39. package/docs/generating-tests.mdx +103 -137
  40. package/docs/guide/best-practices-polling.mdx +65 -141
  41. package/docs/hosted.mdx +80 -144
  42. package/docs/hover.mdx +62 -235
  43. package/docs/locating-elements.mdx +74 -369
  44. package/docs/making-assertions.mdx +116 -74
  45. package/docs/mcp-setup.mdx +80 -63
  46. package/docs/mouse-down.mdx +48 -125
  47. package/docs/mouse-up.mdx +47 -129
  48. package/docs/options.mdx +75 -48
  49. package/docs/parse.mdx +95 -184
  50. package/docs/performing-actions.mdx +104 -94
  51. package/docs/press-keys.mdx +59 -299
  52. package/docs/provision.mdx +137 -182
  53. package/docs/quickstart-claude.mdx +85 -0
  54. package/docs/quickstart-github.mdx +62 -32
  55. package/docs/quickstart-manual.mdx +60 -33
  56. package/docs/quickstart-vscode.mdx +97 -0
  57. package/docs/quickstart.mdx +26 -9
  58. package/docs/redraw.mdx +101 -165
  59. package/docs/reusable-code.mdx +97 -200
  60. package/docs/right-click.mdx +47 -89
  61. package/docs/screenshot.mdx +47 -201
  62. package/docs/screenshots.mdx +87 -137
  63. package/docs/scroll.mdx +71 -154
  64. package/docs/secrets.mdx +96 -91
  65. package/docs/self-hosted.mdx +83 -142
  66. package/docs/snippets/enterprise.mdx +15 -0
  67. package/docs/styles.css +68 -0
  68. package/docs/test-results-json.mdx +167 -204
  69. package/docs/type.mdx +56 -304
  70. package/docs/variables.mdx +121 -81
  71. package/docs/wait.mdx +33 -31
  72. package/docs/what-is-testdriver.mdx +80 -32
  73. package/examples/{hover-text-with-description.test.mjs → add-to-cart.test.mjs} +4 -5
  74. package/examples/assert.test.mjs +3 -20
  75. package/examples/{hover-text.test.mjs → click-button.test.mjs} +3 -4
  76. package/examples/{hover-image.test.mjs → click-icon.test.mjs} +5 -6
  77. package/examples/element-not-found.test.mjs +3 -5
  78. package/examples/findall-coffee-icons.test.mjs +3 -3
  79. package/examples/installer.test.mjs +1 -1
  80. package/examples/launch-vscode-linux.test.mjs +6 -33
  81. package/examples/{scroll-keyboard.test.mjs → navigate-and-scroll.test.mjs} +4 -5
  82. package/examples/parse.test.mjs +1 -2
  83. package/examples/press-keys.test.mjs +4 -4
  84. package/examples/scroll.test.mjs +3 -6
  85. package/examples/type.test.mjs +5 -2
  86. package/lib/provision.js +6 -2
  87. package/package.json +1 -1
  88. package/sdk.d.ts +4 -2
  89. package/sdk.js +1 -1
  90. package/docs/claude-mcp-plugin.mdx +0 -244
  91. package/docs/elements.mdx +0 -606
  92. package/docs/examples/ai.mdx +0 -78
  93. package/docs/examples/captcha-api.mdx +0 -92
  94. package/docs/examples/chrome-extension.mdx +0 -156
  95. package/docs/examples/drag-and-drop.mdx +0 -100
  96. package/docs/examples/formatted-logging.mdx +0 -74
  97. package/docs/examples/hover-text.mdx +0 -70
  98. package/docs/examples/match-image.mdx +0 -97
  99. package/docs/examples/scroll-keyboard.mdx +0 -80
  100. package/docs/examples/scroll-until-text.mdx +0 -109
@@ -1,149 +1,159 @@
1
1
  ---
2
- title: "Self-Healing"
3
- sidebarTitle: "Self-Healing"
4
- description: "Perform actions and handle dynamic, async UI so tests adapt to change"
5
- icon: "arrows-rotate"
2
+ title: "Perform actions"
3
+ sidebarTitle: "Performing actions"
4
+ description: "Click, type, scroll, and read the screen, and wait for a changing UI."
5
+ icon: "arrow-pointer"
6
6
  ---
7
7
 
8
- Real apps move, load, and change. Adapt your tests to these conditions.
8
+ A TestDriver test acts on the screen the way a person does, with the real mouse and keyboard.
9
9
 
10
- You [generated](/generating-tests) and [learned](/caching) your tests. Then you got them to [run](/copilot/running-tests). Now the challenge is the real world: buttons show after a spinner, pages navigate, animations play, and content comes in over the network. To keep the tests reliable, do the correct actions and control the timing. Then your tests adapt to the true behavior of the UI. They do not break.
10
+ - Find an element by describing it.
11
+ - Click it, type into it, or scroll past it.
12
+ - Wait for a UI that loads, animates, or changes as you use it.
11
13
 
12
- ## Performing Actions
14
+ ## Find an element, then act on it
13
15
 
14
- TestDriver gives you many actions. You can [click](/click), [type](/type), [hover](/hover), and [scroll](/scroll). For a full list, read the [API Reference](/click).
16
+ `find()` returns an element, and the element has methods for mouse actions:
15
17
 
16
18
  ```javascript
17
- // Clicking
18
- await testdriver.find('submit button').click();
19
- await testdriver.find('file item').doubleClick();
20
- await testdriver.find('text area').rightClick();
21
-
22
- // Typing
23
- await testdriver.find('email input').type('user@example.com');
24
- await testdriver.find('password input').type('secret', { secret: true });
19
+ const submitButton = await testdriver.find('Submit button');
20
+ await submitButton.click();
21
+ ```
25
22
 
26
- // Keyboard shortcuts
27
- await testdriver.pressKeys(['enter']);
28
- await testdriver.pressKeys(['ctrl', 'c']);
23
+ You can also chain the action straight onto `find()`:
29
24
 
30
- // Hovering
31
- await testdriver.find('dropdown menu').hover();
25
+ ```javascript
26
+ await testdriver.find('Submit button').click();
27
+ ```
32
28
 
33
- // Scrolling
34
- await testdriver.scroll('down', 500);
29
+ | Method | What it does |
30
+ | --- | --- |
31
+ | [`click()`](/click) | Left-clicks the element |
32
+ | [`doubleClick()`](/double-click) | Double-clicks the element |
33
+ | [`rightClick()`](/right-click) | Right-clicks the element, for example to open a context menu |
34
+ | [`hover()`](/hover) | Moves the mouse over the element without clicking |
35
+ | [`mouseDown()`](/mouse-down) and [`mouseUp()`](/mouse-up) | Press and release the mouse button separately, for dragging |
35
36
 
36
- // Waiting
37
- await testdriver.wait(2000); // Wait 2 seconds for animation/state change
37
+ If `find()` can't locate the element, it returns an element whose `found()` is `false`. Calling one of these methods on it throws an `ElementNotFoundError`.
38
38
 
39
- // Extracting information from screen
40
- const price = await testdriver.extract('the total price');
41
- const orderNumber = await testdriver.extract('the order confirmation number');
42
- ```
39
+ See [Locate elements](/locating-elements) to write descriptions that find the right element.
43
40
 
44
- ### Chaining Actions
41
+ ## Type text
45
42
 
46
- TestDriver lets you chain methods for cleaner code:
43
+ [`type()`](/type) types into whatever has keyboard focus, so click the field first:
47
44
 
48
45
  ```javascript
49
- // Chain find() with actions
50
- const button = await testdriver.find('submit button').click();
46
+ const emailField = await testdriver.find('Email input field');
47
+ await emailField.click();
48
+ await testdriver.type('user@example.com');
51
49
  ```
52
50
 
53
- Or keep the element reference for later use:
51
+ Pass `secret: true` for passwords and other sensitive values. TestDriver then doesn't log or store the text:
54
52
 
55
53
  ```javascript
56
- const button = await testdriver.find('submit button');
57
- await button.click();
54
+ await testdriver.find('Password input field').click();
55
+ await testdriver.type(process.env.TD_PASSWORD, { secret: true });
58
56
  ```
59
57
 
60
- ## Waiting for Dynamic Content
58
+ See [Secrets](/secrets) for keeping credentials out of your test code.
61
59
 
62
- By default, `find()` polls automatically for a maximum of 10 seconds. It tries again each 5 seconds until it finds the element. Thus TestDriver finds most elements that show after short async operations. You do not need more configuration.
60
+ ## Press keys and shortcuts
63
61
 
64
- For longer operations, make the `timeout` larger:
62
+ [`pressKeys()`](/press-keys) takes an array of keys and presses them together:
65
63
 
66
64
  ```javascript
67
- // Default behavior - polls for up to 10 seconds automatically
68
- const element = await testdriver.find('Loading complete indicator');
69
- await element.click();
65
+ await testdriver.pressKeys(['enter']);
66
+ await testdriver.pressKeys(['ctrl', 'a']);
67
+ await testdriver.pressKeys(['ctrl', 'shift', 'i']);
68
+ ```
70
69
 
71
- // Wait up to 30 seconds for slower operations
72
- const element = await testdriver.find('Loading complete indicator', { timeout: 30000 });
73
- await element.click();
70
+ ## Scroll
74
71
 
75
- // Useful after actions that trigger loading states
76
- await testdriver.find('submit button').click();
77
- await testdriver.find('success message', { timeout: 15000 });
72
+ [`scroll()`](/scroll) scrolls the page up or down with the mouse wheel. The default `amount` is 300:
78
73
 
79
- // Disable polling for instant checks
80
- const toast = await testdriver.find('notification toast', { timeout: 0 });
74
+ ```javascript
75
+ await testdriver.scroll('down', { amount: 1000 });
81
76
  ```
82
77
 
83
- ### Flake Prevention
78
+ If scrolling doesn't move the page, click somewhere on it first. That gives the page focus instead of another window.
84
79
 
85
- TestDriver automatically waits for the screen and network to stabilize after each action using **redraw detection**. This prevents flaky tests caused by animations, loading states, or dynamic content updates.
80
+ ## Read values from the screen
86
81
 
87
- <Note>
88
- Redraw detection adds a small delay after each action but significantly reduces test flakiness.
89
- </Note>
90
-
91
- For example, when clicking a submit button that navigates to a new page:
82
+ [`extract()`](/extract) reads information off the screen in plain language and returns it as a string. Use it for values the test needs later:
92
83
 
93
84
  ```javascript
94
- // Click submit - TestDriver automatically waits for the new page to load
95
- await testdriver.find('submit button').click();
96
-
97
- // By the time this runs, the page has fully loaded and stabilized
98
- await testdriver.assert('dashboard is displayed');
99
- await testdriver.find('welcome message');
85
+ const orderNumber = await testdriver.extract('the order confirmation number');
86
+ console.log(orderNumber); // for example "A-10482"
100
87
  ```
101
88
 
102
- Without redraw detection, you'd need manual waits or retries to handle the page transition. TestDriver handles this automatically by detecting when the screen stops changing and network requests complete.
89
+ ## Wait for a changing UI
90
+
91
+ Most timing problems come from acting before the app is ready. Wait for what the next step depends on, not for a fixed time.
92
+
93
+ ### Wait for an element with find()
103
94
 
104
- You can disable redraw detection or customize its behavior:
95
+ `find()` already waits. It checks the screen every 5 seconds, for up to 10 seconds, until the element appears.
96
+
97
+ For something slower, such as a page that loads after a form submits, raise the `timeout`:
105
98
 
106
99
  ```javascript
107
- // Disable redraw detection for faster tests (less reliable)
108
- const testdriver = TestDriver(context, {
109
- redraw: false
110
- });
100
+ await testdriver.find('Submit button').click();
101
+
102
+ // Keep looking for up to 30 seconds while the next page loads
103
+ const confirmation = await testdriver.find('Order confirmation message', { timeout: 30000 });
111
104
  ```
112
105
 
113
- Here is an example of customizing redraw detection:
106
+ Set `timeout: 0` to check once without waiting. For example, check whether an optional banner is showing:
114
107
 
115
108
  ```javascript
116
- // Fine-tune redraw detection
117
- const testdriver = TestDriver(context, {
118
- redraw: {
119
- enabled: true,
120
- diffThreshold: 0.1, // Pixel difference threshold (0-1)
121
- screenRedraw: true, // Monitor screen changes
122
- networkMonitor: true, // Wait for network idle
123
- }
124
- });
109
+ const cookieBanner = await testdriver.find('cookie consent banner', { timeout: 0 });
110
+ if (cookieBanner.found()) {
111
+ await testdriver.find('Accept all cookies button').click();
112
+ }
125
113
  ```
126
114
 
127
- ### Simple Delays with `wait()`
115
+ ### Pause with wait()
128
116
 
129
- For simple pauses — waiting for animations, transitions, or state changes after an action — use `wait()`:
117
+ [`wait()`](/wait) pauses for a fixed number of milliseconds. Use it only when there's nothing specific to wait for, such as an animation:
130
118
 
131
119
  ```javascript
132
- // Wait for an animation to complete
133
- await testdriver.find('menu toggle').click();
134
- await testdriver.wait(2000);
135
-
136
- // Wait for a page transition to settle
137
- await testdriver.find('next page button').click();
120
+ await testdriver.find('Menu toggle').click();
138
121
  await testdriver.wait(1000);
139
122
  ```
140
123
 
141
- <Note>
142
- For waiting for specific **elements** to appear, prefer `find()` with a `timeout` option. Use `wait()` only for simple time-based pauses.
143
- </Note>
124
+ ### Wait for the screen to settle with redraw detection
125
+
126
+ For apps with a lot of animation or loading states, turn on redraw detection. After each action, TestDriver waits until the screen stops changing:
127
+
128
+ ```javascript
129
+ const testdriver = TestDriver(context, { redraw: true });
130
+ ```
131
+
132
+ Redraw detection is off by default because it adds a short delay after every action.
133
+
134
+ To also wait for network activity to finish, enable network monitoring:
144
135
 
145
- Once your tests can reliably act on a changing UI and [assert](/making-assertions) the results, the next step is figuring out what happened when something does go wrong.
136
+ ```javascript
137
+ const testdriver = TestDriver(context, {
138
+ redraw: { thresholds: { network: true } },
139
+ });
140
+ ```
146
141
 
147
- <Card title="Next: Debug" icon="bug" href="/debugging-with-screenshots">
148
- Use screenshots and run output to see exactly what your test saw and pinpoint failures.
149
- </Card>
142
+ See [Redraw](/redraw) for every option.
143
+
144
+ ## Next steps
145
+
146
+ <CardGroup cols={2}>
147
+ <Card title="Make assertions" icon="circle-check" href="/making-assertions">
148
+ Check the result of your actions in plain language.
149
+ </Card>
150
+ <Card title="Locate elements" icon="crosshairs" href="/locating-elements">
151
+ Write descriptions that find the right element.
152
+ </Card>
153
+ <Card title="Debug a failing test" icon="bug" href="/debugging-with-screenshots">
154
+ See what the screen showed at each step.
155
+ </Card>
156
+ <Card title="Prevent regressions" icon="shield-check" href="/copilot/auto-healing">
157
+ Catch regressions in CI and repair tests when your UI changes.
158
+ </Card>
159
+ </CardGroup>
@@ -1,349 +1,109 @@
1
1
  ---
2
2
  title: "pressKeys()"
3
3
  sidebarTitle: "pressKeys"
4
- description: "Press keyboard keys and shortcuts"
4
+ description: "Press a key or a keyboard shortcut, like Enter, Tab or Ctrl+A."
5
5
  icon: "keyboard"
6
6
  ---
7
7
 
8
- ## Overview
8
+ Press a single key or a keyboard shortcut in the sandbox:
9
9
 
10
- Push one or more keyboard keys at the same time. Use this for keyboard shortcuts, navigation, and special keys.
10
+ ```javascript
11
+ await testdriver.pressKeys(['enter']);
12
+ await testdriver.pressKeys(['ctrl', 'a']);
13
+ ```
11
14
 
12
- ## Syntax
15
+ To enter text, use [`type()`](/type) instead.
13
16
 
14
- ```javascript
15
- await testdriver.pressKeys(keys)
17
+ ## Signature
18
+
19
+ ```typescript
20
+ testdriver.pressKeys(keys: KeyboardKey[]): Promise<void>
16
21
  ```
17
22
 
18
23
  ## Parameters
19
24
 
20
- <ParamField path="keys" type="Array&lt;string&gt;" required>
21
- An array of the keys to push at the same time
25
+ <ParamField path="keys" type="KeyboardKey[]" required>
26
+ The keys to press, as a shortcut. TestDriver presses them in order, holds them all down, then releases them in reverse order. So `['ctrl', 'shift', 'i']` is Ctrl+Shift+I.
27
+
28
+ Each name must be a valid `KeyboardKey`. See [Key names](#key-names).
22
29
  </ParamField>
23
30
 
24
- ## Returns
31
+ Because the keys are pressed together, `['enter', 'enter']` doesn't press Enter twice. Call `pressKeys()` once per key press instead.
25
32
 
26
- `Promise<void>`
33
+ ## Key names
27
34
 
28
- ## Common Keys
35
+ Key names are lowercase. The most common ones:
29
36
 
30
- ### Special Keys
31
- - `'enter'`, `'tab'`, `'escape'`, `'backspace'`, `'delete'`
32
- - `'space'`, `'up'`, `'down'`, `'left'`, `'right'`
33
- - `'home'`, `'end'`, `'pageup'`, `'pagedown'`
37
+ | Kind | Names |
38
+ | --- | --- |
39
+ | Letters, digits, symbols | `'a'`–`'z'`, `'0'`–`'9'`, and single characters like `'/'` or `'.'` |
40
+ | Editing | `'enter'`, `'tab'`, `'space'`, `'backspace'`, `'delete'`, `'escape'` |
41
+ | Navigation | `'up'`, `'down'`, `'left'`, `'right'`, `'home'`, `'end'`, `'pageup'`, `'pagedown'` |
42
+ | Modifiers | `'ctrl'`, `'shift'`, `'alt'`, `'win'`, and left/right forms like `'ctrlleft'` |
43
+ | Function keys | `'f1'`–`'f24'` |
34
44
 
35
- ### Modifier Keys
36
- - `'ctrl'`, `'alt'`, `'shift'`
37
- - `'command'` (macOS), `'win'` (Windows)
38
- - `'ctrlleft'`, `'ctrlright'`, `'shiftleft'`, `'shiftright'`
45
+ The full list is the `KeyboardKey` type in `sdk.d.ts`, which your editor autocompletes.
39
46
 
40
- ### Function Keys
41
- - `'f1'` through `'f24'`
47
+ <Note>
48
+ Sandboxes run Linux by default, or Windows. Use `'ctrl'` for shortcuts like copy and paste, not `'command'`.
49
+ </Note>
42
50
 
43
51
  ## Examples
44
52
 
45
- ### Navigation
53
+ ### Move between form fields
46
54
 
47
55
  ```javascript
48
- // Tab to next field
56
+ await testdriver.find('Email input field').click();
57
+ await testdriver.type('user@example.com');
49
58
  await testdriver.pressKeys(['tab']);
50
-
51
- // Shift+Tab to previous field
52
- await testdriver.pressKeys(['shift', 'tab']);
53
-
54
- // Arrow keys
55
- await testdriver.pressKeys(['down']);
56
- await testdriver.pressKeys(['up']);
57
- await testdriver.pressKeys(['left']);
58
- await testdriver.pressKeys(['right']);
59
-
60
- // Home/End
61
- await testdriver.pressKeys(['home']); // Start of line
62
- await testdriver.pressKeys(['end']); // End of line
63
-
64
- // Page navigation
65
- await testdriver.pressKeys(['pagedown']);
66
- await testdriver.pressKeys(['pageup']);
59
+ await testdriver.type(process.env.TD_PASSWORD, { secret: true });
60
+ await testdriver.pressKeys(['enter']);
67
61
  ```
68
62
 
69
- ### Keyboard Shortcuts
63
+ ### Replace the text in a field
70
64
 
71
65
  ```javascript
72
- // Copy (Ctrl+C / Cmd+C)
73
- await testdriver.pressKeys(['ctrl', 'c']);
74
-
75
- // Paste (Ctrl+V / Cmd+V)
76
- await testdriver.pressKeys(['ctrl', 'v']);
77
-
78
- // Save (Ctrl+S)
79
- await testdriver.pressKeys(['ctrl', 's']);
80
-
81
- // Select All (Ctrl+A)
66
+ await testdriver.find('Search input').click();
82
67
  await testdriver.pressKeys(['ctrl', 'a']);
83
-
84
- // Undo (Ctrl+Z)
85
- await testdriver.pressKeys(['ctrl', 'z']);
86
-
87
- // Redo (Ctrl+Y)
88
- await testdriver.pressKeys(['ctrl', 'y']);
89
-
90
- // Find (Ctrl+F)
91
- await testdriver.pressKeys(['ctrl', 'f']);
92
-
93
- // New tab (Ctrl+T)
94
- await testdriver.pressKeys(['ctrl', 't']);
95
-
96
- // Close tab (Ctrl+W)
97
- await testdriver.pressKeys(['ctrl', 'w']);
98
-
99
- // Refresh (F5 or Ctrl+R)
100
- await testdriver.pressKeys(['f5']);
101
- await testdriver.pressKeys(['ctrl', 'r']);
102
- ```
103
-
104
- ### System Shortcuts
105
-
106
- ```javascript
107
- // Alt+Tab (Windows - switch apps)
108
- await testdriver.pressKeys(['alt', 'tab']);
109
-
110
- // Alt+F4 (Windows - close window)
111
- await testdriver.pressKeys(['alt', 'f4']);
112
-
113
- // Win+D (Windows - show desktop)
114
- await testdriver.pressKeys(['winleft', 'd']);
115
-
116
- // Win+L (Windows - lock screen)
117
- await testdriver.pressKeys(['winleft', 'l']);
118
-
119
- // Cmd+Tab (macOS - switch apps)
120
- await testdriver.pressKeys(['command', 'tab']);
121
-
122
- // Cmd+Q (macOS - quit app)
123
- await testdriver.pressKeys(['command', 'q']);
68
+ await testdriver.type('wireless headphones');
124
69
  ```
125
70
 
126
- ### Form Submission
71
+ ### Go to a URL in Chrome
127
72
 
128
73
  ```javascript
129
- // Submit form
74
+ await testdriver.pressKeys(['ctrl', 'l']);
75
+ await testdriver.type('https://testdriver-sandbox.vercel.app/login');
130
76
  await testdriver.pressKeys(['enter']);
131
-
132
- // Cancel/Close
133
- await testdriver.pressKeys(['escape']);
134
-
135
- // Check checkbox
136
- await testdriver.pressKeys(['space']);
137
77
  ```
138
78
 
139
- ### Text Editing
140
-
141
- ```javascript
142
- // Delete selected text
143
- await testdriver.pressKeys(['delete']);
144
-
145
- // Backspace
146
- await testdriver.pressKeys(['backspace']);
147
-
148
- // Select all and delete
149
- await testdriver.pressKeys(['ctrl', 'a']);
150
- await testdriver.pressKeys(['delete']);
79
+ ## Complete example
151
80
 
152
- // Cut text
153
- await testdriver.pressKeys(['ctrl', 'x']);
154
- ```
81
+ ```javascript tests/keyboard-login.test.mjs
82
+ import { describe, expect, it } from 'vitest';
83
+ import { TestDriver } from 'testdriverai/vitest/hooks';
155
84
 
156
- ## Best Practices
85
+ describe('Login', () => {
86
+ it('submits the form from the keyboard', async (context) => {
87
+ const testdriver = TestDriver(context);
88
+ await testdriver.provision.chrome({ url: 'https://testdriver-sandbox.vercel.app/login' });
157
89
 
158
- <Check>
159
- **Wait after shortcuts**
160
-
161
- Some keyboard shortcuts trigger animations or navigation:
162
-
163
- ```javascript
164
- await testdriver.pressKeys(['ctrl', 't']); // New tab
165
- await new Promise(r => setTimeout(r, 500)); // Wait for tab
166
- await testdriver.pressKeys(['ctrl', 'l']); // Focus URL bar
167
- ```
168
- </Check>
90
+ // The sandbox login page shows its test password on screen
91
+ const password = await testdriver.extract('the password');
169
92
 
170
- <Check>
171
- **Use Tab for form navigation**
172
-
173
- Tab is more reliable than clicking multiple fields:
174
-
175
- ```javascript
176
- const firstField = await testdriver.find('email input');
177
- await firstField.click();
178
- await testdriver.type('user@example.com');
179
-
180
- await testdriver.pressKeys(['tab']);
181
- await testdriver.type('password123');
182
-
183
- await testdriver.pressKeys(['tab']);
184
- await testdriver.pressKeys(['enter']); // Submit
185
- ```
186
- </Check>
187
-
188
- <Warning>
189
- **Platform-specific keys**
190
-
191
- Use the appropriate modifier key for the platform:
192
- - Windows/Linux: `'ctrl'`
193
- - macOS: `'command'`
194
-
195
- ```javascript
196
- // For cross-platform, you might need to detect OS
197
- const modKey = process.platform === 'darwin' ? 'command' : 'ctrl';
198
- await testdriver.pressKeys([modKey, 'c']); // Copy
199
- ```
200
- </Warning>
201
-
202
- ## Use Cases
203
-
204
- <AccordionGroup>
205
- <Accordion title="Form Navigation">
206
- ```javascript
207
- // Fill form using Tab
208
- const firstField = await testdriver.find('name field');
209
- await firstField.click();
210
- await testdriver.type('John Doe');
211
-
212
- await testdriver.pressKeys(['tab']);
213
- await testdriver.type('john@example.com');
214
-
93
+ await testdriver.find('Username input field').click();
94
+ await testdriver.type('standard_user');
215
95
  await testdriver.pressKeys(['tab']);
216
- await testdriver.type('555-0123');
217
-
218
- await testdriver.pressKeys(['tab']);
219
- await testdriver.pressKeys(['enter']); // Submit
220
- ```
221
- </Accordion>
222
-
223
- <Accordion title="Text Manipulation">
224
- ```javascript
225
- const textArea = await testdriver.find('comment textarea');
226
- await textArea.click();
227
-
228
- // Select all existing text
229
- await testdriver.pressKeys(['ctrl', 'a']);
230
-
231
- // Copy it
232
- await testdriver.pressKeys(['ctrl', 'c']);
233
-
234
- // Type new text
235
- await testdriver.type('New comment');
236
-
237
- // Undo if needed
238
- await testdriver.pressKeys(['ctrl', 'z']);
239
- ```
240
- </Accordion>
241
-
242
- <Accordion title="Browser Navigation">
243
- ```javascript
244
- // Open new tab
245
- await testdriver.pressKeys(['ctrl', 't']);
246
- await new Promise(r => setTimeout(r, 500));
247
-
248
- // Focus address bar
249
- await testdriver.pressKeys(['ctrl', 'l']);
250
- await testdriver.type('https://example.com');
96
+ await testdriver.type(password, { secret: true });
251
97
  await testdriver.pressKeys(['enter']);
252
-
253
- // Refresh page
254
- await testdriver.pressKeys(['f5']);
255
-
256
- // Close tab
257
- await testdriver.pressKeys(['ctrl', 'w']);
258
- ```
259
- </Accordion>
260
-
261
- <Accordion title="Application Shortcuts">
262
- ```javascript
263
- // Save document
264
- await testdriver.pressKeys(['ctrl', 's']);
265
-
266
- // Print
267
- await testdriver.pressKeys(['ctrl', 'p']);
268
-
269
- // Find in page
270
- await testdriver.pressKeys(['ctrl', 'f']);
271
- await testdriver.type('search term');
272
- await testdriver.pressKeys(['escape']); // Close find
273
- ```
274
- </Accordion>
275
- </AccordionGroup>
276
-
277
- ## Complete Example
278
98
 
279
- ```javascript
280
- import { beforeAll, afterAll, describe, it } from 'vitest';
281
- import TestDriver from 'testdriverai';
282
-
283
- describe('Keyboard Navigation', () => {
284
- let testdriver;
285
-
286
- beforeAll(async () => {
287
- client = new TestDriver(process.env.TD_API_KEY);
288
- await testdriver.auth();
289
- await testdriver.connect();
290
- });
291
-
292
- afterAll(async () => {
293
- await testdriver.disconnect();
294
- });
295
-
296
- it('should navigate form with keyboard', async () => {
297
- await testdriver.focusApplication('Google Chrome');
298
-
299
- // Find first field
300
- const emailField = await testdriver.find('email input');
301
- await emailField.click();
302
- await testdriver.type('user@example.com');
303
-
304
- // Tab through fields
305
- await testdriver.pressKeys(['tab']);
306
- await testdriver.type('John');
307
-
308
- await testdriver.pressKeys(['tab']);
309
- await testdriver.type('Doe');
310
-
311
- await testdriver.pressKeys(['tab']);
312
- await testdriver.type('password123');
313
-
314
- // Submit with Enter
315
- await testdriver.pressKeys(['tab']);
316
- await testdriver.pressKeys(['enter']);
317
-
318
- await testdriver.assert('form submitted successfully');
319
- });
320
-
321
- it('should use keyboard shortcuts', async () => {
322
- // Open new browser tab
323
- await testdriver.pressKeys(['ctrl', 't']);
324
- await new Promise(r => setTimeout(r, 500));
325
-
326
- // Focus address bar
327
- await testdriver.pressKeys(['ctrl', 'l']);
328
- await testdriver.type('https://example.com');
329
- await testdriver.pressKeys(['enter']);
330
-
331
- await new Promise(r => setTimeout(r, 2000));
332
-
333
- // Select all page content
334
- await testdriver.pressKeys(['ctrl', 'a']);
335
-
336
- // Copy
337
- await testdriver.pressKeys(['ctrl', 'c']);
338
-
339
- // Refresh page
340
- await testdriver.pressKeys(['f5']);
99
+ const result = await testdriver.assert('the product list is shown');
100
+ expect(result).toBe(true);
341
101
  });
342
102
  });
343
103
  ```
344
104
 
345
- ## Related Methods
105
+ ## Related
346
106
 
347
- - [`type()`](/type) - Type text
348
- - [`click()`](/click) - Click elements
349
- - [`scroll()`](/scroll) - Scroll pages
107
+ - [type()](/type): enter text into the focused field
108
+ - [click()](/click): focus a field before you type or press keys
109
+ - [Perform actions](/performing-actions): every kind of action in one guide