@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,249 +1,146 @@
1
1
  ---
2
- title: "Reusable Code Snippets"
3
- description: "Build maintainable test suites with reusable code patterns"
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 your test suite becomes larger, put the common patterns into reusable code. This keeps the tests short, easy to read, and easy to keep.
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
- export async function logout(testdriver) {
31
- const userMenu = await testdriver.find('user menu');
32
- await userMenu.click();
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
- <Warning>
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
- **❌ Bad:** `await testdriver.find('profile name TestDriver in the top right')`
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
- Hardcoded values like usernames, product names, or prices will cause tests to fail when the data changes. Use generic descriptions that work regardless of the specific content displayed.
46
- </Warning>
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
- Now import and use these helpers in any test:
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
- ```javascript test/checkout.test.mjs
51
- import { describe, expect, it } from "vitest";
52
- import { TestDriver } from "testdriverai/vitest/hooks";
53
- import { login } from './helpers/auth.js';
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
- describe("Checkout", () => {
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
- // Use the helper
64
- await login(testdriver, {
65
- email: 'user@example.com',
66
- password: 'password123'
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
- // Continue with checkout steps...
70
- const cartButton = await testdriver.find('cart button');
71
- await cartButton.click();
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
- ## Page Objects
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
- For larger test suites, the Page Object pattern encapsulates all interactions with a specific page or component:
60
+ ## Group actions with a page object
79
61
 
80
- ```javascript test/pages/LoginPage.js
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.td = testdriver;
67
+ this.testdriver = testdriver;
84
68
  }
85
69
 
86
- async enterEmail(email) {
87
- const input = await this.td.find('email input');
88
- await input.click();
89
- await this.td.type(email);
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 input = await this.td.find('password input');
94
- await input.click();
95
- await this.td.type(password);
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 button = await this.td.find('submit button');
100
- await button.click();
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
- async login(email, password) {
104
- await this.enterEmail(email);
105
- await this.enterPassword(password);
106
- await this.submit();
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
- async assertError(message) {
110
- return await this.td.assert(`error message shows "${message}"`);
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
- async assertLoggedIn() {
114
- return await this.td.assert('user dashboard is visible');
115
- }
116
- }
117
- ```
98
+ const loginPage = new LoginPage(testdriver);
99
+ await loginPage.enterUsername('standard_user');
100
+ await loginPage.submit();
118
101
 
119
- Use the page object in your tests:
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
- ## Shared Test Fixtures
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
- ```javascript test/admin.test.mjs
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
- ## Suggested Project Structure
111
+ A layout that keeps shared code apart from tests:
216
112
 
217
113
  <FileTree>
218
- <Folder name="test" defaultOpen>
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="LoginPage.js" />
229
- <File name="DashboardPage.js" />
230
- <File name="CheckoutPage.js" />
119
+ <File name="login-page.js" />
231
120
  </Folder>
232
- <Folder name="specs" defaultOpen>
233
- <File name="auth.test.mjs" />
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
- | Folder | Purpose |
241
- |--------|---------|
242
- | `fixtures/` | Test data and setup utilities |
243
- | `helpers/` | Reusable helper functions |
244
- | `pages/` | Page object classes |
245
- | `specs/` | Test files |
246
-
247
- <Tip>
248
- Start simple with helper functions. Only introduce page objects when you find yourself duplicating the same element interactions across multiple tests.
249
- </Tip>
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>
@@ -1,123 +1,81 @@
1
1
  ---
2
- title: "rightClick"
3
- description: "Perform a right-click action to open context menus"
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
- ## Overview
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
- // Right-click on an element
15
- await element.rightClick();
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
- ## Parameters
15
+ ## element.rightClick()
22
16
 
23
- When called on an `Element`, no parameters are required.
17
+ Right-clicks the element at the point where `find()` located it. It's the same as `element.click('right-click')`.
24
18
 
25
- When called directly on the AI client:
19
+ ```typescript
20
+ element.rightClick(): Promise<void>
21
+ ```
26
22
 
27
- | Parameter | Type | Description |
28
- |-----------|------|-------------|
29
- | `selector` | `string` | The selector describing the element to right-click |
23
+ It takes no parameters.
30
24
 
31
- ## Returns
25
+ ### Errors
32
26
 
33
- It returns a `Promise<void>`. The promise resolves when the right-click action completes.
27
+ Throws an `ElementNotFoundError` if `find()` didn't locate the element. See [click()](/click#errors) for how to check `found()` first.
34
28
 
35
- ## Examples
29
+ ## Right-click at coordinates
36
30
 
37
- ### Right-Click to Open Context Menu
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
- const fileItem = await ai.find('README.md file');
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
- ### Direct Right-Click with Selector
37
+ Prefer `element.rightClick()`, which keeps working when the layout or screen size changes.
48
38
 
49
- ```javascript
50
- await ai.rightClick('image in the gallery');
51
- await ai.click('Save image as');
52
- ```
39
+ ## Examples
53
40
 
54
- ### VS Code Context Menu
41
+ ### Open a link in a new tab
55
42
 
56
43
  ```javascript
57
- import { test } from 'vitest';
58
- import { vscode } from '@testdriver/sdk';
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
- ### Browser Context Menu
48
+ ### Rename a file in VS Code
80
49
 
81
50
  ```javascript
82
- import { test } from 'vitest';
83
- import { chrome } from '@testdriver/sdk';
84
-
85
- test('opens link in new tab', async () => {
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
- ### Custom Context Menu in Web App
57
+ ## Complete example
97
58
 
98
- ```javascript
99
- test('uses custom context menu', async () => {
100
- const { ai } = await chrome('https://app.example.com');
101
-
102
- // Right-click on custom element
103
- await ai.rightClick('project item in the list');
104
-
105
- // Wait for custom menu to appear
106
- await ai.find('custom context menu');
107
-
108
- // Click menu option
109
- await ai.click('Duplicate project');
110
-
111
- // Verify duplication
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 Methods
76
+ ## Related
118
77
 
119
- - [`click()`](/click) - Single click on an element
120
- - [`doubleClick()`](/double-click) - Double-click on an element
121
- - [`mouseDown()`](/mouse-down) - Press mouse button without releasing
122
- - [`mouseUp()`](/mouse-up) - Release mouse button
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