This commit is contained in:
Iliyan Angelov
2025-09-14 23:24:25 +03:00
commit c67067a2a4
71311 changed files with 6800714 additions and 0 deletions

View 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).

View 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.

View File

@@ -0,0 +1,308 @@
<div align="center">
<h1>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/main/other/dog.png"
/>
</a>
<p>Fire events the same way the user does</p>
<br />
[**Read The Docs**](https://testing-library.com/docs/ecosystem-user-event) |
[Edit the docs](https://github.com/testing-library/testing-library-docs)
<br />
</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]
[![Discord][discord-badge]][discord]
[![Watch on GitHub][github-watch-badge]][github-watch]
[![Star on GitHub][github-star-badge]][github-star]
[![Tweet][twitter-badge]][twitter]
<!-- prettier-ignore-end -->
## 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 -->
- [The problem](#the-problem)
- [The solution](#the-solution)
- [Installation](#installation)
- [Docs](#docs)
- [Known limitations](#known-limitations)
- [Issues](#issues)
- [🐛 Bugs](#-bugs)
- [💡 Feature Requests](#-feature-requests)
- [❓ Questions](#-questions)
- [Contributors](#contributors)
- [LICENSE](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## 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`][fire-event]
## 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 more your tests resemble the way your software is used, the more
> confidence they can give you.][guiding-principle]
## Installation
With NPM:
```sh
npm install --save-dev @testing-library/user-event @testing-library/dom
```
With Yarn:
```sh
yarn add --dev @testing-library/user-event @testing-library/dom
```
## Docs
[**Read The Docs**](https://testing-library.com/docs/ecosystem-user-event) |
[Edit the docs](https://github.com/testing-library/testing-library-docs)
## Known limitations
- No `<input type="color" />` support.
[#423](https://github.com/testing-library/user-event/issues/423#issuecomment-669368863)
## 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]
### ❓ Questions
For questions related to using the library, please visit a support community
instead of filing an issue on GitHub.
- [Discord][discord]
- [Stack Overflow][stackoverflow]
## 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> <a href="https://github.com/testing-library/user-event/issues?q=author%3Abozdoz" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=bozdoz" title="Tests">⚠️</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>
<td align="center"><a href="https://github.com/mkapal"><img src="https://avatars.githubusercontent.com/u/6420535?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin Kapal</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Amkapal" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://gr.linkedin.com/in/bastakis"><img src="https://avatars.githubusercontent.com/u/1146626?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stavros</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Asstauross" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/geoffroymounier"><img src="https://avatars.githubusercontent.com/u/24386870?v=4?s=100" width="100px;" alt=""/><br /><sub><b>geoffroymounier</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Ageoffroymounier" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://fergusmcdonald.com"><img src="https://avatars.githubusercontent.com/u/3115675?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fergus McDonald</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=fergusmcdonald" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/robin-ambachtsheer"><img src="https://avatars.githubusercontent.com/u/2611873?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Robin Ambachtsheer</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Arobin-ambachtsheer" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/MohitPopli"><img src="https://avatars.githubusercontent.com/u/17976072?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mohit</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3AMohitPopli" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=MohitPopli" title="Code">💻</a> <a href="https://github.com/testing-library/user-event/commits?author=MohitPopli" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/InExtremaRes"><img src="https://avatars.githubusercontent.com/u/1635491?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Contreras</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3AInExtremaRes" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://eugene.coding.blog"><img src="https://avatars.githubusercontent.com/u/13572283?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eugene Ghanizadeh</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=loreanvictor" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vicrep"><img src="https://avatars.githubusercontent.com/u/11432241?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Victor Repkow</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=vicrep" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/GreenGremlin"><img src="https://avatars.githubusercontent.com/u/647452?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonathan Felchlin</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=GreenGremlin" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/sydneyjodon-wk"><img src="https://avatars.githubusercontent.com/u/51122966?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sydneyjodon-wk</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Asydneyjodon-wk" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/user-event/commits?author=sydneyjodon-wk" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/codepath2019"><img src="https://avatars.githubusercontent.com/u/49729798?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Charles Magic Woo</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Acodepath2019" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/mkurcius"><img src="https://avatars.githubusercontent.com/u/1613212?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mkurcius</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=mkurcius" title="Code">💻</a></td>
<td align="center"><a href="http://stderr.timfischbach.de"><img src="https://avatars.githubusercontent.com/u/26554?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Fischbach</b></sub></a><br /><a href="https://github.com/testing-library/user-event/issues?q=author%3Atf" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/eventualbuddha"><img src="https://avatars.githubusercontent.com/u/1938?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brian Donovan</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=eventualbuddha" title="Code">💻</a></td>
<td align="center"><a href="http://www.largetimber.com"><img src="https://avatars.githubusercontent.com/u/10626756?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eric Wang</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=fa93hws" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jesperorb"><img src="https://avatars.githubusercontent.com/u/21122051?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jesper Orb</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=jesperorb" title="Code">💻</a></td>
<td align="center"><a href="https://johannesfischer.github.io/"><img src="https://avatars.githubusercontent.com/u/28100?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johannes Fischer</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=JohannesFischer" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/777PolarFox777"><img src="https://avatars.githubusercontent.com/u/19393384?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrew D.</b></sub></a><br /><a href="https://github.com/testing-library/user-event/commits?author=777PolarFox777" title="Code">💻</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](LICENSE)
<!-- 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/main?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/main/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/main/other/CODE_OF_CONDUCT.md
[github-watch-badge]: https://img.shields.io/github/watchers/testing-library/user-event.svg?style=social
[github-watch]: https://github.com/testing-library/user-event/watchers
[github-star-badge]: https://img.shields.io/github/stars/testing-library/user-event.svg?style=social
[github-star]: https://github.com/testing-library/user-event/stargazers
[twitter]: https://twitter.com/intent/tweet?text=Check%20out%20user-event%20by%20%40@TestingLib%20https%3A%2F%2Fgithub.com%2Ftesting-library%2Fuser-event%20%F0%9F%91%8D
[twitter-badge]: https://img.shields.io/twitter/url/https/github.com/testing-library/user-event.svg?style=social
[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
[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106
[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
[fire-event]: https://testing-library.com/docs/dom-testing-library/api-events#fireevent
[discord-badge]: https://img.shields.io/discord/723559267868737556.svg?color=7389D8&labelColor=6A7EC2&logo=discord&logoColor=ffffff&style=flat-square
[discord]: https://discord.gg/testing-library
[stackoverflow]: https://stackoverflow.com/questions/tagged/user-event
<!-- prettier-ignore-end -->

View File

@@ -0,0 +1,2 @@
declare function blur(element: Element): void;
export { blur };

View 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());
}

View File

@@ -0,0 +1,2 @@
declare function clear(element: Element): void;
export { clear };

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.clear = clear;
var _utils = require("./utils");
var _type = require("./type");
function clear(element) {
var _element$selectionSta, _element$selectionEnd;
if (!(0, _utils.isElementType)(element, ['input', 'textarea'])) {
// TODO: support contenteditable
throw new Error('clear currently only supports input and textarea elements.');
}
if ((0, _utils.isDisabled)(element)) {
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;
if (elementType !== 'textarea') {
// 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$selectionSta = element.selectionStart) != null ? _element$selectionSta :
/* istanbul ignore next */
undefined,
initialSelectionEnd: (_element$selectionEnd = element.selectionEnd) != null ? _element$selectionEnd :
/* istanbul ignore next */
undefined
});
if (elementType !== 'textarea') {
;
element.type = elementType;
}
}

View File

@@ -0,0 +1,8 @@
import { PointerOptions } from './utils';
export declare interface clickOptions {
skipHover?: boolean;
clickCount?: number;
}
declare function click(element: Element, init?: MouseEventInit, { skipHover, clickCount, skipPointerEventsCheck, }?: clickOptions & PointerOptions): void;
declare function dblClick(element: Element, init?: MouseEventInit, { skipPointerEventsCheck }?: clickOptions & PointerOptions): void;
export { click, dblClick };

View File

@@ -0,0 +1,176 @@
"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));
fireClick(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);
_dom.fireEvent.pointerUp(element, init);
if (!element.disabled) {
_dom.fireEvent.mouseUp(element, (0, _utils.getMouseEventOptions)('mouseup', init, clickCount));
fireClick(element, (0, _utils.getMouseEventOptions)('click', init, clickCount));
}
}
function clickElement(element, init, {
clickCount
}) {
const previousElement = getPreviouslyFocusedElement(element);
_dom.fireEvent.pointerDown(element, init);
if (!(0, _utils.isDisabled)(element)) {
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);
} else if (closestFocusable) {
(0, _focus.focus)(closestFocusable);
}
}
}
_dom.fireEvent.pointerUp(element, init);
if (!(0, _utils.isDisabled)(element)) {
_dom.fireEvent.mouseUp(element, (0, _utils.getMouseEventOptions)('mouseup', init, clickCount));
fireClick(element, (0, _utils.getMouseEventOptions)('click', init, clickCount));
const parentLabel = element.closest('label');
if (parentLabel != null && parentLabel.control) (0, _focus.focus)(parentLabel.control);
}
}
function findClosest(element, callback) {
let el = element;
do {
if (callback(el)) {
return el;
}
el = el.parentElement;
} while (el && el !== element.ownerDocument.body);
return undefined;
}
function click(element, init, {
skipHover = false,
clickCount = 0,
skipPointerEventsCheck = false
} = {}) {
if (!skipPointerEventsCheck && !(0, _utils.hasPointerEvents)(element)) {
throw new Error('unable to click element as it has or inherits pointer-events set to "none".');
} // We just checked for `pointerEvents`. We can always skip this one in `hover`.
if (!skipHover) (0, _hover.hover)(element, init, {
skipPointerEventsCheck: true
});
if ((0, _utils.isElementType)(element, 'label')) {
clickLabel(element, init, {
clickCount
});
} else if ((0, _utils.isElementType)(element, 'input')) {
if (element.type === 'checkbox' || element.type === 'radio') {
clickBooleanElement(element, init, {
clickCount
});
} else {
clickElement(element, init, {
clickCount
});
}
} else {
clickElement(element, init, {
clickCount
});
}
}
function fireClick(element, mouseEventOptions) {
if (mouseEventOptions.button === 2) {
_dom.fireEvent.contextMenu(element, mouseEventOptions);
} else {
_dom.fireEvent.click(element, mouseEventOptions);
}
}
function dblClick(element, init, {
skipPointerEventsCheck = false
} = {}) {
if (!skipPointerEventsCheck && !(0, _utils.hasPointerEvents)(element)) {
throw new Error('unable to double-click element as it has or inherits pointer-events set to "none".');
}
(0, _hover.hover)(element, init, {
skipPointerEventsCheck
});
click(element, init, {
skipHover: true,
clickCount: 0,
skipPointerEventsCheck
});
click(element, init, {
skipHover: true,
clickCount: 1,
skipPointerEventsCheck
});
_dom.fireEvent.dblClick(element, (0, _utils.getMouseEventOptions)('dblclick', init, 2));
}

View File

@@ -0,0 +1,3 @@
declare module '@testing-library/dom/dist/helpers' {
export function getWindowFromNode(node: Node): Window
}

View File

@@ -0,0 +1,2 @@
declare function focus(element: Element): void;
export { focus };

View 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());
}

View File

@@ -0,0 +1,4 @@
import { PointerOptions } from './utils';
declare function hover(element: Element, init?: MouseEventInit, { skipPointerEventsCheck }?: PointerOptions): void;
declare function unhover(element: Element, init?: MouseEventInit, { skipPointerEventsCheck }?: PointerOptions): void;
export { hover, unhover };

View File

@@ -0,0 +1,85 @@
"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, {
skipPointerEventsCheck = false
} = {}) {
if (!skipPointerEventsCheck && !(0, _utils.hasPointerEvents)(element)) {
throw new Error('unable to hover element as it has or inherits pointer-events set to "none".');
}
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 (!(0, _utils.isDisabled)(element)) {
_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 (!(0, _utils.isDisabled)(element)) {
_dom.fireEvent.mouseMove(element, (0, _utils.getMouseEventOptions)('mousemove', init));
}
}
function unhover(element, init, {
skipPointerEventsCheck = false
} = {}) {
if (!skipPointerEventsCheck && !(0, _utils.hasPointerEvents)(element)) {
throw new Error('unable to unhover element as it has or inherits pointer-events set to "none".');
}
if ((0, _utils.isLabelWithInternallyDisabledControl)(element)) return;
const parentElements = getParentElements(element);
_dom.fireEvent.pointerMove(element, init);
if (!(0, _utils.isDisabled)(element)) {
_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 (!(0, _utils.isDisabled)(element)) {
_dom.fireEvent.mouseOut(element, (0, _utils.getMouseEventOptions)('mouseout', init));
for (const el of parentElements) {
_dom.fireEvent.mouseLeave(el, (0, _utils.getMouseEventOptions)('mouseleave', init));
}
}
}

View File

@@ -0,0 +1,25 @@
import { click, dblClick } from './click';
import { type } from './type';
import { clear } from './clear';
import { tab } from './tab';
import { hover, unhover } from './hover';
import { upload } from './upload';
import { paste } from './paste';
import { keyboard, specialCharMap } from './keyboard';
declare const userEvent: {
click: typeof click;
dblClick: typeof dblClick;
type: typeof type;
clear: typeof clear;
tab: typeof tab;
hover: typeof hover;
unhover: typeof unhover;
upload: typeof upload;
selectOptions: (args_0: Element, args_1: string | string[] | HTMLElement | HTMLElement[], args_2?: MouseEventInit | undefined, args_3?: import("./utils").PointerOptions | undefined) => void;
deselectOptions: (args_0: Element, args_1: string | string[] | HTMLElement | HTMLElement[], args_2?: MouseEventInit | undefined, args_3?: import("./utils").PointerOptions | undefined) => void;
paste: typeof paste;
keyboard: typeof keyboard;
};
export default userEvent;
export { specialCharMap as specialChars };
export type { keyboardKey } from './keyboard';

View File

@@ -0,0 +1,47 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
Object.defineProperty(exports, "specialChars", {
enumerable: true,
get: function () {
return _keyboard.specialCharMap;
}
});
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");
var _keyboard = require("./keyboard");
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,
keyboard: _keyboard.keyboard
};
var _default = userEvent;
exports.default = _default;

View File

@@ -0,0 +1,17 @@
import { keyboardKey, keyboardState } from './types';
export declare function getKeyEventProps(keyDef: keyboardKey, state: keyboardState): {
key: string | undefined;
code: string | undefined;
altKey: boolean;
ctrlKey: boolean;
metaKey: boolean;
shiftKey: boolean;
/** @deprecated use code instead */
keyCode: number | undefined;
};
export declare function getMouseEventProps(state: keyboardState): {
altKey: boolean;
ctrlKey: boolean;
metaKey: boolean;
shiftKey: boolean;
};

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getKeyEventProps = getKeyEventProps;
exports.getMouseEventProps = getMouseEventProps;
function getKeyEventProps(keyDef, state) {
var _keyDef$keyCode, _keyDef$key;
return {
key: keyDef.key,
code: keyDef.code,
altKey: state.modifiers.alt,
ctrlKey: state.modifiers.ctrl,
metaKey: state.modifiers.meta,
shiftKey: state.modifiers.shift,
/** @deprecated use code instead */
keyCode: (_keyDef$keyCode = keyDef.keyCode) != null ? _keyDef$keyCode : // istanbul ignore next
((_keyDef$key = keyDef.key) == null ? void 0 : _keyDef$key.length) === 1 ? keyDef.key.charCodeAt(0) : undefined
};
}
function getMouseEventProps(state) {
return {
altKey: state.modifiers.alt,
ctrlKey: state.modifiers.ctrl,
metaKey: state.modifiers.meta,
shiftKey: state.modifiers.shift
};
}

View File

@@ -0,0 +1,19 @@
import { keyboardKey, keyboardOptions } from './types';
/**
* Get the next key from keyMap
*
* Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
* Everything else will be interpreted as a typed character - e.g. `a`.
* Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
* Keeping the key pressed can be written as `{key>}`.
* When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
* You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
* Modifiers like `{shift}` imply being kept pressed. This can be turned of per `{shift/}`.
*/
export declare function getNextKeyDef(text: string, options: keyboardOptions): {
keyDef: keyboardKey;
consumedLength: number;
releasePrevious: boolean;
releaseSelf: boolean;
repeat: number;
};

View File

@@ -0,0 +1,174 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getNextKeyDef = getNextKeyDef;
var bracketDict;
(function (bracketDict) {
bracketDict["{"] = "}";
bracketDict["["] = "]";
})(bracketDict || (bracketDict = {}));
var legacyModifiers;
(function (legacyModifiers) {
legacyModifiers["alt"] = "alt";
legacyModifiers["ctrl"] = "ctrl";
legacyModifiers["meta"] = "meta";
legacyModifiers["shift"] = "shift";
})(legacyModifiers || (legacyModifiers = {}));
var legacyKeyMap;
/**
* Get the next key from keyMap
*
* Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
* Everything else will be interpreted as a typed character - e.g. `a`.
* Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
* Keeping the key pressed can be written as `{key>}`.
* When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
* You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
* Modifiers like `{shift}` imply being kept pressed. This can be turned of per `{shift/}`.
*/
(function (legacyKeyMap) {
legacyKeyMap["ctrl"] = "Control";
legacyKeyMap["del"] = "Delete";
legacyKeyMap["esc"] = "Escape";
legacyKeyMap["space"] = " ";
})(legacyKeyMap || (legacyKeyMap = {}));
function getNextKeyDef(text, options) {
var _options$keyboardMap$;
const {
type,
descriptor,
consumedLength,
releasePrevious,
releaseSelf,
repeat
} = readNextDescriptor(text);
const keyDef = (_options$keyboardMap$ = options.keyboardMap.find(def => {
if (type === '[') {
var _def$code;
return ((_def$code = def.code) == null ? void 0 : _def$code.toLowerCase()) === descriptor.toLowerCase();
} else if (type === '{') {
var _def$key;
const key = mapLegacyKey(descriptor);
return ((_def$key = def.key) == null ? void 0 : _def$key.toLowerCase()) === key.toLowerCase();
}
return def.key === descriptor;
})) != null ? _options$keyboardMap$ : {
key: 'Unknown',
code: 'Unknown',
[type === '[' ? 'code' : 'key']: descriptor
};
return {
keyDef,
consumedLength,
releasePrevious,
releaseSelf,
repeat
};
}
function readNextDescriptor(text) {
let pos = 0;
const startBracket = text[pos] in bracketDict ? text[pos] : '';
pos += startBracket.length; // `foo{{bar` is an escaped char at position 3,
// but `foo{{{>5}bar` should be treated as `{` pressed down for 5 keydowns.
const startBracketRepeated = startBracket ? text.match(new RegExp(`^\\${startBracket}+`))[0].length : 0;
const isEscapedChar = startBracketRepeated === 2 || startBracket === '{' && startBracketRepeated > 3;
const type = isEscapedChar ? '' : startBracket;
return {
type,
...(type === '' ? readPrintableChar(text, pos) : readTag(text, pos, type))
};
}
function readPrintableChar(text, pos) {
const descriptor = text[pos];
assertDescriptor(descriptor, text, pos);
pos += descriptor.length;
return {
consumedLength: pos,
descriptor,
releasePrevious: false,
releaseSelf: true,
repeat: 1
};
}
function readTag(text, pos, startBracket) {
var _text$slice$match, _text$slice$match$, _text$slice$match2;
const releasePreviousModifier = text[pos] === '/' ? '/' : '';
pos += releasePreviousModifier.length;
const descriptor = (_text$slice$match = text.slice(pos).match(/^\w+/)) == null ? void 0 : _text$slice$match[0];
assertDescriptor(descriptor, text, pos);
pos += descriptor.length;
const repeatModifier = (_text$slice$match$ = (_text$slice$match2 = text.slice(pos).match(/^>\d+/)) == null ? void 0 : _text$slice$match2[0]) != null ? _text$slice$match$ : '';
pos += repeatModifier.length;
const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : '';
pos += releaseSelfModifier.length;
const expectedEndBracket = bracketDict[startBracket];
const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : '';
if (!endBracket) {
throw new Error(getErrorMessage([!repeatModifier && 'repeat modifier', !releaseSelfModifier && 'release modifier', `"${expectedEndBracket}"`].filter(Boolean).join(' or '), text[pos], text));
}
pos += endBracket.length;
return {
consumedLength: pos,
descriptor,
releasePrevious: !!releasePreviousModifier,
repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1,
releaseSelf: hasReleaseSelf(startBracket, descriptor, releaseSelfModifier, repeatModifier)
};
}
function assertDescriptor(descriptor, text, pos) {
if (!descriptor) {
throw new Error(getErrorMessage('key descriptor', text[pos], text));
}
}
function getEnumValue(f, key) {
return f[key];
}
function hasReleaseSelf(startBracket, descriptor, releaseSelfModifier, repeatModifier) {
if (releaseSelfModifier) {
return releaseSelfModifier === '/';
}
if (repeatModifier) {
return false;
}
if (startBracket === '{' && getEnumValue(legacyModifiers, descriptor.toLowerCase())) {
return false;
}
return true;
}
function mapLegacyKey(descriptor) {
var _getEnumValue;
return (_getEnumValue = getEnumValue(legacyKeyMap, descriptor)) != null ? _getEnumValue : descriptor;
}
function getErrorMessage(expected, found, text) {
return `Expected ${expected} but found "${found != null ? found : ''}" in "${text}"
See https://github.com/testing-library/user-event/blob/main/README.md#keyboardtext-options
for more information about how userEvent parses your input.`;
}

View File

@@ -0,0 +1,18 @@
import { keyboardState, keyboardOptions, keyboardKey } from './types';
export { specialCharMap } from './specialCharMap';
export type { keyboardOptions, keyboardKey };
export declare function keyboard(text: string, options?: Partial<keyboardOptions & {
keyboardState: keyboardState;
delay: 0;
}>): keyboardState;
export declare function keyboard(text: string, options: Partial<keyboardOptions & {
keyboardState: keyboardState;
delay: number;
}>): Promise<keyboardState>;
export declare function keyboardImplementationWrapper(text: string, config?: Partial<keyboardOptions & {
keyboardState: keyboardState;
}>): {
promise: Promise<void>;
state: keyboardState;
releaseAllKeys: () => void;
};

View File

@@ -0,0 +1,74 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keyboard = keyboard;
exports.keyboardImplementationWrapper = keyboardImplementationWrapper;
Object.defineProperty(exports, "specialCharMap", {
enumerable: true,
get: function () {
return _specialCharMap.specialCharMap;
}
});
var _dom = require("@testing-library/dom");
var _keyboardImplementation = require("./keyboardImplementation");
var _keyMap = require("./keyMap");
var _specialCharMap = require("./specialCharMap");
function keyboard(text, options) {
var _options$delay;
const {
promise,
state
} = keyboardImplementationWrapper(text, options);
if (((_options$delay = options == null ? void 0 : options.delay) != null ? _options$delay : 0) > 0) {
return (0, _dom.getConfig)().asyncWrapper(() => promise.then(() => state));
} else {
// prevent users from dealing with UnhandledPromiseRejectionWarning in sync call
promise.catch(console.error);
return state;
}
}
function keyboardImplementationWrapper(text, config = {}) {
const {
keyboardState: state = createKeyboardState(),
delay = 0,
document: doc = document,
autoModify = false,
keyboardMap = _keyMap.defaultKeyMap
} = config;
const options = {
delay,
document: doc,
autoModify,
keyboardMap
};
return {
promise: (0, _keyboardImplementation.keyboardImplementation)(text, options, state),
state,
releaseAllKeys: () => (0, _keyboardImplementation.releaseAllKeys)(options, state)
};
}
function createKeyboardState() {
return {
activeElement: null,
pressed: [],
carryChar: '',
modifiers: {
alt: false,
caps: false,
ctrl: false,
meta: false,
shift: false
}
};
}

View File

@@ -0,0 +1,5 @@
import { keyboardKey } from './types';
/**
* Mapping for a default US-104-QWERTY keyboard
*/
export declare const defaultKeyMap: keyboardKey[];

View File

@@ -0,0 +1,139 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.defaultKeyMap = void 0;
var _types = require("./types");
/**
* Mapping for a default US-104-QWERTY keyboard
*/
const defaultKeyMap = [// alphanumeric keys
...'0123456789'.split('').map(c => ({
code: `Digit${c}`,
key: c
})), ...')!@#$%^&*('.split('').map((c, i) => ({
code: `Digit${i}`,
key: c,
shiftKey: true
})), ...'abcdefghijklmnopqrstuvwxyz'.split('').map(c => ({
code: `Key${c.toUpperCase()}`,
key: c
})), ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map(c => ({
code: `Key${c}`,
key: c,
shiftKey: true
})), // alphanumeric block - functional
{
code: 'Space',
key: ' '
}, {
code: 'AltLeft',
key: 'Alt',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 18
}, {
code: 'AltRight',
key: 'Alt',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 18
}, {
code: 'ShiftLeft',
key: 'Shift',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 16
}, {
code: 'ShiftRight',
key: 'Shift',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 16
}, {
code: 'ControlLeft',
key: 'Control',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 17
}, {
code: 'ControlRight',
key: 'Control',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 17
}, {
code: 'MetaLeft',
key: 'Meta',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 93
}, {
code: 'MetaRight',
key: 'Meta',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 93
}, {
code: 'OSLeft',
key: 'OS',
location: _types.DOM_KEY_LOCATION.LEFT,
keyCode: 91
}, {
code: 'OSRight',
key: 'OS',
location: _types.DOM_KEY_LOCATION.RIGHT,
keyCode: 91
}, {
code: 'CapsLock',
key: 'CapsLock',
keyCode: 20
}, {
code: 'Backspace',
key: 'Backspace',
keyCode: 8
}, {
code: 'Enter',
key: 'Enter',
keyCode: 13
}, // function
{
code: 'Escape',
key: 'Escape',
keyCode: 27
}, // arrows
{
code: 'ArrowUp',
key: 'ArrowUp',
keyCode: 38
}, {
code: 'ArrowDown',
key: 'ArrowDown',
keyCode: 40
}, {
code: 'ArrowLeft',
key: 'ArrowLeft',
keyCode: 37
}, {
code: 'ArrowRight',
key: 'ArrowRight',
keyCode: 39
}, // control pad
{
code: 'Home',
key: 'Home',
keyCode: 36
}, {
code: 'End',
key: 'End',
keyCode: 35
}, {
code: 'Delete',
key: 'Delete',
keyCode: 46
}, {
code: 'PageUp',
key: 'PageUp',
keyCode: 33
}, {
code: 'PageDown',
key: 'PageDown',
keyCode: 34
} // TODO: add mappings
];
exports.defaultKeyMap = defaultKeyMap;

View File

@@ -0,0 +1,3 @@
import { keyboardState, keyboardOptions } from './types';
export declare function keyboardImplementation(text: string, options: keyboardOptions, state: keyboardState): Promise<void>;
export declare function releaseAllKeys(options: keyboardOptions, state: keyboardState): void;

View File

@@ -0,0 +1,167 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keyboardImplementation = keyboardImplementation;
exports.releaseAllKeys = releaseAllKeys;
var _dom = require("@testing-library/dom");
var _utils = require("../utils");
var _getNextKeyDef = require("./getNextKeyDef");
var plugins = _interopRequireWildcard(require("./plugins"));
var _getEventProps = require("./getEventProps");
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
async function keyboardImplementation(text, options, state) {
var _state$repeatKey;
const {
document
} = options;
const getCurrentElement = () => getActive(document);
const {
keyDef,
consumedLength,
releasePrevious,
releaseSelf,
repeat
} = (_state$repeatKey = state.repeatKey) != null ? _state$repeatKey : (0, _getNextKeyDef.getNextKeyDef)(text, options);
const replace = applyPlugins(plugins.replaceBehavior, keyDef, getCurrentElement(), options, state);
if (!replace) {
const pressed = state.pressed.find(p => p.keyDef === keyDef); // Release the key automatically if it was pressed before.
// Do not release the key on iterations on `state.repeatKey`.
if (pressed && !state.repeatKey) {
keyup(keyDef, getCurrentElement, options, state, pressed.unpreventedDefault);
}
if (!releasePrevious) {
const unpreventedDefault = keydown(keyDef, getCurrentElement, options, state);
if (unpreventedDefault && hasKeyPress(keyDef, state)) {
keypress(keyDef, getCurrentElement, options, state);
} // Release the key only on the last iteration on `state.repeatKey`.
if (releaseSelf && repeat <= 1) {
keyup(keyDef, getCurrentElement, options, state, unpreventedDefault);
}
}
}
if (repeat > 1) {
state.repeatKey = {
// don't consume again on the next iteration
consumedLength: 0,
keyDef,
releasePrevious,
releaseSelf,
repeat: repeat - 1
};
} else {
delete state.repeatKey;
}
if (text.length > consumedLength || repeat > 1) {
if (options.delay > 0) {
await (0, _utils.wait)(options.delay);
}
return keyboardImplementation(text.slice(consumedLength), options, state);
}
return void undefined;
}
function getActive(document) {
var _getActiveElement;
return (_getActiveElement = (0, _utils.getActiveElement)(document)) != null ? _getActiveElement :
/* istanbul ignore next */
document.body;
}
function releaseAllKeys(options, state) {
const getCurrentElement = () => getActive(options.document);
for (const k of state.pressed) {
keyup(k.keyDef, getCurrentElement, options, state, k.unpreventedDefault);
}
}
function keydown(keyDef, getCurrentElement, options, state) {
const element = getCurrentElement(); // clear carried characters when focus is moved
if (element !== state.activeElement) {
state.carryValue = undefined;
state.carryChar = '';
}
state.activeElement = element;
applyPlugins(plugins.preKeydownBehavior, keyDef, element, options, state);
const unpreventedDefault = _dom.fireEvent.keyDown(element, (0, _getEventProps.getKeyEventProps)(keyDef, state));
state.pressed.push({
keyDef,
unpreventedDefault
});
if (unpreventedDefault) {
// all default behavior like keypress/submit etc is applied to the currentElement
applyPlugins(plugins.keydownBehavior, keyDef, getCurrentElement(), options, state);
}
return unpreventedDefault;
}
function keypress(keyDef, getCurrentElement, options, state) {
const element = getCurrentElement();
const unpreventedDefault = _dom.fireEvent.keyPress(element, (0, _getEventProps.getKeyEventProps)(keyDef, state));
if (unpreventedDefault) {
applyPlugins(plugins.keypressBehavior, keyDef, getCurrentElement(), options, state);
}
}
function keyup(keyDef, getCurrentElement, options, state, unprevented) {
const element = getCurrentElement();
applyPlugins(plugins.preKeyupBehavior, keyDef, element, options, state);
const unpreventedDefault = _dom.fireEvent.keyUp(element, (0, _getEventProps.getKeyEventProps)(keyDef, state));
if (unprevented && unpreventedDefault) {
applyPlugins(plugins.keyupBehavior, keyDef, getCurrentElement(), options, state);
}
state.pressed = state.pressed.filter(k => k.keyDef !== keyDef);
applyPlugins(plugins.postKeyupBehavior, keyDef, element, options, state);
}
function applyPlugins(pluginCollection, keyDef, element, options, state) {
const plugin = pluginCollection.find(p => p.matches(keyDef, element, options, state));
if (plugin) {
plugin.handle(keyDef, element, options, state);
}
return !!plugin;
}
function hasKeyPress(keyDef, state) {
var _keyDef$key;
return (((_keyDef$key = keyDef.key) == null ? void 0 : _keyDef$key.length) === 1 || keyDef.key === 'Enter') && !state.modifiers.ctrl && !state.modifiers.alt;
}

View File

@@ -0,0 +1,6 @@
/**
* This file should contain behavior for arrow keys as described here:
* https://w3c.github.io/uievents-code/#key-arrowpad-section
*/
import { behaviorPlugin } from '../types';
export declare const keydownBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keydownBehavior = void 0;
var _utils = require("../../utils");
/**
* This file should contain behavior for arrow keys as described here:
* https://w3c.github.io/uievents-code/#key-arrowpad-section
*/
const keydownBehavior = [{
// TODO: implement for contentEditable
matches: (keyDef, element) => (keyDef.key === 'ArrowLeft' || keyDef.key === 'ArrowRight') && (0, _utils.isElementType)(element, ['input', 'textarea']),
handle: (keyDef, element) => {
var _ref;
const {
selectionStart,
selectionEnd
} = (0, _utils.getSelectionRange)(element);
const direction = keyDef.key === 'ArrowLeft' ? -1 : 1;
const newPos = (_ref = selectionStart === selectionEnd ? (selectionStart != null ? selectionStart :
/* istanbul ignore next */
0) + direction : direction < 0 ? selectionStart : selectionEnd) != null ? _ref :
/* istanbul ignore next */
0;
(0, _utils.setSelectionRange)(element, newPos, newPos);
}
}];
exports.keydownBehavior = keydownBehavior;

View File

@@ -0,0 +1,5 @@
/**
* This file should cover the behavior for keys that produce character input
*/
import { behaviorPlugin } from '../types';
export declare const keypressBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,195 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keypressBehavior = void 0;
var _dom = require("@testing-library/dom");
var _shared = require("../shared");
var _utils = require("../../utils");
/**
* This file should cover the behavior for keys that produce character input
*/
const keypressBehavior = [{
matches: (keyDef, element) => {
var _keyDef$key;
return ((_keyDef$key = keyDef.key) == null ? void 0 : _keyDef$key.length) === 1 && (0, _utils.isElementType)(element, 'input', {
type: 'time',
readOnly: false
});
},
handle: (keyDef, element, options, state) => {
var _state$carryValue;
let newEntry = keyDef.key;
const textToBeTyped = ((_state$carryValue = state.carryValue) != null ? _state$carryValue : '') + newEntry;
const timeNewEntry = (0, _utils.buildTimeValue)(textToBeTyped);
if ((0, _utils.isValidInputTimeValue)(element, timeNewEntry)) {
newEntry = timeNewEntry;
}
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(newEntry, element);
const prevValue = (0, _utils.getValue)(element); // this check was provided by fireInputEventIfNeeded
// TODO: verify if it is even needed by this handler
if (prevValue !== newValue) {
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
data: keyDef.key,
inputType: 'insertText'
}
});
}
(0, _shared.fireChangeForInputTimeIfValid)(element, prevValue, timeNewEntry);
state.carryValue = textToBeTyped;
}
}, {
matches: (keyDef, element) => {
var _keyDef$key2;
return ((_keyDef$key2 = keyDef.key) == null ? void 0 : _keyDef$key2.length) === 1 && (0, _utils.isElementType)(element, 'input', {
type: 'date',
readOnly: false
});
},
handle: (keyDef, element, options, state) => {
var _state$carryValue2;
let newEntry = keyDef.key;
const textToBeTyped = ((_state$carryValue2 = state.carryValue) != null ? _state$carryValue2 : '') + newEntry;
const isValidToBeTyped = (0, _utils.isValidDateValue)(element, textToBeTyped);
if (isValidToBeTyped) {
newEntry = textToBeTyped;
}
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(newEntry, element);
const prevValue = (0, _utils.getValue)(element); // this check was provided by fireInputEventIfNeeded
// TODO: verify if it is even needed by this handler
if (prevValue !== newValue) {
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
data: keyDef.key,
inputType: 'insertText'
}
});
}
if (isValidToBeTyped) {
_dom.fireEvent.change(element, {
target: {
value: textToBeTyped
}
});
}
state.carryValue = textToBeTyped;
}
}, {
matches: (keyDef, element) => {
var _keyDef$key3;
return ((_keyDef$key3 = keyDef.key) == null ? void 0 : _keyDef$key3.length) === 1 && (0, _utils.isElementType)(element, 'input', {
type: 'number',
readOnly: false
});
},
handle: (keyDef, element, options, state) => {
var _ref, _state$carryValue3, _newValue$match, _newValue$match2;
if (!/[\d.\-e]/.test(keyDef.key)) {
return;
}
const oldValue = (_ref = (_state$carryValue3 = state.carryValue) != null ? _state$carryValue3 : (0, _utils.getValue)(element)) != null ? _ref :
/* istanbul ignore next */
'';
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(keyDef.key, element, oldValue); // the browser allows some invalid input but not others
// it allows up to two '-' at any place before any 'e' or one directly following 'e'
// it allows one '.' at any place before e
const valueParts = newValue.split('e', 2);
if (Number((_newValue$match = newValue.match(/-/g)) == null ? void 0 : _newValue$match.length) > 2 || Number((_newValue$match2 = newValue.match(/\./g)) == null ? void 0 : _newValue$match2.length) > 1 || valueParts[1] && !/^-?\d*$/.test(valueParts[1])) {
return;
}
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
data: keyDef.key,
inputType: 'insertText'
}
});
const appliedValue = (0, _utils.getValue)(element);
if (appliedValue === newValue) {
state.carryValue = undefined;
} else {
state.carryValue = newValue;
}
}
}, {
matches: (keyDef, element) => {
var _keyDef$key4;
return ((_keyDef$key4 = keyDef.key) == null ? void 0 : _keyDef$key4.length) === 1 && ((0, _utils.isElementType)(element, ['input', 'textarea'], {
readOnly: false
}) && !(0, _utils.isClickableInput)(element) || (0, _utils.isContentEditable)(element)) && (0, _utils.getSpaceUntilMaxLength)(element) !== 0;
},
handle: (keyDef, element) => {
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(keyDef.key, element);
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
data: keyDef.key,
inputType: 'insertText'
}
});
}
}, {
matches: (keyDef, element) => keyDef.key === 'Enter' && ((0, _utils.isElementType)(element, 'textarea', {
readOnly: false
}) || (0, _utils.isContentEditable)(element)) && (0, _utils.getSpaceUntilMaxLength)(element) !== 0,
handle: (keyDef, element, options, state) => {
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)('\n', element);
const inputType = (0, _utils.isContentEditable)(element) && !state.modifiers.shift ? 'insertParagraph' : 'insertLineBreak';
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
inputType
}
});
}
}];
exports.keypressBehavior = keypressBehavior;

View File

@@ -0,0 +1,6 @@
/**
* This file should contain behavior for arrow keys as described here:
* https://w3c.github.io/uievents-code/#key-controlpad-section
*/
import { behaviorPlugin } from '../types';
export declare const keydownBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,63 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.keydownBehavior = void 0;
var _utils = require("../../utils");
var _shared = require("../shared");
/**
* This file should contain behavior for arrow keys as described here:
* https://w3c.github.io/uievents-code/#key-controlpad-section
*/
const keydownBehavior = [{
matches: (keyDef, element) => (keyDef.key === 'Home' || keyDef.key === 'End') && ((0, _utils.isElementType)(element, ['input', 'textarea']) || (0, _utils.isContentEditable)(element)),
handle: (keyDef, element) => {
// This could probably been improved by collapsing a selection range
if (keyDef.key === 'Home') {
(0, _utils.setSelectionRange)(element, 0, 0);
} else {
var _getValue$length, _getValue;
const newPos = (_getValue$length = (_getValue = (0, _utils.getValue)(element)) == null ? void 0 : _getValue.length) != null ? _getValue$length :
/* istanbul ignore next */
0;
(0, _utils.setSelectionRange)(element, newPos, newPos);
}
}
}, {
matches: (keyDef, element) => (keyDef.key === 'PageUp' || keyDef.key === 'PageDown') && (0, _utils.isElementType)(element, ['input']),
handle: (keyDef, element) => {
// This could probably been improved by collapsing a selection range
if (keyDef.key === 'PageUp') {
(0, _utils.setSelectionRange)(element, 0, 0);
} else {
var _getValue$length2, _getValue2;
const newPos = (_getValue$length2 = (_getValue2 = (0, _utils.getValue)(element)) == null ? void 0 : _getValue2.length) != null ? _getValue$length2 :
/* istanbul ignore next */
0;
(0, _utils.setSelectionRange)(element, newPos, newPos);
}
}
}, {
matches: (keyDef, element) => keyDef.key === 'Delete' && (0, _utils.isEditable)(element) && !(0, _utils.isCursorAtEnd)(element),
handle: (keDef, element, options, state) => {
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)('', element, state.carryValue, undefined, 'forward');
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
inputType: 'deleteContentForward'
}
});
(0, _shared.carryValue)(element, state, newValue);
}
}];
exports.keydownBehavior = keydownBehavior;

View File

@@ -0,0 +1,11 @@
/**
* This file should contain behavior for functional keys as described here:
* https://w3c.github.io/uievents-code/#key-alphanumeric-functional
*/
import { behaviorPlugin } from '../types';
export declare const preKeydownBehavior: behaviorPlugin[];
export declare const keydownBehavior: behaviorPlugin[];
export declare const keypressBehavior: behaviorPlugin[];
export declare const preKeyupBehavior: behaviorPlugin[];
export declare const keyupBehavior: behaviorPlugin[];
export declare const postKeyupBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,131 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.preKeyupBehavior = exports.preKeydownBehavior = exports.postKeyupBehavior = exports.keyupBehavior = exports.keypressBehavior = exports.keydownBehavior = void 0;
var _dom = require("@testing-library/dom");
var _utils = require("../../utils");
var _getEventProps = require("../getEventProps");
var _shared = require("../shared");
/**
* This file should contain behavior for functional keys as described here:
* https://w3c.github.io/uievents-code/#key-alphanumeric-functional
*/
const modifierKeys = {
Alt: 'alt',
Control: 'ctrl',
Shift: 'shift',
Meta: 'meta'
};
const preKeydownBehavior = [// modifierKeys switch on the modifier BEFORE the keydown event
...Object.entries(modifierKeys).map(([key, modKey]) => ({
matches: keyDef => keyDef.key === key,
handle: (keyDef, element, options, state) => {
state.modifiers[modKey] = true;
}
})), // AltGraph produces an extra keydown for Control
// The modifier does not change
{
matches: keyDef => keyDef.key === 'AltGraph',
handle: (keyDef, element, options, state) => {
var _options$keyboardMap$;
const ctrlKeyDef = (_options$keyboardMap$ = options.keyboardMap.find(k => k.key === 'Control')) != null ? _options$keyboardMap$ :
/* istanbul ignore next */
{
key: 'Control',
code: 'Control'
};
_dom.fireEvent.keyDown(element, (0, _getEventProps.getKeyEventProps)(ctrlKeyDef, state));
}
}];
exports.preKeydownBehavior = preKeydownBehavior;
const keydownBehavior = [{
matches: keyDef => keyDef.key === 'CapsLock',
handle: (keyDef, element, options, state) => {
state.modifiers.caps = !state.modifiers.caps;
}
}, {
matches: (keyDef, element) => keyDef.key === 'Backspace' && (0, _utils.isEditable)(element) && !(0, _utils.isCursorAtStart)(element),
handle: (keyDef, element, options, state) => {
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)('', element, state.carryValue, undefined, 'backward');
(0, _shared.fireInputEvent)(element, {
newValue,
newSelectionStart,
eventOverrides: {
inputType: 'deleteContentBackward'
}
});
(0, _shared.carryValue)(element, state, newValue);
}
}];
exports.keydownBehavior = keydownBehavior;
const keypressBehavior = [{
matches: (keyDef, element) => keyDef.key === 'Enter' && (0, _utils.isElementType)(element, 'input') && ['checkbox', 'radio'].includes(element.type),
handle: (keyDef, element) => {
const form = element.form;
if ((0, _utils.hasFormSubmit)(form)) {
_dom.fireEvent.submit(form);
}
}
}, {
matches: (keyDef, element) => keyDef.key === 'Enter' && ((0, _utils.isClickableInput)(element) || // Links with href defined should handle Enter the same as a click
(0, _utils.isElementType)(element, 'a') && Boolean(element.href)),
handle: (keyDef, element, options, state) => {
_dom.fireEvent.click(element, (0, _getEventProps.getMouseEventProps)(state));
}
}, {
matches: (keyDef, element) => keyDef.key === 'Enter' && (0, _utils.isElementType)(element, 'input'),
handle: (keyDef, element) => {
const form = element.form;
if (form && (form.querySelectorAll('input').length === 1 || (0, _utils.hasFormSubmit)(form))) {
_dom.fireEvent.submit(form);
}
}
}];
exports.keypressBehavior = keypressBehavior;
const preKeyupBehavior = [// modifierKeys switch off the modifier BEFORE the keyup event
...Object.entries(modifierKeys).map(([key, modKey]) => ({
matches: keyDef => keyDef.key === key,
handle: (keyDef, element, options, state) => {
state.modifiers[modKey] = false;
}
}))];
exports.preKeyupBehavior = preKeyupBehavior;
const keyupBehavior = [{
matches: (keyDef, element) => keyDef.key === ' ' && (0, _utils.isClickableInput)(element),
handle: (keyDef, element, options, state) => {
_dom.fireEvent.click(element, (0, _getEventProps.getMouseEventProps)(state));
}
}];
exports.keyupBehavior = keyupBehavior;
const postKeyupBehavior = [// AltGraph produces an extra keyup for Control
// The modifier does not change
{
matches: keyDef => keyDef.key === 'AltGraph',
handle: (keyDef, element, options, state) => {
var _options$keyboardMap$2;
const ctrlKeyDef = (_options$keyboardMap$2 = options.keyboardMap.find(k => k.key === 'Control')) != null ? _options$keyboardMap$2 :
/* istanbul ignore next */
{
key: 'Control',
code: 'Control'
};
_dom.fireEvent.keyUp(element, (0, _getEventProps.getKeyEventProps)(ctrlKeyDef, state));
}
}];
exports.postKeyupBehavior = postKeyupBehavior;

View File

@@ -0,0 +1,8 @@
import { behaviorPlugin } from '../types';
export declare const replaceBehavior: behaviorPlugin[];
export declare const preKeydownBehavior: behaviorPlugin[];
export declare const keydownBehavior: behaviorPlugin[];
export declare const keypressBehavior: behaviorPlugin[];
export declare const preKeyupBehavior: behaviorPlugin[];
export declare const keyupBehavior: behaviorPlugin[];
export declare const postKeyupBehavior: behaviorPlugin[];

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.replaceBehavior = exports.preKeyupBehavior = exports.preKeydownBehavior = exports.postKeyupBehavior = exports.keyupBehavior = exports.keypressBehavior = exports.keydownBehavior = void 0;
var _utils = require("../../utils");
var arrowKeys = _interopRequireWildcard(require("./arrow"));
var controlKeys = _interopRequireWildcard(require("./control"));
var characterKeys = _interopRequireWildcard(require("./character"));
var functionalKeys = _interopRequireWildcard(require("./functional"));
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const replaceBehavior = [{
matches: (keyDef, element) => keyDef.key === 'selectall' && (0, _utils.isElementType)(element, ['input', 'textarea']),
handle: (keyDef, element, options, state) => {
var _state$carryValue;
(0, _utils.setSelectionRange)(element, 0, ((_state$carryValue = state.carryValue) != null ? _state$carryValue : element.value).length);
}
}];
exports.replaceBehavior = replaceBehavior;
const preKeydownBehavior = [...functionalKeys.preKeydownBehavior];
exports.preKeydownBehavior = preKeydownBehavior;
const keydownBehavior = [...arrowKeys.keydownBehavior, ...controlKeys.keydownBehavior, ...functionalKeys.keydownBehavior];
exports.keydownBehavior = keydownBehavior;
const keypressBehavior = [...functionalKeys.keypressBehavior, ...characterKeys.keypressBehavior];
exports.keypressBehavior = keypressBehavior;
const preKeyupBehavior = [...functionalKeys.preKeyupBehavior];
exports.preKeyupBehavior = preKeyupBehavior;
const keyupBehavior = [...functionalKeys.keyupBehavior];
exports.keyupBehavior = keyupBehavior;
const postKeyupBehavior = [...functionalKeys.postKeyupBehavior];
exports.postKeyupBehavior = postKeyupBehavior;

View File

@@ -0,0 +1,2 @@
import { keyboardState } from '../types';
export declare function carryValue(element: Element, state: keyboardState, newValue: string): void;

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.carryValue = carryValue;
var _utils = require("../../utils");
function carryValue(element, state, newValue) {
const value = (0, _utils.getValue)(element);
state.carryValue = value !== newValue && value === '' && (0, _utils.hasUnreliableEmptyValue)(element) ? newValue : undefined;
}

View File

@@ -0,0 +1,3 @@
export declare function fireChangeForInputTimeIfValid(el: HTMLInputElement & {
type: 'time';
}, prevValue: unknown, timeNewEntry: string): void;

View File

@@ -0,0 +1,20 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.fireChangeForInputTimeIfValid = fireChangeForInputTimeIfValid;
var _dom = require("@testing-library/dom");
var _utils = require("../../utils");
function fireChangeForInputTimeIfValid(el, prevValue, timeNewEntry) {
if ((0, _utils.isValidInputTimeValue)(el, timeNewEntry) && prevValue !== timeNewEntry) {
_dom.fireEvent.change(el, {
target: {
value: timeNewEntry
}
});
}
}

View File

@@ -0,0 +1,17 @@
import { fireEvent } from '@testing-library/dom';
export declare function fireInputEvent(element: HTMLElement, { newValue, newSelectionStart, eventOverrides, }: {
newValue: string;
newSelectionStart: number;
eventOverrides: Partial<Parameters<typeof fireEvent>[1]> & {
[k: string]: unknown;
};
}): void;
declare const initial: unique symbol;
declare const onBlur: unique symbol;
declare global {
interface Element {
[initial]?: string;
[onBlur]?: EventListener;
}
}
export {};

View File

@@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.fireInputEvent = fireInputEvent;
var _dom = require("@testing-library/dom");
var _utils = require("../../utils");
function fireInputEvent(element, {
newValue,
newSelectionStart,
eventOverrides
}) {
// apply the changes before firing the input event, so that input handlers can access the altered dom and selection
if ((0, _utils.isContentEditable)(element)) {
applyNative(element, 'textContent', newValue);
} else
/* istanbul ignore else */
if ((0, _utils.isElementType)(element, ['input', 'textarea'])) {
applyNative(element, 'value', newValue);
} else {
// TODO: properly type guard
throw new Error('Invalid Element');
}
setSelectionRangeAfterInput(element, newSelectionStart);
_dom.fireEvent.input(element, { ...eventOverrides
});
setSelectionRangeAfterInputHandler(element, newValue, newSelectionStart);
}
function setSelectionRangeAfterInput(element, newSelectionStart) {
(0, _utils.setSelectionRange)(element, newSelectionStart, newSelectionStart);
}
function setSelectionRangeAfterInputHandler(element, newValue, newSelectionStart) {
const value = (0, _utils.getValue)(element); // don't apply this workaround on elements that don't necessarily report the visible value - e.g. number
// TODO: this could probably be only applied when there is keyboardState.carryValue
const isUnreliableValue = value === '' && (0, _utils.hasUnreliableEmptyValue)(element);
if (!isUnreliableValue && value === newValue) {
const {
selectionStart
} = (0, _utils.getSelectionRange)(element);
if (selectionStart === value.length) {
// The value was changed as expected, but the cursor was moved to the end
// TODO: this could probably be only applied when we work around a framework setter on the element in applyNative
(0, _utils.setSelectionRange)(element, newSelectionStart, newSelectionStart);
}
}
}
const initial = Symbol('initial input value/textContent');
const onBlur = Symbol('onBlur');
/**
* React tracks the changes on element properties.
* This workaround tries to alter the DOM element without React noticing,
* so that it later picks up the change.
*
* @see https://github.com/facebook/react/blob/148f8e497c7d37a3c7ab99f01dec2692427272b1/packages/react-dom/src/client/inputValueTracking.js#L51-L104
*/
function applyNative(element, propName, propValue) {
const descriptor = Object.getOwnPropertyDescriptor(element, propName);
const nativeDescriptor = Object.getOwnPropertyDescriptor(element.constructor.prototype, propName);
if (descriptor && nativeDescriptor) {
Object.defineProperty(element, propName, nativeDescriptor);
} // Keep track of the initial value to determine if a change event should be dispatched.
// CONSTRAINT: We can not determine what happened between focus event and our first API call.
if (element[initial] === undefined) {
element[initial] = String(element[propName]);
}
element[propName] = propValue; // Add an event listener for the blur event to the capture phase on the window.
// CONSTRAINT: Currently there is no cross-platform solution to unshift the event handler stack.
// Our change event might occur after other event handlers on the blur event have been processed.
if (!element[onBlur]) {
var _element$ownerDocumen;
(_element$ownerDocumen = element.ownerDocument.defaultView) == null ? void 0 : _element$ownerDocumen.addEventListener('blur', element[onBlur] = () => {
const initV = element[initial]; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete element[onBlur]; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete element[initial];
if (String(element[propName]) !== initV) {
_dom.fireEvent.change(element);
}
}, {
capture: true,
once: true
});
}
if (descriptor) {
Object.defineProperty(element, propName, descriptor);
}
}

View File

@@ -0,0 +1,3 @@
export * from './carryValue';
export * from './fireChangeForInputTimeIfValid';
export * from './fireInputEvent';

View File

@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _carryValue = require("./carryValue");
Object.keys(_carryValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _carryValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _carryValue[key];
}
});
});
var _fireChangeForInputTimeIfValid = require("./fireChangeForInputTimeIfValid");
Object.keys(_fireChangeForInputTimeIfValid).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _fireChangeForInputTimeIfValid[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _fireChangeForInputTimeIfValid[key];
}
});
});
var _fireInputEvent = require("./fireInputEvent");
Object.keys(_fireInputEvent).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _fireInputEvent[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _fireInputEvent[key];
}
});
});

View File

@@ -0,0 +1,22 @@
/**
* @deprecated This list of strings with special meaning is no longer necessary
* as we've introduced a standardized way to describe any keystroke for `userEvent`.
* @see https://testing-library.com/docs/ecosystem-user-event#keyboardtext-options
*/
export declare const specialCharMap: {
readonly arrowLeft: "{arrowleft}";
readonly arrowRight: "{arrowright}";
readonly arrowDown: "{arrowdown}";
readonly arrowUp: "{arrowup}";
readonly enter: "{enter}";
readonly escape: "{esc}";
readonly delete: "{del}";
readonly backspace: "{backspace}";
readonly home: "{home}";
readonly end: "{end}";
readonly selectAll: "{selectall}";
readonly space: "{space}";
readonly whitespace: " ";
readonly pageUp: "{pageUp}";
readonly pageDown: "{pageDown}";
};

View File

@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.specialCharMap = void 0;
/**
* @deprecated This list of strings with special meaning is no longer necessary
* as we've introduced a standardized way to describe any keystroke for `userEvent`.
* @see https://testing-library.com/docs/ecosystem-user-event#keyboardtext-options
*/
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: ' ',
pageUp: '{pageUp}',
pageDown: '{pageDown}'
};
exports.specialCharMap = specialCharMap;

View File

@@ -0,0 +1,77 @@
import { getNextKeyDef } from './getNextKeyDef';
/**
* @internal Do not create/alter this by yourself as this type might be subject to changes.
*/
export declare type keyboardState = {
/**
All keys that have been pressed and not been lifted up yet.
*/
pressed: {
keyDef: keyboardKey;
unpreventedDefault: boolean;
}[];
/**
Active modifiers
*/
modifiers: {
alt: boolean;
caps: boolean;
ctrl: boolean;
meta: boolean;
shift: boolean;
};
/**
The element the keyboard input is performed on.
Some behavior might differ if the activeElement changes between corresponding keyboard events.
*/
activeElement: Element | null;
/**
For HTMLInputElements type='number':
If the last input char is '.', '-' or 'e',
the IDL value attribute does not reflect the input value.
*/
carryValue?: string;
/**
Carry over characters to following key handlers.
E.g. ^1
*/
carryChar: string;
/**
Repeat keydown and keypress event
*/
repeatKey?: ReturnType<typeof getNextKeyDef>;
};
export declare type keyboardOptions = {
/** Document in which to perform the events */
document: Document;
/** Delay between keystrokes */
delay: number;
/** Add modifiers for given keys - not implemented yet */
autoModify: boolean;
/** Keyboard layout to use */
keyboardMap: keyboardKey[];
};
export declare enum DOM_KEY_LOCATION {
STANDARD = 0,
LEFT = 1,
RIGHT = 2,
NUMPAD = 3
}
export interface keyboardKey {
/** Physical location on a keyboard */
code?: string;
/** Character or functional key descriptor */
key?: string;
/** Location on the keyboard for keys with multiple representation */
location?: DOM_KEY_LOCATION;
/** keyCode for legacy support */
keyCode?: number;
/** Does the character in `key` require/imply AltRight to be pressed? */
altGr?: boolean;
/** Does the character in `key` require/imply a shiftKey to be pressed? */
shift?: boolean;
}
export interface behaviorPlugin {
matches: (keyDef: keyboardKey, element: Element, options: keyboardOptions, state: keyboardState) => boolean;
handle: (keyDef: keyboardKey, element: Element, options: keyboardOptions, state: keyboardState) => void;
}

View File

@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.DOM_KEY_LOCATION = void 0;
/**
* @internal Do not create/alter this by yourself as this type might be subject to changes.
*/
let DOM_KEY_LOCATION;
exports.DOM_KEY_LOCATION = DOM_KEY_LOCATION;
(function (DOM_KEY_LOCATION) {
DOM_KEY_LOCATION[DOM_KEY_LOCATION["STANDARD"] = 0] = "STANDARD";
DOM_KEY_LOCATION[DOM_KEY_LOCATION["LEFT"] = 1] = "LEFT";
DOM_KEY_LOCATION[DOM_KEY_LOCATION["RIGHT"] = 2] = "RIGHT";
DOM_KEY_LOCATION[DOM_KEY_LOCATION["NUMPAD"] = 3] = "NUMPAD";
})(DOM_KEY_LOCATION || (exports.DOM_KEY_LOCATION = DOM_KEY_LOCATION = {}));

View File

@@ -0,0 +1,6 @@
interface pasteOptions {
initialSelectionStart?: number;
initialSelectionEnd?: number;
}
declare function paste(element: HTMLElement, text: string, init?: ClipboardEventInit, { initialSelectionStart, initialSelectionEnd }?: pasteOptions): void;
export { paste };

View File

@@ -0,0 +1,67 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.paste = paste;
var _dom = require("@testing-library/dom");
var _utils = require("./utils");
function isSupportedElement(element) {
return (0, _utils.isElementType)(element, 'input') && Boolean(_utils.editableInputTypes[element.type]) || (0, _utils.isElementType)(element, 'textarea');
}
function paste(element, text, init, {
initialSelectionStart,
initialSelectionEnd
} = {}) {
// TODO: implement for contenteditable
if (!isSupportedElement(element)) {
throw new TypeError(`The given ${element.tagName} element is currently unsupported.
A PR extending this implementation would be very much welcome at https://github.com/testing-library/user-event`);
}
if ((0, _utils.isDisabled)(element)) {
return;
}
(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.setSelectionRange)(element, initialSelectionStart != null ? initialSelectionStart : element.value.length, initialSelectionEnd != null ? initialSelectionEnd : element.value.length);
}
_dom.fireEvent.paste(element, init);
if (element.readOnly) {
return;
}
text = text.substr(0, (0, _utils.getSpaceUntilMaxLength)(element));
const {
newValue,
newSelectionStart
} = (0, _utils.calculateNewValue)(text, element);
_dom.fireEvent.input(element, {
inputType: 'insertFromPaste',
target: {
value: newValue
}
});
(0, _utils.setSelectionRange)(element, // TODO: investigate why the selection caused by invalid parameters was expected
{
newSelectionStart,
selectionEnd: newSelectionStart
}, {});
}

View File

@@ -0,0 +1,4 @@
import { PointerOptions } from './utils';
declare const selectOptions: (args_0: Element, args_1: string | string[] | HTMLElement | HTMLElement[], args_2?: MouseEventInit | undefined, args_3?: PointerOptions | undefined) => void;
declare const deselectOptions: (args_0: Element, args_1: string | string[] | HTMLElement | HTMLElement[], args_2?: MouseEventInit | undefined, args_3?: PointerOptions | undefined) => void;
export { selectOptions, deselectOptions };

View File

@@ -0,0 +1,144 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.selectOptions = exports.deselectOptions = 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, {
skipPointerEventsCheck = false
} = {}) {
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 (typeof val !== 'string' && 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 "${String(val)}" not found in options`, select);
}
}
}).filter(option => !(0, _utils.isDisabled)(option));
if ((0, _utils.isDisabled)(select) || !selectedOptions.length) return;
if ((0, _utils.isElementType)(select, 'select')) {
if (select.multiple) {
for (const option of selectedOptions) {
const withPointerEvents = skipPointerEventsCheck ? true : (0, _utils.hasPointerEvents)(option); // events fired for multiple select are weird. Can't use hover...
if (withPointerEvents) {
_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);
if (withPointerEvents) {
_dom.fireEvent.pointerUp(option, init);
_dom.fireEvent.mouseUp(option, init);
}
selectOption(option);
if (withPointerEvents) {
_dom.fireEvent.click(option, init);
}
}
} else if (selectedOptions.length === 1) {
const withPointerEvents = skipPointerEventsCheck ? true : (0, _utils.hasPointerEvents)(select); // the click to open the select options
if (withPointerEvents) {
(0, _click.click)(select, init, {
skipPointerEventsCheck
});
} else {
(0, _focus.focus)(select);
}
selectOption(selectedOptions[0]);
if (withPointerEvents) {
// 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, {
skipPointerEventsCheck
});
(0, _click.click)(option, init, {
skipPointerEventsCheck
});
(0, _hover.unhover)(option, init, {
skipPointerEventsCheck
});
});
} 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;

View File

@@ -0,0 +1,6 @@
interface tabOptions {
shift?: boolean;
focusTrap?: Document | Element;
}
declare function tab({ shift, focusTrap }?: tabOptions): void;
export { tab };

View File

@@ -0,0 +1,145 @@
"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 ((0, _utils.isDocument)(focusTrap) && (currentIndex === 0 && shift || currentIndex === elements.length - 1 && !shift)) {
return focusTrap.body;
}
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 doc = (_focusTrap$ownerDocum = (_focusTrap = focusTrap) == null ? void 0 : _focusTrap.ownerDocument) != null ? _focusTrap$ownerDocum : document;
const previousElement = (0, _utils.getActiveElement)(doc);
if (!focusTrap) {
focusTrap = doc;
}
const focusableElements = focusTrap.querySelectorAll(_utils.FOCUSABLE_SELECTOR);
const enabledElements = Array.from(focusableElements).filter(el => el === previousElement || el.getAttribute('tabindex') !== '-1' && !(0, _utils.isDisabled)(el) && // 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 = Number(a.el.getAttribute('tabindex'));
const tabIndexB = Number(b.el.getAttribute('tabindex'));
const diff = tabIndexA - tabIndexB;
return diff === 0 ? a.idx - b.idx : diff;
}).map(({
el
}) => el); // TODO: verify/remove type casts
const checkedRadio = {};
let prunedElements = [];
orderedElements.forEach(currentElement => {
// 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
const el = currentElement;
if (el.type === 'radio' && el.name) {
// If the active element is part of the group, add only that
const prev = previousElement;
if (prev && prev.type === el.type && prev.name === el.name) {
if (el === prev) {
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 (typeof checkedRadio[el.name] !== 'undefined') {
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 === doc.body) {
/* istanbul ignore else */
if (previousElement) {
(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",
*/

View File

@@ -0,0 +1,7 @@
import { typeOptions } from './typeImplementation';
export declare function type(element: Element, text: string, options?: typeOptions & {
delay?: 0;
}): void;
export declare function type(element: Element, text: string, options: typeOptions & {
delay: number;
}): Promise<void>;

View File

@@ -0,0 +1,33 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.type = type;
var _dom = require("@testing-library/dom");
var _typeImplementation = require("./typeImplementation");
// 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.
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
if (delay > 0) {
return (0, _dom.getConfig)().asyncWrapper(() => (0, _typeImplementation.typeImplementation)(element, text, {
delay,
...options
}));
} else {
return void (0, _typeImplementation.typeImplementation)(element, text, {
delay,
...options
}) // prevents users from dealing with UnhandledPromiseRejectionWarning
.catch(console.error);
}
}

View File

@@ -0,0 +1,10 @@
export interface typeOptions {
delay?: number;
skipClick?: boolean;
skipAutoClose?: boolean;
initialSelectionStart?: number;
initialSelectionEnd?: number;
}
export declare function typeImplementation(element: Element, text: string, { delay, skipClick, skipAutoClose, initialSelectionStart, initialSelectionEnd, }: typeOptions & {
delay: number;
}): Promise<void>;

View File

@@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.typeImplementation = typeImplementation;
var _utils = require("../utils");
var _click = require("../click");
var _keyboard = require("../keyboard");
async function typeImplementation(element, text, {
delay,
skipClick = false,
skipAutoClose = false,
initialSelectionStart = undefined,
initialSelectionEnd = undefined
}) {
// TODO: properly type guard
// we use this workaround for now to prevent changing behavior
if (element.disabled) return;
if (!skipClick) (0, _click.click)(element); // 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 its 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
// explicitly 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 === null || selectionStart === 0) && (selectionEnd === null || selectionEnd === 0)) {
(0, _utils.setSelectionRange)(currentElement(), initialSelectionStart != null ? initialSelectionStart : value.length, initialSelectionEnd != null ? initialSelectionEnd : value.length);
}
const {
promise,
releaseAllKeys
} = (0, _keyboard.keyboardImplementationWrapper)(text, {
delay,
document: element.ownerDocument
});
if (delay > 0) {
await promise;
}
if (!skipAutoClose) {
releaseAllKeys();
} // eslint-disable-next-line consistent-return -- we need to return the internal Promise so that it is catchable if we don't await
return promise;
}

View File

@@ -0,0 +1,9 @@
interface uploadInit {
clickInit?: MouseEventInit;
changeInit?: EventInit;
}
interface uploadOptions {
applyAccept?: boolean;
}
declare function upload(element: HTMLElement, fileOrFiles: File | File[], init?: uploadInit, { applyAccept }?: uploadOptions): void;
export { upload };

View File

@@ -0,0 +1,98 @@
"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");
var _utils = require("./utils");
function upload(element, fileOrFiles, init, {
applyAccept = false
} = {}) {
var _input$files;
const input = (0, _utils.isElementType)(element, 'label') ? element.control : element;
if (!input || !(0, _utils.isElementType)(input, 'input', {
type: 'file'
})) {
throw new TypeError(`The ${input === element ? 'given' : 'associated'} ${input == null ? void 0 : input.tagName} element does not accept file uploads`);
}
if ((0, _utils.isDisabled)(element)) return;
(0, _click.click)(element, init == null ? void 0 : init.clickInit);
const files = (Array.isArray(fileOrFiles) ? fileOrFiles : [fileOrFiles]).filter(file => !applyAccept || isAcceptableFile(file, input.accept)).slice(0, input.multiple ? undefined : 1); // blur fires when the file selector pops up
(0, _blur.blur)(element); // focus fires when they make their selection
(0, _focus.focus)(element); // do not fire an input event if the file selection does not change
if (files.length === ((_input$files = input.files) == null ? void 0 : _input$files.length) && files.every((f, i) => {
var _input$files2;
return f === ((_input$files2 = input.files) == null ? void 0 : _input$files2.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 = { ...files,
length: files.length,
item: index => files[index],
[Symbol.iterator]() {
let i = 0;
return {
next: () => ({
done: i >= files.length,
value: files[i++]
})
};
}
};
(0, _dom.fireEvent)(input, (0, _dom.createEvent)('input', input, {
target: {
files: inputFiles
},
bubbles: true,
cancelable: false,
composed: true
}));
_dom.fireEvent.change(input, {
target: {
files: inputFiles
},
...(init == null ? void 0 : init.changeInit)
});
}
function isAcceptableFile(file, accept) {
if (!accept) {
return true;
}
const wildcards = ['audio/*', 'image/*', 'video/*'];
return accept.split(',').some(acceptToken => {
if (acceptToken.startsWith('.')) {
// 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;
});
}

View File

@@ -0,0 +1,31 @@
export declare function getMouseEventOptions(event: string, init?: MouseEventInit, clickCount?: number): {
detail: number;
buttons: number;
button: number;
clientX?: number | undefined;
clientY?: number | undefined;
movementX?: number | undefined;
movementY?: number | undefined;
relatedTarget?: EventTarget | null | undefined;
screenX?: number | undefined;
screenY?: number | undefined;
altKey?: boolean | undefined;
ctrlKey?: boolean | undefined;
metaKey?: boolean | undefined;
modifierAltGraph?: boolean | undefined;
modifierCapsLock?: boolean | undefined;
modifierFn?: boolean | undefined;
modifierFnLock?: boolean | undefined;
modifierHyper?: boolean | undefined;
modifierNumLock?: boolean | undefined;
modifierScrollLock?: boolean | undefined;
modifierSuper?: boolean | undefined;
modifierSymbol?: boolean | undefined;
modifierSymbolLock?: boolean | undefined;
shiftKey?: boolean | undefined;
view?: Window | null | undefined;
which?: number | undefined;
bubbles?: boolean | undefined;
cancelable?: boolean | undefined;
composed?: boolean | undefined;
};

View File

@@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getMouseEventOptions = getMouseEventOptions;
function isMousePressEvent(event) {
return event === 'mousedown' || event === 'mouseup' || event === 'click' || event === 'dblclick';
} // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
const BUTTONS_NAMES = {
none: 0,
primary: 1,
secondary: 2,
auxiliary: 4
}; // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
const BUTTON_NAMES = {
primary: 0,
auxiliary: 1,
secondary: 2
};
function translateButtonNumber(value, from) {
var _Object$entries$find;
const [mapIn, mapOut] = from === 'button' ? [BUTTON_NAMES, BUTTONS_NAMES] : [BUTTONS_NAMES, BUTTON_NAMES];
const name = (_Object$entries$find = Object.entries(mapIn).find(([, i]) => i === value)) == null ? void 0 : _Object$entries$find[0]; // istanbul ignore next
return name && Object.prototype.hasOwnProperty.call(mapOut, name) ? mapOut[name] : 0;
}
function convertMouseButtons(event, init, property) {
if (!isMousePressEvent(event)) {
return 0;
}
if (typeof init[property] === 'number') {
return init[property];
} else if (property === 'button' && typeof init.buttons === 'number') {
return translateButtonNumber(init.buttons, 'buttons');
} else if (property === 'buttons' && typeof init.button === 'number') {
return translateButtonNumber(init.button, 'button');
}
return property != 'button' && isMousePressEvent(event) ? 1 : 0;
}
function getMouseEventOptions(event, init, clickCount = 0) {
var _init;
init = (_init = init) != null ? _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'),
button: convertMouseButtons(event, init, 'button')
};
}

View File

@@ -0,0 +1 @@
export declare function isClickableInput(element: Element): boolean;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isClickableInput = isClickableInput;
var _isElementType = require("../misc/isElementType");
const CLICKABLE_INPUT_TYPES = ['button', 'color', 'file', 'image', 'reset', 'submit', 'checkbox', 'radio'];
function isClickableInput(element) {
return (0, _isElementType.isElementType)(element, 'button') || (0, _isElementType.isElementType)(element, 'input') && CLICKABLE_INPUT_TYPES.includes(element.type);
}

View File

@@ -0,0 +1 @@
export declare function buildTimeValue(value: string): string;

View File

@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildTimeValue = buildTimeValue;
function buildTimeValue(value) {
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 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')}`;
}

View File

@@ -0,0 +1,7 @@
export declare function calculateNewValue(newEntry: string, element: HTMLElement, value?: string, selectionRange?: {
selectionStart: number | null;
selectionEnd: number | null;
}, deleteContent?: 'backward' | 'forward'): {
newValue: string;
newSelectionStart: number;
};

View File

@@ -0,0 +1,48 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.calculateNewValue = calculateNewValue;
var _selectionRange = require("./selectionRange");
var _getValue2 = require("./getValue");
var _isValidDateValue = require("./isValidDateValue");
var _isValidInputTimeValue = require("./isValidInputTimeValue");
function calculateNewValue(newEntry, element, value = (() => {
var _getValue;
return (_getValue = (0, _getValue2.getValue)(element)) != null ? _getValue :
/* istanbul ignore next */
'';
})(), selectionRange = (0, _selectionRange.getSelectionRange)(element), deleteContent) {
const selectionStart = selectionRange.selectionStart === null ? value.length : selectionRange.selectionStart;
const selectionEnd = selectionRange.selectionEnd === null ? value.length : selectionRange.selectionEnd;
const prologEnd = Math.max(0, selectionStart === selectionEnd && deleteContent === 'backward' ? selectionStart - 1 : selectionStart);
const prolog = value.substring(0, prologEnd);
const epilogStart = Math.min(value.length, selectionStart === selectionEnd && deleteContent === 'forward' ? selectionEnd + 1 : selectionEnd);
const epilog = value.substring(epilogStart, value.length);
let newValue = `${prolog}${newEntry}${epilog}`;
const newSelectionStart = prologEnd + newEntry.length;
if (element.type === 'date' && !(0, _isValidDateValue.isValidDateValue)(element, newValue)) {
newValue = value;
}
if (element.type === 'time' && !(0, _isValidInputTimeValue.isValidInputTimeValue)(element, newValue)) {
if ((0, _isValidInputTimeValue.isValidInputTimeValue)(element, newEntry)) {
newValue = newEntry;
} else {
newValue = value;
}
}
return {
newValue,
newSelectionStart
};
}

View File

@@ -0,0 +1,2 @@
export declare function isCursorAtEnd(element: Element): boolean;
export declare function isCursorAtStart(element: Element): boolean;

View File

@@ -0,0 +1,35 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isCursorAtEnd = isCursorAtEnd;
exports.isCursorAtStart = isCursorAtStart;
var _selectionRange = require("./selectionRange");
var _getValue2 = require("./getValue");
function isCursorAtEnd(element) {
var _getValue;
const {
selectionStart,
selectionEnd
} = (0, _selectionRange.getSelectionRange)(element);
return selectionStart === selectionEnd && (selectionStart != null ? selectionStart :
/* istanbul ignore next */
0) === ((_getValue = (0, _getValue2.getValue)(element)) != null ? _getValue :
/* istanbul ignore next */
'').length;
}
function isCursorAtStart(element) {
const {
selectionStart,
selectionEnd
} = (0, _selectionRange.getSelectionRange)(element);
return selectionStart === selectionEnd && (selectionStart != null ? selectionStart :
/* istanbul ignore next */
0) === 0;
}

View File

@@ -0,0 +1 @@
export declare function getValue(element: Element | null): string | null;

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getValue = getValue;
var _isContentEditable = require("./isContentEditable");
function getValue(element) {
// istanbul ignore if
if (!element) {
return null;
}
if ((0, _isContentEditable.isContentEditable)(element)) {
return element.textContent;
}
return element.value;
}

View File

@@ -0,0 +1,10 @@
declare enum unreliableValueInputTypes {
'number' = "number"
}
/**
* Check if an empty IDL value on the element could mean a derivation of displayed value and IDL value
*/
export declare function hasUnreliableEmptyValue(element: Element): element is HTMLInputElement & {
type: unreliableValueInputTypes;
};
export {};

View File

@@ -0,0 +1,21 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.hasUnreliableEmptyValue = hasUnreliableEmptyValue;
var _isElementType = require("../misc/isElementType");
var unreliableValueInputTypes;
/**
* Check if an empty IDL value on the element could mean a derivation of displayed value and IDL value
*/
(function (unreliableValueInputTypes) {
unreliableValueInputTypes["number"] = "number";
})(unreliableValueInputTypes || (unreliableValueInputTypes = {}));
function hasUnreliableEmptyValue(element) {
return (0, _isElementType.isElementType)(element, 'input') && Boolean(unreliableValueInputTypes[element.type]);
}

View File

@@ -0,0 +1,3 @@
export declare function isContentEditable(element: Element): element is HTMLElement & {
contenteditable: 'true';
};

View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isContentEditable = isContentEditable;
//jsdom is not supporting isContentEditable
function isContentEditable(element) {
return element.hasAttribute('contenteditable') && (element.getAttribute('contenteditable') == 'true' || element.getAttribute('contenteditable') == '');
}

View File

@@ -0,0 +1,24 @@
import { isContentEditable } from './isContentEditable';
declare type GuardedType<T> = T extends (x: any) => x is infer R ? R : never;
export declare function isEditable(element: Element): element is GuardedType<typeof isContentEditable> | GuardedType<typeof isEditableInput> | (HTMLTextAreaElement & {
readOnly: false;
});
export declare enum editableInputTypes {
'text' = "text",
'date' = "date",
'datetime-local' = "datetime-local",
'email' = "email",
'month' = "month",
'number' = "number",
'password' = "password",
'search' = "search",
'tel' = "tel",
'time' = "time",
'url' = "url",
'week' = "week"
}
export declare function isEditableInput(element: Element): element is HTMLInputElement & {
readOnly: false;
type: editableInputTypes;
};
export {};

View File

@@ -0,0 +1,42 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.editableInputTypes = void 0;
exports.isEditable = isEditable;
exports.isEditableInput = isEditableInput;
var _isElementType = require("../misc/isElementType");
var _isContentEditable = require("./isContentEditable");
function isEditable(element) {
return isEditableInput(element) || (0, _isElementType.isElementType)(element, 'textarea', {
readOnly: false
}) || (0, _isContentEditable.isContentEditable)(element);
}
let editableInputTypes;
exports.editableInputTypes = editableInputTypes;
(function (editableInputTypes) {
editableInputTypes["text"] = "text";
editableInputTypes["date"] = "date";
editableInputTypes["datetime-local"] = "datetime-local";
editableInputTypes["email"] = "email";
editableInputTypes["month"] = "month";
editableInputTypes["number"] = "number";
editableInputTypes["password"] = "password";
editableInputTypes["search"] = "search";
editableInputTypes["tel"] = "tel";
editableInputTypes["time"] = "time";
editableInputTypes["url"] = "url";
editableInputTypes["week"] = "week";
})(editableInputTypes || (exports.editableInputTypes = editableInputTypes = {}));
function isEditableInput(element) {
return (0, _isElementType.isElementType)(element, 'input', {
readOnly: false
}) && Boolean(editableInputTypes[element.type]);
}

View File

@@ -0,0 +1,3 @@
export declare function isValidDateValue(element: HTMLInputElement & {
type: 'date';
}, value: string): boolean;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isValidDateValue = isValidDateValue;
function isValidDateValue(element, value) {
const clone = element.cloneNode();
clone.value = value;
return clone.value === value;
}

View File

@@ -0,0 +1,3 @@
export declare function isValidInputTimeValue(element: HTMLInputElement & {
type: 'time';
}, timeValue: string): boolean;

View File

@@ -0,0 +1,12 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isValidInputTimeValue = isValidInputTimeValue;
function isValidInputTimeValue(element, timeValue) {
const clone = element.cloneNode();
clone.value = timeValue;
return clone.value === timeValue;
}

View File

@@ -0,0 +1 @@
export declare function getSpaceUntilMaxLength(element: Element): number | undefined;

View File

@@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getSpaceUntilMaxLength = getSpaceUntilMaxLength;
var _isElementType = require("../misc/isElementType");
var _getValue = require("./getValue");
var maxLengthSupportedTypes;
(function (maxLengthSupportedTypes) {
maxLengthSupportedTypes["email"] = "email";
maxLengthSupportedTypes["password"] = "password";
maxLengthSupportedTypes["search"] = "search";
maxLengthSupportedTypes["telephone"] = "telephone";
maxLengthSupportedTypes["text"] = "text";
maxLengthSupportedTypes["url"] = "url";
})(maxLengthSupportedTypes || (maxLengthSupportedTypes = {}));
function getSpaceUntilMaxLength(element) {
const value = (0, _getValue.getValue)(element);
/* istanbul ignore if */
if (value === null) {
return undefined;
}
const maxLength = getSanitizedMaxLength(element);
return maxLength ? maxLength - value.length : undefined;
} // can't use .maxLength property because of a jsdom bug:
// https://github.com/jsdom/jsdom/issues/2927
function getSanitizedMaxLength(element) {
var _element$getAttribute;
if (!supportsMaxLength(element)) {
return undefined;
}
const attr = (_element$getAttribute = element.getAttribute('maxlength')) != null ? _element$getAttribute : '';
return /^\d+$/.test(attr) && Number(attr) >= 0 ? Number(attr) : undefined;
}
function supportsMaxLength(element) {
return (0, _isElementType.isElementType)(element, 'textarea') || (0, _isElementType.isElementType)(element, 'input') && Boolean(maxLengthSupportedTypes[element.type]);
}

View File

@@ -0,0 +1,16 @@
declare enum selectionSupportType {
'text' = "text",
'search' = "search",
'url' = "url",
'tel' = "tel",
'password' = "password"
}
export declare function hasSelectionSupport(element: Element): element is HTMLTextAreaElement | (HTMLInputElement & {
type: selectionSupportType;
});
export declare function getSelectionRange(element: Element): {
selectionStart: number | null;
selectionEnd: number | null;
};
export declare function setSelectionRange(element: Element, newSelectionStart: number, newSelectionEnd: number): void;
export {};

View File

@@ -0,0 +1,104 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getSelectionRange = getSelectionRange;
exports.hasSelectionSupport = hasSelectionSupport;
exports.setSelectionRange = setSelectionRange;
var _isElementType = require("../misc/isElementType");
// https://github.com/jsdom/jsdom/blob/c2fb8ff94917a4d45e2398543f5dd2a8fed0bdab/lib/jsdom/living/nodes/HTMLInputElement-impl.js#L45
var selectionSupportType;
(function (selectionSupportType) {
selectionSupportType["text"] = "text";
selectionSupportType["search"] = "search";
selectionSupportType["url"] = "url";
selectionSupportType["tel"] = "tel";
selectionSupportType["password"] = "password";
})(selectionSupportType || (selectionSupportType = {}));
const InputSelection = Symbol('inputSelection');
function hasSelectionSupport(element) {
return (0, _isElementType.isElementType)(element, 'textarea') || (0, _isElementType.isElementType)(element, 'input') && Boolean(selectionSupportType[element.type]);
}
function getSelectionRange(element) {
if (hasSelectionSupport(element)) {
return {
selectionStart: element.selectionStart,
selectionEnd: element.selectionEnd
};
}
if ((0, _isElementType.isElementType)(element, 'input')) {
var _InputSelection;
return (_InputSelection = element[InputSelection]) != null ? _InputSelection : {
selectionStart: null,
selectionEnd: null
};
}
const selection = element.ownerDocument.getSelection(); // there should be no editing if the focusNode is outside of element
// TODO: properly handle selection ranges
if (selection != null && selection.rangeCount && element.contains(selection.focusNode)) {
const range = selection.getRangeAt(0);
return {
selectionStart: range.startOffset,
selectionEnd: range.endOffset
};
} else {
return {
selectionStart: null,
selectionEnd: null
};
}
}
function setSelectionRange(element, newSelectionStart, newSelectionEnd) {
const {
selectionStart,
selectionEnd
} = getSelectionRange(element);
if (selectionStart === newSelectionStart && selectionEnd === newSelectionEnd) {
return;
}
if (hasSelectionSupport(element)) {
element.setSelectionRange(newSelectionStart, newSelectionEnd);
}
if ((0, _isElementType.isElementType)(element, 'input')) {
;
element[InputSelection] = {
selectionStart: newSelectionStart,
selectionEnd: newSelectionEnd
};
} // Moving the selection inside <input> or <textarea> does not alter the document Selection.
if ((0, _isElementType.isElementType)(element, 'input') || (0, _isElementType.isElementType)(element, 'textarea')) {
return;
}
const range = element.ownerDocument.createRange();
range.selectNodeContents(element); // istanbul ignore else
if (element.firstChild) {
range.setStart(element.firstChild, newSelectionStart);
range.setEnd(element.firstChild, newSelectionEnd);
}
const selection = element.ownerDocument.getSelection(); // istanbul ignore else
if (selection) {
selection.removeAllRanges();
selection.addRange(range);
}
}

View File

@@ -0,0 +1 @@
export declare function getActiveElement(document: Document | ShadowRoot): Element | null;

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getActiveElement = getActiveElement;
var _isDisabled = require("../misc/isDisabled");
function getActiveElement(document) {
const activeElement = document.activeElement;
if (activeElement != null && activeElement.shadowRoot) {
return getActiveElement(activeElement.shadowRoot);
} else {
// Browser does not yield disabled elements as document.activeElement - jsdom does
if ((0, _isDisabled.isDisabled)(activeElement)) {
return document.ownerDocument ? // TODO: verify behavior in ShadowRoot
/* istanbul ignore next */
document.ownerDocument.body : document.body;
}
return activeElement;
}
}

View File

@@ -0,0 +1 @@
export declare function isFocusable(element: Element): element is HTMLElement;

View File

@@ -0,0 +1,14 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isFocusable = isFocusable;
var _isLabelWithInternallyDisabledControl = require("../misc/isLabelWithInternallyDisabledControl");
var _selector = require("./selector");
function isFocusable(element) {
return !(0, _isLabelWithInternallyDisabledControl.isLabelWithInternallyDisabledControl)(element) && element.matches(_selector.FOCUSABLE_SELECTOR);
}

View File

@@ -0,0 +1 @@
export declare const FOCUSABLE_SELECTOR: string;

View File

@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.FOCUSABLE_SELECTOR = void 0;
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;

View File

@@ -0,0 +1,25 @@
export * from './click/getMouseEventOptions';
export * from './click/isClickableInput';
export * from './edit/buildTimeValue';
export * from './edit/calculateNewValue';
export * from './edit/cursorPosition';
export * from './edit/getValue';
export * from './edit/hasUnreliableEmptyValue';
export * from './edit/isContentEditable';
export * from './edit/isEditable';
export * from './edit/isValidDateValue';
export * from './edit/isValidInputTimeValue';
export * from './edit/maxLength';
export * from './edit/selectionRange';
export * from './focus/getActiveElement';
export * from './focus/isFocusable';
export * from './focus/selector';
export * from './misc/eventWrapper';
export * from './misc/isElementType';
export * from './misc/isLabelWithInternallyDisabledControl';
export * from './misc/isVisible';
export * from './misc/isDisabled';
export * from './misc/isDocument';
export * from './misc/wait';
export * from './misc/hasPointerEvents';
export * from './misc/hasFormSubmit';

View File

@@ -0,0 +1,330 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _getMouseEventOptions = require("./click/getMouseEventOptions");
Object.keys(_getMouseEventOptions).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _getMouseEventOptions[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _getMouseEventOptions[key];
}
});
});
var _isClickableInput = require("./click/isClickableInput");
Object.keys(_isClickableInput).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isClickableInput[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isClickableInput[key];
}
});
});
var _buildTimeValue = require("./edit/buildTimeValue");
Object.keys(_buildTimeValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _buildTimeValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _buildTimeValue[key];
}
});
});
var _calculateNewValue = require("./edit/calculateNewValue");
Object.keys(_calculateNewValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _calculateNewValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _calculateNewValue[key];
}
});
});
var _cursorPosition = require("./edit/cursorPosition");
Object.keys(_cursorPosition).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _cursorPosition[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _cursorPosition[key];
}
});
});
var _getValue = require("./edit/getValue");
Object.keys(_getValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _getValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _getValue[key];
}
});
});
var _hasUnreliableEmptyValue = require("./edit/hasUnreliableEmptyValue");
Object.keys(_hasUnreliableEmptyValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _hasUnreliableEmptyValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _hasUnreliableEmptyValue[key];
}
});
});
var _isContentEditable = require("./edit/isContentEditable");
Object.keys(_isContentEditable).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isContentEditable[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isContentEditable[key];
}
});
});
var _isEditable = require("./edit/isEditable");
Object.keys(_isEditable).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isEditable[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isEditable[key];
}
});
});
var _isValidDateValue = require("./edit/isValidDateValue");
Object.keys(_isValidDateValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isValidDateValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isValidDateValue[key];
}
});
});
var _isValidInputTimeValue = require("./edit/isValidInputTimeValue");
Object.keys(_isValidInputTimeValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isValidInputTimeValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isValidInputTimeValue[key];
}
});
});
var _maxLength = require("./edit/maxLength");
Object.keys(_maxLength).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _maxLength[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _maxLength[key];
}
});
});
var _selectionRange = require("./edit/selectionRange");
Object.keys(_selectionRange).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _selectionRange[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _selectionRange[key];
}
});
});
var _getActiveElement = require("./focus/getActiveElement");
Object.keys(_getActiveElement).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _getActiveElement[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _getActiveElement[key];
}
});
});
var _isFocusable = require("./focus/isFocusable");
Object.keys(_isFocusable).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isFocusable[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isFocusable[key];
}
});
});
var _selector = require("./focus/selector");
Object.keys(_selector).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _selector[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _selector[key];
}
});
});
var _eventWrapper = require("./misc/eventWrapper");
Object.keys(_eventWrapper).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _eventWrapper[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _eventWrapper[key];
}
});
});
var _isElementType = require("./misc/isElementType");
Object.keys(_isElementType).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isElementType[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isElementType[key];
}
});
});
var _isLabelWithInternallyDisabledControl = require("./misc/isLabelWithInternallyDisabledControl");
Object.keys(_isLabelWithInternallyDisabledControl).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isLabelWithInternallyDisabledControl[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isLabelWithInternallyDisabledControl[key];
}
});
});
var _isVisible = require("./misc/isVisible");
Object.keys(_isVisible).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isVisible[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isVisible[key];
}
});
});
var _isDisabled = require("./misc/isDisabled");
Object.keys(_isDisabled).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isDisabled[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isDisabled[key];
}
});
});
var _isDocument = require("./misc/isDocument");
Object.keys(_isDocument).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _isDocument[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _isDocument[key];
}
});
});
var _wait = require("./misc/wait");
Object.keys(_wait).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _wait[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _wait[key];
}
});
});
var _hasPointerEvents = require("./misc/hasPointerEvents");
Object.keys(_hasPointerEvents).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _hasPointerEvents[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _hasPointerEvents[key];
}
});
});
var _hasFormSubmit = require("./misc/hasFormSubmit");
Object.keys(_hasFormSubmit).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _hasFormSubmit[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _hasFormSubmit[key];
}
});
});

View File

@@ -0,0 +1 @@
export declare function eventWrapper<T>(cb: () => T): T | undefined;

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.eventWrapper = eventWrapper;
var _dom = require("@testing-library/dom");
function eventWrapper(cb) {
let result;
(0, _dom.getConfig)().eventWrapper(() => {
result = cb();
});
return result;
}

View File

@@ -0,0 +1 @@
export declare const hasFormSubmit: (form: HTMLFormElement | null) => form is HTMLFormElement;

View File

@@ -0,0 +1,10 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.hasFormSubmit = void 0;
const hasFormSubmit = form => !!(form && (form.querySelector('input[type="submit"]') || form.querySelector('button[type="submit"]')));
exports.hasFormSubmit = hasFormSubmit;

View File

@@ -0,0 +1,15 @@
/**
* Options that can be passed to any event that relies
* on pointer-events property
*/
export declare interface PointerOptions {
/**
* When set to `true` the event skips checking if any element
* in the DOM-tree has `'pointer-events: none'` set. This check is
* costly in general and very costly when rendering large DOM-trees.
* Can be used to speed up tests.
* Default: `false`
* */
skipPointerEventsCheck?: boolean;
}
export declare function hasPointerEvents(element: Element): boolean;

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.hasPointerEvents = hasPointerEvents;
var _helpers = require("@testing-library/dom/dist/helpers");
function hasPointerEvents(element) {
const window = (0, _helpers.getWindowFromNode)(element);
for (let el = element; (_el = el) != null && _el.ownerDocument; el = el.parentElement) {
var _el;
const pointerEvents = window.getComputedStyle(el).pointerEvents;
if (pointerEvents && !['inherit', 'unset'].includes(pointerEvents)) {
return pointerEvents !== 'none';
}
}
return true;
}

Some files were not shown because too many files have changed in this diff Show More