mirror of
https://github.com/vitest-dev/vitest.git
synced 2025-12-08 18:26:03 +00:00
389 lines
14 KiB
Markdown
389 lines
14 KiB
Markdown
---
|
|
title: Interactivity API | Browser Mode
|
|
---
|
|
|
|
# Interactivity API
|
|
|
|
Vitest implements a subset of [`@testing-library/user-event`](https://testing-library.com/docs/user-event) APIs using [Chrome DevTools Protocol](https://chromedevtools.github.io/devtools-protocol/) or [webdriver](https://www.w3.org/TR/webdriver/) APIs instead of faking events which makes the browser behaviour more reliable and consistent with how users interact with a page.
|
|
|
|
Almost every `userEvent` method inherits its provider options. To see all available options in your IDE, add `webdriver` or `playwright` types to your `tsconfig.json` file:
|
|
|
|
::: code-group
|
|
```json [playwright]
|
|
{
|
|
"compilerOptions": {
|
|
"types": [
|
|
"@vitest/browser/providers/playwright"
|
|
]
|
|
}
|
|
}
|
|
```
|
|
```json [webdriverio]
|
|
{
|
|
"compilerOptions": {
|
|
"types": [
|
|
"@vitest/browser/providers/webdriverio"
|
|
]
|
|
}
|
|
}
|
|
```
|
|
:::
|
|
|
|
## userEvent.click
|
|
|
|
- **Type:** `(element: Element, options?: UserEventClickOptions) => Promise<void>`
|
|
|
|
Click on an element. Inherits provider's options. Please refer to your provider's documentation for detailed explanation about how this method works.
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('clicks on an element', async () => {
|
|
const logo = screen.getByRole('img', { name: /logo/ })
|
|
|
|
await userEvent.click(logo)
|
|
})
|
|
```
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.click` API](https://playwright.dev/docs/api/class-locator#locator-click)
|
|
- [WebdriverIO `element.click` API](https://webdriver.io/docs/api/element/click/)
|
|
- [testing-library `click` API](https://testing-library.com/docs/user-event/convenience/#click)
|
|
|
|
## userEvent.dblClick
|
|
|
|
- **Type:** `(element: Element, options?: UserEventDoubleClickOptions) => Promise<void>`
|
|
|
|
Triggers a double click event on an element.
|
|
|
|
Please refer to your provider's documentation for detailed explanation about how this method works.
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('triggers a double click on an element', async () => {
|
|
const logo = screen.getByRole('img', { name: /logo/ })
|
|
|
|
await userEvent.dblClick(logo)
|
|
})
|
|
```
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.dblclick` API](https://playwright.dev/docs/api/class-locator#locator-dblclick)
|
|
- [WebdriverIO `element.doubleClick` API](https://webdriver.io/docs/api/element/doubleClick/)
|
|
- [testing-library `dblClick` API](https://testing-library.com/docs/user-event/convenience/#dblClick)
|
|
|
|
## userEvent.tripleClick
|
|
|
|
- **Type:** `(element: Element, options?: UserEventTripleClickOptions) => Promise<void>`
|
|
|
|
Triggers a triple click event on an element. Since there is no `tripleclick` in browser api, this method will fire three click events in a row, and so you must check [click event detail](https://developer.mozilla.org/en-US/docs/Web/API/Element/click_event#usage_notes) to filter the event: `evt.detail === 3`.
|
|
|
|
Please refer to your provider's documentation for detailed explanation about how this method works.
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('triggers a triple click on an element', async () => {
|
|
const logo = screen.getByRole('img', { name: /logo/ })
|
|
let tripleClickFired = false
|
|
logo.addEventListener('click', (evt) => {
|
|
if (evt.detail === 3) {
|
|
tripleClickFired = true
|
|
}
|
|
})
|
|
|
|
await userEvent.tripleClick(logo)
|
|
expect(tripleClickFired).toBe(true)
|
|
})
|
|
```
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.click` API](https://playwright.dev/docs/api/class-locator#locator-click): implemented via `click` with `clickCount: 3` .
|
|
- [WebdriverIO `browser.action` API](https://webdriver.io/docs/api/browser/action/): implemented via actions api with `move` plus three `down + up + pause` events in a row
|
|
- [testing-library `tripleClick` API](https://testing-library.com/docs/user-event/convenience/#tripleClick)
|
|
|
|
## userEvent.fill
|
|
|
|
- **Type:** `(element: Element, text: string) => Promise<void>`
|
|
|
|
Fill an `input/textarea/conteneditable` element with text. This will remove any existing text in the input before typing the new value.
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('update input', async () => {
|
|
const input = screen.getByRole('input')
|
|
|
|
await userEvent.fill(input, 'foo') // input.value == foo
|
|
await userEvent.fill(input, '{{a[[') // input.value == {{a[[
|
|
await userEvent.fill(input, '{Shift}') // input.value == {Shift}
|
|
})
|
|
```
|
|
|
|
::: tip
|
|
This API is faster than using [`userEvent.type`](#userevent-type) or [`userEvent.keyboard`](#userevent-keyboard), but it **doesn't support** [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard) (e.g., `{Shift}{selectall}`).
|
|
|
|
We recommend using this API over [`userEvent.type`](#userevent-type) in situations when you don't need to enter special characters.
|
|
:::
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.fill` API](https://playwright.dev/docs/api/class-locator#locator-fill)
|
|
- [WebdriverIO `element.setValue` API](https://webdriver.io/docs/api/element/setValue)
|
|
- [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type)
|
|
|
|
## userEvent.keyboard
|
|
|
|
- **Type:** `(text: string) => Promise<void>`
|
|
|
|
The `userEvent.keyboard` allows you to trigger keyboard strokes. If any input has a focus, it will type characters into that input. Otherwise, it will trigger keyboard events on the currently focused element (`document.body` if there are no focused elements).
|
|
|
|
This API supports [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard).
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('trigger keystrokes', async () => {
|
|
await userEvent.keyboard('foo') // translates to: f, o, o
|
|
await userEvent.keyboard('{{a[[') // translates to: {, a, [
|
|
await userEvent.keyboard('{Shift}{f}{o}{o}') // translates to: Shift, f, o, o
|
|
await userEvent.keyboard('{a>5}') // press a without releasing it and trigger 5 keydown
|
|
await userEvent.keyboard('{a>5/}') // press a for 5 keydown and then release it
|
|
})
|
|
```
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.press` API](https://playwright.dev/docs/api/class-locator#locator-press)
|
|
- [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source)
|
|
- [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type)
|
|
|
|
## userEvent.tab
|
|
|
|
- **Type:** `(options?: UserEventTabOptions) => Promise<void>`
|
|
|
|
Sends a `Tab` key event. This is a shorthand for `userEvent.keyboard('{tab}')`.
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('tab works', async () => {
|
|
const [input1, input2] = screen.getAllByRole('input')
|
|
|
|
expect(input1).toHaveFocus()
|
|
|
|
await userEvent.tab()
|
|
|
|
expect(input2).toHaveFocus()
|
|
|
|
await userEvent.tab({ shift: true })
|
|
|
|
expect(input1).toHaveFocus()
|
|
})
|
|
```
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.press` API](https://playwright.dev/docs/api/class-locator#locator-press)
|
|
- [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source)
|
|
- [testing-library `tab` API](https://testing-library.com/docs/user-event/convenience/#tab)
|
|
|
|
## userEvent.type
|
|
|
|
- **Type:** `(element: Element, text: string, options?: UserEventTypeOptions) => Promise<void>`
|
|
|
|
::: warning
|
|
If you don't rely on [special characters](https://testing-library.com/docs/user-event/keyboard) (e.g., `{shift}` or `{selectall}`), it is recommended to use [`userEvent.fill`](#userevent-fill) instead.
|
|
:::
|
|
|
|
The `type` method implements `@testing-library/user-event`'s [`type`](https://testing-library.com/docs/user-event/utility/#type) utility built on top of [`keyboard`](https://testing-library.com/docs/user-event/keyboard) API.
|
|
|
|
This function allows you to type characters into an input/textarea/conteneditable element. It supports [user-event `keyboard` syntax](https://testing-library.com/docs/user-event/keyboard).
|
|
|
|
If you just need to press characters without an input, use [`userEvent.keyboard`](#userevent-keyboard) API.
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('update input', async () => {
|
|
const input = screen.getByRole('input')
|
|
|
|
await userEvent.type(input, 'foo') // input.value == foo
|
|
await userEvent.type(input, '{{a[[') // input.value == foo{a[
|
|
await userEvent.type(input, '{Shift}') // input.value == foo{a[
|
|
})
|
|
```
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.press` API](https://playwright.dev/docs/api/class-locator#locator-press)
|
|
- [WebdriverIO `action('key')` API](https://webdriver.io/docs/api/browser/action#key-input-source)
|
|
- [testing-library `type` API](https://testing-library.com/docs/user-event/utility/#type)
|
|
|
|
## userEvent.clear
|
|
|
|
- **Type:** `(element: Element) => Promise<void>`
|
|
|
|
This method clears the input element content.
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('clears input', async () => {
|
|
const input = screen.getByRole('input')
|
|
|
|
await userEvent.fill(input, 'foo')
|
|
expect(input).toHaveValue('foo')
|
|
|
|
await userEvent.clear(input)
|
|
expect(input).toHaveValue('')
|
|
})
|
|
```
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.clear` API](https://playwright.dev/docs/api/class-locator#locator-clear)
|
|
- [WebdriverIO `element.clearValue` API](https://webdriver.io/docs/api/element/clearValue)
|
|
- [testing-library `clear` API](https://testing-library.com/docs/user-event/utility/#clear)
|
|
|
|
## userEvent.selectOptions
|
|
|
|
- **Type:** `(element: Element, values: HTMLElement | HTMLElement[] | string | string[], options?: UserEventSelectOptions) => Promise<void>`
|
|
|
|
The `userEvent.selectOptions` allows selecting a value in a `<select>` element.
|
|
|
|
::: warning
|
|
If select element doesn't have [`multiple`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/select#attr-multiple) attribute, Vitest will select only the first element in the array.
|
|
|
|
Unlike `@testing-library`, Vitest doesn't support [listbox](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/listbox_role) at the moment, but we plan to add support for it in the future.
|
|
:::
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('clears input', async () => {
|
|
const select = screen.getByRole('select')
|
|
|
|
await userEvent.selectOptions(select, 'Option 1')
|
|
expect(select).toHaveValue('option-1')
|
|
|
|
await userEvent.selectOptions(select, 'option-1')
|
|
expect(select).toHaveValue('option-1')
|
|
|
|
await userEvent.selectOptions(select, [
|
|
screen.getByRole('option', { name: 'Option 1' }),
|
|
screen.getByRole('option', { name: 'Option 2' }),
|
|
])
|
|
expect(select).toHaveValue(['option-1', 'option-2'])
|
|
})
|
|
```
|
|
|
|
::: warning
|
|
`webdriverio` provider doesn't support selecting multiple elements because it doesn't provide API to do so.
|
|
:::
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.selectOption` API](https://playwright.dev/docs/api/class-locator#locator-select-option)
|
|
- [WebdriverIO `element.selectByIndex` API](https://webdriver.io/docs/api/element/selectByIndex)
|
|
- [testing-library `selectOptions` API](https://testing-library.com/docs/user-event/utility/#-selectoptions-deselectoptions)
|
|
|
|
## userEvent.hover
|
|
|
|
- **Type:** `(element: Element, options?: UserEventHoverOptions) => Promise<void>`
|
|
|
|
This method moves the cursor position to the selected element. Please refer to your provider's documentation for detailed explanation about how this method works.
|
|
|
|
::: warning
|
|
If you are using `webdriverio` provider, the cursor will move to the center of the element by default.
|
|
|
|
If you are using `playwright` provider, the cursor moves to "some" visible point of the element.
|
|
:::
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('hovers logo element', async () => {
|
|
const logo = screen.getByRole('img', { name: /logo/ })
|
|
|
|
await userEvent.hover(logo)
|
|
})
|
|
```
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.hover` API](https://playwright.dev/docs/api/class-locator#locator-hover)
|
|
- [WebdriverIO `element.moveTo` API](https://webdriver.io/docs/api/element/moveTo/)
|
|
- [testing-library `hover` API](https://testing-library.com/docs/user-event/convenience/#hover)
|
|
|
|
## userEvent.unhover
|
|
|
|
- **Type:** `(element: Element, options?: UserEventHoverOptions) => Promise<void>`
|
|
|
|
This works the same as [`userEvent.hover`](#userevent-hover), but moves the cursor to the `document.body` element instead.
|
|
|
|
::: warning
|
|
By default, the cursor position is in the center (in `webdriverio` provider) or in "some" visible place (in `playwright` provider) of the body element, so if the currently hovered element is already in the same position, this method will have no effect.
|
|
:::
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
|
|
test('unhover logo element', async () => {
|
|
const logo = screen.getByRole('img', { name: /logo/ })
|
|
|
|
await userEvent.unhover(logo)
|
|
})
|
|
```
|
|
|
|
References:
|
|
|
|
- [Playwright `locator.hover` API](https://playwright.dev/docs/api/class-locator#locator-hover)
|
|
- [WebdriverIO `element.moveTo` API](https://webdriver.io/docs/api/element/moveTo/)
|
|
- [testing-library `hover` API](https://testing-library.com/docs/user-event/convenience/#hover)
|
|
|
|
## userEvent.dragAndDrop
|
|
|
|
- **Type:** `(source: Element, target: Element, options?: UserEventDragAndDropOptions) => Promise<void>`
|
|
|
|
Drags the source element on top of the target element. Don't forget that the `source` element has to have the `draggable` attribute set to `true`.
|
|
|
|
```ts
|
|
import { userEvent } from '@vitest/browser/context'
|
|
import { screen } from '@testing-library/dom'
|
|
import '@testing-library/jest-dom' // adds support for "toHaveTextContent"
|
|
|
|
test('drag and drop works', async () => {
|
|
const source = screen.getByRole('img', { name: /logo/ })
|
|
const target = screen.getByTestId('logo-target')
|
|
|
|
await userEvent.dragAndDrop(source, target)
|
|
|
|
expect(target).toHaveTextContent('Logo is processed')
|
|
})
|
|
```
|
|
|
|
::: warning
|
|
This API is not supported by the default `preview` provider.
|
|
:::
|
|
|
|
References:
|
|
|
|
- [Playwright `frame.dragAndDrop` API](https://playwright.dev/docs/api/class-frame#frame-drag-and-drop)
|
|
- [WebdriverIO `element.dragAndDrop` API](https://webdriver.io/docs/api/element/dragAndDrop/)
|