diff --git a/docs/ecosystem-user-event.mdx b/docs/ecosystem-user-event.mdx index ea66666cd..e94034f4c 100644 --- a/docs/ecosystem-user-event.mdx +++ b/docs/ecosystem-user-event.mdx @@ -1,6 +1,6 @@ --- id: ecosystem-user-event -title: user-event +title: user-event v13 --- import Tabs from '@theme/Tabs' @@ -10,6 +10,10 @@ import TabItem from '@theme/TabItem' advanced simulation of browser interactions than the built-in [`fireEvent`](dom-testing-library/api-events.mdx#fireevent) method. +> This page describes `user-event@13.5.0`. +If you are starting or actively working on a project, +we recommend to use [`user-event@14.0.0-beta`](user-event/intro) instead, as it includes important bug fixes and new features. + ## Installation diff --git a/docs/user-event/api-clipboard.mdx b/docs/user-event/api-clipboard.mdx new file mode 100644 index 000000000..7e1188971 --- /dev/null +++ b/docs/user-event/api-clipboard.mdx @@ -0,0 +1,49 @@ +--- +id: clipboard +title: Clipboard +--- + +Note that the +[Clipboard API](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard) is +usually not available outside of secure context. +To enable testing of workflows involving the clipboard, +[`userEvent.setup()`](setup) replaces `window.navigator.clipboard` with a stub. + +## copy() + +```ts +copy(): Promise +``` + +Copy the current selection. + +If [`writeToClipboard`](options#writetoclipboard) is `true`, this will also +write the data to the `Clipboard`. + +## cut() + +```ts +cut(): Promise +``` + +Cut the current selection. + +If [`writeToClipboard`](options#writetoclipboard) is `true`, this will also +write the data to the `Clipboard`. + +When performed in editable context, it removes the selected content from the +document. + +## paste() + +```ts +paste(clipboardData?: DataTransfer|string): Promise +``` + +Paste data into the document. + +When called without `clipboardData`, the content to be pasted is read from the +`Clipboard`. + +When performed in editable context, the pasted content is inserted into to the +document. diff --git a/docs/user-event/api-convenience.mdx b/docs/user-event/api-convenience.mdx new file mode 100644 index 000000000..ba14fc0c4 --- /dev/null +++ b/docs/user-event/api-convenience.mdx @@ -0,0 +1,83 @@ +--- +id: convenience +title: Convenience APIs +--- + +The following APIs are shortcuts for equivalent calls to the underlying +[`pointer()`](pointer) and [`keyboard()`](keyboard) APIs. + +## Clicks + +### click() + +```ts +click(element: Element): Promise +``` + +```js +pointer([{target: element}, {keys: '[MouseLeft]', target: element}]) +``` + +The first action might be skipped per [`skipHover`](options#skiphover). + +### dblClick() + +```ts +dblClick(element: Element): Promise +``` + +```js +pointer([{target: element}, {keys: '[MouseLeft][MouseLeft]', target: element}]) +``` + +### tripleClick() + +```ts +tripleClick(element: Element): Promise +``` + +```js +pointer([ + {target: element}, + {keys: '[MouseLeft][MouseLeft][MouseLeft]', target: element}, +]) +``` + +## Mouse movement + +### hover() + +```ts +hover(element: Element): Promise +``` + +```js +pointer({target: element}) +``` + +### unhover() + +```ts +hover(element: Element): Promise +``` + +```js +pointer({target: element.ownerDocument.body}) +``` + +## Keyboard + +### tab() + +```ts +tab(options: {shift?: boolean}) +``` + +```js +// without shift +keyboard('{Tab}') +// with shift=true +keyboard('{Shift>}{Tab}{/Shift}') +// with shift=false +keyboard('[/ShiftLeft][/ShiftRight]{Tab}') +``` diff --git a/docs/user-event/api-keyboard.mdx b/docs/user-event/api-keyboard.mdx new file mode 100644 index 000000000..1bbbd333e --- /dev/null +++ b/docs/user-event/api-keyboard.mdx @@ -0,0 +1,83 @@ +--- +id: keyboard +title: Keyboard +--- + +```ts +keyboard(input: KeyboardInput): Promise +``` + +The `keyboard` API allows to simulate interactions with a keyboard. It accepts a +`string` describing the key actions. + +Keystrokes can be described: + +- Per printable character + ```js + keyboard('foo') // translates to: f, o, o + ``` + The brackets `{` and `[` are used as special characters and can be referenced + by doubling them. + ```js + keyboard('{{a[[') // translates to: {, a, [ + ``` +- Per + [KeyboardEvent.key](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key) + + ```js + keyboard('{Shift}{f}{o}{o}') // translates to: Shift, f, o, o + ``` + + This does not keep any key pressed. So `Shift` will be lifted before pressing + `f`. + + Characters with special meaning inside the key descriptor can be escaped by + prefixing them with a backslash `\`. + + ```js + keyboard('{}}') // translates to: } + ``` + +- Per + [KeyboardEvent.code](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code) + ```js + keyboard('[ShiftLeft][KeyF][KeyO][KeyO]') // translates to: Shift, f, o, o + ``` + +Keys can be kept pressed by adding a `>` to the end of the descriptor. +If this should result in repeated `keydown` events, you can add the number of +repetitions. +If the key should also be released after this, add a slash `/` to the end of the +descriptor. + +```js +keyboard('{a>}') // press a without releasing it +keyboard('{a>5}') // press a without releasing it and trigger 5 keydown +keyboard('{a>5/}') // press a for 5 keydown and then release it +``` + +A previously pressed key can be lifted by prefixing the descriptor with `/`. + +```js +keyboard('{/a}') // release a previously pressed a +``` + +This allows to simulate key combinations. + +```js +keyboard('{Shift>}A{/Shift}') // translates to: Shift(down), A, Shift(up) +``` + +The mapping of `key` to `code` is performed by a +[default key map](https://github.com/testing-library/user-event/blob/beta/src/keyboard/keyMap.ts) +portraying a "default" US-keyboard. You can provide your own local keyboard +mapping per [`keyboardMap`](options#keyboardmap) option. + +Currently the different `key` meanings of single keys are treated as different +keys. + +> Future versions might try to interpolate the modifiers needed to reach a +> printable key on the keyboard. E.g. Automatically pressing `{Shift}` when +> CapsLock is not active and `A` is referenced. If you don't wish this behavior, +> you can deactivate the [`autoModify`](options#automodify) option to opt out +> of this non-breaking change. diff --git a/docs/user-event/api-pointer.mdx b/docs/user-event/api-pointer.mdx new file mode 100644 index 000000000..3e78a02f6 --- /dev/null +++ b/docs/user-event/api-pointer.mdx @@ -0,0 +1,138 @@ +--- +id: pointer +title: Pointer +--- + +```ts +pointer(input: PointerInput): Promise +``` + +The `pointer` API allows to simulate interactions with pointer devices. It +accepts a single pointer action or an array of them. + +```ts +type PointerInput = PointerActionInput | Array +``` + +> Our primary target audience tests per `jest` in a `jsdom` environment and there +is no layout in `jsdom`. This means that different from your browser the +elements don't exist in a specific position, layer and size. +We don't try to determine if the pointer action you describe is possible at that +position in your layout. + +## Pointer action + +There are two types of actions: press and move. + +### Pressing a button or touching the screen + +A pointer action is a press action if it defines a key to be pressed, to be +released, or both. + +```js +pointer({keys: '[MouseLeft]'}) +``` + +You can declare multiple press actions (on the same position) at once which will +be resolved to multiple actions internally. If you don't need to declare any +other properties you can also just supply the `keys` string. + +```js +pointer({keys: '[MouseLeft][MouseRight]'}) +// or +pointer('[MouseLeft][MouseRight]') +``` + +In order to press a button without releasing it, the button name is suffixed with +`>`. +For just releasing a previously pressed button, the tag is started with `/`. + +```js +pointer('[MouseLeft>]') // press the left mouse button +pointer('[/MouseLeft]') // release the left mouse button +``` + +Which buttons are available depends on the [`pointerMap`](options#pointermap). + +### Moving a pointer + +Every pointer action that is not a press action describes a pointer movement. + +You can declare which pointer is moved per `pointerName` property. This defaults +to `mouse`. + +Note that the `mouse` pointer (`pointerId: 1`) is also the only pointer that +always exists and has a position. A `touch` pointer only exists while the screen +is touched and receives a new `pointerId` every time. For these pointers, we use +the "button" name from the press action as `pointerName`. + +```js +pointer([ + // touch the screen at element1 + {keys: '[TouchA>]', target: element1}, + // move the touch pointer to element2 + {pointerName: 'TouchA', target: element2}, + // release the touch pointer at the last position (element2) + {keys: '[/TouchA]'}, +]) +``` + +## Pointer position + +### PointerTarget + +```ts +interface PointerTarget { + target: Element + coords?: PointerCoords +} +``` + +The `PointerTarget` props allows to describe the position of the pointer on the +document. +The `coords` you provide are applied as-is to the resulting +[`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent) and +can be omitted. +The `target` should be the element receiving the pointer input in the browser. +This is the topmost element that can receive pointer event at those coordinates. + +### SelectionTarget + +```ts +interface SelectionTarget { + node?: Node + offset?: number +} +``` + +Pointer actions can alter the selection in the document. +In the browser every pointer position corresponds with a DOM position. This is a +DOM node and a DOM offset which usually translates to the character closest to +the pointer position. +As all character in a no-layout environment are in the same layout position we +assume a pointer position to be closest to the last descendant of the pointer +`target`. + +If you provide `offset`, we assume the pointer position to be closest to the +`offset-th` character of `target.textContent`. + +If you also provide `node`, we treat `node` and `offset` as the exact DOM +position to be used for any selection. + +```jsx +// element:
foobar
+// | marking the cursor. +// [ ] marking a selection. + +pointer({target: element, offset: 2, keys: '[MouseLeft]'}) +// =>
fo|obar
+ +pointer([ + {target: element, offset: 2, keys: '[MouseLeft>]'}, + {offset: 5}, +]) +// =>
fo[oba]r
+ +pointer({target: element, node: element, offset: 1, keys: '[MouseLeft]'}) +// =>
foo|bar
+``` diff --git a/docs/user-event/api-utility.mdx b/docs/user-event/api-utility.mdx new file mode 100644 index 000000000..b119d1fb6 --- /dev/null +++ b/docs/user-event/api-utility.mdx @@ -0,0 +1,190 @@ +--- +id: utility +title: Utility APIs +--- + +The following APIs don't have one-to-one equivalents in a real user +interaction. +Their behavior is therefore an interpretation how the "perceived" user +interaction might be translated to actual events on the DOM. + +## clear() + +```ts +clear(element: Element): Promise +``` + +This API can be used to easily clear an editable element. + +1. Focus element +1. Select all contents as per browser menu +1. Delete contents as per browser menu + +```jsx +test('clear', async () => { + render(