@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.
- package/docs/_data/examples-manifest.json +16 -16
- package/docs/_scripts/generate-examples.js +38 -45
- package/docs/agent.mdx +174 -93
- package/docs/assert.mdx +73 -269
- package/docs/aws-setup.mdx +196 -314
- package/docs/bring-your-own-machine.mdx +139 -206
- package/docs/cache.mdx +144 -151
- package/docs/caching.mdx +99 -89
- package/docs/captcha.mdx +126 -111
- package/docs/ci-cd.mdx +154 -543
- package/docs/click.mdx +74 -229
- package/docs/client.mdx +177 -231
- package/docs/copilot/auto-healing.mdx +86 -370
- package/docs/copilot/running-tests.mdx +121 -930
- package/docs/customizing-devices.mdx +74 -251
- package/docs/dashcam.mdx +72 -405
- package/docs/debugging-with-screenshots.mdx +105 -371
- package/docs/docs.json +79 -22
- package/docs/double-click.mdx +48 -68
- package/docs/errors.mdx +102 -176
- package/docs/events.mdx +170 -327
- package/docs/examples/{hover-text-with-description.mdx → add-to-cart.mdx} +30 -35
- package/docs/examples/assert.mdx +28 -41
- package/docs/examples/click-button.mdx +71 -0
- package/docs/examples/{hover-image.mdx → click-icon.mdx} +32 -28
- package/docs/examples/element-not-found.mdx +27 -26
- package/docs/examples/findall-coffee-icons.mdx +28 -32
- package/docs/examples/installer.mdx +61 -25
- package/docs/examples/launch-vscode-linux.mdx +28 -51
- package/docs/examples/navigate-and-scroll.mdx +82 -0
- package/docs/examples/parse.mdx +24 -28
- package/docs/examples/press-keys.mdx +28 -25
- package/docs/examples/scroll.mdx +25 -27
- package/docs/examples/type.mdx +26 -21
- package/docs/exec.mdx +92 -256
- package/docs/extract.mdx +69 -170
- package/docs/find.mdx +140 -736
- package/docs/focus-application.mdx +69 -246
- package/docs/generating-tests.mdx +103 -137
- package/docs/guide/best-practices-polling.mdx +65 -141
- package/docs/hosted.mdx +80 -144
- package/docs/hover.mdx +62 -235
- package/docs/locating-elements.mdx +74 -369
- package/docs/making-assertions.mdx +116 -74
- package/docs/mcp-setup.mdx +80 -63
- package/docs/mouse-down.mdx +48 -125
- package/docs/mouse-up.mdx +47 -129
- package/docs/options.mdx +75 -48
- package/docs/parse.mdx +95 -184
- package/docs/performing-actions.mdx +104 -94
- package/docs/press-keys.mdx +59 -299
- package/docs/provision.mdx +137 -182
- package/docs/quickstart-claude.mdx +85 -0
- package/docs/quickstart-github.mdx +62 -32
- package/docs/quickstart-manual.mdx +60 -33
- package/docs/quickstart-vscode.mdx +97 -0
- package/docs/quickstart.mdx +26 -9
- package/docs/redraw.mdx +101 -165
- package/docs/reusable-code.mdx +97 -200
- package/docs/right-click.mdx +47 -89
- package/docs/screenshot.mdx +47 -201
- package/docs/screenshots.mdx +87 -137
- package/docs/scroll.mdx +71 -154
- package/docs/secrets.mdx +96 -91
- package/docs/self-hosted.mdx +83 -142
- package/docs/snippets/enterprise.mdx +15 -0
- package/docs/styles.css +68 -0
- package/docs/test-results-json.mdx +167 -204
- package/docs/type.mdx +56 -304
- package/docs/variables.mdx +121 -81
- package/docs/wait.mdx +33 -31
- package/docs/what-is-testdriver.mdx +80 -32
- package/examples/{hover-text-with-description.test.mjs → add-to-cart.test.mjs} +4 -5
- package/examples/assert.test.mjs +3 -20
- package/examples/{hover-text.test.mjs → click-button.test.mjs} +3 -4
- package/examples/{hover-image.test.mjs → click-icon.test.mjs} +5 -6
- package/examples/element-not-found.test.mjs +3 -5
- package/examples/findall-coffee-icons.test.mjs +3 -3
- package/examples/installer.test.mjs +1 -1
- package/examples/launch-vscode-linux.test.mjs +6 -33
- package/examples/{scroll-keyboard.test.mjs → navigate-and-scroll.test.mjs} +4 -5
- package/examples/parse.test.mjs +1 -2
- package/examples/press-keys.test.mjs +4 -4
- package/examples/scroll.test.mjs +3 -6
- package/examples/type.test.mjs +5 -2
- package/lib/provision.js +6 -2
- package/package.json +1 -1
- package/sdk.d.ts +4 -2
- package/sdk.js +1 -1
- package/docs/claude-mcp-plugin.mdx +0 -244
- package/docs/elements.mdx +0 -606
- package/docs/examples/ai.mdx +0 -78
- package/docs/examples/captcha-api.mdx +0 -92
- package/docs/examples/chrome-extension.mdx +0 -156
- package/docs/examples/drag-and-drop.mdx +0 -100
- package/docs/examples/formatted-logging.mdx +0 -74
- package/docs/examples/hover-text.mdx +0 -70
- package/docs/examples/match-image.mdx +0 -97
- package/docs/examples/scroll-keyboard.mdx +0 -80
- package/docs/examples/scroll-until-text.mdx +0 -109
package/docs/reusable-code.mdx
CHANGED
|
@@ -1,249 +1,146 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "
|
|
3
|
-
|
|
2
|
+
title: "Reuse code across tests"
|
|
3
|
+
sidebarTitle: "Reusable code snippets"
|
|
4
|
+
description: "Share login steps and other common flows between tests with helpers and page objects."
|
|
4
5
|
icon: "recycle"
|
|
5
6
|
---
|
|
6
7
|
|
|
7
|
-
When
|
|
8
|
-
|
|
9
|
-
## Helper Functions
|
|
10
|
-
|
|
11
|
-
The most simple method is to put the common actions into helper functions. Make a `helpers/` directory for the shared utilities:
|
|
12
|
-
|
|
13
|
-
```javascript test/helpers/auth.js
|
|
14
|
-
export async function login(testdriver, { email, password }) {
|
|
15
|
-
const emailInput = await testdriver.find('email input');
|
|
16
|
-
await emailInput.click();
|
|
17
|
-
await testdriver.type(email);
|
|
18
|
-
|
|
19
|
-
const passwordInput = await testdriver.find('password input');
|
|
20
|
-
await passwordInput.click();
|
|
21
|
-
await testdriver.type(password);
|
|
22
|
-
|
|
23
|
-
const loginButton = await testdriver.find('login button');
|
|
24
|
-
await loginButton.click();
|
|
25
|
-
|
|
26
|
-
const result = await testdriver.assert('user is logged in');
|
|
27
|
-
return result;
|
|
28
|
-
}
|
|
8
|
+
When several tests repeat the same steps, move those steps into shared code.
|
|
29
9
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const logoutButton = await testdriver.find('logout button');
|
|
35
|
-
await logoutButton.click();
|
|
36
|
-
}
|
|
37
|
-
```
|
|
10
|
+
- **Helper functions** wrap one flow, such as signing in.
|
|
11
|
+
- **Page objects** group every action for one screen in a class.
|
|
12
|
+
|
|
13
|
+
Both are plain JavaScript modules. They take the `testdriver` client as an argument, so they work in any test.
|
|
38
14
|
|
|
39
|
-
|
|
40
|
-
**Do not put dynamic values in element descriptions.** An element selector must describe the *type* of the element. It must not describe specific content that can change.
|
|
15
|
+
## Write a helper function
|
|
41
16
|
|
|
42
|
-
|
|
43
|
-
**✅ Good:** `await testdriver.find('user profile name in the top right')`
|
|
17
|
+
Put the shared steps in a function that takes the client. This helper signs in to the TestDriver demo store:
|
|
44
18
|
|
|
45
|
-
|
|
46
|
-
|
|
19
|
+
```javascript tests/helpers/auth.js
|
|
20
|
+
export async function signIn(testdriver, username = 'standard_user') {
|
|
21
|
+
const usernameField = await testdriver.find('Username input field');
|
|
22
|
+
await usernameField.click();
|
|
23
|
+
await testdriver.type(username);
|
|
47
24
|
|
|
48
|
-
|
|
25
|
+
// The demo store shows its password on the login page
|
|
26
|
+
const password = await testdriver.extract('the password shown on the login page');
|
|
49
27
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
28
|
+
const passwordField = await testdriver.find('Password input field');
|
|
29
|
+
await passwordField.click();
|
|
30
|
+
await testdriver.type(password, { secret: true });
|
|
31
|
+
|
|
32
|
+
const signInButton = await testdriver.find('Sign in button below the password field');
|
|
33
|
+
await signInButton.click();
|
|
34
|
+
}
|
|
35
|
+
```
|
|
54
36
|
|
|
55
|
-
|
|
56
|
-
it("should complete checkout as logged in user", async (context) => {
|
|
57
|
-
const testdriver = TestDriver(context);
|
|
58
|
-
|
|
59
|
-
await testdriver.provision.chrome({
|
|
60
|
-
url: 'https://shop.example.com',
|
|
61
|
-
});
|
|
37
|
+
Import it and call it with your test's client:
|
|
62
38
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
});
|
|
39
|
+
```javascript tests/cart.test.mjs
|
|
40
|
+
import { expect, it } from 'vitest';
|
|
41
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
42
|
+
import { signIn } from './helpers/auth.js';
|
|
68
43
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
44
|
+
it('shows an empty cart after signing in', async (context) => {
|
|
45
|
+
const testdriver = TestDriver(context);
|
|
46
|
+
await testdriver.provision.chrome({ url: 'https://testdriver-sandbox.vercel.app/login' });
|
|
47
|
+
|
|
48
|
+
await signIn(testdriver);
|
|
49
|
+
|
|
50
|
+
const cartIcon = await testdriver.find('Shopping cart icon in the top right corner');
|
|
51
|
+
await cartIcon.click();
|
|
52
|
+
|
|
53
|
+
const result = await testdriver.assert('the cart is empty');
|
|
54
|
+
expect(result).toBe(true);
|
|
73
55
|
});
|
|
74
56
|
```
|
|
75
57
|
|
|
76
|
-
|
|
58
|
+
Pass values that change between tests, like the username, as arguments. Keep them out of the descriptions you pass to [`find()`](/find), so the [cache](/caching) can match them on every run.
|
|
77
59
|
|
|
78
|
-
|
|
60
|
+
## Group actions with a page object
|
|
79
61
|
|
|
80
|
-
|
|
62
|
+
A page object is a class with one method per action on a screen. Use one when many tests work with the same screen.
|
|
63
|
+
|
|
64
|
+
```javascript tests/pages/login-page.js
|
|
81
65
|
export class LoginPage {
|
|
82
66
|
constructor(testdriver) {
|
|
83
|
-
this.
|
|
67
|
+
this.testdriver = testdriver;
|
|
84
68
|
}
|
|
85
69
|
|
|
86
|
-
async
|
|
87
|
-
const
|
|
88
|
-
await
|
|
89
|
-
await this.
|
|
70
|
+
async enterUsername(username) {
|
|
71
|
+
const usernameField = await this.testdriver.find('Username input field');
|
|
72
|
+
await usernameField.click();
|
|
73
|
+
await this.testdriver.type(username);
|
|
90
74
|
}
|
|
91
75
|
|
|
92
76
|
async enterPassword(password) {
|
|
93
|
-
const
|
|
94
|
-
await
|
|
95
|
-
await this.
|
|
77
|
+
const passwordField = await this.testdriver.find('Password input field');
|
|
78
|
+
await passwordField.click();
|
|
79
|
+
await this.testdriver.type(password, { secret: true });
|
|
96
80
|
}
|
|
97
81
|
|
|
98
82
|
async submit() {
|
|
99
|
-
const
|
|
100
|
-
await
|
|
83
|
+
const signInButton = await this.testdriver.find('Sign in button below the password field');
|
|
84
|
+
await signInButton.click();
|
|
101
85
|
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
102
88
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
89
|
+
```javascript tests/login.test.mjs
|
|
90
|
+
import { expect, it } from 'vitest';
|
|
91
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
92
|
+
import { LoginPage } from './pages/login-page.js';
|
|
108
93
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
}
|
|
94
|
+
it('asks for a password when it is missing', async (context) => {
|
|
95
|
+
const testdriver = TestDriver(context);
|
|
96
|
+
await testdriver.provision.chrome({ url: 'https://testdriver-sandbox.vercel.app/login' });
|
|
112
97
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
117
|
-
```
|
|
98
|
+
const loginPage = new LoginPage(testdriver);
|
|
99
|
+
await loginPage.enterUsername('standard_user');
|
|
100
|
+
await loginPage.submit();
|
|
118
101
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
```javascript test/auth.test.mjs
|
|
122
|
-
import { describe, expect, it } from "vitest";
|
|
123
|
-
import { TestDriver } from "testdriverai/vitest/hooks";
|
|
124
|
-
import { LoginPage } from './pages/LoginPage.js';
|
|
125
|
-
|
|
126
|
-
describe("Authentication", () => {
|
|
127
|
-
it("should show error for invalid credentials", async (context) => {
|
|
128
|
-
const testdriver = TestDriver(context);
|
|
129
|
-
|
|
130
|
-
await testdriver.provision.chrome({
|
|
131
|
-
url: 'https://app.example.com/login',
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
const loginPage = new LoginPage(testdriver);
|
|
135
|
-
|
|
136
|
-
await loginPage.login('invalid@test.com', 'wrongpassword');
|
|
137
|
-
|
|
138
|
-
const hasError = await loginPage.assertError('Invalid credentials');
|
|
139
|
-
expect(hasError).toBeTruthy();
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
it("should redirect to dashboard on success", async (context) => {
|
|
143
|
-
const testdriver = TestDriver(context);
|
|
144
|
-
|
|
145
|
-
await testdriver.provision.chrome({
|
|
146
|
-
url: 'https://app.example.com/login',
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
const loginPage = new LoginPage(testdriver);
|
|
150
|
-
|
|
151
|
-
await loginPage.login('valid@test.com', 'correctpassword');
|
|
152
|
-
|
|
153
|
-
const isLoggedIn = await loginPage.assertLoggedIn();
|
|
154
|
-
expect(isLoggedIn).toBeTruthy();
|
|
155
|
-
});
|
|
102
|
+
const result = await testdriver.assert('a "Please fill out this field" message is visible near the password field');
|
|
103
|
+
expect(result).toBe(true);
|
|
156
104
|
});
|
|
157
105
|
```
|
|
158
106
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
Create reusable fixtures for common test setup scenarios:
|
|
162
|
-
|
|
163
|
-
```javascript test/fixtures/index.js
|
|
164
|
-
export const testUsers = {
|
|
165
|
-
admin: { email: 'admin@example.com', password: 'admin123' },
|
|
166
|
-
regular: { email: 'user@example.com', password: 'user123' },
|
|
167
|
-
guest: { email: 'guest@example.com', password: 'guest123' },
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
export const testUrls = {
|
|
171
|
-
staging: 'https://staging.example.com',
|
|
172
|
-
production: 'https://example.com',
|
|
173
|
-
};
|
|
174
|
-
|
|
175
|
-
export async function setupAuthenticatedSession(testdriver, user = testUsers.regular) {
|
|
176
|
-
const emailInput = await testdriver.find('email input');
|
|
177
|
-
await emailInput.click();
|
|
178
|
-
await testdriver.type(user.email);
|
|
179
|
-
|
|
180
|
-
const passwordInput = await testdriver.find('password input');
|
|
181
|
-
await passwordInput.click();
|
|
182
|
-
await testdriver.type(user.password);
|
|
183
|
-
|
|
184
|
-
const loginButton = await testdriver.find('login button');
|
|
185
|
-
await loginButton.click();
|
|
186
|
-
|
|
187
|
-
await testdriver.assert('user is logged in');
|
|
188
|
-
}
|
|
189
|
-
```
|
|
107
|
+
Start with helper functions. Move to page objects when several tests repeat the same actions on one screen.
|
|
190
108
|
|
|
191
|
-
|
|
192
|
-
import { describe, expect, it } from "vitest";
|
|
193
|
-
import { TestDriver } from "testdriverai/vitest/hooks";
|
|
194
|
-
import { testUsers, testUrls, setupAuthenticatedSession } from './fixtures/index.js';
|
|
195
|
-
|
|
196
|
-
describe("Admin Panel", () => {
|
|
197
|
-
it("should access admin settings", async (context) => {
|
|
198
|
-
const testdriver = TestDriver(context);
|
|
199
|
-
|
|
200
|
-
await testdriver.provision.chrome({
|
|
201
|
-
url: `${testUrls.staging}/login`,
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
await setupAuthenticatedSession(testdriver, testUsers.admin);
|
|
205
|
-
|
|
206
|
-
const settingsLink = await testdriver.find('admin settings link');
|
|
207
|
-
await settingsLink.click();
|
|
208
|
-
|
|
209
|
-
const result = await testdriver.assert('admin settings panel is visible');
|
|
210
|
-
expect(result).toBeTruthy();
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
```
|
|
109
|
+
## Organize your files
|
|
214
110
|
|
|
215
|
-
|
|
111
|
+
A layout that keeps shared code apart from tests:
|
|
216
112
|
|
|
217
113
|
<FileTree>
|
|
218
|
-
<Folder name="
|
|
219
|
-
<Folder name="fixtures" defaultOpen>
|
|
220
|
-
<File name="index.js" />
|
|
221
|
-
</Folder>
|
|
114
|
+
<Folder name="tests" defaultOpen>
|
|
222
115
|
<Folder name="helpers" defaultOpen>
|
|
223
116
|
<File name="auth.js" />
|
|
224
|
-
<File name="navigation.js" />
|
|
225
|
-
<File name="forms.js" />
|
|
226
117
|
</Folder>
|
|
227
118
|
<Folder name="pages" defaultOpen>
|
|
228
|
-
<File name="
|
|
229
|
-
<File name="DashboardPage.js" />
|
|
230
|
-
<File name="CheckoutPage.js" />
|
|
119
|
+
<File name="login-page.js" />
|
|
231
120
|
</Folder>
|
|
232
|
-
<Folder name="
|
|
233
|
-
<File name="
|
|
234
|
-
<File name="checkout.test.mjs" />
|
|
235
|
-
<File name="search.test.mjs" />
|
|
121
|
+
<Folder name="fixtures" defaultOpen>
|
|
122
|
+
<File name="usernames.js" />
|
|
236
123
|
</Folder>
|
|
124
|
+
<File name="cart.test.mjs" />
|
|
125
|
+
<File name="login.test.mjs" />
|
|
237
126
|
</Folder>
|
|
238
127
|
</FileTree>
|
|
239
128
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
129
|
+
By default, Vitest runs only files named `*.test.*` or `*.spec.*`, so helpers, page objects, and [fixtures](/variables#reuse-test-data-with-fixtures) aren't run as tests.
|
|
130
|
+
|
|
131
|
+
## Next steps
|
|
132
|
+
|
|
133
|
+
<CardGroup cols={2}>
|
|
134
|
+
<Card title="Use variables and test data" icon="square-root-variable" href="/variables">
|
|
135
|
+
Feed helpers with environment variables and fixtures.
|
|
136
|
+
</Card>
|
|
137
|
+
<Card title="Use secrets" icon="key" href="/secrets">
|
|
138
|
+
Keep the credentials your helpers type out of logs.
|
|
139
|
+
</Card>
|
|
140
|
+
<Card title="Locate elements" icon="crosshairs" href="/locating-elements">
|
|
141
|
+
Write descriptions that find the right element.
|
|
142
|
+
</Card>
|
|
143
|
+
<Card title="Click an icon example" icon="mouse-pointer" href="/examples/click-icon">
|
|
144
|
+
A complete test that uses a login helper.
|
|
145
|
+
</Card>
|
|
146
|
+
</CardGroup>
|
package/docs/right-click.mdx
CHANGED
|
@@ -1,123 +1,81 @@
|
|
|
1
1
|
---
|
|
2
|
-
title: "rightClick"
|
|
3
|
-
|
|
2
|
+
title: "rightClick()"
|
|
3
|
+
sidebarTitle: "rightClick"
|
|
4
|
+
description: "Right-click an element you found, usually to open a context menu."
|
|
4
5
|
icon: "bars"
|
|
5
6
|
---
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
The `rightClick()` method does a right-click action on an element. It usually opens context menus. You can call it on an [`Element`](/core-concepts/elements) instance. Or you can use it with a selector.
|
|
10
|
-
|
|
11
|
-
## Syntax
|
|
8
|
+
Right-click an element you've located with [`find()`](/find), then pick an item from the menu that opens:
|
|
12
9
|
|
|
13
10
|
```javascript
|
|
14
|
-
|
|
15
|
-
await
|
|
16
|
-
|
|
17
|
-
// Right-click using a selector
|
|
18
|
-
await ai.rightClick('selector');
|
|
11
|
+
await testdriver.find('report.pdf in the file list').rightClick();
|
|
12
|
+
await testdriver.find('Rename in the context menu').click();
|
|
19
13
|
```
|
|
20
14
|
|
|
21
|
-
##
|
|
15
|
+
## element.rightClick()
|
|
22
16
|
|
|
23
|
-
|
|
17
|
+
Right-clicks the element at the point where `find()` located it. It's the same as `element.click('right-click')`.
|
|
24
18
|
|
|
25
|
-
|
|
19
|
+
```typescript
|
|
20
|
+
element.rightClick(): Promise<void>
|
|
21
|
+
```
|
|
26
22
|
|
|
27
|
-
|
|
28
|
-
|-----------|------|-------------|
|
|
29
|
-
| `selector` | `string` | The selector describing the element to right-click |
|
|
23
|
+
It takes no parameters.
|
|
30
24
|
|
|
31
|
-
|
|
25
|
+
### Errors
|
|
32
26
|
|
|
33
|
-
|
|
27
|
+
Throws an `ElementNotFoundError` if `find()` didn't locate the element. See [click()](/click#errors) for how to check `found()` first.
|
|
34
28
|
|
|
35
|
-
##
|
|
29
|
+
## Right-click at coordinates
|
|
36
30
|
|
|
37
|
-
|
|
31
|
+
The client has no `rightClick()` method. To right-click a point on the screen, pass `action: 'right-click'` to [`testdriver.click()`](/click#testdriver-click-options):
|
|
38
32
|
|
|
39
33
|
```javascript
|
|
40
|
-
|
|
41
|
-
await fileItem.rightClick();
|
|
42
|
-
|
|
43
|
-
// Select menu option
|
|
44
|
-
await ai.click('Delete from context menu');
|
|
34
|
+
await testdriver.click({ x: 640, y: 360, action: 'right-click' });
|
|
45
35
|
```
|
|
46
36
|
|
|
47
|
-
|
|
37
|
+
Prefer `element.rightClick()`, which keeps working when the layout or screen size changes.
|
|
48
38
|
|
|
49
|
-
|
|
50
|
-
await ai.rightClick('image in the gallery');
|
|
51
|
-
await ai.click('Save image as');
|
|
52
|
-
```
|
|
39
|
+
## Examples
|
|
53
40
|
|
|
54
|
-
###
|
|
41
|
+
### Open a link in a new tab
|
|
55
42
|
|
|
56
43
|
```javascript
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
test('renames a file via context menu', async () => {
|
|
61
|
-
const { ai } = await vscode();
|
|
62
|
-
|
|
63
|
-
// Right-click on a file
|
|
64
|
-
await ai.rightClick('test.js in the file explorer');
|
|
65
|
-
|
|
66
|
-
// Click rename option
|
|
67
|
-
await ai.click('Rename');
|
|
68
|
-
|
|
69
|
-
// Type new name
|
|
70
|
-
await ai.type('test.spec.js');
|
|
71
|
-
await ai.pressKeys('Enter');
|
|
72
|
-
|
|
73
|
-
// Verify rename
|
|
74
|
-
const renamedFile = await ai.find('test.spec.js in the file explorer');
|
|
75
|
-
expect(renamedFile).toBeTruthy();
|
|
76
|
-
});
|
|
44
|
+
await testdriver.find('Documentation link in the page footer').rightClick();
|
|
45
|
+
await testdriver.find('Open link in new tab in the context menu').click();
|
|
77
46
|
```
|
|
78
47
|
|
|
79
|
-
###
|
|
48
|
+
### Rename a file in VS Code
|
|
80
49
|
|
|
81
50
|
```javascript
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const { ai } = await chrome('https://example.com');
|
|
87
|
-
|
|
88
|
-
// Right-click on a link
|
|
89
|
-
await ai.rightClick('Documentation link');
|
|
90
|
-
|
|
91
|
-
// Select "Open in new tab"
|
|
92
|
-
await ai.click('Open link in new tab');
|
|
93
|
-
});
|
|
51
|
+
await testdriver.find('index.js in the Explorer sidebar').rightClick();
|
|
52
|
+
await testdriver.find('Rename in the context menu').click();
|
|
53
|
+
await testdriver.type('main.js');
|
|
54
|
+
await testdriver.pressKeys(['enter']);
|
|
94
55
|
```
|
|
95
56
|
|
|
96
|
-
|
|
57
|
+
## Complete example
|
|
97
58
|
|
|
98
|
-
```javascript
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const duplicatedProject = await ai.find('project item (copy)');
|
|
113
|
-
expect(duplicatedProject).toBeTruthy();
|
|
59
|
+
```javascript tests/context-menu.test.mjs
|
|
60
|
+
import { describe, expect, it } from 'vitest';
|
|
61
|
+
import { TestDriver } from 'testdriverai/vitest/hooks';
|
|
62
|
+
|
|
63
|
+
describe('Context menu', () => {
|
|
64
|
+
it('opens the browser context menu on the page', async (context) => {
|
|
65
|
+
const testdriver = TestDriver(context);
|
|
66
|
+
await testdriver.provision.chrome({ url: 'https://testdriver-sandbox.vercel.app/login' });
|
|
67
|
+
|
|
68
|
+
await testdriver.find('the page heading').rightClick();
|
|
69
|
+
|
|
70
|
+
const result = await testdriver.assert('a context menu with a "Back" item is open');
|
|
71
|
+
expect(result).toBe(true);
|
|
72
|
+
});
|
|
114
73
|
});
|
|
115
74
|
```
|
|
116
75
|
|
|
117
|
-
## Related
|
|
76
|
+
## Related
|
|
118
77
|
|
|
119
|
-
- [
|
|
120
|
-
- [
|
|
121
|
-
- [
|
|
122
|
-
- [
|
|
123
|
-
- [`hover()`](/hover) - Move mouse over element without clicking
|
|
78
|
+
- [click()](/click): single clicks, and clicks at coordinates
|
|
79
|
+
- [doubleClick()](/double-click): open files and select words
|
|
80
|
+
- [find()](/find): locate the element to right-click
|
|
81
|
+
- [Perform actions](/performing-actions): every kind of action in one guide
|