Skip to content

Commit

Permalink
feat: add hover/unhover, expose option types for playwright
Browse files Browse the repository at this point in the history
  • Loading branch information
sheremet-va committed Jun 13, 2024
1 parent f1e8574 commit b45d3a2
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 11 deletions.
20 changes: 18 additions & 2 deletions docs/guide/browser.md
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,22 @@ export const userEvent: {
* @see {@link https://testing-library.com/docs/user-event/convenience/#tab} testing-library API
*/
tab: (options?: UserEventTabOptions) => Promise<void>
/**
* Hovers over an element. Uses provider's API under the hood.
* @see {@link https://playwright.dev/docs/api/class-locator#locator-hover} Playwright API
* @see {@link https://webdriver.io/docs/api/element/moveTo/} WebdriverIO API
* @see {@link https://testing-library.com/docs/user-event/convenience/#hover} testing-library API
*/
hover: (element: Element, options?: UserEventHoverOptions) => Promise<void>
/**
* Moves cursor position to the body element. Uses provider's API under the hood.
* By default, the cursor position is in the center (in webdriverio) or in some visible place (in playwright)
* of the body element, so if the current element is already there, this will have no effect.
* @see {@link https://playwright.dev/docs/api/class-locator#locator-hover} Playwright API
* @see {@link https://webdriver.io/docs/api/element/moveTo/} WebdriverIO API
* @see {@link https://testing-library.com/docs/user-event/convenience/#hover} testing-library API
*/
unhover: (element: Element, options?: UserEventHoverOptions) => Promise<void>
/**
* Fills an input element with text. This will remove any existing text in the input before typing the new text.
* Uses provider's API under the hood.
Expand All @@ -271,7 +287,7 @@ export const userEvent: {
* @see {@link https://webdriver.io/docs/api/element/setValue} WebdriverIO API
* @see {@link https://testing-library.com/docs/user-event/utility/#type} testing-library API
*/
fill: (element: Element, text: string) => Promise<void>
fill: (element: Element, text: string, options?: UserEventFillOptions) => Promise<void>
}

/**
Expand Down Expand Up @@ -415,7 +431,7 @@ export const myCommand = defineCommand(async (ctx, arg1, arg2) => {
```

::: tip
If you are using TypeScript, don't forget to add `@vitest/browser/providers/playwright` to your `tsconfig` "compilerOptions.types" field to get autocompletion:
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:

```json
{
Expand Down
26 changes: 20 additions & 6 deletions packages/browser/context.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,22 @@ export interface UserEvent {
* @see {@link https://testing-library.com/docs/user-event/convenience/#tab} testing-library API
*/
tab: (options?: UserEventTabOptions) => Promise<void>
/**
* Hovers over an element. Uses provider's API under the hood.
* @see {@link https://playwright.dev/docs/api/class-locator#locator-hover} Playwright API
* @see {@link https://webdriver.io/docs/api/element/moveTo/} WebdriverIO API
* @see {@link https://testing-library.com/docs/user-event/convenience/#hover} testing-library API
*/
hover: (element: Element, options?: UserEventHoverOptions) => Promise<void>
/**
* Moves cursor position to the body element. Uses provider's API under the hood.
* By default, the cursor position is in the center (in webdriverio) or in some visible place (in playwright)
* of the body element, so if the current element is already there, this will have no effect.
* @see {@link https://playwright.dev/docs/api/class-locator#locator-hover} Playwright API
* @see {@link https://webdriver.io/docs/api/element/moveTo/} WebdriverIO API
* @see {@link https://testing-library.com/docs/user-event/convenience/#hover} testing-library API
*/
unhover: (element: Element, options?: UserEventHoverOptions) => Promise<void>
/**
* Fills an input element with text. This will remove any existing text in the input before typing the new text.
* Uses provider's API under the hood.
Expand All @@ -93,22 +109,20 @@ export interface UserEvent {
* @see {@link https://webdriver.io/docs/api/element/setValue} WebdriverIO API
* @see {@link https://testing-library.com/docs/user-event/utility/#type} testing-library API
*/
fill: (element: Element, text: string) => Promise<void>
fill: (element: Element, text: string, options?: UserEventFillOptions) => Promise<void>
}

export interface UserEventClickOptions {
[key: string]: any
}
export interface UserEventFillOptions {}
export interface UserEventHoverOptions {}
export interface UserEventClickOptions {}

export interface UserEventTabOptions {
shift?: boolean
[key: string]: any
}

export interface UserEventTypeOptions {
skipClick?: boolean
skipAutoClose?: boolean
[key: string]: any
}

type Platform =
Expand Down
12 changes: 12 additions & 0 deletions packages/browser/providers/playwright.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,15 @@ declare module 'vitest/node' {
context: BrowserContext
}
}

type PWHoverOptions = Parameters<Page['hover']>[1]
type PWClickOptions = Parameters<Page['click']>[1]
type PWFillOptions = Parameters<Page['fill']>[2]
type PWScreenshotOptions = Parameters<Page['screenshot']>[0]

declare module '@vitest/browser/context' {
export interface UserEventHoverOptions extends PWHoverOptions {}
export interface UserEventClickOptions extends PWClickOptions {}
export interface UserEventFillOptions extends PWFillOptions {}
export interface ScreenshotOptions extends PWScreenshotOptions {}
}
12 changes: 10 additions & 2 deletions packages/browser/src/client/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,16 +61,24 @@ export const userEvent: UserEvent = {
const xpath = convertElementToXPath(element)
return triggerCommand('__vitest_clear', xpath)
},
fill(element: Element, text: string) {
fill(element: Element, text: string, options) {
const xpath = convertElementToXPath(element)
return triggerCommand('__vitest_fill', xpath, text)
return triggerCommand('__vitest_fill', xpath, text, options)
},
tab(options: UserEventTabOptions = {}) {
return triggerCommand('__vitest_tab', options)
},
keyboard(text: string) {
return triggerCommand('__vitest_keyboard', text)
},
hover(element: Element) {
const xpath = convertElementToXPath(element)
return triggerCommand('__vitest_hover', xpath)
},
unhover(element: Element) {
const xpath = convertElementToXPath(element.ownerDocument.body)
return triggerCommand('__vitest_hover', xpath)
},
}

const screenshotIds: Record<string, Record<string, string>> = {}
Expand Down
3 changes: 2 additions & 1 deletion packages/browser/src/node/commands/fill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ export const fill: UserEventCommand<UserEvent['fill']> = async (
context,
xpath,
text,
options = {},
) => {
if (context.provider instanceof PlaywrightBrowserProvider) {
const { frame } = context
const element = frame.locator(`xpath=${xpath}`)
await element.fill(text)
await element.fill(text, options)
}
else if (context.provider instanceof WebdriverBrowserProvider) {
const browser = context.browser
Expand Down
23 changes: 23 additions & 0 deletions packages/browser/src/node/commands/hover.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import type { UserEvent } from '../../../context'
import { PlaywrightBrowserProvider } from '../providers/playwright'
import { WebdriverBrowserProvider } from '../providers/webdriver'
import type { UserEventCommand } from './utils'

export const hover: UserEventCommand<UserEvent['hover']> = async (
context,
xpath,
options = {},
) => {
if (context.provider instanceof PlaywrightBrowserProvider) {
await context.frame.locator(`xpath=${xpath}`).hover(options)
}
else if (context.provider instanceof WebdriverBrowserProvider) {
const browser = context.browser
const markedXpath = `//${xpath}`
const element = await browser.$(markedXpath)
await element.moveTo(options)
}
else {
throw new TypeError(`Provider "${context.provider.name}" does not support hover`)
}
}

0 comments on commit b45d3a2

Please sign in to comment.