Skip to content

Commit b45d3a2

Browse files
committed
feat: add hover/unhover, expose option types for playwright
1 parent f1e8574 commit b45d3a2

File tree

6 files changed

+85
-11
lines changed

6 files changed

+85
-11
lines changed

docs/guide/browser.md

+18-2
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,22 @@ export const userEvent: {
259259
* @see {@link https://testing-library.com/docs/user-event/convenience/#tab} testing-library API
260260
*/
261261
tab: (options?: UserEventTabOptions) => Promise<void>
262+
/**
263+
* Hovers over an element. Uses provider's API under the hood.
264+
* @see {@link https://playwright.dev/docs/api/class-locator#locator-hover} Playwright API
265+
* @see {@link https://webdriver.io/docs/api/element/moveTo/} WebdriverIO API
266+
* @see {@link https://testing-library.com/docs/user-event/convenience/#hover} testing-library API
267+
*/
268+
hover: (element: Element, options?: UserEventHoverOptions) => Promise<void>
269+
/**
270+
* Moves cursor position to the body element. Uses provider's API under the hood.
271+
* By default, the cursor position is in the center (in webdriverio) or in some visible place (in playwright)
272+
* of the body element, so if the current element is already there, this will have no effect.
273+
* @see {@link https://playwright.dev/docs/api/class-locator#locator-hover} Playwright API
274+
* @see {@link https://webdriver.io/docs/api/element/moveTo/} WebdriverIO API
275+
* @see {@link https://testing-library.com/docs/user-event/convenience/#hover} testing-library API
276+
*/
277+
unhover: (element: Element, options?: UserEventHoverOptions) => Promise<void>
262278
/**
263279
* Fills an input element with text. This will remove any existing text in the input before typing the new text.
264280
* Uses provider's API under the hood.
@@ -271,7 +287,7 @@ export const userEvent: {
271287
* @see {@link https://webdriver.io/docs/api/element/setValue} WebdriverIO API
272288
* @see {@link https://testing-library.com/docs/user-event/utility/#type} testing-library API
273289
*/
274-
fill: (element: Element, text: string) => Promise<void>
290+
fill: (element: Element, text: string, options?: UserEventFillOptions) => Promise<void>
275291
}
276292

277293
/**
@@ -415,7 +431,7 @@ export const myCommand = defineCommand(async (ctx, arg1, arg2) => {
415431
```
416432

417433
::: tip
418-
If you are using TypeScript, don't forget to add `@vitest/browser/providers/playwright` to your `tsconfig` "compilerOptions.types" field to get autocompletion:
434+
If you are using TypeScript, don't forget to add `@vitest/browser/providers/playwright` to your `tsconfig` "compilerOptions.types" field to get autocompletion in the config and on `userEvent` and `page` options:
419435

420436
```json
421437
{

packages/browser/context.d.ts

+20-6
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,22 @@ export interface UserEvent {
8181
* @see {@link https://testing-library.com/docs/user-event/convenience/#tab} testing-library API
8282
*/
8383
tab: (options?: UserEventTabOptions) => Promise<void>
84+
/**
85+
* Hovers over an element. Uses provider's API under the hood.
86+
* @see {@link https://playwright.dev/docs/api/class-locator#locator-hover} Playwright API
87+
* @see {@link https://webdriver.io/docs/api/element/moveTo/} WebdriverIO API
88+
* @see {@link https://testing-library.com/docs/user-event/convenience/#hover} testing-library API
89+
*/
90+
hover: (element: Element, options?: UserEventHoverOptions) => Promise<void>
91+
/**
92+
* Moves cursor position to the body element. Uses provider's API under the hood.
93+
* By default, the cursor position is in the center (in webdriverio) or in some visible place (in playwright)
94+
* of the body element, so if the current element is already there, this will have no effect.
95+
* @see {@link https://playwright.dev/docs/api/class-locator#locator-hover} Playwright API
96+
* @see {@link https://webdriver.io/docs/api/element/moveTo/} WebdriverIO API
97+
* @see {@link https://testing-library.com/docs/user-event/convenience/#hover} testing-library API
98+
*/
99+
unhover: (element: Element, options?: UserEventHoverOptions) => Promise<void>
84100
/**
85101
* Fills an input element with text. This will remove any existing text in the input before typing the new text.
86102
* Uses provider's API under the hood.
@@ -93,22 +109,20 @@ export interface UserEvent {
93109
* @see {@link https://webdriver.io/docs/api/element/setValue} WebdriverIO API
94110
* @see {@link https://testing-library.com/docs/user-event/utility/#type} testing-library API
95111
*/
96-
fill: (element: Element, text: string) => Promise<void>
112+
fill: (element: Element, text: string, options?: UserEventFillOptions) => Promise<void>
97113
}
98114

99-
export interface UserEventClickOptions {
100-
[key: string]: any
101-
}
115+
export interface UserEventFillOptions {}
116+
export interface UserEventHoverOptions {}
117+
export interface UserEventClickOptions {}
102118

103119
export interface UserEventTabOptions {
104120
shift?: boolean
105-
[key: string]: any
106121
}
107122

108123
export interface UserEventTypeOptions {
109124
skipClick?: boolean
110125
skipAutoClose?: boolean
111-
[key: string]: any
112126
}
113127

114128
type Platform =

packages/browser/providers/playwright.d.ts

+12
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,15 @@ declare module 'vitest/node' {
1818
context: BrowserContext
1919
}
2020
}
21+
22+
type PWHoverOptions = Parameters<Page['hover']>[1]
23+
type PWClickOptions = Parameters<Page['click']>[1]
24+
type PWFillOptions = Parameters<Page['fill']>[2]
25+
type PWScreenshotOptions = Parameters<Page['screenshot']>[0]
26+
27+
declare module '@vitest/browser/context' {
28+
export interface UserEventHoverOptions extends PWHoverOptions {}
29+
export interface UserEventClickOptions extends PWClickOptions {}
30+
export interface UserEventFillOptions extends PWFillOptions {}
31+
export interface ScreenshotOptions extends PWScreenshotOptions {}
32+
}

packages/browser/src/client/context.ts

+10-2
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,24 @@ export const userEvent: UserEvent = {
6161
const xpath = convertElementToXPath(element)
6262
return triggerCommand('__vitest_clear', xpath)
6363
},
64-
fill(element: Element, text: string) {
64+
fill(element: Element, text: string, options) {
6565
const xpath = convertElementToXPath(element)
66-
return triggerCommand('__vitest_fill', xpath, text)
66+
return triggerCommand('__vitest_fill', xpath, text, options)
6767
},
6868
tab(options: UserEventTabOptions = {}) {
6969
return triggerCommand('__vitest_tab', options)
7070
},
7171
keyboard(text: string) {
7272
return triggerCommand('__vitest_keyboard', text)
7373
},
74+
hover(element: Element) {
75+
const xpath = convertElementToXPath(element)
76+
return triggerCommand('__vitest_hover', xpath)
77+
},
78+
unhover(element: Element) {
79+
const xpath = convertElementToXPath(element.ownerDocument.body)
80+
return triggerCommand('__vitest_hover', xpath)
81+
},
7482
}
7583

7684
const screenshotIds: Record<string, Record<string, string>> = {}

packages/browser/src/node/commands/fill.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,12 @@ export const fill: UserEventCommand<UserEvent['fill']> = async (
77
context,
88
xpath,
99
text,
10+
options = {},
1011
) => {
1112
if (context.provider instanceof PlaywrightBrowserProvider) {
1213
const { frame } = context
1314
const element = frame.locator(`xpath=${xpath}`)
14-
await element.fill(text)
15+
await element.fill(text, options)
1516
}
1617
else if (context.provider instanceof WebdriverBrowserProvider) {
1718
const browser = context.browser
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { UserEvent } from '../../../context'
2+
import { PlaywrightBrowserProvider } from '../providers/playwright'
3+
import { WebdriverBrowserProvider } from '../providers/webdriver'
4+
import type { UserEventCommand } from './utils'
5+
6+
export const hover: UserEventCommand<UserEvent['hover']> = async (
7+
context,
8+
xpath,
9+
options = {},
10+
) => {
11+
if (context.provider instanceof PlaywrightBrowserProvider) {
12+
await context.frame.locator(`xpath=${xpath}`).hover(options)
13+
}
14+
else if (context.provider instanceof WebdriverBrowserProvider) {
15+
const browser = context.browser
16+
const markedXpath = `//${xpath}`
17+
const element = await browser.$(markedXpath)
18+
await element.moveTo(options)
19+
}
20+
else {
21+
throw new TypeError(`Provider "${context.provider.name}" does not support hover`)
22+
}
23+
}

0 commit comments

Comments
 (0)