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

20
frontend/node_modules/@testing-library/dom/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2017 Kent C. Dodds
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.

402
frontend/node_modules/@testing-library/dom/README.md generated vendored Normal file
View File

@@ -0,0 +1,402 @@
<div align="center">
<h1>DOM Testing Library</h1>
<a href="https://www.emojione.com/emoji/1f419">
<img
height="80"
width="80"
alt="octopus"
src="https://raw.githubusercontent.com/testing-library/dom-testing-library/main/other/octopus.png"
/>
</a>
<p>Simple and complete DOM testing utilities that encourage good testing
practices.</p>
[**Read the docs**](https://testing-library.com/dom) |
[Edit the docs](https://github.com/testing-library/testing-library-docs)
</div>
<hr />
<!-- 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 -->
<div align="center">
<a href="https://testingjavascript.com">
<img
width="500"
alt="TestingJavaScript.com Learn the smart, efficient way to test any JavaScript application."
src="https://raw.githubusercontent.com/testing-library/dom-testing-library/main/other/testingjavascript.jpg"
/>
</a>
</div>
## 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)
- [This Solution](#this-solution)
- [Installation](#installation)
- [Documentation](#documentation)
- [Guiding Principles](#guiding-principles)
- [Contributors](#contributors)
- [LICENSE](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## The Problem
You want to write maintainable tests for your Web UI. As a part of this goal,
you want your tests to avoid including implementation details of your components
and rather focus on making your tests give you the confidence for which they are
intended. As part of this, you want your testbase to be maintainable in the long
run so refactors of your components (changes to implementation but not
functionality) don't break your tests and slow you and your team down.
## This Solution
The `DOM Testing Library` is a very light-weight solution for testing DOM nodes
(whether simulated with [`JSDOM`](https://github.com/jsdom/jsdom) as provided by
default with [Jest][] or in the browser). The main utilities it provides involve
querying the DOM for nodes in a way that's similar to how the user finds
elements on the page. In this way, the library helps ensure your tests give you
confidence in your UI code. The `DOM Testing Library`'s primary guiding
principle is:
> [The more your tests resemble the way your software is used, the more
> confidence they can give you.][guiding-principle]
## Installation
This module is distributed via [npm][npm] which is bundled with [node][node] and
should be installed as one of your project's `devDependencies`:
```
npm install --save-dev @testing-library/dom
```
> [**Docs**](https://testing-library.com/docs/install)
## Documentation
Read the docs (and discover framework and tool-specific implementations) at
[testing-library.com](https://testing-library.com/dom)
## Guiding Principles
> [The more your tests resemble the way your software is used, the more
> confidence they can give you.][guiding-principle]
We try to only expose methods and utilities that encourage you to write tests
that closely resemble how your web pages are used.
Utilities are included in this project based on the following guiding
principles:
1. If it relates to rendering components, it deals with DOM nodes rather than
component instances, nor should it encourage dealing with component
instances.
2. It should be generally useful for testing the application components in the
way the user would use it. We _are_ making some trade-offs here because
we're using a computer and often a simulated browser environment, but in
general, utilities should encourage tests that use the components the way
they're intended to be used.
3. Utility implementations and APIs should be simple and flexible.
At the end of the day, what we want is for this library to be pretty
light-weight, simple, and understandable.
## 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>
<tbody>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://kentcdodds.com"><img src="https://avatars.githubusercontent.com/u/1500684?v=3?s=100" width="100px;" alt="Kent C. Dodds"/><br /><sub><b>Kent C. Dodds</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=kentcdodds" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=kentcdodds" title="Documentation">📖</a> <a href="#infra-kentcdodds" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=kentcdodds" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://argos-ci.com"><img src="https://avatars2.githubusercontent.com/u/266302?v=4?s=100" width="100px;" alt="Greg Bergé"/><br /><sub><b>Greg Bergé</b></sub></a><br /><a href="#ideas-neoziro" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://audiolion.github.io"><img src="https://avatars1.githubusercontent.com/u/2430381?v=4?s=100" width="100px;" alt="Ryan Castner"/><br /><sub><b>Ryan Castner</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=audiolion" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.dnlsandiego.com"><img src="https://avatars0.githubusercontent.com/u/8008023?v=4?s=100" width="100px;" alt="Daniel Sandiego"/><br /><sub><b>Daniel Sandiego</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=dnlsandiego" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Miklet"><img src="https://avatars2.githubusercontent.com/u/12592677?v=4?s=100" width="100px;" alt="Paweł Mikołajczyk"/><br /><sub><b>Paweł Mikołajczyk</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Miklet" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://co.linkedin.com/in/alejandronanez/"><img src="https://avatars3.githubusercontent.com/u/464978?v=4?s=100" width="100px;" alt="Alejandro Ñáñez Ortiz"/><br /><sub><b>Alejandro Ñáñez Ortiz</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=alejandronanez" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/pbomb"><img src="https://avatars0.githubusercontent.com/u/1402095?v=4?s=100" width="100px;" alt="Matt Parrish"/><br /><sub><b>Matt Parrish</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Apbomb" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=pbomb" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=pbomb" title="Documentation">📖</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=pbomb" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wKovacs64"><img src="https://avatars1.githubusercontent.com/u/1288694?v=4?s=100" width="100px;" alt="Justin Hall"/><br /><sub><b>Justin Hall</b></sub></a><br /><a href="#platform-wKovacs64" title="Packaging/porting to new platform">📦</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/antoaravinth"><img src="https://avatars1.githubusercontent.com/u/1241511?s=460&v=4?s=100" width="100px;" alt="Anto Aravinth"/><br /><sub><b>Anto Aravinth</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=antoaravinth" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=antoaravinth" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=antoaravinth" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JonahMoses"><img src="https://avatars2.githubusercontent.com/u/3462296?v=4?s=100" width="100px;" alt="Jonah Moses"/><br /><sub><b>Jonah Moses</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=JonahMoses" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://team.thebrain.pro"><img src="https://avatars1.githubusercontent.com/u/4002543?v=4?s=100" width="100px;" alt="Łukasz Gandecki"/><br /><sub><b>Łukasz Gandecki</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=lgandecki" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=lgandecki" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=lgandecki" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://sompylasar.github.io"><img src="https://avatars2.githubusercontent.com/u/498274?v=4?s=100" width="100px;" alt="Ivan Babak"/><br /><sub><b>Ivan Babak</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Asompylasar" title="Bug reports">🐛</a> <a href="#ideas-sompylasar" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=sompylasar" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=sompylasar" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jday3"><img src="https://avatars3.githubusercontent.com/u/4439618?v=4?s=100" width="100px;" alt="Jesse Day"/><br /><sub><b>Jesse Day</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=jday3" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://gnapse.github.io"><img src="https://avatars0.githubusercontent.com/u/15199?v=4?s=100" width="100px;" alt="Ernesto García"/><br /><sub><b>Ernesto García</b></sub></a><br /><a href="#question-gnapse" title="Answering Questions">💬</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=gnapse" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=gnapse" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://jomaxx.com"><img src="https://avatars2.githubusercontent.com/u/2747424?v=4?s=100" width="100px;" alt="Josef Maxx Blake"/><br /><sub><b>Josef Maxx Blake</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=jomaxx" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=jomaxx" title="Documentation">📖</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=jomaxx" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/alecook"><img src="https://avatars3.githubusercontent.com/u/725236?v=4?s=100" width="100px;" alt="Alex Cook"/><br /><sub><b>Alex Cook</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=alecook" title="Documentation">📖</a> <a href="#example-alecook" title="Examples">💡</a> <a href="https://github.com/testing-library/dom-testing-library/pulls?q=is%3Apr+reviewed-by%3Aalecook" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dfcook"><img src="https://avatars3.githubusercontent.com/u/10348212?v=4?s=100" width="100px;" alt="Daniel Cook"/><br /><sub><b>Daniel Cook</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=dfcook" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=dfcook" title="Documentation">📖</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=dfcook" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/thchia"><img src="https://avatars2.githubusercontent.com/u/21194045?s=400&v=4?s=100" width="100px;" alt="Thomas Chia"/><br /><sub><b>Thomas Chia</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Athchia" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=thchia" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://timdeschryver.dev"><img src="https://avatars.githubusercontent.com/u/28659384?v=4?s=100" width="100px;" alt="Tim Deschryver"/><br /><sub><b>Tim Deschryver</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=timdeschryver" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=timdeschryver" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/pulls?q=is%3Apr+reviewed-by%3Atimdeschryver" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://alexkrolick.com"><img src="https://avatars3.githubusercontent.com/u/1571667?v=4?s=100" width="100px;" alt="Alex Krolick"/><br /><sub><b>Alex Krolick</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=alexkrolick" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.maddijoyce.com"><img src="https://avatars2.githubusercontent.com/u/2224291?v=4?s=100" width="100px;" alt="Maddi Joyce"/><br /><sub><b>Maddi Joyce</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=maddijoyce" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/npeterkamps"><img src="https://avatars1.githubusercontent.com/u/25429764?v=4?s=100" width="100px;" alt="Peter Kamps"/><br /><sub><b>Peter Kamps</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Anpeterkamps" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=npeterkamps" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=npeterkamps" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://jonathanstoye.de"><img src="https://avatars2.githubusercontent.com/u/21689428?v=4?s=100" width="100px;" alt="Jonathan Stoye"/><br /><sub><b>Jonathan Stoye</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=JonathanStoye" title="Documentation">📖</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=JonathanStoye" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/yongdamsh"><img src="https://avatars2.githubusercontent.com/u/4126644?v=4?s=100" width="100px;" alt="Sanghyeon Lee"/><br /><sub><b>Sanghyeon Lee</b></sub></a><br /><a href="#example-yongdamsh" title="Examples">💡</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Dajust"><img src="https://avatars3.githubusercontent.com/u/8015514?v=4?s=100" width="100px;" alt="Justice Mba "/><br /><sub><b>Justice Mba </b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Dajust" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=Dajust" title="Documentation">📖</a> <a href="#ideas-Dajust" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/wgcrouch"><img src="https://avatars3.githubusercontent.com/u/340761?v=4?s=100" width="100px;" alt="Wayne Crouch"/><br /><sub><b>Wayne Crouch</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=wgcrouch" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://benjaminelliott.co.uk"><img src="https://avatars1.githubusercontent.com/u/4996462?v=4?s=100" width="100px;" alt="Ben Elliott"/><br /><sub><b>Ben Elliott</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=benelliott" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://nuances.co"><img src="https://avatars3.githubusercontent.com/u/577921?v=4?s=100" width="100px;" alt="Ruben Costa"/><br /><sub><b>Ruben Costa</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=rubencosta" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://rbrtsmith.com/"><img src="https://avatars2.githubusercontent.com/u/4982001?v=4?s=100" width="100px;" alt="Robert Smith"/><br /><sub><b>Robert Smith</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Arbrtsmith" title="Bug reports">🐛</a> <a href="#ideas-rbrtsmith" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=rbrtsmith" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dadamssg"><img src="https://avatars3.githubusercontent.com/u/881986?v=4?s=100" width="100px;" alt="dadamssg"/><br /><sub><b>dadamssg</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=dadamssg" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://neilkistner.com/"><img src="https://avatars1.githubusercontent.com/u/186971?v=4?s=100" width="100px;" alt="Neil Kistner"/><br /><sub><b>Neil Kistner</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=wyze" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=wyze" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://bdchauvette.net/"><img src="https://avatars3.githubusercontent.com/u/1448597?v=4?s=100" width="100px;" alt="Ben Chauvette"/><br /><sub><b>Ben Chauvette</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=bdchauvette" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JeffBaumgardt"><img src="https://avatars2.githubusercontent.com/u/777527?v=4?s=100" width="100px;" alt="Jeff Baumgardt"/><br /><sub><b>Jeff Baumgardt</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=JeffBaumgardt" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=JeffBaumgardt" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://matchai.me"><img src="https://avatars0.githubusercontent.com/u/4658208?v=4?s=100" width="100px;" alt="Matan Kushner"/><br /><sub><b>Matan Kushner</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=matchai" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=matchai" title="Documentation">📖</a> <a href="#ideas-matchai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=matchai" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.wendtedesigns.com/"><img src="https://avatars2.githubusercontent.com/u/5779538?v=4?s=100" width="100px;" alt="Alex Wendte"/><br /><sub><b>Alex Wendte</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=themostcolm" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=themostcolm" title="Documentation">📖</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=themostcolm" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ruffle1986"><img src="https://avatars0.githubusercontent.com/u/2196208?v=4?s=100" width="100px;" alt="Tamas Fodor"/><br /><sub><b>Tamas Fodor</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=ruffle1986" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/BenjaminEckardt"><img src="https://avatars3.githubusercontent.com/u/14793495?v=4?s=100" width="100px;" alt="Benjamin Eckardt"/><br /><sub><b>Benjamin Eckardt</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=BenjaminEckardt" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/campbellr"><img src="https://avatars3.githubusercontent.com/u/205752?v=4?s=100" width="100px;" alt="Ryan Campbell"/><br /><sub><b>Ryan Campbell</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=campbellr" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://taylor-briggs.com"><img src="https://avatars2.githubusercontent.com/u/1335519?v=4?s=100" width="100px;" alt="Taylor Briggs"/><br /><sub><b>Taylor Briggs</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=TaylorBriggs" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jgoz"><img src="https://avatars2.githubusercontent.com/u/132233?v=4?s=100" width="100px;" alt="John Gozde"/><br /><sub><b>John Gozde</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=jgoz" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/chentsulin"><img src="https://avatars2.githubusercontent.com/u/3382565?v=4?s=100" width="100px;" alt="C. T. Lin"/><br /><sub><b>C. T. Lin</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=chentsulin" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://terrencewwong.com"><img src="https://avatars3.githubusercontent.com/u/5312329?v=4?s=100" width="100px;" alt="Terrence Wong"/><br /><sub><b>Terrence Wong</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=terrencewwong" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://www.ossfinder.com"><img src="https://avatars0.githubusercontent.com/u/12230408?v=4?s=100" width="100px;" alt="Soo Jae Hwang"/><br /><sub><b>Soo Jae Hwang</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=misoguy" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/RoystonS"><img src="https://avatars0.githubusercontent.com/u/19773?v=4?s=100" width="100px;" alt="Royston Shufflebotham"/><br /><sub><b>Royston Shufflebotham</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3ARoystonS" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=RoystonS" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=RoystonS" title="Documentation">📖</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=RoystonS" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.vadimbrodsky.com"><img src="https://avatars0.githubusercontent.com/u/591673?v=4?s=100" width="100px;" alt="Vadim Brodsky"/><br /><sub><b>Vadim Brodsky</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=VadimBrodsky" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/eunjae_lee"><img src="https://avatars3.githubusercontent.com/u/499898?v=4?s=100" width="100px;" alt="Eunjae Lee"/><br /><sub><b>Eunjae Lee</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=eunjae-lee" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://davidpeter.me"><img src="https://avatars2.githubusercontent.com/u/167743?v=4?s=100" width="100px;" alt="David Peter"/><br /><sub><b>David Peter</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=sarenji" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/@puemos"><img src="https://avatars0.githubusercontent.com/u/13174025?v=4?s=100" width="100px;" alt="Shy Alter"/><br /><sub><b>Shy Alter</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=puemos" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=puemos" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://lukaszmakuch.pl"><img src="https://avatars1.githubusercontent.com/u/11966621?v=4?s=100" width="100px;" alt="Łukasz Makuch"/><br /><sub><b>Łukasz Makuch</b></sub></a><br /><a href="#platform-lukaszmakuch" title="Packaging/porting to new platform">📦</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tylerthehaas"><img src="https://avatars1.githubusercontent.com/u/11150235?v=4?s=100" width="100px;" alt="Tyler Haas"/><br /><sub><b>Tyler Haas</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=tylerthehaas" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=tylerthehaas" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://vesalaakso.com"><img src="https://avatars2.githubusercontent.com/u/482561?v=4?s=100" width="100px;" alt="Vesa Laakso"/><br /><sub><b>Vesa Laakso</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=valscion" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=valscion" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Tolsee"><img src="https://avatars0.githubusercontent.com/u/16590492?v=4?s=100" width="100px;" alt="Tulsi Sapkota"/><br /><sub><b>Tulsi Sapkota</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Tolsee" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tnunes"><img src="https://avatars1.githubusercontent.com/u/163187?v=4?s=100" width="100px;" alt="Tiago Nunes"/><br /><sub><b>Tiago Nunes</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=tnunes" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=tnunes" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/JaxCavalera"><img src="https://avatars1.githubusercontent.com/u/15429762?v=4?s=100" width="100px;" alt="JaxCavalera"/><br /><sub><b>JaxCavalera</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=JaxCavalera" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/pulls?q=is%3Apr+reviewed-by%3AJaxCavalera" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/bopfer"><img src="https://avatars2.githubusercontent.com/u/824368?v=4?s=100" width="100px;" alt="bopfer"/><br /><sub><b>bopfer</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=bopfer" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://blog.alfrescian.com"><img src="https://avatars0.githubusercontent.com/u/1340740?v=4?s=100" width="100px;" alt="Jan Pfitzner"/><br /><sub><b>Jan Pfitzner</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=alfrescian" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dbismut"><img src="https://avatars2.githubusercontent.com/u/5003380?v=4?s=100" width="100px;" alt="David"/><br /><sub><b>David</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=dbismut" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/diego_codes"><img src="https://avatars0.githubusercontent.com/u/5973294?v=4?s=100" width="100px;" alt="Diego Hernandez"/><br /><sub><b>Diego Hernandez</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=diego-codes" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=diego-codes" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/foray1010"><img src="https://avatars3.githubusercontent.com/u/3212221?v=4?s=100" width="100px;" alt="Alex Young"/><br /><sub><b>Alex Young</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=foray1010" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/paularmstrong"><img src="https://avatars1.githubusercontent.com/u/33297?v=4?s=100" width="100px;" alt="Paul Armstrong"/><br /><sub><b>Paul Armstrong</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=paularmstrong" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=paularmstrong" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://hu.linkedin.com/pub/tamas-szabo/57/a4b/242"><img src="https://avatars0.githubusercontent.com/u/3720079?v=4?s=100" width="100px;" alt="Tamás Szabó"/><br /><sub><b>Tamás Szabó</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=szabototo89" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=szabototo89" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/dylan_piercey"><img src="https://avatars2.githubusercontent.com/u/4985201?v=4?s=100" width="100px;" alt="Dylan Piercey"/><br /><sub><b>Dylan Piercey</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=DylanPiercey" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=DylanPiercey" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/michaellasky"><img src="https://avatars2.githubusercontent.com/u/6646599?v=4?s=100" width="100px;" alt="Michael Lasky"/><br /><sub><b>Michael Lasky</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=michaellasky" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=michaellasky" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=michaellasky" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/sebsilbermann"><img src="https://avatars3.githubusercontent.com/u/12292047?v=4?s=100" width="100px;" alt="Sebastian Silbermann"/><br /><sub><b>Sebastian Silbermann</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=eps1lon" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=eps1lon" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=eps1lon" title="Documentation">📖</a> <a href="#infra-eps1lon" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/testing-library/dom-testing-library/pulls?q=is%3Apr+reviewed-by%3Aeps1lon" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://dylanvann.com/"><img src="https://avatars0.githubusercontent.com/u/1537615?v=4?s=100" width="100px;" alt="Dylan Vann"/><br /><sub><b>Dylan Vann</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=DylanVann" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://afontcu.dev"><img src="https://avatars0.githubusercontent.com/u/9197791?v=4?s=100" width="100px;" alt="Adrià Fontcuberta"/><br /><sub><b>Adrià Fontcuberta</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=afontcu" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=afontcu" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=afontcu" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://thomlom.dev"><img src="https://avatars3.githubusercontent.com/u/16003285?v=4?s=100" width="100px;" alt="Thomas Lombart"/><br /><sub><b>Thomas Lombart</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=thomlom" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/SavePointSam"><img src="https://avatars0.githubusercontent.com/u/8203211?v=4?s=100" width="100px;" alt="Sam Horton"/><br /><sub><b>Sam Horton</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=SavePointSam" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=SavePointSam" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://andrewhillcode.com"><img src="https://avatars1.githubusercontent.com/u/12396191?v=4?s=100" width="100px;" alt="Andrew Hill"/><br /><sub><b>Andrew Hill</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=andrewhillcode" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://amann.me"><img src="https://avatars1.githubusercontent.com/u/4038316?v=4?s=100" width="100px;" alt="Jan Amann"/><br /><sub><b>Jan Amann</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=amannn" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=amannn" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/brapifra"><img src="https://avatars3.githubusercontent.com/u/17855450?v=4?s=100" width="100px;" alt="Brais Piñeiro"/><br /><sub><b>Brais Piñeiro</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=brapifra" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=brapifra" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.dominykas.com/"><img src="https://avatars1.githubusercontent.com/u/505619?v=4?s=100" width="100px;" alt="Dominykas Blyžė"/><br /><sub><b>Dominykas Blyžė</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=dominykas" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=dominykas" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://olzhas.de"><img src="https://avatars3.githubusercontent.com/u/15848876?v=4?s=100" width="100px;" alt="Olzhas Askar"/><br /><sub><b>Olzhas Askar</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=pheeria" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=pheeria" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=pheeria" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/mbelsky_"><img src="https://avatars1.githubusercontent.com/u/3923527?v=4?s=100" width="100px;" alt="Max Belsky"/><br /><sub><b>Max Belsky</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=mbelsky" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=mbelsky" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mmantel"><img src="https://avatars2.githubusercontent.com/u/1326403?v=4?s=100" width="100px;" alt="Michael Mantel"/><br /><sub><b>Michael Mantel</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=mmantel" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://tomdoes.tech/"><img src="https://avatars1.githubusercontent.com/u/8683577?v=4?s=100" width="100px;" alt="Tom Nagle"/><br /><sub><b>Tom Nagle</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=tomanagle" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://westbrookjohnson.com"><img src="https://avatars0.githubusercontent.com/u/1156657?v=4?s=100" width="100px;" alt="Westbrook Johnson"/><br /><sub><b>Westbrook Johnson</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Westbrook" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://aziz.js.org"><img src="https://avatars3.githubusercontent.com/u/17024120?v=4?s=100" width="100px;" alt="Mohammad Aziz"/><br /><sub><b>Mohammad Aziz</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=iAziz786" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=iAziz786" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/seetdev/"><img src="https://avatars2.githubusercontent.com/u/35116035?v=4?s=100" width="100px;" alt="seetdev"/><br /><sub><b>seetdev</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=seetdev" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=seetdev" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/xgbuils"><img src="https://avatars2.githubusercontent.com/u/6483614?v=4?s=100" width="100px;" alt="Xavier Garcia Buils"/><br /><sub><b>Xavier Garcia Buils</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=xgbuils" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=xgbuils" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/aw-davidson"><img src="https://avatars2.githubusercontent.com/u/32170938?v=4?s=100" width="100px;" alt="aw-davidson"/><br /><sub><b>aw-davidson</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=aw-davidson" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=aw-davidson" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://michaeldeboey.be"><img src="https://avatars3.githubusercontent.com/u/6643991?v=4?s=100" width="100px;" alt="Michaël De Boey"/><br /><sub><b>Michaël De Boey</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=MichaelDeBoey" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/minh_ngvyen"><img src="https://avatars3.githubusercontent.com/u/2852660?v=4?s=100" width="100px;" alt="Minh Nguyen"/><br /><sub><b>Minh Nguyen</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=NMinhNguyen" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/egilsster"><img src="https://avatars0.githubusercontent.com/u/5672257?v=4?s=100" width="100px;" alt="Egill Sveinbjörnsson"/><br /><sub><b>Egill Sveinbjörnsson</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=egilsster" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://pustovalov.dev"><img src="https://avatars2.githubusercontent.com/u/1568885?v=4?s=100" width="100px;" alt="Pavel Pustovalov"/><br /><sub><b>Pavel Pustovalov</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=pustovalov" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/apalaniuk"><img src="https://avatars1.githubusercontent.com/u/17710124?v=4?s=100" width="100px;" alt="Adam Palaniuk"/><br /><sub><b>Adam Palaniuk</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=apalaniuk" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=apalaniuk" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Yama-Tomo"><img src="https://avatars0.githubusercontent.com/u/4970917?v=4?s=100" width="100px;" alt="Yama-Tomo"/><br /><sub><b>Yama-Tomo</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Yama-Tomo" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=Yama-Tomo" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/airjp73"><img src="https://avatars2.githubusercontent.com/u/25882770?v=4?s=100" width="100px;" alt="Aaron Pettengill"/><br /><sub><b>Aaron Pettengill</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=airjp73" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=airjp73" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://kwboyd.com"><img src="https://avatars0.githubusercontent.com/u/13855750?v=4?s=100" width="100px;" alt="Kate W. Boyd"/><br /><sub><b>Kate W. Boyd</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=kwboyd" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/rahulchavan30"><img src="https://avatars2.githubusercontent.com/u/5296464?v=4?s=100" width="100px;" alt="Rahul Suryakanth"/><br /><sub><b>Rahul Suryakanth</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=rahulchavan30" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=rahulchavan30" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://jamie.tokyo"><img src="https://avatars0.githubusercontent.com/u/5964236?v=4?s=100" width="100px;" alt="Jamie"/><br /><sub><b>Jamie</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=jamsinclair" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=jamsinclair" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nstepien"><img src="https://avatars0.githubusercontent.com/u/567105?v=4?s=100" width="100px;" alt="Nicolas Stepien"/><br /><sub><b>Nicolas Stepien</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=nstepien" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://knpw.rs"><img src="https://avatars0.githubusercontent.com/u/174864?v=4?s=100" width="100px;" alt="Ken Powers"/><br /><sub><b>Ken Powers</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=knpwrs" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mzdunek93"><img src="https://avatars0.githubusercontent.com/u/10826511?v=4?s=100" width="100px;" alt="Michał Zdunek"/><br /><sub><b>Michał Zdunek</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=mzdunek93" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Lagily"><img src="https://avatars2.githubusercontent.com/u/42535205?v=4?s=100" width="100px;" alt="Ali Nasserzadeh"/><br /><sub><b>Ali Nasserzadeh</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Lagily" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://darekkay.com"><img src="https://avatars0.githubusercontent.com/u/3101914?v=4?s=100" width="100px;" alt="Darek Kay"/><br /><sub><b>Darek Kay</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=darekkay" title="Documentation">📖</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=darekkay" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=darekkay" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Lukas-Kullmann"><img src="https://avatars0.githubusercontent.com/u/387547?v=4?s=100" width="100px;" alt="Lukas"/><br /><sub><b>Lukas</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Lukas-Kullmann" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=Lukas-Kullmann" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://twitter.com/pelotom"><img src="https://avatars2.githubusercontent.com/u/128019?v=4?s=100" width="100px;" alt="Tom Crockett"/><br /><sub><b>Tom Crockett</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=pelotom" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=pelotom" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/appleJax"><img src="https://avatars1.githubusercontent.com/u/13618860?v=4?s=100" width="100px;" alt="Kevin Brewer"/><br /><sub><b>Kevin Brewer</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=appleJax" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=appleJax" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/benmonro"><img src="https://avatars3.githubusercontent.com/u/399236?v=4?s=100" width="100px;" alt="Ben Monro"/><br /><sub><b>Ben Monro</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=benmonro" title="Code">💻</a> <a href="#ideas-benmonro" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=benmonro" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=benmonro" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/smeijer"><img src="https://avatars1.githubusercontent.com/u/1196524?v=4?s=100" width="100px;" alt="Stephan Meijer"/><br /><sub><b>Stephan Meijer</b></sub></a><br /><a href="#ideas-smeijer" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=smeijer" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=smeijer" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://joaoforja.com/"><img src="https://avatars2.githubusercontent.com/u/7002157?v=4?s=100" width="100px;" alt="João Forja"/><br /><sub><b>João Forja</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Jnforja" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=Jnforja" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://nickmccurdy.com/"><img src="https://avatars0.githubusercontent.com/u/927220?v=4?s=100" width="100px;" alt="Nick McCurdy"/><br /><sub><b>Nick McCurdy</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=nickmccurdy" title="Documentation">📖</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=nickmccurdy" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=nickmccurdy" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/pulls?q=is%3Apr+reviewed-by%3Anickmccurdy" title="Reviewed Pull Requests">👀</a> <a href="#infra-nickmccurdy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://calebmer.com"><img src="https://avatars1.githubusercontent.com/u/8282507?v=4?s=100" width="100px;" alt="Caleb Meredith"/><br /><sub><b>Caleb Meredith</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=calebmer" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/marcosvega91"><img src="https://avatars2.githubusercontent.com/u/5365582?v=4?s=100" width="100px;" alt="Marco Moretti"/><br /><sub><b>Marco Moretti</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=marcosvega91" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=marcosvega91" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/pulls?q=is%3Apr+reviewed-by%3Amarcosvega91" title="Reviewed Pull Requests">👀</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/tjefferson08"><img src="https://avatars2.githubusercontent.com/u/3535390?v=4?s=100" width="100px;" alt="Travis Jefferson"/><br /><sub><b>Travis Jefferson</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=tjefferson08" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=tjefferson08" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mdjastrzebski"><img src="https://avatars2.githubusercontent.com/u/6368606?v=4?s=100" width="100px;" alt="Maciej Jastrzebski"/><br /><sub><b>Maciej Jastrzebski</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Amdjastrzebski" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://commonlit.org"><img src="https://avatars3.githubusercontent.com/u/319471?v=4?s=100" width="100px;" alt="Geoff Harcourt"/><br /><sub><b>Geoff Harcourt</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=geoffharcourt" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.joshuakgoldberg.com"><img src="https://avatars1.githubusercontent.com/u/3335181?v=4?s=100" width="100px;" alt="Josh Goldberg"/><br /><sub><b>Josh Goldberg</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=JoshuaKGoldberg" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=JoshuaKGoldberg" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://kengregory.com"><img src="https://avatars0.githubusercontent.com/u/3155127?v=4?s=100" width="100px;" alt="Ken Gregory"/><br /><sub><b>Ken Gregory</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=kgregory" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=kgregory" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.jacobparis.com/"><img src="https://avatars2.githubusercontent.com/u/5633704?v=4?s=100" width="100px;" alt="Jacob Paris"/><br /><sub><b>Jacob Paris</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=JacobParis" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=JacobParis" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://keiya01.github.io/portfolio"><img src="https://avatars1.githubusercontent.com/u/34934510?v=4?s=100" width="100px;" alt="keiya sasaki"/><br /><sub><b>keiya sasaki</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=keiya01" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/idanen"><img src="https://avatars2.githubusercontent.com/u/1687893?v=4?s=100" width="100px;" alt="Idan Entin"/><br /><sub><b>Idan Entin</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=idanen" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=idanen" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/deniz-susman-92b40a145/"><img src="https://avatars1.githubusercontent.com/u/39295979?v=4?s=100" width="100px;" alt="Deniz Susman"/><br /><sub><b>Deniz Susman</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=DenrizSusam" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/delca85"><img src="https://avatars1.githubusercontent.com/u/4076043?v=4?s=100" width="100px;" alt="Bianca Del Carretto"/><br /><sub><b>Bianca Del Carretto</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=delca85" title="Tests">⚠️</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=delca85" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.linkedin.com/in/joshlalonde3/"><img src="https://avatars3.githubusercontent.com/u/9097492?v=4?s=100" width="100px;" alt="Josh Lalonde"/><br /><sub><b>Josh Lalonde</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=ryuuji3" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=ryuuji3" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ipap360"><img src="https://avatars2.githubusercontent.com/u/11017666?v=4?s=100" width="100px;" alt="Ioannis Papadopoulos"/><br /><sub><b>Ioannis Papadopoulos</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=ipap360" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=ipap360" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/maxnewlands"><img src="https://avatars3.githubusercontent.com/u/1304166?v=4?s=100" width="100px;" alt="Maxwell Newlands"/><br /><sub><b>Maxwell Newlands</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=maxnewlands" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=maxnewlands" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.jaredlux.com"><img src="https://avatars0.githubusercontent.com/u/450478?v=4?s=100" width="100px;" alt="Jared Luxenberg"/><br /><sub><b>Jared Luxenberg</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=jluxenberg" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=jluxenberg" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Snizhana"><img src="https://avatars3.githubusercontent.com/u/18139946?v=4?s=100" width="100px;" alt="snizhana"/><br /><sub><b>snizhana</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Snizhana" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=Snizhana" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/micha149"><img src="https://avatars2.githubusercontent.com/u/298880?v=4?s=100" width="100px;" alt="Michael van Engelshoven"/><br /><sub><b>Michael van Engelshoven</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/pulls?q=is%3Apr+reviewed-by%3Amicha149" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://ashertuggle.wixsite.com/portfolio"><img src="https://avatars2.githubusercontent.com/u/10679635?v=4?s=100" width="100px;" alt="Asher Tuggle"/><br /><sub><b>Asher Tuggle</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Aawesomeunleashed" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/winterlamon"><img src="https://avatars0.githubusercontent.com/u/16295605?v=4?s=100" width="100px;" alt="Winter LaMon"/><br /><sub><b>Winter LaMon</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=winterlamon" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=winterlamon" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://victorandcode.com"><img src="https://avatars0.githubusercontent.com/u/18427801?v=4?s=100" width="100px;" alt="Victor Cordova"/><br /><sub><b>Victor Cordova</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=victorandcode" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=victorandcode" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/slowselfip"><img src="https://avatars3.githubusercontent.com/u/9762906?v=4?s=100" width="100px;" alt="slowselfip"/><br /><sub><b>slowselfip</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Aslowselfip" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Semigradsky"><img src="https://avatars3.githubusercontent.com/u/1198848?v=4?s=100" width="100px;" alt="Dmitry Semigradsky"/><br /><sub><b>Dmitry Semigradsky</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Semigradsky" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Tismas"><img src="https://avatars2.githubusercontent.com/u/13601275?v=4?s=100" width="100px;" alt="Adam"/><br /><sub><b>Adam</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Tismas" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=Tismas" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/balavishnuvj"><img src="https://avatars3.githubusercontent.com/u/13718688?v=4?s=100" width="100px;" alt="balavishnuvj"/><br /><sub><b>balavishnuvj</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=balavishnuvj" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://chriscolborne.com"><img src="https://avatars2.githubusercontent.com/u/101371?v=4?s=100" width="100px;" alt="Chris Colborne"/><br /><sub><b>Chris Colborne</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=zorfling" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/romain-trotard"><img src="https://avatars0.githubusercontent.com/u/17161484?v=4?s=100" width="100px;" alt="Romain Trotard"/><br /><sub><b>Romain Trotard</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=romain-trotard" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.thomasmarshall.com"><img src="https://avatars0.githubusercontent.com/u/770763?v=4?s=100" width="100px;" alt="Thomas Marshall"/><br /><sub><b>Thomas Marshall</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=thomasmarshall" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=thomasmarshall" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/johnjesse"><img src="https://avatars1.githubusercontent.com/u/6839660?v=4?s=100" width="100px;" alt="johnjessewood"/><br /><sub><b>johnjessewood</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Ajohnjesse" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=johnjesse" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://codepen.io/ariperkkio/"><img src="https://avatars2.githubusercontent.com/u/14806298?v=4?s=100" width="100px;" alt="Ari Perkkiö"/><br /><sub><b>Ari Perkkiö</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3AAriPerkkio" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=AriPerkkio" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=AriPerkkio" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/nathanforce"><img src="https://avatars2.githubusercontent.com/u/6694194?v=4?s=100" width="100px;" alt="Nathan Force"/><br /><sub><b>Nathan Force</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=nathanforce" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ph-fritsche"><img src="https://avatars.githubusercontent.com/u/39068198?v=4?s=100" width="100px;" alt="Philipp Fritsche"/><br /><sub><b>Philipp Fritsche</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=ph-fritsche" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://medium.com/@renatoalencar"><img src="https://avatars.githubusercontent.com/u/6964593?v=4?s=100" width="100px;" alt="Renato Alencar"/><br /><sub><b>Renato Alencar</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=renatoalencar" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=renatoalencar" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/SimenB"><img src="https://avatars.githubusercontent.com/u/1404810?v=4?s=100" width="100px;" alt="Simen Bekkhus"/><br /><sub><b>Simen Bekkhus</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3ASimenB" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/gaearon"><img src="https://avatars.githubusercontent.com/u/810438?v=4?s=100" width="100px;" alt="Dan Abramov"/><br /><sub><b>Dan Abramov</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Agaearon" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/dom-testing-library/pulls?q=is%3Apr+reviewed-by%3Agaearon" title="Reviewed Pull Requests">👀</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://matan.io"><img src="https://avatars.githubusercontent.com/u/12711091?v=4?s=100" width="100px;" alt="Matan Borenkraout"/><br /><sub><b>Matan Borenkraout</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=MatanBobi" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/simcha90"><img src="https://avatars.githubusercontent.com/u/56388545?v=4?s=100" width="100px;" alt="simcha90"/><br /><sub><b>simcha90</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=simcha90" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/amitmiran137"><img src="https://avatars.githubusercontent.com/u/47772523?v=4?s=100" width="100px;" alt="Amit Miran"/><br /><sub><b>Amit Miran</b></sub></a><br /><a href="#infra-amitmiran137" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/leschdom"><img src="https://avatars.githubusercontent.com/u/62334278?v=4?s=100" width="100px;" alt="Dominik Lesch"/><br /><sub><b>Dominik Lesch</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=leschdom" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/G-Rath"><img src="https://avatars.githubusercontent.com/u/3151613?v=4?s=100" width="100px;" alt="Gareth Jones"/><br /><sub><b>Gareth Jones</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=G-Rath" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=G-Rath" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/riotrah"><img src="https://avatars.githubusercontent.com/u/22646419?v=4?s=100" width="100px;" alt="Rayat Rahman"/><br /><sub><b>Rayat Rahman</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=riotrah" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/savcni01"><img src="https://avatars.githubusercontent.com/u/18025894?v=4?s=100" width="100px;" alt="Nik Savchenko"/><br /><sub><b>Nik Savchenko</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=savcni01" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://www.hawkeye.dog"><img src="https://avatars.githubusercontent.com/u/7245931?v=4?s=100" width="100px;" alt="Kevin Fleischman"/><br /><sub><b>Kevin Fleischman</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=istateside" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/commits?author=istateside" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://dfdx.us"><img src="https://avatars.githubusercontent.com/u/3087358?v=4?s=100" width="100px;" alt="Beth Hitch"/><br /><sub><b>Beth Hitch</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=dfoverdx" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="http://www.jacklaurence.net"><img src="https://avatars.githubusercontent.com/u/12763356?v=4?s=100" width="100px;" alt="Jack Laurence"/><br /><sub><b>Jack Laurence</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=jacklaurencegaray" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Lusito"><img src="https://avatars.githubusercontent.com/u/53570854?v=4?s=100" width="100px;" alt="SantoJambit"/><br /><sub><b>SantoJambit</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=SantoJambit" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/mittalyashu"><img src="https://avatars.githubusercontent.com/u/29014463?v=4?s=100" width="100px;" alt="Yashu Mittal"/><br /><sub><b>Yashu Mittal</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=mittalyashu" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/IanVS"><img src="https://avatars.githubusercontent.com/u/4616705?v=4?s=100" width="100px;" alt="Ian VanSchooten"/><br /><sub><b>Ian VanSchooten</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=IanVS" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://supervanya.com"><img src="https://avatars.githubusercontent.com/u/12336038?v=4?s=100" width="100px;" alt="Vanya Prokopovich"/><br /><sub><b>Vanya Prokopovich</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Asupervanya" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jrnail23"><img src="https://avatars.githubusercontent.com/u/392612?v=4?s=100" width="100px;" alt="James Nail"/><br /><sub><b>James Nail</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Ajrnail23" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/robcaldecott"><img src="https://avatars.githubusercontent.com/u/796702?v=4?s=100" width="100px;" alt="Rob Caldecott"/><br /><sub><b>Rob Caldecott</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Arobcaldecott" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Dennis273"><img src="https://avatars.githubusercontent.com/u/19815164?v=4?s=100" width="100px;" alt="Dennis273"/><br /><sub><b>Dennis273</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Dennis273" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.jacksonhardaker.dev"><img src="https://avatars.githubusercontent.com/u/7596320?v=4?s=100" width="100px;" alt="Jackson Hardaker"/><br /><sub><b>Jackson Hardaker</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=jacksonhardaker" title="Tests">⚠️</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://www.robin-drexler.com/"><img src="https://avatars.githubusercontent.com/u/474248?v=4?s=100" width="100px;" alt="Robin Drexler"/><br /><sub><b>Robin Drexler</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=robin-drexler" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/dolevoper"><img src="https://avatars.githubusercontent.com/u/53278705?v=4?s=100" width="100px;" alt="Omer Dolev"/><br /><sub><b>Omer Dolev</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=dolevoper" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Lirlev48"><img src="https://avatars.githubusercontent.com/u/58209233?v=4?s=100" width="100px;" alt="Lirlev48"/><br /><sub><b>Lirlev48</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=Lirlev48" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/kalmi"><img src="https://avatars.githubusercontent.com/u/54426?v=4?s=100" width="100px;" alt="Tarnay Kálmán"/><br /><sub><b>Tarnay Kálmán</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=kalmi" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/MynockSpit"><img src="https://avatars.githubusercontent.com/u/5713867?v=4?s=100" width="100px;" alt="Than Hutchins"/><br /><sub><b>Than Hutchins</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=MynockSpit" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/raplemie"><img src="https://avatars.githubusercontent.com/u/1904889?v=4?s=100" width="100px;" alt="Raphaël LEMIEUX"/><br /><sub><b>Raphaël LEMIEUX</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=raplemie" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/arthurlvilasboas"><img src="https://avatars.githubusercontent.com/u/95368212?v=4?s=100" width="100px;" alt="Arthur Lauck Vilas Boas"/><br /><sub><b>Arthur Lauck Vilas Boas</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=arthurlvilasboas" title="Documentation">📖</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/DaniAcu"><img src="https://avatars.githubusercontent.com/u/26409015?v=4?s=100" width="100px;" alt="Daniel Acuña"/><br /><sub><b>Daniel Acuña</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=DaniAcu" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/jlp-craigmorten"><img src="https://avatars.githubusercontent.com/u/124147726?v=4?s=100" width="100px;" alt="Craig Morten"/><br /><sub><b>Craig Morten</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=jlp-craigmorten" title="Code">💻</a> <a href="#question-jlp-craigmorten" title="Answering Questions">💬</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://naor.dev"><img src="https://avatars.githubusercontent.com/u/6171622?v=4?s=100" width="100px;" alt="Naor Peled"/><br /><sub><b>Naor Peled</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=naorpeled" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="http://everlong.org/"><img src="https://avatars.githubusercontent.com/u/454175?v=4?s=100" width="100px;" alt="Julien Wajsberg"/><br /><sub><b>Julien Wajsberg</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=julienw" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3Ajulienw" title="Bug reports">🐛</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/KevinBon"><img src="https://avatars.githubusercontent.com/u/1910927?v=4?s=100" width="100px;" alt="Kevin BON"/><br /><sub><b>Kevin BON</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=KevinBon" title="Code">💻</a> <a href="https://github.com/testing-library/dom-testing-library/issues?q=author%3AKevinBon" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sieem"><img src="https://avatars.githubusercontent.com/u/36861085?v=4?s=100" width="100px;" alt="sieem"/><br /><sub><b>sieem</b></sub></a><br /><a href="https://github.com/testing-library/dom-testing-library/commits?author=sieem" title="Code">💻</a></td>
</tr>
</tbody>
</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/dom-testing-library/validate?logo=github&style=flat-square
[build]: https://github.com/testing-library/dom-testing-library/actions?query=workflow%3Avalidate
[coverage-badge]: https://img.shields.io/codecov/c/github/testing-library/dom-testing-library.svg?style=flat-square
[coverage]: https://codecov.io/github/testing-library/dom-testing-library
[version-badge]: https://img.shields.io/npm/v/@testing-library/dom.svg?style=flat-square
[package]: https://www.npmjs.com/package/@testing-library/dom
[downloads-badge]: https://img.shields.io/npm/dm/@testing-library/dom.svg?style=flat-square
[npmtrends]: http://www.npmtrends.com/@testing-library/dom
[license-badge]: https://img.shields.io/npm/l/@testing-library/dom.svg?style=flat-square
[license]: https://github.com/testing-library/dom-testing-library/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/dom-testing-library/blob/main/CODE_OF_CONDUCT.md
[github-watch-badge]: https://img.shields.io/github/watchers/testing-library/dom-testing-library.svg?style=social
[github-watch]: https://github.com/testing-library/dom-testing-library/watchers
[github-star-badge]: https://img.shields.io/github/stars/testing-library/dom-testing-library.svg?style=social
[github-star]: https://github.com/testing-library/dom-testing-library/stargazers
[twitter]: https://twitter.com/intent/tweet?text=Check%20out%20dom-testing-library%20by%20%40testing-library%20https%3A%2F%2Fgithub.com%2Ftesting-library%2Fdom-testing-library%20%F0%9F%91%8D
[twitter-badge]: https://img.shields.io/twitter/url/https/github.com/testing-library/dom-testing-library.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/dom-testing-library?color=orange&style=flat-square
[set-immediate]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate
[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106
[jest]: https://facebook.github.io/jest
[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
<!-- prettier-ignore-end -->

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,116 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = createDOMElementFilter;
exports.test = void 0;
/**
* Source: https://github.com/facebook/jest/blob/e7bb6a1e26ffab90611b2593912df15b69315611/packages/pretty-format/src/plugins/DOMElement.ts
*/
/* eslint-disable -- trying to stay as close to the original as possible */
/* istanbul ignore file */
function escapeHTML(str) {
return str.replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
// Return empty string if keys is empty.
const printProps = (keys, props, config, indentation, depth, refs, printer) => {
const indentationNext = indentation + config.indent;
const colors = config.colors;
return keys.map(key => {
const value = props[key];
let printed = printer(value, config, indentationNext, depth, refs);
if (typeof value !== 'string') {
if (printed.indexOf('\n') !== -1) {
printed = config.spacingOuter + indentationNext + printed + config.spacingOuter + indentation;
}
printed = '{' + printed + '}';
}
return config.spacingInner + indentation + colors.prop.open + key + colors.prop.close + '=' + colors.value.open + printed + colors.value.close;
}).join('');
};
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#node_type_constants
const NodeTypeTextNode = 3;
// Return empty string if children is empty.
const printChildren = (children, config, indentation, depth, refs, printer) => children.map(child => {
const printedChild = typeof child === 'string' ? printText(child, config) : printer(child, config, indentation, depth, refs);
if (printedChild === '' && typeof child === 'object' && child !== null && child.nodeType !== NodeTypeTextNode) {
// A plugin serialized this Node to '' meaning we should ignore it.
return '';
}
return config.spacingOuter + indentation + printedChild;
}).join('');
const printText = (text, config) => {
const contentColor = config.colors.content;
return contentColor.open + escapeHTML(text) + contentColor.close;
};
const printComment = (comment, config) => {
const commentColor = config.colors.comment;
return commentColor.open + '<!--' + escapeHTML(comment) + '-->' + commentColor.close;
};
// Separate the functions to format props, children, and element,
// so a plugin could override a particular function, if needed.
// Too bad, so sad: the traditional (but unnecessary) space
// in a self-closing tagColor requires a second test of printedProps.
const printElement = (type, printedProps, printedChildren, config, indentation) => {
const tagColor = config.colors.tag;
return tagColor.open + '<' + type + (printedProps && tagColor.close + printedProps + config.spacingOuter + indentation + tagColor.open) + (printedChildren ? '>' + tagColor.close + printedChildren + config.spacingOuter + indentation + tagColor.open + '</' + type : (printedProps && !config.min ? '' : ' ') + '/') + '>' + tagColor.close;
};
const printElementAsLeaf = (type, config) => {
const tagColor = config.colors.tag;
return tagColor.open + '<' + type + tagColor.close + ' …' + tagColor.open + ' />' + tagColor.close;
};
const ELEMENT_NODE = 1;
const TEXT_NODE = 3;
const COMMENT_NODE = 8;
const FRAGMENT_NODE = 11;
const ELEMENT_REGEXP = /^((HTML|SVG)\w*)?Element$/;
const isCustomElement = val => {
const {
tagName
} = val;
return Boolean(typeof tagName === 'string' && tagName.includes('-') || typeof val.hasAttribute === 'function' && val.hasAttribute('is'));
};
const testNode = val => {
const constructorName = val.constructor.name;
const {
nodeType
} = val;
return nodeType === ELEMENT_NODE && (ELEMENT_REGEXP.test(constructorName) || isCustomElement(val)) || nodeType === TEXT_NODE && constructorName === 'Text' || nodeType === COMMENT_NODE && constructorName === 'Comment' || nodeType === FRAGMENT_NODE && constructorName === 'DocumentFragment';
};
const test = val => (val?.constructor?.name || isCustomElement(val)) && testNode(val);
exports.test = test;
function nodeIsText(node) {
return node.nodeType === TEXT_NODE;
}
function nodeIsComment(node) {
return node.nodeType === COMMENT_NODE;
}
function nodeIsFragment(node) {
return node.nodeType === FRAGMENT_NODE;
}
function createDOMElementFilter(filterNode) {
return {
test: val => (val?.constructor?.name || isCustomElement(val)) && testNode(val),
serialize: (node, config, indentation, depth, refs, printer) => {
if (nodeIsText(node)) {
return printText(node.data, config);
}
if (nodeIsComment(node)) {
return printComment(node.data, config);
}
const type = nodeIsFragment(node) ? `DocumentFragment` : node.tagName.toLowerCase();
if (++depth > config.maxDepth) {
return printElementAsLeaf(type, config);
}
return printElement(type, printProps(nodeIsFragment(node) ? [] : Array.from(node.attributes).map(attr => attr.name).sort(), nodeIsFragment(node) ? {} : Array.from(node.attributes).reduce((props, attribute) => {
props[attribute.name] = attribute.value;
return props;
}, {}), config, indentation + config.indent, depth, refs, printer), printChildren(Array.prototype.slice.call(node.childNodes || node.children).filter(filterNode), config, indentation + config.indent, depth, refs, printer), config, indentation);
}
};
}

View File

@@ -0,0 +1,67 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.configure = configure;
exports.getConfig = getConfig;
exports.runWithExpensiveErrorDiagnosticsDisabled = runWithExpensiveErrorDiagnosticsDisabled;
var _prettyDom = require("./pretty-dom");
// It would be cleaner for this to live inside './queries', but
// other parts of the code assume that all exports from
// './queries' are query functions.
let config = {
testIdAttribute: 'data-testid',
asyncUtilTimeout: 1000,
// asyncWrapper and advanceTimersWrapper is to support React's async `act` function.
// forcing react-testing-library to wrap all async functions would've been
// a total nightmare (consider wrapping every findBy* query and then also
// updating `within` so those would be wrapped too. Total nightmare).
// so we have this config option that's really only intended for
// react-testing-library to use. For that reason, this feature will remain
// undocumented.
asyncWrapper: cb => cb(),
unstable_advanceTimersWrapper: cb => cb(),
eventWrapper: cb => cb(),
// default value for the `hidden` option in `ByRole` queries
defaultHidden: false,
// default value for the `ignore` option in `ByText` queries
defaultIgnore: 'script, style',
// showOriginalStackTrace flag to show the full error stack traces for async errors
showOriginalStackTrace: false,
// throw errors w/ suggestions for better queries. Opt in so off by default.
throwSuggestions: false,
// called when getBy* queries fail. (message, container) => Error
getElementError(message, container) {
const prettifiedDOM = (0, _prettyDom.prettyDOM)(container);
const error = new Error([message, `Ignored nodes: comments, ${config.defaultIgnore}\n${prettifiedDOM}`].filter(Boolean).join('\n\n'));
error.name = 'TestingLibraryElementError';
return error;
},
_disableExpensiveErrorDiagnostics: false,
computedStyleSupportsPseudoElements: false
};
function runWithExpensiveErrorDiagnosticsDisabled(callback) {
try {
config._disableExpensiveErrorDiagnostics = true;
return callback();
} finally {
config._disableExpensiveErrorDiagnostics = false;
}
}
function configure(newConfig) {
if (typeof newConfig === 'function') {
// Pass the existing config out to the provided function
// and accept a delta in return
newConfig = newConfig(config);
}
// Merge the incoming config delta
config = {
...config,
...newConfig
};
}
function getConfig() {
return config;
}

View File

@@ -0,0 +1,711 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.eventMap = exports.eventAliasMap = void 0;
const eventMap = exports.eventMap = {
// Clipboard Events
copy: {
EventType: 'ClipboardEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
cut: {
EventType: 'ClipboardEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
paste: {
EventType: 'ClipboardEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
// Composition Events
compositionEnd: {
EventType: 'CompositionEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
compositionStart: {
EventType: 'CompositionEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
compositionUpdate: {
EventType: 'CompositionEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
// Keyboard Events
keyDown: {
EventType: 'KeyboardEvent',
defaultInit: {
bubbles: true,
cancelable: true,
charCode: 0,
composed: true
}
},
keyPress: {
EventType: 'KeyboardEvent',
defaultInit: {
bubbles: true,
cancelable: true,
charCode: 0,
composed: true
}
},
keyUp: {
EventType: 'KeyboardEvent',
defaultInit: {
bubbles: true,
cancelable: true,
charCode: 0,
composed: true
}
},
// Focus Events
focus: {
EventType: 'FocusEvent',
defaultInit: {
bubbles: false,
cancelable: false,
composed: true
}
},
blur: {
EventType: 'FocusEvent',
defaultInit: {
bubbles: false,
cancelable: false,
composed: true
}
},
focusIn: {
EventType: 'FocusEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
focusOut: {
EventType: 'FocusEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
// Form Events
change: {
EventType: 'Event',
defaultInit: {
bubbles: true,
cancelable: false
}
},
input: {
EventType: 'InputEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
invalid: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: true
}
},
submit: {
EventType: 'Event',
defaultInit: {
bubbles: true,
cancelable: true
}
},
reset: {
EventType: 'Event',
defaultInit: {
bubbles: true,
cancelable: true
}
},
// Mouse Events
click: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: true,
cancelable: true,
button: 0,
composed: true
}
},
contextMenu: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
dblClick: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
drag: {
EventType: 'DragEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
dragEnd: {
EventType: 'DragEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
dragEnter: {
EventType: 'DragEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
dragExit: {
EventType: 'DragEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
dragLeave: {
EventType: 'DragEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
dragOver: {
EventType: 'DragEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
dragStart: {
EventType: 'DragEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
drop: {
EventType: 'DragEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
mouseDown: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
mouseEnter: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: false,
cancelable: false,
composed: true
}
},
mouseLeave: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: false,
cancelable: false,
composed: true
}
},
mouseMove: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
mouseOut: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
mouseOver: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
mouseUp: {
EventType: 'MouseEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
// Selection Events
select: {
EventType: 'Event',
defaultInit: {
bubbles: true,
cancelable: false
}
},
// Touch Events
touchCancel: {
EventType: 'TouchEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
touchEnd: {
EventType: 'TouchEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
touchMove: {
EventType: 'TouchEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
touchStart: {
EventType: 'TouchEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
// UI Events
resize: {
EventType: 'UIEvent',
defaultInit: {
bubbles: false,
cancelable: false
}
},
scroll: {
EventType: 'UIEvent',
defaultInit: {
bubbles: false,
cancelable: false
}
},
// Wheel Events
wheel: {
EventType: 'WheelEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
// Media Events
abort: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
canPlay: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
canPlayThrough: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
durationChange: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
emptied: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
encrypted: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
ended: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
loadedData: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
loadedMetadata: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
loadStart: {
EventType: 'ProgressEvent',
defaultInit: {
bubbles: false,
cancelable: false
}
},
pause: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
play: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
playing: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
progress: {
EventType: 'ProgressEvent',
defaultInit: {
bubbles: false,
cancelable: false
}
},
rateChange: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
seeked: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
seeking: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
stalled: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
suspend: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
timeUpdate: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
volumeChange: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
waiting: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
// Events
load: {
// TODO: load events can be UIEvent or Event depending on what generated them
// This is where this abstraction breaks down.
// But the common targets are <img />, <script /> and window.
// Neither of these targets receive a UIEvent
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
error: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
// Animation Events
animationStart: {
EventType: 'AnimationEvent',
defaultInit: {
bubbles: true,
cancelable: false
}
},
animationEnd: {
EventType: 'AnimationEvent',
defaultInit: {
bubbles: true,
cancelable: false
}
},
animationIteration: {
EventType: 'AnimationEvent',
defaultInit: {
bubbles: true,
cancelable: false
}
},
// Transition Events
transitionCancel: {
EventType: 'TransitionEvent',
defaultInit: {
bubbles: true,
cancelable: false
}
},
transitionEnd: {
EventType: 'TransitionEvent',
defaultInit: {
bubbles: true,
cancelable: true
}
},
transitionRun: {
EventType: 'TransitionEvent',
defaultInit: {
bubbles: true,
cancelable: false
}
},
transitionStart: {
EventType: 'TransitionEvent',
defaultInit: {
bubbles: true,
cancelable: false
}
},
// pointer events
pointerOver: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
pointerEnter: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: false,
cancelable: false
}
},
pointerDown: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
pointerMove: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
pointerUp: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
pointerCancel: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
pointerOut: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: true,
cancelable: true,
composed: true
}
},
pointerLeave: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: false,
cancelable: false
}
},
gotPointerCapture: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
lostPointerCapture: {
EventType: 'PointerEvent',
defaultInit: {
bubbles: true,
cancelable: false,
composed: true
}
},
// history events
popState: {
EventType: 'PopStateEvent',
defaultInit: {
bubbles: true,
cancelable: false
}
},
// window events
offline: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
online: {
EventType: 'Event',
defaultInit: {
bubbles: false,
cancelable: false
}
},
pageHide: {
EventType: 'PageTransitionEvent',
defaultInit: {
bubbles: true,
cancelable: true
}
},
pageShow: {
EventType: 'PageTransitionEvent',
defaultInit: {
bubbles: true,
cancelable: true
}
}
};
const eventAliasMap = exports.eventAliasMap = {
doubleClick: 'dblClick'
};

View File

@@ -0,0 +1,140 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createEvent = createEvent;
exports.fireEvent = fireEvent;
var _config = require("./config");
var _helpers = require("./helpers");
var _eventMap = require("./event-map");
function fireEvent(element, event) {
return (0, _config.getConfig)().eventWrapper(() => {
if (!event) {
throw new Error(`Unable to fire an event - please provide an event object.`);
}
if (!element) {
throw new Error(`Unable to fire a "${event.type}" event - please provide a DOM element.`);
}
return element.dispatchEvent(event);
});
}
function createEvent(eventName, node, init, {
EventType = 'Event',
defaultInit = {}
} = {}) {
if (!node) {
throw new Error(`Unable to fire a "${eventName}" event - please provide a DOM element.`);
}
const eventInit = {
...defaultInit,
...init
};
const {
target: {
value,
files,
...targetProperties
} = {}
} = eventInit;
if (value !== undefined) {
setNativeValue(node, value);
}
if (files !== undefined) {
// input.files is a read-only property so this is not allowed:
// input.files = [file]
// so we have to use this workaround to set the property
Object.defineProperty(node, 'files', {
configurable: true,
enumerable: true,
writable: true,
value: files
});
}
Object.assign(node, targetProperties);
const window = (0, _helpers.getWindowFromNode)(node);
const EventConstructor = window[EventType] || window.Event;
let event;
/* istanbul ignore else */
if (typeof EventConstructor === 'function') {
event = new EventConstructor(eventName, eventInit);
} else {
// IE11 polyfill from https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill
event = window.document.createEvent(EventType);
const {
bubbles,
cancelable,
detail,
...otherInit
} = eventInit;
event.initEvent(eventName, bubbles, cancelable, detail);
Object.keys(otherInit).forEach(eventKey => {
event[eventKey] = otherInit[eventKey];
});
}
// DataTransfer is not supported in jsdom: https://github.com/jsdom/jsdom/issues/1568
const dataTransferProperties = ['dataTransfer', 'clipboardData'];
dataTransferProperties.forEach(dataTransferKey => {
const dataTransferValue = eventInit[dataTransferKey];
if (typeof dataTransferValue === 'object') {
/* istanbul ignore if */
if (typeof window.DataTransfer === 'function') {
Object.defineProperty(event, dataTransferKey, {
value: Object.getOwnPropertyNames(dataTransferValue).reduce((acc, propName) => {
Object.defineProperty(acc, propName, {
value: dataTransferValue[propName]
});
return acc;
}, new window.DataTransfer())
});
} else {
Object.defineProperty(event, dataTransferKey, {
value: dataTransferValue
});
}
}
});
return event;
}
Object.keys(_eventMap.eventMap).forEach(key => {
const {
EventType,
defaultInit
} = _eventMap.eventMap[key];
const eventName = key.toLowerCase();
createEvent[key] = (node, init) => createEvent(eventName, node, init, {
EventType,
defaultInit
});
fireEvent[key] = (node, init) => fireEvent(node, createEvent[key](node, init));
});
// function written after some investigation here:
// https://github.com/facebook/react/issues/10135#issuecomment-401496776
function setNativeValue(element, value) {
const {
set: valueSetter
} = Object.getOwnPropertyDescriptor(element, 'value') || {};
const prototype = Object.getPrototypeOf(element);
const {
set: prototypeValueSetter
} = Object.getOwnPropertyDescriptor(prototype, 'value') || {};
if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
/* istanbul ignore if */
// eslint-disable-next-line no-lonely-if -- Can't be ignored by istanbul otherwise
if (valueSetter) {
valueSetter.call(element, value);
} else {
throw new Error('The given element does not have a value setter');
}
}
}
Object.keys(_eventMap.eventAliasMap).forEach(aliasKey => {
const key = _eventMap.eventAliasMap[aliasKey];
fireEvent[aliasKey] = (...args) => fireEvent[key](...args);
});
/* eslint complexity:["error", 9] */

View File

@@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getNodeText = getNodeText;
var _helpers = require("./helpers");
function getNodeText(node) {
if (node.matches('input[type=submit], input[type=button], input[type=reset]')) {
return node.value;
}
return Array.from(node.childNodes).filter(child => child.nodeType === _helpers.TEXT_NODE && Boolean(child.textContent)).map(c => c.textContent).join('');
}

View File

@@ -0,0 +1,25 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getQueriesForElement = getQueriesForElement;
var defaultQueries = _interopRequireWildcard(require("./queries"));
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
/**
* @typedef {{[key: string]: Function}} FuncMap
*/
/**
* @param {HTMLElement} element container
* @param {FuncMap} queries object of functions
* @param {Object} initialValue for reducer
* @returns {FuncMap} returns object of functions bound to container
*/
function getQueriesForElement(element, queries = defaultQueries, initialValue = {}) {
return Object.keys(queries).reduce((helpers, key) => {
const fn = queries[key];
helpers[key] = fn.bind(null, element);
return helpers;
}, initialValue);
}

View File

@@ -0,0 +1,55 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getUserCodeFrame = getUserCodeFrame;
// We try to load node dependencies
let picocolors = null;
let readFileSync = null;
let codeFrameColumns = null;
try {
const nodeRequire = module && module.require;
readFileSync = nodeRequire.call(module, 'fs').readFileSync;
codeFrameColumns = nodeRequire.call(module, '@babel/code-frame').codeFrameColumns;
picocolors = nodeRequire.call(module, 'picocolors');
} catch {
// We're in a browser environment
}
// frame has the form "at myMethod (location/to/my/file.js:10:2)"
function getCodeFrame(frame) {
const locationStart = frame.indexOf('(') + 1;
const locationEnd = frame.indexOf(')');
const frameLocation = frame.slice(locationStart, locationEnd);
const frameLocationElements = frameLocation.split(':');
const [filename, line, column] = [frameLocationElements[0], parseInt(frameLocationElements[1], 10), parseInt(frameLocationElements[2], 10)];
let rawFileContents = '';
try {
rawFileContents = readFileSync(filename, 'utf-8');
} catch {
return '';
}
const codeFrame = codeFrameColumns(rawFileContents, {
start: {
line,
column
}
}, {
highlightCode: true,
linesBelow: 0
});
return `${picocolors.dim(frameLocation)}\n${codeFrame}\n`;
}
function getUserCodeFrame() {
// If we couldn't load dependencies, we can't generate the user trace
/* istanbul ignore next */
if (!readFileSync || !codeFrameColumns) {
return '';
}
const err = new Error();
const firstClientCodeFrame = err.stack.split('\n').slice(1) // Remove first line which has the form "Error: TypeError"
.find(frame => !frame.includes('node_modules/')); // Ignore frames from 3rd party libraries
return getCodeFrame(firstClientCodeFrame);
}

View File

@@ -0,0 +1,69 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TEXT_NODE = void 0;
exports.checkContainerType = checkContainerType;
exports.getDocument = getDocument;
exports.getWindowFromNode = getWindowFromNode;
exports.jestFakeTimersAreEnabled = jestFakeTimersAreEnabled;
// Constant node.nodeType for text nodes, see:
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#Node_type_constants
const TEXT_NODE = exports.TEXT_NODE = 3;
function jestFakeTimersAreEnabled() {
/* istanbul ignore else */
// eslint-disable-next-line
if (typeof jest !== 'undefined' && jest !== null) {
return (
// legacy timers
setTimeout._isMockFunction === true ||
// modern timers
// eslint-disable-next-line prefer-object-has-own -- not supported by our support matrix
Object.prototype.hasOwnProperty.call(setTimeout, 'clock')
);
}
// istanbul ignore next
return false;
}
function getDocument() {
/* istanbul ignore if */
if (typeof window === 'undefined') {
throw new Error('Could not find default container');
}
return window.document;
}
function getWindowFromNode(node) {
if (node.defaultView) {
// node is document
return node.defaultView;
} else if (node.ownerDocument && node.ownerDocument.defaultView) {
// node is a DOM node
return node.ownerDocument.defaultView;
} else if (node.window) {
// node is window
return node.window;
} else if (node.ownerDocument && node.ownerDocument.defaultView === null) {
throw new Error(`It looks like the window object is not available for the provided node.`);
} else if (node.then instanceof Function) {
throw new Error(`It looks like you passed a Promise object instead of a DOM node. Did you do something like \`fireEvent.click(screen.findBy...\` when you meant to use a \`getBy\` query \`fireEvent.click(screen.getBy...\`, or await the findBy query \`fireEvent.click(await screen.findBy...\`?`);
} else if (Array.isArray(node)) {
throw new Error(`It looks like you passed an Array instead of a DOM node. Did you do something like \`fireEvent.click(screen.getAllBy...\` when you meant to use a \`getBy\` query \`fireEvent.click(screen.getBy...\`?`);
} else if (typeof node.debug === 'function' && typeof node.logTestingPlaygroundURL === 'function') {
throw new Error(`It looks like you passed a \`screen\` object. Did you do something like \`fireEvent.click(screen, ...\` when you meant to use a query, e.g. \`fireEvent.click(screen.getBy..., \`?`);
} else {
// The user passed something unusual to a calling function
throw new Error(`The given node is not an Element, the node type is: ${typeof node}.`);
}
}
function checkContainerType(container) {
if (!container || !(typeof container.querySelector === 'function') || !(typeof container.querySelectorAll === 'function')) {
throw new TypeError(`Expected container to be an Element, a Document or a DocumentFragment but got ${getTypeName(container)}.`);
}
function getTypeName(object) {
if (typeof object === 'object') {
return object === null ? 'null' : object.constructor.name;
}
return typeof object;
}
}

View File

@@ -0,0 +1,185 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
within: true,
queries: true,
queryHelpers: true,
getDefaultNormalizer: true,
getRoles: true,
logRoles: true,
isInaccessible: true,
configure: true,
getConfig: true
};
Object.defineProperty(exports, "configure", {
enumerable: true,
get: function () {
return _config.configure;
}
});
Object.defineProperty(exports, "getConfig", {
enumerable: true,
get: function () {
return _config.getConfig;
}
});
Object.defineProperty(exports, "getDefaultNormalizer", {
enumerable: true,
get: function () {
return _matches.getDefaultNormalizer;
}
});
Object.defineProperty(exports, "getRoles", {
enumerable: true,
get: function () {
return _roleHelpers.getRoles;
}
});
Object.defineProperty(exports, "isInaccessible", {
enumerable: true,
get: function () {
return _roleHelpers.isInaccessible;
}
});
Object.defineProperty(exports, "logRoles", {
enumerable: true,
get: function () {
return _roleHelpers.logRoles;
}
});
exports.queryHelpers = exports.queries = void 0;
Object.defineProperty(exports, "within", {
enumerable: true,
get: function () {
return _getQueriesForElement.getQueriesForElement;
}
});
var _getQueriesForElement = require("./get-queries-for-element");
Object.keys(_getQueriesForElement).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _getQueriesForElement[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _getQueriesForElement[key];
}
});
});
var queries = _interopRequireWildcard(require("./queries"));
exports.queries = queries;
Object.keys(queries).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === queries[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return queries[key];
}
});
});
var queryHelpers = _interopRequireWildcard(require("./query-helpers"));
exports.queryHelpers = queryHelpers;
Object.keys(queryHelpers).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === queryHelpers[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return queryHelpers[key];
}
});
});
var _waitFor = require("./wait-for");
Object.keys(_waitFor).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _waitFor[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _waitFor[key];
}
});
});
var _waitForElementToBeRemoved = require("./wait-for-element-to-be-removed");
Object.keys(_waitForElementToBeRemoved).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _waitForElementToBeRemoved[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _waitForElementToBeRemoved[key];
}
});
});
var _matches = require("./matches");
var _getNodeText = require("./get-node-text");
Object.keys(_getNodeText).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _getNodeText[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _getNodeText[key];
}
});
});
var _events = require("./events");
Object.keys(_events).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _events[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _events[key];
}
});
});
var _screen = require("./screen");
Object.keys(_screen).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _screen[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _screen[key];
}
});
});
var _roleHelpers = require("./role-helpers");
var _prettyDom = require("./pretty-dom");
Object.keys(_prettyDom).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _prettyDom[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _prettyDom[key];
}
});
});
var _config = require("./config");
var _suggestions = require("./suggestions");
Object.keys(_suggestions).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _suggestions[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _suggestions[key];
}
});
});
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }

View File

@@ -0,0 +1,64 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getLabelContent = getLabelContent;
exports.getLabels = getLabels;
exports.getRealLabels = getRealLabels;
var _helpers = require("./helpers");
const labelledNodeNames = ['button', 'meter', 'output', 'progress', 'select', 'textarea', 'input'];
function getTextContent(node) {
if (labelledNodeNames.includes(node.nodeName.toLowerCase())) {
return '';
}
if (node.nodeType === _helpers.TEXT_NODE) return node.textContent;
return Array.from(node.childNodes).map(childNode => getTextContent(childNode)).join('');
}
function getLabelContent(element) {
let textContent;
if (element.tagName.toLowerCase() === 'label') {
textContent = getTextContent(element);
} else {
textContent = element.value || element.textContent;
}
return textContent;
}
// Based on https://github.com/eps1lon/dom-accessibility-api/pull/352
function getRealLabels(element) {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- types are not aware of older browsers that don't implement `labels`
if (element.labels !== undefined) {
return element.labels ?? [];
}
if (!isLabelable(element)) return [];
const labels = element.ownerDocument.querySelectorAll('label');
return Array.from(labels).filter(label => label.control === element);
}
function isLabelable(element) {
return /BUTTON|METER|OUTPUT|PROGRESS|SELECT|TEXTAREA/.test(element.tagName) || element.tagName === 'INPUT' && element.getAttribute('type') !== 'hidden';
}
function getLabels(container, element, {
selector = '*'
} = {}) {
const ariaLabelledBy = element.getAttribute('aria-labelledby');
const labelsId = ariaLabelledBy ? ariaLabelledBy.split(' ') : [];
return labelsId.length ? labelsId.map(labelId => {
const labellingElement = container.querySelector(`[id="${labelId}"]`);
return labellingElement ? {
content: getLabelContent(labellingElement),
formControl: null
} : {
content: '',
formControl: null
};
}) : Array.from(getRealLabels(element)).map(label => {
const textToMatch = getLabelContent(label);
const formControlSelector = 'button, input, meter, output, progress, select, textarea';
const labelledFormControl = Array.from(label.querySelectorAll(formControlSelector)).filter(formControlElement => formControlElement.matches(selector))[0];
return {
content: textToMatch,
formControl: labelledFormControl
};
});
}

View File

@@ -0,0 +1,92 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.fuzzyMatches = fuzzyMatches;
exports.getDefaultNormalizer = getDefaultNormalizer;
exports.makeNormalizer = makeNormalizer;
exports.matches = matches;
function assertNotNullOrUndefined(matcher) {
if (matcher === null || matcher === undefined) {
throw new Error(
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions -- implicitly converting `T` to `string`
`It looks like ${matcher} was passed instead of a matcher. Did you do something like getByText(${matcher})?`);
}
}
function fuzzyMatches(textToMatch, node, matcher, normalizer) {
if (typeof textToMatch !== 'string') {
return false;
}
assertNotNullOrUndefined(matcher);
const normalizedText = normalizer(textToMatch);
if (typeof matcher === 'string' || typeof matcher === 'number') {
return normalizedText.toLowerCase().includes(matcher.toString().toLowerCase());
} else if (typeof matcher === 'function') {
return matcher(normalizedText, node);
} else {
return matchRegExp(matcher, normalizedText);
}
}
function matches(textToMatch, node, matcher, normalizer) {
if (typeof textToMatch !== 'string') {
return false;
}
assertNotNullOrUndefined(matcher);
const normalizedText = normalizer(textToMatch);
if (matcher instanceof Function) {
return matcher(normalizedText, node);
} else if (matcher instanceof RegExp) {
return matchRegExp(matcher, normalizedText);
} else {
return normalizedText === String(matcher);
}
}
function getDefaultNormalizer({
trim = true,
collapseWhitespace = true
} = {}) {
return text => {
let normalizedText = text;
normalizedText = trim ? normalizedText.trim() : normalizedText;
normalizedText = collapseWhitespace ? normalizedText.replace(/\s+/g, ' ') : normalizedText;
return normalizedText;
};
}
/**
* Constructs a normalizer to pass to functions in matches.js
* @param {boolean|undefined} trim The user-specified value for `trim`, without
* any defaulting having been applied
* @param {boolean|undefined} collapseWhitespace The user-specified value for
* `collapseWhitespace`, without any defaulting having been applied
* @param {Function|undefined} normalizer The user-specified normalizer
* @returns {Function} A normalizer
*/
function makeNormalizer({
trim,
collapseWhitespace,
normalizer
}) {
if (!normalizer) {
// No custom normalizer specified. Just use default.
return getDefaultNormalizer({
trim,
collapseWhitespace
});
}
if (typeof trim !== 'undefined' || typeof collapseWhitespace !== 'undefined') {
// They've also specified a value for trim or collapseWhitespace
throw new Error('trim and collapseWhitespace are not supported with a normalizer. ' + 'If you want to use the default trim and collapseWhitespace logic in your normalizer, ' + 'use "getDefaultNormalizer({trim, collapseWhitespace})" and compose that into your normalizer');
}
return normalizer;
}
function matchRegExp(matcher, text) {
const match = matcher.test(text);
if (matcher.global && matcher.lastIndex !== 0) {
console.warn(`To match all elements we had to reset the lastIndex of the RegExp because the global flag is enabled. We encourage to remove the global flag from the RegExp.`);
matcher.lastIndex = 0;
}
return match;
}

View File

@@ -0,0 +1,96 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.logDOM = void 0;
exports.prettyDOM = prettyDOM;
exports.prettyFormat = void 0;
var prettyFormat = _interopRequireWildcard(require("pretty-format"));
exports.prettyFormat = prettyFormat;
var _DOMElementFilter = _interopRequireDefault(require("./DOMElementFilter"));
var _getUserCodeFrame = require("./get-user-code-frame");
var _helpers = require("./helpers");
var _config = require("./config");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
const shouldHighlight = () => {
if (typeof process === 'undefined') {
// Don't colorize in non-node environments (e.g. Browsers)
return false;
}
let colors;
// Try to safely parse env COLORS: We will default behavior if any step fails.
try {
const colorsJSON = process.env?.COLORS;
if (colorsJSON) {
colors = JSON.parse(colorsJSON);
}
} catch {
// If this throws, process.env?.COLORS wasn't parsable. Since we only
// care about `true` or `false`, we can safely ignore the error.
}
if (typeof colors === 'boolean') {
// If `colors` is set explicitly (both `true` and `false`), use that value.
return colors;
} else {
// If `colors` is not set, colorize if we're in node.
return process.versions !== undefined && process.versions.node !== undefined;
}
};
const {
DOMCollection
} = prettyFormat.plugins;
// https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType#node_type_constants
const ELEMENT_NODE = 1;
const COMMENT_NODE = 8;
// https://github.com/facebook/jest/blob/615084195ae1ae61ddd56162c62bbdda17587569/packages/pretty-format/src/plugins/DOMElement.ts#L50
function filterCommentsAndDefaultIgnoreTagsTags(value) {
return value.nodeType !== COMMENT_NODE && (value.nodeType !== ELEMENT_NODE || !value.matches((0, _config.getConfig)().defaultIgnore));
}
function prettyDOM(dom, maxLength, options = {}) {
if (!dom) {
dom = (0, _helpers.getDocument)().body;
}
if (typeof maxLength !== 'number') {
maxLength = typeof process !== 'undefined' && typeof process.env !== 'undefined' && process.env.DEBUG_PRINT_LIMIT || 7000;
}
if (maxLength === 0) {
return '';
}
if (dom.documentElement) {
dom = dom.documentElement;
}
let domTypeName = typeof dom;
if (domTypeName === 'object') {
domTypeName = dom.constructor.name;
} else {
// To don't fall with `in` operator
dom = {};
}
if (!('outerHTML' in dom)) {
throw new TypeError(`Expected an element or document but got ${domTypeName}`);
}
const {
filterNode = filterCommentsAndDefaultIgnoreTagsTags,
...prettyFormatOptions
} = options;
const debugContent = prettyFormat.format(dom, {
plugins: [(0, _DOMElementFilter.default)(filterNode), DOMCollection],
printFunctionName: false,
highlight: shouldHighlight(),
...prettyFormatOptions
});
return maxLength !== undefined && dom.outerHTML.length > maxLength ? `${debugContent.slice(0, maxLength)}...` : debugContent;
}
const logDOM = (...args) => {
const userCodeFrame = (0, _getUserCodeFrame.getUserCodeFrame)();
if (userCodeFrame) {
console.log(`${prettyDOM(...args)}\n\n${userCodeFrame}`);
} else {
console.log(prettyDOM(...args));
}
};
exports.logDOM = logDOM;

View File

@@ -0,0 +1,49 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _matches = require("../matches");
Object.keys(_matches).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _matches[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _matches[key];
}
});
});
var _getNodeText = require("../get-node-text");
Object.keys(_getNodeText).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _getNodeText[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _getNodeText[key];
}
});
});
var _queryHelpers = require("../query-helpers");
Object.keys(_queryHelpers).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _queryHelpers[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _queryHelpers[key];
}
});
});
var _config = require("../config");
Object.keys(_config).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _config[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _config[key];
}
});
});

View File

@@ -0,0 +1,24 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.queryByAltText = exports.queryAllByAltText = exports.getByAltText = exports.getAllByAltText = exports.findByAltText = exports.findAllByAltText = void 0;
var _queryHelpers = require("../query-helpers");
var _helpers = require("../helpers");
var _allUtils = require("./all-utils");
// Valid tags are img, input, area and custom elements
const VALID_TAG_REGEXP = /^(img|input|area|.+-.+)$/i;
const queryAllByAltText = (container, alt, options = {}) => {
(0, _helpers.checkContainerType)(container);
return (0, _queryHelpers.queryAllByAttribute)('alt', container, alt, options).filter(node => VALID_TAG_REGEXP.test(node.tagName));
};
const getMultipleError = (c, alt) => `Found multiple elements with the alt text: ${alt}`;
const getMissingError = (c, alt) => `Unable to find an element with the alt text: ${alt}`;
const queryAllByAltTextWithSuggestions = exports.queryAllByAltText = (0, _queryHelpers.wrapAllByQueryWithSuggestion)(queryAllByAltText, queryAllByAltText.name, 'queryAll');
const [queryByAltText, getAllByAltText, getByAltText, findAllByAltText, findByAltText] = (0, _allUtils.buildQueries)(queryAllByAltText, getMultipleError, getMissingError);
exports.findByAltText = findByAltText;
exports.findAllByAltText = findAllByAltText;
exports.getByAltText = getByAltText;
exports.getAllByAltText = getAllByAltText;
exports.queryByAltText = queryByAltText;

View File

@@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.queryByDisplayValue = exports.queryAllByDisplayValue = exports.getByDisplayValue = exports.getAllByDisplayValue = exports.findByDisplayValue = exports.findAllByDisplayValue = void 0;
var _queryHelpers = require("../query-helpers");
var _helpers = require("../helpers");
var _allUtils = require("./all-utils");
const queryAllByDisplayValue = (container, value, {
exact = true,
collapseWhitespace,
trim,
normalizer
} = {}) => {
(0, _helpers.checkContainerType)(container);
const matcher = exact ? _allUtils.matches : _allUtils.fuzzyMatches;
const matchNormalizer = (0, _allUtils.makeNormalizer)({
collapseWhitespace,
trim,
normalizer
});
return Array.from(container.querySelectorAll(`input,textarea,select`)).filter(node => {
if (node.tagName === 'SELECT') {
const selectedOptions = Array.from(node.options).filter(option => option.selected);
return selectedOptions.some(optionNode => matcher((0, _allUtils.getNodeText)(optionNode), optionNode, value, matchNormalizer));
} else {
return matcher(node.value, node, value, matchNormalizer);
}
});
};
const getMultipleError = (c, value) => `Found multiple elements with the display value: ${value}.`;
const getMissingError = (c, value) => `Unable to find an element with the display value: ${value}.`;
const queryAllByDisplayValueWithSuggestions = exports.queryAllByDisplayValue = (0, _queryHelpers.wrapAllByQueryWithSuggestion)(queryAllByDisplayValue, queryAllByDisplayValue.name, 'queryAll');
const [queryByDisplayValue, getAllByDisplayValue, getByDisplayValue, findAllByDisplayValue, findByDisplayValue] = (0, _allUtils.buildQueries)(queryAllByDisplayValue, getMultipleError, getMissingError);
exports.findByDisplayValue = findByDisplayValue;
exports.findAllByDisplayValue = findAllByDisplayValue;
exports.getByDisplayValue = getByDisplayValue;
exports.getAllByDisplayValue = getAllByDisplayValue;
exports.queryByDisplayValue = queryByDisplayValue;

View File

@@ -0,0 +1,93 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
var _labelText = require("./label-text");
Object.keys(_labelText).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _labelText[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _labelText[key];
}
});
});
var _placeholderText = require("./placeholder-text");
Object.keys(_placeholderText).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _placeholderText[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _placeholderText[key];
}
});
});
var _text = require("./text");
Object.keys(_text).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _text[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _text[key];
}
});
});
var _displayValue = require("./display-value");
Object.keys(_displayValue).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _displayValue[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _displayValue[key];
}
});
});
var _altText = require("./alt-text");
Object.keys(_altText).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _altText[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _altText[key];
}
});
});
var _title = require("./title");
Object.keys(_title).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _title[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _title[key];
}
});
});
var _role = require("./role");
Object.keys(_role).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _role[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _role[key];
}
});
});
var _testId = require("./test-id");
Object.keys(_testId).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _testId[key]) return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function () {
return _testId[key];
}
});
});

View File

@@ -0,0 +1,133 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.queryByLabelText = exports.queryAllByLabelText = exports.getByLabelText = exports.getAllByLabelText = exports.findByLabelText = exports.findAllByLabelText = void 0;
var _config = require("../config");
var _helpers = require("../helpers");
var _labelHelpers = require("../label-helpers");
var _allUtils = require("./all-utils");
function queryAllLabels(container) {
return Array.from(container.querySelectorAll('label,input')).map(node => {
return {
node,
textToMatch: (0, _labelHelpers.getLabelContent)(node)
};
}).filter(({
textToMatch
}) => textToMatch !== null);
}
const queryAllLabelsByText = (container, text, {
exact = true,
trim,
collapseWhitespace,
normalizer
} = {}) => {
const matcher = exact ? _allUtils.matches : _allUtils.fuzzyMatches;
const matchNormalizer = (0, _allUtils.makeNormalizer)({
collapseWhitespace,
trim,
normalizer
});
const textToMatchByLabels = queryAllLabels(container);
return textToMatchByLabels.filter(({
node,
textToMatch
}) => matcher(textToMatch, node, text, matchNormalizer)).map(({
node
}) => node);
};
const queryAllByLabelText = (container, text, {
selector = '*',
exact = true,
collapseWhitespace,
trim,
normalizer
} = {}) => {
(0, _helpers.checkContainerType)(container);
const matcher = exact ? _allUtils.matches : _allUtils.fuzzyMatches;
const matchNormalizer = (0, _allUtils.makeNormalizer)({
collapseWhitespace,
trim,
normalizer
});
const matchingLabelledElements = Array.from(container.querySelectorAll('*')).filter(element => {
return (0, _labelHelpers.getRealLabels)(element).length || element.hasAttribute('aria-labelledby');
}).reduce((labelledElements, labelledElement) => {
const labelList = (0, _labelHelpers.getLabels)(container, labelledElement, {
selector
});
labelList.filter(label => Boolean(label.formControl)).forEach(label => {
if (matcher(label.content, label.formControl, text, matchNormalizer) && label.formControl) {
labelledElements.push(label.formControl);
}
});
const labelsValue = labelList.filter(label => Boolean(label.content)).map(label => label.content);
if (matcher(labelsValue.join(' '), labelledElement, text, matchNormalizer)) {
labelledElements.push(labelledElement);
}
if (labelsValue.length > 1) {
labelsValue.forEach((labelValue, index) => {
if (matcher(labelValue, labelledElement, text, matchNormalizer)) {
labelledElements.push(labelledElement);
}
const labelsFiltered = [...labelsValue];
labelsFiltered.splice(index, 1);
if (labelsFiltered.length > 1) {
if (matcher(labelsFiltered.join(' '), labelledElement, text, matchNormalizer)) {
labelledElements.push(labelledElement);
}
}
});
}
return labelledElements;
}, []).concat((0, _allUtils.queryAllByAttribute)('aria-label', container, text, {
exact,
normalizer: matchNormalizer
}));
return Array.from(new Set(matchingLabelledElements)).filter(element => element.matches(selector));
};
// the getAll* query would normally look like this:
// const getAllByLabelText = makeGetAllQuery(
// queryAllByLabelText,
// (c, text) => `Unable to find a label with the text of: ${text}`,
// )
// however, we can give a more helpful error message than the generic one,
// so we're writing this one out by hand.
const getAllByLabelText = (container, text, ...rest) => {
const els = queryAllByLabelText(container, text, ...rest);
if (!els.length) {
const labels = queryAllLabelsByText(container, text, ...rest);
if (labels.length) {
const tagNames = labels.map(label => getTagNameOfElementAssociatedWithLabelViaFor(container, label)).filter(tagName => !!tagName);
if (tagNames.length) {
throw (0, _config.getConfig)().getElementError(tagNames.map(tagName => `Found a label with the text of: ${text}, however the element associated with this label (<${tagName} />) is non-labellable [https://html.spec.whatwg.org/multipage/forms.html#category-label]. If you really need to label a <${tagName} />, you can use aria-label or aria-labelledby instead.`).join('\n\n'), container);
} else {
throw (0, _config.getConfig)().getElementError(`Found a label with the text of: ${text}, however no form control was found associated to that label. Make sure you're using the "for" attribute or "aria-labelledby" attribute correctly.`, container);
}
} else {
throw (0, _config.getConfig)().getElementError(`Unable to find a label with the text of: ${text}`, container);
}
}
return els;
};
function getTagNameOfElementAssociatedWithLabelViaFor(container, label) {
const htmlFor = label.getAttribute('for');
if (!htmlFor) {
return null;
}
const element = container.querySelector(`[id="${htmlFor}"]`);
return element ? element.tagName.toLowerCase() : null;
}
// the reason mentioned above is the same reason we're not using buildQueries
const getMultipleError = (c, text) => `Found multiple elements with the text of: ${text}`;
const queryByLabelText = exports.queryByLabelText = (0, _allUtils.wrapSingleQueryWithSuggestion)((0, _allUtils.makeSingleQuery)(queryAllByLabelText, getMultipleError), queryAllByLabelText.name, 'query');
const getByLabelText = (0, _allUtils.makeSingleQuery)(getAllByLabelText, getMultipleError);
const findAllByLabelText = exports.findAllByLabelText = (0, _allUtils.makeFindQuery)((0, _allUtils.wrapAllByQueryWithSuggestion)(getAllByLabelText, getAllByLabelText.name, 'findAll'));
const findByLabelText = exports.findByLabelText = (0, _allUtils.makeFindQuery)((0, _allUtils.wrapSingleQueryWithSuggestion)(getByLabelText, getAllByLabelText.name, 'find'));
const getAllByLabelTextWithSuggestions = exports.getAllByLabelText = (0, _allUtils.wrapAllByQueryWithSuggestion)(getAllByLabelText, getAllByLabelText.name, 'getAll');
const getByLabelTextWithSuggestions = exports.getByLabelText = (0, _allUtils.wrapSingleQueryWithSuggestion)(getByLabelText, getAllByLabelText.name, 'get');
const queryAllByLabelTextWithSuggestions = exports.queryAllByLabelText = (0, _allUtils.wrapAllByQueryWithSuggestion)(queryAllByLabelText, queryAllByLabelText.name, 'queryAll');

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.queryByPlaceholderText = exports.queryAllByPlaceholderText = exports.getByPlaceholderText = exports.getAllByPlaceholderText = exports.findByPlaceholderText = exports.findAllByPlaceholderText = void 0;
var _queryHelpers = require("../query-helpers");
var _helpers = require("../helpers");
var _allUtils = require("./all-utils");
const queryAllByPlaceholderText = (...args) => {
(0, _helpers.checkContainerType)(args[0]);
return (0, _allUtils.queryAllByAttribute)('placeholder', ...args);
};
const getMultipleError = (c, text) => `Found multiple elements with the placeholder text of: ${text}`;
const getMissingError = (c, text) => `Unable to find an element with the placeholder text of: ${text}`;
const queryAllByPlaceholderTextWithSuggestions = exports.queryAllByPlaceholderText = (0, _queryHelpers.wrapAllByQueryWithSuggestion)(queryAllByPlaceholderText, queryAllByPlaceholderText.name, 'queryAll');
const [queryByPlaceholderText, getAllByPlaceholderText, getByPlaceholderText, findAllByPlaceholderText, findByPlaceholderText] = (0, _allUtils.buildQueries)(queryAllByPlaceholderText, getMultipleError, getMissingError);
exports.findByPlaceholderText = findByPlaceholderText;
exports.findAllByPlaceholderText = findAllByPlaceholderText;
exports.getByPlaceholderText = getByPlaceholderText;
exports.getAllByPlaceholderText = getAllByPlaceholderText;
exports.queryByPlaceholderText = queryByPlaceholderText;

View File

@@ -0,0 +1,274 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.queryByRole = exports.queryAllByRole = exports.getByRole = exports.getAllByRole = exports.findByRole = exports.findAllByRole = void 0;
var _domAccessibilityApi = require("dom-accessibility-api");
var _ariaQuery = require("aria-query");
var _roleHelpers = require("../role-helpers");
var _queryHelpers = require("../query-helpers");
var _helpers = require("../helpers");
var _allUtils = require("./all-utils");
/* eslint-disable complexity */
const queryAllByRole = (container, role, {
hidden = (0, _allUtils.getConfig)().defaultHidden,
name,
description,
queryFallbacks = false,
selected,
busy,
checked,
pressed,
current,
level,
expanded,
value: {
now: valueNow,
min: valueMin,
max: valueMax,
text: valueText
} = {}
} = {}) => {
(0, _helpers.checkContainerType)(container);
if (selected !== undefined) {
// guard against unknown roles
if (_ariaQuery.roles.get(role)?.props['aria-selected'] === undefined) {
throw new Error(`"aria-selected" is not supported on role "${role}".`);
}
}
if (busy !== undefined) {
// guard against unknown roles
if (_ariaQuery.roles.get(role)?.props['aria-busy'] === undefined) {
throw new Error(`"aria-busy" is not supported on role "${role}".`);
}
}
if (checked !== undefined) {
// guard against unknown roles
if (_ariaQuery.roles.get(role)?.props['aria-checked'] === undefined) {
throw new Error(`"aria-checked" is not supported on role "${role}".`);
}
}
if (pressed !== undefined) {
// guard against unknown roles
if (_ariaQuery.roles.get(role)?.props['aria-pressed'] === undefined) {
throw new Error(`"aria-pressed" is not supported on role "${role}".`);
}
}
if (current !== undefined) {
/* istanbul ignore next */
// guard against unknown roles
// All currently released ARIA versions support `aria-current` on all roles.
// Leaving this for symmetry and forward compatibility
if (_ariaQuery.roles.get(role)?.props['aria-current'] === undefined) {
throw new Error(`"aria-current" is not supported on role "${role}".`);
}
}
if (level !== undefined) {
// guard against using `level` option with any role other than `heading`
if (role !== 'heading') {
throw new Error(`Role "${role}" cannot have "level" property.`);
}
}
if (valueNow !== undefined) {
// guard against unknown roles
if (_ariaQuery.roles.get(role)?.props['aria-valuenow'] === undefined) {
throw new Error(`"aria-valuenow" is not supported on role "${role}".`);
}
}
if (valueMax !== undefined) {
// guard against unknown roles
if (_ariaQuery.roles.get(role)?.props['aria-valuemax'] === undefined) {
throw new Error(`"aria-valuemax" is not supported on role "${role}".`);
}
}
if (valueMin !== undefined) {
// guard against unknown roles
if (_ariaQuery.roles.get(role)?.props['aria-valuemin'] === undefined) {
throw new Error(`"aria-valuemin" is not supported on role "${role}".`);
}
}
if (valueText !== undefined) {
// guard against unknown roles
if (_ariaQuery.roles.get(role)?.props['aria-valuetext'] === undefined) {
throw new Error(`"aria-valuetext" is not supported on role "${role}".`);
}
}
if (expanded !== undefined) {
// guard against unknown roles
if (_ariaQuery.roles.get(role)?.props['aria-expanded'] === undefined) {
throw new Error(`"aria-expanded" is not supported on role "${role}".`);
}
}
const subtreeIsInaccessibleCache = new WeakMap();
function cachedIsSubtreeInaccessible(element) {
if (!subtreeIsInaccessibleCache.has(element)) {
subtreeIsInaccessibleCache.set(element, (0, _roleHelpers.isSubtreeInaccessible)(element));
}
return subtreeIsInaccessibleCache.get(element);
}
return Array.from(container.querySelectorAll(
// Only query elements that can be matched by the following filters
makeRoleSelector(role))).filter(node => {
const isRoleSpecifiedExplicitly = node.hasAttribute('role');
if (isRoleSpecifiedExplicitly) {
const roleValue = node.getAttribute('role');
if (queryFallbacks) {
return roleValue.split(' ').filter(Boolean).some(roleAttributeToken => roleAttributeToken === role);
}
// other wise only send the first token to match
const [firstRoleAttributeToken] = roleValue.split(' ');
return firstRoleAttributeToken === role;
}
const implicitRoles = (0, _roleHelpers.getImplicitAriaRoles)(node);
return implicitRoles.some(implicitRole => {
return implicitRole === role;
});
}).filter(element => {
if (selected !== undefined) {
return selected === (0, _roleHelpers.computeAriaSelected)(element);
}
if (busy !== undefined) {
return busy === (0, _roleHelpers.computeAriaBusy)(element);
}
if (checked !== undefined) {
return checked === (0, _roleHelpers.computeAriaChecked)(element);
}
if (pressed !== undefined) {
return pressed === (0, _roleHelpers.computeAriaPressed)(element);
}
if (current !== undefined) {
return current === (0, _roleHelpers.computeAriaCurrent)(element);
}
if (expanded !== undefined) {
return expanded === (0, _roleHelpers.computeAriaExpanded)(element);
}
if (level !== undefined) {
return level === (0, _roleHelpers.computeHeadingLevel)(element);
}
if (valueNow !== undefined || valueMax !== undefined || valueMin !== undefined || valueText !== undefined) {
let valueMatches = true;
if (valueNow !== undefined) {
valueMatches &&= valueNow === (0, _roleHelpers.computeAriaValueNow)(element);
}
if (valueMax !== undefined) {
valueMatches &&= valueMax === (0, _roleHelpers.computeAriaValueMax)(element);
}
if (valueMin !== undefined) {
valueMatches &&= valueMin === (0, _roleHelpers.computeAriaValueMin)(element);
}
if (valueText !== undefined) {
valueMatches &&= (0, _allUtils.matches)((0, _roleHelpers.computeAriaValueText)(element) ?? null, element, valueText, text => text);
}
return valueMatches;
}
// don't care if aria attributes are unspecified
return true;
}).filter(element => {
if (name === undefined) {
// Don't care
return true;
}
return (0, _allUtils.matches)((0, _domAccessibilityApi.computeAccessibleName)(element, {
computedStyleSupportsPseudoElements: (0, _allUtils.getConfig)().computedStyleSupportsPseudoElements
}), element, name, text => text);
}).filter(element => {
if (description === undefined) {
// Don't care
return true;
}
return (0, _allUtils.matches)((0, _domAccessibilityApi.computeAccessibleDescription)(element, {
computedStyleSupportsPseudoElements: (0, _allUtils.getConfig)().computedStyleSupportsPseudoElements
}), element, description, text => text);
}).filter(element => {
return hidden === false ? (0, _roleHelpers.isInaccessible)(element, {
isSubtreeInaccessible: cachedIsSubtreeInaccessible
}) === false : true;
});
};
function makeRoleSelector(role) {
const explicitRoleSelector = `*[role~="${role}"]`;
const roleRelations = _ariaQuery.roleElements.get(role) ?? new Set();
const implicitRoleSelectors = new Set(Array.from(roleRelations).map(({
name
}) => name));
// Current transpilation config sometimes assumes `...` is always applied to arrays.
// `...` is equivalent to `Array.prototype.concat` for arrays.
// If you replace this code with `[explicitRoleSelector, ...implicitRoleSelectors]`, make sure every transpilation target retains the `...` in favor of `Array.prototype.concat`.
return [explicitRoleSelector].concat(Array.from(implicitRoleSelectors)).join(',');
}
const getNameHint = name => {
let nameHint = '';
if (name === undefined) {
nameHint = '';
} else if (typeof name === 'string') {
nameHint = ` and name "${name}"`;
} else {
nameHint = ` and name \`${name}\``;
}
return nameHint;
};
const getMultipleError = (c, role, {
name
} = {}) => {
return `Found multiple elements with the role "${role}"${getNameHint(name)}`;
};
const getMissingError = (container, role, {
hidden = (0, _allUtils.getConfig)().defaultHidden,
name,
description
} = {}) => {
if ((0, _allUtils.getConfig)()._disableExpensiveErrorDiagnostics) {
return `Unable to find role="${role}"${getNameHint(name)}`;
}
let roles = '';
Array.from(container.children).forEach(childElement => {
roles += (0, _roleHelpers.prettyRoles)(childElement, {
hidden,
includeDescription: description !== undefined
});
});
let roleMessage;
if (roles.length === 0) {
if (hidden === false) {
roleMessage = 'There are no accessible roles. But there might be some inaccessible roles. ' + 'If you wish to access them, then set the `hidden` option to `true`. ' + 'Learn more about this here: https://testing-library.com/docs/dom-testing-library/api-queries#byrole';
} else {
roleMessage = 'There are no available roles.';
}
} else {
roleMessage = `
Here are the ${hidden === false ? 'accessible' : 'available'} roles:
${roles.replace(/\n/g, '\n ').replace(/\n\s\s\n/g, '\n\n')}
`.trim();
}
let nameHint = '';
if (name === undefined) {
nameHint = '';
} else if (typeof name === 'string') {
nameHint = ` and name "${name}"`;
} else {
nameHint = ` and name \`${name}\``;
}
let descriptionHint = '';
if (description === undefined) {
descriptionHint = '';
} else if (typeof description === 'string') {
descriptionHint = ` and description "${description}"`;
} else {
descriptionHint = ` and description \`${description}\``;
}
return `
Unable to find an ${hidden === false ? 'accessible ' : ''}element with the role "${role}"${nameHint}${descriptionHint}
${roleMessage}`.trim();
};
const queryAllByRoleWithSuggestions = exports.queryAllByRole = (0, _queryHelpers.wrapAllByQueryWithSuggestion)(queryAllByRole, queryAllByRole.name, 'queryAll');
const [queryByRole, getAllByRole, getByRole, findAllByRole, findByRole] = (0, _allUtils.buildQueries)(queryAllByRole, getMultipleError, getMissingError);
exports.findByRole = findByRole;
exports.findAllByRole = findAllByRole;
exports.getByRole = getByRole;
exports.getAllByRole = getAllByRole;
exports.queryByRole = queryByRole;

View File

@@ -0,0 +1,23 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.queryByTestId = exports.queryAllByTestId = exports.getByTestId = exports.getAllByTestId = exports.findByTestId = exports.findAllByTestId = void 0;
var _helpers = require("../helpers");
var _queryHelpers = require("../query-helpers");
var _allUtils = require("./all-utils");
const getTestIdAttribute = () => (0, _allUtils.getConfig)().testIdAttribute;
const queryAllByTestId = (...args) => {
(0, _helpers.checkContainerType)(args[0]);
return (0, _allUtils.queryAllByAttribute)(getTestIdAttribute(), ...args);
};
const getMultipleError = (c, id) => `Found multiple elements by: [${getTestIdAttribute()}="${id}"]`;
const getMissingError = (c, id) => `Unable to find an element by: [${getTestIdAttribute()}="${id}"]`;
const queryAllByTestIdWithSuggestions = exports.queryAllByTestId = (0, _queryHelpers.wrapAllByQueryWithSuggestion)(queryAllByTestId, queryAllByTestId.name, 'queryAll');
const [queryByTestId, getAllByTestId, getByTestId, findAllByTestId, findByTestId] = (0, _allUtils.buildQueries)(queryAllByTestId, getMultipleError, getMissingError);
exports.findByTestId = findByTestId;
exports.findAllByTestId = findAllByTestId;
exports.getByTestId = getByTestId;
exports.getAllByTestId = getAllByTestId;
exports.queryByTestId = queryByTestId;

View File

@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.queryByText = exports.queryAllByText = exports.getByText = exports.getAllByText = exports.findByText = exports.findAllByText = void 0;
var _queryHelpers = require("../query-helpers");
var _helpers = require("../helpers");
var _allUtils = require("./all-utils");
const queryAllByText = (container, text, {
selector = '*',
exact = true,
collapseWhitespace,
trim,
ignore = (0, _allUtils.getConfig)().defaultIgnore,
normalizer
} = {}) => {
(0, _helpers.checkContainerType)(container);
const matcher = exact ? _allUtils.matches : _allUtils.fuzzyMatches;
const matchNormalizer = (0, _allUtils.makeNormalizer)({
collapseWhitespace,
trim,
normalizer
});
let baseArray = [];
if (typeof container.matches === 'function' && container.matches(selector)) {
baseArray = [container];
}
return [...baseArray, ...Array.from(container.querySelectorAll(selector))]
// TODO: `matches` according lib.dom.d.ts can get only `string` but according our code it can handle also boolean :)
.filter(node => !ignore || !node.matches(ignore)).filter(node => matcher((0, _allUtils.getNodeText)(node), node, text, matchNormalizer));
};
const getMultipleError = (c, text) => `Found multiple elements with the text: ${text}`;
const getMissingError = (c, text, options = {}) => {
const {
collapseWhitespace,
trim,
normalizer,
selector
} = options;
const matchNormalizer = (0, _allUtils.makeNormalizer)({
collapseWhitespace,
trim,
normalizer
});
const normalizedText = matchNormalizer(text.toString());
const isNormalizedDifferent = normalizedText !== text.toString();
const isCustomSelector = (selector ?? '*') !== '*';
return `Unable to find an element with the text: ${isNormalizedDifferent ? `${normalizedText} (normalized from '${text}')` : text}${isCustomSelector ? `, which matches selector '${selector}'` : ''}. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.`;
};
const queryAllByTextWithSuggestions = exports.queryAllByText = (0, _queryHelpers.wrapAllByQueryWithSuggestion)(queryAllByText, queryAllByText.name, 'queryAll');
const [queryByText, getAllByText, getByText, findAllByText, findByText] = (0, _allUtils.buildQueries)(queryAllByText, getMultipleError, getMissingError);
exports.findByText = findByText;
exports.findAllByText = findAllByText;
exports.getByText = getByText;
exports.getAllByText = getAllByText;
exports.queryByText = queryByText;

View File

@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.queryByTitle = exports.queryAllByTitle = exports.getByTitle = exports.getAllByTitle = exports.findByTitle = exports.findAllByTitle = void 0;
var _queryHelpers = require("../query-helpers");
var _helpers = require("../helpers");
var _allUtils = require("./all-utils");
const isSvgTitle = node => node.tagName.toLowerCase() === 'title' && node.parentElement?.tagName.toLowerCase() === 'svg';
const queryAllByTitle = (container, text, {
exact = true,
collapseWhitespace,
trim,
normalizer
} = {}) => {
(0, _helpers.checkContainerType)(container);
const matcher = exact ? _allUtils.matches : _allUtils.fuzzyMatches;
const matchNormalizer = (0, _allUtils.makeNormalizer)({
collapseWhitespace,
trim,
normalizer
});
return Array.from(container.querySelectorAll('[title], svg > title')).filter(node => matcher(node.getAttribute('title'), node, text, matchNormalizer) || isSvgTitle(node) && matcher((0, _allUtils.getNodeText)(node), node, text, matchNormalizer));
};
const getMultipleError = (c, title) => `Found multiple elements with the title: ${title}.`;
const getMissingError = (c, title) => `Unable to find an element with the title: ${title}.`;
const queryAllByTitleWithSuggestions = exports.queryAllByTitle = (0, _queryHelpers.wrapAllByQueryWithSuggestion)(queryAllByTitle, queryAllByTitle.name, 'queryAll');
const [queryByTitle, getAllByTitle, getByTitle, findAllByTitle, findByTitle] = (0, _allUtils.buildQueries)(queryAllByTitle, getMultipleError, getMissingError);
exports.findByTitle = findByTitle;
exports.findAllByTitle = findAllByTitle;
exports.getByTitle = getByTitle;
exports.getAllByTitle = getAllByTitle;
exports.queryByTitle = queryByTitle;

View File

@@ -0,0 +1,141 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.buildQueries = buildQueries;
exports.getElementError = getElementError;
exports.getMultipleElementsFoundError = getMultipleElementsFoundError;
exports.makeFindQuery = makeFindQuery;
exports.makeGetAllQuery = makeGetAllQuery;
exports.makeSingleQuery = makeSingleQuery;
exports.queryAllByAttribute = queryAllByAttribute;
exports.queryByAttribute = queryByAttribute;
exports.wrapSingleQueryWithSuggestion = exports.wrapAllByQueryWithSuggestion = void 0;
var _suggestions = require("./suggestions");
var _matches = require("./matches");
var _waitFor = require("./wait-for");
var _config = require("./config");
function getElementError(message, container) {
return (0, _config.getConfig)().getElementError(message, container);
}
function getMultipleElementsFoundError(message, container) {
return getElementError(`${message}\n\n(If this is intentional, then use the \`*AllBy*\` variant of the query (like \`queryAllByText\`, \`getAllByText\`, or \`findAllByText\`)).`, container);
}
function queryAllByAttribute(attribute, container, text, {
exact = true,
collapseWhitespace,
trim,
normalizer
} = {}) {
const matcher = exact ? _matches.matches : _matches.fuzzyMatches;
const matchNormalizer = (0, _matches.makeNormalizer)({
collapseWhitespace,
trim,
normalizer
});
return Array.from(container.querySelectorAll(`[${attribute}]`)).filter(node => matcher(node.getAttribute(attribute), node, text, matchNormalizer));
}
function queryByAttribute(attribute, container, text, options) {
const els = queryAllByAttribute(attribute, container, text, options);
if (els.length > 1) {
throw getMultipleElementsFoundError(`Found multiple elements by [${attribute}=${text}]`, container);
}
return els[0] || null;
}
// this accepts a query function and returns a function which throws an error
// if more than one elements is returned, otherwise it returns the first
// element or null
function makeSingleQuery(allQuery, getMultipleError) {
return (container, ...args) => {
const els = allQuery(container, ...args);
if (els.length > 1) {
const elementStrings = els.map(element => getElementError(null, element).message).join('\n\n');
throw getMultipleElementsFoundError(`${getMultipleError(container, ...args)}
Here are the matching elements:
${elementStrings}`, container);
}
return els[0] || null;
};
}
function getSuggestionError(suggestion, container) {
return (0, _config.getConfig)().getElementError(`A better query is available, try this:
${suggestion.toString()}
`, container);
}
// this accepts a query function and returns a function which throws an error
// if an empty list of elements is returned
function makeGetAllQuery(allQuery, getMissingError) {
return (container, ...args) => {
const els = allQuery(container, ...args);
if (!els.length) {
throw (0, _config.getConfig)().getElementError(getMissingError(container, ...args), container);
}
return els;
};
}
// this accepts a getter query function and returns a function which calls
// waitFor and passing a function which invokes the getter.
function makeFindQuery(getter) {
return (container, text, options, waitForOptions) => {
return (0, _waitFor.waitFor)(() => {
return getter(container, text, options);
}, {
container,
...waitForOptions
});
};
}
const wrapSingleQueryWithSuggestion = (query, queryAllByName, variant) => (container, ...args) => {
const element = query(container, ...args);
const [{
suggest = (0, _config.getConfig)().throwSuggestions
} = {}] = args.slice(-1);
if (element && suggest) {
const suggestion = (0, _suggestions.getSuggestedQuery)(element, variant);
if (suggestion && !queryAllByName.endsWith(suggestion.queryName)) {
throw getSuggestionError(suggestion.toString(), container);
}
}
return element;
};
exports.wrapSingleQueryWithSuggestion = wrapSingleQueryWithSuggestion;
const wrapAllByQueryWithSuggestion = (query, queryAllByName, variant) => (container, ...args) => {
const els = query(container, ...args);
const [{
suggest = (0, _config.getConfig)().throwSuggestions
} = {}] = args.slice(-1);
if (els.length && suggest) {
// get a unique list of all suggestion messages. We are only going to make a suggestion if
// all the suggestions are the same
const uniqueSuggestionMessages = [...new Set(els.map(element => (0, _suggestions.getSuggestedQuery)(element, variant)?.toString()))];
if (
// only want to suggest if all the els have the same suggestion.
uniqueSuggestionMessages.length === 1 && !queryAllByName.endsWith(
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- TODO: Can this be null at runtime?
(0, _suggestions.getSuggestedQuery)(els[0], variant).queryName)) {
throw getSuggestionError(uniqueSuggestionMessages[0], container);
}
}
return els;
};
// TODO: This deviates from the published declarations
// However, the implementation always required a dyadic (after `container`) not variadic `queryAllBy` considering the implementation of `makeFindQuery`
// This is at least statically true and can be verified by accepting `QueryMethod<Arguments, HTMLElement[]>`
exports.wrapAllByQueryWithSuggestion = wrapAllByQueryWithSuggestion;
function buildQueries(queryAllBy, getMultipleError, getMissingError) {
const queryBy = wrapSingleQueryWithSuggestion(makeSingleQuery(queryAllBy, getMultipleError), queryAllBy.name, 'query');
const getAllBy = makeGetAllQuery(queryAllBy, getMissingError);
const getBy = makeSingleQuery(getAllBy, getMultipleError);
const getByWithSuggestions = wrapSingleQueryWithSuggestion(getBy, queryAllBy.name, 'get');
const getAllWithSuggestions = wrapAllByQueryWithSuggestion(getAllBy, queryAllBy.name.replace('query', 'get'), 'getAll');
const findAllBy = makeFindQuery(wrapAllByQueryWithSuggestion(getAllBy, queryAllBy.name, 'findAll'));
const findBy = makeFindQuery(wrapSingleQueryWithSuggestion(getBy, queryAllBy.name, 'find'));
return [queryBy, getAllWithSuggestions, getByWithSuggestions, findAllBy, findBy];
}

View File

@@ -0,0 +1,355 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.computeAriaBusy = computeAriaBusy;
exports.computeAriaChecked = computeAriaChecked;
exports.computeAriaCurrent = computeAriaCurrent;
exports.computeAriaExpanded = computeAriaExpanded;
exports.computeAriaPressed = computeAriaPressed;
exports.computeAriaSelected = computeAriaSelected;
exports.computeAriaValueMax = computeAriaValueMax;
exports.computeAriaValueMin = computeAriaValueMin;
exports.computeAriaValueNow = computeAriaValueNow;
exports.computeAriaValueText = computeAriaValueText;
exports.computeHeadingLevel = computeHeadingLevel;
exports.getImplicitAriaRoles = getImplicitAriaRoles;
exports.getRoles = getRoles;
exports.isInaccessible = isInaccessible;
exports.isSubtreeInaccessible = isSubtreeInaccessible;
exports.logRoles = void 0;
exports.prettyRoles = prettyRoles;
var _ariaQuery = require("aria-query");
var _domAccessibilityApi = require("dom-accessibility-api");
var _prettyDom = require("./pretty-dom");
var _config = require("./config");
const elementRoleList = buildElementRoleList(_ariaQuery.elementRoles);
/**
* @param {Element} element -
* @returns {boolean} - `true` if `element` and its subtree are inaccessible
*/
function isSubtreeInaccessible(element) {
if (element.hidden === true) {
return true;
}
if (element.getAttribute('aria-hidden') === 'true') {
return true;
}
const window = element.ownerDocument.defaultView;
if (window.getComputedStyle(element).display === 'none') {
return true;
}
return false;
}
/**
* Partial implementation https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion
* which should only be used for elements with a non-presentational role i.e.
* `role="none"` and `role="presentation"` will not be excluded.
*
* Implements aria-hidden semantics (i.e. parent overrides child)
* Ignores "Child Presentational: True" characteristics
*
* @param {Element} element -
* @param {object} [options] -
* @param {function (element: Element): boolean} options.isSubtreeInaccessible -
* can be used to return cached results from previous isSubtreeInaccessible calls
* @returns {boolean} true if excluded, otherwise false
*/
function isInaccessible(element, options = {}) {
const {
isSubtreeInaccessible: isSubtreeInaccessibleImpl = isSubtreeInaccessible
} = options;
const window = element.ownerDocument.defaultView;
// since visibility is inherited we can exit early
if (window.getComputedStyle(element).visibility === 'hidden') {
return true;
}
let currentElement = element;
while (currentElement) {
if (isSubtreeInaccessibleImpl(currentElement)) {
return true;
}
currentElement = currentElement.parentElement;
}
return false;
}
function getImplicitAriaRoles(currentNode) {
// eslint bug here:
// eslint-disable-next-line no-unused-vars
for (const {
match,
roles
} of elementRoleList) {
if (match(currentNode)) {
return [...roles];
}
}
return [];
}
function buildElementRoleList(elementRolesMap) {
function makeElementSelector({
name,
attributes
}) {
return `${name}${attributes.map(({
name: attributeName,
value,
constraints = []
}) => {
const shouldNotExist = constraints.indexOf('undefined') !== -1;
const shouldBeNonEmpty = constraints.indexOf('set') !== -1;
const hasExplicitValue = typeof value !== 'undefined';
if (hasExplicitValue) {
return `[${attributeName}="${value}"]`;
} else if (shouldNotExist) {
return `:not([${attributeName}])`;
} else if (shouldBeNonEmpty) {
return `[${attributeName}]:not([${attributeName}=""])`;
}
return `[${attributeName}]`;
}).join('')}`;
}
function getSelectorSpecificity({
attributes = []
}) {
return attributes.length;
}
function bySelectorSpecificity({
specificity: leftSpecificity
}, {
specificity: rightSpecificity
}) {
return rightSpecificity - leftSpecificity;
}
function match(element) {
let {
attributes = []
} = element;
// https://github.com/testing-library/dom-testing-library/issues/814
const typeTextIndex = attributes.findIndex(attribute => attribute.value && attribute.name === 'type' && attribute.value === 'text');
if (typeTextIndex >= 0) {
// not using splice to not mutate the attributes array
attributes = [...attributes.slice(0, typeTextIndex), ...attributes.slice(typeTextIndex + 1)];
}
const selector = makeElementSelector({
...element,
attributes
});
return node => {
if (typeTextIndex >= 0 && node.type !== 'text') {
return false;
}
return node.matches(selector);
};
}
let result = [];
// eslint bug here:
// eslint-disable-next-line no-unused-vars
for (const [element, roles] of elementRolesMap.entries()) {
result = [...result, {
match: match(element),
roles: Array.from(roles),
specificity: getSelectorSpecificity(element)
}];
}
return result.sort(bySelectorSpecificity);
}
function getRoles(container, {
hidden = false
} = {}) {
function flattenDOM(node) {
return [node, ...Array.from(node.children).reduce((acc, child) => [...acc, ...flattenDOM(child)], [])];
}
return flattenDOM(container).filter(element => {
return hidden === false ? isInaccessible(element) === false : true;
}).reduce((acc, node) => {
let roles = [];
// TODO: This violates html-aria which does not allow any role on every element
if (node.hasAttribute('role')) {
roles = node.getAttribute('role').split(' ').slice(0, 1);
} else {
roles = getImplicitAriaRoles(node);
}
return roles.reduce((rolesAcc, role) => Array.isArray(rolesAcc[role]) ? {
...rolesAcc,
[role]: [...rolesAcc[role], node]
} : {
...rolesAcc,
[role]: [node]
}, acc);
}, {});
}
function prettyRoles(dom, {
hidden,
includeDescription
}) {
const roles = getRoles(dom, {
hidden
});
// We prefer to skip generic role, we don't recommend it
return Object.entries(roles).filter(([role]) => role !== 'generic').map(([role, elements]) => {
const delimiterBar = '-'.repeat(50);
const elementsString = elements.map(el => {
const nameString = `Name "${(0, _domAccessibilityApi.computeAccessibleName)(el, {
computedStyleSupportsPseudoElements: (0, _config.getConfig)().computedStyleSupportsPseudoElements
})}":\n`;
const domString = (0, _prettyDom.prettyDOM)(el.cloneNode(false));
if (includeDescription) {
const descriptionString = `Description "${(0, _domAccessibilityApi.computeAccessibleDescription)(el, {
computedStyleSupportsPseudoElements: (0, _config.getConfig)().computedStyleSupportsPseudoElements
})}":\n`;
return `${nameString}${descriptionString}${domString}`;
}
return `${nameString}${domString}`;
}).join('\n\n');
return `${role}:\n\n${elementsString}\n\n${delimiterBar}`;
}).join('\n');
}
const logRoles = (dom, {
hidden = false
} = {}) => console.log(prettyRoles(dom, {
hidden
}));
/**
* @param {Element} element -
* @returns {boolean | undefined} - false/true if (not)selected, undefined if not selectable
*/
exports.logRoles = logRoles;
function computeAriaSelected(element) {
// implicit value from html-aam mappings: https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings
// https://www.w3.org/TR/html-aam-1.0/#details-id-97
if (element.tagName === 'OPTION') {
return element.selected;
}
// explicit value
return checkBooleanAttribute(element, 'aria-selected');
}
/**
* @param {Element} element -
* @returns {boolean} -
*/
function computeAriaBusy(element) {
// https://www.w3.org/TR/wai-aria-1.1/#aria-busy
return element.getAttribute('aria-busy') === 'true';
}
/**
* @param {Element} element -
* @returns {boolean | undefined} - false/true if (not)checked, undefined if not checked-able
*/
function computeAriaChecked(element) {
// implicit value from html-aam mappings: https://www.w3.org/TR/html-aam-1.0/#html-attribute-state-and-property-mappings
// https://www.w3.org/TR/html-aam-1.0/#details-id-56
// https://www.w3.org/TR/html-aam-1.0/#details-id-67
if ('indeterminate' in element && element.indeterminate) {
return undefined;
}
if ('checked' in element) {
return element.checked;
}
// explicit value
return checkBooleanAttribute(element, 'aria-checked');
}
/**
* @param {Element} element -
* @returns {boolean | undefined} - false/true if (not)pressed, undefined if not press-able
*/
function computeAriaPressed(element) {
// https://www.w3.org/TR/wai-aria-1.1/#aria-pressed
return checkBooleanAttribute(element, 'aria-pressed');
}
/**
* @param {Element} element -
* @returns {boolean | string | null} -
*/
function computeAriaCurrent(element) {
// https://www.w3.org/TR/wai-aria-1.1/#aria-current
return checkBooleanAttribute(element, 'aria-current') ?? element.getAttribute('aria-current') ?? false;
}
/**
* @param {Element} element -
* @returns {boolean | undefined} - false/true if (not)expanded, undefined if not expand-able
*/
function computeAriaExpanded(element) {
// https://www.w3.org/TR/wai-aria-1.1/#aria-expanded
return checkBooleanAttribute(element, 'aria-expanded');
}
function checkBooleanAttribute(element, attribute) {
const attributeValue = element.getAttribute(attribute);
if (attributeValue === 'true') {
return true;
}
if (attributeValue === 'false') {
return false;
}
return undefined;
}
/**
* @param {Element} element -
* @returns {number | undefined} - number if implicit heading or aria-level present, otherwise undefined
*/
function computeHeadingLevel(element) {
// https://w3c.github.io/html-aam/#el-h1-h6
// https://w3c.github.io/html-aam/#el-h1-h6
const implicitHeadingLevels = {
H1: 1,
H2: 2,
H3: 3,
H4: 4,
H5: 5,
H6: 6
};
// explicit aria-level value
// https://www.w3.org/TR/wai-aria-1.2/#aria-level
const ariaLevelAttribute = element.getAttribute('aria-level') && Number(element.getAttribute('aria-level'));
return ariaLevelAttribute || implicitHeadingLevels[element.tagName];
}
/**
* @param {Element} element -
* @returns {number | undefined} -
*/
function computeAriaValueNow(element) {
const valueNow = element.getAttribute('aria-valuenow');
return valueNow === null ? undefined : +valueNow;
}
/**
* @param {Element} element -
* @returns {number | undefined} -
*/
function computeAriaValueMax(element) {
const valueMax = element.getAttribute('aria-valuemax');
return valueMax === null ? undefined : +valueMax;
}
/**
* @param {Element} element -
* @returns {number | undefined} -
*/
function computeAriaValueMin(element) {
const valueMin = element.getAttribute('aria-valuemin');
return valueMin === null ? undefined : +valueMin;
}
/**
* @param {Element} element -
* @returns {string | undefined} -
*/
function computeAriaValueText(element) {
const valueText = element.getAttribute('aria-valuetext');
return valueText === null ? undefined : valueText;
}

View File

@@ -0,0 +1,57 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.screen = void 0;
var _lzString = _interopRequireDefault(require("lz-string"));
var _getQueriesForElement = require("./get-queries-for-element");
var _helpers = require("./helpers");
var _prettyDom = require("./pretty-dom");
var queries = _interopRequireWildcard(require("./queries"));
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
// WARNING: `lz-string` only has a default export but statically we assume named exports are allowed
// TODO: Statically verify we don't rely on NodeJS implicit named imports.
function unindent(string) {
// remove white spaces first, to save a few bytes.
// testing-playground will reformat on load any ways.
return string.replace(/[ \t]*[\n][ \t]*/g, '\n');
}
function encode(value) {
return _lzString.default.compressToEncodedURIComponent(unindent(value));
}
function getPlaygroundUrl(markup) {
return `https://testing-playground.com/#markup=${encode(markup)}`;
}
const debug = (element, maxLength, options) => Array.isArray(element) ? element.forEach(el => (0, _prettyDom.logDOM)(el, maxLength, options)) : (0, _prettyDom.logDOM)(element, maxLength, options);
const logTestingPlaygroundURL = (element = (0, _helpers.getDocument)().body) => {
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!element || !('innerHTML' in element)) {
console.log(`The element you're providing isn't a valid DOM element.`);
return;
}
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (!element.innerHTML) {
console.log(`The provided element doesn't have any children.`);
return;
}
const playgroundUrl = getPlaygroundUrl(element.innerHTML);
console.log(`Open this URL in your browser\n\n${playgroundUrl}`);
return playgroundUrl;
};
const initialValue = {
debug,
logTestingPlaygroundURL
};
const screen = exports.screen = typeof document !== 'undefined' && document.body // eslint-disable-line @typescript-eslint/no-unnecessary-condition
? (0, _getQueriesForElement.getQueriesForElement)(document.body, queries, initialValue) : Object.keys(queries).reduce((helpers, key) => {
// `key` is for all intents and purposes the type of keyof `helpers`, which itself is the type of `initialValue` plus incoming properties from `queries`
// if `Object.keys(something)` returned Array<keyof typeof something> this explicit type assertion would not be necessary
// see https://stackoverflow.com/questions/55012174/why-doesnt-object-keys-return-a-keyof-type-in-typescript
helpers[key] = () => {
throw new TypeError('For queries bound to document.body a global document has to be available... Learn more: https://testing-library.com/s/screen-global-error');
};
return helpers;
}, initialValue);

View File

@@ -0,0 +1,118 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getSuggestedQuery = getSuggestedQuery;
var _domAccessibilityApi = require("dom-accessibility-api");
var _matches = require("./matches");
var _getNodeText = require("./get-node-text");
var _config = require("./config");
var _roleHelpers = require("./role-helpers");
var _labelHelpers = require("./label-helpers");
const normalize = (0, _matches.getDefaultNormalizer)();
function escapeRegExp(string) {
return string.replace(/[.*+\-?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
}
function getRegExpMatcher(string) {
return new RegExp(escapeRegExp(string.toLowerCase()), 'i');
}
function makeSuggestion(queryName, element, content, {
variant,
name
}) {
let warning = '';
const queryOptions = {};
const queryArgs = [['Role', 'TestId'].includes(queryName) ? content : getRegExpMatcher(content)];
if (name) {
queryOptions.name = getRegExpMatcher(name);
}
if (queryName === 'Role' && (0, _roleHelpers.isInaccessible)(element)) {
queryOptions.hidden = true;
warning = `Element is inaccessible. This means that the element and all its children are invisible to screen readers.
If you are using the aria-hidden prop, make sure this is the right choice for your case.
`;
}
if (Object.keys(queryOptions).length > 0) {
queryArgs.push(queryOptions);
}
const queryMethod = `${variant}By${queryName}`;
return {
queryName,
queryMethod,
queryArgs,
variant,
warning,
toString() {
if (warning) {
console.warn(warning);
}
let [text, options] = queryArgs;
text = typeof text === 'string' ? `'${text}'` : text;
options = options ? `, { ${Object.entries(options).map(([k, v]) => `${k}: ${v}`).join(', ')} }` : '';
return `${queryMethod}(${text}${options})`;
}
};
}
function canSuggest(currentMethod, requestedMethod, data) {
return data && (!requestedMethod || requestedMethod.toLowerCase() === currentMethod.toLowerCase());
}
function getSuggestedQuery(element, variant = 'get', method) {
// don't create suggestions for script and style elements
if (element.matches((0, _config.getConfig)().defaultIgnore)) {
return undefined;
}
//We prefer to suggest something else if the role is generic
const role = element.getAttribute('role') ?? (0, _roleHelpers.getImplicitAriaRoles)(element)?.[0];
if (role !== 'generic' && canSuggest('Role', method, role)) {
return makeSuggestion('Role', element, role, {
variant,
name: (0, _domAccessibilityApi.computeAccessibleName)(element, {
computedStyleSupportsPseudoElements: (0, _config.getConfig)().computedStyleSupportsPseudoElements
})
});
}
const labelText = (0, _labelHelpers.getLabels)(document, element).map(label => label.content).join(' ');
if (canSuggest('LabelText', method, labelText)) {
return makeSuggestion('LabelText', element, labelText, {
variant
});
}
const placeholderText = element.getAttribute('placeholder');
if (canSuggest('PlaceholderText', method, placeholderText)) {
return makeSuggestion('PlaceholderText', element, placeholderText, {
variant
});
}
const textContent = normalize((0, _getNodeText.getNodeText)(element));
if (canSuggest('Text', method, textContent)) {
return makeSuggestion('Text', element, textContent, {
variant
});
}
if (canSuggest('DisplayValue', method, element.value)) {
return makeSuggestion('DisplayValue', element, normalize(element.value), {
variant
});
}
const alt = element.getAttribute('alt');
if (canSuggest('AltText', method, alt)) {
return makeSuggestion('AltText', element, alt, {
variant
});
}
const title = element.getAttribute('title');
if (canSuggest('Title', method, title)) {
return makeSuggestion('Title', element, title, {
variant
});
}
const testId = element.getAttribute((0, _config.getConfig)().testIdAttribute);
if (canSuggest('TestId', method, testId)) {
return makeSuggestion('TestId', element, testId, {
variant
});
}
return undefined;
}

View File

@@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.waitForElementToBeRemoved = waitForElementToBeRemoved;
var _waitFor = require("./wait-for");
const isRemoved = result => !result || Array.isArray(result) && !result.length;
// Check if the element is not present.
// As the name implies, waitForElementToBeRemoved should check `present` --> `removed`
function initialCheck(elements) {
if (isRemoved(elements)) {
throw new Error('The element(s) given to waitForElementToBeRemoved are already removed. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal.');
}
}
async function waitForElementToBeRemoved(callback, options) {
// created here so we get a nice stacktrace
const timeoutError = new Error('Timed out in waitForElementToBeRemoved.');
if (typeof callback !== 'function') {
initialCheck(callback);
const elements = Array.isArray(callback) ? callback : [callback];
const getRemainingElements = elements.map(element => {
let parent = element.parentElement;
if (parent === null) return () => null;
while (parent.parentElement) parent = parent.parentElement;
return () => parent.contains(element) ? element : null;
});
callback = () => getRemainingElements.map(c => c()).filter(Boolean);
}
initialCheck(callback());
return (0, _waitFor.waitFor)(() => {
let result;
try {
result = callback();
} catch (error) {
if (error.name === 'TestingLibraryElementError') {
return undefined;
}
throw error;
}
if (!isRemoved(result)) {
throw timeoutError;
}
return undefined;
}, options);
}
/*
eslint
require-await: "off"
*/

View File

@@ -0,0 +1,173 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.waitFor = waitForWrapper;
var _helpers = require("./helpers");
var _config = require("./config");
// This is so the stack trace the developer sees is one that's
// closer to their code (because async stack traces are hard to follow).
function copyStackTrace(target, source) {
target.stack = source.stack.replace(source.message, target.message);
}
function waitFor(callback, {
container = (0, _helpers.getDocument)(),
timeout = (0, _config.getConfig)().asyncUtilTimeout,
showOriginalStackTrace = (0, _config.getConfig)().showOriginalStackTrace,
stackTraceError,
interval = 50,
onTimeout = error => {
Object.defineProperty(error, 'message', {
value: (0, _config.getConfig)().getElementError(error.message, container).message
});
return error;
},
mutationObserverOptions = {
subtree: true,
childList: true,
attributes: true,
characterData: true
}
}) {
if (typeof callback !== 'function') {
throw new TypeError('Received `callback` arg must be a function');
}
return new Promise(async (resolve, reject) => {
let lastError, intervalId, observer;
let finished = false;
let promiseStatus = 'idle';
const overallTimeoutTimer = setTimeout(handleTimeout, timeout);
const usingJestFakeTimers = (0, _helpers.jestFakeTimersAreEnabled)();
if (usingJestFakeTimers) {
const {
unstable_advanceTimersWrapper: advanceTimersWrapper
} = (0, _config.getConfig)();
checkCallback();
// this is a dangerous rule to disable because it could lead to an
// infinite loop. However, eslint isn't smart enough to know that we're
// setting finished inside `onDone` which will be called when we're done
// waiting or when we've timed out.
// eslint-disable-next-line no-unmodified-loop-condition
while (!finished) {
if (!(0, _helpers.jestFakeTimersAreEnabled)()) {
const error = new Error(`Changed from using fake timers to real timers while using waitFor. This is not allowed and will result in very strange behavior. Please ensure you're awaiting all async things your test is doing before changing to real timers. For more info, please go to https://github.com/testing-library/dom-testing-library/issues/830`);
if (!showOriginalStackTrace) copyStackTrace(error, stackTraceError);
reject(error);
return;
}
// In this rare case, we *need* to wait for in-flight promises
// to resolve before continuing. We don't need to take advantage
// of parallelization so we're fine.
// https://stackoverflow.com/a/59243586/971592
// eslint-disable-next-line no-await-in-loop
await advanceTimersWrapper(async () => {
// we *could* (maybe should?) use `advanceTimersToNextTimer` but it's
// possible that could make this loop go on forever if someone is using
// third party code that's setting up recursive timers so rapidly that
// the user's timer's don't get a chance to resolve. So we'll advance
// by an interval instead. (We have a test for this case).
jest.advanceTimersByTime(interval);
});
// Could have timed-out
if (finished) {
break;
}
// It's really important that checkCallback is run *before* we flush
// in-flight promises. To be honest, I'm not sure why, and I can't quite
// think of a way to reproduce the problem in a test, but I spent
// an entire day banging my head against a wall on this.
checkCallback();
}
} else {
try {
(0, _helpers.checkContainerType)(container);
} catch (e) {
reject(e);
return;
}
intervalId = setInterval(checkRealTimersCallback, interval);
const {
MutationObserver
} = (0, _helpers.getWindowFromNode)(container);
observer = new MutationObserver(checkRealTimersCallback);
observer.observe(container, mutationObserverOptions);
checkCallback();
}
function onDone(error, result) {
finished = true;
clearTimeout(overallTimeoutTimer);
if (!usingJestFakeTimers) {
clearInterval(intervalId);
observer.disconnect();
}
if (error) {
reject(error);
} else {
resolve(result);
}
}
function checkRealTimersCallback() {
if ((0, _helpers.jestFakeTimersAreEnabled)()) {
const error = new Error(`Changed from using real timers to fake timers while using waitFor. This is not allowed and will result in very strange behavior. Please ensure you're awaiting all async things your test is doing before changing to fake timers. For more info, please go to https://github.com/testing-library/dom-testing-library/issues/830`);
if (!showOriginalStackTrace) copyStackTrace(error, stackTraceError);
return reject(error);
} else {
return checkCallback();
}
}
function checkCallback() {
if (promiseStatus === 'pending') return;
try {
const result = (0, _config.runWithExpensiveErrorDiagnosticsDisabled)(callback);
if (typeof result?.then === 'function') {
promiseStatus = 'pending';
result.then(resolvedValue => {
promiseStatus = 'resolved';
onDone(null, resolvedValue);
}, rejectedValue => {
promiseStatus = 'rejected';
lastError = rejectedValue;
});
} else {
onDone(null, result);
}
// If `callback` throws, wait for the next mutation, interval, or timeout.
} catch (error) {
// Save the most recent callback error to reject the promise with it in the event of a timeout
lastError = error;
}
}
function handleTimeout() {
let error;
if (lastError) {
error = lastError;
if (!showOriginalStackTrace && error.name === 'TestingLibraryElementError') {
copyStackTrace(error, stackTraceError);
}
} else {
error = new Error('Timed out in waitFor.');
if (!showOriginalStackTrace) {
copyStackTrace(error, stackTraceError);
}
}
onDone(onTimeout(error), null);
}
});
}
function waitForWrapper(callback, options) {
// create the error here so its stack trace is as close to the
// calling code as possible
const stackTraceError = new Error('STACK_TRACE_MESSAGE');
return (0, _config.getConfig)().asyncWrapper(() => waitFor(callback, {
stackTraceError,
...options
}));
}
/*
eslint
max-lines-per-function: ["error", {"max": 200}],
*/

120
frontend/node_modules/@testing-library/dom/package.json generated vendored Normal file
View File

@@ -0,0 +1,120 @@
{
"name": "@testing-library/dom",
"version": "10.4.1",
"description": "Simple and complete DOM testing utilities that encourage good testing practices.",
"main": "dist/index.js",
"types": "types/index.d.ts",
"module": "dist/@testing-library/dom.esm.js",
"umd:main": "dist/@testing-library/dom.umd.js",
"source": "src/index.js",
"keywords": [
"testing",
"ui",
"dom",
"jsdom",
"unit",
"integration",
"functional",
"end-to-end",
"e2e"
],
"author": "Kent C. Dodds <me@kentcdodds.com> (https://kentcdodds.com)",
"license": "MIT",
"engines": {
"node": ">=18"
},
"browserslist": [
"and_chr 103",
"and_ff 101",
"and_qq 10.4",
"and_uc 12.12",
"android 103",
"chrome 102",
"edge 102",
"firefox 91",
"ios_saf 12.2-12.5",
"kaios 2.5",
"op_mini all",
"op_mob 64",
"opera 88",
"safari 15.5",
"samsung 17.0",
"samsung 16.0",
"node 18.0"
],
"scripts": {
"build": "kcd-scripts build --no-ts-defs --ignore \"**/__tests__/**,**/__node_tests__/**,**/__mocks__/**\" && kcd-scripts build --no-ts-defs --bundle --no-clean",
"format": "kcd-scripts format",
"install:csb": "npm install",
"lint": "kcd-scripts lint",
"setup": "npm install && npm run validate -s",
"test": "kcd-scripts test",
"test:debug": "node --inspect-brk ./node_modules/.bin/jest --watch --runInBand",
"test:update": "npm test -- --updateSnapshot --coverage",
"validate": "kcd-scripts validate",
"typecheck": "kcd-scripts typecheck --build types"
},
"files": [
"dist",
"types/*.d.ts"
],
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
"@types/aria-query": "^5.0.1",
"aria-query": "5.3.0",
"dom-accessibility-api": "^0.5.9",
"lz-string": "^1.5.0",
"picocolors": "1.1.1",
"pretty-format": "^27.0.2"
},
"devDependencies": {
"@testing-library/jest-dom": "^5.11.6",
"browserslist": "4.21.8",
"caniuse-lite": "1.0.30001502",
"jest-in-case": "^1.0.2",
"jest-snapshot-serializer-ansi": "^1.0.0",
"jest-watch-select-projects": "^2.0.0",
"jsdom": "20.0.0",
"kcd-scripts": "^13.0.0",
"typescript": "^4.1.2"
},
"overrides": {
"@babel/helper-compilation-targets": "7.24.7",
"browserslist": "4.21.8",
"caniuse-lite": "1.0.30001502"
},
"eslintConfig": {
"extends": [
"./node_modules/kcd-scripts/eslint.js",
"plugin:import/typescript"
],
"parserOptions": {
"ecmaVersion": 2020
},
"rules": {
"@typescript-eslint/prefer-optional-chain": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unsafe-member-access": "off",
"@typescript-eslint/no-unnecessary-boolean-literal-compare": "off",
"@typescript-eslint/prefer-includes": "off",
"import/prefer-default-export": "off",
"import/no-unassigned-import": "off",
"import/no-useless-path-segments": "off",
"no-console": "off"
}
},
"eslintIgnore": [
"node_modules",
"coverage",
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/testing-library/dom-testing-library"
},
"bugs": {
"url": "https://github.com/testing-library/dom-testing-library/issues"
},
"homepage": "https://github.com/testing-library/dom-testing-library#readme"
}

View File

@@ -0,0 +1,27 @@
export interface Config {
testIdAttribute: string
/**
* WARNING: `unstable` prefix means this API may change in patch and minor releases.
* @param cb
*/
unstable_advanceTimersWrapper(cb: (...args: unknown[]) => unknown): unknown
// eslint-disable-next-line @typescript-eslint/no-explicit-any
asyncWrapper(cb: (...args: any[]) => any): Promise<any>
// eslint-disable-next-line @typescript-eslint/no-explicit-any
eventWrapper(cb: (...args: any[]) => any): void
asyncUtilTimeout: number
computedStyleSupportsPseudoElements: boolean
defaultHidden: boolean
/** default value for the `ignore` option in `ByText` queries */
defaultIgnore: string
showOriginalStackTrace: boolean
throwSuggestions: boolean
getElementError: (message: string | null, container: Element) => Error
}
export interface ConfigFn {
(existingConfig: Config): Partial<Config>
}
export function configure(configDelta: ConfigFn | Partial<Config>): void
export function getConfig(): Config

View File

@@ -0,0 +1,118 @@
export type EventType =
| 'copy'
| 'cut'
| 'paste'
| 'compositionEnd'
| 'compositionStart'
| 'compositionUpdate'
| 'keyDown'
| 'keyPress'
| 'keyUp'
| 'focus'
| 'blur'
| 'focusIn'
| 'focusOut'
| 'change'
| 'input'
| 'invalid'
| 'submit'
| 'reset'
| 'click'
| 'contextMenu'
| 'dblClick'
| 'drag'
| 'dragEnd'
| 'dragEnter'
| 'dragExit'
| 'dragLeave'
| 'dragOver'
| 'dragStart'
| 'drop'
| 'mouseDown'
| 'mouseEnter'
| 'mouseLeave'
| 'mouseMove'
| 'mouseOut'
| 'mouseOver'
| 'mouseUp'
| 'popState'
| 'select'
| 'touchCancel'
| 'touchEnd'
| 'touchMove'
| 'touchStart'
| 'resize'
| 'scroll'
| 'wheel'
| 'abort'
| 'canPlay'
| 'canPlayThrough'
| 'durationChange'
| 'emptied'
| 'encrypted'
| 'ended'
| 'loadedData'
| 'loadedMetadata'
| 'loadStart'
| 'pause'
| 'play'
| 'playing'
| 'progress'
| 'rateChange'
| 'seeked'
| 'seeking'
| 'stalled'
| 'suspend'
| 'timeUpdate'
| 'volumeChange'
| 'waiting'
| 'load'
| 'error'
| 'animationStart'
| 'animationEnd'
| 'animationIteration'
| 'transitionCancel'
| 'transitionEnd'
| 'transitionRun'
| 'transitionStart'
| 'doubleClick'
| 'pointerOver'
| 'pointerEnter'
| 'pointerDown'
| 'pointerMove'
| 'pointerUp'
| 'pointerCancel'
| 'pointerOut'
| 'pointerLeave'
| 'gotPointerCapture'
| 'lostPointerCapture'
| 'offline'
| 'online'
| 'pageHide'
| 'pageShow'
export type FireFunction = (
element: Document | Element | Window | Node,
event: Event,
) => boolean
export type FireObject = {
[K in EventType]: (
element: Document | Element | Window | Node,
options?: {},
) => boolean
}
export type CreateFunction = (
eventName: string,
node: Document | Element | Window | Node,
init?: {},
options?: {EventType?: string; defaultInit?: {}},
) => Event
export type CreateObject = {
[K in EventType]: (
element: Document | Element | Window | Node,
options?: {},
) => Event
}
export const createEvent: CreateObject & CreateFunction
export const fireEvent: FireFunction & FireObject

View File

@@ -0,0 +1 @@
export function getNodeText(node: HTMLElement): string

View File

@@ -0,0 +1,182 @@
import * as queries from './queries'
export type BoundFunction<T> = T extends (
container: HTMLElement,
...args: infer P
) => infer R
? (...args: P) => R
: never
export type BoundFunctions<Q> = Q extends typeof queries
? {
getByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.GetByText<T>>>
): ReturnType<queries.GetByText<T>>
getAllByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByText<T>>>
): ReturnType<queries.AllByText<T>>
queryByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.QueryByText<T>>>
): ReturnType<queries.QueryByText<T>>
queryAllByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByText<T>>>
): ReturnType<queries.AllByText<T>>
findByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindByText<T>>>
): ReturnType<queries.FindByText<T>>
findAllByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindAllByText<T>>>
): ReturnType<queries.FindAllByText<T>>
getByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.GetByBoundAttribute<T>>>
): ReturnType<queries.GetByBoundAttribute<T>>
getAllByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
queryByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.QueryByBoundAttribute<T>>>
): ReturnType<queries.QueryByBoundAttribute<T>>
queryAllByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
findByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindByBoundAttribute<T>>>
): ReturnType<queries.FindByBoundAttribute<T>>
findAllByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindAllByBoundAttribute<T>>>
): ReturnType<queries.FindAllByBoundAttribute<T>>
getByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.GetByText<T>>>
): ReturnType<queries.GetByText<T>>
getAllByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByText<T>>>
): ReturnType<queries.AllByText<T>>
queryByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.QueryByText<T>>>
): ReturnType<queries.QueryByText<T>>
queryAllByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByText<T>>>
): ReturnType<queries.AllByText<T>>
findByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindByText<T>>>
): ReturnType<queries.FindByText<T>>
findAllByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindAllByText<T>>>
): ReturnType<queries.FindAllByText<T>>
getByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.GetByBoundAttribute<T>>>
): ReturnType<queries.GetByBoundAttribute<T>>
getAllByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
queryByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.QueryByBoundAttribute<T>>>
): ReturnType<queries.QueryByBoundAttribute<T>>
queryAllByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
findByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindByBoundAttribute<T>>>
): ReturnType<queries.FindByBoundAttribute<T>>
findAllByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindAllByBoundAttribute<T>>>
): ReturnType<queries.FindAllByBoundAttribute<T>>
getByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.GetByBoundAttribute<T>>>
): ReturnType<queries.GetByBoundAttribute<T>>
getAllByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
queryByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.QueryByBoundAttribute<T>>>
): ReturnType<queries.QueryByBoundAttribute<T>>
queryAllByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
findByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindByBoundAttribute<T>>>
): ReturnType<queries.FindByBoundAttribute<T>>
findAllByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindAllByBoundAttribute<T>>>
): ReturnType<queries.FindAllByBoundAttribute<T>>
getByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.GetByBoundAttribute<T>>>
): ReturnType<queries.GetByBoundAttribute<T>>
getAllByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
queryByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.QueryByBoundAttribute<T>>>
): ReturnType<queries.QueryByBoundAttribute<T>>
queryAllByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
findByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindByBoundAttribute<T>>>
): ReturnType<queries.FindByBoundAttribute<T>>
findAllByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindAllByBoundAttribute<T>>>
): ReturnType<queries.FindAllByBoundAttribute<T>>
getByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.GetByRole<T>>>
): ReturnType<queries.GetByRole<T>>
getAllByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByRole<T>>>
): ReturnType<queries.AllByRole<T>>
queryByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.QueryByRole<T>>>
): ReturnType<queries.QueryByRole<T>>
queryAllByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByRole<T>>>
): ReturnType<queries.AllByRole<T>>
findByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindByRole<T>>>
): ReturnType<queries.FindByRole<T>>
findAllByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindAllByRole<T>>>
): ReturnType<queries.FindAllByRole<T>>
getByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.GetByBoundAttribute<T>>>
): ReturnType<queries.GetByBoundAttribute<T>>
getAllByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
queryByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.QueryByBoundAttribute<T>>>
): ReturnType<queries.QueryByBoundAttribute<T>>
queryAllByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.AllByBoundAttribute<T>>>
): ReturnType<queries.AllByBoundAttribute<T>>
findByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindByBoundAttribute<T>>>
): ReturnType<queries.FindByBoundAttribute<T>>
findAllByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<BoundFunction<queries.FindAllByBoundAttribute<T>>>
): ReturnType<queries.FindAllByBoundAttribute<T>>
} & {
[P in keyof Q]: BoundFunction<Q[P]>
}
: {
[P in keyof Q]: BoundFunction<Q[P]>
}
export type Query = (
container: HTMLElement,
...args: any[]
) =>
| Error
| HTMLElement
| HTMLElement[]
| Promise<HTMLElement[]>
| Promise<HTMLElement>
| null
export interface Queries {
[T: string]: Query
}
export function getQueriesForElement<
QueriesToBind extends Queries = typeof queries,
// Extra type parameter required for reassignment.
T extends QueriesToBind = QueriesToBind,
>(element: HTMLElement, queriesToBind?: T): BoundFunctions<T>

View File

@@ -0,0 +1,22 @@
// TypeScript Version: 3.8
import {getQueriesForElement} from './get-queries-for-element'
import * as queries from './queries'
import * as queryHelpers from './query-helpers'
declare const within: typeof getQueriesForElement
export {queries, queryHelpers, within}
export * from './queries'
export * from './query-helpers'
export * from './screen'
export * from './wait-for'
export * from './wait-for-element-to-be-removed'
export * from './matches'
export * from './get-node-text'
export * from './events'
export * from './get-queries-for-element'
export * from './pretty-dom'
export * from './role-helpers'
export * from './config'
export * from './suggestions'

View File

@@ -0,0 +1,46 @@
import {ARIARole} from 'aria-query'
export type MatcherFunction = (
content: string,
element: Element | null,
) => boolean
export type Matcher = MatcherFunction | RegExp | number | string
// Get autocomplete for ARIARole union types, while still supporting another string
// Ref: https://github.com/microsoft/TypeScript/issues/29729#issuecomment-567871939
export type ByRoleMatcher = ARIARole | (string & {})
export type NormalizerFn = (text: string) => string
export interface NormalizerOptions extends DefaultNormalizerOptions {
normalizer?: NormalizerFn
}
export interface MatcherOptions {
exact?: boolean
/** Use normalizer with getDefaultNormalizer instead */
trim?: boolean
/** Use normalizer with getDefaultNormalizer instead */
collapseWhitespace?: boolean
normalizer?: NormalizerFn
/** suppress suggestions for a specific query */
suggest?: boolean
}
export type Match = (
textToMatch: string,
node: HTMLElement | null,
matcher: Matcher,
options?: MatcherOptions,
) => boolean
export interface DefaultNormalizerOptions {
trim?: boolean
collapseWhitespace?: boolean
}
export function getDefaultNormalizer(
options?: DefaultNormalizerOptions,
): NormalizerFn
// N.B. Don't expose fuzzyMatches + matches here: they're not public API

View File

@@ -0,0 +1,21 @@
import * as prettyFormat from 'pretty-format'
export interface PrettyDOMOptions extends prettyFormat.OptionsReceived {
/**
* Given a `Node` return `false` if you wish to ignore that node in the output.
* By default, ignores `<style />`, `<script />` and comment nodes.
*/
filterNode?: (node: Node) => boolean
}
export function prettyDOM(
dom?: Element | HTMLDocument,
maxLength?: number,
options?: PrettyDOMOptions,
): string | false
export function logDOM(
dom?: Element | HTMLDocument,
maxLength?: number,
options?: PrettyDOMOptions,
): void
export {prettyFormat}

View File

@@ -0,0 +1,315 @@
import {ByRoleMatcher, Matcher, MatcherOptions} from './matches'
import {SelectorMatcherOptions} from './query-helpers'
import {waitForOptions} from './wait-for'
export type QueryByBoundAttribute<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: MatcherOptions,
) => T | null
export type AllByBoundAttribute<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: MatcherOptions,
) => T[]
export type FindAllByBoundAttribute<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: MatcherOptions,
waitForElementOptions?: waitForOptions,
) => Promise<T[]>
export type GetByBoundAttribute<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: MatcherOptions,
) => T
export type FindByBoundAttribute<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: MatcherOptions,
waitForElementOptions?: waitForOptions,
) => Promise<T>
export type QueryByText<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: SelectorMatcherOptions,
) => T | null
export type AllByText<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: SelectorMatcherOptions,
) => T[]
export type FindAllByText<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: SelectorMatcherOptions,
waitForElementOptions?: waitForOptions,
) => Promise<T[]>
export type GetByText<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: SelectorMatcherOptions,
) => T
export type FindByText<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
id: Matcher,
options?: SelectorMatcherOptions,
waitForElementOptions?: waitForOptions,
) => Promise<T>
export interface ByRoleOptions {
/** suppress suggestions for a specific query */
suggest?: boolean
/**
* If true includes elements in the query set that are usually excluded from
* the accessibility tree. `role="none"` or `role="presentation"` are included
* in either case.
*/
hidden?: boolean
/**
* If true only includes elements in the query set that are marked as
* selected in the accessibility tree, i.e., `aria-selected="true"`
*/
selected?: boolean
/**
* If true only includes elements in the query set that are marked as
* busy in the accessibility tree, i.e., `aria-busy="true"`
*/
busy?: boolean
/**
* If true only includes elements in the query set that are marked as
* checked in the accessibility tree, i.e., `aria-checked="true"`
*/
checked?: boolean
/**
* If true only includes elements in the query set that are marked as
* pressed in the accessibility tree, i.e., `aria-pressed="true"`
*/
pressed?: boolean
/**
* Filters elements by their `aria-current` state. `true` and `false` match `aria-current="true"` and `aria-current="false"` (as well as a missing `aria-current` attribute) respectively.
*/
current?: boolean | string
/**
* If true only includes elements in the query set that are marked as
* expanded in the accessibility tree, i.e., `aria-expanded="true"`
*/
expanded?: boolean
/**
* Includes elements with the `"heading"` role matching the indicated level,
* either by the semantic HTML heading elements `<h1>-<h6>` or matching
* the `aria-level` attribute.
*/
level?: number
value?: {
now?: number
min?: number
max?: number
text?: Matcher
}
/**
* Includes every role used in the `role` attribute
* For example *ByRole('progressbar', {queryFallbacks: true})` will find <div role="meter progressbar">`.
*/
queryFallbacks?: boolean
/**
* Only considers elements with the specified accessible name.
*/
name?:
| RegExp
| string
| ((accessibleName: string, element: Element) => boolean)
/**
* Only considers elements with the specified accessible description.
*/
description?:
| RegExp
| string
| ((accessibleDescription: string, element: Element) => boolean)
}
export type AllByRole<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
role: ByRoleMatcher,
options?: ByRoleOptions,
) => T[]
export type GetByRole<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
role: ByRoleMatcher,
options?: ByRoleOptions,
) => T
export type QueryByRole<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
role: ByRoleMatcher,
options?: ByRoleOptions,
) => T | null
export type FindByRole<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
role: ByRoleMatcher,
options?: ByRoleOptions,
waitForElementOptions?: waitForOptions,
) => Promise<T>
export type FindAllByRole<T extends HTMLElement = HTMLElement> = (
container: HTMLElement,
role: ByRoleMatcher,
options?: ByRoleOptions,
waitForElementOptions?: waitForOptions,
) => Promise<T[]>
export function getByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<GetByText<T>>
): ReturnType<GetByText<T>>
export function getAllByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByText<T>>
): ReturnType<AllByText<T>>
export function queryByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<QueryByText<T>>
): ReturnType<QueryByText<T>>
export function queryAllByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByText<T>>
): ReturnType<AllByText<T>>
export function findByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindByText<T>>
): ReturnType<FindByText<T>>
export function findAllByLabelText<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindAllByText<T>>
): ReturnType<FindAllByText<T>>
export function getByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<GetByBoundAttribute<T>>
): ReturnType<GetByBoundAttribute<T>>
export function getAllByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function queryByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<QueryByBoundAttribute<T>>
): ReturnType<QueryByBoundAttribute<T>>
export function queryAllByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function findByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindByBoundAttribute<T>>
): ReturnType<FindByBoundAttribute<T>>
export function findAllByPlaceholderText<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindAllByBoundAttribute<T>>
): ReturnType<FindAllByBoundAttribute<T>>
export function getByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<GetByText<T>>
): ReturnType<GetByText<T>>
export function getAllByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByText<T>>
): ReturnType<AllByText<T>>
export function queryByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<QueryByText<T>>
): ReturnType<QueryByText<T>>
export function queryAllByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByText<T>>
): ReturnType<AllByText<T>>
export function findByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindByText<T>>
): ReturnType<FindByText<T>>
export function findAllByText<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindAllByText<T>>
): ReturnType<FindAllByText<T>>
export function getByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<GetByBoundAttribute<T>>
): ReturnType<GetByBoundAttribute<T>>
export function getAllByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function queryByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<QueryByBoundAttribute<T>>
): ReturnType<QueryByBoundAttribute<T>>
export function queryAllByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function findByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindByBoundAttribute<T>>
): ReturnType<FindByBoundAttribute<T>>
export function findAllByAltText<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindAllByBoundAttribute<T>>
): ReturnType<FindAllByBoundAttribute<T>>
export function getByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<GetByBoundAttribute<T>>
): ReturnType<GetByBoundAttribute<T>>
export function getAllByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function queryByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<QueryByBoundAttribute<T>>
): ReturnType<QueryByBoundAttribute<T>>
export function queryAllByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function findByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindByBoundAttribute<T>>
): ReturnType<FindByBoundAttribute<T>>
export function findAllByTitle<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindAllByBoundAttribute<T>>
): ReturnType<FindAllByBoundAttribute<T>>
export function getByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<GetByBoundAttribute<T>>
): ReturnType<GetByBoundAttribute<T>>
export function getAllByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function queryByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<QueryByBoundAttribute<T>>
): ReturnType<QueryByBoundAttribute<T>>
export function queryAllByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function findByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindByBoundAttribute<T>>
): ReturnType<FindByBoundAttribute<T>>
export function findAllByDisplayValue<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindAllByBoundAttribute<T>>
): ReturnType<FindAllByBoundAttribute<T>>
export function getByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<GetByRole<T>>
): ReturnType<GetByRole<T>>
export function getAllByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByRole<T>>
): ReturnType<AllByRole<T>>
export function queryByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<QueryByRole<T>>
): ReturnType<QueryByRole<T>>
export function queryAllByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByRole<T>>
): ReturnType<AllByRole<T>>
export function findByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindByRole<T>>
): ReturnType<FindByRole<T>>
export function findAllByRole<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindAllByRole<T>>
): ReturnType<FindAllByRole<T>>
export function getByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<GetByBoundAttribute<T>>
): ReturnType<GetByBoundAttribute<T>>
export function getAllByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function queryByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<QueryByBoundAttribute<T>>
): ReturnType<QueryByBoundAttribute<T>>
export function queryAllByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<AllByBoundAttribute<T>>
): ReturnType<AllByBoundAttribute<T>>
export function findByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindByBoundAttribute<T>>
): ReturnType<FindByBoundAttribute<T>>
export function findAllByTestId<T extends HTMLElement = HTMLElement>(
...args: Parameters<FindAllByBoundAttribute<T>>
): ReturnType<FindAllByBoundAttribute<T>>

View File

@@ -0,0 +1,74 @@
import {Matcher, MatcherOptions} from './matches'
import {waitForOptions} from './wait-for'
export type WithSuggest = {suggest?: boolean}
export type GetErrorFunction<Arguments extends any[] = [string]> = (
c: Element | null,
...args: Arguments
) => string
export interface SelectorMatcherOptions extends MatcherOptions {
selector?: string
ignore?: boolean | string
}
export type QueryByAttribute = (
attribute: string,
container: HTMLElement,
id: Matcher,
options?: MatcherOptions,
) => HTMLElement | null
export type AllByAttribute = (
attribute: string,
container: HTMLElement,
id: Matcher,
options?: MatcherOptions,
) => HTMLElement[]
export const queryByAttribute: QueryByAttribute
export const queryAllByAttribute: AllByAttribute
export function getElementError(
message: string | null,
container: HTMLElement,
): Error
/**
* query methods have a common call signature. Only the return type differs.
*/
export type QueryMethod<Arguments extends any[], Return> = (
container: HTMLElement,
...args: Arguments
) => Return
export type QueryBy<Arguments extends any[]> = QueryMethod<
Arguments,
HTMLElement | null
>
export type GetAllBy<Arguments extends any[]> = QueryMethod<
Arguments,
HTMLElement[]
>
export type FindAllBy<Arguments extends any[]> = QueryMethod<
[Arguments[0], Arguments[1]?, waitForOptions?],
Promise<HTMLElement[]>
>
export type GetBy<Arguments extends any[]> = QueryMethod<Arguments, HTMLElement>
export type FindBy<Arguments extends any[]> = QueryMethod<
[Arguments[0], Arguments[1]?, waitForOptions?],
Promise<HTMLElement>
>
export type BuiltQueryMethods<Arguments extends any[]> = [
QueryBy<Arguments>,
GetAllBy<Arguments>,
GetBy<Arguments>,
FindAllBy<Arguments>,
FindBy<Arguments>,
]
export function buildQueries<Arguments extends any[]>(
queryAllBy: GetAllBy<Arguments>,
getMultipleError: GetErrorFunction<Arguments>,
getMissingError: GetErrorFunction<Arguments>,
): BuiltQueryMethods<Arguments>

View File

@@ -0,0 +1,19 @@
export function logRoles(
container: HTMLElement,
options?: LogRolesOptions,
): string
interface LogRolesOptions {
hidden?: boolean
}
export function getRoles(container: HTMLElement): {
[index: string]: HTMLElement[]
}
/**
* https://testing-library.com/docs/dom-testing-library/api-helpers#isinaccessible
*/
export function isInaccessible(element: Element): boolean
export function computeHeadingLevel(element: Element): number | undefined

View File

@@ -0,0 +1,22 @@
import {OptionsReceived} from 'pretty-format'
import {BoundFunctions, Queries} from './get-queries-for-element'
import * as queries from './queries'
export type Screen<Q extends Queries = typeof queries> = BoundFunctions<Q> & {
/**
* Convenience function for `pretty-dom` which also allows an array
* of elements
*/
debug: (
element?: Array<Element | HTMLDocument> | Element | HTMLDocument,
maxLength?: number,
options?: OptionsReceived,
) => void
/**
* Convenience function for `Testing Playground` which logs and returns the URL that
* can be opened in a browser
*/
logTestingPlaygroundURL: (element?: Element | HTMLDocument) => string
}
export const screen: Screen

View File

@@ -0,0 +1,46 @@
export interface QueryOptions {
[key: string]: RegExp | boolean
}
export type QueryArgs = [string, QueryOptions?]
export interface Suggestion {
queryName: string
queryMethod: string
queryArgs: QueryArgs
variant: string
warning?: string
toString(): string
}
export type Variant =
| 'find'
| 'findAll'
| 'get'
| 'getAll'
| 'query'
| 'queryAll'
export type Method =
| 'AltText'
| 'alttext'
| 'DisplayValue'
| 'displayvalue'
| 'LabelText'
| 'labeltext'
| 'PlaceholderText'
| 'placeholdertext'
| 'Role'
| 'role'
| 'TestId'
| 'testid'
| 'Text'
| 'text'
| 'Title'
| 'title'
export function getSuggestedQuery(
element: HTMLElement,
variant?: Variant,
method?: Method,
): Suggestion | undefined

View File

@@ -0,0 +1,6 @@
import {waitForOptions} from './wait-for'
export function waitForElementToBeRemoved<T>(
callback: T | (() => T),
options?: waitForOptions,
): Promise<void>

View File

@@ -0,0 +1,12 @@
export interface waitForOptions {
container?: HTMLElement
timeout?: number
interval?: number
onTimeout?: (error: Error) => Error
mutationObserverOptions?: MutationObserverInit
}
export function waitFor<T>(
callback: () => Promise<T> | T,
options?: waitForOptions,
): Promise<T>

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) 2017 Kent C. Dodds
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.

1491
frontend/node_modules/@testing-library/jest-dom/README.md generated vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,6 @@
"use strict";
var extensions = _interopRequireWildcard(require("./matchers"));
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; }
expect.extend(extensions);

View File

@@ -0,0 +1,3 @@
"use strict";
require("./extend-expect");

View File

@@ -0,0 +1,192 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "toBeChecked", {
enumerable: true,
get: function () {
return _toBeChecked.toBeChecked;
}
});
Object.defineProperty(exports, "toBeDisabled", {
enumerable: true,
get: function () {
return _toBeDisabled.toBeDisabled;
}
});
Object.defineProperty(exports, "toBeEmpty", {
enumerable: true,
get: function () {
return _toBeEmpty.toBeEmpty;
}
});
Object.defineProperty(exports, "toBeEmptyDOMElement", {
enumerable: true,
get: function () {
return _toBeEmptyDomElement.toBeEmptyDOMElement;
}
});
Object.defineProperty(exports, "toBeEnabled", {
enumerable: true,
get: function () {
return _toBeDisabled.toBeEnabled;
}
});
Object.defineProperty(exports, "toBeInTheDOM", {
enumerable: true,
get: function () {
return _toBeInTheDom.toBeInTheDOM;
}
});
Object.defineProperty(exports, "toBeInTheDocument", {
enumerable: true,
get: function () {
return _toBeInTheDocument.toBeInTheDocument;
}
});
Object.defineProperty(exports, "toBeInvalid", {
enumerable: true,
get: function () {
return _toBeInvalid.toBeInvalid;
}
});
Object.defineProperty(exports, "toBePartiallyChecked", {
enumerable: true,
get: function () {
return _toBePartiallyChecked.toBePartiallyChecked;
}
});
Object.defineProperty(exports, "toBeRequired", {
enumerable: true,
get: function () {
return _toBeRequired.toBeRequired;
}
});
Object.defineProperty(exports, "toBeValid", {
enumerable: true,
get: function () {
return _toBeInvalid.toBeValid;
}
});
Object.defineProperty(exports, "toBeVisible", {
enumerable: true,
get: function () {
return _toBeVisible.toBeVisible;
}
});
Object.defineProperty(exports, "toContainElement", {
enumerable: true,
get: function () {
return _toContainElement.toContainElement;
}
});
Object.defineProperty(exports, "toContainHTML", {
enumerable: true,
get: function () {
return _toContainHtml.toContainHTML;
}
});
Object.defineProperty(exports, "toHaveAccessibleDescription", {
enumerable: true,
get: function () {
return _toHaveAccessibleDescription.toHaveAccessibleDescription;
}
});
Object.defineProperty(exports, "toHaveAccessibleErrorMessage", {
enumerable: true,
get: function () {
return _toHaveAccessibleErrormessage.toHaveAccessibleErrorMessage;
}
});
Object.defineProperty(exports, "toHaveAccessibleName", {
enumerable: true,
get: function () {
return _toHaveAccessibleName.toHaveAccessibleName;
}
});
Object.defineProperty(exports, "toHaveAttribute", {
enumerable: true,
get: function () {
return _toHaveAttribute.toHaveAttribute;
}
});
Object.defineProperty(exports, "toHaveClass", {
enumerable: true,
get: function () {
return _toHaveClass.toHaveClass;
}
});
Object.defineProperty(exports, "toHaveDescription", {
enumerable: true,
get: function () {
return _toHaveDescription.toHaveDescription;
}
});
Object.defineProperty(exports, "toHaveDisplayValue", {
enumerable: true,
get: function () {
return _toHaveDisplayValue.toHaveDisplayValue;
}
});
Object.defineProperty(exports, "toHaveErrorMessage", {
enumerable: true,
get: function () {
return _toHaveErrormessage.toHaveErrorMessage;
}
});
Object.defineProperty(exports, "toHaveFocus", {
enumerable: true,
get: function () {
return _toHaveFocus.toHaveFocus;
}
});
Object.defineProperty(exports, "toHaveFormValues", {
enumerable: true,
get: function () {
return _toHaveFormValues.toHaveFormValues;
}
});
Object.defineProperty(exports, "toHaveStyle", {
enumerable: true,
get: function () {
return _toHaveStyle.toHaveStyle;
}
});
Object.defineProperty(exports, "toHaveTextContent", {
enumerable: true,
get: function () {
return _toHaveTextContent.toHaveTextContent;
}
});
Object.defineProperty(exports, "toHaveValue", {
enumerable: true,
get: function () {
return _toHaveValue.toHaveValue;
}
});
var _toBeInTheDom = require("./to-be-in-the-dom");
var _toBeInTheDocument = require("./to-be-in-the-document");
var _toBeEmpty = require("./to-be-empty");
var _toBeEmptyDomElement = require("./to-be-empty-dom-element");
var _toContainElement = require("./to-contain-element");
var _toContainHtml = require("./to-contain-html");
var _toHaveTextContent = require("./to-have-text-content");
var _toHaveAccessibleDescription = require("./to-have-accessible-description");
var _toHaveAccessibleErrormessage = require("./to-have-accessible-errormessage");
var _toHaveAccessibleName = require("./to-have-accessible-name");
var _toHaveAttribute = require("./to-have-attribute");
var _toHaveClass = require("./to-have-class");
var _toHaveStyle = require("./to-have-style");
var _toHaveFocus = require("./to-have-focus");
var _toHaveFormValues = require("./to-have-form-values");
var _toBeVisible = require("./to-be-visible");
var _toBeDisabled = require("./to-be-disabled");
var _toBeRequired = require("./to-be-required");
var _toBeInvalid = require("./to-be-invalid");
var _toHaveValue = require("./to-have-value");
var _toHaveDisplayValue = require("./to-have-display-value");
var _toBeChecked = require("./to-be-checked");
var _toBePartiallyChecked = require("./to-be-partially-checked");
var _toHaveDescription = require("./to-have-description");
var _toHaveErrormessage = require("./to-have-errormessage");

View File

@@ -0,0 +1,46 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBeChecked = toBeChecked;
var _ariaQuery = require("aria-query");
var _utils = require("./utils");
function toBeChecked(element) {
(0, _utils.checkHtmlElement)(element, toBeChecked, this);
const isValidInput = () => {
return element.tagName.toLowerCase() === 'input' && ['checkbox', 'radio'].includes(element.type);
};
const isValidAriaElement = () => {
return roleSupportsChecked(element.getAttribute('role')) && ['true', 'false'].includes(element.getAttribute('aria-checked'));
};
if (!isValidInput() && !isValidAriaElement()) {
return {
pass: false,
message: () => `only inputs with type="checkbox" or type="radio" or elements with ${supportedRolesSentence()} and a valid aria-checked attribute can be used with .toBeChecked(). Use .toHaveValue() instead`
};
}
const isChecked = () => {
if (isValidInput()) return element.checked;
return element.getAttribute('aria-checked') === 'true';
};
return {
pass: isChecked(),
message: () => {
const is = isChecked() ? 'is' : 'is not';
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeChecked`, 'element', ''), '', `Received element ${is} checked:`, ` ${this.utils.printReceived(element.cloneNode(false))}`].join('\n');
}
};
}
function supportedRolesSentence() {
return (0, _utils.toSentence)(supportedRoles().map(role => `role="${role}"`), {
lastWordConnector: ' or '
});
}
function supportedRoles() {
return _ariaQuery.roles.keys().filter(roleSupportsChecked);
}
function roleSupportsChecked(role) {
var _roles$get;
return ((_roles$get = _ariaQuery.roles.get(role)) == null ? void 0 : _roles$get.props['aria-checked']) !== undefined;
}

View File

@@ -0,0 +1,70 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBeDisabled = toBeDisabled;
exports.toBeEnabled = toBeEnabled;
var _utils = require("./utils");
// form elements that support 'disabled'
const FORM_TAGS = ['fieldset', 'input', 'select', 'optgroup', 'option', 'button', 'textarea'];
/*
* According to specification:
* If <fieldset> is disabled, the form controls that are its descendants,
* except descendants of its first optional <legend> element, are disabled
*
* https://html.spec.whatwg.org/multipage/form-elements.html#concept-fieldset-disabled
*
* This method tests whether element is first legend child of fieldset parent
*/
function isFirstLegendChildOfFieldset(element, parent) {
return (0, _utils.getTag)(element) === 'legend' && (0, _utils.getTag)(parent) === 'fieldset' && element.isSameNode(Array.from(parent.children).find(child => (0, _utils.getTag)(child) === 'legend'));
}
function isElementDisabledByParent(element, parent) {
return isElementDisabled(parent) && !isFirstLegendChildOfFieldset(element, parent);
}
function isCustomElement(tag) {
return tag.includes('-');
}
/*
* Only certain form elements and custom elements can actually be disabled:
* https://html.spec.whatwg.org/multipage/semantics-other.html#disabled-elements
*/
function canElementBeDisabled(element) {
const tag = (0, _utils.getTag)(element);
return FORM_TAGS.includes(tag) || isCustomElement(tag);
}
function isElementDisabled(element) {
return canElementBeDisabled(element) && element.hasAttribute('disabled');
}
function isAncestorDisabled(element) {
const parent = element.parentElement;
return Boolean(parent) && (isElementDisabledByParent(element, parent) || isAncestorDisabled(parent));
}
function isElementOrAncestorDisabled(element) {
return canElementBeDisabled(element) && (isElementDisabled(element) || isAncestorDisabled(element));
}
function toBeDisabled(element) {
(0, _utils.checkHtmlElement)(element, toBeDisabled, this);
const isDisabled = isElementOrAncestorDisabled(element);
return {
pass: isDisabled,
message: () => {
const is = isDisabled ? 'is' : 'is not';
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeDisabled`, 'element', ''), '', `Received element ${is} disabled:`, ` ${this.utils.printReceived(element.cloneNode(false))}`].join('\n');
}
};
}
function toBeEnabled(element) {
(0, _utils.checkHtmlElement)(element, toBeEnabled, this);
const isEnabled = !isElementOrAncestorDisabled(element);
return {
pass: isEnabled,
message: () => {
const is = isEnabled ? 'is' : 'is not';
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeEnabled`, 'element', ''), '', `Received element ${is} enabled:`, ` ${this.utils.printReceived(element.cloneNode(false))}`].join('\n');
}
};
}

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBeEmptyDOMElement = toBeEmptyDOMElement;
var _utils = require("./utils");
function toBeEmptyDOMElement(element) {
(0, _utils.checkHtmlElement)(element, toBeEmptyDOMElement, this);
return {
pass: isEmptyElement(element),
message: () => {
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeEmptyDOMElement`, 'element', ''), '', 'Received:', ` ${this.utils.printReceived(element.innerHTML)}`].join('\n');
}
};
}
/**
* Identifies if an element doesn't contain child nodes (excluding comments)
* Node.COMMENT_NODE can't be used because of the following issue
* https://github.com/jsdom/jsdom/issues/2220
*
* @param {*} element an HtmlElement or SVGElement
* @return {*} true if the element only contains comments or none
*/
function isEmptyElement(element) {
const nonCommentChildNodes = [...element.childNodes].filter(node => node.nodeType !== 8);
return nonCommentChildNodes.length === 0;
}

View File

@@ -0,0 +1,17 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBeEmpty = toBeEmpty;
var _utils = require("./utils");
function toBeEmpty(element) {
(0, _utils.deprecate)('toBeEmpty', 'Please use instead toBeEmptyDOMElement for finding empty nodes in the DOM.');
(0, _utils.checkHtmlElement)(element, toBeEmpty, this);
return {
pass: element.innerHTML === '',
message: () => {
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeEmpty`, 'element', ''), '', 'Received:', ` ${this.utils.printReceived(element.innerHTML)}`].join('\n');
}
};
}

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBeInTheDocument = toBeInTheDocument;
var _utils = require("./utils");
function toBeInTheDocument(element) {
if (element !== null || !this.isNot) {
(0, _utils.checkHtmlElement)(element, toBeInTheDocument, this);
}
const pass = element === null ? false : element.ownerDocument === element.getRootNode({
composed: true
});
const errorFound = () => {
return `expected document not to contain element, found ${this.utils.stringify(element.cloneNode(true))} instead`;
};
const errorNotFound = () => {
return `element could not be found in the document`;
};
return {
pass,
message: () => {
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeInTheDocument`, 'element', ''), '',
// eslint-disable-next-line @babel/new-cap
this.utils.RECEIVED_COLOR(this.isNot ? errorFound() : errorNotFound())].join('\n');
}
};
}

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBeInTheDOM = toBeInTheDOM;
var _utils = require("./utils");
function toBeInTheDOM(element, container) {
(0, _utils.deprecate)('toBeInTheDOM', 'Please use toBeInTheDocument for searching the entire document and toContainElement for searching a specific container.');
if (element) {
(0, _utils.checkHtmlElement)(element, toBeInTheDOM, this);
}
if (container) {
(0, _utils.checkHtmlElement)(container, toBeInTheDOM, this);
}
return {
pass: container ? container.contains(element) : !!element,
message: () => {
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeInTheDOM`, 'element', ''), '', 'Received:', ` ${this.utils.printReceived(element ? element.cloneNode(false) : element)}`].join('\n');
}
};
}

View File

@@ -0,0 +1,45 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBeInvalid = toBeInvalid;
exports.toBeValid = toBeValid;
var _utils = require("./utils");
const FORM_TAGS = ['form', 'input', 'select', 'textarea'];
function isElementHavingAriaInvalid(element) {
return element.hasAttribute('aria-invalid') && element.getAttribute('aria-invalid') !== 'false';
}
function isSupportsValidityMethod(element) {
return FORM_TAGS.includes((0, _utils.getTag)(element));
}
function isElementInvalid(element) {
const isHaveAriaInvalid = isElementHavingAriaInvalid(element);
if (isSupportsValidityMethod(element)) {
return isHaveAriaInvalid || !element.checkValidity();
} else {
return isHaveAriaInvalid;
}
}
function toBeInvalid(element) {
(0, _utils.checkHtmlElement)(element, toBeInvalid, this);
const isInvalid = isElementInvalid(element);
return {
pass: isInvalid,
message: () => {
const is = isInvalid ? 'is' : 'is not';
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeInvalid`, 'element', ''), '', `Received element ${is} currently invalid:`, ` ${this.utils.printReceived(element.cloneNode(false))}`].join('\n');
}
};
}
function toBeValid(element) {
(0, _utils.checkHtmlElement)(element, toBeValid, this);
const isValid = !isElementInvalid(element);
return {
pass: isValid,
message: () => {
const is = isValid ? 'is' : 'is not';
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeValid`, 'element', ''), '', `Received element ${is} currently valid:`, ` ${this.utils.printReceived(element.cloneNode(false))}`].join('\n');
}
};
}

View File

@@ -0,0 +1,36 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBePartiallyChecked = toBePartiallyChecked;
var _utils = require("./utils");
function toBePartiallyChecked(element) {
(0, _utils.checkHtmlElement)(element, toBePartiallyChecked, this);
const isValidInput = () => {
return element.tagName.toLowerCase() === 'input' && element.type === 'checkbox';
};
const isValidAriaElement = () => {
return element.getAttribute('role') === 'checkbox';
};
if (!isValidInput() && !isValidAriaElement()) {
return {
pass: false,
message: () => 'only inputs with type="checkbox" or elements with role="checkbox" and a valid aria-checked attribute can be used with .toBePartiallyChecked(). Use .toHaveValue() instead'
};
}
const isPartiallyChecked = () => {
const isAriaMixed = element.getAttribute('aria-checked') === 'mixed';
if (isValidInput()) {
return element.indeterminate || isAriaMixed;
}
return isAriaMixed;
};
return {
pass: isPartiallyChecked(),
message: () => {
const is = isPartiallyChecked() ? 'is' : 'is not';
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBePartiallyChecked`, 'element', ''), '', `Received element ${is} partially checked:`, ` ${this.utils.printReceived(element.cloneNode(false))}`].join('\n');
}
};
}

View File

@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBeRequired = toBeRequired;
var _utils = require("./utils");
// form elements that support 'required'
const FORM_TAGS = ['select', 'textarea'];
const ARIA_FORM_TAGS = ['input', 'select', 'textarea'];
const UNSUPPORTED_INPUT_TYPES = ['color', 'hidden', 'range', 'submit', 'image', 'reset'];
const SUPPORTED_ARIA_ROLES = ['combobox', 'gridcell', 'radiogroup', 'spinbutton', 'tree'];
function isRequiredOnFormTagsExceptInput(element) {
return FORM_TAGS.includes((0, _utils.getTag)(element)) && element.hasAttribute('required');
}
function isRequiredOnSupportedInput(element) {
return (0, _utils.getTag)(element) === 'input' && element.hasAttribute('required') && (element.hasAttribute('type') && !UNSUPPORTED_INPUT_TYPES.includes(element.getAttribute('type')) || !element.hasAttribute('type'));
}
function isElementRequiredByARIA(element) {
return element.hasAttribute('aria-required') && element.getAttribute('aria-required') === 'true' && (ARIA_FORM_TAGS.includes((0, _utils.getTag)(element)) || element.hasAttribute('role') && SUPPORTED_ARIA_ROLES.includes(element.getAttribute('role')));
}
function toBeRequired(element) {
(0, _utils.checkHtmlElement)(element, toBeRequired, this);
const isRequired = isRequiredOnFormTagsExceptInput(element) || isRequiredOnSupportedInput(element) || isElementRequiredByARIA(element);
return {
pass: isRequired,
message: () => {
const is = isRequired ? 'is' : 'is not';
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeRequired`, 'element', ''), '', `Received element ${is} required:`, ` ${this.utils.printReceived(element.cloneNode(false))}`].join('\n');
}
};
}

View File

@@ -0,0 +1,44 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toBeVisible = toBeVisible;
var _utils = require("./utils");
function isStyleVisible(element) {
const {
getComputedStyle
} = element.ownerDocument.defaultView;
const {
display,
visibility,
opacity
} = getComputedStyle(element);
return display !== 'none' && visibility !== 'hidden' && visibility !== 'collapse' && opacity !== '0' && opacity !== 0;
}
function isAttributeVisible(element, previousElement) {
let detailsVisibility;
if (previousElement) {
detailsVisibility = element.nodeName === 'DETAILS' && previousElement.nodeName !== 'SUMMARY' ? element.hasAttribute('open') : true;
} else {
detailsVisibility = element.nodeName === 'DETAILS' ? element.hasAttribute('open') : true;
}
return !element.hasAttribute('hidden') && detailsVisibility;
}
function isElementVisible(element, previousElement) {
return isStyleVisible(element) && isAttributeVisible(element, previousElement) && (!element.parentElement || isElementVisible(element.parentElement, element));
}
function toBeVisible(element) {
(0, _utils.checkHtmlElement)(element, toBeVisible, this);
const isInDocument = element.ownerDocument === element.getRootNode({
composed: true
});
const isVisible = isInDocument && isElementVisible(element);
return {
pass: isVisible,
message: () => {
const is = isVisible ? 'is' : 'is not';
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toBeVisible`, 'element', ''), '', `Received element ${is} visible${isInDocument ? '' : ' (element is not in the document)'}:`, ` ${this.utils.printReceived(element.cloneNode(false))}`].join('\n');
}
};
}

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toContainElement = toContainElement;
var _utils = require("./utils");
function toContainElement(container, element) {
(0, _utils.checkHtmlElement)(container, toContainElement, this);
if (element !== null) {
(0, _utils.checkHtmlElement)(element, toContainElement, this);
}
return {
pass: container.contains(element),
message: () => {
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toContainElement`, 'element', 'element'), '',
// eslint-disable-next-line @babel/new-cap
this.utils.RECEIVED_COLOR(`${this.utils.stringify(container.cloneNode(false))} ${this.isNot ? 'contains:' : 'does not contain:'} ${this.utils.stringify(element ? element.cloneNode(false) : element)}
`)].join('\n');
}
};
}

View File

@@ -0,0 +1,26 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toContainHTML = toContainHTML;
var _utils = require("./utils");
function getNormalizedHtml(container, htmlText) {
const div = container.ownerDocument.createElement('div');
div.innerHTML = htmlText;
return div.innerHTML;
}
function toContainHTML(container, htmlText) {
(0, _utils.checkHtmlElement)(container, toContainHTML, this);
if (typeof htmlText !== 'string') {
throw new Error(`.toContainHTML() expects a string value, got ${htmlText}`);
}
return {
pass: container.outerHTML.includes(getNormalizedHtml(container, htmlText)),
message: () => {
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toContainHTML`, 'element', ''), 'Expected:',
// eslint-disable-next-line @babel/new-cap
` ${this.utils.EXPECTED_COLOR(htmlText)}`, 'Received:', ` ${this.utils.printReceived(container.cloneNode(true))}`].join('\n');
}
};
}

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveAccessibleDescription = toHaveAccessibleDescription;
var _domAccessibilityApi = require("dom-accessibility-api");
var _utils = require("./utils");
function toHaveAccessibleDescription(htmlElement, expectedAccessibleDescription) {
(0, _utils.checkHtmlElement)(htmlElement, toHaveAccessibleDescription, this);
const actualAccessibleDescription = (0, _domAccessibilityApi.computeAccessibleDescription)(htmlElement);
const missingExpectedValue = arguments.length === 1;
let pass = false;
if (missingExpectedValue) {
// When called without an expected value we only want to validate that the element has an
// accessible description, whatever it may be.
pass = actualAccessibleDescription !== '';
} else {
pass = expectedAccessibleDescription instanceof RegExp ? expectedAccessibleDescription.test(actualAccessibleDescription) : this.equals(actualAccessibleDescription, expectedAccessibleDescription);
}
return {
pass,
message: () => {
const to = this.isNot ? 'not to' : 'to';
return (0, _utils.getMessage)(this, this.utils.matcherHint(`${this.isNot ? '.not' : ''}.${toHaveAccessibleDescription.name}`, 'element', ''), `Expected element ${to} have accessible description`, expectedAccessibleDescription, 'Received', actualAccessibleDescription);
}
};
}

View File

@@ -0,0 +1,50 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveAccessibleErrorMessage = toHaveAccessibleErrorMessage;
var _utils = require("./utils");
const ariaInvalidName = 'aria-invalid';
const validStates = ['false'];
// See `aria-errormessage` spec at https://www.w3.org/TR/wai-aria-1.2/#aria-errormessage
function toHaveAccessibleErrorMessage(htmlElement, expectedAccessibleErrorMessage) {
var _htmlElement$ownerDoc, _htmlElement$ownerDoc2;
(0, _utils.checkHtmlElement)(htmlElement, toHaveAccessibleErrorMessage, this);
const to = this.isNot ? 'not to' : 'to';
const method = this.isNot ? '.not.toHaveAccessibleErrorMessage' : '.toHaveAccessibleErrorMessage';
// Enforce Valid Id
const errormessageId = htmlElement.getAttribute('aria-errormessage');
const errormessageIdInvalid = !!errormessageId && /\s+/.test(errormessageId);
if (errormessageIdInvalid) {
return {
pass: false,
message: () => {
return (0, _utils.getMessage)(this, this.utils.matcherHint(method, 'element'), "Expected element's `aria-errormessage` attribute to be empty or a single, valid ID", '', 'Received', `aria-errormessage="${errormessageId}"`);
}
};
}
// See `aria-invalid` spec at https://www.w3.org/TR/wai-aria-1.2/#aria-invalid
const ariaInvalidVal = htmlElement.getAttribute(ariaInvalidName);
const fieldValid = !htmlElement.hasAttribute(ariaInvalidName) || validStates.includes(ariaInvalidVal);
// Enforce Valid `aria-invalid` Attribute
if (fieldValid) {
return {
pass: false,
message: () => {
return (0, _utils.getMessage)(this, this.utils.matcherHint(method, 'element'), 'Expected element to be marked as invalid with attribute', `${ariaInvalidName}="${String(true)}"`, 'Received', htmlElement.hasAttribute('aria-invalid') ? `${ariaInvalidName}="${htmlElement.getAttribute(ariaInvalidName)}` : null);
}
};
}
const error = (0, _utils.normalize)((_htmlElement$ownerDoc = (_htmlElement$ownerDoc2 = htmlElement.ownerDocument.getElementById(errormessageId)) == null ? void 0 : _htmlElement$ownerDoc2.textContent) != null ? _htmlElement$ownerDoc : '');
return {
pass: expectedAccessibleErrorMessage === undefined ? Boolean(error) : expectedAccessibleErrorMessage instanceof RegExp ? expectedAccessibleErrorMessage.test(error) : this.equals(error, expectedAccessibleErrorMessage),
message: () => {
return (0, _utils.getMessage)(this, this.utils.matcherHint(method, 'element'), `Expected element ${to} have accessible error message`, expectedAccessibleErrorMessage != null ? expectedAccessibleErrorMessage : '', 'Received', error);
}
};
}

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveAccessibleName = toHaveAccessibleName;
var _domAccessibilityApi = require("dom-accessibility-api");
var _utils = require("./utils");
function toHaveAccessibleName(htmlElement, expectedAccessibleName) {
(0, _utils.checkHtmlElement)(htmlElement, toHaveAccessibleName, this);
const actualAccessibleName = (0, _domAccessibilityApi.computeAccessibleName)(htmlElement);
const missingExpectedValue = arguments.length === 1;
let pass = false;
if (missingExpectedValue) {
// When called without an expected value we only want to validate that the element has an
// accessible name, whatever it may be.
pass = actualAccessibleName !== '';
} else {
pass = expectedAccessibleName instanceof RegExp ? expectedAccessibleName.test(actualAccessibleName) : this.equals(actualAccessibleName, expectedAccessibleName);
}
return {
pass,
message: () => {
const to = this.isNot ? 'not to' : 'to';
return (0, _utils.getMessage)(this, this.utils.matcherHint(`${this.isNot ? '.not' : ''}.${toHaveAccessibleName.name}`, 'element', ''), `Expected element ${to} have accessible name`, expectedAccessibleName, 'Received', actualAccessibleName);
}
};
}

View File

@@ -0,0 +1,31 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveAttribute = toHaveAttribute;
var _utils = require("./utils");
function printAttribute(stringify, name, value) {
return value === undefined ? name : `${name}=${stringify(value)}`;
}
function getAttributeComment(stringify, name, value) {
return value === undefined ? `element.hasAttribute(${stringify(name)})` : `element.getAttribute(${stringify(name)}) === ${stringify(value)}`;
}
function toHaveAttribute(htmlElement, name, expectedValue) {
(0, _utils.checkHtmlElement)(htmlElement, toHaveAttribute, this);
const isExpectedValuePresent = expectedValue !== undefined;
const hasAttribute = htmlElement.hasAttribute(name);
const receivedValue = htmlElement.getAttribute(name);
return {
pass: isExpectedValuePresent ? hasAttribute && this.equals(receivedValue, expectedValue) : hasAttribute,
message: () => {
const to = this.isNot ? 'not to' : 'to';
const receivedAttribute = hasAttribute ? printAttribute(this.utils.stringify, name, receivedValue) : null;
const matcher = this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toHaveAttribute`, 'element', this.utils.printExpected(name), {
secondArgument: isExpectedValuePresent ? this.utils.printExpected(expectedValue) : undefined,
comment: getAttributeComment(this.utils.stringify, name, expectedValue)
});
return (0, _utils.getMessage)(this, matcher, `Expected the element ${to} have attribute`, printAttribute(this.utils.stringify, name, expectedValue), 'Received', receivedAttribute);
}
};
}

View File

@@ -0,0 +1,61 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveClass = toHaveClass;
var _utils = require("./utils");
function getExpectedClassNamesAndOptions(params) {
const lastParam = params.pop();
let expectedClassNames, options;
if (typeof lastParam === 'object') {
expectedClassNames = params;
options = lastParam;
} else {
expectedClassNames = params.concat(lastParam);
options = {
exact: false
};
}
return {
expectedClassNames,
options
};
}
function splitClassNames(str) {
if (!str) {
return [];
}
return str.split(/\s+/).filter(s => s.length > 0);
}
function isSubset(subset, superset) {
return subset.every(item => superset.includes(item));
}
function toHaveClass(htmlElement, ...params) {
(0, _utils.checkHtmlElement)(htmlElement, toHaveClass, this);
const {
expectedClassNames,
options
} = getExpectedClassNamesAndOptions(params);
const received = splitClassNames(htmlElement.getAttribute('class'));
const expected = expectedClassNames.reduce((acc, className) => acc.concat(splitClassNames(className)), []);
if (options.exact) {
return {
pass: isSubset(expected, received) && expected.length === received.length,
message: () => {
const to = this.isNot ? 'not to' : 'to';
return (0, _utils.getMessage)(this, this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toHaveClass`, 'element', this.utils.printExpected(expected.join(' '))), `Expected the element ${to} have EXACTLY defined classes`, expected.join(' '), 'Received', received.join(' '));
}
};
}
return expected.length > 0 ? {
pass: isSubset(expected, received),
message: () => {
const to = this.isNot ? 'not to' : 'to';
return (0, _utils.getMessage)(this, this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toHaveClass`, 'element', this.utils.printExpected(expected.join(' '))), `Expected the element ${to} have class`, expected.join(' '), 'Received', received.join(' '));
}
} : {
pass: this.isNot ? received.length > 0 : false,
message: () => this.isNot ? (0, _utils.getMessage)(this, this.utils.matcherHint('.not.toHaveClass', 'element', ''), 'Expected the element to have classes', '(none)', 'Received', received.join(' ')) : [this.utils.matcherHint(`.toHaveClass`, 'element'), 'At least one expected class must be provided.'].join('\n')
};
}

View File

@@ -0,0 +1,28 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveDescription = toHaveDescription;
var _utils = require("./utils");
// See algoritm: https://www.w3.org/TR/accname-1.1/#mapping_additional_nd_description
function toHaveDescription(htmlElement, checkWith) {
(0, _utils.deprecate)('toHaveDescription', 'Please use toHaveAccessibleDescription.');
(0, _utils.checkHtmlElement)(htmlElement, toHaveDescription, this);
const expectsDescription = checkWith !== undefined;
const descriptionIDRaw = htmlElement.getAttribute('aria-describedby') || '';
const descriptionIDs = descriptionIDRaw.split(/\s+/).filter(Boolean);
let description = '';
if (descriptionIDs.length > 0) {
const document = htmlElement.ownerDocument;
const descriptionEls = descriptionIDs.map(descriptionID => document.getElementById(descriptionID)).filter(Boolean);
description = (0, _utils.normalize)(descriptionEls.map(el => el.textContent).join(' '));
}
return {
pass: expectsDescription ? checkWith instanceof RegExp ? checkWith.test(description) : this.equals(description, checkWith) : Boolean(description),
message: () => {
const to = this.isNot ? 'not to' : 'to';
return (0, _utils.getMessage)(this, this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toHaveDescription`, 'element', ''), `Expected the element ${to} have description`, this.utils.printExpected(checkWith), 'Received', this.utils.printReceived(description));
}
};
}

View File

@@ -0,0 +1,32 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveDisplayValue = toHaveDisplayValue;
var _utils = require("./utils");
function toHaveDisplayValue(htmlElement, expectedValue) {
(0, _utils.checkHtmlElement)(htmlElement, toHaveDisplayValue, this);
const tagName = htmlElement.tagName.toLowerCase();
if (!['select', 'input', 'textarea'].includes(tagName)) {
throw new Error('.toHaveDisplayValue() currently supports only input, textarea or select elements, try with another matcher instead.');
}
if (tagName === 'input' && ['radio', 'checkbox'].includes(htmlElement.type)) {
throw new Error(`.toHaveDisplayValue() currently does not support input[type="${htmlElement.type}"], try with another matcher instead.`);
}
const values = getValues(tagName, htmlElement);
const expectedValues = getExpectedValues(expectedValue);
const numberOfMatchesWithValues = expectedValues.filter(expected => values.some(value => expected instanceof RegExp ? expected.test(value) : this.equals(value, String(expected)))).length;
const matchedWithAllValues = numberOfMatchesWithValues === values.length;
const matchedWithAllExpectedValues = numberOfMatchesWithValues === expectedValues.length;
return {
pass: matchedWithAllValues && matchedWithAllExpectedValues,
message: () => (0, _utils.getMessage)(this, this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toHaveDisplayValue`, 'element', ''), `Expected element ${this.isNot ? 'not ' : ''}to have display value`, expectedValue, 'Received', values)
};
}
function getValues(tagName, htmlElement) {
return tagName === 'select' ? Array.from(htmlElement).filter(option => option.selected).map(option => option.textContent) : [htmlElement.value];
}
function getExpectedValues(expectedValue) {
return expectedValue instanceof Array ? expectedValue : [expectedValue];
}

View File

@@ -0,0 +1,37 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveErrorMessage = toHaveErrorMessage;
var _utils = require("./utils");
// See aria-errormessage spec https://www.w3.org/TR/wai-aria-1.2/#aria-errormessage
function toHaveErrorMessage(htmlElement, checkWith) {
(0, _utils.deprecate)('toHaveErrorMessage', 'Please use toHaveAccessibleErrorMessage.');
(0, _utils.checkHtmlElement)(htmlElement, toHaveErrorMessage, this);
if (!htmlElement.hasAttribute('aria-invalid') || htmlElement.getAttribute('aria-invalid') === 'false') {
const not = this.isNot ? '.not' : '';
return {
pass: false,
message: () => {
return (0, _utils.getMessage)(this, this.utils.matcherHint(`${not}.toHaveErrorMessage`, 'element', ''), `Expected the element to have invalid state indicated by`, 'aria-invalid="true"', 'Received', htmlElement.hasAttribute('aria-invalid') ? `aria-invalid="${htmlElement.getAttribute('aria-invalid')}"` : this.utils.printReceived(''));
}
};
}
const expectsErrorMessage = checkWith !== undefined;
const errormessageIDRaw = htmlElement.getAttribute('aria-errormessage') || '';
const errormessageIDs = errormessageIDRaw.split(/\s+/).filter(Boolean);
let errormessage = '';
if (errormessageIDs.length > 0) {
const document = htmlElement.ownerDocument;
const errormessageEls = errormessageIDs.map(errormessageID => document.getElementById(errormessageID)).filter(Boolean);
errormessage = (0, _utils.normalize)(errormessageEls.map(el => el.textContent).join(' '));
}
return {
pass: expectsErrorMessage ? checkWith instanceof RegExp ? checkWith.test(errormessage) : this.equals(errormessage, checkWith) : Boolean(errormessage),
message: () => {
const to = this.isNot ? 'not to' : 'to';
return (0, _utils.getMessage)(this, this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toHaveErrorMessage`, 'element', ''), `Expected the element ${to} have error message`, this.utils.printExpected(checkWith), 'Received', this.utils.printReceived(errormessage));
}
};
}

View File

@@ -0,0 +1,16 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveFocus = toHaveFocus;
var _utils = require("./utils");
function toHaveFocus(element) {
(0, _utils.checkHtmlElement)(element, toHaveFocus, this);
return {
pass: element.ownerDocument.activeElement === element,
message: () => {
return [this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toHaveFocus`, 'element', ''), '', ...(this.isNot ? ['Received element is focused:', ` ${this.utils.printReceived(element)}`] : ['Expected element with focus:', ` ${this.utils.printExpected(element)}`, 'Received element with focus:', ` ${this.utils.printReceived(element.ownerDocument.activeElement)}`])].join('\n');
}
};
}

View File

@@ -0,0 +1,76 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveFormValues = toHaveFormValues;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _isEqualWith = _interopRequireDefault(require("lodash/isEqualWith"));
var _uniq = _interopRequireDefault(require("lodash/uniq"));
var _css = _interopRequireDefault(require("css.escape"));
var _utils = require("./utils");
// Returns the combined value of several elements that have the same name
// e.g. radio buttons or groups of checkboxes
function getMultiElementValue(elements) {
const types = (0, _uniq.default)(elements.map(element => element.type));
if (types.length !== 1) {
throw new Error('Multiple form elements with the same name must be of the same type');
}
switch (types[0]) {
case 'radio':
{
const theChosenOne = elements.find(radio => radio.checked);
return theChosenOne ? theChosenOne.value : undefined;
}
case 'checkbox':
return elements.filter(checkbox => checkbox.checked).map(checkbox => checkbox.value);
default:
// NOTE: Not even sure this is a valid use case, but just in case...
return elements.map(element => element.value);
}
}
function getFormValue(container, name) {
const elements = [...container.querySelectorAll(`[name="${(0, _css.default)(name)}"]`)];
/* istanbul ignore if */
if (elements.length === 0) {
return undefined; // shouldn't happen, but just in case
}
switch (elements.length) {
case 1:
return (0, _utils.getSingleElementValue)(elements[0]);
default:
return getMultiElementValue(elements);
}
}
// Strips the `[]` suffix off a form value name
function getPureName(name) {
return /\[\]$/.test(name) ? name.slice(0, -2) : name;
}
function getAllFormValues(container) {
const names = Array.from(container.elements).map(element => element.name);
return names.reduce((obj, name) => (0, _extends2.default)({}, obj, {
[getPureName(name)]: getFormValue(container, name)
}), {});
}
function toHaveFormValues(formElement, expectedValues) {
(0, _utils.checkHtmlElement)(formElement, toHaveFormValues, this);
if (!formElement.elements) {
// TODO: Change condition to use instanceof against the appropriate element classes instead
throw new Error('toHaveFormValues must be called on a form or a fieldset');
}
const formValues = getAllFormValues(formElement);
return {
pass: Object.entries(expectedValues).every(([name, expectedValue]) => (0, _isEqualWith.default)(formValues[name], expectedValue, _utils.compareArraysAsSet)),
message: () => {
const to = this.isNot ? 'not to' : 'to';
const matcher = `${this.isNot ? '.not' : ''}.toHaveFormValues`;
const commonKeyValues = Object.keys(formValues).filter(key => expectedValues.hasOwnProperty(key)).reduce((obj, key) => (0, _extends2.default)({}, obj, {
[key]: formValues[key]
}), {});
return [this.utils.matcherHint(matcher, 'element', ''), `Expected the element ${to} have form values`, this.utils.diff(expectedValues, commonKeyValues)].join('\n\n');
}
};
}

View File

@@ -0,0 +1,53 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveStyle = toHaveStyle;
var _chalk = _interopRequireDefault(require("chalk"));
var _utils = require("./utils");
function getStyleDeclaration(document, css) {
const styles = {};
// The next block is necessary to normalize colors
const copy = document.createElement('div');
Object.keys(css).forEach(property => {
copy.style[property] = css[property];
styles[property] = copy.style[property];
});
return styles;
}
function isSubset(styles, computedStyle) {
return !!Object.keys(styles).length && Object.entries(styles).every(([prop, value]) => computedStyle[prop] === value || computedStyle.getPropertyValue(prop.toLowerCase()) === value);
}
function printoutStyles(styles) {
return Object.keys(styles).sort().map(prop => `${prop}: ${styles[prop]};`).join('\n');
}
// Highlights only style rules that were expected but were not found in the
// received computed styles
function expectedDiff(diffFn, expected, computedStyles) {
const received = Array.from(computedStyles).filter(prop => expected[prop] !== undefined).reduce((obj, prop) => Object.assign(obj, {
[prop]: computedStyles.getPropertyValue(prop)
}), {});
const diffOutput = diffFn(printoutStyles(expected), printoutStyles(received));
// Remove the "+ Received" annotation because this is a one-way diff
return diffOutput.replace(`${_chalk.default.red('+ Received')}\n`, '');
}
function toHaveStyle(htmlElement, css) {
(0, _utils.checkHtmlElement)(htmlElement, toHaveStyle, this);
const parsedCSS = typeof css === 'object' ? css : (0, _utils.parseCSS)(css, toHaveStyle, this);
const {
getComputedStyle
} = htmlElement.ownerDocument.defaultView;
const expected = getStyleDeclaration(htmlElement.ownerDocument, parsedCSS);
const received = getComputedStyle(htmlElement);
return {
pass: isSubset(expected, received),
message: () => {
const matcher = `${this.isNot ? '.not' : ''}.toHaveStyle`;
return [this.utils.matcherHint(matcher, 'element', ''), expectedDiff(this.utils.diff, expected, received)].join('\n\n');
}
};
}

View File

@@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveTextContent = toHaveTextContent;
var _utils = require("./utils");
function toHaveTextContent(node, checkWith, options = {
normalizeWhitespace: true
}) {
(0, _utils.checkNode)(node, toHaveTextContent, this);
const textContent = options.normalizeWhitespace ? (0, _utils.normalize)(node.textContent) : node.textContent.replace(/\u00a0/g, ' '); // Replace &nbsp; with normal spaces
const checkingWithEmptyString = textContent !== '' && checkWith === '';
return {
pass: !checkingWithEmptyString && (0, _utils.matches)(textContent, checkWith),
message: () => {
const to = this.isNot ? 'not to' : 'to';
return (0, _utils.getMessage)(this, this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toHaveTextContent`, 'element', ''), checkingWithEmptyString ? `Checking with empty string will always match, use .toBeEmptyDOMElement() instead` : `Expected element ${to} have text content`, checkWith, 'Received', textContent);
}
};
}

View File

@@ -0,0 +1,31 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.toHaveValue = toHaveValue;
var _isEqualWith = _interopRequireDefault(require("lodash/isEqualWith"));
var _utils = require("./utils");
function toHaveValue(htmlElement, expectedValue) {
(0, _utils.checkHtmlElement)(htmlElement, toHaveValue, this);
if (htmlElement.tagName.toLowerCase() === 'input' && ['checkbox', 'radio'].includes(htmlElement.type)) {
throw new Error('input with type=checkbox or type=radio cannot be used with .toHaveValue(). Use .toBeChecked() for type=checkbox or .toHaveFormValues() instead');
}
const receivedValue = (0, _utils.getSingleElementValue)(htmlElement);
const expectsValue = expectedValue !== undefined;
let expectedTypedValue = expectedValue;
let receivedTypedValue = receivedValue;
if (expectedValue == receivedValue && expectedValue !== receivedValue) {
expectedTypedValue = `${expectedValue} (${typeof expectedValue})`;
receivedTypedValue = `${receivedValue} (${typeof receivedValue})`;
}
return {
pass: expectsValue ? (0, _isEqualWith.default)(receivedValue, expectedValue, _utils.compareArraysAsSet) : Boolean(receivedValue),
message: () => {
const to = this.isNot ? 'not to' : 'to';
const matcher = this.utils.matcherHint(`${this.isNot ? '.not' : ''}.toHaveValue`, 'element', expectedValue);
return (0, _utils.getMessage)(this, matcher, `Expected the element ${to} have value`, expectsValue ? expectedTypedValue : '(any)', 'Received', receivedTypedValue);
}
};
}

View File

@@ -0,0 +1,188 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.NodeTypeError = exports.HtmlElementTypeError = void 0;
exports.checkHtmlElement = checkHtmlElement;
exports.checkNode = checkNode;
exports.compareArraysAsSet = compareArraysAsSet;
exports.deprecate = deprecate;
exports.getMessage = getMessage;
exports.getSingleElementValue = getSingleElementValue;
exports.getTag = getTag;
exports.matches = matches;
exports.normalize = normalize;
exports.parseCSS = parseCSS;
exports.toSentence = toSentence;
var _redent = _interopRequireDefault(require("redent"));
var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
var _cssTools = require("@adobe/css-tools");
class GenericTypeError extends Error {
constructor(expectedString, received, matcherFn, context) {
super();
/* istanbul ignore next */
if (Error.captureStackTrace) {
Error.captureStackTrace(this, matcherFn);
}
let withType = '';
try {
withType = context.utils.printWithType('Received', received, context.utils.printReceived);
} catch (e) {
// Can throw for Document:
// https://github.com/jsdom/jsdom/issues/2304
}
this.message = [context.utils.matcherHint(`${context.isNot ? '.not' : ''}.${matcherFn.name}`, 'received', ''), '',
// eslint-disable-next-line @babel/new-cap
`${context.utils.RECEIVED_COLOR('received')} value must ${expectedString}.`, withType].join('\n');
}
}
class HtmlElementTypeError extends GenericTypeError {
constructor(...args) {
super('be an HTMLElement or an SVGElement', ...args);
}
}
exports.HtmlElementTypeError = HtmlElementTypeError;
class NodeTypeError extends GenericTypeError {
constructor(...args) {
super('be a Node', ...args);
}
}
exports.NodeTypeError = NodeTypeError;
function checkHasWindow(htmlElement, ErrorClass, ...args) {
if (!htmlElement || !htmlElement.ownerDocument || !htmlElement.ownerDocument.defaultView) {
throw new ErrorClass(htmlElement, ...args);
}
}
function checkNode(node, ...args) {
checkHasWindow(node, NodeTypeError, ...args);
const window = node.ownerDocument.defaultView;
if (!(node instanceof window.Node)) {
throw new NodeTypeError(node, ...args);
}
}
function checkHtmlElement(htmlElement, ...args) {
checkHasWindow(htmlElement, HtmlElementTypeError, ...args);
const window = htmlElement.ownerDocument.defaultView;
if (!(htmlElement instanceof window.HTMLElement) && !(htmlElement instanceof window.SVGElement)) {
throw new HtmlElementTypeError(htmlElement, ...args);
}
}
class InvalidCSSError extends Error {
constructor(received, matcherFn, context) {
super();
/* istanbul ignore next */
if (Error.captureStackTrace) {
Error.captureStackTrace(this, matcherFn);
}
this.message = [received.message, '',
// eslint-disable-next-line @babel/new-cap
context.utils.RECEIVED_COLOR(`Failing css:`),
// eslint-disable-next-line @babel/new-cap
context.utils.RECEIVED_COLOR(`${received.css}`)].join('\n');
}
}
function parseCSS(css, ...args) {
const ast = (0, _cssTools.parse)(`selector { ${css} }`, {
silent: true
}).stylesheet;
if (ast.parsingErrors && ast.parsingErrors.length > 0) {
const {
reason,
line
} = ast.parsingErrors[0];
throw new InvalidCSSError({
css,
message: `Syntax error parsing expected css: ${reason} on line: ${line}`
}, ...args);
}
const parsedRules = ast.rules[0].declarations.filter(d => d.type === 'declaration').reduce((obj, {
property,
value
}) => Object.assign(obj, {
[property]: value
}), {});
return parsedRules;
}
function display(context, value) {
return typeof value === 'string' ? value : context.utils.stringify(value);
}
function getMessage(context, matcher, expectedLabel, expectedValue, receivedLabel, receivedValue) {
return [`${matcher}\n`,
// eslint-disable-next-line @babel/new-cap
`${expectedLabel}:\n${context.utils.EXPECTED_COLOR((0, _redent.default)(display(context, expectedValue), 2))}`,
// eslint-disable-next-line @babel/new-cap
`${receivedLabel}:\n${context.utils.RECEIVED_COLOR((0, _redent.default)(display(context, receivedValue), 2))}`].join('\n');
}
function matches(textToMatch, matcher) {
if (matcher instanceof RegExp) {
return matcher.test(textToMatch);
} else {
return textToMatch.includes(String(matcher));
}
}
function deprecate(name, replacementText) {
// Notify user that they are using deprecated functionality.
// eslint-disable-next-line no-console
console.warn(`Warning: ${name} has been deprecated and will be removed in future updates.`, replacementText);
}
function normalize(text) {
return text.replace(/\s+/g, ' ').trim();
}
function getTag(element) {
return element.tagName && element.tagName.toLowerCase();
}
function getSelectValue({
multiple,
options
}) {
const selectedOptions = [...options].filter(option => option.selected);
if (multiple) {
return [...selectedOptions].map(opt => opt.value);
}
/* istanbul ignore if */
if (selectedOptions.length === 0) {
return undefined; // Couldn't make this happen, but just in case
}
return selectedOptions[0].value;
}
function getInputValue(inputElement) {
switch (inputElement.type) {
case 'number':
return inputElement.value === '' ? null : Number(inputElement.value);
case 'checkbox':
return inputElement.checked;
default:
return inputElement.value;
}
}
function getSingleElementValue(element) {
/* istanbul ignore if */
if (!element) {
return undefined;
}
switch (element.tagName.toLowerCase()) {
case 'input':
return getInputValue(element);
case 'select':
return getSelectValue(element);
default:
return element.value;
}
}
function compareArraysAsSet(a, b) {
if (Array.isArray(a) && Array.isArray(b)) {
return (0, _isEqual.default)(new Set(a), new Set(b));
}
return undefined;
}
function toSentence(array, {
wordConnector = ', ',
lastWordConnector = ' and '
} = {}) {
return [array.slice(0, -1).join(wordConnector), array[array.length - 1]].join(array.length > 1 ? lastWordConnector : '');
}

View File

@@ -0,0 +1,2 @@
// eslint-disable-next-line
require('./dist/extend-expect')

View File

@@ -0,0 +1,2 @@
const matchers = require('./dist/matchers')
module.exports = matchers

View File

@@ -0,0 +1,80 @@
{
"name": "@testing-library/jest-dom",
"version": "5.17.0",
"description": "Custom jest matchers to test the state of the DOM",
"main": "dist/index.js",
"engines": {
"node": ">=8",
"npm": ">=6",
"yarn": ">=1"
},
"scripts": {
"build": "kcd-scripts build",
"format": "kcd-scripts format",
"lint": "kcd-scripts lint",
"setup": "npm install && npm run validate -s",
"test": "kcd-scripts test",
"test:update": "npm test -- --updateSnapshot --coverage",
"validate": "kcd-scripts validate"
},
"files": [
"dist",
"extend-expect.js",
"matchers.js"
],
"keywords": [
"testing",
"dom",
"jest",
"jsdom"
],
"author": "Ernesto Garcia <gnapse@gmail.com> (http://gnapse.github.io)",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.9.2",
"@types/testing-library__jest-dom": "^5.9.1",
"aria-query": "^5.0.0",
"chalk": "^3.0.0",
"@adobe/css-tools": "^4.0.1",
"css.escape": "^1.5.1",
"dom-accessibility-api": "^0.5.6",
"lodash": "^4.17.15",
"redent": "^3.0.0"
},
"devDependencies": {
"jest-environment-jsdom-sixteen": "^1.0.3",
"jest-watch-select-projects": "^2.0.0",
"jsdom": "^16.2.1",
"kcd-scripts": "^11.1.0",
"pretty-format": "^25.1.0"
},
"eslintConfig": {
"extends": "./node_modules/kcd-scripts/eslint.js",
"rules": {
"@babel/no-invalid-this": "off"
},
"overrides": [
{
"files": [
"src/__tests__/*.js"
],
"rules": {
"max-lines-per-function": "off"
}
}
]
},
"eslintIgnore": [
"node_modules",
"coverage",
"dist"
],
"repository": {
"type": "git",
"url": "https://github.com/testing-library/jest-dom"
},
"bugs": {
"url": "https://github.com/testing-library/jest-dom/issues"
},
"homepage": "https://github.com/testing-library/jest-dom#readme"
}

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

20
frontend/node_modules/@testing-library/react/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2017 Kent C. Dodds
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.

673
frontend/node_modules/@testing-library/react/README.md generated vendored Normal file
View File

@@ -0,0 +1,673 @@
<div align="center">
<h1>React Testing Library</h1>
<a href="https://www.emojione.com/emoji/1f410">
<img
height="80"
width="80"
alt="goat"
src="https://raw.githubusercontent.com/testing-library/react-testing-library/main/other/goat.png"
/>
</a>
<p>Simple and complete React DOM testing utilities that encourage good testing
practices.</p>
<br />
[**Read The Docs**](https://testing-library.com/react) |
[Edit the docs](https://github.com/testing-library/testing-library-docs)
<br />
</div>
<hr />
<!-- 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 -->
<div align="center">
<a href="https://testingjavascript.com">
<img
width="500"
alt="TestingJavaScript.com Learn the smart, efficient way to test any JavaScript application."
src="https://raw.githubusercontent.com/testing-library/react-testing-library/main/other/testingjavascript.jpg"
/>
</a>
</div>
## 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)
- [Suppressing unnecessary warnings on React DOM 16.8](#suppressing-unnecessary-warnings-on-react-dom-168)
- [Examples](#examples)
- [Basic Example](#basic-example)
- [Complex Example](#complex-example)
- [More Examples](#more-examples)
- [Hooks](#hooks)
- [Guiding Principles](#guiding-principles)
- [Docs](#docs)
- [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
You want to write maintainable tests for your React components. As a part of
this goal, you want your tests to avoid including implementation details of your
components and rather focus on making your tests give you the confidence for
which they are intended. As part of this, you want your testbase to be
maintainable in the long run so refactors of your components (changes to
implementation but not functionality) don't break your tests and slow you and
your team down.
## The solution
The `React Testing Library` is a very lightweight solution for testing React
components. It provides light utility functions on top of `react-dom` and
`react-dom/test-utils`, in a way that encourages better testing practices. Its
primary guiding principle is:
> [The more your tests resemble the way your software is used, the more
> confidence they can give you.][guiding-principle]
## Installation
This module is distributed via [npm][npm] which is bundled with [node][node] and
should be installed as one of your project's `devDependencies`:
```
npm install --save-dev @testing-library/react
```
or
for installation via [yarn][yarn]
```
yarn add --dev @testing-library/react
```
This library has `peerDependencies` listings for `react` and `react-dom`.
You may also be interested in installing `@testing-library/jest-dom` so you can
use [the custom jest matchers](https://github.com/testing-library/jest-dom).
> [**Docs**](https://testing-library.com/react)
### Suppressing unnecessary warnings on React DOM 16.8
There is a known compatibility issue with React DOM 16.8 where you will see the
following warning:
```
Warning: An update to ComponentName inside a test was not wrapped in act(...).
```
If you cannot upgrade to React DOM 16.9, you may suppress the warnings by adding
the following snippet to your test configuration
([learn more](https://github.com/testing-library/react-testing-library/issues/281)):
```js
// this is just a little hack to silence a warning that we'll get until we
// upgrade to 16.9. See also: https://github.com/facebook/react/pull/14853
const originalError = console.error
beforeAll(() => {
console.error = (...args) => {
if (/Warning.*not wrapped in act/.test(args[0])) {
return
}
originalError.call(console, ...args)
}
})
afterAll(() => {
console.error = originalError
})
```
## Examples
### Basic Example
```jsx
// hidden-message.js
import * as React from 'react'
// NOTE: React Testing Library works well with React Hooks and classes.
// Your tests will be the same regardless of how you write your components.
function HiddenMessage({children}) {
const [showMessage, setShowMessage] = React.useState(false)
return (
<div>
<label htmlFor="toggle">Show Message</label>
<input
id="toggle"
type="checkbox"
onChange={e => setShowMessage(e.target.checked)}
checked={showMessage}
/>
{showMessage ? children : null}
</div>
)
}
export default HiddenMessage
```
```jsx
// __tests__/hidden-message.js
// these imports are something you'd normally configure Jest to import for you
// automatically. Learn more in the setup docs: https://testing-library.com/docs/react-testing-library/setup#cleanup
import '@testing-library/jest-dom'
// NOTE: jest-dom adds handy assertions to Jest and is recommended, but not required
import * as React from 'react'
import {render, fireEvent, screen} from '@testing-library/react'
import HiddenMessage from '../hidden-message'
test('shows the children when the checkbox is checked', () => {
const testMessage = 'Test Message'
render(<HiddenMessage>{testMessage}</HiddenMessage>)
// query* functions will return the element or null if it cannot be found
// get* functions will return the element or throw an error if it cannot be found
expect(screen.queryByText(testMessage)).toBeNull()
// the queries can accept a regex to make your selectors more resilient to content tweaks and changes.
fireEvent.click(screen.getByLabelText(/show/i))
// .toBeInTheDocument() is an assertion that comes from jest-dom
// otherwise you could use .toBeDefined()
expect(screen.getByText(testMessage)).toBeInTheDocument()
})
```
### Complex Example
```jsx
// login.js
import * as React from 'react'
function Login() {
const [state, setState] = React.useReducer((s, a) => ({...s, ...a}), {
resolved: false,
loading: false,
error: null,
})
function handleSubmit(event) {
event.preventDefault()
const {usernameInput, passwordInput} = event.target.elements
setState({loading: true, resolved: false, error: null})
window
.fetch('/api/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
username: usernameInput.value,
password: passwordInput.value,
}),
})
.then(r => r.json().then(data => (r.ok ? data : Promise.reject(data))))
.then(
user => {
setState({loading: false, resolved: true, error: null})
window.localStorage.setItem('token', user.token)
},
error => {
setState({loading: false, resolved: false, error: error.message})
},
)
}
return (
<div>
<form onSubmit={handleSubmit}>
<div>
<label htmlFor="usernameInput">Username</label>
<input id="usernameInput" />
</div>
<div>
<label htmlFor="passwordInput">Password</label>
<input id="passwordInput" type="password" />
</div>
<button type="submit">Submit{state.loading ? '...' : null}</button>
</form>
{state.error ? <div role="alert">{state.error}</div> : null}
{state.resolved ? (
<div role="alert">Congrats! You're signed in!</div>
) : null}
</div>
)
}
export default Login
```
```jsx
// __tests__/login.js
// again, these first two imports are something you'd normally handle in
// your testing framework configuration rather than importing them in every file.
import '@testing-library/jest-dom'
import * as React from 'react'
// import API mocking utilities from Mock Service Worker.
import {rest} from 'msw'
import {setupServer} from 'msw/node'
// import testing utilities
import {render, fireEvent, screen} from '@testing-library/react'
import Login from '../login'
const fakeUserResponse = {token: 'fake_user_token'}
const server = setupServer(
rest.post('/api/login', (req, res, ctx) => {
return res(ctx.json(fakeUserResponse))
}),
)
beforeAll(() => server.listen())
afterEach(() => {
server.resetHandlers()
window.localStorage.removeItem('token')
})
afterAll(() => server.close())
test('allows the user to login successfully', async () => {
render(<Login />)
// fill out the form
fireEvent.change(screen.getByLabelText(/username/i), {
target: {value: 'chuck'},
})
fireEvent.change(screen.getByLabelText(/password/i), {
target: {value: 'norris'},
})
fireEvent.click(screen.getByText(/submit/i))
// just like a manual tester, we'll instruct our test to wait for the alert
// to show up before continuing with our assertions.
const alert = await screen.findByRole('alert')
// .toHaveTextContent() comes from jest-dom's assertions
// otherwise you could use expect(alert.textContent).toMatch(/congrats/i)
// but jest-dom will give you better error messages which is why it's recommended
expect(alert).toHaveTextContent(/congrats/i)
expect(window.localStorage.getItem('token')).toEqual(fakeUserResponse.token)
})
test('handles server exceptions', async () => {
// mock the server error response for this test suite only.
server.use(
rest.post('/api/login', (req, res, ctx) => {
return res(ctx.status(500), ctx.json({message: 'Internal server error'}))
}),
)
render(<Login />)
// fill out the form
fireEvent.change(screen.getByLabelText(/username/i), {
target: {value: 'chuck'},
})
fireEvent.change(screen.getByLabelText(/password/i), {
target: {value: 'norris'},
})
fireEvent.click(screen.getByText(/submit/i))
// wait for the error message
const alert = await screen.findByRole('alert')
expect(alert).toHaveTextContent(/internal server error/i)
expect(window.localStorage.getItem('token')).toBeNull()
})
```
> We recommend using [Mock Service Worker](https://github.com/mswjs/msw) library
> to declaratively mock API communication in your tests instead of stubbing
> `window.fetch`, or relying on third-party adapters.
### More Examples
> We're in the process of moving examples to the
> [docs site](https://testing-library.com/docs/example-codesandbox)
You'll find runnable examples of testing with different libraries in
[the `react-testing-library-examples` codesandbox](https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples).
Some included are:
- [`react-redux`](https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples/tree/main/?fontsize=14&module=%2Fsrc%2F__tests__%2Freact-redux.js&previewwindow=tests)
- [`react-router`](https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples/tree/main/?fontsize=14&module=%2Fsrc%2F__tests__%2Freact-router.js&previewwindow=tests)
- [`react-context`](https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples/tree/main/?fontsize=14&module=%2Fsrc%2F__tests__%2Freact-context.js&previewwindow=tests)
You can also find React Testing Library examples at
[react-testing-examples.com](https://react-testing-examples.com/jest-rtl/).
## Hooks
If you are interested in testing a custom hook, check out [React Hooks Testing
Library][react-hooks-testing-library].
> NOTE: it is not recommended to test single-use custom hooks in isolation from
> the components where it's being used. It's better to test the component that's
> using the hook rather than the hook itself. The `React Hooks Testing Library`
> is intended to be used for reusable hooks/libraries.
## Guiding Principles
> [The more your tests resemble the way your software is used, the more
> confidence they can give you.][guiding-principle]
We try to only expose methods and utilities that encourage you to write tests
that closely resemble how your React components are used.
Utilities are included in this project based on the following guiding
principles:
1. If it relates to rendering components, it deals with DOM nodes rather than
component instances, nor should it encourage dealing with component
instances.
2. It should be generally useful for testing individual React components or
full React applications. While this library is focused on `react-dom`,
utilities could be included even if they don't directly relate to
`react-dom`.
3. Utility implementations and APIs should be simple and flexible.
Most importantly, we want React Testing Library to be pretty light-weight,
simple, and easy to understand.
## Docs
[**Read The Docs**](https://testing-library.com/react) |
[Edit the docs](https://github.com/testing-library/testing-library-docs)
## 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://kentcdodds.com"><img src="https://avatars.githubusercontent.com/u/1500684?v=3?s=100" width="100px;" alt=""/><br /><sub><b>Kent C. Dodds</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=kentcdodds" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=kentcdodds" title="Documentation">📖</a> <a href="#infra-kentcdodds" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=kentcdodds" title="Tests">⚠️</a></td>
<td align="center"><a href="http://audiolion.github.io"><img src="https://avatars1.githubusercontent.com/u/2430381?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ryan Castner</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=audiolion" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.dnlsandiego.com"><img src="https://avatars0.githubusercontent.com/u/8008023?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Sandiego</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=dnlsandiego" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Miklet"><img src="https://avatars2.githubusercontent.com/u/12592677?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Paweł Mikołajczyk</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=Miklet" title="Code">💻</a></td>
<td align="center"><a href="http://co.linkedin.com/in/alejandronanez/"><img src="https://avatars3.githubusercontent.com/u/464978?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alejandro Ñáñez Ortiz</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=alejandronanez" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/pbomb"><img src="https://avatars0.githubusercontent.com/u/1402095?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Parrish</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Apbomb" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=pbomb" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=pbomb" title="Documentation">📖</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=pbomb" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/wKovacs64"><img src="https://avatars1.githubusercontent.com/u/1288694?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Justin Hall</b></sub></a><br /><a href="#platform-wKovacs64" title="Packaging/porting to new platform">📦</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/antoaravinth"><img src="https://avatars1.githubusercontent.com/u/1241511?s=460&v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anto Aravinth</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=antoaravinth" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=antoaravinth" title="Tests">⚠️</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=antoaravinth" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/JonahMoses"><img src="https://avatars2.githubusercontent.com/u/3462296?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jonah Moses</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=JonahMoses" title="Documentation">📖</a></td>
<td align="center"><a href="http://team.thebrain.pro"><img src="https://avatars1.githubusercontent.com/u/4002543?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Łukasz Gandecki</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=lgandecki" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=lgandecki" title="Tests">⚠️</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=lgandecki" title="Documentation">📖</a></td>
<td align="center"><a href="https://sompylasar.github.io"><img src="https://avatars2.githubusercontent.com/u/498274?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ivan Babak</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Asompylasar" title="Bug reports">🐛</a> <a href="#ideas-sompylasar" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/jday3"><img src="https://avatars3.githubusercontent.com/u/4439618?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jesse Day</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=jday3" title="Code">💻</a></td>
<td align="center"><a href="http://gnapse.github.io"><img src="https://avatars0.githubusercontent.com/u/15199?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ernesto García</b></sub></a><br /><a href="#question-gnapse" title="Answering Questions">💬</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=gnapse" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=gnapse" title="Documentation">📖</a></td>
<td align="center"><a href="http://jomaxx.com"><img src="https://avatars2.githubusercontent.com/u/2747424?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Josef Maxx Blake</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=jomaxx" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=jomaxx" title="Documentation">📖</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=jomaxx" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center"><a href="https://twitter.com/baranovskim"><img src="https://avatars1.githubusercontent.com/u/29602306?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michal Baranowski</b></sub></a><br /><a href="#blog-mbaranovski" title="Blogposts">📝</a> <a href="#tutorial-mbaranovski" title="Tutorials"></a></td>
<td align="center"><a href="https://github.com/aputhin"><img src="https://avatars3.githubusercontent.com/u/13985684?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arthur Puthin</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=aputhin" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/thchia"><img src="https://avatars2.githubusercontent.com/u/21194045?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Thomas Chia</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=thchia" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=thchia" title="Documentation">📖</a></td>
<td align="center"><a href="http://ilegra.com/"><img src="https://avatars3.githubusercontent.com/u/20430611?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Thiago Galvani</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=thiagopaiva99" title="Documentation">📖</a></td>
<td align="center"><a href="http://Chriswcs.github.io"><img src="https://avatars1.githubusercontent.com/u/19828824?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=ChrisWcs" title="Tests">⚠️</a></td>
<td align="center"><a href="https://alexkrolick.com"><img src="https://avatars3.githubusercontent.com/u/1571667?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alex Krolick</b></sub></a><br /><a href="#question-alexkrolick" title="Answering Questions">💬</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=alexkrolick" title="Documentation">📖</a> <a href="#example-alexkrolick" title="Examples">💡</a> <a href="#ideas-alexkrolick" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/johann-sonntagbauer"><img src="https://avatars3.githubusercontent.com/u/1239401?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johann Hubert Sonntagbauer</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=johann-sonntagbauer" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=johann-sonntagbauer" title="Documentation">📖</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=johann-sonntagbauer" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.maddijoyce.com"><img src="https://avatars2.githubusercontent.com/u/2224291?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Maddi Joyce</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=maddijoyce" title="Code">💻</a></td>
<td align="center"><a href="http://www.vicesoftware.com"><img src="https://avatars2.githubusercontent.com/u/10080111?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ryan Vice</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=RyanAtViceSoftware" title="Documentation">📖</a></td>
<td align="center"><a href="https://ianwilson.io"><img src="https://avatars1.githubusercontent.com/u/7942604?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ian Wilson</b></sub></a><br /><a href="#blog-iwilsonq" title="Blogposts">📝</a> <a href="#tutorial-iwilsonq" title="Tutorials"></a></td>
<td align="center"><a href="https://github.com/InExtremaRes"><img src="https://avatars2.githubusercontent.com/u/1635491?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3AInExtremaRes" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=InExtremaRes" title="Code">💻</a></td>
<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/react-testing-library/issues?q=author%3AGpx" title="Bug reports">🐛</a> <a href="#ideas-Gpx" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/jgoz"><img src="https://avatars2.githubusercontent.com/u/132233?v=4?s=100" width="100px;" alt=""/><br /><sub><b>John Gozde</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=jgoz" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/SavePointSam"><img src="https://avatars0.githubusercontent.com/u/8203211?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sam Horton</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=SavePointSam" title="Documentation">📖</a> <a href="#example-SavePointSam" title="Examples">💡</a> <a href="#ideas-SavePointSam" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.richardkotze.com"><img src="https://avatars2.githubusercontent.com/u/10452163?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Richard Kotze (mobile)</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=rkotze" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/sotobuild"><img src="https://avatars2.githubusercontent.com/u/10819833?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brahian E. Soto Mercedes</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=sotobuild" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/bdelaforest"><img src="https://avatars2.githubusercontent.com/u/7151559?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Benoit de La Forest</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=bdelaforest" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/thesalah"><img src="https://avatars3.githubusercontent.com/u/6624197?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Salah</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=thesalah" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=thesalah" title="Tests">⚠️</a></td>
<td align="center"><a href="http://gordonizer.com"><img src="https://avatars2.githubusercontent.com/u/370054?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Adam Gordon</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Aicfantv" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=icfantv" title="Code">💻</a></td>
<td align="center"><a href="https://silvenon.com"><img src="https://avatars2.githubusercontent.com/u/471278?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matija Marohnić</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=silvenon" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Dajust"><img src="https://avatars3.githubusercontent.com/u/8015514?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Justice Mba</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=Dajust" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://markpollmann.com/"><img src="https://avatars2.githubusercontent.com/u/5286559?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mark Pollmann</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=MarkPollmann" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/ehteshamkafeel"><img src="https://avatars1.githubusercontent.com/u/1213123?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ehtesham Kafeel</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=ehteshamkafeel" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=ehteshamkafeel" title="Documentation">📖</a></td>
<td align="center"><a href="http://jpavon.com"><img src="https://avatars2.githubusercontent.com/u/1493505?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Julio Pavón</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=jpavon" title="Code">💻</a></td>
<td align="center"><a href="http://www.duncanleung.com/"><img src="https://avatars3.githubusercontent.com/u/1765048?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Duncan L</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=duncanleung" title="Documentation">📖</a> <a href="#example-duncanleung" title="Examples">💡</a></td>
<td align="center"><a href="https://www.linkedin.com/in/tyagow/?locale=en_US"><img src="https://avatars1.githubusercontent.com/u/700778?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tiago Almeida</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=tyagow" title="Documentation">📖</a></td>
<td align="center"><a href="http://rbrtsmith.com/"><img src="https://avatars2.githubusercontent.com/u/4982001?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Robert Smith</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Arbrtsmith" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://offbyone.tech"><img src="https://avatars0.githubusercontent.com/u/1700355?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zach Green</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=zgreen" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/dadamssg"><img src="https://avatars3.githubusercontent.com/u/881986?v=4?s=100" width="100px;" alt=""/><br /><sub><b>dadamssg</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=dadamssg" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.yaabed.com/"><img src="https://avatars0.githubusercontent.com/u/8734097?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yazan Aabed</b></sub></a><br /><a href="#blog-YazanAabeed" title="Blogposts">📝</a></td>
<td align="center"><a href="https://github.com/timbonicus"><img src="https://avatars0.githubusercontent.com/u/556258?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Atimbonicus" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=timbonicus" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=timbonicus" title="Documentation">📖</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=timbonicus" title="Tests">⚠️</a></td>
<td align="center"><a href="http://divyanshu.xyz"><img src="https://avatars3.githubusercontent.com/u/6682655?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Divyanshu Maithani</b></sub></a><br /><a href="#tutorial-divyanshu013" title="Tutorials"></a> <a href="#video-divyanshu013" title="Videos">📹</a></td>
<td align="center"><a href="https://www.linkedin.com/in/metagrover"><img src="https://avatars2.githubusercontent.com/u/9116042?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Deepak Grover</b></sub></a><br /><a href="#tutorial-metagrover" title="Tutorials"></a> <a href="#video-metagrover" title="Videos">📹</a></td>
<td align="center"><a href="https://github.com/eyalcohen4"><img src="https://avatars0.githubusercontent.com/u/16276358?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eyal Cohen</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=eyalcohen4" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/petermakowski"><img src="https://avatars3.githubusercontent.com/u/7452681?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peter Makowski</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=petermakowski" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Michielnuyts"><img src="https://avatars2.githubusercontent.com/u/20361668?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michiel Nuyts</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=Michielnuyts" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/joeynimu"><img src="https://avatars0.githubusercontent.com/u/1195863?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joe Ng'ethe</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=joeynimu" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=joeynimu" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Enikol"><img src="https://avatars3.githubusercontent.com/u/19998290?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kate</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=Enikol" title="Documentation">📖</a></td>
<td align="center"><a href="http://www.seanrparker.com"><img src="https://avatars1.githubusercontent.com/u/11980217?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sean</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=SeanRParker" title="Documentation">📖</a></td>
<td align="center"><a href="http://jlongster.com"><img src="https://avatars2.githubusercontent.com/u/17031?v=4?s=100" width="100px;" alt=""/><br /><sub><b>James Long</b></sub></a><br /><a href="#ideas-jlongster" title="Ideas, Planning, & Feedback">🤔</a> <a href="#platform-jlongster" title="Packaging/porting to new platform">📦</a></td>
<td align="center"><a href="https://github.com/hhagely"><img src="https://avatars1.githubusercontent.com/u/10118777?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Herb Hagely</b></sub></a><br /><a href="#example-hhagely" title="Examples">💡</a></td>
<td align="center"><a href="http://www.wendtedesigns.com/"><img src="https://avatars2.githubusercontent.com/u/5779538?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alex Wendte</b></sub></a><br /><a href="#example-themostcolm" title="Examples">💡</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.aboutmonica.com"><img src="https://avatars0.githubusercontent.com/u/6998954?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Monica Powell</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=M0nica" title="Documentation">📖</a></td>
<td align="center"><a href="http://sivkoff.com"><img src="https://avatars1.githubusercontent.com/u/2699953?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vitaly Sivkov</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=sivkoff" title="Code">💻</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="#ideas-weyert" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3Aweyert" title="Reviewed Pull Requests">👀</a> <a href="#design-weyert" title="Design">🎨</a></td>
<td align="center"><a href="https://github.com/EstebanMarin"><img src="https://avatars3.githubusercontent.com/u/13613037?v=4?s=100" width="100px;" alt=""/><br /><sub><b>EstebanMarin</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=EstebanMarin" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/vctormb"><img src="https://avatars2.githubusercontent.com/u/13953703?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Victor Martins</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=vctormb" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/RoystonS"><img src="https://avatars0.githubusercontent.com/u/19773?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Royston Shufflebotham</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3ARoystonS" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=RoystonS" title="Documentation">📖</a> <a href="#example-RoystonS" title="Examples">💡</a></td>
<td align="center"><a href="https://github.com/chrbala"><img src="https://avatars0.githubusercontent.com/u/6834804?v=4?s=100" width="100px;" alt=""/><br /><sub><b>chrbala</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=chrbala" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://donavon.com"><img src="https://avatars3.githubusercontent.com/u/887639?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Donavon West</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=donavon" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=donavon" title="Documentation">📖</a> <a href="#ideas-donavon" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=donavon" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/maisano"><img src="https://avatars2.githubusercontent.com/u/689081?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Richard Maisano</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=maisano" title="Code">💻</a></td>
<td align="center"><a href="https://www.marcobiedermann.com"><img src="https://avatars0.githubusercontent.com/u/5244986?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marco Biedermann</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=marcobiedermann" title="Code">💻</a> <a href="#maintenance-marcobiedermann" title="Maintenance">🚧</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=marcobiedermann" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/alexzherdev"><img src="https://avatars3.githubusercontent.com/u/93752?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alex Zherdev</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Aalexzherdev" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=alexzherdev" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/Andrewmat"><img src="https://avatars0.githubusercontent.com/u/5133846?v=4?s=100" width="100px;" alt=""/><br /><sub><b>André Matulionis dos Santos</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=Andrewmat" title="Code">💻</a> <a href="#example-Andrewmat" title="Examples">💡</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=Andrewmat" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/FredyC"><img src="https://avatars0.githubusercontent.com/u/1096340?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel K.</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3AFredyC" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=FredyC" title="Code">💻</a> <a href="#ideas-FredyC" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=FredyC" title="Tests">⚠️</a> <a href="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3AFredyC" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/mohamedmagdy17593"><img src="https://avatars0.githubusercontent.com/u/40938625?v=4?s=100" width="100px;" alt=""/><br /><sub><b>mohamedmagdy17593</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=mohamedmagdy17593" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://lorensr.me"><img src="https://avatars2.githubusercontent.com/u/251288?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Loren ☺️</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=lorensr" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/MarkFalconbridge"><img src="https://avatars1.githubusercontent.com/u/20678943?v=4?s=100" width="100px;" alt=""/><br /><sub><b>MarkFalconbridge</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3AMarkFalconbridge" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=MarkFalconbridge" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/viniciusavieira"><img src="https://avatars0.githubusercontent.com/u/2073019?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vinicius</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=viniciusavieira" title="Documentation">📖</a> <a href="#example-viniciusavieira" title="Examples">💡</a></td>
<td align="center"><a href="https://github.com/pschyma"><img src="https://avatars2.githubusercontent.com/u/2489928?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peter Schyma</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=pschyma" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ianschmitz"><img src="https://avatars1.githubusercontent.com/u/6355370?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ian Schmitz</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=ianschmitz" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/joual"><img src="https://avatars0.githubusercontent.com/u/157877?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joel Marcotte</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Ajoual" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=joual" title="Tests">⚠️</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=joual" title="Code">💻</a></td>
<td align="center"><a href="http://aledustet.com"><img src="https://avatars3.githubusercontent.com/u/2413802?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alejandro Dustet</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Aaledustet" title="Bug reports">🐛</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/bcarroll22"><img src="https://avatars2.githubusercontent.com/u/11020406?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Brandon Carroll</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=bcarroll22" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/lucas0707"><img src="https://avatars1.githubusercontent.com/u/26284338?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lucas Machado</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=lucas0707" title="Documentation">📖</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="#platform-pascalduez" title="Packaging/porting to new platform">📦</a></td>
<td align="center"><a href="https://twitter.com/minh_ngvyen"><img src="https://avatars3.githubusercontent.com/u/2852660?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Minh Nguyen</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=NMinhNguyen" title="Code">💻</a></td>
<td align="center"><a href="http://iababy46.blogspot.tw/"><img src="https://avatars0.githubusercontent.com/u/11155585?v=4?s=100" width="100px;" alt=""/><br /><sub><b>LiaoJimmy</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=LiaoJimmy" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/threepointone"><img src="https://avatars2.githubusercontent.com/u/18808?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sunil Pai</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=threepointone" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=threepointone" title="Tests">⚠️</a></td>
<td align="center"><a href="http://twitter.com/dan_abramov"><img src="https://avatars0.githubusercontent.com/u/810438?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dan Abramov</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3Agaearon" title="Reviewed Pull Requests">👀</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/ChristianMurphy"><img src="https://avatars3.githubusercontent.com/u/3107513?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Murphy</b></sub></a><br /><a href="#infra-ChristianMurphy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://jeetiss.github.io/"><img src="https://avatars1.githubusercontent.com/u/6726016?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ivakhnenko Dmitry</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=jeetiss" title="Code">💻</a></td>
<td align="center"><a href="https://ghuser.io/jamesgeorge007"><img src="https://avatars2.githubusercontent.com/u/25279263?v=4?s=100" width="100px;" alt=""/><br /><sub><b>James George</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=jamesgeorge007" title="Documentation">📖</a></td>
<td align="center"><a href="https://joaofernandes.me/"><img src="https://avatars1.githubusercontent.com/u/1075053?v=4?s=100" width="100px;" alt=""/><br /><sub><b>João Fernandes</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=JSFernandes" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/alejandroperea"><img src="https://avatars3.githubusercontent.com/u/6084749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alejandro Perea</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3Aalejandroperea" title="Reviewed Pull Requests">👀</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="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3Anickmccurdy" title="Reviewed Pull Requests">👀</a> <a href="#question-nickmccurdy" title="Answering Questions">💬</a> <a href="#infra-nickmccurdy" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a></td>
<td align="center"><a href="https://twitter.com/sebsilbermann"><img src="https://avatars3.githubusercontent.com/u/12292047?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sebastian Silbermann</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3Aeps1lon" 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/react-testing-library/pulls?q=is%3Apr+reviewed-by%3Aafontcu" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=afontcu" title="Documentation">📖</a></td>
<td align="center"><a href="https://blog.johnnyreilly.com/"><img src="https://avatars0.githubusercontent.com/u/1010525?v=4?s=100" width="100px;" alt=""/><br /><sub><b>John Reilly</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3Ajohnnyreilly" title="Reviewed Pull Requests">👀</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/react-testing-library/pulls?q=is%3Apr+reviewed-by%3AMichaelDeBoey" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=MichaelDeBoey" title="Code">💻</a></td>
<td align="center"><a href="https://cimbul.com"><img src="https://avatars2.githubusercontent.com/u/927923?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tim Yates</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3Acimbul" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/eventualbuddha"><img src="https://avatars3.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/react-testing-library/commits?author=eventualbuddha" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/JaysQubeXon"><img src="https://avatars1.githubusercontent.com/u/18309230?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Noam Gabriel Jacobson</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=JaysQubeXon" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/rvdkooy"><img src="https://avatars1.githubusercontent.com/u/4119960?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ronald van der Kooij</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=rvdkooy" title="Tests">⚠️</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=rvdkooy" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/aayushrajvanshi"><img src="https://avatars0.githubusercontent.com/u/14968551?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aayush Rajvanshi</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=aayushrajvanshi" title="Documentation">📖</a></td>
<td align="center"><a href="https://elyalamillo.com"><img src="https://avatars2.githubusercontent.com/u/24350492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ely Alamillo</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=ely-alamillo" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=ely-alamillo" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/danieljcafonso"><img src="https://avatars3.githubusercontent.com/u/35337607?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Daniel Afonso</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=danieljcafonso" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=danieljcafonso" title="Tests">⚠️</a></td>
<td align="center"><a href="http://www.laurensbosscher.nl"><img src="https://avatars0.githubusercontent.com/u/13363196?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Laurens Bosscher</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=LaurensBosscher" title="Code">💻</a></td>
<td align="center"><a href="https://twitter.com/__sakito__"><img src="https://avatars1.githubusercontent.com/u/15010907?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sakito Mukai</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=sakito21" title="Documentation">📖</a></td>
<td align="center"><a href="http://turkerteke.com"><img src="https://avatars3.githubusercontent.com/u/12457162?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Türker Teke</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=tteke" title="Documentation">📖</a></td>
<td align="center"><a href="http://linkedin.com/in/zachbrogan"><img src="https://avatars1.githubusercontent.com/u/319162?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zach Brogan</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=zbrogz" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=zbrogz" title="Tests">⚠️</a></td>
</tr>
<tr>
<td align="center"><a href="https://ryota-murakami.github.io/"><img src="https://avatars2.githubusercontent.com/u/5501268?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ryota Murakami</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=ryota-murakami" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/hottmanmichael"><img src="https://avatars3.githubusercontent.com/u/10534502?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael Hottman</b></sub></a><br /><a href="#ideas-hottmanmichael" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://github.com/stevenfitzpatrick"><img src="https://avatars0.githubusercontent.com/u/23268855?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Steven Fitzpatrick</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Astevenfitzpatrick" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/juangl"><img src="https://avatars0.githubusercontent.com/u/1887029?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Juan Je García</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=juangl" title="Documentation">📖</a></td>
<td align="center"><a href="https://ghuser.io/Ishaan28malik"><img src="https://avatars3.githubusercontent.com/u/27343592?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Championrunner</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=Ishaan28malik" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/samtsai"><img src="https://avatars0.githubusercontent.com/u/225526?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sam Tsai</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=samtsai" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=samtsai" title="Tests">⚠️</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=samtsai" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.echooff.dev"><img src="https://avatars0.githubusercontent.com/u/149248?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Rackerseder</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=screendriver" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/NiGhTTraX"><img src="https://avatars0.githubusercontent.com/u/485061?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Andrei Picus</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3ANiGhTTraX" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3ANiGhTTraX" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://redd.one"><img src="https://avatars3.githubusercontent.com/u/14984911?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Artem Zakharchenko</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=kettanaito" title="Documentation">📖</a></td>
<td align="center"><a href="http://michaelsiek.com"><img src="https://avatars0.githubusercontent.com/u/45568605?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michael</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=michael-siek" title="Documentation">📖</a></td>
<td align="center"><a href="http://2dubbing.tistory.com"><img src="https://avatars2.githubusercontent.com/u/15885679?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Braden Lee</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=2dubbing" title="Documentation">📖</a></td>
<td align="center"><a href="http://kamranicus.com/"><img src="https://avatars1.githubusercontent.com/u/563819?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kamran Ayub</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=kamranayub" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=kamranayub" title="Tests">⚠️</a></td>
<td align="center"><a href="https://twitter.com/matanbobi"><img src="https://avatars2.githubusercontent.com/u/12711091?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matan Borenkraout</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=MatanBobi" title="Code">💻</a></td>
<td align="center"><a href="http://ryanbigg.com"><img src="https://avatars3.githubusercontent.com/u/2687?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ryan Bigg</b></sub></a><br /><a href="#maintenance-radar" title="Maintenance">🚧</a></td>
</tr>
<tr>
<td align="center"><a href="https://antonhalim.com"><img src="https://avatars1.githubusercontent.com/u/10498035?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anton Halim</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=antonhalim" title="Documentation">📖</a></td>
<td align="center"><a href="http://artmalko.ru"><img src="https://avatars0.githubusercontent.com/u/1823689?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Artem Malko</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=artem-malko" title="Code">💻</a></td>
<td align="center"><a href="http://gerritalex.de"><img src="https://avatars1.githubusercontent.com/u/29307652?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gerrit Alex</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=ljosberinn" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/karthick3018"><img src="https://avatars1.githubusercontent.com/u/47154512?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Karthick Raja</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=karthick3018" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/theashraf"><img src="https://avatars1.githubusercontent.com/u/39750790?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abdelrahman Ashraf</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=theashraf" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/lidoravitan"><img src="https://avatars0.githubusercontent.com/u/35113398?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lidor Avitan</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=lidoravitan" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/ljharb"><img src="https://avatars1.githubusercontent.com/u/45469?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jordan Harband</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/pulls?q=is%3Apr+reviewed-by%3Aljharb" title="Reviewed Pull Requests">👀</a> <a href="#ideas-ljharb" title="Ideas, Planning, & Feedback">🤔</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/react-testing-library/commits?author=marcosvega91" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/sanchit121"><img src="https://avatars2.githubusercontent.com/u/30828115?v=4?s=100" width="100px;" alt=""/><br /><sub><b>sanchit121</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Asanchit121" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=sanchit121" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/solufa"><img src="https://avatars.githubusercontent.com/u/9402912?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Solufa</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Asolufa" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=solufa" title="Code">💻</a></td>
<td align="center"><a href="https://codepen.io/ariperkkio/"><img src="https://avatars.githubusercontent.com/u/14806298?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ari Perkkiö</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=AriPerkkio" title="Tests">⚠️</a></td>
<td align="center"><a href="https://github.com/jhnns"><img src="https://avatars.githubusercontent.com/u/781746?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Johannes Ewald</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=jhnns" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/anpaopao"><img src="https://avatars.githubusercontent.com/u/44686792?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Angus J. Pope</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=anpaopao" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/leschdom"><img src="https://avatars.githubusercontent.com/u/62334278?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dominik Lesch</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=leschdom" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/ImADrafter"><img src="https://avatars.githubusercontent.com/u/44379989?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marcos Gómez</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=ImADrafter" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.akashshyam.online/"><img src="https://avatars.githubusercontent.com/u/56759828?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akash Shyam</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Aakashshyamdev" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://hen.ne.ke"><img src="https://avatars.githubusercontent.com/u/4312191?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fabian Meumertzheim</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=fmeum" title="Code">💻</a> <a href="https://github.com/testing-library/react-testing-library/issues?q=author%3Afmeum" title="Bug reports">🐛</a></td>
<td align="center"><a href="https://github.com/Nokel81"><img src="https://avatars.githubusercontent.com/u/8225332?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sebastian Malton</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/issues?q=author%3ANokel81" title="Bug reports">🐛</a> <a href="https://github.com/testing-library/react-testing-library/commits?author=Nokel81" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mboettcher"><img src="https://avatars.githubusercontent.com/u/2325337?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin Böttcher</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=mboettcher" title="Code">💻</a></td>
<td align="center"><a href="http://tkdodo.eu"><img src="https://avatars.githubusercontent.com/u/1021430?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dominik Dorfmeister</b></sub></a><br /><a href="https://github.com/testing-library/react-testing-library/commits?author=TkDodo" 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/
[yarn]: https://classic.yarnpkg.com
[node]: https://nodejs.org
[build-badge]: https://img.shields.io/github/workflow/status/testing-library/react-testing-library/validate?logo=github&style=flat-square
[build]: https://github.com/testing-library/react-testing-library/actions?query=workflow%3Avalidate
[coverage-badge]: https://img.shields.io/codecov/c/github/testing-library/react-testing-library.svg?style=flat-square
[coverage]: https://codecov.io/github/testing-library/react-testing-library
[version-badge]: https://img.shields.io/npm/v/@testing-library/react.svg?style=flat-square
[package]: https://www.npmjs.com/package/@testing-library/react
[downloads-badge]: https://img.shields.io/npm/dm/@testing-library/react.svg?style=flat-square
[npmtrends]: http://www.npmtrends.com/@testing-library/react
[license-badge]: https://img.shields.io/npm/l/@testing-library/react.svg?style=flat-square
[license]: https://github.com/testing-library/react-testing-library/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/react-testing-library/blob/main/CODE_OF_CONDUCT.md
[github-watch-badge]: https://img.shields.io/github/watchers/testing-library/react-testing-library.svg?style=social
[github-watch]: https://github.com/testing-library/react-testing-library/watchers
[github-star-badge]: https://img.shields.io/github/stars/testing-library/react-testing-library.svg?style=social
[github-star]: https://github.com/testing-library/react-testing-library/stargazers
[twitter]: https://twitter.com/intent/tweet?text=Check%20out%20react-testing-library%20by%20%40@TestingLib%20https%3A%2F%2Fgithub.com%2Ftesting-library%2Freact-testing-library%20%F0%9F%91%8D
[twitter-badge]: https://img.shields.io/twitter/url/https/github.com/testing-library/react-testing-library.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/react-testing-library?color=orange&style=flat-square
[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106
[bugs]: https://github.com/testing-library/react-testing-library/issues?q=is%3Aissue+is%3Aopen+label%3Abug+sort%3Acreated-desc
[requests]: https://github.com/testing-library/react-testing-library/issues?q=is%3Aissue+sort%3Areactions-%2B1-desc+label%3Aenhancement+is%3Aopen
[good-first-issue]: https://github.com/testing-library/react-testing-library/issues?utf8=✓&q=is%3Aissue+is%3Aopen+sort%3Areactions-%2B1-desc+label%3A"good+first+issue"+
[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/react-testing-library
[react-hooks-testing-library]: https://github.com/testing-library/react-hooks-testing-library
<!-- prettier-ignore-end -->

View File

@@ -0,0 +1,515 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var testUtils = require('react-dom/test-utils');
var React = require('react');
var ReactDOM = require('react-dom');
var ReactDOMClient = require('react-dom/client');
var dom = require('@testing-library/dom');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var testUtils__namespace = /*#__PURE__*/_interopNamespace(testUtils);
var React__namespace = /*#__PURE__*/_interopNamespace(React);
var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
var ReactDOMClient__namespace = /*#__PURE__*/_interopNamespace(ReactDOMClient);
const domAct = testUtils__namespace.act;
function getGlobalThis() {
/* istanbul ignore else */
if (typeof globalThis !== 'undefined') {
return globalThis;
}
/* istanbul ignore next */
if (typeof self !== 'undefined') {
return self;
}
/* istanbul ignore next */
if (typeof window !== 'undefined') {
return window;
}
/* istanbul ignore next */
if (typeof global !== 'undefined') {
return global;
}
/* istanbul ignore next */
throw new Error('unable to locate global object');
}
function setIsReactActEnvironment(isReactActEnvironment) {
getGlobalThis().IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment;
}
function getIsReactActEnvironment() {
return getGlobalThis().IS_REACT_ACT_ENVIRONMENT;
}
function withGlobalActEnvironment(actImplementation) {
return callback => {
const previousActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(true);
try {
// The return value of `act` is always a thenable.
let callbackNeedsToBeAwaited = false;
const actResult = actImplementation(() => {
const result = callback();
if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
callbackNeedsToBeAwaited = true;
}
return result;
});
if (callbackNeedsToBeAwaited) {
const thenable = actResult;
return {
then: (resolve, reject) => {
thenable.then(returnValue => {
setIsReactActEnvironment(previousActEnvironment);
resolve(returnValue);
}, error => {
setIsReactActEnvironment(previousActEnvironment);
reject(error);
});
}
};
} else {
setIsReactActEnvironment(previousActEnvironment);
return actResult;
}
} catch (error) {
// Can't be a `finally {}` block since we don't know if we have to immediately restore IS_REACT_ACT_ENVIRONMENT
// or if we have to await the callback first.
setIsReactActEnvironment(previousActEnvironment);
throw error;
}
};
}
const act = withGlobalActEnvironment(domAct);
/* eslint no-console:0 */
// dom-testing-library's version of fireEvent. The reason
// we make this distinction however is because we have
// a few extra events that work a bit differently
const fireEvent = function () {
return dom.fireEvent(...arguments);
};
Object.keys(dom.fireEvent).forEach(key => {
fireEvent[key] = function () {
return dom.fireEvent[key](...arguments);
};
}); // React event system tracks native mouseOver/mouseOut events for
// running onMouseEnter/onMouseLeave handlers
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/EnterLeaveEventPlugin.js#L24-L31
const mouseEnter = fireEvent.mouseEnter;
const mouseLeave = fireEvent.mouseLeave;
fireEvent.mouseEnter = function () {
mouseEnter(...arguments);
return fireEvent.mouseOver(...arguments);
};
fireEvent.mouseLeave = function () {
mouseLeave(...arguments);
return fireEvent.mouseOut(...arguments);
};
const pointerEnter = fireEvent.pointerEnter;
const pointerLeave = fireEvent.pointerLeave;
fireEvent.pointerEnter = function () {
pointerEnter(...arguments);
return fireEvent.pointerOver(...arguments);
};
fireEvent.pointerLeave = function () {
pointerLeave(...arguments);
return fireEvent.pointerOut(...arguments);
};
const select = fireEvent.select;
fireEvent.select = (node, init) => {
select(node, init); // React tracks this event only on focused inputs
node.focus(); // React creates this event when one of the following native events happens
// - contextMenu
// - mouseUp
// - dragEnd
// - keyUp
// - keyDown
// so we can use any here
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/SelectEventPlugin.js#L203-L224
fireEvent.keyUp(node, init);
}; // React event system tracks native focusout/focusin events for
// running blur/focus handlers
// @link https://github.com/facebook/react/pull/19186
const blur = fireEvent.blur;
const focus = fireEvent.focus;
fireEvent.blur = function () {
fireEvent.focusOut(...arguments);
return blur(...arguments);
};
fireEvent.focus = function () {
fireEvent.focusIn(...arguments);
return focus(...arguments);
};
dom.configure({
unstable_advanceTimersWrapper: cb => {
return act(cb);
},
// We just want to run `waitFor` without IS_REACT_ACT_ENVIRONMENT
// But that's not necessarily how `asyncWrapper` is used since it's a public method.
// Let's just hope nobody else is using it.
asyncWrapper: async cb => {
const previousActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(false);
try {
return await cb();
} finally {
setIsReactActEnvironment(previousActEnvironment);
}
},
eventWrapper: cb => {
let result;
act(() => {
result = cb();
});
return result;
}
}); // Ideally we'd just use a WeakMap where containers are keys and roots are values.
// We use two variables so that we can bail out in constant time when we render with a new container (most common use case)
/**
* @type {Set<import('react-dom').Container>}
*/
const mountedContainers = new Set();
/**
* @type Array<{container: import('react-dom').Container, root: ReturnType<typeof createConcurrentRoot>}>
*/
const mountedRootEntries = [];
function createConcurrentRoot(container, _ref) {
let {
hydrate,
ui,
wrapper: WrapperComponent
} = _ref;
let root;
if (hydrate) {
act(() => {
root = ReactDOMClient__namespace.hydrateRoot(container, WrapperComponent ? /*#__PURE__*/React__namespace.createElement(WrapperComponent, null, ui) : ui);
});
} else {
root = ReactDOMClient__namespace.createRoot(container);
}
return {
hydrate() {
/* istanbul ignore if */
if (!hydrate) {
throw new Error('Attempted to hydrate a non-hydrateable root. This is a bug in `@testing-library/react`.');
} // Nothing to do since hydration happens when creating the root object.
},
render(element) {
root.render(element);
},
unmount() {
root.unmount();
}
};
}
function createLegacyRoot(container) {
return {
hydrate(element) {
ReactDOM__default["default"].hydrate(element, container);
},
render(element) {
ReactDOM__default["default"].render(element, container);
},
unmount() {
ReactDOM__default["default"].unmountComponentAtNode(container);
}
};
}
function renderRoot(ui, _ref2) {
let {
baseElement,
container,
hydrate,
queries,
root,
wrapper: WrapperComponent
} = _ref2;
const wrapUiIfNeeded = innerElement => WrapperComponent ? /*#__PURE__*/React__namespace.createElement(WrapperComponent, null, innerElement) : innerElement;
act(() => {
if (hydrate) {
root.hydrate(wrapUiIfNeeded(ui), container);
} else {
root.render(wrapUiIfNeeded(ui), container);
}
});
return {
container,
baseElement,
debug: function (el, maxLength, options) {
if (el === void 0) {
el = baseElement;
}
return Array.isArray(el) ? // eslint-disable-next-line no-console
el.forEach(e => console.log(dom.prettyDOM(e, maxLength, options))) : // eslint-disable-next-line no-console,
console.log(dom.prettyDOM(el, maxLength, options));
},
unmount: () => {
act(() => {
root.unmount();
});
},
rerender: rerenderUi => {
renderRoot(wrapUiIfNeeded(rerenderUi), {
container,
baseElement,
root
}); // Intentionally do not return anything to avoid unnecessarily complicating the API.
// folks can use all the same utilities we return in the first place that are bound to the container
},
asFragment: () => {
/* istanbul ignore else (old jsdom limitation) */
if (typeof document.createRange === 'function') {
return document.createRange().createContextualFragment(container.innerHTML);
} else {
const template = document.createElement('template');
template.innerHTML = container.innerHTML;
return template.content;
}
},
...dom.getQueriesForElement(baseElement, queries)
};
}
function render(ui, _temp) {
let {
container,
baseElement = container,
legacyRoot = false,
queries,
hydrate = false,
wrapper
} = _temp === void 0 ? {} : _temp;
if (!baseElement) {
// default to document.body instead of documentElement to avoid output of potentially-large
// head elements (such as JSS style blocks) in debug output
baseElement = document.body;
}
if (!container) {
container = baseElement.appendChild(document.createElement('div'));
}
let root; // eslint-disable-next-line no-negated-condition -- we want to map the evolution of this over time. The root is created first. Only later is it re-used so we don't want to read the case that happens later first.
if (!mountedContainers.has(container)) {
const createRootImpl = legacyRoot ? createLegacyRoot : createConcurrentRoot;
root = createRootImpl(container, {
hydrate,
ui,
wrapper
});
mountedRootEntries.push({
container,
root
}); // we'll add it to the mounted containers regardless of whether it's actually
// added to document.body so the cleanup method works regardless of whether
// they're passing us a custom container or not.
mountedContainers.add(container);
} else {
mountedRootEntries.forEach(rootEntry => {
// Else is unreachable since `mountedContainers` has the `container`.
// Only reachable if one would accidentally add the container to `mountedContainers` but not the root to `mountedRootEntries`
/* istanbul ignore else */
if (rootEntry.container === container) {
root = rootEntry.root;
}
});
}
return renderRoot(ui, {
container,
baseElement,
queries,
hydrate,
wrapper,
root
});
}
function cleanup() {
mountedRootEntries.forEach(_ref3 => {
let {
root,
container
} = _ref3;
act(() => {
root.unmount();
});
if (container.parentNode === document.body) {
document.body.removeChild(container);
}
});
mountedRootEntries.length = 0;
mountedContainers.clear();
}
function renderHook(renderCallback, options) {
if (options === void 0) {
options = {};
}
const {
initialProps,
...renderOptions
} = options;
const result = /*#__PURE__*/React__namespace.createRef();
function TestComponent(_ref4) {
let {
renderCallbackProps
} = _ref4;
const pendingResult = renderCallback(renderCallbackProps);
React__namespace.useEffect(() => {
result.current = pendingResult;
});
return null;
}
const {
rerender: baseRerender,
unmount
} = render( /*#__PURE__*/React__namespace.createElement(TestComponent, {
renderCallbackProps: initialProps
}), renderOptions);
function rerender(rerenderCallbackProps) {
return baseRerender( /*#__PURE__*/React__namespace.createElement(TestComponent, {
renderCallbackProps: rerenderCallbackProps
}));
}
return {
result,
rerender,
unmount
};
} // just re-export everything from dom-testing-library
/* eslint func-name-matching:0 */
var _process$env;
// or teardown then we'll automatically run cleanup afterEach test
// this ensures that tests run in isolation from each other
// if you don't like this then either import the `pure` module
// or set the RTL_SKIP_AUTO_CLEANUP env variable to 'true'.
if (typeof process === 'undefined' || !((_process$env = process.env) != null && _process$env.RTL_SKIP_AUTO_CLEANUP)) {
// ignore teardown() in code coverage because Jest does not support it
/* istanbul ignore else */
if (typeof afterEach === 'function') {
afterEach(() => {
cleanup();
});
} else if (typeof teardown === 'function') {
// Block is guarded by `typeof` check.
// eslint does not support `typeof` guards.
// eslint-disable-next-line no-undef
teardown(() => {
cleanup();
});
} // No test setup with other test runners available
/* istanbul ignore else */
if (typeof beforeAll === 'function' && typeof afterAll === 'function') {
// This matches the behavior of React < 18.
let previousIsReactActEnvironment = getIsReactActEnvironment();
beforeAll(() => {
previousIsReactActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(true);
});
afterAll(() => {
setIsReactActEnvironment(previousIsReactActEnvironment);
});
}
}
exports.act = act;
exports.cleanup = cleanup;
exports.fireEvent = fireEvent;
exports.render = render;
exports.renderHook = renderHook;
Object.keys(dom).forEach(function (k) {
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
enumerable: true,
get: function () { return dom[k]; }
});
});

View File

@@ -0,0 +1,477 @@
import * as testUtils from 'react-dom/test-utils';
import * as React from 'react';
import ReactDOM from 'react-dom';
import * as ReactDOMClient from 'react-dom/client';
import { fireEvent as fireEvent$1, configure, prettyDOM, getQueriesForElement } from '@testing-library/dom';
export * from '@testing-library/dom';
const domAct = testUtils.act;
function getGlobalThis() {
/* istanbul ignore else */
if (typeof globalThis !== 'undefined') {
return globalThis;
}
/* istanbul ignore next */
if (typeof self !== 'undefined') {
return self;
}
/* istanbul ignore next */
if (typeof window !== 'undefined') {
return window;
}
/* istanbul ignore next */
if (typeof global !== 'undefined') {
return global;
}
/* istanbul ignore next */
throw new Error('unable to locate global object');
}
function setIsReactActEnvironment(isReactActEnvironment) {
getGlobalThis().IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment;
}
function getIsReactActEnvironment() {
return getGlobalThis().IS_REACT_ACT_ENVIRONMENT;
}
function withGlobalActEnvironment(actImplementation) {
return callback => {
const previousActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(true);
try {
// The return value of `act` is always a thenable.
let callbackNeedsToBeAwaited = false;
const actResult = actImplementation(() => {
const result = callback();
if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
callbackNeedsToBeAwaited = true;
}
return result;
});
if (callbackNeedsToBeAwaited) {
const thenable = actResult;
return {
then: (resolve, reject) => {
thenable.then(returnValue => {
setIsReactActEnvironment(previousActEnvironment);
resolve(returnValue);
}, error => {
setIsReactActEnvironment(previousActEnvironment);
reject(error);
});
}
};
} else {
setIsReactActEnvironment(previousActEnvironment);
return actResult;
}
} catch (error) {
// Can't be a `finally {}` block since we don't know if we have to immediately restore IS_REACT_ACT_ENVIRONMENT
// or if we have to await the callback first.
setIsReactActEnvironment(previousActEnvironment);
throw error;
}
};
}
const act = withGlobalActEnvironment(domAct);
/* eslint no-console:0 */
// dom-testing-library's version of fireEvent. The reason
// we make this distinction however is because we have
// a few extra events that work a bit differently
const fireEvent = function () {
return fireEvent$1(...arguments);
};
Object.keys(fireEvent$1).forEach(key => {
fireEvent[key] = function () {
return fireEvent$1[key](...arguments);
};
}); // React event system tracks native mouseOver/mouseOut events for
// running onMouseEnter/onMouseLeave handlers
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/EnterLeaveEventPlugin.js#L24-L31
const mouseEnter = fireEvent.mouseEnter;
const mouseLeave = fireEvent.mouseLeave;
fireEvent.mouseEnter = function () {
mouseEnter(...arguments);
return fireEvent.mouseOver(...arguments);
};
fireEvent.mouseLeave = function () {
mouseLeave(...arguments);
return fireEvent.mouseOut(...arguments);
};
const pointerEnter = fireEvent.pointerEnter;
const pointerLeave = fireEvent.pointerLeave;
fireEvent.pointerEnter = function () {
pointerEnter(...arguments);
return fireEvent.pointerOver(...arguments);
};
fireEvent.pointerLeave = function () {
pointerLeave(...arguments);
return fireEvent.pointerOut(...arguments);
};
const select = fireEvent.select;
fireEvent.select = (node, init) => {
select(node, init); // React tracks this event only on focused inputs
node.focus(); // React creates this event when one of the following native events happens
// - contextMenu
// - mouseUp
// - dragEnd
// - keyUp
// - keyDown
// so we can use any here
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/SelectEventPlugin.js#L203-L224
fireEvent.keyUp(node, init);
}; // React event system tracks native focusout/focusin events for
// running blur/focus handlers
// @link https://github.com/facebook/react/pull/19186
const blur = fireEvent.blur;
const focus = fireEvent.focus;
fireEvent.blur = function () {
fireEvent.focusOut(...arguments);
return blur(...arguments);
};
fireEvent.focus = function () {
fireEvent.focusIn(...arguments);
return focus(...arguments);
};
configure({
unstable_advanceTimersWrapper: cb => {
return act(cb);
},
// We just want to run `waitFor` without IS_REACT_ACT_ENVIRONMENT
// But that's not necessarily how `asyncWrapper` is used since it's a public method.
// Let's just hope nobody else is using it.
asyncWrapper: async cb => {
const previousActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(false);
try {
return await cb();
} finally {
setIsReactActEnvironment(previousActEnvironment);
}
},
eventWrapper: cb => {
let result;
act(() => {
result = cb();
});
return result;
}
}); // Ideally we'd just use a WeakMap where containers are keys and roots are values.
// We use two variables so that we can bail out in constant time when we render with a new container (most common use case)
/**
* @type {Set<import('react-dom').Container>}
*/
const mountedContainers = new Set();
/**
* @type Array<{container: import('react-dom').Container, root: ReturnType<typeof createConcurrentRoot>}>
*/
const mountedRootEntries = [];
function createConcurrentRoot(container, _ref) {
let {
hydrate,
ui,
wrapper: WrapperComponent
} = _ref;
let root;
if (hydrate) {
act(() => {
root = ReactDOMClient.hydrateRoot(container, WrapperComponent ? /*#__PURE__*/React.createElement(WrapperComponent, null, ui) : ui);
});
} else {
root = ReactDOMClient.createRoot(container);
}
return {
hydrate() {
/* istanbul ignore if */
if (!hydrate) {
throw new Error('Attempted to hydrate a non-hydrateable root. This is a bug in `@testing-library/react`.');
} // Nothing to do since hydration happens when creating the root object.
},
render(element) {
root.render(element);
},
unmount() {
root.unmount();
}
};
}
function createLegacyRoot(container) {
return {
hydrate(element) {
ReactDOM.hydrate(element, container);
},
render(element) {
ReactDOM.render(element, container);
},
unmount() {
ReactDOM.unmountComponentAtNode(container);
}
};
}
function renderRoot(ui, _ref2) {
let {
baseElement,
container,
hydrate,
queries,
root,
wrapper: WrapperComponent
} = _ref2;
const wrapUiIfNeeded = innerElement => WrapperComponent ? /*#__PURE__*/React.createElement(WrapperComponent, null, innerElement) : innerElement;
act(() => {
if (hydrate) {
root.hydrate(wrapUiIfNeeded(ui), container);
} else {
root.render(wrapUiIfNeeded(ui), container);
}
});
return {
container,
baseElement,
debug: function (el, maxLength, options) {
if (el === void 0) {
el = baseElement;
}
return Array.isArray(el) ? // eslint-disable-next-line no-console
el.forEach(e => console.log(prettyDOM(e, maxLength, options))) : // eslint-disable-next-line no-console,
console.log(prettyDOM(el, maxLength, options));
},
unmount: () => {
act(() => {
root.unmount();
});
},
rerender: rerenderUi => {
renderRoot(wrapUiIfNeeded(rerenderUi), {
container,
baseElement,
root
}); // Intentionally do not return anything to avoid unnecessarily complicating the API.
// folks can use all the same utilities we return in the first place that are bound to the container
},
asFragment: () => {
/* istanbul ignore else (old jsdom limitation) */
if (typeof document.createRange === 'function') {
return document.createRange().createContextualFragment(container.innerHTML);
} else {
const template = document.createElement('template');
template.innerHTML = container.innerHTML;
return template.content;
}
},
...getQueriesForElement(baseElement, queries)
};
}
function render(ui, _temp) {
let {
container,
baseElement = container,
legacyRoot = false,
queries,
hydrate = false,
wrapper
} = _temp === void 0 ? {} : _temp;
if (!baseElement) {
// default to document.body instead of documentElement to avoid output of potentially-large
// head elements (such as JSS style blocks) in debug output
baseElement = document.body;
}
if (!container) {
container = baseElement.appendChild(document.createElement('div'));
}
let root; // eslint-disable-next-line no-negated-condition -- we want to map the evolution of this over time. The root is created first. Only later is it re-used so we don't want to read the case that happens later first.
if (!mountedContainers.has(container)) {
const createRootImpl = legacyRoot ? createLegacyRoot : createConcurrentRoot;
root = createRootImpl(container, {
hydrate,
ui,
wrapper
});
mountedRootEntries.push({
container,
root
}); // we'll add it to the mounted containers regardless of whether it's actually
// added to document.body so the cleanup method works regardless of whether
// they're passing us a custom container or not.
mountedContainers.add(container);
} else {
mountedRootEntries.forEach(rootEntry => {
// Else is unreachable since `mountedContainers` has the `container`.
// Only reachable if one would accidentally add the container to `mountedContainers` but not the root to `mountedRootEntries`
/* istanbul ignore else */
if (rootEntry.container === container) {
root = rootEntry.root;
}
});
}
return renderRoot(ui, {
container,
baseElement,
queries,
hydrate,
wrapper,
root
});
}
function cleanup() {
mountedRootEntries.forEach(_ref3 => {
let {
root,
container
} = _ref3;
act(() => {
root.unmount();
});
if (container.parentNode === document.body) {
document.body.removeChild(container);
}
});
mountedRootEntries.length = 0;
mountedContainers.clear();
}
function renderHook(renderCallback, options) {
if (options === void 0) {
options = {};
}
const {
initialProps,
...renderOptions
} = options;
const result = /*#__PURE__*/React.createRef();
function TestComponent(_ref4) {
let {
renderCallbackProps
} = _ref4;
const pendingResult = renderCallback(renderCallbackProps);
React.useEffect(() => {
result.current = pendingResult;
});
return null;
}
const {
rerender: baseRerender,
unmount
} = render( /*#__PURE__*/React.createElement(TestComponent, {
renderCallbackProps: initialProps
}), renderOptions);
function rerender(rerenderCallbackProps) {
return baseRerender( /*#__PURE__*/React.createElement(TestComponent, {
renderCallbackProps: rerenderCallbackProps
}));
}
return {
result,
rerender,
unmount
};
} // just re-export everything from dom-testing-library
/* eslint func-name-matching:0 */
var _process$env;
// or teardown then we'll automatically run cleanup afterEach test
// this ensures that tests run in isolation from each other
// if you don't like this then either import the `pure` module
// or set the RTL_SKIP_AUTO_CLEANUP env variable to 'true'.
if (typeof process === 'undefined' || !((_process$env = process.env) != null && _process$env.RTL_SKIP_AUTO_CLEANUP)) {
// ignore teardown() in code coverage because Jest does not support it
/* istanbul ignore else */
if (typeof afterEach === 'function') {
afterEach(() => {
cleanup();
});
} else if (typeof teardown === 'function') {
// Block is guarded by `typeof` check.
// eslint does not support `typeof` guards.
// eslint-disable-next-line no-undef
teardown(() => {
cleanup();
});
} // No test setup with other test runners available
/* istanbul ignore else */
if (typeof beforeAll === 'function' && typeof afterAll === 'function') {
// This matches the behavior of React < 18.
let previousIsReactActEnvironment = getIsReactActEnvironment();
beforeAll(() => {
previousIsReactActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(true);
});
afterAll(() => {
setIsReactActEnvironment(previousIsReactActEnvironment);
});
}
}
export { act, cleanup, fireEvent, render, renderHook };

View File

@@ -0,0 +1,476 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var React = require('react');
var ReactDOM = require('react-dom');
var ReactDOMClient = require('react-dom/client');
var dom = require('@testing-library/dom');
var testUtils = require('react-dom/test-utils');
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n["default"] = e;
return Object.freeze(n);
}
var React__namespace = /*#__PURE__*/_interopNamespace(React);
var ReactDOM__default = /*#__PURE__*/_interopDefaultLegacy(ReactDOM);
var ReactDOMClient__namespace = /*#__PURE__*/_interopNamespace(ReactDOMClient);
var testUtils__namespace = /*#__PURE__*/_interopNamespace(testUtils);
const domAct = testUtils__namespace.act;
function getGlobalThis() {
/* istanbul ignore else */
if (typeof globalThis !== 'undefined') {
return globalThis;
}
/* istanbul ignore next */
if (typeof self !== 'undefined') {
return self;
}
/* istanbul ignore next */
if (typeof window !== 'undefined') {
return window;
}
/* istanbul ignore next */
if (typeof global !== 'undefined') {
return global;
}
/* istanbul ignore next */
throw new Error('unable to locate global object');
}
function setIsReactActEnvironment(isReactActEnvironment) {
getGlobalThis().IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment;
}
function getIsReactActEnvironment() {
return getGlobalThis().IS_REACT_ACT_ENVIRONMENT;
}
function withGlobalActEnvironment(actImplementation) {
return callback => {
const previousActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(true);
try {
// The return value of `act` is always a thenable.
let callbackNeedsToBeAwaited = false;
const actResult = actImplementation(() => {
const result = callback();
if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
callbackNeedsToBeAwaited = true;
}
return result;
});
if (callbackNeedsToBeAwaited) {
const thenable = actResult;
return {
then: (resolve, reject) => {
thenable.then(returnValue => {
setIsReactActEnvironment(previousActEnvironment);
resolve(returnValue);
}, error => {
setIsReactActEnvironment(previousActEnvironment);
reject(error);
});
}
};
} else {
setIsReactActEnvironment(previousActEnvironment);
return actResult;
}
} catch (error) {
// Can't be a `finally {}` block since we don't know if we have to immediately restore IS_REACT_ACT_ENVIRONMENT
// or if we have to await the callback first.
setIsReactActEnvironment(previousActEnvironment);
throw error;
}
};
}
const act = withGlobalActEnvironment(domAct);
/* eslint no-console:0 */
// dom-testing-library's version of fireEvent. The reason
// we make this distinction however is because we have
// a few extra events that work a bit differently
const fireEvent = function () {
return dom.fireEvent(...arguments);
};
Object.keys(dom.fireEvent).forEach(key => {
fireEvent[key] = function () {
return dom.fireEvent[key](...arguments);
};
}); // React event system tracks native mouseOver/mouseOut events for
// running onMouseEnter/onMouseLeave handlers
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/EnterLeaveEventPlugin.js#L24-L31
const mouseEnter = fireEvent.mouseEnter;
const mouseLeave = fireEvent.mouseLeave;
fireEvent.mouseEnter = function () {
mouseEnter(...arguments);
return fireEvent.mouseOver(...arguments);
};
fireEvent.mouseLeave = function () {
mouseLeave(...arguments);
return fireEvent.mouseOut(...arguments);
};
const pointerEnter = fireEvent.pointerEnter;
const pointerLeave = fireEvent.pointerLeave;
fireEvent.pointerEnter = function () {
pointerEnter(...arguments);
return fireEvent.pointerOver(...arguments);
};
fireEvent.pointerLeave = function () {
pointerLeave(...arguments);
return fireEvent.pointerOut(...arguments);
};
const select = fireEvent.select;
fireEvent.select = (node, init) => {
select(node, init); // React tracks this event only on focused inputs
node.focus(); // React creates this event when one of the following native events happens
// - contextMenu
// - mouseUp
// - dragEnd
// - keyUp
// - keyDown
// so we can use any here
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/SelectEventPlugin.js#L203-L224
fireEvent.keyUp(node, init);
}; // React event system tracks native focusout/focusin events for
// running blur/focus handlers
// @link https://github.com/facebook/react/pull/19186
const blur = fireEvent.blur;
const focus = fireEvent.focus;
fireEvent.blur = function () {
fireEvent.focusOut(...arguments);
return blur(...arguments);
};
fireEvent.focus = function () {
fireEvent.focusIn(...arguments);
return focus(...arguments);
};
dom.configure({
unstable_advanceTimersWrapper: cb => {
return act(cb);
},
// We just want to run `waitFor` without IS_REACT_ACT_ENVIRONMENT
// But that's not necessarily how `asyncWrapper` is used since it's a public method.
// Let's just hope nobody else is using it.
asyncWrapper: async cb => {
const previousActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(false);
try {
return await cb();
} finally {
setIsReactActEnvironment(previousActEnvironment);
}
},
eventWrapper: cb => {
let result;
act(() => {
result = cb();
});
return result;
}
}); // Ideally we'd just use a WeakMap where containers are keys and roots are values.
// We use two variables so that we can bail out in constant time when we render with a new container (most common use case)
/**
* @type {Set<import('react-dom').Container>}
*/
const mountedContainers = new Set();
/**
* @type Array<{container: import('react-dom').Container, root: ReturnType<typeof createConcurrentRoot>}>
*/
const mountedRootEntries = [];
function createConcurrentRoot(container, _ref) {
let {
hydrate,
ui,
wrapper: WrapperComponent
} = _ref;
let root;
if (hydrate) {
act(() => {
root = ReactDOMClient__namespace.hydrateRoot(container, WrapperComponent ? /*#__PURE__*/React__namespace.createElement(WrapperComponent, null, ui) : ui);
});
} else {
root = ReactDOMClient__namespace.createRoot(container);
}
return {
hydrate() {
/* istanbul ignore if */
if (!hydrate) {
throw new Error('Attempted to hydrate a non-hydrateable root. This is a bug in `@testing-library/react`.');
} // Nothing to do since hydration happens when creating the root object.
},
render(element) {
root.render(element);
},
unmount() {
root.unmount();
}
};
}
function createLegacyRoot(container) {
return {
hydrate(element) {
ReactDOM__default["default"].hydrate(element, container);
},
render(element) {
ReactDOM__default["default"].render(element, container);
},
unmount() {
ReactDOM__default["default"].unmountComponentAtNode(container);
}
};
}
function renderRoot(ui, _ref2) {
let {
baseElement,
container,
hydrate,
queries,
root,
wrapper: WrapperComponent
} = _ref2;
const wrapUiIfNeeded = innerElement => WrapperComponent ? /*#__PURE__*/React__namespace.createElement(WrapperComponent, null, innerElement) : innerElement;
act(() => {
if (hydrate) {
root.hydrate(wrapUiIfNeeded(ui), container);
} else {
root.render(wrapUiIfNeeded(ui), container);
}
});
return {
container,
baseElement,
debug: function (el, maxLength, options) {
if (el === void 0) {
el = baseElement;
}
return Array.isArray(el) ? // eslint-disable-next-line no-console
el.forEach(e => console.log(dom.prettyDOM(e, maxLength, options))) : // eslint-disable-next-line no-console,
console.log(dom.prettyDOM(el, maxLength, options));
},
unmount: () => {
act(() => {
root.unmount();
});
},
rerender: rerenderUi => {
renderRoot(wrapUiIfNeeded(rerenderUi), {
container,
baseElement,
root
}); // Intentionally do not return anything to avoid unnecessarily complicating the API.
// folks can use all the same utilities we return in the first place that are bound to the container
},
asFragment: () => {
/* istanbul ignore else (old jsdom limitation) */
if (typeof document.createRange === 'function') {
return document.createRange().createContextualFragment(container.innerHTML);
} else {
const template = document.createElement('template');
template.innerHTML = container.innerHTML;
return template.content;
}
},
...dom.getQueriesForElement(baseElement, queries)
};
}
function render(ui, _temp) {
let {
container,
baseElement = container,
legacyRoot = false,
queries,
hydrate = false,
wrapper
} = _temp === void 0 ? {} : _temp;
if (!baseElement) {
// default to document.body instead of documentElement to avoid output of potentially-large
// head elements (such as JSS style blocks) in debug output
baseElement = document.body;
}
if (!container) {
container = baseElement.appendChild(document.createElement('div'));
}
let root; // eslint-disable-next-line no-negated-condition -- we want to map the evolution of this over time. The root is created first. Only later is it re-used so we don't want to read the case that happens later first.
if (!mountedContainers.has(container)) {
const createRootImpl = legacyRoot ? createLegacyRoot : createConcurrentRoot;
root = createRootImpl(container, {
hydrate,
ui,
wrapper
});
mountedRootEntries.push({
container,
root
}); // we'll add it to the mounted containers regardless of whether it's actually
// added to document.body so the cleanup method works regardless of whether
// they're passing us a custom container or not.
mountedContainers.add(container);
} else {
mountedRootEntries.forEach(rootEntry => {
// Else is unreachable since `mountedContainers` has the `container`.
// Only reachable if one would accidentally add the container to `mountedContainers` but not the root to `mountedRootEntries`
/* istanbul ignore else */
if (rootEntry.container === container) {
root = rootEntry.root;
}
});
}
return renderRoot(ui, {
container,
baseElement,
queries,
hydrate,
wrapper,
root
});
}
function cleanup() {
mountedRootEntries.forEach(_ref3 => {
let {
root,
container
} = _ref3;
act(() => {
root.unmount();
});
if (container.parentNode === document.body) {
document.body.removeChild(container);
}
});
mountedRootEntries.length = 0;
mountedContainers.clear();
}
function renderHook(renderCallback, options) {
if (options === void 0) {
options = {};
}
const {
initialProps,
...renderOptions
} = options;
const result = /*#__PURE__*/React__namespace.createRef();
function TestComponent(_ref4) {
let {
renderCallbackProps
} = _ref4;
const pendingResult = renderCallback(renderCallbackProps);
React__namespace.useEffect(() => {
result.current = pendingResult;
});
return null;
}
const {
rerender: baseRerender,
unmount
} = render( /*#__PURE__*/React__namespace.createElement(TestComponent, {
renderCallbackProps: initialProps
}), renderOptions);
function rerender(rerenderCallbackProps) {
return baseRerender( /*#__PURE__*/React__namespace.createElement(TestComponent, {
renderCallbackProps: rerenderCallbackProps
}));
}
return {
result,
rerender,
unmount
};
} // just re-export everything from dom-testing-library
/* eslint func-name-matching:0 */
exports.act = act;
exports.cleanup = cleanup;
exports.fireEvent = fireEvent;
exports.render = render;
exports.renderHook = renderHook;
Object.keys(dom).forEach(function (k) {
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
enumerable: true,
get: function () { return dom[k]; }
});
});

View File

@@ -0,0 +1,438 @@
import * as React from 'react';
import ReactDOM from 'react-dom';
import * as ReactDOMClient from 'react-dom/client';
import { fireEvent as fireEvent$1, configure, prettyDOM, getQueriesForElement } from '@testing-library/dom';
export * from '@testing-library/dom';
import * as testUtils from 'react-dom/test-utils';
const domAct = testUtils.act;
function getGlobalThis() {
/* istanbul ignore else */
if (typeof globalThis !== 'undefined') {
return globalThis;
}
/* istanbul ignore next */
if (typeof self !== 'undefined') {
return self;
}
/* istanbul ignore next */
if (typeof window !== 'undefined') {
return window;
}
/* istanbul ignore next */
if (typeof global !== 'undefined') {
return global;
}
/* istanbul ignore next */
throw new Error('unable to locate global object');
}
function setIsReactActEnvironment(isReactActEnvironment) {
getGlobalThis().IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment;
}
function getIsReactActEnvironment() {
return getGlobalThis().IS_REACT_ACT_ENVIRONMENT;
}
function withGlobalActEnvironment(actImplementation) {
return callback => {
const previousActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(true);
try {
// The return value of `act` is always a thenable.
let callbackNeedsToBeAwaited = false;
const actResult = actImplementation(() => {
const result = callback();
if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
callbackNeedsToBeAwaited = true;
}
return result;
});
if (callbackNeedsToBeAwaited) {
const thenable = actResult;
return {
then: (resolve, reject) => {
thenable.then(returnValue => {
setIsReactActEnvironment(previousActEnvironment);
resolve(returnValue);
}, error => {
setIsReactActEnvironment(previousActEnvironment);
reject(error);
});
}
};
} else {
setIsReactActEnvironment(previousActEnvironment);
return actResult;
}
} catch (error) {
// Can't be a `finally {}` block since we don't know if we have to immediately restore IS_REACT_ACT_ENVIRONMENT
// or if we have to await the callback first.
setIsReactActEnvironment(previousActEnvironment);
throw error;
}
};
}
const act = withGlobalActEnvironment(domAct);
/* eslint no-console:0 */
// dom-testing-library's version of fireEvent. The reason
// we make this distinction however is because we have
// a few extra events that work a bit differently
const fireEvent = function () {
return fireEvent$1(...arguments);
};
Object.keys(fireEvent$1).forEach(key => {
fireEvent[key] = function () {
return fireEvent$1[key](...arguments);
};
}); // React event system tracks native mouseOver/mouseOut events for
// running onMouseEnter/onMouseLeave handlers
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/EnterLeaveEventPlugin.js#L24-L31
const mouseEnter = fireEvent.mouseEnter;
const mouseLeave = fireEvent.mouseLeave;
fireEvent.mouseEnter = function () {
mouseEnter(...arguments);
return fireEvent.mouseOver(...arguments);
};
fireEvent.mouseLeave = function () {
mouseLeave(...arguments);
return fireEvent.mouseOut(...arguments);
};
const pointerEnter = fireEvent.pointerEnter;
const pointerLeave = fireEvent.pointerLeave;
fireEvent.pointerEnter = function () {
pointerEnter(...arguments);
return fireEvent.pointerOver(...arguments);
};
fireEvent.pointerLeave = function () {
pointerLeave(...arguments);
return fireEvent.pointerOut(...arguments);
};
const select = fireEvent.select;
fireEvent.select = (node, init) => {
select(node, init); // React tracks this event only on focused inputs
node.focus(); // React creates this event when one of the following native events happens
// - contextMenu
// - mouseUp
// - dragEnd
// - keyUp
// - keyDown
// so we can use any here
// @link https://github.com/facebook/react/blob/b87aabdfe1b7461e7331abb3601d9e6bb27544bc/packages/react-dom/src/events/SelectEventPlugin.js#L203-L224
fireEvent.keyUp(node, init);
}; // React event system tracks native focusout/focusin events for
// running blur/focus handlers
// @link https://github.com/facebook/react/pull/19186
const blur = fireEvent.blur;
const focus = fireEvent.focus;
fireEvent.blur = function () {
fireEvent.focusOut(...arguments);
return blur(...arguments);
};
fireEvent.focus = function () {
fireEvent.focusIn(...arguments);
return focus(...arguments);
};
configure({
unstable_advanceTimersWrapper: cb => {
return act(cb);
},
// We just want to run `waitFor` without IS_REACT_ACT_ENVIRONMENT
// But that's not necessarily how `asyncWrapper` is used since it's a public method.
// Let's just hope nobody else is using it.
asyncWrapper: async cb => {
const previousActEnvironment = getIsReactActEnvironment();
setIsReactActEnvironment(false);
try {
return await cb();
} finally {
setIsReactActEnvironment(previousActEnvironment);
}
},
eventWrapper: cb => {
let result;
act(() => {
result = cb();
});
return result;
}
}); // Ideally we'd just use a WeakMap where containers are keys and roots are values.
// We use two variables so that we can bail out in constant time when we render with a new container (most common use case)
/**
* @type {Set<import('react-dom').Container>}
*/
const mountedContainers = new Set();
/**
* @type Array<{container: import('react-dom').Container, root: ReturnType<typeof createConcurrentRoot>}>
*/
const mountedRootEntries = [];
function createConcurrentRoot(container, _ref) {
let {
hydrate,
ui,
wrapper: WrapperComponent
} = _ref;
let root;
if (hydrate) {
act(() => {
root = ReactDOMClient.hydrateRoot(container, WrapperComponent ? /*#__PURE__*/React.createElement(WrapperComponent, null, ui) : ui);
});
} else {
root = ReactDOMClient.createRoot(container);
}
return {
hydrate() {
/* istanbul ignore if */
if (!hydrate) {
throw new Error('Attempted to hydrate a non-hydrateable root. This is a bug in `@testing-library/react`.');
} // Nothing to do since hydration happens when creating the root object.
},
render(element) {
root.render(element);
},
unmount() {
root.unmount();
}
};
}
function createLegacyRoot(container) {
return {
hydrate(element) {
ReactDOM.hydrate(element, container);
},
render(element) {
ReactDOM.render(element, container);
},
unmount() {
ReactDOM.unmountComponentAtNode(container);
}
};
}
function renderRoot(ui, _ref2) {
let {
baseElement,
container,
hydrate,
queries,
root,
wrapper: WrapperComponent
} = _ref2;
const wrapUiIfNeeded = innerElement => WrapperComponent ? /*#__PURE__*/React.createElement(WrapperComponent, null, innerElement) : innerElement;
act(() => {
if (hydrate) {
root.hydrate(wrapUiIfNeeded(ui), container);
} else {
root.render(wrapUiIfNeeded(ui), container);
}
});
return {
container,
baseElement,
debug: function (el, maxLength, options) {
if (el === void 0) {
el = baseElement;
}
return Array.isArray(el) ? // eslint-disable-next-line no-console
el.forEach(e => console.log(prettyDOM(e, maxLength, options))) : // eslint-disable-next-line no-console,
console.log(prettyDOM(el, maxLength, options));
},
unmount: () => {
act(() => {
root.unmount();
});
},
rerender: rerenderUi => {
renderRoot(wrapUiIfNeeded(rerenderUi), {
container,
baseElement,
root
}); // Intentionally do not return anything to avoid unnecessarily complicating the API.
// folks can use all the same utilities we return in the first place that are bound to the container
},
asFragment: () => {
/* istanbul ignore else (old jsdom limitation) */
if (typeof document.createRange === 'function') {
return document.createRange().createContextualFragment(container.innerHTML);
} else {
const template = document.createElement('template');
template.innerHTML = container.innerHTML;
return template.content;
}
},
...getQueriesForElement(baseElement, queries)
};
}
function render(ui, _temp) {
let {
container,
baseElement = container,
legacyRoot = false,
queries,
hydrate = false,
wrapper
} = _temp === void 0 ? {} : _temp;
if (!baseElement) {
// default to document.body instead of documentElement to avoid output of potentially-large
// head elements (such as JSS style blocks) in debug output
baseElement = document.body;
}
if (!container) {
container = baseElement.appendChild(document.createElement('div'));
}
let root; // eslint-disable-next-line no-negated-condition -- we want to map the evolution of this over time. The root is created first. Only later is it re-used so we don't want to read the case that happens later first.
if (!mountedContainers.has(container)) {
const createRootImpl = legacyRoot ? createLegacyRoot : createConcurrentRoot;
root = createRootImpl(container, {
hydrate,
ui,
wrapper
});
mountedRootEntries.push({
container,
root
}); // we'll add it to the mounted containers regardless of whether it's actually
// added to document.body so the cleanup method works regardless of whether
// they're passing us a custom container or not.
mountedContainers.add(container);
} else {
mountedRootEntries.forEach(rootEntry => {
// Else is unreachable since `mountedContainers` has the `container`.
// Only reachable if one would accidentally add the container to `mountedContainers` but not the root to `mountedRootEntries`
/* istanbul ignore else */
if (rootEntry.container === container) {
root = rootEntry.root;
}
});
}
return renderRoot(ui, {
container,
baseElement,
queries,
hydrate,
wrapper,
root
});
}
function cleanup() {
mountedRootEntries.forEach(_ref3 => {
let {
root,
container
} = _ref3;
act(() => {
root.unmount();
});
if (container.parentNode === document.body) {
document.body.removeChild(container);
}
});
mountedRootEntries.length = 0;
mountedContainers.clear();
}
function renderHook(renderCallback, options) {
if (options === void 0) {
options = {};
}
const {
initialProps,
...renderOptions
} = options;
const result = /*#__PURE__*/React.createRef();
function TestComponent(_ref4) {
let {
renderCallbackProps
} = _ref4;
const pendingResult = renderCallback(renderCallbackProps);
React.useEffect(() => {
result.current = pendingResult;
});
return null;
}
const {
rerender: baseRerender,
unmount
} = render( /*#__PURE__*/React.createElement(TestComponent, {
renderCallbackProps: initialProps
}), renderOptions);
function rerender(rerenderCallbackProps) {
return baseRerender( /*#__PURE__*/React.createElement(TestComponent, {
renderCallbackProps: rerenderCallbackProps
}));
}
return {
result,
rerender,
unmount
};
} // just re-export everything from dom-testing-library
/* eslint func-name-matching:0 */
export { act, cleanup, fireEvent, render, renderHook };

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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