mirror of
https://github.com/idanoo/GoScrobble
synced 2025-07-01 13:42:20 +00:00
0.2.0 - Mid migration
This commit is contained in:
parent
139e6a915e
commit
7e38fdbd7d
42393 changed files with 5358157 additions and 62 deletions
5
web/node_modules/@testing-library/user-event/CHANGELOG.md
generated
vendored
Normal file
5
web/node_modules/@testing-library/user-event/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
# CHANGELOG
|
||||
|
||||
The changelog is automatically updated using
|
||||
[semantic-release](https://github.com/semantic-release/semantic-release). You
|
||||
can see it on the [releases page](../../releases).
|
20
web/node_modules/@testing-library/user-event/LICENSE
generated
vendored
Normal file
20
web/node_modules/@testing-library/user-event/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
Copyright (c) 2020 Giorgio Polvara
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
737
web/node_modules/@testing-library/user-event/README.md
generated
vendored
Normal file
737
web/node_modules/@testing-library/user-event/README.md
generated
vendored
Normal file
|
@ -0,0 +1,737 @@
|
|||
<div align="center">
|
||||
<h1>@testing-library/user-event</h1>
|
||||
|
||||
<a href="https://www.joypixels.com/profiles/emoji/1f415">
|
||||
<img
|
||||
height="80"
|
||||
width="80"
|
||||
alt="dog"
|
||||
src="https://raw.githubusercontent.com/testing-library/user-event/master/other/dog.png"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<p>Fire events the same way the user does</p>
|
||||
</div>
|
||||
|
||||
---
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[![Build Status][build-badge]][build]
|
||||
[![Code Coverage][coverage-badge]][coverage]
|
||||
[![version][version-badge]][package]
|
||||
[![downloads][downloads-badge]][npmtrends]
|
||||
[![MIT License][license-badge]][license]
|
||||
[![All Contributors][all-contributors-badge]](#contributors)
|
||||
[![PRs Welcome][prs-badge]][prs]
|
||||
[![Code of Conduct][coc-badge]][coc]
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
## The problem
|
||||
|
||||
From
|
||||
[testing-library/dom-testing-library#107](https://github.com/testing-library/dom-testing-library/issues/107):
|
||||
|
||||
> [...] it is becoming apparent the need to express user actions on a web page
|
||||
> using a higher-level abstraction than `fireEvent`
|
||||
|
||||
## The solution
|
||||
|
||||
`user-event` tries to simulate the real events that would happen in the browser
|
||||
as the user interacts with it. For example `userEvent.click(checkbox)` would
|
||||
change the state of the checkbox.
|
||||
|
||||
**The library is still a work in progress and any help is appreciated.**
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
||||
- [Installation](#installation)
|
||||
- [API](#api)
|
||||
- [`click(element, eventInit, options)`](#clickelement-eventinit-options)
|
||||
- [`dblClick(element, eventInit, options)`](#dblclickelement-eventinit-options)
|
||||
- [`type(element, text, [options])`](#typeelement-text-options)
|
||||
- [`upload(element, file, [{ clickInit, changeInit }])`](#uploadelement-file--clickinit-changeinit-)
|
||||
- [`clear(element)`](#clearelement)
|
||||
- [`selectOptions(element, values)`](#selectoptionselement-values)
|
||||
- [`deselectOptions(element, values)`](#deselectoptionselement-values)
|
||||
- [`tab({shift, focusTrap})`](#tabshift-focustrap)
|
||||
- [`hover(element)`](#hoverelement)
|
||||
- [`unhover(element)`](#unhoverelement)
|
||||
- [`paste(element, text, eventInit, options)`](#pasteelement-text-eventinit-options)
|
||||
- [`specialChars`](#specialchars)
|
||||
- [Issues](#issues)
|
||||
- [🐛 Bugs](#-bugs)
|
||||
- [💡 Feature Requests](#-feature-requests)
|
||||
- [Contributors ✨](#contributors-)
|
||||
- [LICENSE](#license)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
## Installation
|
||||
|
||||
With NPM:
|
||||
|
||||
```sh
|
||||
npm install @testing-library/user-event @testing-library/dom --save-dev
|
||||
```
|
||||
|
||||
With Yarn:
|
||||
|
||||
```sh
|
||||
yarn add @testing-library/user-event @testing-library/dom --dev
|
||||
```
|
||||
|
||||
Now simply import it in your tests:
|
||||
|
||||
```js
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
// or
|
||||
|
||||
const {default: userEvent} = require('@testing-library/user-event')
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
Note: All userEvent methods are synchronous with one exception: when `delay`
|
||||
with `userEvent.type` as described below). We also discourage using `userEvent`
|
||||
inside `before/after` blocks at all, for important reasons described in
|
||||
["Avoid Nesting When You're Testing"](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing).
|
||||
|
||||
### `click(element, eventInit, options)`
|
||||
|
||||
Clicks `element`, depending on what `element` is it can have different side
|
||||
effects.
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
test('click', () => {
|
||||
render(
|
||||
<div>
|
||||
<label htmlFor="checkbox">Check</label>
|
||||
<input id="checkbox" type="checkbox" />
|
||||
</div>,
|
||||
)
|
||||
|
||||
userEvent.click(screen.getByText('Check'))
|
||||
expect(screen.getByLabelText('Check')).toBeChecked()
|
||||
})
|
||||
```
|
||||
|
||||
You can also ctrlClick / shiftClick etc with
|
||||
|
||||
```js
|
||||
userEvent.click(elem, {ctrlKey: true, shiftKey: true})
|
||||
```
|
||||
|
||||
See the
|
||||
[`MouseEvent`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/MouseEvent)
|
||||
constructor documentation for more options.
|
||||
|
||||
Note that `click` will trigger hover events before clicking. To disable this,
|
||||
set the `skipHover` option to `true`.
|
||||
|
||||
### `dblClick(element, eventInit, options)`
|
||||
|
||||
Clicks `element` twice, depending on what `element` is it can have different
|
||||
side effects.
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
test('double click', () => {
|
||||
const onChange = jest.fn()
|
||||
render(<input type="checkbox" onChange={onChange} />)
|
||||
const checkbox = screen.getByRole('checkbox')
|
||||
userEvent.dblClick(checkbox)
|
||||
expect(onChange).toHaveBeenCalledTimes(2)
|
||||
expect(checkbox).not.toBeChecked()
|
||||
})
|
||||
```
|
||||
|
||||
### `type(element, text, [options])`
|
||||
|
||||
Writes `text` inside an `<input>` or a `<textarea>`.
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
test('type', () => {
|
||||
render(<textarea />)
|
||||
|
||||
userEvent.type(screen.getByRole('textbox'), 'Hello,{enter}World!')
|
||||
expect(screen.getByRole('textbox')).toHaveValue('Hello,\nWorld!')
|
||||
})
|
||||
```
|
||||
|
||||
`options.delay` is the number of milliseconds that pass between two characters
|
||||
are typed. By default it's 0. You can use this option if your component has a
|
||||
different behavior for fast or slow users. If you do this, you need to make sure
|
||||
to `await`!
|
||||
|
||||
> To be clear, `userEvent.type` _always_ returns a promise, but you _only_ need
|
||||
> to `await` the promise it returns if you're using the `delay` option.
|
||||
> Otherwise everything runs synchronously and you can ignore the promise.
|
||||
|
||||
`type` will click the element before typing. To disable this, set the
|
||||
`skipClick` option to `true`.
|
||||
|
||||
#### Special characters
|
||||
|
||||
The following special character strings are supported:
|
||||
|
||||
| Text string | Key | Modifier | Notes |
|
||||
| -------------- | ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `{enter}` | Enter | N/A | Will insert a newline character (`<textarea />` only). |
|
||||
| `{space}` | `' '` | N/A | |
|
||||
| `{esc}` | Escape | N/A | |
|
||||
| `{backspace}` | Backspace | N/A | Will delete the previous character (or the characters within the `selectedRange`, see example below). |
|
||||
| `{del}` | Delete | N/A | Will delete the next character (or the characters within the `selectedRange`, see example below) |
|
||||
| `{selectall}` | N/A | N/A | Selects all the text of the element. Note that this will only work for elements that support selection ranges (so, not `email`, `password`, `number`, among others) |
|
||||
| `{arrowleft}` | ArrowLeft | N/A | |
|
||||
| `{arrowright}` | ArrowRight | N/A | |
|
||||
| `{arrowup}` | ArrowUp | N/A | |
|
||||
| `{arrowdown}` | ArrowDown | N/A | |
|
||||
| `{home}` | Home | N/A | |
|
||||
| `{end}` | End | N/A | |
|
||||
| `{shift}` | Shift | `shiftKey` | Does **not** capitalize following characters. |
|
||||
| `{ctrl}` | Control | `ctrlKey` | |
|
||||
| `{alt}` | Alt | `altKey` | |
|
||||
| `{meta}` | OS | `metaKey` | |
|
||||
| `{capslock}` | CapsLock | `modifierCapsLock` | Fires both keydown and keyup when used (simulates a user clicking their "Caps Lock" button to enable caps lock). |
|
||||
|
||||
> **A note about modifiers:** Modifier keys (`{shift}`, `{ctrl}`, `{alt}`,
|
||||
> `{meta}`) will activate their corresponding event modifiers for the duration
|
||||
> of type command or until they are closed (via `{/shift}`, `{/ctrl}`, etc.). If
|
||||
> they are not closed explicitly, then events will be fired to close them
|
||||
> automatically (to disable this, set the `skipAutoClose` option to `true`).
|
||||
|
||||
<!-- space out these notes -->
|
||||
|
||||
> We take the same
|
||||
> [stance as Cypress](https://docs.cypress.io/api/commands/type.html#Modifiers)
|
||||
> in that we do not simulate the behavior that happens with modifier key
|
||||
> combinations as different operating systems function differently in this
|
||||
> regard.
|
||||
|
||||
An example of an usage with a selection range:
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
test('delete characters within the selectedRange', () => {
|
||||
render(
|
||||
<div>
|
||||
<label htmlFor="my-input">Example:</label>
|
||||
<input id="my-input" type="text" value="This is a bad example" />
|
||||
</div>,
|
||||
)
|
||||
const input = screen.getByLabelText(/example/i)
|
||||
input.setSelectionRange(10, 13)
|
||||
userEvent.type(input, '{backspace}good')
|
||||
|
||||
expect(input).toHaveValue('This is a good example')
|
||||
})
|
||||
```
|
||||
|
||||
#### <input type="time" /> support
|
||||
|
||||
The following is an example of usage of this library with
|
||||
`<input type="time" />`
|
||||
|
||||
```jsx
|
||||
import React from 'react
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
test('types into the input', () => {
|
||||
render(
|
||||
<>
|
||||
<label for="time">Enter a time</label>
|
||||
<input
|
||||
type="time"
|
||||
id="time"
|
||||
/>
|
||||
</>
|
||||
)
|
||||
const input = screen.getByLabelText(/enter a time/i)
|
||||
userEvent.type(input, '13:58')
|
||||
expect(input.value).toBe('13:58')
|
||||
})
|
||||
```
|
||||
|
||||
### `upload(element, file, [{ clickInit, changeInit }], [options])`
|
||||
|
||||
Uploads file to an `<input>`. For uploading multiple files use `<input>` with
|
||||
`multiple` attribute and the second `upload` argument must be array then. Also
|
||||
it's possible to initialize click or change event with using third argument.
|
||||
|
||||
If `options.applyAccept` is set to `true` and there is an `accept` attribute on
|
||||
the element, files that don't match will be discarded.
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
test('upload file', () => {
|
||||
const file = new File(['hello'], 'hello.png', {type: 'image/png'})
|
||||
|
||||
render(
|
||||
<div>
|
||||
<label htmlFor="file-uploader">Upload file:</label>
|
||||
<input id="file-uploader" type="file" />
|
||||
</div>,
|
||||
)
|
||||
const input = screen.getByLabelText(/upload file/i)
|
||||
userEvent.upload(input, file)
|
||||
|
||||
expect(input.files[0]).toStrictEqual(file)
|
||||
expect(input.files.item(0)).toStrictEqual(file)
|
||||
expect(input.files).toHaveLength(1)
|
||||
})
|
||||
|
||||
test('upload multiple files', () => {
|
||||
const files = [
|
||||
new File(['hello'], 'hello.png', {type: 'image/png'}),
|
||||
new File(['there'], 'there.png', {type: 'image/png'}),
|
||||
]
|
||||
|
||||
render(
|
||||
<div>
|
||||
<label htmlFor="file-uploader">Upload file:</label>
|
||||
<input id="file-uploader" type="file" multiple />
|
||||
</div>,
|
||||
)
|
||||
const input = screen.getByLabelText(/upload file/i)
|
||||
userEvent.upload(input, files)
|
||||
|
||||
expect(input.files).toHaveLength(2)
|
||||
expect(input.files[0]).toStrictEqual(files[0])
|
||||
expect(input.files[1]).toStrictEqual(files[1])
|
||||
})
|
||||
```
|
||||
|
||||
### `clear(element)`
|
||||
|
||||
Selects the text inside an `<input>` or `<textarea>` and deletes it.
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
test('clear', () => {
|
||||
render(<textarea value="Hello, World!" />)
|
||||
|
||||
userEvent.clear(screen.getByRole('textbox', 'email'))
|
||||
expect(screen.getByRole('textbox', 'email')).toHaveAttribute('value', '')
|
||||
})
|
||||
```
|
||||
|
||||
### `selectOptions(element, values)`
|
||||
|
||||
Selects the specified option(s) of a `<select>` or a `<select multiple>`
|
||||
element.
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
test('selectOptions', () => {
|
||||
render(
|
||||
<select multiple data-testid="select-multiple">
|
||||
<option data-testid="val1" value="1">
|
||||
A
|
||||
</option>
|
||||
<option data-testid="val2" value="2">
|
||||
B
|
||||
</option>
|
||||
<option data-testid="val3" value="3">
|
||||
C
|
||||
</option>
|
||||
</select>,
|
||||
)
|
||||
|
||||
userEvent.selectOptions(screen.getByTestId('select-multiple'), ['1', '3'])
|
||||
|
||||
expect(screen.getByTestId('val1').selected).toBe(true)
|
||||
expect(screen.getByTestId('val2').selected).toBe(false)
|
||||
expect(screen.getByTestId('val3').selected).toBe(true)
|
||||
})
|
||||
```
|
||||
|
||||
The `values` parameter can be either an array of values or a singular scalar
|
||||
value.
|
||||
|
||||
It also accepts option nodes:
|
||||
|
||||
```js
|
||||
userEvent.selectOptions(screen.getByTestId('select-multiple'), [
|
||||
screen.getByText('A'),
|
||||
screen.getByText('B'),
|
||||
])
|
||||
```
|
||||
|
||||
### `deselectOptions(element, values)`
|
||||
|
||||
Remove the selection for the specified option(s) of a `<select multiple>`
|
||||
element.
|
||||
|
||||
```jsx
|
||||
import * as React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
test('deselectOptions', () => {
|
||||
render(
|
||||
<select multiple>
|
||||
<option value="1">A</option>
|
||||
<option value="2">B</option>
|
||||
<option value="3">C</option>
|
||||
</select>,
|
||||
)
|
||||
|
||||
userEvent.selectOptions(screen.getByRole('listbox'), '2')
|
||||
expect(screen.getByText('B').selected).toBe(true)
|
||||
userEvent.deselectOptions(screen.getByRole('listbox'), '2')
|
||||
expect(screen.getByText('B').selected).toBe(false)
|
||||
// can do multiple at once as well:
|
||||
// userEvent.deselectOptions(screen.getByRole('listbox'), ['1', '2'])
|
||||
})
|
||||
```
|
||||
|
||||
The `values` parameter can be either an array of values or a singular scalar
|
||||
value.
|
||||
|
||||
### `tab({shift, focusTrap})`
|
||||
|
||||
Fires a tab event changing the document.activeElement in the same way the
|
||||
browser does.
|
||||
|
||||
Options:
|
||||
|
||||
- `shift` (default `false`) can be true or false to invert tab direction.
|
||||
- `focusTrap` (default `document`) a container element to restrict the tabbing
|
||||
within.
|
||||
|
||||
> **A note about tab**:
|
||||
> [jsdom does not support tabbing](https://github.com/jsdom/jsdom/issues/2102),
|
||||
> so this feature is a way to enable tests to verify tabbing from the end user's
|
||||
> perspective. However, this limitation in jsdom will mean that components like
|
||||
> [focus-trap-react](https://github.com/davidtheclark/focus-trap-react) will not
|
||||
> work with `userEvent.tab()` or jsdom. For that reason, the `focusTrap` option
|
||||
> is available to let you ensure your user is restricted within a focus-trap.
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import '@testing-library/jest-dom/extend-expect'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
|
||||
it('should cycle elements in document tab order', () => {
|
||||
render(
|
||||
<div>
|
||||
<input data-testid="element" type="checkbox" />
|
||||
<input data-testid="element" type="radio" />
|
||||
<input data-testid="element" type="number" />
|
||||
</div>,
|
||||
)
|
||||
|
||||
const [checkbox, radio, number] = screen.getAllByTestId('element')
|
||||
|
||||
expect(document.body).toHaveFocus()
|
||||
|
||||
userEvent.tab()
|
||||
|
||||
expect(checkbox).toHaveFocus()
|
||||
|
||||
userEvent.tab()
|
||||
|
||||
expect(radio).toHaveFocus()
|
||||
|
||||
userEvent.tab()
|
||||
|
||||
expect(number).toHaveFocus()
|
||||
|
||||
userEvent.tab()
|
||||
|
||||
// cycle goes back to the body element
|
||||
expect(document.body).toHaveFocus()
|
||||
|
||||
userEvent.tab()
|
||||
|
||||
expect(checkbox).toHaveFocus()
|
||||
})
|
||||
```
|
||||
|
||||
### `hover(element)`
|
||||
|
||||
Hovers over `element`.
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent from '@testing-library/user-event'
|
||||
import Tooltip from '../tooltip'
|
||||
|
||||
test('hover', () => {
|
||||
const messageText = 'Hello'
|
||||
render(
|
||||
<Tooltip messageText={messageText}>
|
||||
<TrashIcon aria-label="Delete" />
|
||||
</Tooltip>,
|
||||
)
|
||||
|
||||
userEvent.hover(screen.getByLabelText(/delete/i))
|
||||
expect(screen.getByText(messageText)).toBeInTheDocument()
|
||||
userEvent.unhover(screen.getByLabelText(/delete/i))
|
||||
expect(screen.queryByText(messageText)).not.toBeInTheDocument()
|
||||
})
|
||||
```
|
||||
|
||||
### `unhover(element)`
|
||||
|
||||
Unhovers out of `element`.
|
||||
|
||||
> See [above](#hoverelement) for an example
|
||||
|
||||
### `paste(element, text, eventInit, options)`
|
||||
|
||||
Allows you to simulate the user pasting some text into an input.
|
||||
|
||||
```javascript
|
||||
test('should paste text in input', () => {
|
||||
render(<MyInput />)
|
||||
|
||||
const text = 'Hello, world!'
|
||||
userEvent.paste(getByRole('textbox', {name: /paste your greeting/i}), text)
|
||||
expect(element).toHaveValue(text)
|
||||
})
|
||||
```
|
||||
|
||||
You can use the `eventInit` if what you're pasting should have `clipboardData`
|
||||
(like `files`).
|
||||
|
||||
### `specialChars`
|
||||
|
||||
A handful set of special characters used in [type](#typeelement-text-options)
|
||||
method.
|
||||
|
||||
| Key | Character |
|
||||
| ---------- | -------------- |
|
||||
| arrowLeft | `{arrowleft}` |
|
||||
| arrowRight | `{arrowright}` |
|
||||
| arrowDown | `{arrowdown}` |
|
||||
| arrowUp | `{arrowup}` |
|
||||
| home | `{home}` |
|
||||
| end | `{end}` |
|
||||
| enter | `{enter}` |
|
||||
| escape | `{esc}` |
|
||||
| delete | `{del}` |
|
||||
| backspace | `{backspace}` |
|
||||
| selectAll | `{selectall}` |
|
||||
| space | `{space}` |
|
||||
| whitespace | `' '` |
|
||||
|
||||
Usage example:
|
||||
|
||||
```jsx
|
||||
import React from 'react'
|
||||
import {render, screen} from '@testing-library/react'
|
||||
import userEvent, {specialChars} from '@testing-library/user-event'
|
||||
|
||||
test('delete characters within the selectedRange', () => {
|
||||
render(
|
||||
<div>
|
||||
<label htmlFor="my-input">Example:</label>
|
||||
<input id="my-input" type="text" value="This is a bad example" />
|
||||
</div>,
|
||||
)
|
||||
const input = screen.getByLabelText(/example/i)
|
||||
input.setSelectionRange(10, 13)
|
||||
userEvent.type(input, `${specialChars.backspace}good`)
|
||||
|
||||
expect(input).toHaveValue('This is a good example')
|
||||
})
|
||||
```
|
||||
|
||||
## Issues
|
||||
|
||||
_Looking to contribute? Look for the [Good First Issue][good-first-issue]
|
||||
label._
|
||||
|
||||
### 🐛 Bugs
|
||||
|
||||
Please file an issue for bugs, missing documentation, or unexpected behavior.
|
||||
|
||||
[**See Bugs**][bugs]
|
||||
|
||||
### 💡 Feature Requests
|
||||
|
||||
Please file an issue to suggest new features. Vote on feature requests by adding
|
||||
a 👍. This helps maintainers prioritize what to work on.
|
||||
|
||||
[**See Feature Requests**][requests]
|
||||
|
||||
## Contributors ✨
|
||||
|
||||
Thanks goes to these people ([emoji key][emojis]):
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
||||
<!-- prettier-ignore-start -->
|
||||
<!-- markdownlint-disable -->
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center"><a href="https://twitter.com/Gpx"><img src="https://avatars0.githubusercontent.com/u/767959?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Giorgio Polvara</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3AGpx" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=Gpx" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=Gpx" title="Documentation">📖</a> <a href="#ideas-Gpx" title="Ideas, Planning, & Feedback">🤔</a> <a href="#infra-Gpx" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/testing-library/user-event/pulls?q=is%3Apr+reviewed-by%3AGpx" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/testing-library/user-event/commits?author=Gpx" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/weyert"><img src="https://avatars3.githubusercontent.com/u/7049?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Weyert de Boer</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=weyert" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=weyert" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/twhitbeck"><img src="https://avatars2.githubusercontent.com/u/762471?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Whitbeck</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Atwhitbeck" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=twhitbeck" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://michaeldeboey.be"><img src="https://avatars3.githubusercontent.com/u/6643991?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michaël De Boey</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=MichaelDeBoey" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/michaellasky"><img src="https://avatars2.githubusercontent.com/u/6646599?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Lasky</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=michaellasky" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=michaellasky" title="Documentation">📖</a> <a href="#ideas-michaellasky" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||
<td align="center"><a href="https://github.com/shomalgan"><img src="https://avatars0.githubusercontent.com/u/2883620?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ahmad Esmaeilzadeh</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=shomalgan" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://calebeby.ml"><img src="https://avatars1.githubusercontent.com/u/13206945?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Caleb Eby</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=calebeby" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/issues?q=author%3Acalebeby" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/pulls?q=is%3Apr+reviewed-by%3Acalebeby" title="Reviewed Pull Requests">👀</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://afontcu.dev"><img src="https://avatars0.githubusercontent.com/u/9197791?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Adrià Fontcuberta</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Aafontcu" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=afontcu" title="Tests">⚠️</a> <a href="https://github.com/testing-library/user-event/commits?author=afontcu" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/skywickenden"><img src="https://avatars2.githubusercontent.com/u/4930551?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sky Wickenden</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Askywickenden" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=skywickenden" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/bogdanbodnar"><img src="https://avatars2.githubusercontent.com/u/9034868?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bodnar Bogdan</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Abogdanbodnar" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=bogdanbodnar" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://zach.website"><img src="https://avatars0.githubusercontent.com/u/1699281?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zach Perrault</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=zperrault" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://twitter.com/ryanastelly"><img src="https://avatars1.githubusercontent.com/u/4138357?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ryan Stelly</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=FLGMwt" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/benmonro"><img src="https://avatars3.githubusercontent.com/u/399236?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ben Monro</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=benmonro" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/GentlemanHal"><img src="https://avatars2.githubusercontent.com/u/415521?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christopher Martin</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=GentlemanHal" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://fullgallop.me"><img src="https://avatars0.githubusercontent.com/u/32252769?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yuancheng Wu</b></sub></a><br /><a href="https://github.com/testing-library/user-event/pulls?q=is%3Apr+reviewed-by%3AYuanchengWu" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://github.com/maheshjag"><img src="https://avatars0.githubusercontent.com/u/1705603?v=4?s=100" width="100px;" alt=""/><br /><sub><b>MJ</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=maheshjag" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/jmcriffey"><img src="https://avatars0.githubusercontent.com/u/2831294?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeff McRiffey</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=jmcriffey" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=jmcriffey" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="http://jagascript.com"><img src="https://avatars0.githubusercontent.com/u/4562878?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jaga Santagostino</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=kandros" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=kandros" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="http://jordy.app"><img src="https://avatars3.githubusercontent.com/u/12712484?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jordyvandomselaar</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=jordyvandomselaar" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=jordyvandomselaar" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://lyamkin.com"><img src="https://avatars2.githubusercontent.com/u/3854930?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ilya Lyamkin</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=ilyamkin" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=ilyamkin" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="http://todofullstack.com"><img src="https://avatars2.githubusercontent.com/u/4474353?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kenneth Luján Rosas</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=klujanrosas" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=klujanrosas" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://thejoemorgan.com"><img src="https://avatars1.githubusercontent.com/u/2388943?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe Morgan</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=jsmapr1" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://twitter.com/wachunga"><img src="https://avatars0.githubusercontent.com/u/438545?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Hirtle</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=wachunga" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/bdh1011"><img src="https://avatars2.githubusercontent.com/u/8446067?v=4?s=100" width="100px;" alt=""/><br /><sub><b>whiteUnicorn</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=bdh1011" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://www.matej.snuderl.si/"><img src="https://avatars3.githubusercontent.com/u/8524109?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matej Šnuderl</b></sub></a><br /><a href="https://github.com/testing-library/user-event/pulls?q=is%3Apr+reviewed-by%3AMeemaw" title="Reviewed Pull Requests">👀</a></td>
|
||||
<td align="center"><a href="https://pomb.us"><img src="https://avatars1.githubusercontent.com/u/1911623?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rodrigo Pombo</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=pomber" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://github.com/Raynos"><img src="https://avatars3.githubusercontent.com/u/479538?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jake Verbaten</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=Raynos" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://skovy.dev"><img src="https://avatars1.githubusercontent.com/u/5247455?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Spencer Miskoviak</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=skovy" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://proling.ru/"><img src="https://avatars2.githubusercontent.com/u/16336572?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vadim Shvetsov</b></sub></a><br /><a href="#ideas-vadimshvetsov" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/user-event/commits?author=vadimshvetsov" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=vadimshvetsov" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/9still"><img src="https://avatars0.githubusercontent.com/u/4924760?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Greg Shtilman</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=9still" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=9still" title="Tests">⚠️</a> <a href="https://github.com/testing-library/user-event/issues?q=author%3A9still" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/rbusquet"><img src="https://avatars1.githubusercontent.com/u/7198302?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ricardo Busquet</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Arbusquet" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=rbusquet" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=rbusquet" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://www.linkedin.com/in/dougbacelar/en"><img src="https://avatars3.githubusercontent.com/u/9267678?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Doug Bacelar</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=dougbacelar" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=dougbacelar" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/kayleighridd"><img src="https://avatars3.githubusercontent.com/u/36446015?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kayleigh Ridd</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Akayleighridd" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=kayleighridd" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=kayleighridd" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://malcolmkee.com"><img src="https://avatars0.githubusercontent.com/u/24528512?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Malcolm Kee</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=malcolm-kee" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=malcolm-kee" title="Documentation">📖</a> <a href="https://github.com/testing-library/user-event/commits?author=malcolm-kee" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/kelvinlzhang"><img src="https://avatars3.githubusercontent.com/u/8291294?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kelvinlzhang</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Akelvinlzhang" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/krzysztof-hellostudio"><img src="https://avatars3.githubusercontent.com/u/1942664?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krzysztof</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Akrzysztof-hellostudio" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/hontas"><img src="https://avatars2.githubusercontent.com/u/1521113?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pontus Lundin</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=hontas" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=hontas" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://hudochenkov.com/"><img src="https://avatars2.githubusercontent.com/u/654597?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleks Hudochenkov</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Ahudochenkov" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/nanivijay"><img src="https://avatars0.githubusercontent.com/u/5945591?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vijay Kumar Otti</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Ananivijay" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="http://tompicton.com"><img src="https://avatars2.githubusercontent.com/u/12588098?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tom Picton</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Atpict" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=tpict" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=tpict" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://hung.dev"><img src="https://avatars3.githubusercontent.com/u/8603085?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hung Viet Nguyen</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Anvh95" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://nickmccurdy.com/"><img src="https://avatars0.githubusercontent.com/u/927220?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nick McCurdy</b></sub></a><br /><a href="#projectManagement-nickmccurdy" title="Project Management">📆</a> <a href="#question-nickmccurdy" title="Answering Questions">💬</a> <a href="https://github.com/testing-library/user-event/commits?author=nickmccurdy" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=nickmccurdy" title="Tests">⚠️</a> <a href="https://github.com/testing-library/user-event/commits?author=nickmccurdy" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="http://timdeschryver.dev"><img src="https://avatars1.githubusercontent.com/u/28659384?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Deschryver</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=timdeschryver" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/ben-dyer"><img src="https://avatars2.githubusercontent.com/u/43922444?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ben Dyer</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=ben-dyer" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=ben-dyer" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://twitter.com/herecydev"><img src="https://avatars1.githubusercontent.com/u/11328618?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dan Kirkham</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=herecydev" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/Johannesklint"><img src="https://avatars3.githubusercontent.com/u/16774845?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johannesklint</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=Johannesklint" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/juanca"><img src="https://avatars0.githubusercontent.com/u/841084?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Juan Carlos Medina</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=juanca" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=juanca" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/WretchedDade"><img src="https://avatars0.githubusercontent.com/u/17183431?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dade Cook</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=WretchedDade" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=WretchedDade" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://blog.lourenci.com/"><img src="https://avatars3.githubusercontent.com/u/2339362?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leandro Lourenci</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=lourenci" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=lourenci" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/marcosvega91"><img src="https://avatars2.githubusercontent.com/u/5365582?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marco Moretti</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=marcosvega91" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=marcosvega91" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/ybentz"><img src="https://avatars3.githubusercontent.com/u/14811577?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ybentz</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=ybentz" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=ybentz" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="http://www.lemoncode.net/"><img src="https://avatars2.githubusercontent.com/u/4374977?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nasdan</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3ANasdan" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/JavierMartinz"><img src="https://avatars1.githubusercontent.com/u/1155507?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Javier Martínez</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=JavierMartinz" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://www.visualjerk.de"><img src="https://avatars0.githubusercontent.com/u/28823153?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jörg Bayreuther</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=visualjerk" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=visualjerk" title="Tests">⚠️</a> <a href="https://github.com/testing-library/user-event/commits?author=visualjerk" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://ko-fi.com/thislucas"><img src="https://avatars0.githubusercontent.com/u/8645841?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lucas Bernalte</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=lucbpz" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/maxnewlands"><img src="https://avatars3.githubusercontent.com/u/1304166?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Maxwell Newlands</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=maxnewlands" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=maxnewlands" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/ph-fritsche"><img src="https://avatars3.githubusercontent.com/u/39068198?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ph-fritsche</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=ph-fritsche" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=ph-fritsche" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/reywright"><img src="https://avatars3.githubusercontent.com/u/708820?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rey Wright</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Areywright" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=reywright" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/mischnic"><img src="https://avatars1.githubusercontent.com/u/4586894?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Niklas Mischkulnig</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=mischnic" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=mischnic" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="http://pascalduez.me"><img src="https://avatars3.githubusercontent.com/u/335467?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pascal Duez</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=pascalduez" title="Code">💻</a></td>
|
||||
<td align="center"><a href="http://malachi.dev"><img src="https://avatars3.githubusercontent.com/u/10888943?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Malachi Willey</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=malwilley" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=malwilley" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://clarkwinters.com"><img src="https://avatars2.githubusercontent.com/u/40615752?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Clark Winters</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=cwinters8" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/lazytype"><img src="https://avatars1.githubusercontent.com/u/840985?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lazytype</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=lazytype" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=lazytype" title="Tests">⚠️</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://www.linkedin.com/in/luis-takahashi/"><img src="https://avatars0.githubusercontent.com/u/19766035?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Luís Takahashi</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=luistak" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=luistak" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/jesujcastillom"><img src="https://avatars3.githubusercontent.com/u/7827281?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jesu Castillo</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=jesujcastillom" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=jesujcastillom" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://sarahdayan.dev"><img src="https://avatars1.githubusercontent.com/u/5370675?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sarah Dayan</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=sarahdayan" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://saul-mirone.github.io/"><img src="https://avatars0.githubusercontent.com/u/10047788?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mirone</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3ASaul-Mirone" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://github.com/amandapouget"><img src="https://avatars3.githubusercontent.com/u/12855692?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amanda Pouget</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=amandapouget" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/Sonic12040"><img src="https://avatars3.githubusercontent.com/u/21055893?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sonic12040</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=Sonic12040" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=Sonic12040" title="Tests">⚠️</a> <a href="https://github.com/testing-library/user-event/commits?author=Sonic12040" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://github.com/gndelia"><img src="https://avatars1.githubusercontent.com/u/352474?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gonzalo D'Elia</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=gndelia" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=gndelia" title="Tests">⚠️</a> <a href="https://github.com/testing-library/user-event/commits?author=gndelia" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/vasilii-kovalev"><img src="https://avatars0.githubusercontent.com/u/10310491?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vasilii Kovalev</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=vasilii-kovalev" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=vasilii-kovalev" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://www.daleseo.com"><img src="https://avatars1.githubusercontent.com/u/5466341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dale Seo</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=daleseo" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="http://www.alex-boyce.me/"><img src="https://avatars.githubusercontent.com/u/4050934?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alex Boyce</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=curiosity26" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://benadamstyles.com"><img src="https://avatars.githubusercontent.com/u/4380655?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ben Styles</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=benadamstyles" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=benadamstyles" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="http://laurabeatris.com"><img src="https://avatars.githubusercontent.com/u/48022589?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Laura Beatris</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=LauraBeatris" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=LauraBeatris" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://twitter.com/boriscoder"><img src="https://avatars.githubusercontent.com/u/812240?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Boris Serdiuk</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Ajust-boris" title="Bug reports">🐛</a></td>
|
||||
<td align="center"><a href="https://bozdoz.com"><img src="https://avatars.githubusercontent.com/u/1410985?v=4?s=100" width="100px;" alt=""/><br /><sub><b>bozdoz</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=bozdoz" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://github.com/jKatt"><img src="https://avatars.githubusercontent.com/u/5550790?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jan Kattelans</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=jKatt" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/schoeneu"><img src="https://avatars.githubusercontent.com/u/3261341?v=4?s=100" width="100px;" alt=""/><br /><sub><b>schoeneu</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Aschoeneu" title="Bug reports">🐛</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
<!-- prettier-ignore-end -->
|
||||
|
||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
||||
|
||||
This project follows the [all-contributors][all-contributors] specification.
|
||||
Contributions of any kind welcome!
|
||||
|
||||
## LICENSE
|
||||
|
||||
MIT
|
||||
|
||||
<!-- prettier-ignore-start -->
|
||||
[npm]: https://www.npmjs.com
|
||||
[node]: https://nodejs.org
|
||||
[build-badge]: https://img.shields.io/github/workflow/status/testing-library/user-event/validate/master?logo=github&style=flat-square
|
||||
[build]: https://github.com/testing-library/user-event/actions?query=workflow%3Avalidate
|
||||
[coverage-badge]: https://img.shields.io/codecov/c/github/testing-library/user-event.svg?style=flat-square
|
||||
[coverage]: https://codecov.io/github/testing-library/user-event
|
||||
[version-badge]: https://img.shields.io/npm/v/@testing-library/user-event.svg?style=flat-square
|
||||
[package]: https://www.npmjs.com/package/@testing-library/user-event
|
||||
[downloads-badge]: https://img.shields.io/npm/dm/@testing-library/user-event.svg?style=flat-square
|
||||
[npmtrends]: http://www.npmtrends.com/@testing-library/user-event
|
||||
[license-badge]: https://img.shields.io/npm/l/@testing-library/user-event.svg?style=flat-square
|
||||
[license]: https://github.com/testing-library/user-event/blob/master/LICENSE
|
||||
[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
|
||||
[prs]: http://makeapullrequest.com
|
||||
[coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square
|
||||
[coc]: https://github.com/testing-library/user-event/blob/master/other/CODE_OF_CONDUCT.md
|
||||
[emojis]: https://github.com/all-contributors/all-contributors#emoji-key
|
||||
[all-contributors]: https://github.com/all-contributors/all-contributors
|
||||
[all-contributors-badge]: https://img.shields.io/github/all-contributors/testing-library/user-event?color=orange&style=flat-square
|
||||
[bugs]: https://github.com/testing-library/user-event/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Acreated-desc+label%3Abug
|
||||
[requests]: https://github.com/testing-library/user-event/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement
|
||||
[good-first-issue]: https://github.com/testing-library/user-event/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3Aenhancement+label%3A%22good+first+issue%22
|
||||
<!-- prettier-ignore-end -->
|
15
web/node_modules/@testing-library/user-event/dist/blur.js
generated
vendored
Normal file
15
web/node_modules/@testing-library/user-event/dist/blur.js
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.blur = blur;
|
||||
|
||||
var _utils = require("./utils");
|
||||
|
||||
function blur(element) {
|
||||
if (!(0, _utils.isFocusable)(element)) return;
|
||||
const wasActive = (0, _utils.getActiveElement)(element.ownerDocument) === element;
|
||||
if (!wasActive) return;
|
||||
(0, _utils.eventWrapper)(() => element.blur());
|
||||
}
|
35
web/node_modules/@testing-library/user-event/dist/clear.js
generated
vendored
Normal file
35
web/node_modules/@testing-library/user-event/dist/clear.js
generated
vendored
Normal file
|
@ -0,0 +1,35 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.clear = clear;
|
||||
|
||||
var _type = require("./type");
|
||||
|
||||
function clear(element) {
|
||||
if (element.tagName !== 'INPUT' && element.tagName !== 'TEXTAREA') {
|
||||
// TODO: support contenteditable
|
||||
throw new Error('clear currently only supports input and textarea elements.');
|
||||
}
|
||||
|
||||
if (element.disabled) return; // TODO: track the selection range ourselves so we don't have to do this input "type" trickery
|
||||
// just like cypress does: https://github.com/cypress-io/cypress/blob/8d7f1a0bedc3c45a2ebf1ff50324b34129fdc683/packages/driver/src/dom/selection.ts#L16-L37
|
||||
|
||||
const elementType = element.type; // type is a readonly property on textarea, so check if element is an input before trying to modify it
|
||||
|
||||
if (element.tagName === 'INPUT') {
|
||||
// setSelectionRange is not supported on certain types of inputs, e.g. "number" or "email"
|
||||
element.type = 'text';
|
||||
}
|
||||
|
||||
(0, _type.type)(element, '{selectall}{del}', {
|
||||
delay: 0,
|
||||
initialSelectionStart: element.selectionStart,
|
||||
initialSelectionEnd: element.selectionEnd
|
||||
});
|
||||
|
||||
if (element.tagName === 'INPUT') {
|
||||
element.type = elementType;
|
||||
}
|
||||
}
|
154
web/node_modules/@testing-library/user-event/dist/click.js
generated
vendored
Normal file
154
web/node_modules/@testing-library/user-event/dist/click.js
generated
vendored
Normal file
|
@ -0,0 +1,154 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.click = click;
|
||||
exports.dblClick = dblClick;
|
||||
|
||||
var _dom = require("@testing-library/dom");
|
||||
|
||||
var _utils = require("./utils");
|
||||
|
||||
var _hover = require("./hover");
|
||||
|
||||
var _blur = require("./blur");
|
||||
|
||||
var _focus = require("./focus");
|
||||
|
||||
function getPreviouslyFocusedElement(element) {
|
||||
const focusedElement = element.ownerDocument.activeElement;
|
||||
const wasAnotherElementFocused = focusedElement && focusedElement !== element.ownerDocument.body && focusedElement !== element;
|
||||
return wasAnotherElementFocused ? focusedElement : null;
|
||||
}
|
||||
|
||||
function clickLabel(label, init, {
|
||||
clickCount
|
||||
}) {
|
||||
if ((0, _utils.isLabelWithInternallyDisabledControl)(label)) return;
|
||||
|
||||
_dom.fireEvent.pointerDown(label, init);
|
||||
|
||||
_dom.fireEvent.mouseDown(label, (0, _utils.getMouseEventOptions)('mousedown', init, clickCount));
|
||||
|
||||
_dom.fireEvent.pointerUp(label, init);
|
||||
|
||||
_dom.fireEvent.mouseUp(label, (0, _utils.getMouseEventOptions)('mouseup', init, clickCount));
|
||||
|
||||
_dom.fireEvent.click(label, (0, _utils.getMouseEventOptions)('click', init, clickCount)); // clicking the label will trigger a click of the label.control
|
||||
// however, it will not focus the label.control so we have to do it
|
||||
// ourselves.
|
||||
|
||||
|
||||
if (label.control) (0, _focus.focus)(label.control);
|
||||
}
|
||||
|
||||
function clickBooleanElement(element, init, clickCount) {
|
||||
_dom.fireEvent.pointerDown(element, init);
|
||||
|
||||
if (!element.disabled) {
|
||||
_dom.fireEvent.mouseDown(element, (0, _utils.getMouseEventOptions)('mousedown', init, clickCount));
|
||||
}
|
||||
|
||||
(0, _focus.focus)(element, init);
|
||||
|
||||
_dom.fireEvent.pointerUp(element, init);
|
||||
|
||||
if (!element.disabled) {
|
||||
_dom.fireEvent.mouseUp(element, (0, _utils.getMouseEventOptions)('mouseup', init, clickCount));
|
||||
|
||||
_dom.fireEvent.click(element, (0, _utils.getMouseEventOptions)('click', init, clickCount));
|
||||
}
|
||||
}
|
||||
|
||||
function clickElement(element, init, {
|
||||
clickCount
|
||||
}) {
|
||||
const previousElement = getPreviouslyFocusedElement(element);
|
||||
|
||||
_dom.fireEvent.pointerDown(element, init);
|
||||
|
||||
if (!element.disabled) {
|
||||
const continueDefaultHandling = _dom.fireEvent.mouseDown(element, (0, _utils.getMouseEventOptions)('mousedown', init, clickCount));
|
||||
|
||||
if (continueDefaultHandling) {
|
||||
const closestFocusable = findClosest(element, _utils.isFocusable);
|
||||
|
||||
if (previousElement && !closestFocusable) {
|
||||
(0, _blur.blur)(previousElement, init);
|
||||
} else if (closestFocusable) {
|
||||
(0, _focus.focus)(closestFocusable, init);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_dom.fireEvent.pointerUp(element, init);
|
||||
|
||||
if (!element.disabled) {
|
||||
_dom.fireEvent.mouseUp(element, (0, _utils.getMouseEventOptions)('mouseup', init, clickCount));
|
||||
|
||||
_dom.fireEvent.click(element, (0, _utils.getMouseEventOptions)('click', init, clickCount));
|
||||
|
||||
const parentLabel = element.closest('label');
|
||||
if (parentLabel != null && parentLabel.control) (0, _focus.focus)(parentLabel.control, init);
|
||||
}
|
||||
}
|
||||
|
||||
function findClosest(el, callback) {
|
||||
do {
|
||||
if (callback(el)) {
|
||||
return el;
|
||||
}
|
||||
|
||||
el = el.parentElement;
|
||||
} while (el && el !== document.body);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function click(element, init, {
|
||||
skipHover = false,
|
||||
clickCount = 0
|
||||
} = {}) {
|
||||
if (!skipHover) (0, _hover.hover)(element, init);
|
||||
|
||||
switch (element.tagName) {
|
||||
case 'LABEL':
|
||||
clickLabel(element, init, {
|
||||
clickCount
|
||||
});
|
||||
break;
|
||||
|
||||
case 'INPUT':
|
||||
if (element.type === 'checkbox' || element.type === 'radio') {
|
||||
clickBooleanElement(element, init, {
|
||||
clickCount
|
||||
});
|
||||
} else {
|
||||
clickElement(element, init, {
|
||||
clickCount
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
clickElement(element, init, {
|
||||
clickCount
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function dblClick(element, init) {
|
||||
(0, _hover.hover)(element, init);
|
||||
click(element, init, {
|
||||
skipHover: true,
|
||||
clickCount: 0
|
||||
});
|
||||
click(element, init, {
|
||||
skipHover: true,
|
||||
clickCount: 1
|
||||
});
|
||||
|
||||
_dom.fireEvent.dblClick(element, (0, _utils.getMouseEventOptions)('dblclick', init, 2));
|
||||
}
|
15
web/node_modules/@testing-library/user-event/dist/focus.js
generated
vendored
Normal file
15
web/node_modules/@testing-library/user-event/dist/focus.js
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.focus = focus;
|
||||
|
||||
var _utils = require("./utils");
|
||||
|
||||
function focus(element) {
|
||||
if (!(0, _utils.isFocusable)(element)) return;
|
||||
const isAlreadyActive = (0, _utils.getActiveElement)(element.ownerDocument) === element;
|
||||
if (isAlreadyActive) return;
|
||||
(0, _utils.eventWrapper)(() => element.focus());
|
||||
}
|
73
web/node_modules/@testing-library/user-event/dist/hover.js
generated
vendored
Normal file
73
web/node_modules/@testing-library/user-event/dist/hover.js
generated
vendored
Normal file
|
@ -0,0 +1,73 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.hover = hover;
|
||||
exports.unhover = unhover;
|
||||
|
||||
var _dom = require("@testing-library/dom");
|
||||
|
||||
var _utils = require("./utils");
|
||||
|
||||
// includes `element`
|
||||
function getParentElements(element) {
|
||||
const parentElements = [element];
|
||||
let currentElement = element;
|
||||
|
||||
while ((currentElement = currentElement.parentElement) != null) {
|
||||
parentElements.push(currentElement);
|
||||
}
|
||||
|
||||
return parentElements;
|
||||
}
|
||||
|
||||
function hover(element, init) {
|
||||
if ((0, _utils.isLabelWithInternallyDisabledControl)(element)) return;
|
||||
const parentElements = getParentElements(element).reverse();
|
||||
|
||||
_dom.fireEvent.pointerOver(element, init);
|
||||
|
||||
for (const el of parentElements) {
|
||||
_dom.fireEvent.pointerEnter(el, init);
|
||||
}
|
||||
|
||||
if (!element.disabled) {
|
||||
_dom.fireEvent.mouseOver(element, (0, _utils.getMouseEventOptions)('mouseover', init));
|
||||
|
||||
for (const el of parentElements) {
|
||||
_dom.fireEvent.mouseEnter(el, (0, _utils.getMouseEventOptions)('mouseenter', init));
|
||||
}
|
||||
}
|
||||
|
||||
_dom.fireEvent.pointerMove(element, init);
|
||||
|
||||
if (!element.disabled) {
|
||||
_dom.fireEvent.mouseMove(element, (0, _utils.getMouseEventOptions)('mousemove', init));
|
||||
}
|
||||
}
|
||||
|
||||
function unhover(element, init) {
|
||||
if ((0, _utils.isLabelWithInternallyDisabledControl)(element)) return;
|
||||
const parentElements = getParentElements(element);
|
||||
|
||||
_dom.fireEvent.pointerMove(element, init);
|
||||
|
||||
if (!element.disabled) {
|
||||
_dom.fireEvent.mouseMove(element, (0, _utils.getMouseEventOptions)('mousemove', init));
|
||||
}
|
||||
|
||||
_dom.fireEvent.pointerOut(element, init);
|
||||
|
||||
for (const el of parentElements) {
|
||||
_dom.fireEvent.pointerLeave(el, init);
|
||||
}
|
||||
|
||||
if (!element.disabled) {
|
||||
_dom.fireEvent.mouseOut(element, (0, _utils.getMouseEventOptions)('mouseout', init));
|
||||
|
||||
for (const el of parentElements) {
|
||||
_dom.fireEvent.mouseLeave(el, (0, _utils.getMouseEventOptions)('mouseleave', init));
|
||||
}
|
||||
}
|
||||
}
|
44
web/node_modules/@testing-library/user-event/dist/index.js
generated
vendored
Normal file
44
web/node_modules/@testing-library/user-event/dist/index.js
generated
vendored
Normal file
|
@ -0,0 +1,44 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
Object.defineProperty(exports, "specialChars", {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return _type.specialCharMap;
|
||||
}
|
||||
});
|
||||
exports.default = void 0;
|
||||
|
||||
var _click = require("./click");
|
||||
|
||||
var _type = require("./type");
|
||||
|
||||
var _clear = require("./clear");
|
||||
|
||||
var _tab = require("./tab");
|
||||
|
||||
var _hover = require("./hover");
|
||||
|
||||
var _upload = require("./upload");
|
||||
|
||||
var _selectOptions = require("./select-options");
|
||||
|
||||
var _paste = require("./paste");
|
||||
|
||||
const userEvent = {
|
||||
click: _click.click,
|
||||
dblClick: _click.dblClick,
|
||||
type: _type.type,
|
||||
clear: _clear.clear,
|
||||
tab: _tab.tab,
|
||||
hover: _hover.hover,
|
||||
unhover: _hover.unhover,
|
||||
upload: _upload.upload,
|
||||
selectOptions: _selectOptions.selectOptions,
|
||||
deselectOptions: _selectOptions.deselectOptions,
|
||||
paste: _paste.paste
|
||||
};
|
||||
var _default = userEvent;
|
||||
exports.default = _default;
|
75
web/node_modules/@testing-library/user-event/dist/keys/navigation-key.js
generated
vendored
Normal file
75
web/node_modules/@testing-library/user-event/dist/keys/navigation-key.js
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.navigationKey = navigationKey;
|
||||
|
||||
var _dom = require("@testing-library/dom");
|
||||
|
||||
var _utils = require("../utils");
|
||||
|
||||
const keys = {
|
||||
Home: {
|
||||
keyCode: 36
|
||||
},
|
||||
End: {
|
||||
keyCode: 35
|
||||
},
|
||||
ArrowLeft: {
|
||||
keyCode: 37
|
||||
},
|
||||
ArrowRight: {
|
||||
keyCode: 39
|
||||
}
|
||||
};
|
||||
|
||||
function getSelectionRange(currentElement, key) {
|
||||
const {
|
||||
selectionStart,
|
||||
selectionEnd
|
||||
} = currentElement();
|
||||
|
||||
if (key === 'Home') {
|
||||
return {
|
||||
selectionStart: 0,
|
||||
selectionEnd: 0
|
||||
};
|
||||
}
|
||||
|
||||
if (key === 'End') {
|
||||
return {
|
||||
selectionStart: selectionEnd + 1,
|
||||
selectionEnd: selectionEnd + 1
|
||||
};
|
||||
}
|
||||
|
||||
const cursorChange = Number(key in keys) * (key === 'ArrowLeft' ? -1 : 1);
|
||||
return {
|
||||
selectionStart: selectionStart + cursorChange,
|
||||
selectionEnd: selectionEnd + cursorChange
|
||||
};
|
||||
}
|
||||
|
||||
function navigationKey(key) {
|
||||
const event = {
|
||||
key,
|
||||
keyCode: keys[key].keyCode,
|
||||
which: keys[key].keyCode
|
||||
};
|
||||
return ({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) => {
|
||||
_dom.fireEvent.keyDown(currentElement(), { ...event,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
const range = getSelectionRange(currentElement, key);
|
||||
(0, _utils.setSelectionRangeIfNecessary)(currentElement(), range.selectionStart, range.selectionEnd);
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), { ...event,
|
||||
...eventOverrides
|
||||
});
|
||||
};
|
||||
}
|
55
web/node_modules/@testing-library/user-event/dist/paste.js
generated
vendored
Normal file
55
web/node_modules/@testing-library/user-event/dist/paste.js
generated
vendored
Normal file
|
@ -0,0 +1,55 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.paste = paste;
|
||||
|
||||
var _dom = require("@testing-library/dom");
|
||||
|
||||
var _utils = require("./utils");
|
||||
|
||||
function paste(element, text, init, {
|
||||
initialSelectionStart,
|
||||
initialSelectionEnd
|
||||
} = {}) {
|
||||
if (element.disabled) return;
|
||||
|
||||
if (typeof element.value === 'undefined') {
|
||||
throw new TypeError(`the current element is of type ${element.tagName} and doesn't have a valid value`);
|
||||
}
|
||||
|
||||
(0, _utils.eventWrapper)(() => element.focus()); // by default, a new element has it's selection start and end at 0
|
||||
// but most of the time when people call "paste", they expect it to paste
|
||||
// at the end of the current input value. So, if the selection start
|
||||
// and end are both the default of 0, then we'll go ahead and change
|
||||
// them to the length of the current value.
|
||||
// the only time it would make sense to pass the initialSelectionStart or
|
||||
// initialSelectionEnd is if you have an input with a value and want to
|
||||
// explicitely start typing with the cursor at 0. Not super common.
|
||||
|
||||
if (element.selectionStart === 0 && element.selectionEnd === 0) {
|
||||
(0, _utils.setSelectionRangeIfNecessary)(element, initialSelectionStart != null ? initialSelectionStart : element.value.length, initialSelectionEnd != null ? initialSelectionEnd : element.value.length);
|
||||
}
|
||||
|
||||
_dom.fireEvent.paste(element, init);
|
||||
|
||||
if (!element.readOnly) {
|
||||
const {
|
||||
newValue,
|
||||
newSelectionStart
|
||||
} = (0, _utils.calculateNewValue)(text, element);
|
||||
|
||||
_dom.fireEvent.input(element, {
|
||||
inputType: 'insertFromPaste',
|
||||
target: {
|
||||
value: newValue
|
||||
}
|
||||
});
|
||||
|
||||
(0, _utils.setSelectionRangeIfNecessary)(element, {
|
||||
newSelectionStart,
|
||||
newSelectionEnd: newSelectionStart
|
||||
});
|
||||
}
|
||||
}
|
118
web/node_modules/@testing-library/user-event/dist/select-options.js
generated
vendored
Normal file
118
web/node_modules/@testing-library/user-event/dist/select-options.js
generated
vendored
Normal file
|
@ -0,0 +1,118 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.deselectOptions = exports.selectOptions = void 0;
|
||||
|
||||
var _dom = require("@testing-library/dom");
|
||||
|
||||
var _utils = require("./utils");
|
||||
|
||||
var _click = require("./click");
|
||||
|
||||
var _focus = require("./focus");
|
||||
|
||||
var _hover = require("./hover");
|
||||
|
||||
function selectOptionsBase(newValue, select, values, init) {
|
||||
if (!newValue && !select.multiple) {
|
||||
throw (0, _dom.getConfig)().getElementError(`Unable to deselect an option in a non-multiple select. Use selectOptions to change the selection instead.`, select);
|
||||
}
|
||||
|
||||
const valArray = Array.isArray(values) ? values : [values];
|
||||
const allOptions = Array.from(select.querySelectorAll('option, [role="option"]'));
|
||||
const selectedOptions = valArray.map(val => {
|
||||
if (allOptions.includes(val)) {
|
||||
return val;
|
||||
} else {
|
||||
const matchingOption = allOptions.find(o => o.value === val || o.innerHTML === val);
|
||||
|
||||
if (matchingOption) {
|
||||
return matchingOption;
|
||||
} else {
|
||||
throw (0, _dom.getConfig)().getElementError(`Value "${val}" not found in options`, select);
|
||||
}
|
||||
}
|
||||
}).filter(option => !option.disabled);
|
||||
if (select.disabled || !selectedOptions.length) return;
|
||||
|
||||
if ((0, _utils.isInstanceOfElement)(select, 'HTMLSelectElement')) {
|
||||
if (select.multiple) {
|
||||
for (const option of selectedOptions) {
|
||||
// events fired for multiple select are weird. Can't use hover...
|
||||
_dom.fireEvent.pointerOver(option, init);
|
||||
|
||||
_dom.fireEvent.pointerEnter(select, init);
|
||||
|
||||
_dom.fireEvent.mouseOver(option);
|
||||
|
||||
_dom.fireEvent.mouseEnter(select);
|
||||
|
||||
_dom.fireEvent.pointerMove(option, init);
|
||||
|
||||
_dom.fireEvent.mouseMove(option, init);
|
||||
|
||||
_dom.fireEvent.pointerDown(option, init);
|
||||
|
||||
_dom.fireEvent.mouseDown(option, init);
|
||||
|
||||
(0, _focus.focus)(select, init);
|
||||
|
||||
_dom.fireEvent.pointerUp(option, init);
|
||||
|
||||
_dom.fireEvent.mouseUp(option, init);
|
||||
|
||||
selectOption(option);
|
||||
|
||||
_dom.fireEvent.click(option, init);
|
||||
}
|
||||
} else if (selectedOptions.length === 1) {
|
||||
// the click to open the select options
|
||||
(0, _click.click)(select, init);
|
||||
selectOption(selectedOptions[0]); // the browser triggers another click event on the select for the click on the option
|
||||
// this second click has no 'down' phase
|
||||
|
||||
_dom.fireEvent.pointerOver(select, init);
|
||||
|
||||
_dom.fireEvent.pointerEnter(select, init);
|
||||
|
||||
_dom.fireEvent.mouseOver(select);
|
||||
|
||||
_dom.fireEvent.mouseEnter(select);
|
||||
|
||||
_dom.fireEvent.pointerUp(select, init);
|
||||
|
||||
_dom.fireEvent.mouseUp(select, init);
|
||||
|
||||
_dom.fireEvent.click(select, init);
|
||||
} else {
|
||||
throw (0, _dom.getConfig)().getElementError(`Cannot select multiple options on a non-multiple select`, select);
|
||||
}
|
||||
} else if (select.getAttribute('role') === 'listbox') {
|
||||
selectedOptions.forEach(option => {
|
||||
(0, _hover.hover)(option, init);
|
||||
(0, _click.click)(option, init);
|
||||
(0, _hover.unhover)(option, init);
|
||||
});
|
||||
} else {
|
||||
throw (0, _dom.getConfig)().getElementError(`Cannot select options on elements that are neither select nor listbox elements`, select);
|
||||
}
|
||||
|
||||
function selectOption(option) {
|
||||
option.selected = newValue;
|
||||
(0, _dom.fireEvent)(select, (0, _dom.createEvent)('input', select, {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
composed: true,
|
||||
...init
|
||||
}));
|
||||
|
||||
_dom.fireEvent.change(select, init);
|
||||
}
|
||||
}
|
||||
|
||||
const selectOptions = selectOptionsBase.bind(null, true);
|
||||
exports.selectOptions = selectOptions;
|
||||
const deselectOptions = selectOptionsBase.bind(null, false);
|
||||
exports.deselectOptions = deselectOptions;
|
138
web/node_modules/@testing-library/user-event/dist/tab.js
generated
vendored
Normal file
138
web/node_modules/@testing-library/user-event/dist/tab.js
generated
vendored
Normal file
|
@ -0,0 +1,138 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.tab = tab;
|
||||
|
||||
var _dom = require("@testing-library/dom");
|
||||
|
||||
var _utils = require("./utils");
|
||||
|
||||
var _focus = require("./focus");
|
||||
|
||||
var _blur = require("./blur");
|
||||
|
||||
function getNextElement(currentIndex, shift, elements, focusTrap) {
|
||||
if (focusTrap === document && currentIndex === 0 && shift) {
|
||||
return document.body;
|
||||
} else if (focusTrap === document && currentIndex === elements.length - 1 && !shift) {
|
||||
return document.body;
|
||||
} else {
|
||||
const nextIndex = shift ? currentIndex - 1 : currentIndex + 1;
|
||||
const defaultIndex = shift ? elements.length - 1 : 0;
|
||||
return elements[nextIndex] || elements[defaultIndex];
|
||||
}
|
||||
}
|
||||
|
||||
function tab({
|
||||
shift = false,
|
||||
focusTrap
|
||||
} = {}) {
|
||||
var _focusTrap$ownerDocum, _focusTrap;
|
||||
|
||||
const previousElement = (0, _utils.getActiveElement)((_focusTrap$ownerDocum = (_focusTrap = focusTrap) == null ? void 0 : _focusTrap.ownerDocument) != null ? _focusTrap$ownerDocum : document);
|
||||
|
||||
if (!focusTrap) {
|
||||
focusTrap = document;
|
||||
}
|
||||
|
||||
const focusableElements = focusTrap.querySelectorAll(_utils.FOCUSABLE_SELECTOR);
|
||||
const enabledElements = [...focusableElements].filter(el => el === previousElement || el.getAttribute('tabindex') !== '-1' && !el.disabled && // Hidden elements are not tabable
|
||||
(0, _utils.isVisible)(el));
|
||||
if (enabledElements.length === 0) return;
|
||||
const orderedElements = enabledElements.map((el, idx) => ({
|
||||
el,
|
||||
idx
|
||||
})).sort((a, b) => {
|
||||
// tabindex has no effect if the active element has tabindex="-1"
|
||||
if (previousElement && previousElement.getAttribute('tabindex') === '-1') {
|
||||
return a.idx - b.idx;
|
||||
}
|
||||
|
||||
const tabIndexA = a.el.getAttribute('tabindex');
|
||||
const tabIndexB = b.el.getAttribute('tabindex');
|
||||
const diff = tabIndexA - tabIndexB;
|
||||
return diff === 0 ? a.idx - b.idx : diff;
|
||||
}).map(({
|
||||
el
|
||||
}) => el);
|
||||
const checkedRadio = {};
|
||||
let prunedElements = [];
|
||||
orderedElements.forEach(el => {
|
||||
// For radio groups keep only the active radio
|
||||
// If there is no active radio, keep only the checked radio
|
||||
// If there is no checked radio, treat like everything else
|
||||
if (el.type === 'radio' && el.name) {
|
||||
// If the active element is part of the group, add only that
|
||||
if (previousElement && previousElement.type === el.type && previousElement.name === el.name) {
|
||||
if (el === previousElement) {
|
||||
prunedElements.push(el);
|
||||
}
|
||||
|
||||
return;
|
||||
} // If we stumble upon a checked radio, remove the others
|
||||
|
||||
|
||||
if (el.checked) {
|
||||
prunedElements = prunedElements.filter(e => e.type !== el.type || e.name !== el.name);
|
||||
prunedElements.push(el);
|
||||
checkedRadio[el.name] = el;
|
||||
return;
|
||||
} // If we already found the checked one, skip
|
||||
|
||||
|
||||
if (checkedRadio[el.name]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
prunedElements.push(el);
|
||||
});
|
||||
const index = prunedElements.findIndex(el => el === previousElement);
|
||||
const nextElement = getNextElement(index, shift, prunedElements, focusTrap);
|
||||
const shiftKeyInit = {
|
||||
key: 'Shift',
|
||||
keyCode: 16,
|
||||
shiftKey: true
|
||||
};
|
||||
const tabKeyInit = {
|
||||
key: 'Tab',
|
||||
keyCode: 9,
|
||||
shiftKey: shift
|
||||
};
|
||||
let continueToTab = true; // not sure how to make it so there's no previous element...
|
||||
// istanbul ignore else
|
||||
|
||||
if (previousElement) {
|
||||
// preventDefault on the shift key makes no difference
|
||||
if (shift) _dom.fireEvent.keyDown(previousElement, { ...shiftKeyInit
|
||||
});
|
||||
continueToTab = _dom.fireEvent.keyDown(previousElement, { ...tabKeyInit
|
||||
});
|
||||
}
|
||||
|
||||
const keyUpTarget = !continueToTab && previousElement ? previousElement : nextElement;
|
||||
|
||||
if (continueToTab) {
|
||||
if (nextElement === document.body) {
|
||||
(0, _blur.blur)(previousElement);
|
||||
} else {
|
||||
(0, _focus.focus)(nextElement);
|
||||
}
|
||||
}
|
||||
|
||||
_dom.fireEvent.keyUp(keyUpTarget, { ...tabKeyInit
|
||||
});
|
||||
|
||||
if (shift) {
|
||||
_dom.fireEvent.keyUp(keyUpTarget, { ...shiftKeyInit,
|
||||
shiftKey: false
|
||||
});
|
||||
}
|
||||
}
|
||||
/*
|
||||
eslint
|
||||
complexity: "off",
|
||||
max-statements: "off",
|
||||
*/
|
867
web/node_modules/@testing-library/user-event/dist/type.js
generated
vendored
Normal file
867
web/node_modules/@testing-library/user-event/dist/type.js
generated
vendored
Normal file
|
@ -0,0 +1,867 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.type = type;
|
||||
exports.specialCharMap = void 0;
|
||||
|
||||
var _dom = require("@testing-library/dom");
|
||||
|
||||
var _utils = require("./utils");
|
||||
|
||||
var _click = require("./click");
|
||||
|
||||
var _navigationKey = require("./keys/navigation-key");
|
||||
|
||||
// TODO: wrap in asyncWrapper
|
||||
const modifierCallbackMap = { ...createModifierCallbackEntries({
|
||||
name: 'shift',
|
||||
key: 'Shift',
|
||||
keyCode: 16,
|
||||
modifierProperty: 'shiftKey'
|
||||
}),
|
||||
...createModifierCallbackEntries({
|
||||
name: 'ctrl',
|
||||
key: 'Control',
|
||||
keyCode: 17,
|
||||
modifierProperty: 'ctrlKey'
|
||||
}),
|
||||
...createModifierCallbackEntries({
|
||||
name: 'alt',
|
||||
key: 'Alt',
|
||||
keyCode: 18,
|
||||
modifierProperty: 'altKey'
|
||||
}),
|
||||
...createModifierCallbackEntries({
|
||||
name: 'meta',
|
||||
key: 'Meta',
|
||||
keyCode: 93,
|
||||
modifierProperty: 'metaKey'
|
||||
}),
|
||||
// capslock is inline because of the need to fire both keydown and keyup on use, while preserving the modifier state.
|
||||
'{capslock}': function capslockOn({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const newEventOverrides = {
|
||||
modifierCapsLock: true
|
||||
};
|
||||
|
||||
_dom.fireEvent.keyDown(currentElement(), {
|
||||
key: 'CapsLock',
|
||||
keyCode: 20,
|
||||
which: 20,
|
||||
...eventOverrides,
|
||||
...newEventOverrides
|
||||
});
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key: 'CapsLock',
|
||||
keyCode: 20,
|
||||
which: 20,
|
||||
...eventOverrides,
|
||||
...newEventOverrides
|
||||
});
|
||||
|
||||
return {
|
||||
eventOverrides: newEventOverrides
|
||||
};
|
||||
},
|
||||
'{/capslock}': function capslockOff({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const newEventOverrides = {
|
||||
modifierCapsLock: false
|
||||
};
|
||||
|
||||
_dom.fireEvent.keyDown(currentElement(), {
|
||||
key: 'CapsLock',
|
||||
keyCode: 20,
|
||||
which: 20,
|
||||
...eventOverrides,
|
||||
...newEventOverrides
|
||||
});
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key: 'CapsLock',
|
||||
keyCode: 20,
|
||||
which: 20,
|
||||
...eventOverrides,
|
||||
...newEventOverrides
|
||||
});
|
||||
|
||||
return {
|
||||
eventOverrides: newEventOverrides
|
||||
};
|
||||
}
|
||||
};
|
||||
const specialCharMap = {
|
||||
arrowLeft: '{arrowleft}',
|
||||
arrowRight: '{arrowright}',
|
||||
arrowDown: '{arrowdown}',
|
||||
arrowUp: '{arrowup}',
|
||||
enter: '{enter}',
|
||||
escape: '{esc}',
|
||||
delete: '{del}',
|
||||
backspace: '{backspace}',
|
||||
home: '{home}',
|
||||
end: '{end}',
|
||||
selectAll: '{selectall}',
|
||||
space: '{space}',
|
||||
whitespace: ' '
|
||||
};
|
||||
exports.specialCharMap = specialCharMap;
|
||||
const specialCharCallbackMap = {
|
||||
[specialCharMap.arrowLeft]: (0, _navigationKey.navigationKey)('ArrowLeft'),
|
||||
[specialCharMap.arrowRight]: (0, _navigationKey.navigationKey)('ArrowRight'),
|
||||
[specialCharMap.arrowDown]: handleArrowDown,
|
||||
[specialCharMap.arrowUp]: handleArrowUp,
|
||||
[specialCharMap.home]: (0, _navigationKey.navigationKey)('Home'),
|
||||
[specialCharMap.end]: (0, _navigationKey.navigationKey)('End'),
|
||||
[specialCharMap.enter]: handleEnter,
|
||||
[specialCharMap.escape]: handleEsc,
|
||||
[specialCharMap.delete]: handleDel,
|
||||
[specialCharMap.backspace]: handleBackspace,
|
||||
[specialCharMap.selectAll]: handleSelectall,
|
||||
[specialCharMap.space]: handleSpace,
|
||||
[specialCharMap.whitespace]: handleSpace
|
||||
};
|
||||
|
||||
function wait(time) {
|
||||
return new Promise(resolve => setTimeout(() => resolve(), time));
|
||||
} // this needs to be wrapped in the event/asyncWrapper for React's act and angular's change detection
|
||||
// depending on whether it will be async.
|
||||
|
||||
|
||||
async function type(element, text, {
|
||||
delay = 0,
|
||||
...options
|
||||
} = {}) {
|
||||
// we do not want to wrap in the asyncWrapper if we're not
|
||||
// going to actually be doing anything async, so we only wrap
|
||||
// if the delay is greater than 0
|
||||
let result;
|
||||
|
||||
if (delay > 0) {
|
||||
await (0, _dom.getConfig)().asyncWrapper(async () => {
|
||||
result = await typeImpl(element, text, {
|
||||
delay,
|
||||
...options
|
||||
});
|
||||
});
|
||||
} else {
|
||||
result = typeImpl(element, text, {
|
||||
delay,
|
||||
...options
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
async function typeImpl(element, text, {
|
||||
delay,
|
||||
skipClick = false,
|
||||
skipAutoClose = false,
|
||||
initialSelectionStart,
|
||||
initialSelectionEnd
|
||||
}) {
|
||||
if (element.disabled) return;
|
||||
if (!skipClick) (0, _click.click)(element);
|
||||
|
||||
if ((0, _utils.isContentEditable)(element) && document.getSelection().rangeCount === 0) {
|
||||
const range = document.createRange();
|
||||
range.setStart(element, 0);
|
||||
range.setEnd(element, 0);
|
||||
document.getSelection().addRange(range);
|
||||
} // The focused element could change between each event, so get the currently active element each time
|
||||
|
||||
|
||||
const currentElement = () => (0, _utils.getActiveElement)(element.ownerDocument); // by default, a new element has it's selection start and end at 0
|
||||
// but most of the time when people call "type", they expect it to type
|
||||
// at the end of the current input value. So, if the selection start
|
||||
// and end are both the default of 0, then we'll go ahead and change
|
||||
// them to the length of the current value.
|
||||
// the only time it would make sense to pass the initialSelectionStart or
|
||||
// initialSelectionEnd is if you have an input with a value and want to
|
||||
// explicitely start typing with the cursor at 0. Not super common.
|
||||
|
||||
|
||||
const value = (0, _utils.getValue)(currentElement());
|
||||
const {
|
||||
selectionStart,
|
||||
selectionEnd
|
||||
} = (0, _utils.getSelectionRange)(element);
|
||||
|
||||
if (value != null && selectionStart === 0 && selectionEnd === 0) {
|
||||
(0, _utils.setSelectionRangeIfNecessary)(currentElement(), initialSelectionStart != null ? initialSelectionStart : value.length, initialSelectionEnd != null ? initialSelectionEnd : value.length);
|
||||
}
|
||||
|
||||
const eventCallbacks = queueCallbacks();
|
||||
await runCallbacks(eventCallbacks);
|
||||
|
||||
function queueCallbacks() {
|
||||
const callbacks = [];
|
||||
let remainingString = text;
|
||||
|
||||
while (remainingString) {
|
||||
const {
|
||||
callback,
|
||||
remainingString: newRemainingString
|
||||
} = getNextCallback(remainingString, skipAutoClose);
|
||||
callbacks.push(callback);
|
||||
remainingString = newRemainingString;
|
||||
}
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
async function runCallbacks(callbacks) {
|
||||
const eventOverrides = {};
|
||||
let prevWasMinus, prevWasPeriod, prevValue, typedValue;
|
||||
|
||||
for (const callback of callbacks) {
|
||||
if (delay > 0) await wait(delay);
|
||||
|
||||
if (!currentElement().disabled) {
|
||||
const returnValue = callback({
|
||||
currentElement,
|
||||
prevWasMinus,
|
||||
prevWasPeriod,
|
||||
prevValue,
|
||||
eventOverrides,
|
||||
typedValue
|
||||
});
|
||||
Object.assign(eventOverrides, returnValue == null ? void 0 : returnValue.eventOverrides);
|
||||
prevWasMinus = returnValue == null ? void 0 : returnValue.prevWasMinus;
|
||||
prevWasPeriod = returnValue == null ? void 0 : returnValue.prevWasPeriod;
|
||||
prevValue = returnValue == null ? void 0 : returnValue.prevValue;
|
||||
typedValue = returnValue == null ? void 0 : returnValue.typedValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNextCallback(remainingString, skipAutoClose) {
|
||||
const modifierCallback = getModifierCallback(remainingString, skipAutoClose);
|
||||
|
||||
if (modifierCallback) {
|
||||
return modifierCallback;
|
||||
}
|
||||
|
||||
const specialCharCallback = getSpecialCharCallback(remainingString);
|
||||
|
||||
if (specialCharCallback) {
|
||||
return specialCharCallback;
|
||||
}
|
||||
|
||||
return getTypeCallback(remainingString);
|
||||
}
|
||||
|
||||
function getModifierCallback(remainingString, skipAutoClose) {
|
||||
const modifierKey = Object.keys(modifierCallbackMap).find(key => remainingString.startsWith(key));
|
||||
|
||||
if (!modifierKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const callback = modifierCallbackMap[modifierKey]; // if this modifier has an associated "close" callback and the developer
|
||||
// doesn't close it themselves, then we close it for them automatically
|
||||
// Effectively if they send in: '{alt}a' then we type: '{alt}a{/alt}'
|
||||
|
||||
if (!skipAutoClose && callback.closeName && !remainingString.includes(callback.closeName)) {
|
||||
remainingString += callback.closeName;
|
||||
}
|
||||
|
||||
remainingString = remainingString.slice(modifierKey.length);
|
||||
return {
|
||||
callback,
|
||||
remainingString
|
||||
};
|
||||
}
|
||||
|
||||
function getSpecialCharCallback(remainingString) {
|
||||
const specialChar = Object.keys(specialCharCallbackMap).find(key => remainingString.startsWith(key));
|
||||
|
||||
if (!specialChar) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
callback: specialCharCallbackMap[specialChar],
|
||||
remainingString: remainingString.slice(specialChar.length)
|
||||
};
|
||||
}
|
||||
|
||||
function getTypeCallback(remainingString) {
|
||||
const character = remainingString[0];
|
||||
|
||||
const callback = context => typeCharacter(character, context);
|
||||
|
||||
return {
|
||||
callback,
|
||||
remainingString: remainingString.slice(1)
|
||||
};
|
||||
}
|
||||
|
||||
function setSelectionRange({
|
||||
currentElement,
|
||||
newValue,
|
||||
newSelectionStart
|
||||
}) {
|
||||
// if we *can* change the selection start, then we will if the new value
|
||||
// is the same as the current value (so it wasn't programatically changed
|
||||
// when the fireEvent.input was triggered).
|
||||
// The reason we have to do this at all is because it actually *is*
|
||||
// programmatically changed by fireEvent.input, so we have to simulate the
|
||||
// browser's default behavior
|
||||
const value = (0, _utils.getValue)(currentElement());
|
||||
|
||||
if (value === newValue) {
|
||||
(0, _utils.setSelectionRangeIfNecessary)(currentElement(), newSelectionStart, newSelectionStart);
|
||||
} else {
|
||||
// If the currentValue is different than the expected newValue and we *can*
|
||||
// change the selection range, than we should set it to the length of the
|
||||
// currentValue to ensure that the browser behavior is mimicked.
|
||||
(0, _utils.setSelectionRangeIfNecessary)(currentElement(), value.length, value.length);
|
||||
}
|
||||
}
|
||||
|
||||
function fireInputEventIfNeeded({
|
||||
currentElement,
|
||||
newValue,
|
||||
newSelectionStart,
|
||||
eventOverrides
|
||||
}) {
|
||||
const prevValue = (0, _utils.getValue)(currentElement());
|
||||
|
||||
if (!currentElement().readOnly && !(0, _utils.isClickableInput)(currentElement()) && newValue !== prevValue) {
|
||||
if ((0, _utils.isContentEditable)(currentElement())) {
|
||||
_dom.fireEvent.input(currentElement(), {
|
||||
target: {
|
||||
textContent: newValue
|
||||
},
|
||||
...eventOverrides
|
||||
});
|
||||
} else {
|
||||
_dom.fireEvent.input(currentElement(), {
|
||||
target: {
|
||||
value: newValue
|
||||
},
|
||||
...eventOverrides
|
||||
});
|
||||
}
|
||||
|
||||
setSelectionRange({
|
||||
currentElement,
|
||||
newValue,
|
||||
newSelectionStart
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
prevValue
|
||||
};
|
||||
}
|
||||
|
||||
function typeCharacter(char, {
|
||||
currentElement,
|
||||
prevWasMinus = false,
|
||||
prevWasPeriod = false,
|
||||
prevValue = '',
|
||||
typedValue = '',
|
||||
eventOverrides
|
||||
}) {
|
||||
const key = char; // TODO: check if this also valid for characters with diacritic markers e.g. úé etc
|
||||
|
||||
const keyCode = char.charCodeAt(0);
|
||||
let nextPrevWasMinus, nextPrevWasPeriod;
|
||||
const textToBeTyped = typedValue + char;
|
||||
|
||||
const keyDownDefaultNotPrevented = _dom.fireEvent.keyDown(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
if (keyDownDefaultNotPrevented) {
|
||||
const keyPressDefaultNotPrevented = _dom.fireEvent.keyPress(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
charCode: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
if ((0, _utils.getValue)(currentElement()) != null && keyPressDefaultNotPrevented) {
|
||||
let newEntry = char;
|
||||
|
||||
if (prevWasMinus) {
|
||||
newEntry = `-${char}`;
|
||||
} else if (prevWasPeriod) {
|
||||
newEntry = `${prevValue}.${char}`;
|
||||
}
|
||||
|
||||
if ((0, _utils.isValidDateValue)(currentElement(), textToBeTyped)) {
|
||||
newEntry = textToBeTyped;
|
||||
}
|
||||
|
||||
const timeNewEntry = (0, _utils.buildTimeValue)(textToBeTyped);
|
||||
|
||||
if ((0, _utils.isValidInputTimeValue)(currentElement(), timeNewEntry)) {
|
||||
newEntry = timeNewEntry;
|
||||
}
|
||||
|
||||
const inputEvent = fireInputEventIfNeeded({ ...(0, _utils.calculateNewValue)(newEntry, currentElement()),
|
||||
eventOverrides: {
|
||||
data: key,
|
||||
inputType: 'insertText',
|
||||
...eventOverrides
|
||||
},
|
||||
currentElement
|
||||
});
|
||||
prevValue = inputEvent.prevValue;
|
||||
|
||||
if ((0, _utils.isValidDateValue)(currentElement(), textToBeTyped)) {
|
||||
_dom.fireEvent.change(currentElement(), {
|
||||
target: {
|
||||
value: textToBeTyped
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fireChangeForInputTimeIfValid(currentElement, prevValue, timeNewEntry); // typing "-" into a number input will not actually update the value
|
||||
// so for the next character we type, the value should be set to
|
||||
// `-${newEntry}`
|
||||
// we also preserve the prevWasMinus when the value is unchanged due
|
||||
// to typing an invalid character (typing "-a3" results in "-3")
|
||||
// same applies for the decimal character.
|
||||
|
||||
if (currentElement().type === 'number') {
|
||||
const newValue = (0, _utils.getValue)(currentElement());
|
||||
|
||||
if (newValue === prevValue && newEntry !== '-') {
|
||||
nextPrevWasMinus = prevWasMinus;
|
||||
} else {
|
||||
nextPrevWasMinus = newEntry === '-';
|
||||
}
|
||||
|
||||
if (newValue === prevValue && newEntry !== '.') {
|
||||
nextPrevWasPeriod = prevWasPeriod;
|
||||
} else {
|
||||
nextPrevWasPeriod = newEntry === '.';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
return {
|
||||
prevWasMinus: nextPrevWasMinus,
|
||||
prevWasPeriod: nextPrevWasPeriod,
|
||||
prevValue,
|
||||
typedValue: textToBeTyped
|
||||
};
|
||||
}
|
||||
|
||||
function fireChangeForInputTimeIfValid(currentElement, prevValue, timeNewEntry) {
|
||||
if ((0, _utils.isValidInputTimeValue)(currentElement(), timeNewEntry) && prevValue !== timeNewEntry) {
|
||||
_dom.fireEvent.change(currentElement(), {
|
||||
target: {
|
||||
value: timeNewEntry
|
||||
}
|
||||
});
|
||||
}
|
||||
} // yes, calculateNewBackspaceValue and calculateNewValue look extremely similar
|
||||
// and you may be tempted to create a shared abstraction.
|
||||
// If you, brave soul, decide to so endevor, please increment this count
|
||||
// when you inevitably fail: 1
|
||||
|
||||
|
||||
function calculateNewBackspaceValue(element) {
|
||||
const {
|
||||
selectionStart,
|
||||
selectionEnd
|
||||
} = (0, _utils.getSelectionRange)(element);
|
||||
const value = (0, _utils.getValue)(element);
|
||||
let newValue, newSelectionStart;
|
||||
|
||||
if (selectionStart === null) {
|
||||
// at the end of an input type that does not support selection ranges
|
||||
// https://github.com/testing-library/user-event/issues/316#issuecomment-639744793
|
||||
newValue = value.slice(0, value.length - 1);
|
||||
newSelectionStart = selectionStart - 1;
|
||||
} else if (selectionStart === selectionEnd) {
|
||||
if (selectionStart === 0) {
|
||||
// at the beginning of the input
|
||||
newValue = value;
|
||||
newSelectionStart = selectionStart;
|
||||
} else if (selectionStart === value.length) {
|
||||
// at the end of the input
|
||||
newValue = value.slice(0, value.length - 1);
|
||||
newSelectionStart = selectionStart - 1;
|
||||
} else {
|
||||
// in the middle of the input
|
||||
newValue = value.slice(0, selectionStart - 1) + value.slice(selectionEnd);
|
||||
newSelectionStart = selectionStart - 1;
|
||||
}
|
||||
} else {
|
||||
// we have something selected
|
||||
const firstPart = value.slice(0, selectionStart);
|
||||
newValue = firstPart + value.slice(selectionEnd);
|
||||
newSelectionStart = firstPart.length;
|
||||
}
|
||||
|
||||
return {
|
||||
newValue,
|
||||
newSelectionStart
|
||||
};
|
||||
}
|
||||
|
||||
function calculateNewDeleteValue(element) {
|
||||
const {
|
||||
selectionStart,
|
||||
selectionEnd
|
||||
} = (0, _utils.getSelectionRange)(element);
|
||||
const value = (0, _utils.getValue)(element);
|
||||
let newValue;
|
||||
|
||||
if (selectionStart === null) {
|
||||
// at the end of an input type that does not support selection ranges
|
||||
// https://github.com/testing-library/user-event/issues/316#issuecomment-639744793
|
||||
newValue = value;
|
||||
} else if (selectionStart === selectionEnd) {
|
||||
if (selectionStart === 0) {
|
||||
// at the beginning of the input
|
||||
newValue = value.slice(1);
|
||||
} else if (selectionStart === value.length) {
|
||||
// at the end of the input
|
||||
newValue = value;
|
||||
} else {
|
||||
// in the middle of the input
|
||||
newValue = value.slice(0, selectionStart) + value.slice(selectionEnd + 1);
|
||||
}
|
||||
} else {
|
||||
// we have something selected
|
||||
const firstPart = value.slice(0, selectionStart);
|
||||
newValue = firstPart + value.slice(selectionEnd);
|
||||
}
|
||||
|
||||
return {
|
||||
newValue,
|
||||
newSelectionStart: selectionStart
|
||||
};
|
||||
}
|
||||
|
||||
function createModifierCallbackEntries({
|
||||
name,
|
||||
key,
|
||||
keyCode,
|
||||
modifierProperty
|
||||
}) {
|
||||
const openName = `{${name}}`;
|
||||
const closeName = `{/${name}}`;
|
||||
|
||||
function open({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const newEventOverrides = {
|
||||
[modifierProperty]: true
|
||||
};
|
||||
|
||||
_dom.fireEvent.keyDown(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides,
|
||||
...newEventOverrides
|
||||
});
|
||||
|
||||
return {
|
||||
eventOverrides: newEventOverrides
|
||||
};
|
||||
}
|
||||
|
||||
open.closeName = closeName;
|
||||
|
||||
function close({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const newEventOverrides = {
|
||||
[modifierProperty]: false
|
||||
};
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides,
|
||||
...newEventOverrides
|
||||
});
|
||||
|
||||
return {
|
||||
eventOverrides: newEventOverrides
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
[openName]: open,
|
||||
[closeName]: close
|
||||
};
|
||||
}
|
||||
|
||||
function handleEnter({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const key = 'Enter';
|
||||
const keyCode = 13;
|
||||
|
||||
const keyDownDefaultNotPrevented = _dom.fireEvent.keyDown(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
if (keyDownDefaultNotPrevented) {
|
||||
const keyPressDefaultNotPrevented = _dom.fireEvent.keyPress(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
charCode: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
if (keyPressDefaultNotPrevented) {
|
||||
if ((0, _utils.isClickableInput)(currentElement()) || // Links with href defined should handle Enter the same as a click
|
||||
(0, _utils.isInstanceOfElement)(currentElement(), 'HTMLAnchorElement') && currentElement().href) {
|
||||
_dom.fireEvent.click(currentElement(), { ...eventOverrides
|
||||
});
|
||||
}
|
||||
|
||||
if (currentElement().tagName === 'TEXTAREA') {
|
||||
const {
|
||||
newValue,
|
||||
newSelectionStart
|
||||
} = (0, _utils.calculateNewValue)('\n', currentElement());
|
||||
|
||||
_dom.fireEvent.input(currentElement(), {
|
||||
target: {
|
||||
value: newValue
|
||||
},
|
||||
inputType: 'insertLineBreak',
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
setSelectionRange({
|
||||
currentElement,
|
||||
newValue,
|
||||
newSelectionStart
|
||||
});
|
||||
}
|
||||
|
||||
if (currentElement().tagName === 'INPUT' && currentElement().form && (currentElement().form.querySelectorAll('input').length === 1 || currentElement().form.querySelector('input[type="submit"]') || currentElement().form.querySelector('button[type="submit"]'))) {
|
||||
_dom.fireEvent.submit(currentElement().form);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
}
|
||||
|
||||
function handleEsc({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const key = 'Escape';
|
||||
const keyCode = 27;
|
||||
|
||||
_dom.fireEvent.keyDown(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
}); // NOTE: Browsers do not fire a keypress on meta key presses
|
||||
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
}
|
||||
|
||||
function handleDel({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const key = 'Delete';
|
||||
const keyCode = 46;
|
||||
|
||||
const keyPressDefaultNotPrevented = _dom.fireEvent.keyDown(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
if (keyPressDefaultNotPrevented) {
|
||||
fireInputEventIfNeeded({ ...calculateNewDeleteValue(currentElement()),
|
||||
eventOverrides: {
|
||||
inputType: 'deleteContentForward',
|
||||
...eventOverrides
|
||||
},
|
||||
currentElement
|
||||
});
|
||||
}
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
}
|
||||
|
||||
function handleBackspace({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const key = 'Backspace';
|
||||
const keyCode = 8;
|
||||
|
||||
const keyPressDefaultNotPrevented = _dom.fireEvent.keyDown(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
if (keyPressDefaultNotPrevented) {
|
||||
fireInputEventIfNeeded({ ...calculateNewBackspaceValue(currentElement()),
|
||||
eventOverrides: {
|
||||
inputType: 'deleteContentBackward',
|
||||
...eventOverrides
|
||||
},
|
||||
currentElement
|
||||
});
|
||||
}
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
}
|
||||
|
||||
function handleSelectall({
|
||||
currentElement
|
||||
}) {
|
||||
currentElement().setSelectionRange(0, (0, _utils.getValue)(currentElement()).length);
|
||||
}
|
||||
|
||||
function handleSpace(context) {
|
||||
if ((0, _utils.isClickableInput)(context.currentElement())) {
|
||||
handleSpaceOnClickable(context);
|
||||
return;
|
||||
}
|
||||
|
||||
typeCharacter(' ', context);
|
||||
}
|
||||
|
||||
function handleSpaceOnClickable({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const key = ' ';
|
||||
const keyCode = 32;
|
||||
|
||||
const keyDownDefaultNotPrevented = _dom.fireEvent.keyDown(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
if (keyDownDefaultNotPrevented) {
|
||||
_dom.fireEvent.keyPress(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
charCode: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
}
|
||||
|
||||
const keyUpDefaultNotPrevented = _dom.fireEvent.keyUp(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
if (keyDownDefaultNotPrevented && keyUpDefaultNotPrevented) {
|
||||
_dom.fireEvent.click(currentElement(), { ...eventOverrides
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function handleArrowDown({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const key = 'ArrowDown';
|
||||
const keyCode = 40;
|
||||
|
||||
_dom.fireEvent.keyDown(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
}
|
||||
|
||||
function handleArrowUp({
|
||||
currentElement,
|
||||
eventOverrides
|
||||
}) {
|
||||
const key = 'ArrowUp';
|
||||
const keyCode = 38;
|
||||
|
||||
_dom.fireEvent.keyDown(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
|
||||
_dom.fireEvent.keyUp(currentElement(), {
|
||||
key,
|
||||
keyCode,
|
||||
which: keyCode,
|
||||
...eventOverrides
|
||||
});
|
||||
}
|
74
web/node_modules/@testing-library/user-event/dist/upload.js
generated
vendored
Normal file
74
web/node_modules/@testing-library/user-event/dist/upload.js
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.upload = upload;
|
||||
|
||||
var _dom = require("@testing-library/dom");
|
||||
|
||||
var _click = require("./click");
|
||||
|
||||
var _blur = require("./blur");
|
||||
|
||||
var _focus = require("./focus");
|
||||
|
||||
function upload(element, fileOrFiles, init, {
|
||||
applyAccept = false
|
||||
} = {}) {
|
||||
if (element.disabled) return;
|
||||
(0, _click.click)(element, init);
|
||||
const input = element.tagName === 'LABEL' ? element.control : element;
|
||||
const files = (Array.isArray(fileOrFiles) ? fileOrFiles : [fileOrFiles]).filter(file => !applyAccept || isAcceptableFile(file, element.accept)).slice(0, input.multiple ? undefined : 1); // blur fires when the file selector pops up
|
||||
|
||||
(0, _blur.blur)(element, init); // focus fires when they make their selection
|
||||
|
||||
(0, _focus.focus)(element, init); // do not fire an input event if the file selection does not change
|
||||
|
||||
if (files.length === input.files.length && files.every((f, i) => f === input.files.item(i))) {
|
||||
return;
|
||||
} // the event fired in the browser isn't actually an "input" or "change" event
|
||||
// but a new Event with a type set to "input" and "change"
|
||||
// Kinda odd...
|
||||
|
||||
|
||||
const inputFiles = {
|
||||
length: files.length,
|
||||
item: index => files[index],
|
||||
...files
|
||||
};
|
||||
(0, _dom.fireEvent)(input, (0, _dom.createEvent)('input', input, {
|
||||
target: {
|
||||
files: inputFiles
|
||||
},
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
composed: true,
|
||||
...init
|
||||
}));
|
||||
|
||||
_dom.fireEvent.change(input, {
|
||||
target: {
|
||||
files: inputFiles
|
||||
},
|
||||
...init
|
||||
});
|
||||
}
|
||||
|
||||
function isAcceptableFile(file, accept) {
|
||||
if (!accept) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const wildcards = ['audio/*', 'image/*', 'video/*'];
|
||||
return accept.split(',').some(acceptToken => {
|
||||
if (acceptToken[0] === '.') {
|
||||
// tokens starting with a dot represent a file extension
|
||||
return file.name.endsWith(acceptToken);
|
||||
} else if (wildcards.includes(acceptToken)) {
|
||||
return file.type.startsWith(acceptToken.substr(0, acceptToken.length - 1));
|
||||
}
|
||||
|
||||
return file.type === acceptToken;
|
||||
});
|
||||
}
|
354
web/node_modules/@testing-library/user-event/dist/utils.js
generated
vendored
Normal file
354
web/node_modules/@testing-library/user-event/dist/utils.js
generated
vendored
Normal file
|
@ -0,0 +1,354 @@
|
|||
"use strict";
|
||||
|
||||
Object.defineProperty(exports, "__esModule", {
|
||||
value: true
|
||||
});
|
||||
exports.isFocusable = isFocusable;
|
||||
exports.isClickableInput = isClickableInput;
|
||||
exports.getMouseEventOptions = getMouseEventOptions;
|
||||
exports.isLabelWithInternallyDisabledControl = isLabelWithInternallyDisabledControl;
|
||||
exports.getActiveElement = getActiveElement;
|
||||
exports.calculateNewValue = calculateNewValue;
|
||||
exports.setSelectionRangeIfNecessary = setSelectionRangeIfNecessary;
|
||||
exports.eventWrapper = eventWrapper;
|
||||
exports.isValidDateValue = isValidDateValue;
|
||||
exports.isValidInputTimeValue = isValidInputTimeValue;
|
||||
exports.buildTimeValue = buildTimeValue;
|
||||
exports.getValue = getValue;
|
||||
exports.getSelectionRange = getSelectionRange;
|
||||
exports.isContentEditable = isContentEditable;
|
||||
exports.isInstanceOfElement = isInstanceOfElement;
|
||||
exports.isVisible = isVisible;
|
||||
exports.FOCUSABLE_SELECTOR = void 0;
|
||||
|
||||
var _dom = require("@testing-library/dom");
|
||||
|
||||
var _helpers = require("@testing-library/dom/dist/helpers");
|
||||
|
||||
// isInstanceOfElement can be removed once the peerDependency for @testing-library/dom is bumped to a version that includes https://github.com/testing-library/dom-testing-library/pull/885
|
||||
|
||||
/**
|
||||
* Check if an element is of a given type.
|
||||
*
|
||||
* @param {Element} element The element to test
|
||||
* @param {string} elementType Constructor name. E.g. 'HTMLSelectElement'
|
||||
*/
|
||||
function isInstanceOfElement(element, elementType) {
|
||||
try {
|
||||
const window = (0, _helpers.getWindowFromNode)(element); // Window usually has the element constructors as properties but is not required to do so per specs
|
||||
|
||||
if (typeof window[elementType] === 'function') {
|
||||
return element instanceof window[elementType];
|
||||
}
|
||||
} catch (e) {// The document might not be associated with a window
|
||||
} // Fall back to the constructor name as workaround for test environments that
|
||||
// a) not associate the document with a window
|
||||
// b) not provide the constructor as property of window
|
||||
|
||||
|
||||
if (/^HTML(\w+)Element$/.test(element.constructor.name)) {
|
||||
return element.constructor.name === elementType;
|
||||
} // The user passed some node that is not created in a browser-like environment
|
||||
|
||||
|
||||
throw new Error(`Unable to verify if element is instance of ${elementType}. Please file an issue describing your test environment: https://github.com/testing-library/dom-testing-library/issues/new`);
|
||||
}
|
||||
|
||||
function isMousePressEvent(event) {
|
||||
return event === 'mousedown' || event === 'mouseup' || event === 'click' || event === 'dblclick';
|
||||
}
|
||||
|
||||
function invert(map) {
|
||||
const res = {};
|
||||
|
||||
for (const key of Object.keys(map)) {
|
||||
res[map[key]] = key;
|
||||
}
|
||||
|
||||
return res;
|
||||
} // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
|
||||
|
||||
|
||||
const BUTTONS_TO_NAMES = {
|
||||
0: 'none',
|
||||
1: 'primary',
|
||||
2: 'secondary',
|
||||
4: 'auxiliary'
|
||||
};
|
||||
const NAMES_TO_BUTTONS = invert(BUTTONS_TO_NAMES); // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
|
||||
|
||||
const BUTTON_TO_NAMES = {
|
||||
0: 'primary',
|
||||
1: 'auxiliary',
|
||||
2: 'secondary'
|
||||
};
|
||||
const NAMES_TO_BUTTON = invert(BUTTON_TO_NAMES);
|
||||
|
||||
function convertMouseButtons(event, init, property, mapping) {
|
||||
if (!isMousePressEvent(event)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (init[property] != null) {
|
||||
return init[property];
|
||||
}
|
||||
|
||||
if (init.buttons != null) {
|
||||
// not sure how to test this. Feel free to try and add a test if you want.
|
||||
// istanbul ignore next
|
||||
return mapping[BUTTONS_TO_NAMES[init.buttons]] || 0;
|
||||
}
|
||||
|
||||
if (init.button != null) {
|
||||
// not sure how to test this. Feel free to try and add a test if you want.
|
||||
// istanbul ignore next
|
||||
return mapping[BUTTON_TO_NAMES[init.button]] || 0;
|
||||
}
|
||||
|
||||
return property != 'button' && isMousePressEvent(event) ? 1 : 0;
|
||||
}
|
||||
|
||||
function getMouseEventOptions(event, init, clickCount = 0) {
|
||||
init = init || {};
|
||||
return { ...init,
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail
|
||||
detail: event === 'mousedown' || event === 'mouseup' || event === 'click' ? 1 + clickCount : clickCount,
|
||||
buttons: convertMouseButtons(event, init, 'buttons', NAMES_TO_BUTTONS),
|
||||
button: convertMouseButtons(event, init, 'button', NAMES_TO_BUTTON)
|
||||
};
|
||||
} // Absolutely NO events fire on label elements that contain their control
|
||||
// if that control is disabled. NUTS!
|
||||
// no joke. There are NO events for: <label><input disabled /><label>
|
||||
|
||||
|
||||
function isLabelWithInternallyDisabledControl(element) {
|
||||
var _element$control;
|
||||
|
||||
return element.tagName === 'LABEL' && ((_element$control = element.control) == null ? void 0 : _element$control.disabled) && element.contains(element.control);
|
||||
}
|
||||
|
||||
function getActiveElement(document) {
|
||||
const activeElement = document.activeElement;
|
||||
|
||||
if (activeElement != null && activeElement.shadowRoot) {
|
||||
return getActiveElement(activeElement.shadowRoot);
|
||||
} else {
|
||||
return activeElement;
|
||||
}
|
||||
}
|
||||
|
||||
function supportsMaxLength(element) {
|
||||
if (element.tagName === 'TEXTAREA') return true;
|
||||
|
||||
if (element.tagName === 'INPUT') {
|
||||
const type = element.getAttribute('type'); // Missing value default is "text"
|
||||
|
||||
if (!type) return true; // https://html.spec.whatwg.org/multipage/input.html#concept-input-apply
|
||||
|
||||
if (type.match(/email|password|search|telephone|text|url/)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function getSelectionRange(element) {
|
||||
if (isContentEditable(element)) {
|
||||
const range = element.ownerDocument.getSelection().getRangeAt(0);
|
||||
return {
|
||||
selectionStart: range.startOffset,
|
||||
selectionEnd: range.endOffset
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
selectionStart: element.selectionStart,
|
||||
selectionEnd: element.selectionEnd
|
||||
};
|
||||
} //jsdom is not supporting isContentEditable
|
||||
|
||||
|
||||
function isContentEditable(element) {
|
||||
return element.hasAttribute('contenteditable') && (element.getAttribute('contenteditable') == 'true' || element.getAttribute('contenteditable') == '');
|
||||
}
|
||||
|
||||
function getValue(element) {
|
||||
if (isContentEditable(element)) {
|
||||
return element.textContent;
|
||||
}
|
||||
|
||||
return element.value;
|
||||
}
|
||||
|
||||
function calculateNewValue(newEntry, element) {
|
||||
var _element$getAttribute;
|
||||
|
||||
const {
|
||||
selectionStart,
|
||||
selectionEnd
|
||||
} = getSelectionRange(element);
|
||||
const value = getValue(element); // can't use .maxLength property because of a jsdom bug:
|
||||
// https://github.com/jsdom/jsdom/issues/2927
|
||||
|
||||
const maxLength = Number((_element$getAttribute = element.getAttribute('maxlength')) != null ? _element$getAttribute : -1);
|
||||
let newValue, newSelectionStart;
|
||||
|
||||
if (selectionStart === null) {
|
||||
// at the end of an input type that does not support selection ranges
|
||||
// https://github.com/testing-library/user-event/issues/316#issuecomment-639744793
|
||||
newValue = value + newEntry;
|
||||
} else if (selectionStart === selectionEnd) {
|
||||
if (selectionStart === 0) {
|
||||
// at the beginning of the input
|
||||
newValue = newEntry + value;
|
||||
} else if (selectionStart === value.length) {
|
||||
// at the end of the input
|
||||
newValue = value + newEntry;
|
||||
} else {
|
||||
// in the middle of the input
|
||||
newValue = value.slice(0, selectionStart) + newEntry + value.slice(selectionEnd);
|
||||
}
|
||||
|
||||
newSelectionStart = selectionStart + newEntry.length;
|
||||
} else {
|
||||
// we have something selected
|
||||
const firstPart = value.slice(0, selectionStart) + newEntry;
|
||||
newValue = firstPart + value.slice(selectionEnd);
|
||||
newSelectionStart = firstPart.length;
|
||||
}
|
||||
|
||||
if (element.type === 'date' && !isValidDateValue(element, newValue)) {
|
||||
newValue = value;
|
||||
}
|
||||
|
||||
if (element.type === 'time' && !isValidInputTimeValue(element, newValue)) {
|
||||
if (isValidInputTimeValue(element, newEntry)) {
|
||||
newValue = newEntry;
|
||||
} else {
|
||||
newValue = value;
|
||||
}
|
||||
}
|
||||
|
||||
if (!supportsMaxLength(element) || maxLength < 0) {
|
||||
return {
|
||||
newValue,
|
||||
newSelectionStart
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
newValue: newValue.slice(0, maxLength),
|
||||
newSelectionStart: newSelectionStart > maxLength ? maxLength : newSelectionStart
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function setSelectionRangeIfNecessary(element, newSelectionStart, newSelectionEnd) {
|
||||
const {
|
||||
selectionStart,
|
||||
selectionEnd
|
||||
} = getSelectionRange(element);
|
||||
|
||||
if (!isContentEditable(element) && (!element.setSelectionRange || selectionStart === null)) {
|
||||
// cannot set selection
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectionStart !== newSelectionStart || selectionEnd !== newSelectionStart) {
|
||||
if (isContentEditable(element)) {
|
||||
const range = element.ownerDocument.createRange();
|
||||
range.selectNodeContents(element);
|
||||
range.setStart(element.firstChild, newSelectionStart);
|
||||
range.setEnd(element.firstChild, newSelectionEnd);
|
||||
element.ownerDocument.getSelection().removeAllRanges();
|
||||
element.ownerDocument.getSelection().addRange(range);
|
||||
} else {
|
||||
element.setSelectionRange(newSelectionStart, newSelectionEnd);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const FOCUSABLE_SELECTOR = ['input:not([type=hidden]):not([disabled])', 'button:not([disabled])', 'select:not([disabled])', 'textarea:not([disabled])', '[contenteditable=""]', '[contenteditable="true"]', 'a[href]', '[tabindex]:not([disabled])'].join(', ');
|
||||
exports.FOCUSABLE_SELECTOR = FOCUSABLE_SELECTOR;
|
||||
|
||||
function isFocusable(element) {
|
||||
return !isLabelWithInternallyDisabledControl(element) && (element == null ? void 0 : element.matches(FOCUSABLE_SELECTOR));
|
||||
}
|
||||
|
||||
const CLICKABLE_INPUT_TYPES = ['button', 'color', 'file', 'image', 'reset', 'submit'];
|
||||
|
||||
function isClickableInput(element) {
|
||||
return element.tagName === 'BUTTON' || isInstanceOfElement(element, 'HTMLInputElement') && CLICKABLE_INPUT_TYPES.includes(element.type);
|
||||
}
|
||||
|
||||
function isVisible(element) {
|
||||
const getComputedStyle = (0, _helpers.getWindowFromNode)(element).getComputedStyle;
|
||||
|
||||
for (; element && element.ownerDocument; element = element.parentNode) {
|
||||
const display = getComputedStyle(element).display;
|
||||
|
||||
if (display === 'none') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function eventWrapper(cb) {
|
||||
let result;
|
||||
(0, _dom.getConfig)().eventWrapper(() => {
|
||||
result = cb();
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function isValidDateValue(element, value) {
|
||||
if (element.type !== 'date') return false;
|
||||
const clone = element.cloneNode();
|
||||
clone.value = value;
|
||||
return clone.value === value;
|
||||
}
|
||||
|
||||
function buildTimeValue(value) {
|
||||
function build(onlyDigitsValue, index) {
|
||||
const hours = onlyDigitsValue.slice(0, index);
|
||||
const validHours = Math.min(parseInt(hours, 10), 23);
|
||||
const minuteCharacters = onlyDigitsValue.slice(index);
|
||||
const parsedMinutes = parseInt(minuteCharacters, 10);
|
||||
const validMinutes = Math.min(parsedMinutes, 59);
|
||||
return `${validHours.toString().padStart(2, '0')}:${validMinutes.toString().padStart(2, '0')}`;
|
||||
}
|
||||
|
||||
const onlyDigitsValue = value.replace(/\D/g, '');
|
||||
|
||||
if (onlyDigitsValue.length < 2) {
|
||||
return value;
|
||||
}
|
||||
|
||||
const firstDigit = parseInt(onlyDigitsValue[0], 10);
|
||||
const secondDigit = parseInt(onlyDigitsValue[1], 10);
|
||||
|
||||
if (firstDigit >= 3 || firstDigit === 2 && secondDigit >= 4) {
|
||||
let index;
|
||||
|
||||
if (firstDigit >= 3) {
|
||||
index = 1;
|
||||
} else {
|
||||
index = 2;
|
||||
}
|
||||
|
||||
return build(onlyDigitsValue, index);
|
||||
}
|
||||
|
||||
if (value.length === 2) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return build(onlyDigitsValue, 2);
|
||||
}
|
||||
|
||||
function isValidInputTimeValue(element, timeValue) {
|
||||
if (element.type !== 'time') return false;
|
||||
const clone = element.cloneNode();
|
||||
clone.value = timeValue;
|
||||
return clone.value === timeValue;
|
||||
}
|
82
web/node_modules/@testing-library/user-event/package.json
generated
vendored
Normal file
82
web/node_modules/@testing-library/user-event/package.json
generated
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
{
|
||||
"name": "@testing-library/user-event",
|
||||
"version": "12.8.3",
|
||||
"description": "Fire events the same way the user does",
|
||||
"main": "dist/index.js",
|
||||
"typings": "typings/index.d.ts",
|
||||
"keywords": [
|
||||
"react-testing-library",
|
||||
"dom-testing-library",
|
||||
"react",
|
||||
"testing"
|
||||
],
|
||||
"author": "Giorgio Polvara <polvara@gmail.com>",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/testing-library/user-event"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/testing-library/user-event/issues"
|
||||
},
|
||||
"homepage": "https://github.com/testing-library/user-event#readme",
|
||||
"files": [
|
||||
"dist",
|
||||
"typings/index.d.ts"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "kcd-scripts build",
|
||||
"lint": "kcd-scripts lint",
|
||||
"setup": "npm install && npm run validate -s",
|
||||
"test": "kcd-scripts test",
|
||||
"test:debug": "kcd-scripts --inspect-brk test --runInBand",
|
||||
"test:update": "npm test -- --updateSnapshot --coverage",
|
||||
"validate": "kcd-scripts validate",
|
||||
"typecheck": "kcd-scripts typecheck --build typings"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@testing-library/dom": "^7.28.1",
|
||||
"@testing-library/jest-dom": "^5.11.6",
|
||||
"@types/estree": "0.0.45",
|
||||
"is-ci": "^2.0.0",
|
||||
"jest-serializer-ansi": "^1.0.3",
|
||||
"kcd-scripts": "^7.5.1",
|
||||
"typescript": "^4.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@testing-library/dom": ">=7.21.4"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "./node_modules/kcd-scripts/eslint.js",
|
||||
"rules": {
|
||||
"jsx-a11y/click-events-have-key-events": "off",
|
||||
"jsx-a11y/tabindex-no-positive": "off",
|
||||
"no-func-assign": "off",
|
||||
"no-return-assign": "off",
|
||||
"react/prop-types": "off",
|
||||
"testing-library/no-dom-import": "off"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/__tests__/**"
|
||||
],
|
||||
"rules": {
|
||||
"no-console": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"eslintIgnore": [
|
||||
"node_modules",
|
||||
"coverage",
|
||||
"dist"
|
||||
]
|
||||
}
|
97
web/node_modules/@testing-library/user-event/typings/index.d.ts
generated
vendored
Normal file
97
web/node_modules/@testing-library/user-event/typings/index.d.ts
generated
vendored
Normal file
|
@ -0,0 +1,97 @@
|
|||
// Definitions by: Wu Haotian <https://github.com/whtsky>
|
||||
export interface ITypeOpts {
|
||||
skipClick?: boolean
|
||||
skipAutoClose?: boolean
|
||||
delay?: number
|
||||
initialSelectionStart?: number
|
||||
initialSelectionEnd?: number
|
||||
}
|
||||
|
||||
export interface ITabUserOptions {
|
||||
shift?: boolean
|
||||
focusTrap?: Document | Element
|
||||
}
|
||||
|
||||
export type TargetElement = Element | Window
|
||||
|
||||
export type FilesArgument = File | File[]
|
||||
|
||||
export type UploadInitArgument = {
|
||||
clickInit?: MouseEventInit
|
||||
changeInit?: Event
|
||||
}
|
||||
|
||||
export interface IClickOptions {
|
||||
skipHover?: boolean
|
||||
clickCount?: number
|
||||
}
|
||||
|
||||
export interface IUploadOptions {
|
||||
applyAccept?: boolean
|
||||
}
|
||||
|
||||
declare const userEvent: {
|
||||
clear: (element: TargetElement) => void
|
||||
click: (
|
||||
element: TargetElement,
|
||||
init?: MouseEventInit,
|
||||
options?: IClickOptions,
|
||||
) => void
|
||||
dblClick: (
|
||||
element: TargetElement,
|
||||
init?: MouseEventInit,
|
||||
options?: IClickOptions,
|
||||
) => void
|
||||
selectOptions: (
|
||||
element: TargetElement,
|
||||
values: string | string[] | HTMLElement | HTMLElement[],
|
||||
init?: MouseEventInit,
|
||||
) => void
|
||||
deselectOptions: (
|
||||
element: TargetElement,
|
||||
values: string | string[] | HTMLElement | HTMLElement[],
|
||||
init?: MouseEventInit,
|
||||
) => void
|
||||
upload: (
|
||||
element: TargetElement,
|
||||
files: FilesArgument,
|
||||
init?: UploadInitArgument,
|
||||
options?: IUploadOptions,
|
||||
) => void
|
||||
type: <T extends ITypeOpts>(
|
||||
element: TargetElement,
|
||||
text: string,
|
||||
userOpts?: T,
|
||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||
) => T extends {delay: number} ? Promise<void> : void
|
||||
tab: (userOpts?: ITabUserOptions) => void
|
||||
paste: (
|
||||
element: TargetElement,
|
||||
text: string,
|
||||
init?: MouseEventInit,
|
||||
pasteOptions?: {
|
||||
initialSelectionStart?: number
|
||||
initialSelectionEnd?: number
|
||||
},
|
||||
) => void
|
||||
hover: (element: TargetElement, init?: MouseEventInit) => void
|
||||
unhover: (element: TargetElement, init?: MouseEventInit) => void
|
||||
}
|
||||
|
||||
export default userEvent
|
||||
|
||||
export enum specialChars {
|
||||
arrowLeft = '{arrowleft}',
|
||||
arrowRight = '{arrowright}',
|
||||
arrowDown = '{arrowdown}',
|
||||
arrowUp = '{arrowup}',
|
||||
enter = '{enter}',
|
||||
escape = '{esc}',
|
||||
delete = '{del}',
|
||||
backspace = '{backspace}',
|
||||
home = '{home}',
|
||||
end = '{end}',
|
||||
selectAll = '{selectall}',
|
||||
space = '{space}',
|
||||
whitespace = ' ',
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue