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