diff --git a/.all-contributorsrc b/.all-contributorsrc
index bccd25fea..9bc2e912d 100644
--- a/.all-contributorsrc
+++ b/.all-contributorsrc
@@ -84,7 +84,8 @@
"avatar_url": "https://avatars0.githubusercontent.com/u/9197791?v=4",
"profile": "https://afontcu.dev/",
"contributions": [
- "doc"
+ "doc",
+ "review"
]
},
{
@@ -93,7 +94,8 @@
"avatar_url": "https://avatars1.githubusercontent.com/u/28659384?v=4",
"profile": "http://timdeschryver.dev/",
"contributions": [
- "doc"
+ "doc",
+ "code"
]
},
{
@@ -132,11 +134,2679 @@
"contributions": [
"doc"
]
+ },
+ {
+ "login": "michalak111",
+ "name": "michalak111",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/9253631?v=4",
+ "profile": "https://twitter.com/jasi3k11",
+ "contributions": [
+ "doc",
+ "test"
+ ]
+ },
+ {
+ "login": "huyenltnguyen",
+ "name": "Huyen Nguyen",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/25715018?v=4",
+ "profile": "https://github.com/huyenltnguyen",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "kierenhughes",
+ "name": "Kieren Hughes",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/2124299?v=4",
+ "profile": "http://www.kierenhughes.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "SeanMcP",
+ "name": "Sean McPherson",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/6360367?v=4",
+ "profile": "https://seanmcp.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "michaellasky",
+ "name": "Michael Lasky",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/6646599?v=4",
+ "profile": "https://github.com/michaellasky",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "thomlom",
+ "name": "Thomas Lombart",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/16003285?v=4",
+ "profile": "http://thomlom.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Pklong",
+ "name": "Patrick K Long",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/10551697?v=4",
+ "profile": "https://pklong.io/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "pedroapfilho",
+ "name": "Pedro Filho",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/13142568?v=4",
+ "profile": "https://github.com/pedroapfilho",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "tanguyantoine",
+ "name": "Tanguy Antoine",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/263097?v=4",
+ "profile": "https://github.com/tanguyantoine",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Agentkma",
+ "name": "Kevin Anderson",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/22228809?v=4",
+ "profile": "https://kevinanderson.codes/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "dustinmyers",
+ "name": "Dustin Myers",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/10288477?v=4",
+ "profile": "https://github.com/dustinmyers",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "vojtaholik",
+ "name": "Vojta Holik",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/25487857?v=4",
+ "profile": "http://vojta.io/",
+ "contributions": [
+ "design"
+ ]
+ },
+ {
+ "login": "saschwarz",
+ "name": "Steve Schwarz",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/184171?v=4",
+ "profile": "http://tech.agilitynerd.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "aayushrajvanshi",
+ "name": "Aayush Rajvanshi",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/14968551?v=4",
+ "profile": "https://github.com/aayushrajvanshi",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "eugiellimpin",
+ "name": "Eugie Limpin",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/431442?v=4",
+ "profile": "http://eugielimpin.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "KevinHu2014",
+ "name": "Kevin",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/7204070?v=4",
+ "profile": "https://hsiangyu.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jennifer-shehane",
+ "name": "Jennifer Shehane",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/1271364?v=4",
+ "profile": "https://github.com/jennifer-shehane",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "stevetaggart",
+ "name": "Steve Taggart",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/11730266?v=4",
+ "profile": "https://github.com/stevetaggart",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "grncdr",
+ "name": "Stephen Sugden",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/82634?v=4",
+ "profile": "http://stephensugden.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Samitier",
+ "name": "Blai Samitier",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/4160121?v=4",
+ "profile": "http://samitier.github.io/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "vernonk",
+ "name": "Vernon Kesner",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/74096?v=4",
+ "profile": "https://github.com/vernonk",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "wdoug",
+ "name": "Will Douglas",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/5432102?v=4",
+ "profile": "https://github.com/wdoug",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "HTMLhead",
+ "name": "Head",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/40166539?v=4",
+ "profile": "https://velog.io/@head",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mynar7",
+ "name": "Lee",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/32332479?v=4",
+ "profile": "https://github.com/mynar7",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Belco90",
+ "name": "Mario Beltrán Alarcón",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/2677072?v=4",
+ "profile": "https://mario.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jameslevine",
+ "name": "jameslevine",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/41184245?v=4",
+ "profile": "https://github.com/jameslevine",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mihar-22",
+ "name": "Rahim Alwer",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/14304599?v=4",
+ "profile": "https://github.com/mihar-22",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ariesjia",
+ "name": "Chenjia",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/1797160?v=4",
+ "profile": "http://www.btorange.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "olethanh",
+ "name": "Olivier Le Thanh Duong",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/82500?v=4",
+ "profile": "http://blog.staz.be/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "KubaJastrz",
+ "name": "Jakub Jastrzębski",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/6443113?v=4",
+ "profile": "https://kubajastrz.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "trurl-master",
+ "name": "Ivan Galiatin",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/640657?v=4",
+ "profile": "http://russianbrandgardeners.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "HendrikRoehm",
+ "name": "Hendrik Röhm",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/26203080?v=4",
+ "profile": "https://github.com/HendrikRoehm",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "samvk",
+ "name": "Sam Kauffman",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/12996081?v=4",
+ "profile": "https://samvk.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "turadg",
+ "name": "Turadg Aleahmad",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/21505?v=4",
+ "profile": "http://turadg.aleahmad.net/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "MarkGeeRomano",
+ "name": "mark g romano",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/13630752?v=4",
+ "profile": "https://github.com/MarkGeeRomano",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "arturoromeroslc",
+ "name": "Arturo Romero",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/7406639?v=4",
+ "profile": "https://github.com/arturoromeroslc",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "dustinsoftware",
+ "name": "Dustin Masters",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/942358?v=4",
+ "profile": "https://www.dustinsoftware.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "darren-lester",
+ "name": "Darren Lester",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/19534488?v=4",
+ "profile": "https://www.darrenlester.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "iswara108",
+ "name": "Iswara Chaitanya",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/9681451?v=4",
+ "profile": "https://github.com/iswara108",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "pylnata",
+ "name": "Nataliia Pylypenko",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/33361478?v=4",
+ "profile": "https://github.com/pylnata",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "huchenme",
+ "name": "Hu Chen",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/2078389?v=4",
+ "profile": "https://huchen.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jkdowdle",
+ "name": "Josh",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/19804196?v=4",
+ "profile": "https://github.com/jkdowdle",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "numb86",
+ "name": "numb86",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/16703337?v=4",
+ "profile": "https://numb86.net/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "NicholasBoll",
+ "name": "Nicholas Boll",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/338257?v=4",
+ "profile": "https://github.com/NicholasBoll",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Billy-",
+ "name": "Billy Matthews",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/4316168?v=4",
+ "profile": "https://github.com/Billy-",
+ "contributions": [
+ "talk",
+ "doc"
+ ]
+ },
+ {
+ "login": "dale-french",
+ "name": "Dale French",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/6544116?v=4",
+ "profile": "https://github.com/dale-french",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "aw-davidson",
+ "name": "aw-davidson",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/32170938?v=4",
+ "profile": "https://github.com/aw-davidson",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "BBlackwo",
+ "name": "Benjamin Blackwood",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/7598058?v=4",
+ "profile": "https://twitter.com/B_Blackwo",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "rockingskier",
+ "name": "Ben",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/681614?v=4",
+ "profile": "https://github.com/rockingskier",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "danieljcafonso",
+ "name": "Daniel Afonso",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/35337607?v=4",
+ "profile": "https://github.com/danieljcafonso",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "NomanGul",
+ "name": "Noman Gul",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/39244918?v=4",
+ "profile": "https://dev.to/nomangul",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "rafaelangical",
+ "name": "Rafael Souza",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/20307803?v=4",
+ "profile": "http://rafatech.tk/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "pustovalov",
+ "name": "Pavel Pustovalov",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/1568885?v=4",
+ "profile": "http://pustovalov.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "dymafr",
+ "name": "Dyma",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/32801166?v=4",
+ "profile": "https://github.com/dymafr",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "MatanBobi",
+ "name": "Matan Borenkraout",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/12711091?v=4",
+ "profile": "https://twitter.com/matan_bobi",
+ "contributions": [
+ "doc",
+ "code"
+ ]
+ },
+ {
+ "login": "timrobinson33",
+ "name": "timrobinson33",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/57178390?v=4",
+ "profile": "https://github.com/timrobinson33",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mdugue",
+ "name": "Manuel Dugué",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/894149?v=4",
+ "profile": "http://manueldugue.de/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "karthik20",
+ "name": "karthik20",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/12153250?v=4",
+ "profile": "https://github.com/karthik20",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "pobch",
+ "name": "Pob Ch",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/19894957?v=4",
+ "profile": "https://github.com/pobch",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mkermani144",
+ "name": "Mohammad Kermani",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/12621708?v=4",
+ "profile": "https://github.com/mkermani144",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "adeelibr",
+ "name": "Adeel Imran",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/16651811?v=4",
+ "profile": "http://twitter.com/adeelibr",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "varundey",
+ "name": "Varun Dey",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/9202135?v=4",
+ "profile": "https://varundey.me/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "PabloDinella",
+ "name": "Pablo R. Dinella",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/2482730?v=4",
+ "profile": "http://pablodinella.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jamsinclair",
+ "name": "Jamie",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/5964236?v=4",
+ "profile": "https://jamie.tokyo/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "coryhouse",
+ "name": "Cory House",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/1688997?v=4",
+ "profile": "http://www.bitnative.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "bugzpodder",
+ "name": "Jack Zhao",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/14841421?v=4",
+ "profile": "http://fb.me/yz",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "rkennel",
+ "name": "Ryan Kennel",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/28492538?v=4",
+ "profile": "https://github.com/rkennel",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "JesuHrz",
+ "name": "Jesus Hernandez",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/28031187?v=4",
+ "profile": "https://github.com/JesuHrz",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "airjp73",
+ "name": "Aaron Pettengill",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/25882770?v=4",
+ "profile": "https://github.com/airjp73",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Izhaki",
+ "name": "Izhaki",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/880132?v=4",
+ "profile": "https://github.com/Izhaki",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "rwieruch",
+ "name": "Robin Wieruch",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/2479967?v=4",
+ "profile": "https://www.robinwieruch.de/",
+ "contributions": [
+ "tutorial"
+ ]
+ },
+ {
+ "login": "Alex-Sokolov",
+ "name": "Alexander Sokolov",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/4497128?v=4",
+ "profile": "https://github.com/Alex-Sokolov",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "sastan",
+ "name": "Sascha Tandel",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/514405?v=4",
+ "profile": "https://github.com/sastan",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "gyuwon",
+ "name": "Gyuwon Yi",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/973743?v=4",
+ "profile": "https://gyuwon.github.io/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "just-boris",
+ "name": "Boris Serdiuk",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/812240?v=4",
+ "profile": "https://twitter.com/boriscoder",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "balavishnuvj",
+ "name": "balavishnuvj",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/13718688?v=4",
+ "profile": "https://github.com/balavishnuvj",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "sharmilajesupaul",
+ "name": "Sharmila Jesupaul",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/7876997?v=4",
+ "profile": "http://jesupaul.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "BatuhanW",
+ "name": "Batuhan Wilhelm",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/16444991?v=4",
+ "profile": "https://github.com/BatuhanW",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "deadkff01",
+ "name": "Dennis Kaffer",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/5138179?v=4",
+ "profile": "https://github.com/deadkff01",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "camjackson",
+ "name": "Cam Jackson",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/1930451?v=4",
+ "profile": "http://camjackson.net/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jluxenberg",
+ "name": "Jared Luxenberg",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/450478?v=4",
+ "profile": "http://www.jaredlux.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "yakirn",
+ "name": "Yakir Narkis",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/6659632?v=4",
+ "profile": "https://github.com/yakirn",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "rahulbhooteshwar",
+ "name": "Rahul Bhooteshwar",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/15920476?v=4",
+ "profile": "https://rahulbhooteshwar.github.io/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "majapw",
+ "name": "Maja Wichrowska",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/1383861?v=4",
+ "profile": "https://github.com/majapw",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "m98",
+ "name": "Kermani",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/7496103?v=4",
+ "profile": "http://medium.com/@kermani",
+ "contributions": [
+ "doc",
+ "review"
+ ]
+ },
+ {
+ "login": "malykhinvi",
+ "name": "Vasily Malykhin",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/5537715?v=4",
+ "profile": "https://malykhinvi.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "brrianalexis",
+ "name": "Brian Alexis",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/51463930?v=4",
+ "profile": "https://github.com/brrianalexis",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "kalpeshsingh",
+ "name": "Kalpesh Singh",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/13119410?v=4",
+ "profile": "http://knowkalpesh.in/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ljosberinn",
+ "name": "Gerrit Alex",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/29307652?v=4",
+ "profile": "http://gerritalex.de/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "winterlamon",
+ "name": "Winter LaMon",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/16295605?v=4",
+ "profile": "https://github.com/winterlamon",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jpenna",
+ "name": "Juliano Penna",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/16005946?v=4",
+ "profile": "https://github.com/jpenna",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "PaulACoroneos",
+ "name": "Paul Coroneos",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/40515238?v=4",
+ "profile": "http://www.pcoroneos.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "eps1lon",
+ "name": "Sebastian Silbermann",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/12292047?v=4",
+ "profile": "https://solverfox.dev/",
+ "contributions": [
+ "doc",
+ "review"
+ ]
+ },
+ {
+ "login": "psalaets",
+ "name": "Paul Salaets",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/123952?v=4",
+ "profile": "https://paulsalaets.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "michaeldeboey",
+ "name": "Michaël De Boey",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/6643991?v=4",
+ "profile": "https://michaeldeboey.be/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "washingtonsoares",
+ "name": "Washington Soares",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/5726140?v=4",
+ "profile": "https://thewashington.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jamisuomalainen",
+ "name": "Jami Suomalainen",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/19325270?v=4",
+ "profile": "https://github.com/jamisuomalainen",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "oriolpuig",
+ "name": "Oriol Puig",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/3933098?v=4",
+ "profile": "http://www.oriolpuig.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "hedgecox",
+ "name": "hedgecox",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/41297418?v=4",
+ "profile": "https://hedgecox.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "shermanhui",
+ "name": "Sherman Hui",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/11592023?v=4",
+ "profile": "https://github.com/shermanhui",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jesujcastillom",
+ "name": "Jesu Castillo",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/7827281?v=4",
+ "profile": "https://github.com/jesujcastillom",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "corehtml5canvas",
+ "name": "Core HTML5 Canvas, the book",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/1553693?v=4",
+ "profile": "http://gearysite.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "johnste",
+ "name": "John Sterling",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/886051?v=4",
+ "profile": "https://github.com/johnste",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "velusgautam",
+ "name": "Velu S Gautam",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/8556085?v=4",
+ "profile": "http://www.velusgautam.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "RichardBray",
+ "name": "Richard Oliver Bray",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/1377253?v=4",
+ "profile": "https://www.youtube.com/c/RichardBray",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "cncolder",
+ "name": "Yanlin Jiang",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/127009?v=4",
+ "profile": "https://github.com/cncolder",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "lidoravitan",
+ "name": "Lidor Avitan",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/35113398?v=4",
+ "profile": "https://github.com/lidoravitan",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ITenthusiasm",
+ "name": "Isaiah Thomason",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/47364027?v=4",
+ "profile": "https://github.com/ITenthusiasm",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "denis",
+ "name": "Denis Barushev",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/805?v=4",
+ "profile": "http://barushev.net/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "domasx2",
+ "name": "Domas",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/847684?v=4",
+ "profile": "https://github.com/domasx2",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "AntonNiklasson",
+ "name": "Anton Niklasson",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/785676?v=4",
+ "profile": "http://www.antn.se/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "nick722",
+ "name": "Nikolai Yakuschenko",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/31370625?v=4",
+ "profile": "https://github.com/nick722",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "vier31",
+ "name": "Jan Schröder",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/34372068?v=4",
+ "profile": "https://github.com/vier31",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "nickmccurdy",
+ "name": "Nick McCurdy",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/927220?v=4",
+ "profile": "https://nickmccurdy.com/",
+ "contributions": [
+ "doc",
+ "review",
+ "code"
+ ]
+ },
+ {
+ "login": "tsuki42",
+ "name": "Sudhanshu",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/22864071?v=4",
+ "profile": "http://sudhanshu-ranjan.tech/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "aleks-rope",
+ "name": "Aleksandr Chernov",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/58922379?v=4",
+ "profile": "http://newrope.biz/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mathiassoeholm",
+ "name": "Mathias",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/1747242?v=4",
+ "profile": "https://github.com/mathiassoeholm",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "davidseow",
+ "name": "davidseow",
+ "avatar_url": "https://avatars1.githubusercontent.com/u/502503?v=4",
+ "profile": "https://github.com/davidseow",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "tonyhallett",
+ "name": "Tony Hallett",
+ "avatar_url": "https://avatars2.githubusercontent.com/u/11292998?v=4",
+ "profile": "https://github.com/tonyhallett",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "prsdta",
+ "name": "prsdta",
+ "avatar_url": "https://avatars3.githubusercontent.com/u/19373361?v=4",
+ "profile": "https://github.com/prsdta",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "tal-joffe",
+ "name": "tal-joffe",
+ "avatar_url": "https://avatars0.githubusercontent.com/u/7221753?v=4",
+ "profile": "https://github.com/tal-joffe",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mayank23",
+ "name": "Mayank Jethva",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1103708?v=4",
+ "profile": "http://mayankjethva.xyz/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "EladIsraeli",
+ "name": "Elad Israeli",
+ "avatar_url": "https://avatars.githubusercontent.com/u/13487086?v=4",
+ "profile": "https://github.com/EladIsraeli",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "chartrandf",
+ "name": "Francis Chartrand",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1503758?v=4",
+ "profile": "https://francischartrand.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "prestoncarman",
+ "name": "Preston Carman",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3517157?v=4",
+ "profile": "https://github.com/prestoncarman",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "olivierwilkinson",
+ "name": "Olivier Wilkinson",
+ "avatar_url": "https://avatars.githubusercontent.com/u/15321261?v=4",
+ "profile": "https://github.com/olivierwilkinson",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "amitmiran137",
+ "name": "Amit Miran",
+ "avatar_url": "https://avatars.githubusercontent.com/u/47772523?v=4",
+ "profile": "https://github.com/amitmiran137",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "gangsthub",
+ "name": "Paul Melero",
+ "avatar_url": "https://avatars.githubusercontent.com/u/6775220?v=4",
+ "profile": "https://graficos.net/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "modosc",
+ "name": "jonathan schatz",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2231664?v=4",
+ "profile": "https://modo.sc/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "rickysullivan",
+ "name": "Ricky Sullivan Himself",
+ "avatar_url": "https://avatars.githubusercontent.com/u/437480?v=4",
+ "profile": "https://github.com/rickysullivan",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "wuarmin",
+ "name": "Armin",
+ "avatar_url": "https://avatars.githubusercontent.com/u/9987680?v=4",
+ "profile": "https://github.com/wuarmin",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "dalebaldwin",
+ "name": "Dale Baldwin",
+ "avatar_url": "https://avatars.githubusercontent.com/u/403546?v=4",
+ "profile": "http://linktr.ee/dalebaldwin",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "AdriSolid",
+ "name": "AdriSolid",
+ "avatar_url": "https://avatars.githubusercontent.com/u/25487037?v=4",
+ "profile": "https://www.linkedin.com/in/adrisolid/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "naruthk",
+ "name": "Naruth Kongurai",
+ "avatar_url": "https://avatars.githubusercontent.com/u/9120451?v=4",
+ "profile": "https://naruth.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ErfanMirzapour",
+ "name": "Erfan Mirzapour",
+ "avatar_url": "https://avatars.githubusercontent.com/u/52346515?v=4",
+ "profile": "https://github.com/ErfanMirzapour",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "shemExelate",
+ "name": "Shem Mahluf",
+ "avatar_url": "https://avatars.githubusercontent.com/u/46557021?v=4",
+ "profile": "https://github.com/shemExelate",
+ "contributions": [
+ "code",
+ "infra",
+ "doc"
+ ]
+ },
+ {
+ "login": "scoobster17",
+ "name": "Phil Gibbins",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12157029?v=4",
+ "profile": "https://github.com/scoobster17",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Liadshiran",
+ "name": "Liad Shiran",
+ "avatar_url": "https://avatars.githubusercontent.com/u/18011106?v=4",
+ "profile": "https://github.com/Liadshiran",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "EduardoSimon",
+ "name": "Eduardo Simón Picón",
+ "avatar_url": "https://avatars.githubusercontent.com/u/19764148?v=4",
+ "profile": "https://github.com/EduardoSimon",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "thesanjeevsharma",
+ "name": "Sanjeev Sharma",
+ "avatar_url": "https://avatars.githubusercontent.com/u/29539278?v=4",
+ "profile": "https://thesanjeevsharma.now.sh/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "drorheller",
+ "name": "dror-heller",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12750737?v=4",
+ "profile": "https://github.com/drorheller",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "yialo",
+ "name": "Aleksei Arro",
+ "avatar_url": "https://avatars.githubusercontent.com/u/38593881?v=4",
+ "profile": "https://github.com/yialo",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "carlobeltrame",
+ "name": "Carlo Beltrame",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7566995?v=4",
+ "profile": "https://github.com/carlobeltrame",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ggorlen",
+ "name": "ggorlen",
+ "avatar_url": "https://avatars.githubusercontent.com/u/17895165?v=4",
+ "profile": "https://github.com/ggorlen",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mattstobbs",
+ "name": "mattstobbs",
+ "avatar_url": "https://avatars.githubusercontent.com/u/15341757?v=4",
+ "profile": "https://blog.scottlogic.com/mstobbs/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "vxxce",
+ "name": "Zach",
+ "avatar_url": "https://avatars.githubusercontent.com/u/38638365?v=4",
+ "profile": "https://vxxce.github.io/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "anpaopao",
+ "name": "Angus J. Pope",
+ "avatar_url": "https://avatars.githubusercontent.com/u/44686792?v=4",
+ "profile": "https://github.com/anpaopao",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "DylanPiercey",
+ "name": "Dylan Piercey",
+ "avatar_url": "https://avatars.githubusercontent.com/u/4985201?v=4",
+ "profile": "https://twitter.com/dylan_piercey",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ruhollahh",
+ "name": "Ruhollah",
+ "avatar_url": "https://avatars.githubusercontent.com/u/53814636?v=4",
+ "profile": "https://github.com/ruhollahh",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "votemike",
+ "name": "Michael Gwynne",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3957065?v=4",
+ "profile": "https://votemike.co.uk/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "thetric",
+ "name": "Dominik Broj",
+ "avatar_url": "https://avatars.githubusercontent.com/u/19861998?v=4",
+ "profile": "https://github.com/thetric",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "stephenwade",
+ "name": "Stephen Wade",
+ "avatar_url": "https://avatars.githubusercontent.com/u/4148577?v=4",
+ "profile": "https://github.com/stephenwade",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "luizbaldi",
+ "name": "Luiz Baldi",
+ "avatar_url": "https://avatars.githubusercontent.com/u/17226904?v=4",
+ "profile": "https://luizbaldi.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ravinggenius",
+ "name": "Thomas Ingram",
+ "avatar_url": "https://avatars.githubusercontent.com/u/21517?v=4",
+ "profile": "http://www.ravinggenius.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "dwjohnston",
+ "name": "David Johnston",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2467377?v=4",
+ "profile": "https://blacksheepcode.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "fildon",
+ "name": "Rupert McKay",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10462288?v=4",
+ "profile": "https://fildon.me/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "SebastianMaciel",
+ "name": "Sebastián Maciel",
+ "avatar_url": "https://avatars.githubusercontent.com/u/4841148?v=4",
+ "profile": "http://www.sebastianmaciel.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "sidharthv96",
+ "name": "Sidharth Vinod",
+ "avatar_url": "https://avatars.githubusercontent.com/u/10703445?v=4",
+ "profile": "http://sidharth.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jankalfus",
+ "name": "Honza Kalfus",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12714276?v=4",
+ "profile": "https://github.com/jankalfus",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "claidler",
+ "name": "Christopher Laidler",
+ "avatar_url": "https://avatars.githubusercontent.com/u/43636691?v=4",
+ "profile": "https://github.com/claidler",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "savcni01",
+ "name": "Nik Savchenko",
+ "avatar_url": "https://avatars.githubusercontent.com/u/18025894?v=4",
+ "profile": "https://github.com/savcni01",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "sfrieson",
+ "name": "Steven Frieson",
+ "avatar_url": "https://avatars.githubusercontent.com/u/15187179?v=4",
+ "profile": "http://stevenfrieson.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "andrewarosario",
+ "name": "Andrew Rosário",
+ "avatar_url": "https://avatars.githubusercontent.com/u/21269337?v=4",
+ "profile": "https://andrewrosario.medium.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "brleinad",
+ "name": "Daniel RB",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12233785?v=4",
+ "profile": "http://danielrb.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "HonkingGoose",
+ "name": "HonkingGoose",
+ "avatar_url": "https://avatars.githubusercontent.com/u/34918129?v=4",
+ "profile": "https://github.com/HonkingGoose",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Daniel-Kolev",
+ "name": "Daniel Kolev",
+ "avatar_url": "https://avatars.githubusercontent.com/u/43587261?v=4",
+ "profile": "https://github.com/Daniel-Kolev",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "arryanggaputra",
+ "name": "Arryangga Aliev Pratamaputra",
+ "avatar_url": "https://avatars.githubusercontent.com/u/4743772?v=4",
+ "profile": "http://www.kopi.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "arahansen",
+ "name": "Andrew Hansen",
+ "avatar_url": "https://avatars.githubusercontent.com/u/8746094?v=4",
+ "profile": "http://arahansen.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jgsneves",
+ "name": "JOAO GABRIEL SANTOS NEVES",
+ "avatar_url": "https://avatars.githubusercontent.com/u/48700146?v=4",
+ "profile": "https://www.linkedin.com/in/jgsneves/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "lukeingalls",
+ "name": "Luke Ingalls",
+ "avatar_url": "https://avatars.githubusercontent.com/u/45518011?v=4",
+ "profile": "http://lukeingalls.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "levenleven",
+ "name": "Aleksey Levenstein",
+ "avatar_url": "https://avatars.githubusercontent.com/u/6463364?v=4",
+ "profile": "https://github.com/levenleven",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "zaicevas",
+ "name": "Tomas Zaicevas",
+ "avatar_url": "https://avatars.githubusercontent.com/u/34719980?v=4",
+ "profile": "https://medium.com/@zaicevas",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "tsriram",
+ "name": "Sriram Thiagarajan",
+ "avatar_url": "https://avatars.githubusercontent.com/u/450559?v=4",
+ "profile": "https://github.com/tsriram",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "EstebanBorai",
+ "name": "Esteban Borai",
+ "avatar_url": "https://avatars.githubusercontent.com/u/34756077?v=4",
+ "profile": "http://estebanborai.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "artivilla",
+ "name": "Arti Villa",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2120159?v=4",
+ "profile": "https://twitter.com/artivilla",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jakeboone02",
+ "name": "Jake Boone",
+ "avatar_url": "https://avatars.githubusercontent.com/u/366438?v=4",
+ "profile": "https://github.com/jakeboone02",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Dennis273",
+ "name": "Dennis273",
+ "avatar_url": "https://avatars.githubusercontent.com/u/19815164?v=4",
+ "profile": "https://github.com/Dennis273",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "moshfeu",
+ "name": "Mosh Feu",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3723951?v=4",
+ "profile": "https://www.linkedin.com/in/moshfeu/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "make-github-pseudonymous-again",
+ "name": "Notas Hellout",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5165674?v=4",
+ "profile": "https://fosstodon.org/@sudonymouse",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "msmolens",
+ "name": "Max Smolens",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7122091?v=4",
+ "profile": "https://maxsmolens.org/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "nimaebra",
+ "name": "Nima Ebrazeh",
+ "avatar_url": "https://avatars.githubusercontent.com/u/78249749?v=4",
+ "profile": "https://github.com/nimaebra",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ph-fritsche",
+ "name": "Philipp Fritsche",
+ "avatar_url": "https://avatars.githubusercontent.com/u/39068198?v=4",
+ "profile": "http://ph-fritsche.github.io/",
+ "contributions": [
+ "review"
+ ]
+ },
+ {
+ "login": "clemp6r",
+ "name": "Clément Plantier",
+ "avatar_url": "https://avatars.githubusercontent.com/u/4757677?v=4",
+ "profile": "https://github.com/clemp6r",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "pppp606",
+ "name": "pppp606",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7203958?v=4",
+ "profile": "https://github.com/pppp606",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "bilouw",
+ "name": "Bilou",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7089641?v=4",
+ "profile": "https://github.com/bilouw",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "dten",
+ "name": "David Hewson",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1019038?v=4",
+ "profile": "https://github.com/dten",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "alex-kim-dev",
+ "name": "Alex Kim",
+ "avatar_url": "https://avatars.githubusercontent.com/u/45559664?v=4",
+ "profile": "https://github.com/alex-kim-dev",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "PaquitoSoft",
+ "name": "PaquitoSoft",
+ "avatar_url": "https://avatars.githubusercontent.com/u/166022?v=4",
+ "profile": "http://paquitosoftware.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "marcioggs",
+ "name": "Márcio Gabriel",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2506127?v=4",
+ "profile": "https://br.linkedin.com/in/marcioggs",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "krychaxp",
+ "name": "Krychaxp",
+ "avatar_url": "https://avatars.githubusercontent.com/u/55843050?v=4",
+ "profile": "http://krychaxp.pl/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mohetti",
+ "name": "momokolo",
+ "avatar_url": "https://avatars.githubusercontent.com/u/73931283?v=4",
+ "profile": "https://github.com/mohetti",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "meatnordrink",
+ "name": "AndyG",
+ "avatar_url": "https://avatars.githubusercontent.com/u/35735666?v=4",
+ "profile": "https://github.com/meatnordrink",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jbutz",
+ "name": "Jason Butz",
+ "avatar_url": "https://avatars.githubusercontent.com/u/736696?v=4",
+ "profile": "http://www.jasonbutz.info/",
+ "contributions": [
+ "infra"
+ ]
+ },
+ {
+ "login": "olejech",
+ "name": "Oleg",
+ "avatar_url": "https://avatars.githubusercontent.com/u/64708593?v=4",
+ "profile": "https://github.com/olejech",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "danywalls",
+ "name": "Dany Paredes",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5494535?v=4",
+ "profile": "http://www.danywalls.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "abelhbeyene",
+ "name": "Abel",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12272815?v=4",
+ "profile": "https://github.com/abelhbeyene",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "patrady",
+ "name": "Patrick Brady",
+ "avatar_url": "https://avatars.githubusercontent.com/u/17067460?v=4",
+ "profile": "https://github.com/patrady",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "GrantIsEaton",
+ "name": "Grant Eaton",
+ "avatar_url": "https://avatars.githubusercontent.com/u/101603396?v=4",
+ "profile": "https://github.com/GrantIsEaton",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "aarondunphy",
+ "name": "Aaron Dunphy",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5583119?v=4",
+ "profile": "https://aarondunphy.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mrazauskas",
+ "name": "Tom Mrazauskas",
+ "avatar_url": "https://avatars.githubusercontent.com/u/72159681?v=4",
+ "profile": "https://github.com/mrazauskas",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "LuckyMona",
+ "name": "Supermaryy",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12708942?v=4",
+ "profile": "http://supermaryy.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "bennewton999",
+ "name": "Ben Newton",
+ "avatar_url": "https://avatars.githubusercontent.com/u/458991?v=4",
+ "profile": "http://benenewton.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "silvenon",
+ "name": "Matija Marohnić",
+ "avatar_url": "https://avatars.githubusercontent.com/u/471278?v=4",
+ "profile": "https://silvenon.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ThaddeusJiang",
+ "name": "Thaddeus Jiang",
+ "avatar_url": "https://avatars.githubusercontent.com/u/17308201?v=4",
+ "profile": "https://thaddeusjiang.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "robin-drexler",
+ "name": "Robin Drexler",
+ "avatar_url": "https://avatars.githubusercontent.com/u/474248?v=4",
+ "profile": "https://www.robin-drexler.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "caffeine-storm",
+ "name": "Thomas McKee",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1609128?v=4",
+ "profile": "https://github.com/caffeine-storm",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "satanTime",
+ "name": "satanTime",
+ "avatar_url": "https://avatars.githubusercontent.com/u/966305?v=4",
+ "profile": "https://sudo.eu/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "polinamochan",
+ "name": "polinamochan",
+ "avatar_url": "https://avatars.githubusercontent.com/u/108654322?v=4",
+ "profile": "https://github.com/polinamochan",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "shaiRose",
+ "name": "Shai Rose",
+ "avatar_url": "https://avatars.githubusercontent.com/u/4546940?v=4",
+ "profile": "https://github.com/shaiRose",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "AldinRekic",
+ "name": "AldinRekic",
+ "avatar_url": "https://avatars.githubusercontent.com/u/65592409?v=4",
+ "profile": "https://github.com/AldinRekic",
+ "contributions": [
+ "design"
+ ]
+ },
+ {
+ "login": "thanhsonng",
+ "name": "Son Nguyen",
+ "avatar_url": "https://avatars.githubusercontent.com/u/28614996?v=4",
+ "profile": "https://sonng.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Lirlev48",
+ "name": "Lirlev48",
+ "avatar_url": "https://avatars.githubusercontent.com/u/58209233?v=4",
+ "profile": "https://github.com/Lirlev48",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "tarjei",
+ "name": "Tarjei Huse",
+ "avatar_url": "https://avatars.githubusercontent.com/u/211263?v=4",
+ "profile": "https://github.com/tarjei",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ObieMunoz",
+ "name": "Obie Munoz",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5696449?v=4",
+ "profile": "https://www.linkedin.com/in/obedmunozjr/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "bertybot",
+ "name": "Bert B",
+ "avatar_url": "https://avatars.githubusercontent.com/u/44912991?v=4",
+ "profile": "https://github.com/bertybot",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "alirezahi",
+ "name": "Alireza Heydari",
+ "avatar_url": "https://avatars.githubusercontent.com/u/16666064?v=4",
+ "profile": "https://github.com/alirezahi",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "glebinsky",
+ "name": "Gleb Radutsky",
+ "avatar_url": "https://avatars.githubusercontent.com/u/898643?v=4",
+ "profile": "https://github.com/glebinsky",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "giovanniPepi",
+ "name": "giovanniPepi",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5255535?v=4",
+ "profile": "https://github.com/giovanniPepi",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mdjastrzebski",
+ "name": "Maciej Jastrzebski",
+ "avatar_url": "https://avatars.githubusercontent.com/u/6368606?v=4",
+ "profile": "https://github.com/mdjastrzebski",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "semoal",
+ "name": "Sergio Moreno",
+ "avatar_url": "https://avatars.githubusercontent.com/u/22656541?v=4",
+ "profile": "https://semoal.github.io/sergiomoreno/",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "davidnixon",
+ "name": "David Nixon",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7536103?v=4",
+ "profile": "https://github.com/davidnixon",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "khitrenovich",
+ "name": "Anton Khitrenovich",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3424762?v=4",
+ "profile": "http://technotes.khitrenovich.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "kostasx",
+ "name": "Kostas Minaidis",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1638325?v=4",
+ "profile": "https://plethorathemes.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "MynockSpit",
+ "name": "Than Hutchins",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5713867?v=4",
+ "profile": "https://github.com/MynockSpit",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "edmundsj",
+ "name": "Jordan Edmunds",
+ "avatar_url": "https://avatars.githubusercontent.com/u/11085127?v=4",
+ "profile": "https://github.com/edmundsj",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mouse484",
+ "name": "mouse",
+ "avatar_url": "https://avatars.githubusercontent.com/u/38714187?v=4",
+ "profile": "https://portfolio.mouse484.vercel.app/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "robertoms99",
+ "name": "Roberto Molina",
+ "avatar_url": "https://avatars.githubusercontent.com/u/54558382?v=4",
+ "profile": "https://roberto-molina.netlify.app/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "louis-young",
+ "name": "Louis Young",
+ "avatar_url": "https://avatars.githubusercontent.com/u/35606709?v=4",
+ "profile": "https://www.louisyoung.co.uk/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "lukaselmer",
+ "name": "Lukas Elmer",
+ "avatar_url": "https://avatars.githubusercontent.com/u/326935?v=4",
+ "profile": "https://www.linkedin.com/in/lukaselmer/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "brentguf",
+ "name": "Brent Guffens",
+ "avatar_url": "https://avatars.githubusercontent.com/u/16427929?v=4",
+ "profile": "https://github.com/brentguf",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "estebanfelipep",
+ "name": "Esteban",
+ "avatar_url": "https://avatars.githubusercontent.com/u/15718690?v=4",
+ "profile": "https://github.com/estebanfelipep",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "tiborbarsi",
+ "name": "Tibor Barsi",
+ "avatar_url": "https://avatars.githubusercontent.com/u/21023594?v=4",
+ "profile": "https://github.com/tiborbarsi",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "tnyo43",
+ "name": "Tomoya Kashifuku",
+ "avatar_url": "https://avatars.githubusercontent.com/u/11014018?v=4",
+ "profile": "https://github.com/tnyo43",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "wtlin1228",
+ "name": "Leo",
+ "avatar_url": "https://avatars.githubusercontent.com/u/16910748?v=4",
+ "profile": "https://leonerd.gatsbyjs.io/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "erik-metz",
+ "name": "Erik Metz",
+ "avatar_url": "https://avatars.githubusercontent.com/u/69653055?v=4",
+ "profile": "http://spray-r.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mrtyagi07",
+ "name": "Vaibhav Tyagi",
+ "avatar_url": "https://avatars.githubusercontent.com/u/83115094?v=4",
+ "profile": "https://github.com/mrtyagi07",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "alleksei37",
+ "name": "Aleksei Drokin",
+ "avatar_url": "https://avatars.githubusercontent.com/u/18336748?v=4",
+ "profile": "https://github.com/alleksei37",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "shaman-apprentice",
+ "name": "Torsten Knauf",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3596742?v=4",
+ "profile": "https://github.com/shaman-apprentice",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "jharlowuk",
+ "name": "John Harlow",
+ "avatar_url": "https://avatars.githubusercontent.com/u/41212861?v=4",
+ "profile": "http://jharlow.uk/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "morgan121",
+ "name": "Morgan Hunter",
+ "avatar_url": "https://avatars.githubusercontent.com/u/62575603?v=4",
+ "profile": "https://github.com/morgan121",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Mozl",
+ "name": "Louis Moselhi",
+ "avatar_url": "https://avatars.githubusercontent.com/u/24245755?v=4",
+ "profile": "https://github.com/Mozl",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "rydash",
+ "name": "Ryan McGill",
+ "avatar_url": "https://avatars.githubusercontent.com/u/9492442?v=4",
+ "profile": "https://github.com/rydash",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "renansoares",
+ "name": "Renan Andrade",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1657840?v=4",
+ "profile": "http://rensoares.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "boldurean",
+ "name": "Vasilii Boldurean",
+ "avatar_url": "https://avatars.githubusercontent.com/u/64642760?v=4",
+ "profile": "https://github.com/boldurean",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "josiasds",
+ "name": "Josias Schneider",
+ "avatar_url": "https://avatars.githubusercontent.com/u/882253?v=4",
+ "profile": "http://theoutsider.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ledenis",
+ "name": "Denis LE",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2855723?v=4",
+ "profile": "https://github.com/ledenis",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Nicoss54",
+ "name": "Nicolas Frizzarin",
+ "avatar_url": "https://avatars.githubusercontent.com/u/24563545?v=4",
+ "profile": "https://github.com/Nicoss54",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "santoshyadavdev",
+ "name": "Santosh Yadav",
+ "avatar_url": "https://avatars.githubusercontent.com/u/11923975?v=4",
+ "profile": "https://github.com/santoshyadavdev",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "caiangums",
+ "name": "Ilê Caian",
+ "avatar_url": "https://avatars.githubusercontent.com/u/7551787?v=4",
+ "profile": "http://caian.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Clarity-89",
+ "name": "Alex Khomenko",
+ "avatar_url": "https://avatars.githubusercontent.com/u/8878045?v=4",
+ "profile": "https://claritydev.net/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "LLCampos",
+ "name": "Luís Campos",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12008784?v=4",
+ "profile": "https://llcampos.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "YozhEzhi",
+ "name": "Alexandr Zhidovlenko",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2706062?v=4",
+ "profile": "https://github.com/YozhEzhi",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "lyannel",
+ "name": "lyannel",
+ "avatar_url": "https://avatars.githubusercontent.com/u/34246811?v=4",
+ "profile": "https://github.com/lyannel",
+ "contributions": [
+ "a11y"
+ ]
+ },
+ {
+ "login": "yanick",
+ "name": "Yanick Champoux",
+ "avatar_url": "https://avatars.githubusercontent.com/u/19954?v=4",
+ "profile": "https://techblog.babyl.ca/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ali-kamalizade",
+ "name": "Ali",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1886605?v=4",
+ "profile": "https://ali-dev.medium.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "macmillen",
+ "name": "Milan Jaritz",
+ "avatar_url": "https://avatars.githubusercontent.com/u/33156526?v=4",
+ "profile": "https://github.com/macmillen",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "dzonatan",
+ "name": "Rokas Brazdžionis",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5166666?v=4",
+ "profile": "https://github.com/dzonatan",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "tomalaforge",
+ "name": "Laforge Thomas",
+ "avatar_url": "https://avatars.githubusercontent.com/u/30832608?v=4",
+ "profile": "https://thomaslaforge.dev/home",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "StephNathai",
+ "name": "Steph Nathai",
+ "avatar_url": "https://avatars.githubusercontent.com/u/11414958?v=4",
+ "profile": "https://github.com/StephNathai",
+ "contributions": [
+ "code",
+ "a11y"
+ ]
+ },
+ {
+ "login": "friederbluemle",
+ "name": "Frieder Bluemle",
+ "avatar_url": "https://avatars.githubusercontent.com/u/743291?v=4",
+ "profile": "https://github.com/friederbluemle",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ssi02014",
+ "name": "Gromit (전민재)",
+ "avatar_url": "https://avatars.githubusercontent.com/u/64779472?v=4",
+ "profile": "https://blog.naver.com/ssi02014",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "csantos1113",
+ "name": "Cesar S",
+ "avatar_url": "https://avatars.githubusercontent.com/u/9648559?v=4",
+ "profile": "https://github.com/csantos1113",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "crutchcorn",
+ "name": "Corbin Crutchley",
+ "avatar_url": "https://avatars.githubusercontent.com/u/9100169?v=4",
+ "profile": "https://crutchcorn.dev/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "rbatsenko",
+ "name": "Roman Batsenko",
+ "avatar_url": "https://avatars.githubusercontent.com/u/6410057?v=4",
+ "profile": "https://github.com/rbatsenko",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "snjro",
+ "name": "snjro",
+ "avatar_url": "https://avatars.githubusercontent.com/u/34432943?v=4",
+ "profile": "https://github.com/snjro",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "taro-28",
+ "name": "taro",
+ "avatar_url": "https://avatars.githubusercontent.com/u/66539019?v=4",
+ "profile": "https://bento.me/taro",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "andnorda",
+ "name": "Andreas Nordahl",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1894119?v=4",
+ "profile": "https://github.com/andnorda",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "neaumusic",
+ "name": "neaumusic",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3423750?v=4",
+ "profile": "https://neaumusic.github.io/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "JoyelJohny",
+ "name": "Joyel Johny",
+ "avatar_url": "https://avatars.githubusercontent.com/u/81413791?v=4",
+ "profile": "https://github.com/JoyelJohny",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "bsheps",
+ "name": "bsheps",
+ "avatar_url": "https://avatars.githubusercontent.com/u/35780702?v=4",
+ "profile": "https://github.com/bsheps",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "kyle-n",
+ "name": "Kyle Nazario",
+ "avatar_url": "https://avatars.githubusercontent.com/u/13384477?v=4",
+ "profile": "http://www.kylenazario.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "thefalked",
+ "name": "Giuliano Crivelli",
+ "avatar_url": "https://avatars.githubusercontent.com/u/28029756?v=4",
+ "profile": "https://github.com/thefalked",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mroforolhc",
+ "name": "mrkv",
+ "avatar_url": "https://avatars.githubusercontent.com/u/26799398?v=4",
+ "profile": "https://t.me/markov_ka",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "smk267",
+ "name": "smk267",
+ "avatar_url": "https://avatars.githubusercontent.com/u/88115182?v=4",
+ "profile": "https://github.com/smk267",
+ "contributions": [
+ "infra"
+ ]
+ },
+ {
+ "login": "agentdylan",
+ "name": "Dylan Gordon",
+ "avatar_url": "https://avatars.githubusercontent.com/u/3656794?v=4",
+ "profile": "https://www.dylangordon.co.nz/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "mcous",
+ "name": "Michael Cousins",
+ "avatar_url": "https://avatars.githubusercontent.com/u/2963448?v=4",
+ "profile": "https://michael.cousins.io/",
+ "contributions": [
+ "doc",
+ "review"
+ ]
+ },
+ {
+ "login": "kettanaito",
+ "name": "Artem Zakharchenko",
+ "avatar_url": "https://avatars.githubusercontent.com/u/14984911?v=4",
+ "profile": "https://kettanaito.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "vadimshvetsov",
+ "name": "Vadim Shvetsov",
+ "avatar_url": "https://avatars.githubusercontent.com/u/16336572?v=4",
+ "profile": "https://github.com/vadimshvetsov",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "domnantas",
+ "name": "Domantas Petrauskas",
+ "avatar_url": "https://avatars.githubusercontent.com/u/51953549?v=4",
+ "profile": "https://domnantas.lt/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "Efim-Kapliy",
+ "name": "Efim",
+ "avatar_url": "https://avatars.githubusercontent.com/u/126895483?v=4",
+ "profile": "https://portfolio.edkt.ru/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "denisx",
+ "name": "denisx",
+ "avatar_url": "https://avatars.githubusercontent.com/u/427776?v=4",
+ "profile": "https://github.com/denisx",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "ggualiato",
+ "name": "Giovanni Gualiato",
+ "avatar_url": "https://avatars.githubusercontent.com/u/26698704?v=4",
+ "profile": "https://github.com/ggualiato",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "saubaig456",
+ "name": "Saud Baig",
+ "avatar_url": "https://avatars.githubusercontent.com/u/143418574?v=4",
+ "profile": "https://github.com/saubaig456",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "moeyashi",
+ "name": "Ren Adachi",
+ "avatar_url": "https://avatars.githubusercontent.com/u/49895682?v=4",
+ "profile": "https://github.com/moeyashi",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "TyMick",
+ "name": "Ty Mick",
+ "avatar_url": "https://avatars.githubusercontent.com/u/5317080?v=4",
+ "profile": "https://tymick.me/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "enmanuelduran",
+ "name": "Enmanuel Durán",
+ "avatar_url": "https://avatars.githubusercontent.com/u/8060530?v=4",
+ "profile": "https://enmascript.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "gbhasha",
+ "name": "Galeel Bhasha Satthar",
+ "avatar_url": "https://avatars.githubusercontent.com/u/501794?v=4",
+ "profile": "https://github.com/gbhasha",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "ianlet",
+ "name": "Ian Létourneau",
+ "avatar_url": "https://avatars.githubusercontent.com/u/6018732?v=4",
+ "profile": "https://noma.to/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "brianlu2610",
+ "name": "brianlu2610",
+ "avatar_url": "https://avatars.githubusercontent.com/u/68666483?v=4",
+ "profile": "https://github.com/brianlu2610",
+ "contributions": [
+ "code"
+ ]
+ },
+ {
+ "login": "ezzatron",
+ "name": "Erin",
+ "avatar_url": "https://avatars.githubusercontent.com/u/100152?v=4",
+ "profile": "https://github.com/ezzatron",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "nchen000",
+ "name": "Nan Chen",
+ "avatar_url": "https://avatars.githubusercontent.com/u/23487984?v=4",
+ "profile": "https://www.linkedin.com/in/nan-chen-000/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "aburdiss",
+ "name": "Alexander Burdiss",
+ "avatar_url": "https://avatars.githubusercontent.com/u/31200600?v=4",
+ "profile": "http://alexanderburdiss.com/",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "peterh-capella",
+ "name": "Peter Hentges",
+ "avatar_url": "https://avatars.githubusercontent.com/u/1122723?v=4",
+ "profile": "https://github.com/peterh-capella",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "neknalb",
+ "name": "neknalb",
+ "avatar_url": "https://avatars.githubusercontent.com/u/23031302?v=4",
+ "profile": "https://github.com/neknalb",
+ "contributions": [
+ "doc"
+ ]
+ },
+ {
+ "login": "fetsorn",
+ "name": "fetsorn",
+ "avatar_url": "https://avatars.githubusercontent.com/u/12858105?v=4",
+ "profile": "https://github.com/fetsorn",
+ "contributions": [
+ "doc"
+ ]
}
],
"contributorsPerLine": 7,
"projectName": "testing-library-docs",
"projectOwner": "testing-library",
"repoType": "github",
- "repoHost": "https://github.com/"
+ "repoHost": "https://github.com/",
+ "skipCi": true,
+ "commitType": "docs"
}
diff --git a/.dockerignore b/.dockerignore
deleted file mode 100755
index 27d2dae2b..000000000
--- a/.dockerignore
+++ /dev/null
@@ -1,2 +0,0 @@
-*/node_modules
-*.log
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
deleted file mode 100644
index 9bf526656..000000000
--- a/.github/FUNDING.yml
+++ /dev/null
@@ -1 +0,0 @@
-open_collective: testing-library
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index dd84ea782..8bc4a7ed8 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -4,35 +4,33 @@ about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
-
---
-**Describe the bug**
-A clear and concise description of what the bug is.
+**Describe the bug** A clear and concise description of what the bug is.
+
+**To Reproduce** Steps to reproduce the behavior:
-**To Reproduce**
-Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
-**Expected behavior**
-A clear and concise description of what you expected to happen.
+**Expected behavior** A clear and concise description of what you expected to
+happen.
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
+**Screenshots** If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser [e.g. chrome, safari]
- - Version [e.g. 22]
+
+- OS: [e.g. iOS]
+- Browser [e.g. chrome, safari]
+- Version [e.g. 22]
**Smartphone (please complete the following information):**
- - Device: [e.g. iPhone6]
- - OS: [e.g. iOS8.1]
- - Browser [e.g. stock browser, safari]
- - Version [e.g. 22]
-**Additional context**
-Add any other context about the problem here.
+- Device: [e.g. iPhone6]
+- OS: [e.g. iOS8.1]
+- Browser [e.g. stock browser, safari]
+- Version [e.g. 22]
+
+**Additional context** Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
index bbcbbe7d6..2866f790f 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -4,17 +4,16 @@ about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
-
---
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+**Is your feature request related to a problem? Please describe.** A clear and
+concise description of what the problem is. Ex. I'm always frustrated when [...]
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
+**Describe the solution you'd like** A clear and concise description of what you
+want to happen.
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
+**Describe alternatives you've considered** A clear and concise description of
+any alternative solutions or features you've considered.
-**Additional context**
-Add any other context or screenshots about the feature request here.
+**Additional context** Add any other context or screenshots about the feature
+request here.
diff --git a/.gitignore b/.gitignore
index ce04b46a7..5c447595b 100755
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.idea
.DS_Store
node_modules
@@ -5,12 +6,12 @@ node_modules
lib/core/metadata.js
lib/core/MetadataBlog.js
-website/translated_docs
-website/build/
-website/yarn.lock
-website/node_modules
-website/i18n/*
+translated_docs
+build/
+i18n/*
+.docusaurus
+.cache-loader
*.log
-.idea
+yarn.lock
diff --git a/.node-version b/.node-version
new file mode 100644
index 000000000..dcf74e268
--- /dev/null
+++ b/.node-version
@@ -0,0 +1 @@
+16.14
\ No newline at end of file
diff --git a/.prettierrc.js b/.prettierrc.js
index 30f575a92..4679d9bf6 100644
--- a/.prettierrc.js
+++ b/.prettierrc.js
@@ -1,6 +1 @@
-module.exports = {
- proseWrap: 'always',
- singleQuote: true,
- semi: false,
- trailingComma: 'es5',
-}
\ No newline at end of file
+module.exports = require('kcd-scripts/prettier')
diff --git a/.vscode/markdown.code-snippets b/.vscode/markdown.code-snippets
index 8df3257d7..bed57a2f8 100644
--- a/.vscode/markdown.code-snippets
+++ b/.vscode/markdown.code-snippets
@@ -1,53 +1,47 @@
{
- // Place your testing-library-docs workspace snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
- // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
- // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
- // used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
- // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
- // Placeholders with the same ids are connected.
- // Example:
- // "Print to console": {
- // "scope": "javascript,typescript",
- // "prefix": "log",
- // "body": [
- // "console.log('$1');",
- // "$2"
- // ],
- // "description": "Log output to console"
- // },
- "Multilanguage Code Block": {
- "scope": "markdown",
- "prefix": "docblock",
- "body": [
- "```html",
- "$4",
- "```",
- "",
- "",
- "",
- "",
- "```js",
- "import { $1 } from '@testing-library/dom'",
- "",
- "const container = document.body",
- "const $3 = $1(container, '$2')",
- "```",
- "",
- "",
- "```js",
- "import { render } from '@testing-library/react'",
- "",
- "const { $1 } = render( )",
- "const $3 = $1('$2')",
- "```",
- "",
- "",
- "```js",
- "cy.$1('$2').should('exist')",
- "```",
- "",
- ""
- ],
- "description": "Create a Docusaurus multi-language code block"
- }
-}
\ No newline at end of file
+ "MDX title": {
+ "prefix": "mdxtitle",
+ "body": ["---", "id: $1", "title: $2", "sidebar_label: $3", "---"],
+ "description": "MDX file titles"
+ },
+ "MDX Multilingual Tabs": {
+ "prefix": "mdxtabs",
+ "body": [
+ "",
+ "import Tabs from '@theme/Tabs';",
+ "import TabItem from '@theme/TabItem';",
+ "",
+ " ",
+ " ",
+ "",
+ " ```js",
+ " $1",
+ " ```",
+ "",
+ " ",
+ " ",
+ "",
+ " ```jsx",
+ " $2",
+ " ```",
+ "",
+ " ",
+ " ",
+ "",
+ " ```js",
+ " $3",
+ " ```",
+ "",
+ " ",
+ " ",
+ ""
+ ],
+ "description": "MDX multilingual tabs"
+ }
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 7a17ef2a4..176df8375 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -1,6 +1,9 @@
{
"editor.rulers": [
60, // try to keep examples this long
- 80, // hard wrap
- ]
+ 80 // hard wrap
+ ],
+ "files.associations": {
+ "*.md": "mdx"
+ }
}
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 26cd10755..6a568d709 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -5,31 +5,31 @@
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
-size, disability, ethnicity, sex characteristics, gender identity and expression,
-level of experience, education, socio-economic status, nationality, personal
-appearance, race, religion, or sexual identity and orientation.
+size, disability, ethnicity, sex characteristics, gender identity and
+expression, level of experience, education, socio-economic status, nationality,
+personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
-* The use of sexualized language or imagery and unwelcome sexual attention or
- advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic
- address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
- professional setting
+- The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
## Our Responsibilities
@@ -37,11 +37,11 @@ Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
+Project maintainers have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, or to ban temporarily or permanently any
+contributor for other behaviors that they deem inappropriate, threatening,
+offensive, or harmful.
## Scope
@@ -55,11 +55,12 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at alexander.krolick+testing@gmail.com. All
-complaints will be reviewed and investigated and will result in a response that
-is deemed necessary and appropriate to the circumstances. The project team is
-obligated to maintain confidentiality with regard to the reporter of an incident.
-Further details of specific enforcement policies may be posted separately.
+reported by contacting the project team at alexander.krolick+testing@gmail.com.
+All complaints will be reviewed and investigated and will result in a response
+that is deemed necessary and appropriate to the circumstances. The project team
+is obligated to maintain confidentiality with regard to the reporter of an
+incident. Further details of specific enforcement policies may be posted
+separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
@@ -67,8 +68,9 @@ members of the project's leadership.
## Attribution
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
-available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 1.4, available at
+https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100755
index d369844d5..000000000
--- a/Dockerfile
+++ /dev/null
@@ -1,10 +0,0 @@
-FROM node:8.11.4
-
-WORKDIR /app/website
-
-EXPOSE 3000 35729
-COPY ./docs /app/docs
-COPY ./website /app/website
-RUN yarn install
-
-CMD ["yarn", "start"]
diff --git a/README.md b/README.md
index 64c8b70d4..4ffd940f5 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,15 @@
-testing-library-docs
---------------------
-
+## testing-library-docs
-[![Open Collective Sponsors and Backers][opencollective-badge]][opencollective]
[![All Contributors][allcontributors-badge]](#contributors)
[![Code of Conduct][coc-badge]][coc]
-Documentation site for [React Testing Library](https://github.com/testing-library/react-testing-library), [DOM Testing Library](https://github.com/testing-library/dom-testing-library), and [related projects](https://github.com/testing-library)
-
-See [./website/README.md](./website/README.md) for instructions on building the site
+Documentation site for
+[React Testing Library](https://github.com/testing-library/react-testing-library),
+[DOM Testing Library](https://github.com/testing-library/dom-testing-library),
+[Angular Testing Library](https://github.com/testing-library/angular-testing-library),
+and [related projects](https://github.com/testing-library)
**https://testing-library.com**
@@ -18,22 +17,532 @@ See [./website/README.md](./website/README.md) for instructions on building the
[![Netlify Status][netlify-badge]][build]
-[netlify-badge]: https://api.netlify.com/api/v1/badges/24366204-84ca-41e9-b573-2a64f0845e46/deploy-status
+[netlify-badge]:
+ https://api.netlify.com/api/v1/badges/24366204-84ca-41e9-b573-2a64f0845e46/deploy-status
[build]: https://app.netlify.com/sites/testing-library/deploys
-[opencollective]: https://opencollective.com/testing-library/
-[opencollective-badge]: https://img.shields.io/opencollective/all/testing-library.svg?label=opencollective%20backers&style=flat-square
-[allcontributors-badge]: https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square
-[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/master/CODE_OF_CONDUCT.md
+[allcontributors-badge]:
+ https://img.shields.io/github/all-contributors/testing-library/testing-library-docs?color=ee8449&style=flat-square
+[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
+
+This website was created with [Docusaurus](https://v2.docusaurus.io).
+
+# What's In This Document
+
+- [Get Started in 5 Minutes](#get-started-in-5-minutes)
+- [Editing Content](#editing-content)
+- [Adding Content](#adding-content)
+
+# Get Started in 5 Minutes
+
+1. Make sure all the dependencies for the website are installed:
+
+```sh
+# Install dependencies
+$ npm install
+```
+
+2. Run your dev server:
+
+```sh
+# Start the site
+$ npm start
+```
+
+# Editing Content
+
+## Editing an existing docs page
+
+Edit docs by navigating to `docs/` and editing the corresponding document:
+
+`docs/doc-to-be-edited.mdx`
+
+```markdown
+---
+id: page-needs-edit
+title: This Doc Needs To Be Edited
+---
+
+Edit me...
+```
+
+For more information about docs, click [here](https://v2.docusaurus.io/docs)
+
+## Editing an existing blog post
+
+Edit blog posts by navigating to `blog` and editing the corresponding post:
+
+`blog/post-to-be-edited.mdx`
+
+```markdown
+---
+id: post-needs-edit
+title: This Blog Post Needs To Be Edited
+---
+
+Edit me...
+```
+
+For more information about blog posts, click
+[here](https://v2.docusaurus.io/docs/blog)
+
+# Adding Content
+
+## Adding a new docs page to an existing sidebar
+
+1. Create the doc as a new markdown file in `/docs`, example
+ `docs/newly-created-doc.mdx`:
+
+```md
+---
+id: newly-created-doc
+title: This Doc Needs To Be Edited
+---
+
+My new content here..
+```
+
+Note: Ensure the file name and the id value do not include non-url safe
+characters i.e. '\*'.
+
+2. Refer to that doc's ID in an existing sidebar in `sidebar.json`:
+
+```javascript
+// Add newly-created-doc to the Getting Started category of docs
+{
+ "docs": {
+ "Getting Started": [
+ "quick-start",
+ "newly-created-doc" // new doc here
+ ],
+ ...
+ },
+ ...
+}
+```
+
+For more information about adding new docs, click
+[here](https://v2.docusaurus.io/docs/)
## Contributors
-Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
+Thanks goes to these wonderful people
+([emoji key](https://allcontributors.org/docs/en/emoji-key)):
-
-
+
+
+
+
+
+
-This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
+This project follows the
+[all-contributors](https://github.com/all-contributors/all-contributors)
+specification. Contributions of any kind welcome!
diff --git a/website/blog/2018-12-29-new-site.md b/blog/2018-12-29-new-site.mdx
similarity index 79%
rename from website/blog/2018-12-29-new-site.md
rename to blog/2018-12-29-new-site.mdx
index cd3652a16..592607164 100755
--- a/website/blog/2018-12-29-new-site.md
+++ b/blog/2018-12-29-new-site.mdx
@@ -1,7 +1,7 @@
---
title: New Site
author: Alex Krolick
-authorURL: http://github.com/alexkrolick
+authorURL: 'http://github.com/alexkrolick'
---
We have a docs site now! It's built with [Docusaurus](https://docusaurus.io).
@@ -20,6 +20,6 @@ beyond!
🎉 Happy new year!
-[dtl]: /
-[rtl]: /react
-[ctl]: /cypress
+[dtl]: /docs/dom-testing-library/intro
+[rtl]: /docs/react-testing-library/intro
+[ctl]: /docs/cypress-testing-library/intro
diff --git a/website/blog/2019-03-17-code-blocks.md b/blog/2019-03-17-code-blocks.mdx
similarity index 92%
rename from website/blog/2019-03-17-code-blocks.md
rename to blog/2019-03-17-code-blocks.mdx
index 49b0c45b1..b17d70ea2 100755
--- a/website/blog/2019-03-17-code-blocks.md
+++ b/blog/2019-03-17-code-blocks.mdx
@@ -1,7 +1,7 @@
---
title: Multi-Framework Code Blocks
author: Alex Krolick
-authorURL: http://github.com/alexkrolick
+authorURL: 'http://github.com/alexkrolick'
---
Many of the code samples have been updated to include tabs to switch between
diff --git a/website/blog/2019-04-25-new-org.md b/blog/2019-04-25-new-org.mdx
similarity index 83%
rename from website/blog/2019-04-25-new-org.md
rename to blog/2019-04-25-new-org.mdx
index c835f1a8b..b0e09c200 100644
--- a/website/blog/2019-04-25-new-org.md
+++ b/blog/2019-04-25-new-org.mdx
@@ -1,10 +1,12 @@
---
-# prettier doesn't like how long this line is.
-# prettier-ignore
-title: "Testing Library Updates: new release, github org, open collective, and twitter account"
+# prettier doesn't like how long this line is.: ""
+# prettier-ignore: ""
+title:
+ 'Testing Library Updates: new release, github org, open collective, and
+ twitter account'
author: Kent C. Dodds
-authorURL: https://kentcdodds.com
-authorImageURL: https://avatars0.githubusercontent.com/u/1500684?s=120&v=4
+authorURL: 'https://kentcdodds.com/'
+authorImageURL: 'https://avatars0.githubusercontent.com/u/1500684?s=120&v=4'
---
Hello friends! I'm pleased to announce the recent updates to the testing-library
@@ -35,8 +37,7 @@ just disable it here -->
We hope that this helps you catch bugs better!
@@ -71,7 +72,7 @@ Here are the current (or soon to be) members of the org:
(Puppeteer)
- [bs-react-testing-library](https://github.com/wyze/bs-react-testing-library)
(ReasonReact)
-- [testcafe-testing-library](https://github.com/benmonro/testcafe-testing-library)
+- [testcafe-testing-library](https://github.com/testing-library/testcafe-testing-library)
- [user-event](https://github.com/Gpx/user-event)
- [jest-dom](https://github.com/testing-library/jest-dom)
- [jest-native](https://github.com/testing-library/jest-native)
@@ -122,33 +123,27 @@ there.) Please join us!
> [Join us on Spectrum](https://spectrum.chat/testing-library)
-## [Testing Library on React Native](https://www.native-testing-library.com/)
+## [Testing Library on React Native](https://callstack.github.io/react-native-testing-library/)
I'm really happy to announce a super solution to the React Native testing space.
The DOM is quite different from native, as I mentioned before, it's not the code
but the concepts that really make the Testing Library great. I'm happy to say
-that [Brandon Carroll](https://twitter.com/bcarroll22) has successfully ported
+that [Brandon Carroll](https://github.com/bcarroll22) has successfully ported
those concepts to a solution for React Native and I'm really happy with it. Give
it a look if you're building React Native applications and want confidence that
they continue to work as you make changes!
-(Note, there is already `react-native-testing-library`, but it doesn't quite
-follow the guiding principles of Testing Library, so we recommend you use
-`native-testing-library`.
-[Learn More](https://medium.com/@brandoncarroll/why-native-testing-library-exists-629ffb85cae2)).
+> [Checkout native-testing-library](https://callstack.github.io/react-native-testing-library/)
-> [Checkout native-testing-library](https://www.native-testing-library.com/)
-
-## [Learn Testing Library](https://testing-library.com/docs/learning)
+## [Learn Testing Library](/docs/learning)
There's been a LOT of activity around the Testing Library principles and tools
in the content space. We do have
-[a page that lists learning materials](https://testing-library.com/docs/learning),
-and more gets added to it daily. If you know of blog posts, YouTube videos,
-courses, or anything else about the Testing Library family of tools, please
-contribute to the list!
+[a page that lists learning materials](/docs/learning), and more gets added to
+it daily. If you know of blog posts, YouTube videos, courses, or anything else
+about the Testing Library family of tools, please contribute to the list!
-> [Contribute to the learning materials page](https://github.com/testing-library/testing-library-docs/edit/master/docs/learning.md)
+> [Contribute to the learning materials page](https://github.com/testing-library/testing-library-docs/edit/main/docs/learning.mdx)
## Other Exciting news
@@ -157,9 +152,9 @@ As of a few months ago,
you use react-testing-library to test your react applications. That's kinda neat
:)
-At the React Amsterdam [Open Source awards](https://osawards.com/react/) ceremony,
-react-testing-library won the award for the Most impactful contribution to the
-community!
+At the React Amsterdam [Open Source awards](https://osawards.com/react/)
+ceremony, react-testing-library won the award for the Most impactful
+contribution to the community!

@@ -178,7 +173,7 @@ an especially significant impact on the Testing Library family of tools and
community.
[Myself 👋](https://kentcdodds.com), [Alex Krolick](https://alexkrolick.com/),
-[Brandon Carroll](https://twitter.com/bcarroll22),
+[Brandon Carroll](https://github.com/bcarroll22),
[Giorgio](https://twitter.com/Gpx),
[Ernesto García](https://twitter.com/gnapse), and
[Daniel Cook](https://github.com/dfcook).
diff --git a/docker-compose.yml b/docker-compose.yml
deleted file mode 100755
index 6711192ae..000000000
--- a/docker-compose.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-version: "3"
-
-services:
- docusaurus:
- build: .
- ports:
- - 3000:3000
- - 35729:35729
- volumes:
- - ./docs:/app/docs
- - ./website/blog:/app/website/blog
- - ./website/core:/app/website/core
- - ./website/i18n:/app/website/i18n
- - ./website/pages:/app/website/pages
- - ./website/static:/app/website/static
- - ./website/sidebars.json:/app/website/sidebars.json
- - ./website/siteConfig.js:/app/website/siteConfig.js
- working_dir: /app/website
diff --git a/docs/angular-testing-library/api.mdx b/docs/angular-testing-library/api.mdx
new file mode 100644
index 000000000..a5ba65c4b
--- /dev/null
+++ b/docs/angular-testing-library/api.mdx
@@ -0,0 +1,654 @@
+---
+id: api
+title: API
+---
+
+`Angular Testing Library` re-exports everything from `DOM Testing Library` as
+well as the `render` method.
+
+The following re-exports are patched to make them easier to use with Angular:
+
+- The events on `fireEvent` automatically invoke a change detection cycle after
+ the event has been fired
+- The `findBy` queries automatically invoke a change detection cycle before the
+ query is invoked function
+- The `waitFor` functions automatically invoke a change detection cycle before
+ invoking the callback function
+
+## `render`
+
+With Angular Testing Library, the component can be rendered in two ways, via the
+component's type or with a template.
+
+> By default, `render` also imports the `NoopAnimationsModule`.
+
+## `Type`
+
+To render a component, you need to pass component's type to the `render` method.
+For components that don't use other parts of your application (for example
+design modules or services), rendering a component can be as simple as the
+following example.
+
+```typescript
+await render(AppComponent)
+```
+
+## `template`
+
+Instead of passing the component's type as first argument, you can also provide
+a template. This practice is required to render directives but can also be
+applied to components, it might even be more useful. The directive's (or
+component's) type must then be added to the `imports` (or `declarations` in case
+of non-standalone components).
+
+**example with directive**:
+
+```typescript
+await render('
', {
+ declarations: [SpoilerDirective],
+})
+```
+
+**example with component**:
+
+```typescript
+await render(
+ ' ',
+ {
+ declarations: [AppComponent],
+ componentProperties: {
+ anotherValue: 'valueOfAnotherProperty',
+ sendValue: jest.fn(),
+ },
+ },
+)
+```
+
+```typescript
+export async function render(
+ component: Type,
+ renderOptions?: RenderComponentOptions,
+): Promise>
+export async function render(
+ template: string,
+ renderOptions?: RenderTemplateOptions,
+): Promise>
+```
+
+## Component RenderOptions
+
+
+### `inputs`
+
+An object to set `@Input` or `input()` properties of the component.
+
+**default** : `{}`
+
+```typescript
+await render(AppComponent, {
+ inputs: {
+ counterValue: 10,
+ // explicitly define aliases using `aliasedInput`
+ ...aliasedInput('someAlias', 'someValue'),
+ },
+})
+```
+
+### `on`
+
+An object with callbacks to subscribe to `EventEmitters` and `Observables` of
+the component.
+
+**default** : `{}`
+
+```ts
+// using a manual callback
+const sendValue = (value) => { ... }
+await render(AppComponent, {
+ on: {
+ send: (value) => sendValue(value),
+ }
+})
+
+// using a (jest) spy
+const sendValueSpy = jest.fn()
+
+await render(AppComponent, {
+ on: {
+ send: sendValueSpy
+ }
+})
+```
+
+### `bindings`
+
+An array of bindings to apply to the component using Angular's native bindings API. This provides a more direct way to bind inputs and outputs compared to the `inputs` and `on` options. The bindings API uses Angular's `inputBinding`, `outputBinding`, and `twoWayBinding` functions from `@angular/core`.
+
+**default** : `[]`
+
+```typescript
+import { inputBinding, outputBinding, twoWayBinding, signal } from '@angular/core'
+
+await render(AppComponent, {
+ bindings: [
+ // Bind a static input value
+ inputBinding('greeting', () => 'Hello'),
+
+ // Bind a signal as an input
+ inputBinding('age', () => 25),
+
+ // Two-way binding with a signal
+ twoWayBinding('name', signal('John')),
+
+ // Output binding with a callback
+ outputBinding('submitValue', (event) => console.log(event)),
+ ],
+})
+```
+
+**Using signals for reactive updates**:
+
+```typescript
+const greetingSignal = signal('Good day')
+const nameSignal = signal('David')
+const ageSignal = signal(35)
+
+const { fixture } = await render(AppComponent, {
+ bindings: [
+ inputBinding('greeting', greetingSignal),
+ inputBinding('age', ageSignal),
+ twoWayBinding('name', nameSignal),
+ ],
+})
+
+// Update signals externally
+greetingSignal.set('Good evening')
+```
+
+**Handling outputs**:
+
+```typescript
+const submitHandler = jest.fn()
+
+await render(AppComponent, {
+ bindings: [
+ outputBinding('submit', submitHandler),
+ ],
+})
+```
+
+### `declarations`
+
+A collection of components, directives and pipes needed to render the component.
+For example, nested components of the component.
+
+For more info see the
+[Angular docs](https://angular.dev/guide/ngmodules/overview#declarations).
+
+**default** : `[]`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ declarations: [CustomerDetailComponent, ButtonComponent],
+})
+```
+
+### `deferBlockBehavior`
+
+Set the defer blocks behavior.
+
+For more info see the
+[Angular docs](https://angular.dev/api/core/testing/DeferBlockBehavior)
+
+**default** : `undefined` (uses `DeferBlockBehavior.Manual`, which is different
+from the Angular default of `DeferBlockBehavior.Playthrough`)
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ deferBlockBehavior: DeferBlockBehavior.Playthrough,
+})
+```
+
+### `deferBlockStates`
+
+Set the initial state of a deferrable blocks in a component.
+
+For more info see the
+[Angular docs](https://angular.dev/api/core/testing/DeferBlockState)
+
+**default** : `undefined` (uses the Angular default, which is
+`DeferBlockState.Placeholder`)
+
+**example**:
+
+```typescript
+await render(FixtureComponent, {
+ deferBlockStates: DeferBlockState.Loading,
+})
+```
+
+### `componentProviders`
+
+A collection of providers needed to render the component via Dependency
+Injection.
+
+These will be provided at the component level. To inject dependencies at the
+module level, use [`providers`](#providers).
+
+For more info see the
+[Angular docs](https://angular.dev/guide/di/hierarchical-dependency-injection#example-providing-services-in-component).
+
+**default** : `[]`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ componentProviders: [AppComponentService],
+})
+```
+
+### `componentImports`
+
+A collection of imports to override a standalone component's imports with.
+
+**default** : `undefined`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ componentImports: [MockChildComponent],
+})
+```
+
+### `childComponentOverrides`
+
+Collection of child component specified providers to override with.
+
+**default** : `undefined`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ childComponentOverrides: [
+ {
+ component: ChildOfAppComponent,
+ providers: [{provide: ChildService, useValue: {hello: 'world'}}],
+ },
+ ],
+})
+```
+
+### `detectChangesOnRender`
+
+Invokes `detectChanges` after the component is rendered.
+
+**default** : `true`
+
+**example**:
+
+```typescript
+await render(AppComponent, {detectChangesOnRender: false})
+```
+
+### `autoDetectChanges`
+
+Automatically detect changes as a "real" running component would do. For
+example, runs a change detection cycle when an event has occured.
+
+**default** : `true`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ autoDetectChanges: false,
+})
+```
+
+### `excludeComponentDeclaration`
+
+Exclude the component to be automatically be added as a declaration. This is
+needed when the component is declared in an imported module, for example with
+SCAMs.
+
+**default** : `false`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ imports: [AppModule], // a module that includes AppComponent
+ excludeComponentDeclaration: true,
+})
+```
+
+### `imports`
+
+A collection of imports needed to render the component, for example, shared
+modules. Adds `NoopAnimationsModule` by default if `BrowserAnimationsModule`
+isn't added to the collection
+
+For more info see the
+[Angular docs](https://angular.dev/guide/components#imports-in-the-component-decorator).
+
+**default** : `[NoopAnimationsModule]`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ imports: [AppSharedModule, MaterialModule],
+})
+```
+
+### `providers`
+
+A collection of providers needed to render the component via Dependency
+Injection.
+
+These will be provided at the module level. To inject dependencies at the
+component level, use [`componentProviders`](#componentProviders).
+
+For more info see the
+[Angular docs](https://angular.dev/guide/di/dependency-injection-providers#).
+
+**default** : `[]`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ providers: [
+ CustomersService,
+ {
+ provide: MAX_CUSTOMERS_TOKEN,
+ useValue: 10,
+ },
+ ],
+})
+```
+
+### `queries`
+
+Queries to bind. Overrides the default set from DOM Testing Library unless
+merged.
+
+**default** : `undefined`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ queries: {...queries, ...customQueries},
+})
+```
+
+### `routes`
+
+The route configuration to set up the router service via
+`RouterTestingModule.withRoutes`. For more info see the
+[Angular Routes docs](https://angular.dev/api/router/Routes).
+
+**default** : `[]`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ declarations: [ChildComponent],
+ routes: [
+ {
+ path: '',
+ children: [
+ {
+ path: 'child/:id',
+ component: ChildComponent,
+ },
+ ],
+ },
+ ],
+})
+```
+
+### `schemas`
+
+A collection of schemas needed to render the component. Allowed values are
+`NO_ERRORS_SCHEMA` and `CUSTOM_ELEMENTS_SCHEMA`.
+
+For more info see the
+[Angular docs](https://angular.dev/guide/components/advanced-configuration#custom-element-schemas).
+
+**default** : `[]`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ schemas: [NO_ERRORS_SCHEMA],
+})
+```
+
+### `removeAngularAttributes`
+
+Removes the Angular attributes (ng-version, and root-id) from the fixture.
+
+**default** : `false`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ removeAngularAttributes: true,
+})
+```
+
+
+### ~~`componentInputs`~~ (deprecated)
+
+An object to set `@Input` properties of the component. Uses `setInput` to set
+the input property. Throws if the component property is not annotated with the
+`@Input` attribute.
+
+**default** : `{}`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ componentInputs: {
+ counterValue: 10,
+ },
+})
+```
+
+### ~~`componentOutputs`~~ (deprecated)
+
+An object to set `@Output` properties of the component.
+
+**default** : `{}`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ componentOutputs: {
+ clicked: (value) => { ... }
+ }
+})
+```
+
+### ~~`componentProperties`~~ (deprecated)
+
+An object to set `@Input` and `@Output` properties of the component. Doesn't
+have a fine-grained control as `inputs` and `on`.
+
+**default** : `{}`
+
+**example**:
+
+```typescript
+await render(AppComponent, {
+ componentProperties: {
+ // an input
+ counterValue: 10,
+ // an output
+ send: (value) => { ... }
+ // a public property
+ name: 'Tim'
+ }
+})
+```
+
+
+## `RenderResult`
+
+### `container`
+
+The containing DOM node of your rendered Angular Component. This is a regular
+DOM node, so you can call `container.querySelector` etc. to inspect the
+children.
+
+### `debug`
+
+Prints out the component's DOM with syntax highlighting. Accepts an optional
+parameter, to print out a specific DOM node.
+
+```typescript
+const {debug} = await render(AppComponent)
+
+debug()
+```
+
+### `rerender`
+
+Changes the input properties of the existing component instance by following
+Angular component lifecycle events (i.e. `ngOnChanges` is called). Input
+properties that are not defined are cleared.
+
+```typescript
+const {rerender} = await render(Counter, {
+ inputs: {count: 4, name: 'Sarah'},
+})
+
+expect(screen.getByTestId('count-value').textContent).toBe('4')
+expect(screen.getByTestId('name-value').textContent).toBe('Sarah')
+
+await rerender({
+ inputs: {count: 7}
+})
+
+// count is updated to 7
+expect(screen.getByTestId('count-value').textContent).toBe('7')
+// name is undefined because it's not provided in rerender
+expect(screen.getByTestId('name-value').textContent).toBeUndefined()
+```
+
+Using `partialUpdate`, only the newly provided properties will be updated. Other
+input properties that aren't provided won't be cleared.
+
+```typescript
+const {rerender} = await render(Counter, {
+ inputs: {count: 4, name: 'Sarah'},
+})
+
+expect(screen.getByTestId('count-value').textContent).toBe('4')
+expect(screen.getByTestId('name-value').textContent).toBe('Sarah')
+
+await rerender({inputs: {count: 7}, partialUpdate: true})
+
+// count is updated to 7
+expect(screen.getByTestId('count-value').textContent).toBe('7')
+// name is still rendered as "Sarah" because of the partial update
+expect(screen.getByTestId('name-value').textContent).toBe('Sarah')
+```
+
+### `detectChanges`
+
+Trigger a change detection cycle for the component.
+
+For more info see the
+[Angular docs](https://angular.dev/api/core/testing/ComponentFixture#detectChanges).
+
+### `debugElement`
+
+The Angular `DebugElement` of the component.
+
+For more info see the [Angular docs](https://angular.dev/api/core/DebugElement).
+
+### `fixture`
+
+The Angular `ComponentFixture` of the component.
+
+For more info see the
+[Angular docs](https://angular.dev/api/core/testing/ComponentFixture).
+
+```typescript
+const {fixture} = await render(AppComponent)
+
+// componentInstance is typed as AppComponent
+const componentInstance = fixture.componentInstance
+```
+
+> 🚨 If you find yourself using `fixture` to access the component's internal
+> values you should reconsider! This probable means, you're testing
+> implementation details.
+
+### `navigate`
+
+Accepts a DOM element or a path as parameter. If an element is passed,
+`navigate` will navigate to the `href` value of the element. If a path is
+passed, `navigate` will navigate to the path.
+
+```typescript
+const { navigate } = await render(AppComponent, {
+ routes: [...]
+})
+
+await navigate(component.getByLabelText('To details'))
+await navigate('details/3')
+```
+
+### `...queries`
+
+The most important feature of `render` is that the queries from
+[DOM Testing Library](/docs/dom-testing-library/intro) are automatically
+returned with their first argument bound to the component under test.
+
+See [Queries](queries/about.mdx) for a complete list.
+
+**example**:
+
+```typescript
+const {getByText, queryByLabelText} = await render(AppComponent)
+
+screen.getByRole('heading', {
+ name: /api/i,
+})
+queryByLabelText(/First name/i')
+```
+
+### `renderDeferBlock`
+
+To test [Deferrable views](https://angular.dev/guide/defer#defer), you can make
+use of `renderDeferBlock`. `renderDeferBlock` will set the desired defer state
+for a specific deferrable block. The default value of a deferrable view is
+`Placeholder`, but you can also set the initial state while rendering the
+component.
+
+```typescript
+const {renderDeferBlock} = await render(FixtureComponent, {
+ deferBlockStates: DeferBlockState.Loading,
+})
+
+expect(screen.getByText(/loading/i)).toBeInTheDocument()
+
+await renderDeferBlock(DeferBlockState.Complete)
+expect(screen.getByText(/completed/i)).toBeInTheDocument()
+```
diff --git a/docs/angular-testing-library/examples.mdx b/docs/angular-testing-library/examples.mdx
new file mode 100644
index 000000000..00ae4fa1a
--- /dev/null
+++ b/docs/angular-testing-library/examples.mdx
@@ -0,0 +1,85 @@
+---
+id: examples
+title: Example
+---
+
+> Read about
+> [best practices](https://timdeschryver.dev/blog/good-testing-practices-with-angular-testing-library),
+> or follow the
+> [guided example](https://timdeschryver.dev/blog/getting-the-most-value-out-of-your-angular-component-tests)
+
+Angular Testing Library can be used with standalone components and also with Angular components that uses Modules.
+The example below shows how to test a standalone component, but the same principles apply to Angular components that uses Modules.
+In fact, there should be no difference in how you test both types of components, only the setup might be different.
+
+```ts title="counter.component.ts"
+@Component({
+ selector: 'app-counter',
+ template: `
+ {{ hello() }}
+ -
+ Current Count: {{ counter() }}
+ +
+ `,
+})
+export class CounterComponent {
+ counter = model(0);
+ hello = input('Hi', { alias: 'greeting' });
+
+ increment() {
+ this.counter.set(this.counter() + 1);
+ }
+
+ decrement() {
+ this.counter.set(this.counter() - 1);
+ }
+}
+```
+
+```typescript title="counter.component.spec.ts"
+import { signal, inputBinding, twoWayBinding } from '@angular/core';
+import { render, screen, fireEvent } from '@testing-library/angular';
+import { CounterComponent } from './counter.component';
+
+describe('Counter', () => {
+ it('renders counter', async () => {
+ await render(CounterComponent, {
+ bindings: [
+ twoWayBinding('counter', signal(5)),
+ // aliases are handled naturally with inputBinding
+ inputBinding('greeting', () => 'Hello Alias!'),
+ ],
+ });
+
+ expect(screen.getByText('Current Count: 5')).toBeVisible();
+ expect(screen.getByText('Hello Alias!')).toBeVisible();
+ });
+
+ it('increments the counter on click', async () => {
+ await render(CounterComponent, {
+ bindings: [twoWayBinding('counter', signal(5))],
+ });
+
+ const incrementButton = screen.getByRole('button', { name: '+' });
+ fireEvent.click(incrementButton);
+
+ expect(screen.getByText('Current Count: 6')).toBeVisible();
+ });
+});
+```
+
+## More examples
+
+More examples can be found in the
+[GitHub project](https://github.com/testing-library/angular-testing-library/tree/master/apps/example-app/src/app/examples).
+These examples include:
+
+- `input` and `output` properties
+- Forms
+- Integration injected services
+- And
+ [more](https://github.com/testing-library/angular-testing-library/tree/master/apps/example-app/src/app/examples)
+
+If you're looking for an example that isn't on the list, please feel free to
+create a
+[new issue](https://github.com/testing-library/angular-testing-library/issues/new).
diff --git a/docs/angular-testing-library/faq.mdx b/docs/angular-testing-library/faq.mdx
new file mode 100644
index 000000000..e333db4b3
--- /dev/null
+++ b/docs/angular-testing-library/faq.mdx
@@ -0,0 +1,109 @@
+---
+id: faq
+title: FAQ
+---
+
+See also the [main FAQ](dom-testing-library/faq.mdx) for questions not specific
+to Angular testing.
+
+
+
+Can I write unit tests with this library?
+
+Definitely yes! You can write unit and integration tests with this library. See
+below for more on how to mock dependencies (because this library intentionally
+does NOT support shallow rendering) if you want to unit test a high level
+component. The tests in this project show several examples of unit testing with
+this library.
+
+As you write your tests, keep in mind:
+
+> The more your tests resemble the way your software is used, the more
+> confidence they can give you. - [17 Feb 2018][guiding-principle]
+
+
+
+
+
+
+ If I can't use shallow rendering, how do I mock out components in tests?
+
+
+In general, you should avoid mocking out components (see
+[the Guiding Principles section](guiding-principles.mdx)). However, if you need
+to, then try to use [ng-mocks](https://ng-mocks.sudo.eu/) and its
+[`MockBuilder`](https://ng-mocks.sudo.eu/extra/with-3rd-party#testing-libraryangular-and-mockbuilder).
+
+```typescript
+import {Component, NgModule} from '@angular/core'
+import {render, screen} from '@testing-library/angular'
+import {MockBuilder} from 'ng-mocks'
+
+@Component({
+ selector: 'app-parent-component',
+ template: ' ',
+})
+class ParentComponent {}
+
+@Component({
+ selector: 'app-child-component',
+ template: 'Child component
',
+})
+class ChildComponent {}
+
+@NgModule({
+ declarations: [ParentComponent, ChildComponent],
+})
+export class AppModule {}
+
+describe('ParentComponent', () => {
+ it('should not render ChildComponent when shallow rendering', async () => {
+ // all imports, declarations and exports of AppModule will be mocked.
+ const dependencies = MockBuilder(ParentComponent, AppModule).build()
+
+ await render(ParentComponent, dependencies)
+
+ expect(screen.queryByText('Child component')).toBeNull()
+ })
+})
+```
+
+
+
+
+
+
+ What level of a component tree should I test? Children, parents, or both?
+
+
+Following the guiding principle of this library, it is useful to break down how
+tests are organized around how the user experiences and interacts with
+application functionality rather than around specific components themselves. In
+some cases, for example for reusable component libraries, it might be useful to
+include developers in the list of users to test for and test each of the
+reusable components individually. Other times, the specific break down of a
+component tree is just an implementation detail and testing every component
+within that tree individually can cause issues (see
+https://kentcdodds.com/blog/avoid-the-test-user).
+
+In practice this means that it is often preferable to test high enough up the
+component tree to simulate realistic user interactions. The question of whether
+it is worth additionally testing at a higher or lower level on top of this comes
+down to a question of tradeoffs and what will provide enough value for the cost
+(see https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests on more info
+on different levels of testing).
+
+For a more in-depth discussion of this topic see
+[this video](https://youtu.be/0qmPdcV-rN8).
+
+
+
+
+
+
+
+[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106
+
+
diff --git a/docs/angular-testing-library/intro.md b/docs/angular-testing-library/intro.md
deleted file mode 100644
index 830975b59..000000000
--- a/docs/angular-testing-library/intro.md
+++ /dev/null
@@ -1,87 +0,0 @@
----
-id: intro
-title: Angular Testing Library
----
-
-🦔 [`@testing-library/angular`][gh] Simple and complete
-[Angular](https://angular.io) testing utilities that encourage good testing
-practices.
-
-```bash
-npm install --save-dev @testing-library/angular
-```
-
-- [`@testing-library/angular-testing-library` on GitHub][gh]
-
-## Usage
-
-counter.component.ts
-
-```typescript
-@Component({
- selector: 'counter',
- template: `
- -
- Current Count: {{ counter }}
- +
- `,
-})
-export class CounterComponent {
- @Input() counter = 0
-
- increment() {
- this.counter += 1
- }
-
- decrement() {
- this.counter -= 1
- }
-}
-```
-
-counter.component.spec.ts
-
-```typescript
-import { render } from '@testing-library/angular'
-import CounterComponent from './counter.component.ts'
-
-describe('Counter', () => {
- test('should render counter', async () => {
- const { getByText } = await render(CounterComponent, {
- componentProperties: { counter: 5 },
- })
-
- expect(getByText('Current Count: 5'))
- })
-
- test('should increment the counter on click', async () => {
- const { getByText, click } = await render(CounterComponent, {
- componentProperties: { counter: 5 },
- })
-
- click(getByText('+'))
-
- expect(getByText('Current Count: 6'))
- })
-})
-```
-
-## API
-
-There is a small difference between `@testing-library/angular` and the
-`testing-library` family, in this library we also expose all of the events via
-the `render` function. This is done to trigger Angular's change detection after
-each interaction.
-
-You can also import these event via `@testing-library/angular`, but the
-Angular's change detection will not be triggered automatically.
-
-The same counts for all the queries provided by the DOM Testing Library
-(`@testing-library/dom`), these are also re-exported and can be imported via
-`@testing-library/angular`.
-
-```typescript
-import { getByText, click } from '@testing-library/angular'
-```
-
-[gh]: https://github.com/testing-library/angular-testing-library
diff --git a/docs/angular-testing-library/intro.mdx b/docs/angular-testing-library/intro.mdx
new file mode 100644
index 000000000..a7820b7f0
--- /dev/null
+++ b/docs/angular-testing-library/intro.mdx
@@ -0,0 +1,65 @@
+---
+id: intro
+title: Angular Testing Library
+sidebar_label: Introduction
+---
+
+[`Angular Testing Library`](https://github.com/testing-library/angular-testing-library)
+builds on top of
+[`DOM Testing Library`](https://github.com/testing-library/dom-testing-library)
+by adding APIs for working with Angular components.
+Starting from ATL version 17, you also need to install `@testing-library/dom`:
+
+```bash npm2yarn
+npm install --save-dev @testing-library/angular @testing-library/dom
+```
+
+Or, you can use the `ng add` command.
+This sets up your project to use Angular Testing Library, which also includes the installation of `@testing-library/dom`.
+
+```bash
+ng add @testing-library/angular
+```
+
+- [`@testing-library/angular-testing-library` on GitHub](https://github.com/testing-library/angular-testing-library)
+
+## The problem
+
+You want to write maintainable tests for your Angular 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.
+
+## This solution
+
+The `Angular Testing Library` is a very lightweight solution for testing Angular
+components. It provides light utility functions on top of
+[`DOM Testing Library`](https://github.com/testing-library/dom-testing-library)
+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-principles.mdx)
+
+So rather than dealing with instances of rendered Angular components, your tests
+will work with actual DOM nodes. The utilities this library provides facilitate
+querying the DOM in the same way the user would. Finding form elements by their
+label text (just like a user would), finding links and buttons from their text
+(like a user would). It also exposes a recommended way to find elements by a
+`data-testid` as an "escape hatch" for elements where the text content and label
+do not make sense or is not practical.
+
+This library encourages your applications to be more accessible and allows you
+to get your tests closer to using your components the way a user will, which
+allows your tests to give you more confidence that your application will work
+when a real user uses it.
+
+The `Angular Testing Library`:
+
+- Re-exports the `query` and `fireEvent` utility functions from
+ [`DOM Testing Library`](https://github.com/testing-library/dom-testing-library).
+- Encapsulates the `fireEvent` functions of your component to automatically call
+ `detectChanges()` after an event occurs
+- Is test framework agnostic, it runs on every test framework
diff --git a/docs/angular-testing-library/version-compatibility.mdx b/docs/angular-testing-library/version-compatibility.mdx
new file mode 100644
index 000000000..3b64dd5d2
--- /dev/null
+++ b/docs/angular-testing-library/version-compatibility.mdx
@@ -0,0 +1,18 @@
+---
+id: version-compatibility
+title: Version compatibility
+---
+
+An overview of the compatibility between different versions of Angular Testing
+Library and Angular.
+
+| Angular | Angular Testing Library |
+| ------- | ---------------------------------- |
+| 20.x | 18.x |
+| 19.x | 18.x, 17.x, 16.x, 15.x, 14.x, 13.x |
+| 18.x | 17.x, 16.x, 15.x, 14.x, 13.x |
+| 17.x | 17.x, 16.x, 15.x, 14.x, 13.x |
+| 16.x | 14.x, 13.x |
+| >= 15.1 | 14.x, 13.x |
+| < 15.1 | 12.x, 11.x |
+| 14.x | 12.x, 11.x |
diff --git a/docs/bs-react-testing-library/examples.md b/docs/bs-react-testing-library/examples.mdx
similarity index 97%
rename from docs/bs-react-testing-library/examples.md
rename to docs/bs-react-testing-library/examples.mdx
index ce8d544a7..08e96a7c9 100644
--- a/docs/bs-react-testing-library/examples.md
+++ b/docs/bs-react-testing-library/examples.mdx
@@ -1,6 +1,6 @@
---
id: examples
-title: Examples
+title: Example
---
You can find more bs-dom-testing-library examples at
@@ -11,9 +11,7 @@ You can find more bs-react-testing-library examples at
## React Testing Library
-```reason
-/* Component_test.re */
-
+```reason title="Component_test.re"
open Jest;
open Expect;
open ReactTestingLibrary;
@@ -37,8 +35,7 @@ with typings and creating events.
### getByText
-```reason
-/* __tests__/example_test.re */
+```reason title="__tests__/example_test.re"
open Jest;
open DomTestingLibrary;
open Expect;
diff --git a/docs/bs-react-testing-library/intro.md b/docs/bs-react-testing-library/intro.mdx
similarity index 95%
rename from docs/bs-react-testing-library/intro.md
rename to docs/bs-react-testing-library/intro.mdx
index 8e2545423..316ccc9b2 100644
--- a/docs/bs-react-testing-library/intro.md
+++ b/docs/bs-react-testing-library/intro.mdx
@@ -13,8 +13,11 @@ Bindings for several testing libraries have been ported to [ReasonML][re].
[`bs-dom-testing-library`][gh-dom] contains [BuckleScript][bs] bindings for
`DOM Testing Library`.
-```
+```bash npm2yarn
npm install --save-dev bs-dom-testing-library
+```
+
+```bash npm2yarn
npm install --save-dev bs-react-testing-library
```
@@ -49,7 +52,7 @@ _or_
This is what [BuckleScript][bs] uses to compile the [Reason][re] code to JS. If
it is not in your project you can install it like so:
-```
+```bash npm2yarn
npm install --save-dev bs-platform
```
@@ -60,7 +63,7 @@ examples here will be using it.
- [bs-jest on GitHub](https://github.com/glennsl/bs-jest)
-```
+```bash npm2yarn
npm install --save-dev @glennsl/bs-jest
```
diff --git a/docs/contributing.md b/docs/contributing.md
deleted file mode 100644
index d446eacbe..000000000
--- a/docs/contributing.md
+++ /dev/null
@@ -1,81 +0,0 @@
----
-id: contributing
-title: Contributing
-sidebar_label: Contributing
----
-
-## License
-
-`React Testing Library` is distributed under the open-source MIT license
-
-## 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.
-
-- [Spectrum][spectrum]
-- [Reactiflux on Discord][reactiflux]
-- [Stack Overflow][stackoverflow]
-
-
-
-
-
-[npm]: https://www.npmjs.com/
-[node]: https://nodejs.org
-[build-badge]: https://img.shields.io/travis/kentcdodds/react-testing-library.svg?style=flat-square
-[build]: https://travis-ci.org/kentcdodds/react-testing-library
-[coverage-badge]: https://img.shields.io/codecov/c/github/kentcdodds/react-testing-library.svg?style=flat-square
-[coverage]: https://codecov.io/github/kentcdodds/react-testing-library
-[version-badge]: https://img.shields.io/npm/v/react-testing-library.svg?style=flat-square
-[package]: https://www.npmjs.com/package/react-testing-library
-[downloads-badge]: https://img.shields.io/npm/dm/react-testing-library.svg?style=flat-square
-[npmtrends]: http://www.npmtrends.com/react-testing-library
-[spectrum-badge]: https://withspectrum.github.io/badge/badge.svg
-[spectrum]: https://spectrum.chat/testing-library
-[license-badge]: https://img.shields.io/npm/l/react-testing-library.svg?style=flat-square
-[license]: https://github.com/testing-library/react-testing-library/blob/master/LICENSE
-[prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square
-[prs]: http://makeapullrequest.com
-[donate-badge]: https://img.shields.io/badge/$-support-green.svg?style=flat-square
-[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/master/CODE_OF_CONDUCT.md
-[github-watch-badge]: https://img.shields.io/github/watchers/kentcdodds/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/kentcdodds/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%40kentcdodds%20https%3A%2F%2Fgithub.com%2Fkentcdodds%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/kentcdodds/all-contributors#emoji-key
-[all-contributors]: https://github.com/kentcdodds/all-contributors
-[set-immediate]: https://developer.mozilla.org/en-US/docs/Web/API/Window/setImmediate
-[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106
-[data-testid-blog-post]: https://blog.kentcdodds.com/making-your-ui-tests-resilient-to-change-d37a6ee37269
-[dom-testing-lib-textmatch]: https://github.com/testing-library/dom-testing-library#textmatch
-[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"+
-[reactiflux]: https://www.reactiflux.com/
-[stackoverflow]: https://stackoverflow.com/questions/tagged/react-testing-library
-
-
diff --git a/docs/contributing.mdx b/docs/contributing.mdx
new file mode 100644
index 000000000..167844e54
--- /dev/null
+++ b/docs/contributing.mdx
@@ -0,0 +1,49 @@
+---
+id: contributing
+title: Contributing
+sidebar_label: Contributing
+---
+
+## License
+
+`React Testing Library` is distributed under the open-source MIT license
+
+## 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]
+
+
+
+
+
+[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]: https://discord.gg/testing-library
+[stackoverflow]: https://stackoverflow.com/questions/tagged/react-testing-library
+
+
diff --git a/docs/cypress-testing-library/intro.md b/docs/cypress-testing-library/intro.md
deleted file mode 100644
index 4fcf3da49..000000000
--- a/docs/cypress-testing-library/intro.md
+++ /dev/null
@@ -1,53 +0,0 @@
----
-id: intro
-title: Cypress Testing Library
----
-
-[`Cypress Testing Library`][gh] allows the use of dom-testing queries within
-[Cypress](https://cypress.io) end-to-end browser tests.
-
-```
-npm install --save-dev cypress @testing-library/cypress
-```
-
-- [Cypress Testing Library on GitHub][gh]
-
-## Usage
-
-`Cypress Testing Library` extends Cypress' `cy` command.
-
-Add this line to your project's `cypress/support/commands.js`:
-
-```
-import '@testing-library/cypress/add-commands';
-```
-
-You can now use all of `DOM Testing Library`'s `getBy`, `getAllBy`, `queryBy`
-and `queryAllBy` commands.
-[See `DOM Testing Library` API for reference](dom-testing-library/api-queries.md)
-
-## Examples
-
-To show some simple examples (from
-[https://github.com/testing-library/cypress-testing-library/blob/master/cypress/integration/commands.spec.js](https://github.com/testing-library/cypress-testing-library/blob/master/cypress/integration/commands.spec.js)):
-
-```javascript
-cy.getAllByText('Jackie Chan').click()
-cy.queryByText('Button Text').should('exist')
-cy.queryByText('Non-existing Button Text').should('not.exist')
-cy.queryByLabelText('Label text', { timeout: 7000 }).should('exist')
-cy.get('form').within(() => {
- cy.getByText('Button Text').should('exist')
-})
-cy.get('form').then(subject => {
- cy.getByText('Button Text', { container: subject }).should('exist')
-})
-```
-
-`Cypress Testing Library` supports both jQuery elements and DOM nodes. This is
-necessary because Cypress uses jQuery elements, while `DOM Testing Library`
-expects DOM nodes. When you pass a jQuery element as `container`, it will get
-the first DOM node from the collection and use that as the `container` parameter
-for the DOM Testing Library functions.
-
-[gh]: https://github.com/testing-library/cypress-testing-library
diff --git a/docs/cypress-testing-library/intro.mdx b/docs/cypress-testing-library/intro.mdx
new file mode 100644
index 000000000..3c88d4d2b
--- /dev/null
+++ b/docs/cypress-testing-library/intro.mdx
@@ -0,0 +1,75 @@
+---
+id: intro
+title: Cypress Testing Library
+---
+
+[`Cypress Testing Library`][gh] allows the use of dom-testing queries within
+[Cypress](https://cypress.io) end-to-end browser tests.
+
+```bash npm2yarn
+npm install --save-dev cypress @testing-library/cypress
+```
+
+- [Cypress Testing Library on GitHub][gh]
+
+## Usage
+
+`Cypress Testing Library` extends Cypress's `cy` commands.
+
+Add this line to your project's `cypress/support/commands.js`:
+
+```js
+import '@testing-library/cypress/add-commands'
+```
+
+You can now use some of `DOM Testing Library`'s `findBy`, and `findAllBy` commands off the global `cy` object.
+[See the `About queries` docs for reference](/docs/queries/about).
+
+> Note: the `get*` queries are not supported because for reasonable Cypress
+> tests you need retryability and `find*` queries already support that. `query*`
+> queries are no longer necessary since v5 and will be removed in v6. `find*`
+> fully support built-in Cypress assertions (removes the only use-case for
+> `query*`).
+
+## With TypeScript
+
+Typings should be added as follows in `tsconfig.json`:
+
+```json
+{
+ "compilerOptions": {
+ "types": ["cypress", "@testing-library/cypress"]
+ }
+}
+```
+
+You can find
+[all Library definitions here](https://github.com/testing-library/cypress-testing-library/blob/master/types/index.d.ts).
+
+## Examples
+
+To show some simple examples (from
+[cypress/integration/find.spec.js](https://github.com/testing-library/cypress-testing-library/blob/97939da7d4707a71049884c0324c0eda56e26fc2/cypress/integration/find.spec.js)):
+
+```javascript
+cy.findByRole('button', {name: /Jackie Chan/i}).click()
+cy.findByRole('button', {name: /Button Text/i}).should('exist')
+cy.findByRole('button', {name: /Non-existing Button Text/i}).should('not.exist')
+cy.findByLabelText(/Label text/i, {timeout: 7000}).should('exist')
+
+// findByRole _inside_ a form element
+cy.get('form')
+ .findByRole('button', {name: /Button Text/i})
+ .should('exist')
+cy.findByRole('dialog').within(() => {
+ cy.findByRole('button', {name: /confirm/i})
+})
+```
+
+`Cypress Testing Library` supports both jQuery elements and DOM nodes. This is
+necessary because Cypress uses jQuery elements, while `DOM Testing Library`
+expects DOM nodes. When you pass a jQuery element as `container`, it will get
+the first DOM node from the collection and use that as the `container` parameter
+for the `DOM Testing Library` functions.
+
+[gh]: https://github.com/testing-library/cypress-testing-library
diff --git a/docs/dom-testing-library/api-accessibility.mdx b/docs/dom-testing-library/api-accessibility.mdx
new file mode 100644
index 000000000..9b5e4e761
--- /dev/null
+++ b/docs/dom-testing-library/api-accessibility.mdx
@@ -0,0 +1,92 @@
+---
+id: api-accessibility
+title: Accessibility
+---
+
+## Testing for Accessibility
+
+One of the guiding principles of the Testing Library APIs is that they should
+enable you to test your app the way your users use it, including through
+accessibility interfaces like screen readers.
+
+See the page on [queries](queries/about.mdx#priority) for details on how using a
+semantic HTML query can make sure your app works with browser accessibility
+APIs.
+
+## `getRoles`
+
+This function allows iteration over the implicit ARIA roles represented in a
+given tree of DOM nodes.
+
+It returns an object, indexed by role name, with each value being an array of
+elements which have that implicit ARIA role.
+
+See
+[ARIA in HTML](https://www.w3.org/TR/html-aria/#document-conformance-requirements-for-use-of-aria-attributes-in-html)
+for more information about implicit ARIA roles.
+
+```javascript
+import {getRoles} from '@testing-library/dom'
+
+const nav = document.createElement('nav')
+nav.innerHTML = `
+`
+console.log(getRoles(nav))
+
+// Object {
+// navigation: [ ],
+// list: [],
+// listitem: [ , ]
+// }
+```
+
+## `isInaccessible`
+
+This function will compute if the given element should be excluded from the
+accessibility API by the browser. It implements every **MUST** criteria from the
+[Excluding Elements from the Accessibility Tree](https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion)
+section in WAI-ARIA 1.2 with the exception of checking the `role` attribute.
+
+It is defined as:
+
+```typescript
+function isInaccessible(element: Element): boolean
+```
+
+## `logRoles`
+
+This helper function can be used to print out a list of all the implicit ARIA
+roles within a tree of DOM nodes, each role containing a list of all of the
+nodes which match that role. This can be helpful for finding ways to query the
+DOM under test with [getByRole](queries/byrole.mdx).
+
+```javascript
+import {logRoles} from '@testing-library/dom'
+
+const nav = document.createElement('nav')
+nav.innerHTML = `
+`
+
+logRoles(nav)
+```
+
+Result:
+
+```
+navigation:
+
+--------------------------------------------------
+list:
+
+--------------------------------------------------
+listitem:
+
+
+--------------------------------------------------
+```
diff --git a/docs/dom-testing-library/api-async.md b/docs/dom-testing-library/api-async.md
deleted file mode 100644
index 24008c9db..000000000
--- a/docs/dom-testing-library/api-async.md
+++ /dev/null
@@ -1,260 +0,0 @@
----
-id: api-async
-title: Async Utilities
----
-
-Several utilities are provided for dealing with asynchronous code. These can be
-useful to wait for an element to appear or disappear in response to an action.
-(See the [guide to testing disappearance](guide-disappearance.md).)
-
-## `wait`
-
-```typescript
-function wait(
- callback?: () => void,
- options?: {
- timeout?: number
- interval?: number
- }
-): Promise
-```
-
-When in need to wait for non-deterministic periods of time you can use `wait`,
-to wait for your expectations to pass. The `wait` function is a small wrapper
-around the
-[`wait-for-expect`](https://github.com/TheBrainFamily/wait-for-expect) module.
-Here's a simple example:
-
-```javascript
-// ...
-// Wait until the callback does not throw an error. In this case, that means
-// it'll wait until we can get a form control with a label that matches "username".
-await wait(() => getByLabelText(container, 'username'))
-getByLabelText(container, 'username').value = 'chucknorris'
-// ...
-```
-
-This can be useful if you have a unit test that mocks API calls and you need to
-wait for your mock promises to all resolve.
-
-The default `callback` is a no-op function (used like `await wait()`). This can
-be helpful if you only need to wait for one tick of the event loop (in the case
-of mocked API calls with promises that resolve immediately).
-
-The default `timeout` is `4500ms` which will keep you under
-[Jest's default timeout of `5000ms`](https://facebook.github.io/jest/docs/en/jest-object.html#jestsettimeouttimeout).
-
-The default `interval` is `50ms`. However it will run your callback immediately
-on the next tick of the event loop (in a `setTimeout`) before starting the
-intervals.
-
-## `waitForElement`
-
-```typescript
-function waitForElement(
- callback: () => T,
- options?: {
- container?: HTMLElement
- timeout?: number
- mutationObserverOptions?: MutationObserverInit
- }
-): Promise
-```
-
-When in need to wait for DOM elements to appear, disappear, or change you can
-use `waitForElement`. The `waitForElement` function is a small wrapper around
-the
-[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver).
-
-Here's a simple example:
-
-```javascript
-// ...
-// Wait until the callback does not throw an error and returns a truthy value. In this case, that means
-// it'll wait until we can get a form control with a label that matches "username".
-// The difference from `wait` is that rather than running your callback on
-// an interval, it's run as soon as there are DOM changes in the container
-// and returns the value returned by the callback.
-const usernameElement = await waitForElement(
- () => getByLabelText(container, 'username'),
- { container }
-)
-usernameElement.value = 'chucknorris'
-// ...
-```
-
-You can also wait for multiple elements at once:
-
-```javascript
-const [usernameElement, passwordElement] = await waitForElement(
- () => [
- getByLabelText(container, 'username'),
- getByLabelText(container, 'password'),
- ],
- { container }
-)
-```
-
-Using
-[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
-is more efficient than polling the DOM at regular intervals with `wait`. This
-library sets up a
-[`'mutationobserver-shim'`](https://github.com/megawac/MutationObserver.js) on
-the global `window` object for cross-platform compatibility with older browsers
-and the [`jsdom`](https://github.com/jsdom/jsdom/issues/639) that is usually
-used in Node-based tests.
-
-The default `container` is the global `document`. Make sure the elements you
-wait for will be attached to it, or set a different `container`.
-
-The default `timeout` is `4500ms` which will keep you under
-[Jest's default timeout of `5000ms`](https://facebook.github.io/jest/docs/en/jest-object.html#jestsettimeouttimeout).
-
- The default `mutationObserverOptions` is
-`{subtree: true, childList: true, attributes: true, characterData: true}` which
-will detect additions and removals of child elements (including text nodes) in
-the `container` and any of its descendants. It will also detect attribute
-changes.
-
-## `waitForDomChange`
-
-```typescript
-function waitForDomChange(options?: {
- container?: HTMLElement
- timeout?: number
- mutationObserverOptions?: MutationObserverInit
-}): Promise
-```
-
-When in need to wait for the DOM to change you can use `waitForDomChange`. The
-`waitForDomChange` function is a small wrapper around the
-[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver).
-
-Here is an example where the promise will be resolved because the container is
-changed:
-
-```javascript
-const container = document.createElement('div')
-waitForDomChange({ container })
- .then(() => console.log('DOM changed!'))
- .catch(err => console.log(`Error you need to deal with: ${err}`))
-container.append(document.createElement('p'))
-// if 👆 was the only code affecting the container and it was not run,
-// waitForDomChange would throw an error
-```
-
-The promise will resolve with a
-[`mutationsList`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/MutationObserver)
-which you can use to determine what kind of a change (or changes) affected the
-container
-
-```javascript
-const container = document.createElement('div')
-container.setAttribute('data-cool', 'true')
-waitForDomChange({ container }).then(mutationsList => {
- const mutation = mutationsList[0]
- console.log(
- `was cool: ${mutation.oldValue}\ncurrently cool: ${
- mutation.target.dataset.cool
- }`
- )
-})
-container.setAttribute('data-cool', 'false')
-/*
- logs:
- was cool: true
- currently cool: false
-*/
-```
-
-Using
-[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
-is more efficient than polling the DOM at regular intervals with `wait`. This
-library sets up a
-[`'mutationobserver-shim'`](https://github.com/megawac/MutationObserver.js) on
-the global `window` object for cross-platform compatibility with older browsers
-and the [`jsdom`](https://github.com/jsdom/jsdom/issues/639) that is usually
-used in Node-based tests.
-
-The default `container` is the global `document`. Make sure the elements you
-wait for will be attached to it, or set a different `container`.
-
-The default `timeout` is `4500ms` which will keep you under
-[Jest's default timeout of `5000ms`](https://facebook.github.io/jest/docs/en/jest-object.html#jestsettimeouttimeout).
-
- The default `mutationObserverOptions` is
-`{subtree: true, childList: true, attributes: true, characterData: true}` which
-will detect additions and removals of child elements (including text nodes) in
-the `container` and any of its descendants. It will also detect attribute
-changes.
-
-## `waitForElementToBeRemoved`
-
-```typescript
-function waitForElementToBeRemoved(
- callback: () => T,
- options?: {
- container?: HTMLElement
- timeout?: number
- mutationObserverOptions?: MutationObserverInit
- }
-): Promise
-```
-
-To wait for the removal of element(s) from the DOM you can use
-`waitForElementToBeRemoved`. The `waitForElementToBeRemoved` function is a small
-wrapper around the
-[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver).
-
-The callback must return the pre-existing element or array of elements that are
-expected to be removed.
-
-Here is an example where the promise resolves with `true` because the element is
-removed:
-
-```javascript
-const el = document.querySelector('div.getOuttaHere')
-
-waitForElementToBeRemoved(() =>
- document.querySelector('div.getOuttaHere')
-).then(() => console.log('Element no longer in DOM'))
-
-el.setAttribute('data-neat', true)
-// other mutations are ignored...
-
-el.parentElement.removeChild(el)
-// logs 'Element no longer in DOM'
-```
-
-`waitForElementToBeRemoved` will throw an error when the provided callback does
-not return an element.
-
-```javascript
-waitForElementToBeRemoved(() => null).catch(err => console.log(err))
-
-// 'The callback function which was passed did not return an element
-// or non-empty array of elements.
-// waitForElementToBeRemoved requires that the element(s) exist
-// before waiting for removal.'
-```
-
-Using
-[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
-is more efficient than polling the DOM at regular intervals with `wait`. This
-library sets up a
-[`'mutationobserver-shim'`](https://github.com/megawac/MutationObserver.js) on
-the global `window` object for cross-platform compatibility with older browsers
-and the [`jsdom`](https://github.com/jsdom/jsdom/issues/639) that is usually
-used in Node-based tests.
-
-The default `container` is the global `document`. Make sure the elements you
-wait for are descendants of `container`.
-
-The default `timeout` is `4500ms` which will keep you under
-[Jest's default timeout of `5000ms`](https://facebook.github.io/jest/docs/en/jest-object.html#jestsettimeouttimeout).
-
- The default `mutationObserverOptions` is
-`{subtree: true, childList: true, attributes: true, characterData: true}` which
-will detect additions and removals of child elements (including text nodes) in
-the `container` and any of its descendants. It will also detect attribute
-changes.
diff --git a/docs/dom-testing-library/api-async.mdx b/docs/dom-testing-library/api-async.mdx
new file mode 100644
index 000000000..700fbe81d
--- /dev/null
+++ b/docs/dom-testing-library/api-async.mdx
@@ -0,0 +1,146 @@
+---
+id: api-async
+title: Async Methods
+---
+
+Several utilities are provided for dealing with asynchronous code. These can be
+useful to wait for an element to appear or disappear in response to an event,
+user action, timeout, or Promise. (See the
+[guide to testing disappearance](guide-disappearance.mdx).)
+
+The async methods return Promises, so be sure to use `await` or `.then` when
+calling them.
+
+## `findBy` Queries
+
+`findBy` methods are a combination of `getBy`
+[queries](queries/about.mdx#types-of-queries) and [`waitFor`](#waitfor). They
+accept the waitFor options as the last argument (e.g.
+`await screen.findByText('text', queryOptions, waitForOptions)`).
+
+`findBy` queries work when you expect an element to appear but the change to the
+DOM might not happen immediately.
+
+```js
+const button = screen.getByRole('button', {name: 'Click Me'})
+fireEvent.click(button)
+await screen.findByText('Clicked once')
+fireEvent.click(button)
+await screen.findByText('Clicked twice')
+```
+
+## `waitFor`
+
+```typescript
+function waitFor(
+ callback: () => T | Promise,
+ options?: {
+ container?: HTMLElement
+ timeout?: number
+ interval?: number
+ onTimeout?: (error: Error) => Error
+ mutationObserverOptions?: MutationObserverInit
+ },
+): Promise
+```
+
+When in need to wait for any period of time you can use `waitFor`, to wait for
+your expectations to pass. Returning _a falsy condition is not sufficient_ to
+trigger a retry, the callback must throw an error in order to retry the
+condition. Here's a simple example:
+
+```javascript
+// ...
+// Wait until the callback does not throw an error. In this case, that means
+// it'll wait until the mock function has been called once.
+await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1))
+// ...
+```
+
+`waitFor` may run the callback a number of times until the timeout is reached.
+Note that the number of calls is constrained by the `timeout` and `interval`
+options.
+
+This can be useful if you have a unit test that mocks API calls and you need to
+wait for your mock promises to all resolve.
+
+If you return a promise in the `waitFor` callback (either explicitly or
+implicitly with the `async` syntax), then the `waitFor` utility does not call
+your callback again until that promise rejects. This allows you to `waitFor`
+things that must be checked asynchronously.
+
+The default `container` is the global `document`. Make sure the elements you
+wait for are descendants of `container`.
+
+The default `interval` is `50ms`. However it runs your callback immediately
+before starting the intervals.
+
+The default `timeout` is `1000ms`.
+
+The default `onTimeout` takes the error and appends the `container`'s printed
+state to the error message which should hopefully make it easier to track down
+what caused the timeout.
+
+The default `mutationObserverOptions` is
+`{subtree: true, childList: true, attributes: true, characterData: true}` which
+detects additions and removals of child elements (including text nodes) in the
+`container` and any of its descendants. It also detects attribute changes. When
+any of those changes occur, it re-runs the callback.
+
+## `waitForElementToBeRemoved`
+
+```typescript
+function waitForElementToBeRemoved(
+ callback: (() => T) | T,
+ options?: {
+ container?: HTMLElement
+ timeout?: number
+ interval?: number
+ onTimeout?: (error: Error) => Error
+ mutationObserverOptions?: MutationObserverInit
+ },
+): Promise
+```
+
+To wait for the removal of element(s) from the DOM you can use
+`waitForElementToBeRemoved`. The `waitForElementToBeRemoved` function is a small
+wrapper around the `waitFor` utility.
+
+The first argument must be an element, array of elements, or a callback which
+returns an element or array of elements.
+
+Here is an example where the promise resolves because the element is removed:
+
+```javascript
+const el = document.querySelector('div.getOuttaHere')
+
+waitForElementToBeRemoved(document.querySelector('div.getOuttaHere')).then(() =>
+ console.log('Element no longer in DOM'),
+)
+
+el.setAttribute('data-neat', true)
+// other mutations are ignored...
+
+el.parentElement.removeChild(el)
+// logs 'Element no longer in DOM'
+```
+
+`waitForElementToBeRemoved` throws an error if the first argument is `null` or
+an empty array:
+
+```javascript
+waitForElementToBeRemoved(null).catch(err => console.log(err))
+waitForElementToBeRemoved(queryByText(/not here/i)).catch(err =>
+ console.log(err),
+)
+waitForElementToBeRemoved(queryAllByText(/not here/i)).catch(err =>
+ console.log(err),
+)
+waitForElementToBeRemoved(() => getByText(/not here/i)).catch(err =>
+ console.log(err),
+)
+
+// Error: The element(s) given to waitForElementToBeRemoved are already removed. waitForElementToBeRemoved requires that the element(s) exist(s) before waiting for removal.
+```
+
+The options object is forwarded to `waitFor`.
diff --git a/docs/dom-testing-library/api-configuration.md b/docs/dom-testing-library/api-configuration.md
deleted file mode 100644
index 33c27718f..000000000
--- a/docs/dom-testing-library/api-configuration.md
+++ /dev/null
@@ -1,51 +0,0 @@
----
-id: api-configuration
-title: Configuration
----
-
-## Configuration
-
-The library can be configured via the `configure` function, which accepts:
-
-- a plain JS object; this will be merged into the existing configuration. e.g.
- `configure({testIdAttribute: 'not-data-testid'})`
-- a function; the function will be given the existing configuration, and should
- return a plain JS object which will be merged as above, e.g.
- `configure(existingConfig => ({something: [...existingConfig.something, 'extra value for the something array']}))`
-
-Configuration options:
-
-`testIdAttribute`: The attribute used by `getByTestId` and related queries.
-Defaults to `data-testid`. See [`getByTestId`](#getbytestid).
-
-```html
-
-```
-
-
-
-
-
-```js
-// setup-tests.js
-import { configure } from '@testing-library/dom'
-
-configure({testIdAttribute: 'my-data-test-id'})`
-```
-
-
-
-```js
-// setup-tests.js
-import { configure } from '@testing-library/react'
-
-configure({testIdAttribute: 'my-data-test-id'})`
-```
-
-
-
-```
-The configuration object is not currently exposed to in Cypress Testing Library
-```
-
-
diff --git a/docs/dom-testing-library/api-configuration.mdx b/docs/dom-testing-library/api-configuration.mdx
new file mode 100644
index 000000000..72dd2e196
--- /dev/null
+++ b/docs/dom-testing-library/api-configuration.mdx
@@ -0,0 +1,177 @@
+---
+id: api-configuration
+title: Configuration Options
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+## Introduction
+
+The library can be configured via the `configure` function, which accepts:
+
+- a plain JS object; this will be merged into the existing configuration. e.g.
+ `configure({testIdAttribute: 'not-data-testid'})`
+- a function; the function will be given the existing configuration, and should
+ return a plain JS object which will be merged as above, e.g.
+ `configure(existingConfig => ({something: [...existingConfig.something, 'extra value for the something array']}))`
+
+> **Note**
+>
+> Framework-specific wrappers like React Testing Library may add more options to
+> the ones shown below.
+
+
+
+
+```js title="setup-tests.js"
+import {configure} from '@testing-library/dom'
+import serialize from 'my-custom-dom-serializer'
+
+configure({
+ testIdAttribute: 'data-my-test-id',
+ getElementError: (message, container) => {
+ const customMessage = [message, serialize(container.firstChild)].join(
+ '\n\n',
+ )
+ return new Error(customMessage)
+ },
+})
+```
+
+
+
+
+```js title="setup-tests.js"
+import {configure} from '@testing-library/react'
+
+configure({testIdAttribute: 'data-my-test-id'})
+```
+
+
+
+
+```ts title="setup-tests.ts"
+import {configure} from '@testing-library/angular'
+
+configure({
+ dom: {
+ testIdAttribute: 'data-my-test-id',
+ },
+})
+```
+
+
+
+
+```js title="setup-tests.js"
+import {configure} from '@testing-library/cypress'
+
+configure({testIdAttribute: 'data-my-test-id'})
+```
+
+
+
+
+## Options
+
+### `computedStyleSupportsPseudoElements`
+
+Set to `true` if `window.getComputedStyle` supports pseudo-elements i.e. a
+second argument. If you're using testing-library in a browser you almost always
+want to set this to `true`. Only very old browser don't support this property
+(such as IE 8 and earlier). However, `jsdom` does not support the second
+argument currently. This includes versions of `jsdom` prior to `16.4.0` and any
+version that logs a `not implemented` warning when calling
+[`getComputedStyle`](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle)
+with a second argument e.g.
+`window.getComputedStyle(document.createElement('div'), '::after')`. Defaults to
+`false`
+
+### `defaultHidden`
+
+The default value for the [`hidden` option](queries/byrole#hidden) used by
+[`getByRole`](queries/byrole.mdx). Defaults to `false`.
+
+### `defaultIgnore`
+
+The default value for the [`ignore` option](queries/bytext.mdx#ignore) used by
+[`getByText`](queries/bytext.mdx). Also determines the nodes that are being
+ignored when errors are printed.
+
+Defaults to `script, style`.
+
+### `showOriginalStackTrace`
+
+By default, [`waitFor`](api-async#waitfor) will ensure that the stack trace for
+errors thrown by Testing Library is cleaned up and shortened so it's easier for
+you to identify the part of your code that resulted in the error (async stack
+traces are hard to debug). If you want to disable this, then
+set`showOriginalStackTrace` to `false`. You can also disable this for a specific
+call in the options you pass to `waitFor`.
+
+### `throwSuggestions` (experimental)
+
+When enabled, if [better queries](queries/about.mdx#priority) are available, the
+test will fail and provide a suggested query to use instead. Defaults to
+`false`.
+
+To disable a suggestion for a single query just add `{suggest:false}` as an
+option.
+
+```js
+screen.getByTestId('foo', {suggest: false}) // will not throw a suggestion
+```
+
+:::note
+
+When this option is enabled, it may provide suggestions that lack an intuitive
+implementation. Typically this happens for
+[roles which cannot be named](https://w3c.github.io/aria/#namefromprohibited),
+most notably paragraphs. For instance, if you attempt to use
+[`getByText`](queries/bytext.mdx), you may encounter the following error:
+
+```
+TestingLibraryElementError: A better query is available, try this:
+ getByRole('paragraph')
+```
+
+However, there is no direct way to query paragraphs using the config object
+parameter, such as in `getByRole('paragraph', { name: 'Hello World' })`.
+
+To address this issue, you can leverage a custom function to validate the
+element's structure, as shown in the example below. More information can be
+found in the
+[GitHub issue](https://github.com/testing-library/dom-testing-library/issues/1306)
+
+```js
+getByRole('paragraph', {
+ name: (_, element) => element.textContent === 'Hello world',
+})
+```
+
+:::
+
+### `testIdAttribute`
+
+The attribute used by [`getByTestId`](queries/bytestid.mdx) and related queries.
+Defaults to `data-testid`.
+
+### `getElementError`
+
+A function that returns the error used when
+[get or find queries](queries/about.mdx#types-of-queries) fail. Takes the error
+message and container object as arguments.
+
+### `asyncUtilTimeout`
+
+The global timeout value in milliseconds used by [`waitFor`](api-async#waitfor)
+utilities. Defaults to 1000ms.
diff --git a/docs/dom-testing-library/api-custom-queries.mdx b/docs/dom-testing-library/api-custom-queries.mdx
new file mode 100644
index 000000000..8147ead50
--- /dev/null
+++ b/docs/dom-testing-library/api-custom-queries.mdx
@@ -0,0 +1,119 @@
+---
+id: api-custom-queries
+title: Custom Queries
+---
+
+`DOM Testing Library` exposes many of the helper functions that are used to
+implement the default queries. You can use the helpers to build custom queries.
+For example, the code below shows a way to override the default `testId` queries
+to use a different data-attribute. (Note: test files would import
+`test-utils.js` instead of using `DOM Testing Library` directly).
+
+> **Note**
+>
+> Custom queries can be added to `React Testing Library`'s `render` method by
+> adding `queries` to the options config object. See the render
+> [options](react-testing-library/api.mdx#render-options).
+
+> Custom queries are different than
+> [custom render](react-testing-library/setup.mdx#custom-render) methods.
+
+```js title="test-utils.js"
+const domTestingLib = require('@testing-library/dom')
+const {queryHelpers} = domTestingLib
+
+export const queryByTestId = queryHelpers.queryByAttribute.bind(
+ null,
+ 'data-test-id',
+)
+export const queryAllByTestId = queryHelpers.queryAllByAttribute.bind(
+ null,
+ 'data-test-id',
+)
+
+export function getAllByTestId(container, id, ...rest) {
+ const els = queryAllByTestId(container, id, ...rest)
+ if (!els.length) {
+ throw queryHelpers.getElementError(
+ `Unable to find an element by: [data-test-id="${id}"]`,
+ container,
+ )
+ }
+ return els
+}
+
+export function getByTestId(container, id, ...rest) {
+ // result >= 1
+ const result = getAllByTestId(container, id, ...rest)
+ if (result.length > 1) {
+ throw queryHelpers.getElementError(
+ `Found multiple elements with the [data-test-id="${id}"]`,
+ container,
+ )
+ }
+ return result[0]
+}
+
+// re-export with overrides
+module.exports = {
+ ...domTestingLib,
+ getByTestId,
+ getAllByTestId,
+ queryByTestId,
+ queryAllByTestId,
+}
+```
+
+## `buildQueries`
+
+The `buildQueries` helper allows you to create custom queries with all the
+[standard queries](queries/about.mdx) in testing-library.
+
+See the [Add custom queries](react-testing-library/setup.mdx#add-custom-queries)
+section of the custom render guide for example usage.
+
+### Using other assertion libraries
+
+If you're not using jest, you may be able to find a similar set of custom
+assertions for your library of choice. Here's a list of alternatives to jest-dom
+for other popular assertion libraries:
+
+- [chai-dom](https://github.com/nathanboktae/chai-dom)
+
+If you're aware of some other alternatives, please
+[make a pull request](https://github.com/testing-library/testing-library-docs/pulls)
+and add it here!
+
+## `getNodeText`
+
+```typescript
+getNodeText(node: HTMLElement)
+```
+
+Returns the complete text content of an HTML element, removing any extra
+whitespace. The intention is to treat text in nodes exactly as how it is
+perceived by users in a browser, where any extra whitespace within words in the
+html code is not meaningful when the text is rendered.
+
+```javascript
+//
+// Hello
+// World !
+//
+const text = getNodeText(container.querySelector('div')) // "Hello World !"
+```
+
+This function is also used internally when querying nodes by their text content.
+This enables functions like `getByText` and `queryByText` to work as expected,
+finding elements in the DOM similarly to how users would do.
+
+The function has a special behavior for some input elements:
+
+```javascript
+//
+//
+const submitText = getNodeText(container.querySelector('input[type=submit]')) // "Send data"
+const buttonText = getNodeText(container.querySelector('input[type=button]')) // "Push me"
+
+These elements use the attribute `value` and display its value to the user.
+```
diff --git a/docs/dom-testing-library/api-debugging.mdx b/docs/dom-testing-library/api-debugging.mdx
new file mode 100644
index 000000000..492542440
--- /dev/null
+++ b/docs/dom-testing-library/api-debugging.mdx
@@ -0,0 +1,183 @@
+---
+id: api-debugging
+title: Debugging
+---
+
+## Automatic Logging
+
+When any `get` or `find` calls you use in your test cases fail, the current
+state of the `container` (DOM) gets printed on the console. For example:
+
+```javascript
+// Hello world
+getByText(container, 'Goodbye world') // will fail by throwing error
+```
+
+The above test case will fail, however it prints the state of your DOM under
+test, so you will get to see:
+
+```
+Unable to find an element with the text: Goodbye world. 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.
+Here is the state of your container:
+
+```
+
+**Note**: Since the DOM size can get really large, you can set the limit of DOM
+content to be printed via environment variable `DEBUG_PRINT_LIMIT`. The default
+value is `7000`. You will see `...` in the console, when the DOM content is
+stripped off, because of the length you have set or due to default size limit.
+Here's how you might increase this limit when running tests:
+
+```bash npm2yarn
+DEBUG_PRINT_LIMIT=10000 npm test
+```
+
+This works on macOS/Linux, you'll need to do something else for Windows. If
+you'd like a solution that works for both, see
+[`cross-env`](https://www.npmjs.com/package/cross-env).
+
+**Note**: The output of the DOM is colorized by default if your tests are
+running in a node environment. However, you may sometimes want to turn off
+colors, such as in cases where the output is written to a log file for debugging
+purposes. You can use the environment variable `COLORS` to explicitly force the
+colorization off or on. For example:
+
+```bash npm2yarn
+COLORS=false npm test
+```
+
+This works on macOS/Linux, you'll need to do something else for Windows. If
+you'd like a solution that works for both, see
+[`cross-env`](https://www.npmjs.com/package/cross-env).
+
+## `prettyDOM`
+
+Built on top of
+[`pretty-format`](https://github.com/jestjs/jest/tree/main/packages/pretty-format),
+this helper function can be used to print out readable representation of the DOM
+tree of a node. This can be helpful for instance when debugging tests.
+
+It is defined as:
+
+```typescript
+interface Options extends prettyFormat.OptionsReceived {
+ filterNode?: (node: Node) => boolean
+}
+
+function prettyDOM(
+ node: HTMLElement,
+ maxLength?: number,
+ options?: Options,
+): string
+```
+
+It receives the root node to print out, an optional extra parameter to limit the
+size of the resulting string, for cases when it becomes too large. It has a last
+parameter which allows you to configure your formatting. In addition to the
+options listed you can also pass the
+[options](https://github.com/jestjs/jest/tree/main/packages/pretty-format#usage-with-options)
+of `pretty-format`.
+
+By default, ``, `` and comment nodes are ignored. You can
+configure this behavior by passing a custom `filterNode` function that should
+return `true` for every node that you wish to include in the output.
+
+This function is usually used alongside `console.log` to temporarily print out
+DOM trees during tests for debugging purposes:
+
+```javascript
+import {prettyDOM} from '@testing-library/dom'
+
+const div = document.createElement('div')
+div.innerHTML = '
Hello World '
+console.log(prettyDOM(div))
+//
+//
Hello World
+//
+```
+
+This function is what also powers
+[the automatic debugging output described above](#automatic-logging).
+
+## `screen.debug()`
+
+For convenience `screen` also exposes a `debug` method. This method is
+essentially a shortcut for `console.log(prettyDOM())`. It supports debugging the
+document, a single element, or an array of elements.
+
+```javascript
+import {screen} from '@testing-library/dom'
+
+document.body.innerHTML = `
+ test
+ multi-test
+ multi-test
+`
+
+// debug document
+screen.debug()
+// debug single element
+screen.debug(screen.getByText('test'))
+// debug multiple elements
+screen.debug(screen.getAllByText('multi-test'))
+```
+
+## `screen.logTestingPlaygroundURL()`
+
+For debugging using [testing-playground](https://testing-playground.com), screen
+exposes this convenient method which logs and returns a URL that can be opened
+in a browser.
+
+```javascript
+import {screen} from '@testing-library/dom'
+
+document.body.innerHTML = `
+ test
+ multi-test
+ multi-test
+`
+
+// log entire document to testing-playground
+screen.logTestingPlaygroundURL()
+// log a single element
+screen.logTestingPlaygroundURL(screen.getByText('test'))
+```
+
+## `logRoles`
+
+This helper function can be used to print out a list of all the implicit ARIA
+roles within a tree of DOM nodes, each role containing a list of all of the
+nodes which match that role. This can be helpful for finding ways to query the
+DOM under test with [getByRole](queries/byrole.mdx).
+
+```javascript
+import {logRoles} from '@testing-library/dom'
+
+const nav = document.createElement('nav')
+nav.innerHTML = `
+`
+
+logRoles(nav)
+```
+
+Result:
+
+```
+navigation:
+
+--------------------------------------------------
+list:
+
+--------------------------------------------------
+listitem:
+
+
+--------------------------------------------------
+```
diff --git a/docs/dom-testing-library/api-events.md b/docs/dom-testing-library/api-events.md
deleted file mode 100644
index 736c94bba..000000000
--- a/docs/dom-testing-library/api-events.md
+++ /dev/null
@@ -1,95 +0,0 @@
----
-id: api-events
-title: Firing Events
----
-
-## `fireEvent`
-
-```typescript
-fireEvent(node: HTMLElement, event: Event)
-```
-
-Fire DOM events.
-
-```javascript
-// Submit
-fireEvent(
- getByText(container, 'Submit'),
- new MouseEvent('click', {
- bubbles: true,
- cancelable: true,
- })
-)
-```
-
-## `fireEvent[eventName]`
-
-```typescript
-fireEvent[eventName](node: HTMLElement, eventProperties: Object)
-```
-
-Convenience methods for firing DOM events. Check out
-[src/events.js](https://github.com/testing-library/dom-testing-library/blob/master/src/events.js)
-for a full list as well as default `eventProperties`.
-
-```javascript
-// Submit
-const rightClick = { button: 2 }
-fireEvent.click(getByText('Submit'), rightClick)
-// default `button` property for click events is set to `0` which is a left click.
-```
-
-**target**: When an event is dispatched on an element, the event has the
-subjected element on a property called `target`. As a convenience, if you
-provide a `target` property in the `eventProperties` (second argument), then
-those properties will be assigned to the node which is receiving the event.
-
-This is particularly useful for a change event:
-
-```javascript
-fireEvent.change(getByLabelText(/username/i), { target: { value: 'a' } })
-
-// note: attempting to manually set the files property of an HTMLInputElement
-// results in an error as the files property is read-only.
-// this feature works around that by using Object.defineProperty.
-fireEvent.change(getByLabelText(/picture/i), {
- target: {
- files: [new File(['(⌐□_□)'], 'chucknorris.png', { type: 'image/png' })],
- },
-})
-```
-
-**Keyboard events**: There are three event types related to keyboard input -
-`keyPress`, `keyDown`, and `keyUp`. When firing these you need to reference an
-element in the DOM and the key you want to fire.
-
-```javascript
-fireEvent.keyDown(domNode, { key: 'Enter', code: 13 })
-
-// note: you should set the charCode or it will be fallback to 0
-// will Fire an KeyboardEvent with charCode = 0
-fireEvent.keyDown(domNode, { key: 'Enter', code: 13 })
-
-// will Fire an KeyboardEvent with charCode = 65
-fireEvent.keyDown(domNode, { key: 'A', code: 65, charCode: 65 })
-```
-
-You can find out which key code to use at
-[https://keycode.info/](https://keycode.info/).
-
-## `createEvent[eventName]`
-
-```typescript
-createEvent[eventName](node: HTMLElement, eventProperties: Object)
-```
-
-Convenience methods for creating DOM events that can then be fired by
-`fireEvent`, allowing you to have a reference to the event created: this might
-be useful if you need to access event properties that cannot be initiated
-programmatically (such as `timeStamp`).
-
-```javascript
-const myEvent = createEvent.click(node, { button: 2 })
-fireEvent(node, myEvent)
-// myEvent.timeStamp can be accessed just like any other properties from myEvent
-```
diff --git a/docs/dom-testing-library/api-events.mdx b/docs/dom-testing-library/api-events.mdx
new file mode 100644
index 000000000..614ff1689
--- /dev/null
+++ b/docs/dom-testing-library/api-events.mdx
@@ -0,0 +1,191 @@
+---
+id: api-events
+title: Firing Events
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+> **Note**
+>
+> Most projects have a few use cases for `fireEvent`, but the majority of the
+> time you should probably use
+> [`@testing-library/user-event`](user-event/intro.mdx).
+
+## `fireEvent`
+
+```typescript
+fireEvent(node: HTMLElement, event: Event)
+```
+
+Fire DOM events.
+
+```javascript
+// Submit
+fireEvent(
+ getByText(container, 'Submit'),
+ new MouseEvent('click', {
+ bubbles: true,
+ cancelable: true,
+ }),
+)
+```
+
+## `fireEvent[eventName]`
+
+```typescript
+fireEvent[eventName](node: HTMLElement, eventProperties: Object)
+```
+
+Convenience methods for firing DOM events. Check out
+[src/event-map.js](https://github.com/testing-library/dom-testing-library/blob/master/src/event-map.js)
+for a full list as well as default `eventProperties`.
+
+**target**: When an event is dispatched on an element, the event has the
+subjected element on a property called `target`. As a convenience, if you
+provide a `target` property in the `eventProperties` (second argument), then
+those properties will be assigned to the node which is receiving the event.
+
+This is particularly useful for a change event:
+
+```javascript
+fireEvent.change(getByLabelText(/username/i), {target: {value: 'a'}})
+
+// note: attempting to manually set the files property of an HTMLInputElement
+// results in an error as the files property is read-only.
+// this feature works around that by using Object.defineProperty.
+fireEvent.change(getByLabelText(/picture/i), {
+ target: {
+ files: [new File(['(⌐□_□)'], 'chucknorris.png', {type: 'image/png'})],
+ },
+})
+
+// Note: The 'value' attribute must use ISO 8601 format when firing a
+// change event on an input of type "date". Otherwise the element will not
+// reflect the changed value.
+
+// Invalid:
+fireEvent.change(input, {target: {value: '24/05/2020'}})
+
+// Valid:
+fireEvent.change(input, {target: {value: '2020-05-24'}})
+```
+
+**dataTransfer**: Drag events have a `dataTransfer` property that contains data
+transferred during the operation. As a convenience, if you provide a
+`dataTransfer` property in the `eventProperties` (second argument), then those
+properties will be added to the event.
+
+This should predominantly be used for testing drag and drop interactions.
+
+```javascript
+fireEvent.drop(getByLabelText(/drop files here/i), {
+ dataTransfer: {
+ files: [new File(['(⌐□_□)'], 'chucknorris.png', {type: 'image/png'})],
+ },
+})
+```
+
+**Keyboard events**: There are three event types related to keyboard input -
+`keyPress`, `keyDown`, and `keyUp`. When firing these you need to reference an
+element in the DOM and the key you want to fire.
+
+```javascript
+fireEvent.keyDown(domNode, {key: 'Enter', code: 'Enter', charCode: 13})
+
+fireEvent.keyDown(domNode, {key: 'A', code: 'KeyA'})
+```
+
+You can find out which key code to use at
+[https://www.toptal.com/developers/keycode](https://www.toptal.com/developers/keycode).
+
+## `createEvent[eventName]`
+
+```typescript
+createEvent[eventName](node: HTMLElement, eventProperties: Object)
+```
+
+Convenience methods for creating DOM events that can then be fired by
+`fireEvent`, allowing you to have a reference to the event created: this might
+be useful if you need to access event properties that cannot be initiated
+programmatically (such as `timeStamp`).
+
+```javascript
+const myEvent = createEvent.click(node, {button: 2})
+fireEvent(node, myEvent)
+// myEvent.timeStamp can be accessed just like any other properties from myEvent
+// note: The access to the events created by `createEvent` is based on the native event API,
+// Therefore, native properties of HTMLEvent object (e.g. `timeStamp`, `cancelable`, `type`) should be set using Object.defineProperty
+// For more info see: https://developer.mozilla.org/en-US/docs/Web/API/Event
+```
+
+You can also create generic events:
+
+```javascript
+// simulate the 'input' event on a file input
+fireEvent(
+ input,
+ createEvent('input', input, {
+ target: {files: inputFiles},
+ ...init,
+ }),
+)
+```
+
+## Using Jest Function Mocks
+
+[Jest's Mock functions](https://jestjs.io/docs/en/mock-functions) can be used to
+test that a component will call its bound callback in response to a particular
+event.
+
+
+
+
+```jsx
+import {render, screen, fireEvent} from '@testing-library/react'
+
+const Button = ({onClick, children}) => (
+ {children}
+)
+
+test('calls onClick prop when clicked', () => {
+ const handleClick = jest.fn()
+ render(Click Me )
+ fireEvent.click(screen.getByText(/click me/i))
+ expect(handleClick).toHaveBeenCalledTimes(1)
+})
+```
+
+
+
+
+```ts
+import {render, screen, fireEvent} from '@testing-library/angular'
+
+@Component({
+ template: `Click Me `,
+})
+class ButtonComponent {
+ @Output() handleClick = new EventEmitter()
+}
+
+test('calls onClick prop when clicked', async () => {
+ const handleClick = jest.fn()
+ await render(ButtonComponent, {
+ componentOutputs: {
+ handleClick: {emit: handleClick} as any,
+ },
+ })
+ await fireEvent.click(screen.getByText(/click me/i))
+ expect(handleClick).toHaveBeenCalledTimes(1)
+})
+```
+
+
+
diff --git a/docs/dom-testing-library/api-helpers.md b/docs/dom-testing-library/api-helpers.md
deleted file mode 100644
index 596d6494a..000000000
--- a/docs/dom-testing-library/api-helpers.md
+++ /dev/null
@@ -1,209 +0,0 @@
----
-id: api-helpers
-title: Helpers
----
-
-## Custom Queries
-
-`DOM Testing Library` exposes many of the helper functions that are used to
-implement the default queries. You can use the helpers to build custom queries.
-For example, the code below shows a way to override the default `testId` queries
-to use a different data-attribute. (Note: test files would import
-`test-utils.js` instead of using `DOM Testing Library` directly).
-
-```js
-// test-utils.js
-const domTestingLib = require('@testing-library/dom')
-const { queryHelpers } = domTestingLib
-
-export const queryByTestId = queryHelpers.queryByAttribute.bind(
- null,
- 'data-test-id'
-)
-export const queryAllByTestId = queryHelpers.queryAllByAttribute.bind(
- null,
- 'data-test-id'
-)
-
-export function getAllByTestId(container, id, ...rest) {
- const els = queryAllByTestId(container, id, ...rest)
- if (!els.length) {
- throw queryHelpers.getElementError(
- `Unable to find an element by: [data-test-id="${id}"]`,
- container
- )
- }
- return els
-}
-
-export function getByTestId(...args) {
- return queryHelpers.firstResultOrNull(getAllByTestId, ...args)
-}
-
-// re-export with overrides
-module.exports = {
- ...domTestingLib,
- getByTestId,
- getAllByTestId,
- queryByTestId,
- queryAllByTestId,
-}
-```
-
-> **Note**
->
-> Custom queries can be added to `React Testing Library`'s `render` method by
-> adding `queries` to the options config object. See the render
-> [options](/docs/react-testing-library/api#render-options).
-
-### Using other assertion libraries
-
-If you're not using jest, you may be able to find a similar set of custom
-assertions for your library of choice. Here's a list of alternatives to jest-dom
-for other popular assertion libraries:
-
-- [chai-dom](https://github.com/nathanboktae/chai-dom)
-
-If you're aware of some other alternatives, please [make a pull request][prs]
-and add it here!
-
-## `getNodeText`
-
-```typescript
-getNodeText(node: HTMLElement)
-```
-
-Returns the complete text content of a html element, removing any extra
-whitespace. The intention is to treat text in nodes exactly as how it is
-perceived by users in a browser, where any extra whitespace within words in the
-html code is not meaningful when the text is rendered.
-
-```javascript
-//
-// Hello
-// World !
-//
-const text = getNodeText(container.querySelector('div')) // "Hello World !"
-```
-
-This function is also used internally when querying nodes by their text content.
-This enables functions like `getByText` and `queryByText` to work as expected,
-finding elements in the DOM similarly to how users would do.
-
-The function has a special behavior for some input elements:
-
-```javascript
-//
-//
-const submitText = getNodeText(container.querySelector('input[type=submit]')) // "Send data"
-const buttonText = getNodeText(container.querySelector('input[type=button]')) // "Push me"
-
-These elements use the attribute `value` and display its value to the user.
-```
-
-## `within` and `getQueriesForElement` APIs
-
-`within` (an alias to `getQueriesForElement`) takes a DOM element and binds it
-to the raw query functions, allowing them to be used without specifying a
-container. It is the recommended approach for libraries built on this API and is
-in use in `React Testing Library` and `Vue Testing Library`.
-
-Example: To get the text 'hello' only within a section called 'messages', you
-could do:
-
-
-
-
-
-```js
-import { within } from '@testing-library/dom'
-
-const { getByText } = within(document.getElementById('messages'))
-const helloMessage = getByText('hello')
-```
-
-
-
-```js
-import { render, within } from '@testing-library/react'
-
-const { getByLabelText } = render( )
-const signinModal = getByLabelText('Sign In')
-within(signinModal).getByPlaceholderText('Username')
-```
-
-
-
-```js
-cy.get('form').within(() => {
- cy.getByText('Button Text').should('be.disabled')
-})
-```
-
-
-
-## Debugging
-
-When you use any `get` calls in your test cases, the current state of the
-`container` (DOM) gets printed on the console. For example:
-
-```javascript
-// Hello world
-getByText(container, 'Goodbye world') // will fail by throwing error
-```
-
-The above test case will fail, however it prints the state of your DOM under
-test, so you will get to see:
-
-```
-Unable to find an element with the text: Goodbye world. 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.
-Here is the state of your container:
-
-```
-
-Note: Since the DOM size can get really large, you can set the limit of DOM
-content to be printed via environment variable `DEBUG_PRINT_LIMIT`. The default
-value is `7000`. You will see `...` in the console, when the DOM content is
-stripped off, because of the length you have set or due to default size limit.
-Here's how you might increase this limit when running tests:
-
-```
-DEBUG_PRINT_LIMIT=10000 npm test
-```
-
-This works on macOS/linux, you'll need to do something else for windows. If
-you'd like a solution that works for both, see
-[`cross-env`](https://www.npmjs.com/package/cross-env)
-
-### `prettyDOM`
-
-This helper function can be used to print out readable representation of the DOM
-tree of a node. This can be helpful for instance when debugging tests.
-
-It is defined as:
-
-```typescript
-function prettyDOM(node: HTMLElement, maxLength?: number): string
-```
-
-It receives the root node to print out, and an optional extra argument to limit
-the size of the resulting string, for cases when it becomes too large.
-
-This function is usually used alongside `console.log` to temporarily print out
-DOM trees during tests for debugging purposes:
-
-```javascript
-const div = document.createElement('div')
-div.innerHTML = '
Hello World '
-console.log(prettyDOM(div))
-//
-//
Hello World
-//
-```
-
-This function is what also powers
-[the automatic debugging output described above](#debugging).
diff --git a/docs/dom-testing-library/api-queries.md b/docs/dom-testing-library/api-queries.md
deleted file mode 100644
index 6124a29e8..000000000
--- a/docs/dom-testing-library/api-queries.md
+++ /dev/null
@@ -1,744 +0,0 @@
----
-id: api-queries
-title: Queries
----
-
-## Variants
-
-> `getBy` queries are shown by default in the [query documentation](#queries)
-> below.
-
-### getBy
-
-`getBy*` queries returns the first matching node for a query, and throws an
-error if no elements match or if more than one match is found (use `getAllBy`
-instead).
-
-### getAllBy
-
-`getAllBy*` queries return an array of all matching nodes for a query, and
-throws an error if no elements match.
-
-### queryBy
-
-`queryBy*` queries returns the first matching node for a query, and return
-`null` if no elements match. This is useful for asserting an element is not
-present. This throws if more than one match is found (use `queryAllBy` instead).
-
-### queryAllBy
-
-`queryAllBy*` queries return an array of all matching nodes for a query, and
-return an empty array (`[]`) if no elements match.
-
-### findBy
-
-`findBy*` queries return a promise which resolves when an element is found which
-matches the given query. The promise is rejected if no element is found or if
-more than one element is found after a default timeout of `4500`ms. If you need
-to find more than one element, then use `findAllBy`.
-
-> Note, this is a simple combination of `getBy*` queries and
-> [`waitForElement`](/docs/api-async#waitforelement). The `findBy*` queries
-> accept the `waitForElement` options as the last argument. (i.e.
-> `findByText(container, 'text', queryOptions, waitForElementOptions)`)
-
-### findAllBy
-
-`findAllBy*` queries return a promise which resolves to an array of elements
-when any elements are found which match the given query. The promise is rejected
-if no elements are found after a default timeout of `4500`ms.
-
-## Options
-
-The argument to a query can be a _string_, _regular expression_, or _function_.
-There are also options to adjust how node text is parsed.
-
-See [TextMatch](#textmatch) for documentation on what can be passed to a query.
-
-## Queries
-
-### `ByLabelText`
-
-> getByLabelText, queryByLabelText, getAllByLabelText, queryAllByLabelText
-> findByLabelText, findAllByLabelText
-
-```typescript
-getByLabelText(
- container: HTMLElement,
- text: TextMatch,
- options?: {
- selector?: string = '*',
- exact?: boolean = true,
- normalizer?: NormalizerFn,
- }): HTMLElement
-```
-
-This will search for the label that matches the given [`TextMatch`](#textmatch),
-then find the element associated with that label.
-
-The example below will find the input node for the following DOM structures:
-
-```js
-// for/htmlFor relationship between label and form element id
-Username
-
-
-// The aria-labelledby attribute with form elements
-Username
-
-
-// The aria-labelledby attribute with non-form elements
-
-
-// Wrapper labels
-Username
-
-// aria-label attributes
-// Take care because this is not a label that users can see on the page,
-// so the purpose of your input must be obvious to visual users.
-
-```
-
-
-
-
-
-```javascript
-import { getByLabelText } from '@testing-library/dom'
-
-const container = document.body
-const inputNode = getByLabelText(container, 'Username')
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByLabelText } = render( )
-
-const inputNode = getByLabelText('username')
-```
-
-
-
-```js
-cy.getByLabelText('username').should('exist')
-```
-
-
-
-It will NOT find the input node for label text broken up by elements. For this
-case, you can provide a `selector` in the options:
-
-```html
- Username
-```
-
-```js
-const container = document.body
-const inputNode = getByLabelText(container, 'username', {
- selector: 'input',
-})
-```
-
-### `ByPlaceholderText`
-
-> getByPlaceholderText, queryByPlaceholderText, getAllByPlaceholderText,
-> queryAllByPlaceholderText, findByPlaceholderText, findAllByPlaceholderText
-
-```typescript
-getByPlaceholderText(
- container: HTMLElement,
- text: TextMatch,
- options?: {
- exact?: boolean = true,
- normalizer?: NormalizerFn,
- }): HTMLElement
-```
-
-This will search for all elements with a placeholder attribute and find one that
-matches the given [`TextMatch`](#textmatch).
-
-```html
-
-```
-
-
-
-
-
-```js
-import { getByPlaceholderText } from '@testing-library/dom'
-
-const container = document.body
-const inputNode = getByPlaceholderText(container, 'Username')
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByPlaceholderText } = render( )
-const inputNode = getByPlaceholderText('Username')
-```
-
-
-
-```js
-cy.getByPlaceholderText('Username').should('exist')
-```
-
-
-
-> **Note**
->
-> A placeholder is not a good substitute for a label so you should generally use
-> `getByLabelText` instead.
-
-### `ByText`
-
-> getByText, queryByText, getAllByText, queryAllByText, findByText,
-> findAllByText
-
-```typescript
-getByText(
- container: HTMLElement,
- text: TextMatch,
- options?: {
- selector?: string = '*',
- exact?: boolean = true,
- ignore?: string|boolean = 'script, style',
- normalizer?: NormalizerFn,
- }): HTMLElement
-```
-
-This will search for all elements that have a text node with `textContent`
-matching the given [`TextMatch`](#textmatch).
-
-```html
-About ℹ️
-```
-
-
-
-
-
-```js
-import { getByText } from '@testing-library/dom'
-
-const container = document.body
-const aboutAnchorNode = getByText(container, /about/i)
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByText } = render( )
-const aboutAnchorNode = getByText(/about/i)
-```
-
-
-
-```js
-cy.getByText(/about/i).should('exist')
-```
-
-
-
-It also works with `input`s whose `type` attribute is either `submit` or
-`button`:
-
-```js
-
-```
-
-> **Note**
->
-> See [`getByLabelText`](#bylabeltext) for more details on how and when to use
-> the `selector` option
-
-The `ignore` option accepts a query selector. If the
-[`node.matches`](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches)
-returns true for that selector, the node will be ignored. This defaults to
-`'script'` because generally you don't want to select script tags, but if your
-content is in an inline script file, then the script tag could be returned.
-
-If you'd rather disable this behavior, set `ignore` to `false`.
-
-### `ByAltText`
-
-> getByAltText, queryByAltText, getAllByAltText, queryAllByAltText,
-> findByAltText, findAllByAltText
-
-```typescript
-getByAltText(
- container: HTMLElement,
- text: TextMatch,
- options?: {
- exact?: boolean = true,
- normalizer?: NormalizerFn,
- }): HTMLElement
-```
-
-This will return the element (normally an ` `) that has the given `alt`
-text. Note that it only supports elements which accept an `alt` attribute:
-[` `](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img),
-[` `](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input),
-and [` `](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
-(intentionally excluding
-[``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/applet)
-as it's deprecated).
-
-```html
-
-```
-
-
-
-
-
-```js
-import { getByAltText } from '@testing-library/dom'
-
-const container = document.body
-const incrediblesPosterImg = getByAltText(container, /incredibles.*? poster/i)
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByAltText } = render( )
-const incrediblesPosterImg = getByAltText(/incredibles.*? poster/i)
-```
-
-
-
-```js
-cy.getByAltText(/incredibles.*? poster/i).should('exist')
-```
-
-
-
-### `ByTitle`
-
-> getByTitle, queryByTitle, getAllByTitle, queryAllByTitle, findByTitle,
-> findAllByTitle
-
-```typescript
-getByTitle(
- container: HTMLElement,
- title: TextMatch,
- options?: {
- exact?: boolean = true,
- normalizer?: NormalizerFn,
- }): HTMLElement
-```
-
-Returns the element that has the matching `title` attribute.
-
-Will also find a `title` element within an SVG.
-
-```html
-
-
- Close
-
-
-```
-
-
-
-
-
-```js
-import { getByTitle } from '@testing-library/dom'
-
-const container = document.body
-const deleteElement = getByTitle(container, 'Delete')
-const closeElement = getByTitle(container, 'Close')
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByTitle } = render( )
-const deleteElement = getByTitle('Delete')
-const closeElement = getByTitle('Close')
-```
-
-
-
-```js
-cy.getByTitle('Delete').should('exist')
-cy.getByTitle('Close').should('exist')
-```
-
-
-
-### `ByDisplayValue`
-
-> getByDisplayValue, queryByDisplayValue, getAllByDisplayValue,
-> queryAllByDisplayValue, findByDisplayValue, findAllByDisplayValue
-
-```typescript
-getByDisplayValue(
- container: HTMLElement,
- value: TextMatch,
- options?: {
- exact?: boolean = true,
- normalizer?: NormalizerFn,
- }): HTMLElement
-```
-
-Returns the `input`, `textarea`, or `select` element that has the matching
-display value.
-
-#### `input`
-
-```html
-
-```
-
-```js
-document.getElementById('lastName').value = 'Norris'
-```
-
-
-
-
-
-```js
-import { getByDisplayValue } from '@testing-library/dom'
-
-const container = document.body
-const lastNameInput = getByDisplayValue(container, 'Norris')
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByDisplayValue } = render( )
-const lastNameInput = getByDisplayValue('Norris')
-```
-
-
-
-```js
-cy.getByDisplayValue('Norris').should('exist')
-```
-
-
-
-#### `textarea`
-
-```html
-
-```
-
-```js
-document.getElementById('messageTextArea').value = 'Hello World'
-```
-
-
-
-
-
-```js
-import { getByDisplayValue } from '@testing-library/dom'
-
-const container = document.body
-const messageTextArea = getByDisplayValue(container, 'Hello World')
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByDisplayValue } = render( )
-const messageTextArea = getByDisplayValue('Hello World')
-```
-
-
-
-```js
-cy.getByDisplayValue('Hello World').should('exist')
-```
-
-
-
-#### `select`
-
-In case of `select`, this will search for a `` whose selected ``
-matches the given [`TextMatch`](#textmatch).
-
-```html
-
- State
- Alabama
- Alaska
- Arizona
-
-```
-
-
-
-
-
-```js
-import { getByDisplayValue } from '@testing-library/dom'
-
-const container = document.body
-const selectElement = getByDisplayValue(container, 'Alaska')
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByDisplayValue } = render( )
-const selectElement = getByDisplayValue('Alaska')
-```
-
-
-
-```js
-cy.getByDisplayValue('Alaska').should('exist')
-```
-
-
-
-### `ByRole`
-
-> getByRole, queryByRole, getAllByRole, queryAllByRole, findByRole,
-> findAllByRole
-
-```typescript
-getByRole(
- container: HTMLElement,
- text: TextMatch,
- options?: {
- exact?: boolean = true,
- normalizer?: NormalizerFn,
- }): HTMLElement
-```
-
-A shortcut to `` container.querySelector(`[role="${yourRole}"]`) `` (and it also
-accepts a [`TextMatch`](#textmatch)).
-
-```html
-...
-```
-
-
-
-
-
-```js
-import { getByRole } from '@testing-library/dom'
-
-const container = document.body
-const dialogContainer = getByRole(container, 'dialog')
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByRole } = render( )
-const dialogContainer = getByRole('dialog')
-```
-
-
-
-```js
-cy.getByRole('dialog').should('exist')
-```
-
-
-
-### `ByTestId`
-
-> getByTestId, queryByTestId, getAllByTestId, queryAllByTestId, findByTestId,
-> findAllByTestId
-
-```typescript
-getByTestId(
- container: HTMLElement,
- text: TextMatch,
- options?: {
- exact?: boolean = true,
- normalizer?: NormalizerFn,
- }): HTMLElement
-```
-
-A shortcut to `` container.querySelector(`[data-testid="${yourId}"]`) `` (and it
-also accepts a [`TextMatch`](#textmatch)).
-
-```html
-
-```
-
-
-
-
-
-```js
-import { getByTestId } from '@testing-library/dom'
-
-const container = document.body
-const usernameInput = getByTestId(container, 'username-input')
-```
-
-
-
-```js
-import { render } from '@testing-library/react'
-
-const { getByTestId } = render( )
-const usernameInput = getByTestId('username-input')
-```
-
-
-
-```js
-cy.getByTestId('username-input').should('exist')
-```
-
-
-
-> In the spirit of [the guiding principles](#guiding-principles), it is
-> recommended to use this only after the other queries don't work for your use
-> case. Using data-testid attributes do not resemble how your software is used
-> and should be avoided if possible. That said, they are _way_ better than
-> querying based on DOM structure or styling css class names. Learn more about
-> `data-testid`s from the blog post
-> ["Making your UI tests resilient to change"](https://blog.kentcdodds.com/making-your-ui-tests-resilient-to-change-d37a6ee37269)
-
-#### Overriding `data-testid`
-
-The `...ByTestId` functions in `DOM Testing Library` use the attribute
-`data-testid` by default, following the precedent set by
-[React Native Web](https://github.com/testing-library/react-testing-library/issues/1)
-which uses a `testID` prop to emit a `data-testid` attribute on the element, and
-we recommend you adopt that attribute where possible. But if you already have an
-existing codebase that uses a different attribute for this purpose, you can
-override this value via
-`configure({testIdAttribute: 'data-my-test-attribute'})`.
-
-## `TextMatch`
-
-Several APIs accept a `TextMatch` which can be a `string`, `regex` or a
-`function` which returns `true` for a match and `false` for a mismatch.
-
-### Precision
-
-Some APIs accept an object as the final argument that can contain options that
-affect the precision of string matching:
-
-- `exact`: Defaults to `true`; matches full strings, case-sensitive. When false,
- matches substrings and is not case-sensitive.
- - `exact` has no effect on `regex` or `function` arguments.
- - In most cases using a regex instead of a string gives you more control over
- fuzzy matching and should be preferred over `{ exact: false }`.
-- `normalizer`: An optional function which overrides normalization behavior. See
- [`Normalization`](#normalization).
-
-### Normalization
-
-Before running any matching logic against text in the DOM, `DOM Testing Library`
-automatically normalizes that text. By default, normalization consists of
-trimming whitespace from the start and end of text, and collapsing multiple
-adjacent whitespace characters into a single space.
-
-If you want to prevent that normalization, or provide alternative normalization
-(e.g. to remove Unicode control characters), you can provide a `normalizer`
-function in the options object. This function will be given a string and is
-expected to return a normalized version of that string.
-
-Note: Specifying a value for `normalizer` _replaces_ the built-in normalization,
-but you can call `getDefaultNormalizer` to obtain a built-in normalizer, either
-to adjust that normalization or to call it from your own normalizer.
-
-`getDefaultNormalizer` takes an options object which allows the selection of
-behaviour:
-
-- `trim`: Defaults to `true`. Trims leading and trailing whitespace
-- `collapseWhitespace`: Defaults to `true`. Collapses inner whitespace
- (newlines, tabs, repeated spaces) into a single space.
-
-#### Normalization Examples
-
-To perform a match against text without trimming:
-
-```javascript
-getByText(node, 'text', {
- normalizer: getDefaultNormalizer({ trim: false }),
-})
-```
-
-To override normalization to remove some Unicode characters whilst keeping some
-(but not all) of the built-in normalization behavior:
-
-```javascript
-getByText(node, 'text', {
- normalizer: str =>
- getDefaultNormalizer({ trim: false })(str).replace(/[\u200E-\u200F]*/g, ''),
-})
-```
-
-### TextMatch Examples
-
-Given the following HTML:
-
-```html
-Hello World
-```
-
-**_Will_ find the div:**
-
-```javascript
-// Matching a string:
-getByText(container, 'Hello World') // full string match
-getByText(container, 'llo Worl', { exact: false }) // substring match
-getByText(container, 'hello world', { exact: false }) // ignore case
-
-// Matching a regex:
-getByText(container, /World/) // substring match
-getByText(container, /world/i) // substring match, ignore case
-getByText(container, /^hello world$/i) // full string match, ignore case
-getByText(container, /Hello W?oRlD/i) // advanced regex
-
-// Matching with a custom function:
-getByText(container, (content, element) => content.startsWith('Hello'))
-```
-
-**_Will not_ find the div:**
-
-```javascript
-// full string does not match
-getByText(container, 'Goodbye World')
-
-// case-sensitive regex with different case
-getByText(container, /hello world/)
-
-// function looking for a span when it's actually a div:
-getByText(container, (content, element) => {
- return element.tagName.toLowerCase() === 'span' && content.startsWith('Hello')
-})
-```
diff --git a/docs/dom-testing-library/api-within.mdx b/docs/dom-testing-library/api-within.mdx
new file mode 100644
index 000000000..eda32b9a4
--- /dev/null
+++ b/docs/dom-testing-library/api-within.mdx
@@ -0,0 +1,67 @@
+---
+id: api-within
+title: Querying Within Elements
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+`within` (an alias to `getQueriesForElement`) takes a DOM element and binds it
+to the raw query functions, allowing them to be used without specifying a
+container. It is the recommended approach for libraries built on this API and is
+used under the hood in React Testing Library and Vue Testing Library.
+
+Example: To get the text 'hello' only within a section called 'messages', you
+could do:
+
+
+
+
+```js
+import {within} from '@testing-library/dom'
+
+const messages = document.getElementById('messages')
+const helloMessage = within(messages).getByText('hello')
+```
+
+
+
+
+```jsx
+import {render, within} from '@testing-library/react'
+
+const {getByText} = render( )
+const messages = getByText('messages')
+const helloMessage = within(messages).getByText('hello')
+```
+
+
+
+
+```ts
+import {render, within} from '@testing-library/angular'
+
+const {getByText} = await render(MyComponent)
+const messages = getByText('messages')
+const helloMessage = within(messages).getByText('hello')
+```
+
+
+
+
+```js
+cy.findByText('messages').within(() => {
+ cy.findByText('hello')
+})
+```
+
+
+
diff --git a/docs/dom-testing-library/api.mdx b/docs/dom-testing-library/api.mdx
new file mode 100644
index 000000000..bbaeaa6bc
--- /dev/null
+++ b/docs/dom-testing-library/api.mdx
@@ -0,0 +1,16 @@
+---
+id: api
+title: API
+---
+
+DOM Testing Library is the "core API" of the Testing Library family, so please
+refer to the Core API section for details. The other libraries (with a few
+exceptions) re-export the full DOM Testing Library API, with additional methods
+built on top.
+
+
+
+- [Queries](queries/about.mdx)
+- [Events](api-events.mdx)
+- [Advanced](api-accessibility.mdx)
+- [Configuration](api-configuration.mdx)
diff --git a/docs/dom-testing-library/cheatsheet.md b/docs/dom-testing-library/cheatsheet.mdx
similarity index 74%
rename from docs/dom-testing-library/cheatsheet.md
rename to docs/dom-testing-library/cheatsheet.mdx
index b171540be..d5625d104 100644
--- a/docs/dom-testing-library/cheatsheet.md
+++ b/docs/dom-testing-library/cheatsheet.mdx
@@ -7,7 +7,7 @@ A short guide to all the exported functions in `DOM Testing Library`
## Queries
-See [Which query should I use?](guide-which-query.md)
+See [Which query should I use?](queries/about.mdx#priority)
| | No Match | 1 Match | 1+ Match | Await? |
| -------------- | -------- | ------- | -------- | ------ |
@@ -77,32 +77,39 @@ See [Which query should I use?](guide-which-query.md)
## Async
-See [Async API](dom-testing-library/api-async.md)
+See [Async API](dom-testing-library/api-async.mdx). Remember to `await` or
+`.then()` the result of async functions in your tests!
-- **wait** (Promise) retry the function within until it stops throwing or times
- out
-- **waitForElement** (Promise) retry the function until it returns an element or
- an array of elements
- - `findBy` and `findAllBy` queries are async and retry until either a timeout
- or if the query returns successfully; they wrap `waitForElement`
-- **waitForDomChange** (Promise) retry the function each time the DOM is changed
+- **waitFor** (Promise) retry the function within until it stops throwing or
+ times out
- **waitForElementToBeRemoved** (Promise) retry the function until it no longer
returns a DOM node
-> Remember to `await` or `.then()` the result of async functions in your tests!
+> **Deprecated since v7.0.0:**
+>
+> - **wait** (Promise) retry the function within until it stops throwing or
+> times
+> - **waitForElement** (Promise) retry the function until it returns an element
+> or an array of elements. The `findBy` and `findAllBy` queries are async and
+> retry until the query returns successfully, or when the query times out;
+> they wrap `waitForElement`
+> - **waitForDomChange** (Promise) retry the function each time the DOM is
+> changed
## Events
-See [Events API](api-events.md)
+See [Considerations for fireEvent](guide-events.mdx),
+[Events API](api-events.mdx)
- **fireEvent** trigger DOM event: `fireEvent(node, event)`
- **fireEvent.\*** helpers for default event types
- **click** `fireEvent.click(node)`
- - [See all supported events](https://github.com/testing-library/dom-testing-library/blob/master/src/events.js)
+ - [See all supported events](https://github.com/testing-library/dom-testing-library/blob/master/src/event-map.js)
## Other
-See [Helpers API](api-helpers.md), [Config API](api-configuration.md)
+See [Querying Within Elements](api-within.mdx),
+[Config API](api-configuration.mdx)
- **within** take a node and return an object with all the queries bound to the
node (used to return the queries from `React Testing Library`'s render
@@ -123,8 +130,8 @@ Given the following HTML:
```javascript
// Matching a string:
getByText(container, 'Hello World') // full string match
-getByText(container, 'llo Worl', { exact: false }) // substring match
-getByText(container, 'hello world', { exact: false }) // ignore case
+getByText(container, 'llo Worl', {exact: false}) // substring match
+getByText(container, 'hello world', {exact: false}) // ignore case
// Matching a regex:
getByText(container, /World/) // substring match
@@ -144,7 +151,7 @@ test('loads items eventually', async () => {
fireEvent.click(getByText(node, 'Load'))
// Wait for page to update with query text
- const items = await findByText(node, /Item #[0-9]: /)
+ const items = await findAllByText(node, /Item #[0-9]: /)
expect(items).toHaveLength(10)
})
```
diff --git a/docs/dom-testing-library/example-intro.md b/docs/dom-testing-library/example-intro.mdx
similarity index 86%
rename from docs/dom-testing-library/example-intro.md
rename to docs/dom-testing-library/example-intro.mdx
index d60e89f3d..d169dc915 100644
--- a/docs/dom-testing-library/example-intro.md
+++ b/docs/dom-testing-library/example-intro.mdx
@@ -4,8 +4,7 @@ title: Example
sidebar_label: Example
---
-```javascript
-// src/__tests__/example.js
+```js title="src/__tests__/example.js"
// query utilities:
import {
getByLabelText,
@@ -14,10 +13,10 @@ import {
queryByTestId,
// Tip: all queries are also exposed on an object
// called "queries" which you could import here as well
- wait,
+ waitFor,
} from '@testing-library/dom'
// adds special assertions like toHaveTextContent
-import 'jest-dom/extend-expect'
+import '@testing-library/jest-dom'
function getExampleDOM() {
// This is just a raw example of setting up some DOM
@@ -46,25 +45,25 @@ function getExampleDOM() {
}
test('examples of some things', async () => {
- const famousWomanInHistory = 'Ada Lovelace'
+ const famousProgrammerInHistory = 'Ada Lovelace'
const container = getExampleDOM()
// Get form elements by their label text.
// An error will be thrown if one cannot be found (accessibility FTW!)
const input = getByLabelText(container, 'Username')
- input.value = famousWomanInHistory
+ input.value = famousProgrammerInHistory
// Get elements by their text, just like a real user does.
getByText(container, 'Print Username').click()
- await wait(() =>
- expect(queryByTestId(container, 'printed-username')).toBeTruthy()
+ await waitFor(() =>
+ expect(queryByTestId(container, 'printed-username')).toBeTruthy(),
)
// getByTestId and queryByTestId are an escape hatch to get elements
- // by a test id (could also attempt to get this element by it's text)
+ // by a test id (could also attempt to get this element by its text)
expect(getByTestId(container, 'printed-username')).toHaveTextContent(
- famousWomanInHistory
+ famousProgrammerInHistory,
)
// jest snapshots work great with regular DOM nodes!
expect(container).toMatchSnapshot()
diff --git a/docs/dom-testing-library/faq.md b/docs/dom-testing-library/faq.mdx
similarity index 65%
rename from docs/dom-testing-library/faq.md
rename to docs/dom-testing-library/faq.mdx
index ae3c094bb..39e8de991 100644
--- a/docs/dom-testing-library/faq.md
+++ b/docs/dom-testing-library/faq.mdx
@@ -7,7 +7,7 @@ title: FAQ
Which get method should I use?
-See [Which Query Should I Use](guide-which-query.md)
+See [Which Query Should I Use](queries/about.mdx#priority)
@@ -27,7 +27,9 @@ As you write your tests, keep in mind:
-What if my app is localized and I don't have access to the text in test?
+
+ What if my app is localized and I don't have access to the text in test?
+
This is fairly common. Our first bit of advice is to try to get the default text
used in your tests. That will make everything much easier (more than just using
@@ -38,7 +40,10 @@ with `data-testid`s (which is not bad anyway).
-I really don't like data-testids, but none of the other queries make sense. Do I have to use a data-testid?
+
+ I really don't like data-testids, but none of the other queries make sense. Do
+ I have to use a data-testid?
+
Definitely not. That said, a common reason people don't like the `data-testid`
attribute is they're concerned about shipping that to production. I'd suggest
@@ -67,7 +72,10 @@ const rootElement = container.firstChild
-What if I’m iterating over a list of items that I want to put the data-testid="item" attribute on. How do I distinguish them from each other?
+
+ What if I’m iterating over a list of items that I want to put the
+ data-testid="item" attribute on. How do I distinguish them from each other?
+
You can make your selector just choose the one you want by including :nth-child
in the selector.
@@ -76,27 +84,38 @@ in the selector.
const thirdLiInUl = container.querySelector('ul > li:nth-child(3)')
```
-Or you could include the index or an ID in your attribute:
-
-```javascript
-;`{item.text} `
-```
-
-And then you could use the `getByTestId` utility:
+Or you could use `getAllByRole` to query the `listitem` role and access the
+index in question:
```javascript
const items = [
/* your items */
]
-const container = render(/* however you render this stuff */)
-const thirdItem = getByTestId(container, `item-${items[2].id}`)
+const {container} = render(/* however you render this stuff */)
+const thirdItem = getAllByRole(container, 'listitem')[2]
```
-
+
+
+ Help! I can't access component methods or the component instance!
+
+
+This is **intentional**.
+
+We want you to focus on testing the output and functionality of the component as
+it is observed by the user and to **avoid worrying about the implementation
+details** of the component.
+
+We believe this leads to less brittle and more meaningful test code.
+
+Please refer to the [Guiding Principles](guiding-principles.mdx) of this testing
+library for more info.
+
+
+
+
diff --git a/docs/dom-testing-library/install.md b/docs/dom-testing-library/install.md
deleted file mode 100644
index 8f85db926..000000000
--- a/docs/dom-testing-library/install.md
+++ /dev/null
@@ -1,58 +0,0 @@
----
-id: install
-title: Install
-sidebar_label: Install
----
-
-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
-```
-
-## Wrappers
-
-If you are using a framework such as React, you will likely want to install the
-wrapper:
-
-- [React Testing Library](react-testing-library/intro.md)
-- [Cypress Testing Library](cypress-testing-library/intro.md)
-
-## Ecosystem
-
-`DOM Testing Library` works well with these companion libraries:
-
-- [user-event](ecosystem-user-event.md) browser event simulation
-- [jest-dom](ecosystem-jest-dom.md) custom Jest matchers
-
-## Main Exports
-
-You can
-[review the `DOM Testing Library` package.json here](https://unpkg.com/@testing-library/dom/package.json).
-
-In particular, the `main`, `module`, and `umd:main` fields are useful. Each of
-these points to a file that's useful in certain situations. Typically, your
-testing framework will resolve to the correct one for your situation, but if it
-does not, then you can either configure your testing framework to resolve to the
-right file when you require/import `@testing-library/dom` or you can import the
-file you need more explicitly. For example:
-
-```js
-import { within } from '@testing-library/dom/dist/@testing-library/dom.umd.js'
-```
-
-You can
-[review the published `dist` files here](https://unpkg.com/@testing-library/dom/dist/).
-
-The `main` file is configured to compile down to support the version of node
-that is referenced in the `package.json` `engines.node` field. But the `module`
-and `umd:main` files are configured to compile down to support browsers as old
-as IE 10.
-
-
-
-[npm]: https://www.npmjs.com/
-[node]: https://nodejs.org
diff --git a/docs/dom-testing-library/install.mdx b/docs/dom-testing-library/install.mdx
new file mode 100644
index 000000000..903a0325f
--- /dev/null
+++ b/docs/dom-testing-library/install.mdx
@@ -0,0 +1,81 @@
+---
+id: install
+title: Install
+sidebar_label: Install
+---
+
+This module is distributed via [npm][npm] which is bundled with [node][node] and
+should be installed as one of your project's `devDependencies`:
+
+```bash npm2yarn
+npm install --save-dev @testing-library/dom
+```
+
+## Wrappers
+
+If you are using a framework or library such as React, you will likely want to
+install the wrapper:
+
+- [React Testing Library](react-testing-library/intro.mdx)
+- [Reason Testing Library](bs-react-testing-library/intro.mdx)
+- [React Native Testing Library](react-native-testing-library/intro.mdx)
+- [Vue Testing Library](vue-testing-library/intro.mdx)
+- [Marko Testing Library](marko-testing-library/intro.mdx)
+- [Angular Testing Library](angular-testing-library/intro.mdx)
+- [Preact Testing Library](preact-testing-library/intro.mdx)
+- [Svelte Testing Library](svelte-testing-library/intro.mdx)
+- [Cypress Testing Library](cypress-testing-library/intro.mdx)
+- [Puppeteer Testing Library](pptr-testing-library/intro.mdx)
+- [Testcafe Testing Library](testcafe-testing-library/intro.mdx)
+- [Nightwatch Testing Library](nightwatch-testing-library/intro.mdx)
+- [Solid Testing Library](solid-testing-library/intro.mdx)
+
+## Ecosystem
+
+`DOM Testing Library` works well with these companion libraries:
+
+- [user-event](user-event/intro.mdx) browser event simulation
+- [jest-dom](ecosystem-jest-dom.mdx) custom Jest matchers
+- [bs-jest-dom](ecosystem-bs-jest-dom.mdx) companion library for
+ `bs-react-testing-library`
+- [jest-native](ecosystem-jest-native.mdx) companion library for
+ `React Native Testing Library`
+- [react-select-event](ecosystem-react-select-event.mdx) companion library for
+ `React Testing Library`
+- [eslint-plugin-testing-library](ecosystem-eslint-plugin-testing-library.mdx)
+ ESLint plugin for Testing Library
+- [eslint-plugin-jest-dom](ecosystem-eslint-plugin-jest-dom.mdx) ESLint plugin
+ for Jest DOM
+- [riot-testing-library](ecosystem-riot-testing-library.mdx) adds APIs for
+ working with Riot.js components
+
+## Main Exports
+
+You can
+[review the `DOM Testing Library` package.json here](https://unpkg.com/@testing-library/dom/package.json).
+
+In particular, the `main`, `module`, and `umd:main` fields are useful. Each of
+these points to a file that's useful in certain situations. Typically, your
+testing framework will resolve to the correct one for your situation, but if it
+does not, then you can either configure your testing framework to resolve to the
+right file when you require/import `@testing-library/dom` or you can import the
+file you need more explicitly. For example:
+
+```js
+import {within} from '@testing-library/dom/dist/@testing-library/dom.umd.js'
+```
+
+You can
+[review the published `dist` files here](https://unpkg.com/@testing-library/dom/dist/).
+
+The `main` file is configured to compile down to support the version of node
+that is referenced in the `package.json` `engines.node` field. But the `module`
+and `umd:main` files are configured to compile down to support browsers as old
+as IE 10.
+
+
+
+[npm]: https://www.npmjs.com/
+[node]: https://nodejs.org
diff --git a/docs/dom-testing-library/intro.md b/docs/dom-testing-library/intro.mdx
similarity index 83%
rename from docs/dom-testing-library/intro.md
rename to docs/dom-testing-library/intro.mdx
index 1cbd1b66a..7b323265c 100644
--- a/docs/dom-testing-library/intro.md
+++ b/docs/dom-testing-library/intro.mdx
@@ -22,12 +22,12 @@ 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-principles.md)
+> [The more your tests resemble the way your software is used, the more confidence they can give you.](guiding-principles.mdx)
As part of this goal, the utilities this library provides facilitate querying
-the DOM in the same way the user would. Finding for elements by their label text
-(just like a user would), finding links and buttons from their text (like a user
-would), and more. It also exposes a recommended way to find elements by a
+the DOM in the same way the user would. Finding form elements by their label
+text (just like a user would), finding links and buttons from their text (like a
+user would), and more. It also exposes a recommended way to find elements by a
`data-testid` as an "escape hatch" for elements where the text content and label
do not make sense or is not practical.
@@ -41,6 +41,6 @@ when a real user uses it.
1. A test runner or framework
2. Specific to a testing framework (though we recommend Jest as our preference,
the library works with any framework. See
- [Using Without Jest](setup.md#using-without-jest))
+ [Using Without Jest](setup.mdx#using-without-jest))
[jest]: https://jestjs.io
diff --git a/docs/dom-testing-library/setup.md b/docs/dom-testing-library/setup.mdx
similarity index 62%
rename from docs/dom-testing-library/setup.md
rename to docs/dom-testing-library/setup.mdx
index fa4263359..b3be51807 100644
--- a/docs/dom-testing-library/setup.md
+++ b/docs/dom-testing-library/setup.mdx
@@ -10,7 +10,6 @@ people using `DOM Testing Library` are using it with
[the Jest testing framework](https://jestjs.io/) with the `testEnvironment` set
to
[`jest-environment-jsdom`](https://www.npmjs.com/package/jest-environment-jsdom)
-(which is the default configuration with Jest).
## Using Without Jest
@@ -18,21 +17,20 @@ to
the DOM and browser APIs that runs in node. If you're not using Jest and you
would like to run your tests in Node, then you must install jsdom yourself.
There's also a package called
-[jsdom-global](https://github.com/rstacruz/jsdom-global) which can be used to
-setup the global environment to simulate the browser APIs.
+[global-jsdom](https://github.com/modosc/global-jsdom) which can be used to
+setup the global environment to simulate the browser APIs. Note that if you're
+using Vitest you only need to configure
+[`environment`](https://vitest.dev/config/#environment) to `jsdom` to achieve
+the same effect, you don't need global-jsdom.
-First, install jsdom and jsdom-global.
+First, install jsdom and global-jsdom.
-```
-npm install --save-dev jsdom jsdom-global
+```bash npm2yarn
+npm install --save-dev jsdom global-jsdom
```
With mocha, the test command would look something like this:
```
-mocha --require jsdom-global/register
+mocha --require global-jsdom/register
```
-
-> Note, depending on the version of Node you're running, you may also need to
-> install `@babel/polyfill` (if you're using babel 7) or `babel-polyfill` (for
-> babel 6).
diff --git a/docs/ecosystem-bs-jest-dom.md b/docs/ecosystem-bs-jest-dom.mdx
similarity index 90%
rename from docs/ecosystem-bs-jest-dom.md
rename to docs/ecosystem-bs-jest-dom.mdx
index ae30bbd3a..0f75bb463 100644
--- a/docs/ecosystem-bs-jest-dom.md
+++ b/docs/ecosystem-bs-jest-dom.mdx
@@ -4,11 +4,11 @@ title: bs-jest-dom
---
[`bs-jest-dom`][gh] is a companion library for
-[`bs-react-testing-library`](/docs/bs-react-testing-library/intro) that provides
+[`bs-react-testing-library`](bs-react-testing-library/intro.mdx) that provides
custom DOM element matchers for Jest in [ReasonML][re] via
[BuckleScript][bucklescript].
-```
+```bash npm2yarn
npm install --save-dev bs-jest-dom
```
@@ -30,9 +30,7 @@ full list of available matchers.
## Example
-```reason
-/* A_test.re */
-
+```reason title="A_test.re"
open Jest;
open JestDom;
open ReactTestingLibrary;
diff --git a/docs/ecosystem-cli-testing-library.mdx b/docs/ecosystem-cli-testing-library.mdx
new file mode 100644
index 000000000..8b5a4074d
--- /dev/null
+++ b/docs/ecosystem-cli-testing-library.mdx
@@ -0,0 +1,48 @@
+---
+id: ecosystem-cli-testing-library
+title: cli-testing-library
+---
+
+[`CLI Testing Library`](https://github.com/crutchcorn/cli-testing-library) is a
+companion library to `Testing Library` that aims to mimic the API of
+`Testing Library` for testing CLI applications.
+
+```bash npm2yarn
+npm install --save-dev cli-testing-library
+```
+
+```js
+import {resolve} from 'path'
+import {render} from 'cli-testing-library'
+
+test('Is able to make terminal input and view in-progress stdout', async () => {
+ const {clear, findByText, queryByText, userEvent} = await render('node', [
+ resolve(__dirname, './execute-scripts/stdio-inquirer.js'),
+ ])
+
+ const instance = await findByText('First option')
+
+ expect(instance).toBeInTheConsole()
+
+ expect(await findByText('❯ One')).toBeInTheConsole()
+
+ clear()
+
+ userEvent('[ArrowDown]')
+
+ expect(await findByText('❯ Two')).toBeInTheConsole()
+
+ clear()
+
+ userEvent.keyboard('[Enter]')
+
+ expect(await findByText('First option: Two')).toBeInTheConsole()
+ expect(await queryByText('First option: Three')).not.toBeInTheConsole()
+})
+```
+
+Check out
+[CLI Testing Library's documentation](https://github.com/crutchcorn/cli-testing-library)
+for a full list of its API.
+
+- [CLI Testing Library on GitHub](https://github.com/crutchcorn/cli-testing-library)
diff --git a/docs/ecosystem-eslint-plugin-jest-dom.mdx b/docs/ecosystem-eslint-plugin-jest-dom.mdx
new file mode 100644
index 000000000..62953480c
--- /dev/null
+++ b/docs/ecosystem-eslint-plugin-jest-dom.mdx
@@ -0,0 +1,19 @@
+---
+id: ecosystem-eslint-plugin-jest-dom
+title: eslint-plugin-jest-dom
+---
+
+[`eslint-plugin-jest-dom`][gh] is an ESLint plugin for Jest DOM that helps users
+to follow best practices and anticipate common mistakes when writing tests.
+
+This plugin includes:
+
+- Several rules for linting Jest DOM specific code.
+- Shareable config for recommended rules.
+- Some autofixable rules.
+
+Find more info about available rules and configs on GitHub.
+
+- [eslint-plugin-jest-dom on GitHub][gh]
+
+[gh]: https://github.com/testing-library/eslint-plugin-jest-dom
diff --git a/docs/ecosystem-eslint-plugin-testing-library.mdx b/docs/ecosystem-eslint-plugin-testing-library.mdx
new file mode 100644
index 000000000..0b69bd33b
--- /dev/null
+++ b/docs/ecosystem-eslint-plugin-testing-library.mdx
@@ -0,0 +1,23 @@
+---
+id: ecosystem-eslint-plugin-testing-library
+title: eslint-plugin-testing-library
+---
+
+[`eslint-plugin-testing-library`][gh] is an ESLint plugin for Testing Library
+that helps users to follow best practices and anticipate common mistakes when
+writing tests.
+
+
+
+This plugin includes:
+
+- Several rules for linting Testing Library specific code.
+- Shareable config for recommended rules.
+- Shareable configs for specific rules by framework: Angular, React, Vue.
+- Some autofixable rules.
+
+Find more info about available rules and configs on GitHub.
+
+- [eslint-plugin-testing-library on GitHub][gh]
+
+[gh]: https://github.com/testing-library/eslint-plugin-testing-library
diff --git a/docs/ecosystem-jasmine-dom.mdx b/docs/ecosystem-jasmine-dom.mdx
new file mode 100644
index 000000000..853baff10
--- /dev/null
+++ b/docs/ecosystem-jasmine-dom.mdx
@@ -0,0 +1,35 @@
+---
+id: ecosystem-jasmine-dom
+title: jasmine-dom
+---
+
+[`jasmine-dom`][gh] is a companion library for Testing Library that provides
+custom DOM element matchers for Jasmine
+
+```bash npm2yarn
+npm install --save-dev @testing-library/jasmine-dom
+```
+
+Then follow [usage section][gh-usage] from jasmine-dom's documentation to add
+the matchers to Jasmine.
+
+```jsx
+
+Visible Example
+
+expect(screen.queryByTestId('not-empty')).not.toBeEmptyDOMElement()
+expect(screen.getByText('Visible Example')).toBeVisible()
+```
+
+> Note: when using some of these matchers, you may need to make sure you use a
+> query function (like `queryByTestId`) rather than a get function (like
+> `getByTestId`). Otherwise the `get*` function could throw an error before your
+> assertion.
+
+Check out [jasmine-dom's documentation][gh] for a full list of available
+matchers.
+
+- [jasmine-dom on GitHub][gh]
+
+[gh]: https://github.com/testing-library/jasmine-dom
+[gh-usage]: https://github.com/testing-library/jasmine-dom#usage
diff --git a/docs/ecosystem-jest-dom.md b/docs/ecosystem-jest-dom.md
deleted file mode 100644
index 9f5b16367..000000000
--- a/docs/ecosystem-jest-dom.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-id: ecosystem-jest-dom
-title: jest-dom
----
-
-[`jest-dom`][gh] is a companion library for `React Testing Library` that
-provides custom DOM element matchers for Jest
-
-```
-npm install --save-dev jest-dom
-```
-
-```jsx
-
-Visible Example
-
-expect(queryByTestId(container, 'not-empty')).not.toBeEmpty()
-expect(getByText(container, 'Visible Example')).toBeVisible()
-```
-
-> Note: when using some of these matchers, you may need to make sure you use a
-> query function (like `queryByTestId`) rather than a get function (like
-> `getByTestId`). Otherwise the `get*` function could throw an error before your
-> assertion.
-
-Check out [jest-dom's documentation][gh] for a full list of available matchers.
-
-- [jest-dom on GitHub][gh]
-
-[gh]: https://github.com/testing-library/jest-dom
diff --git a/docs/ecosystem-jest-dom.mdx b/docs/ecosystem-jest-dom.mdx
new file mode 100644
index 000000000..33d473fe6
--- /dev/null
+++ b/docs/ecosystem-jest-dom.mdx
@@ -0,0 +1,40 @@
+---
+id: ecosystem-jest-dom
+title: jest-dom
+---
+
+[`jest-dom`][gh] is a companion library for Testing Library that provides custom
+DOM element matchers for Jest
+
+```bash npm2yarn
+npm install --save-dev @testing-library/jest-dom
+```
+
+Then follow [usage section][gh-usage] from jest-dom's documentation to add the
+matchers to Jest.
+
+```jsx
+import {screen} from '@testing-library/dom'
+
+test('uses jest-dom', () => {
+ document.body.innerHTML = `
+
+ Visible Example
+ `
+
+ expect(screen.queryByTestId('not-empty')).not.toBeEmptyDOMElement()
+ expect(screen.getByText('Visible Example')).toBeVisible()
+})
+```
+
+> Note: when using some of these matchers, you may need to make sure you use a
+> query function (like `queryByTestId`) rather than a get function (like
+> `getByTestId`). Otherwise the `get*` function could throw an error before your
+> assertion.
+
+Check out [jest-dom's documentation][gh] for a full list of available matchers.
+
+- [jest-dom on GitHub][gh]
+
+[gh]: https://github.com/testing-library/jest-dom
+[gh-usage]: https://github.com/testing-library/jest-dom#usage
diff --git a/docs/ecosystem-jest-native.md b/docs/ecosystem-jest-native.md
deleted file mode 100644
index a21c8d48c..000000000
--- a/docs/ecosystem-jest-native.md
+++ /dev/null
@@ -1,36 +0,0 @@
----
-id: ecosystem-jest-native
-title: jest-native
-sidebar_label: jest-native
----
-
-[`Jest Native`](https://github.com/testing-library/jest-native) is a companion
-library for `Native Testing Library` that provides custom element matchers for
-Jest.
-
-```
-npm install --save-dev @testing-library/jest-native
-```
-
-```javascript
-;
-
-
-
- Visible Example
-
-
-expect(queryByTestId(baseElement, 'not-empty')).not.toBeEmpty()
-expect(getByText(baseElement, 'Visible Example')).toBeVisible()
-```
-
-> Note: when using some of these matchers, you may need to make sure you use a
-> query function (like `queryByTestId`) rather than a get function (like
-> `getByTestId`). Otherwise the `get*` function could throw an error before your
-> assertion.
-
-Check out
-[Jest Native's documentation](https://github.com/testing-library/jest-native)
-for a full list of available matchers.
-
-- [Jest Native on GitHub](https://github.com/testing-library/jest-native)
diff --git a/docs/ecosystem-jest-native.mdx b/docs/ecosystem-jest-native.mdx
new file mode 100644
index 000000000..b201e8a9f
--- /dev/null
+++ b/docs/ecosystem-jest-native.mdx
@@ -0,0 +1,51 @@
+---
+id: ecosystem-jest-native
+title: jest-native
+sidebar_label: jest-native
+---
+
+:::warning
+
+**This package is deprecated and is no longer actively maintained.**
+
+We encourage you to migrate to React Native Testing Library, v12.4 or later,
+which includes modern
+[built-in Jest matchers](https://callstack.github.io/react-native-testing-library/docs/api/jest-matchers)
+based on the matchers for this repository. The migration process should be
+relatively straightforward, we have a
+[migration guide](https://callstack.github.io/react-native-testing-library/docs/migration/jest-matchers)
+available.
+
+:::
+
+[`Jest Native`](https://github.com/testing-library/jest-native) is a companion
+library for `React Native Testing Library` that provides custom element matchers
+for Jest.
+
+```bash npm2yarn
+npm install --save-dev @testing-library/jest-native
+```
+
+```jsx
+const {queryByTestId} = render(
+
+
+
+
+ Visible Example
+ ,
+)
+
+expect(queryByTestId('not-empty')).not.toBeEmpty()
+```
+
+> Note: when using some of these matchers, you may need to make sure you use a
+> query function (like `queryByTestId`) rather than a get function (like
+> `getByTestId`). Otherwise the `get*` function could throw an error before your
+> assertion.
+
+Check out
+[Jest Native's documentation](https://github.com/testing-library/jest-native)
+for a full list of available matchers.
+
+- [Jest Native on GitHub](https://github.com/testing-library/jest-native)
diff --git a/docs/ecosystem-native-testing-library.md b/docs/ecosystem-native-testing-library.md
deleted file mode 100644
index 6e3a2fdcb..000000000
--- a/docs/ecosystem-native-testing-library.md
+++ /dev/null
@@ -1,76 +0,0 @@
----
-id: ecosystem-native-testing-library
-title: Native Testing Library
-sidebar_label: native-testing-library
----
-
-Native Testing Library is a testing library for **React Native** inspired by
-`DOM Testing Library`. Because React Native does not run in a browser
-environment, the core queries are implemented independently, unlike other
-wrappers that use `DOM Testing Library` as the base. You'll find much more
-information about the library, including examples, on the project website:
-
-- [Docs](https://native-testing-library.com)
-- [Docs repo](https://github.com/bcarroll22/native-testing-library-docs)
-- [Project repo](https://github.com/bcarroll22/native-testing-library)
-
-## Installation
-
-This module should be installed in your project's `devDependencies`:
-
-```
-npm --save-dev @testing-library/react-native
-```
-
-You will also need `react` and `react-native` installed as `dependencies` in
-order to use this project.
-
-## Example
-
-```javascript
-import React from 'react'
-import { Button, Text, TextInput, View } from 'react-native'
-import { fireEvent, render, wait } from '@testing-library/react-native'
-
-function Example() {
- const [name, setUser] = React.useState('')
- const [show, setShow] = React.useState(false)
-
- return (
-
-
- {
- // let's pretend this is making a server request, so it's async
- // (you'd want to mock this imaginary request in your unit tests)...
- setTimeout(() => {
- setShow(!show)
- }, Math.floor(Math.random() * 200))
- }}
- />
- {show && {name} }
-
- )
-}
-
-test('examples of some things', async () => {
- const { getByTestId, getByText, queryByTestId, rootInstance } = render(
-
- )
- const famousWomanInHistory = 'Ada Lovelace'
-
- const input = getByTestId('input')
- fireEvent.changeText(input, famousWomanInHistory)
-
- const button = getByText('Print Username')
- fireEvent.press(button)
-
- await wait(() => expect(queryByTestId('printed-username')).toBeTruthy())
-
- expect(getByTestId('printed-username').props.children).toBe(
- famousWomanInHistory
- )
- expect(rootInstance).toMatchSnapshot()
-})
-```
diff --git a/docs/ecosystem-query-extensions.mdx b/docs/ecosystem-query-extensions.mdx
new file mode 100644
index 000000000..f69c706e0
--- /dev/null
+++ b/docs/ecosystem-query-extensions.mdx
@@ -0,0 +1,16 @@
+---
+id: ecosystem-query-extensions
+title: query-extensions
+---
+
+[`query-extensions`][gh] is an experimental companion library for Testing
+Library that mixes a higher-level API into the standard `@testing-library` core
+queries
+
+```bash npm2yarn
+npm install --save-dev query-extensions
+```
+
+Check out the [github repo][gh] for documentation for examples and rationale.
+
+[gh]: https://github.com/tjefferson08/query-extensions
diff --git a/docs/ecosystem-react-select-event.md b/docs/ecosystem-react-select-event.mdx
similarity index 78%
rename from docs/ecosystem-react-select-event.md
rename to docs/ecosystem-react-select-event.mdx
index c3cbe39a0..d9362d85f 100644
--- a/docs/ecosystem-react-select-event.md
+++ b/docs/ecosystem-react-select-event.mdx
@@ -7,27 +7,27 @@ title: react-select-event
that provides helper methods for interacting with [`react-select`][react-select]
elements.
-```
+```bash npm2yarn
npm install --save-dev react-select-event
```
```jsx
import React from 'react'
import Select from 'react-select'
-import { render } from '@testing-library/react'
+import {render} from '@testing-library/react'
import selectEvent from 'react-select-event'
-const { getByTestId, getByLabelText } = render(
+const {getByTestId, getByLabelText} = render(
+ ,
)
-expect(getByTestId('form')).toHaveFormValues({ food: '' }) // empty select
+expect(getByTestId('form')).toHaveFormValues({food: ''}) // empty select
// select two values...
await selectEvent.select(getByLabelText('Food'), ['Strawberry', 'Mango'])
-expect(getByTestId('form')).toHaveFormValues({ food: ['strawberry', 'mango'] })
+expect(getByTestId('form')).toHaveFormValues({food: ['strawberry', 'mango']})
// ...and add a third one
await selectEvent.select(getByLabelText('Food'), 'Chocolate')
diff --git a/docs/ecosystem-riot-testing-library.mdx b/docs/ecosystem-riot-testing-library.mdx
new file mode 100644
index 000000000..d8fff54d9
--- /dev/null
+++ b/docs/ecosystem-riot-testing-library.mdx
@@ -0,0 +1,33 @@
+---
+id: ecosystem-riot-testing-library
+title: riot-testing-library
+---
+
+[`riot-testing-library`][gh] builds on top of
+[DOM Testing Library](https://github.com/testing-library/dom-testing-library) by
+adding APIs for working with [Riot.js](https://riot.js.org/) components.
+
+```bash npm2yarn
+npm install --save-dev riot-testing-library
+```
+
+```javascript
+import render, {fireEvent} from 'riot-testing-library'
+import TestTag from './test.tag'
+
+test('should show count text when rendered', () => {
+ const {queryByTestId} = render(TestTag, {count: 10})
+ expect(queryByTestId('count').textContent).toBe('10')
+})
+
+test('should add count when click add button text', () => {
+ const {queryByTestId} = render(TestTag, {count: 1})
+ expect(queryByTestId('count').textContent).toBe('1')
+ fireEvent.click(queryByTestId('button'))
+ expect(queryByTestId('count').textContent).toBe('2')
+})
+```
+
+- [riot-testing-library on GitHub][gh]
+
+[gh]: https://github.com/ariesjia/riot-testing-library
diff --git a/docs/ecosystem-rtl-simple-queries.md b/docs/ecosystem-rtl-simple-queries.md
new file mode 100644
index 000000000..5cead58aa
--- /dev/null
+++ b/docs/ecosystem-rtl-simple-queries.md
@@ -0,0 +1,26 @@
+---
+id: ecosystem-rtl-simple-queries
+title: rtl-simple-queries
+---
+
+[`rtl-simple-queries`][gh] is a companion library for `React Testing Library`
+that provides an alternative query API.
+
+```bash npm2yarn
+npm install --save-dev rtl-simple-queries
+```
+
+```jsx
+import {screen} from 'rtl-simple-queries'
+
+screen.fetchByText(/text/, {allowEmpty: true, allowMultiple: false})
+screen.fetchByText(/text/, {allowMultiple: false})
+screen.fetchByText(/text/)
+
+// async
+await screen.fetchByTextAsync(/text/, {allowMultiple: true})
+```
+
+- [rtl-simple-queries on GitHub][gh]
+
+[gh]: https://github.com/balavishnuvj/rtl-simple-queries
diff --git a/docs/ecosystem-testing-library-selector.md b/docs/ecosystem-testing-library-selector.md
new file mode 100644
index 000000000..b2556949d
--- /dev/null
+++ b/docs/ecosystem-testing-library-selector.md
@@ -0,0 +1,44 @@
+---
+id: ecosystem-testing-library-selector
+title: testing-library-selector
+---
+
+[`testing-library-selector`][gh] is a library for `@testing-library` that
+provides reusable selectors. Written in typescript.
+
+```bash npm2yarn
+npm install --save-dev testing-library-selector
+```
+
+```typescript
+import {byLabelText, byRole, byTestId} from './selector'
+
+// define reusable selectors
+const ui = {
+ container: byTestId('my-container'),
+ submitButton: byRole('button', {name: 'Submit'}),
+ usernameInput: byLabelText('Username:'),
+
+ // can encode more specific html element type
+ passwordInput: byLabelText('Password:'),
+}
+
+// reuse them in the same test or across multiple tests by calling
+// .get(), .getAll(), .find(), .findAll(), .query(), .queryAll()
+it('example test', async () => {
+ // by default elements will be queried against screen
+ await ui.submitButton.find()
+ expect(ui.submitButton.query()).not.toBeInDocument()
+ expect(ui.submitButton.get()).toBeInDocument()
+
+ const containers = ui.container.getAll()
+ expect(containers).toHaveLength(3)
+
+ // provide a container as first param to query element inside that container
+ const username = ui.usernameInput.get(containers[0])
+})
+```
+
+- [testing-library-selector on GitHub][gh]
+
+[gh]: https://github.com/domasx2/testing-library-selector
diff --git a/docs/ecosystem-user-event.md b/docs/ecosystem-user-event.md
deleted file mode 100644
index 312f4273a..000000000
--- a/docs/ecosystem-user-event.md
+++ /dev/null
@@ -1,29 +0,0 @@
----
-id: ecosystem-user-event
-title: user-event
----
-
-[`user-event`][gh] is a companion library for `React Testing Library` that
-provides more advanced simulation of browser interactions than the built-in
-`fireEvent` method.
-
-```
-npm install --save-dev @testing-library/user-event
-```
-
-```jsx
-import React from 'react'
-import { render } from '@testing-library/react'
-import userEvent from '@testing-library/user-event'
-
-const { getByText } = test('click', () => {
- render()
-})
-
-userEvent.type(getByTestId('email'), 'Hello, World!')
-expect(getByTestId('email')).toHaveAttribute('value', 'Hello, World!')
-```
-
-- [user-event on GitHub][gh]
-
-[gh]: https://github.com/testing-library/user-event
diff --git a/docs/example-codesandbox.md b/docs/example-codesandbox.md
deleted file mode 100644
index e028f2099..000000000
--- a/docs/example-codesandbox.md
+++ /dev/null
@@ -1,11 +0,0 @@
----
-id: example-codesandbox
-title: Codesandbox Examples
-sidebar_label: Codesandbox Examples
----
-
-You can find runnable examples for many common use cases on Codesandbox.
-
-[](https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples/tree/master/)
-
-
diff --git a/docs/example-codesandbox.mdx b/docs/example-codesandbox.mdx
new file mode 100644
index 000000000..fda0b475c
--- /dev/null
+++ b/docs/example-codesandbox.mdx
@@ -0,0 +1,26 @@
+---
+id: example-codesandbox
+title: Codesandbox Examples
+sidebar_label: Codesandbox Examples
+---
+
+You can find runnable examples for many common use cases on Codesandbox.
+
+[](https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples/tree/main/)
+
+
+
+## Playground
+
+Are you looking for a quick way to run queries against your own html instead?
+Try [testing-playground.com](https://testing-playground.com)
diff --git a/docs/example-drag.mdx b/docs/example-drag.mdx
new file mode 100644
index 000000000..7203e6336
--- /dev/null
+++ b/docs/example-drag.mdx
@@ -0,0 +1,102 @@
+---
+id: example-drag
+title: Drag
+sidebar_label: Drag
+---
+
+> **Note**
+>
+> This example only works with a real browser (not with jsdom, as it does not
+> support `getBoundingClientRect`).
+
+# Usage
+
+```js
+await drag(slider, {
+ delta: {x: -100, y: 0},
+})
+```
+
+# Capture
+
+
+
+# Code
+
+```js
+import {fireEvent} from '@testing-library/dom'
+
+// https://stackoverflow.com/a/53946549/1179377
+function isElement(obj) {
+ if (typeof obj !== 'object') {
+ return false
+ }
+ let prototypeStr, prototype
+ do {
+ prototype = Object.getPrototypeOf(obj)
+ // to work in iframe
+ prototypeStr = Object.prototype.toString.call(prototype)
+ // '[object Document]' is used to detect document
+ if (
+ prototypeStr === '[object Element]' ||
+ prototypeStr === '[object Document]'
+ ) {
+ return true
+ }
+ obj = prototype
+ // null is the terminal of object
+ } while (prototype !== null)
+ return false
+}
+
+function getElementClientCenter(element) {
+ const {left, top, width, height} = element.getBoundingClientRect()
+ return {
+ x: left + width / 2,
+ y: top + height / 2,
+ }
+}
+
+const getCoords = charlie =>
+ isElement(charlie) ? getElementClientCenter(charlie) : charlie
+
+const sleep = ms =>
+ new Promise(resolve => {
+ setTimeout(resolve, ms)
+ })
+
+export default async function drag(
+ element,
+ {to: inTo, delta, steps = 20, duration = 500},
+) {
+ const from = getElementClientCenter(element)
+ const to = delta
+ ? {
+ x: from.x + delta.x,
+ y: from.y + delta.y,
+ }
+ : getCoords(inTo)
+
+ const step = {
+ x: (to.x - from.x) / steps,
+ y: (to.y - from.y) / steps,
+ }
+
+ const current = {
+ clientX: from.x,
+ clientY: from.y,
+ }
+
+ fireEvent.mouseEnter(element, current)
+ fireEvent.mouseOver(element, current)
+ fireEvent.mouseMove(element, current)
+ fireEvent.mouseDown(element, current)
+ for (let i = 0; i < steps; i++) {
+ current.clientX += step.x
+ current.clientY += step.y
+ await sleep(duration / steps)
+ fireEvent.mouseMove(element, current)
+ }
+ fireEvent.mouseUp(element, current)
+}
+```
diff --git a/docs/example-external.md b/docs/example-external.md
deleted file mode 100644
index fa4b369d8..000000000
--- a/docs/example-external.md
+++ /dev/null
@@ -1,19 +0,0 @@
----
-id: example-external
-title: External Examples
-sidebar_label: External Examples
----
-
-## Code Samples
-
-- You can find more React Testing Library examples at
- [react-testing-examples.com](https://react-testing-examples.com/jest-rtl/).
-
-## Videos
-
-- [What is React Testing Library?](https://youtu.be/JKOwJUM4_RM) by Scott
- Tolinski
-
-
-
-
diff --git a/docs/example-external.mdx b/docs/example-external.mdx
new file mode 100644
index 000000000..430e6b38f
--- /dev/null
+++ b/docs/example-external.mdx
@@ -0,0 +1,24 @@
+---
+id: example-external
+title: External Examples
+sidebar_label: External Examples
+---
+
+## Playground
+
+- An interactive sandbox with visual feedback on different queries
+ [testing-playground.com](https://testing-playground.com).
+
+## Videos
+
+- [What is React Testing Library?](https://youtu.be/JKOwJUM4_RM) by Scott
+ Tolinski
+
+
+
+
diff --git a/docs/example-findByText.md b/docs/example-findByText.md
new file mode 100644
index 000000000..f2b02b2d6
--- /dev/null
+++ b/docs/example-findByText.md
@@ -0,0 +1,135 @@
+---
+id: example-findByText
+title: Using findByText
+---
+
+```javascript
+// src/__tests__/example.test.js
+// This is an example of how to use findByText to query for text that
+// is not visible right away
+
+import {screen} from '@testing-library/dom'
+import userEvent from '@testing-library/user-event'
+// provides a set of custom jest matchers that you can use to extend jest
+// i.e. `.toBeVisible`
+import '@testing-library/jest-dom'
+
+function renderApp() {
+ const el = document.body.appendChild(document.createElement('div'))
+ el.innerHTML = `
+
+ `
+
+ const submitButton = el.querySelector('#submit')
+ const formEl = el.querySelector('#login_form')
+
+ submitButton.addEventListener('click', () => {
+ const userInput = el.querySelector('#username_input')
+ const passwordInput = el.querySelector('#password_input')
+
+ const userName = userInput.value
+ const password = passwordInput.value
+ if (!userName) {
+ el.querySelector('#username_required_error').style.display = 'inline'
+ }
+
+ if (!password) {
+ el.querySelector('#password_required_error').style.display = 'inline'
+ }
+
+ if (userName && userName !== 'Bob') {
+ el.querySelector('#invalid_username_error').style.display = 'inline'
+ }
+
+ if (password && password !== 'theBuilder') {
+ el.querySelector('#invalid_password_error').style.display = 'inline'
+ }
+ })
+
+ formEl.addEventListener('submit', function (evt) {
+ evt.preventDefault()
+ window.history.back()
+ })
+
+ return {user: userEvent.setup()}
+}
+
+afterEach(() => (document.body.innerHTML = ``))
+
+describe('findByText Examples', () => {
+ it('should show a required field warning for each empty input field', async () => {
+ const {user} = renderApp()
+ await user.click(
+ screen.getByRole('button', {
+ name: 'Login',
+ }),
+ )
+
+ expect(await screen.findByText('User Name Required')).toBeVisible()
+
+ expect(await screen.findByText('Password Required')).toBeVisible()
+ })
+
+ it('should show invalid field errors for each invalid input field', async () => {
+ const {user} = renderApp()
+ const userNameField = screen.getByPlaceholderText('Enter user name')
+ const passwordField = screen.getByPlaceholderText('Enter password')
+
+ expect(await screen.findByText('Invalid Password')).not.toBeVisible()
+ expect(await screen.findByText('Invalid User Name')).not.toBeVisible()
+
+ await user.type(userNameField, 'Philchard')
+ await user.type(passwordField, 'theCat')
+
+ expect(userNameField).toHaveValue('Philchard')
+ expect(passwordField).toHaveValue('theCat')
+
+ await user.click(
+ screen.getByRole('button', {
+ name: 'Login',
+ }),
+ )
+
+ expect(await screen.findByText('Invalid User Name')).toBeVisible()
+ expect(await screen.findByText('Invalid Password')).toBeVisible()
+ })
+})
+```
diff --git a/docs/example-formik.md b/docs/example-formik.md
new file mode 100644
index 000000000..4d2525b04
--- /dev/null
+++ b/docs/example-formik.md
@@ -0,0 +1,85 @@
+---
+id: example-react-formik
+title: Formik
+---
+
+Example based in this
+[Async Submission Formik example](https://formik.org/docs/examples/async-submission)
+
+```jsx
+// myForm.js
+import React from 'react'
+import {Formik, Field, Form} from 'formik'
+
+const sleep = ms => new Promise(r => setTimeout(r, ms))
+
+export const MyForm = ({onSubmit}) => {
+ const handleSubmit = async values => {
+ await sleep(500)
+ onSubmit(values)
+ }
+
+ return (
+
+
Sign Up
+
+
+
+
+ )
+}
+```
+
+```jsx
+// myForm.test.js
+import React from 'react'
+import {render, screen, waitFor} from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+
+import {MyForm} from './myForm.js'
+
+test('rendering and submitting a basic Formik form', async () => {
+ const handleSubmit = jest.fn()
+ render( )
+ const user = userEvent.setup()
+
+ await user.type(screen.getByRole('textbox', {name: /first name/i}), 'John')
+ await user.type(screen.getByRole('textbox', {name: /last name/i}), 'Dee')
+ await user.type(
+ screen.getByRole('textbox', {name: /email/i}),
+ 'john.dee@someemail.com',
+ )
+
+ await user.click(screen.getByRole('button', {name: /submit/i}))
+
+ await waitFor(() =>
+ expect(handleSubmit).toHaveBeenCalledWith({
+ email: 'john.dee@someemail.com',
+ firstName: 'John',
+ lastName: 'Dee',
+ }),
+ )
+})
+```
diff --git a/docs/example-input-event.md b/docs/example-input-event.md
deleted file mode 100644
index 5d25ee3e9..000000000
--- a/docs/example-input-event.md
+++ /dev/null
@@ -1,79 +0,0 @@
----
-id: example-input-event
-title: Input Event
-sidebar_label: Input Event
----
-
-> **Note**
->
-> If you want to simulate a more natural typing behaviour while testing your
-> component, consider the companion library
-> [`user-event`](ecosystem-user-event.md)
-
-```jsx
-import React from 'react'
-import { render, fireEvent, cleanup } from '@testing-library/react'
-
-class CostInput extends React.Component {
- state = {
- value: '',
- }
-
- removeDollarSign = value => (value[0] === '$' ? value.slice(1) : value)
- getReturnValue = value => (value === '' ? '' : `$${value}`)
- handleChange = ev => {
- ev.preventDefault()
- const inputtedValue = ev.currentTarget.value
- const noDollarSign = this.removeDollarSign(inputtedValue)
- if (isNaN(noDollarSign)) return
- this.setState({ value: this.getReturnValue(noDollarSign) })
- }
-
- render() {
- return (
-
- )
- }
-}
-
-const setup = () => {
- const utils = render( )
- const input = utils.getByLabelText('cost-input')
- return {
- input,
- ...utils,
- }
-}
-
-afterEach(cleanup)
-
-test('It should keep a $ in front of the input', () => {
- const { input } = setup()
- fireEvent.change(input, { target: { value: '23' } })
- expect(input.value).toBe('$23')
-})
-test('It should allow a $ to be in the input when the value is changed', () => {
- const { input } = setup()
- fireEvent.change(input, { target: { value: '$23.0' } })
- expect(input.value).toBe('$23.0')
-})
-
-test('It should not allow letters to be inputted', () => {
- const { input } = setup()
- expect(input.value).toBe('') // empty before
- fireEvent.change(input, { target: { value: 'Good Day' } })
- expect(input.value).toBe('') //empty after
-})
-
-test('It should allow the $ to be deleted', () => {
- const { input } = setup()
- fireEvent.change(input, { target: { value: '23' } })
- expect(input.value).toBe('$23') // need to make a change so React registers "" as a change
- fireEvent.change(input, { target: { value: '' } })
- expect(input.value).toBe('')
-})
-```
diff --git a/docs/example-input-event.mdx b/docs/example-input-event.mdx
new file mode 100644
index 000000000..13b28cd1a
--- /dev/null
+++ b/docs/example-input-event.mdx
@@ -0,0 +1,67 @@
+---
+id: example-input-event
+title: Input Event
+sidebar_label: Input Event
+---
+
+> **Note**
+>
+> If you want to simulate a more natural typing behaviour while testing your
+> component, consider the companion library [`user-event`](user-event/intro.mdx)
+
+```jsx
+import React, {useState} from 'react'
+import {screen, render, fireEvent} from '@testing-library/react'
+
+function CostInput() {
+ const [value, setValue] = useState('')
+
+ const removeDollarSign = value => (value[0] === '$' ? value.slice(1) : value)
+ const getReturnValue = value => (value === '' ? '' : `$${value}`)
+
+ const handleChange = ev => {
+ ev.preventDefault()
+ const inputtedValue = ev.currentTarget.value
+ const noDollarSign = removeDollarSign(inputtedValue)
+ if (isNaN(noDollarSign)) return
+ setValue(getReturnValue(noDollarSign))
+ }
+
+ return
+}
+
+const setup = () => {
+ const utils = render( )
+ const input = screen.getByLabelText('cost-input')
+ return {
+ input,
+ ...utils,
+ }
+}
+
+test('It should keep a $ in front of the input', () => {
+ const {input} = setup()
+ fireEvent.change(input, {target: {value: '23'}})
+ expect(input.value).toBe('$23')
+})
+test('It should allow a $ to be in the input when the value is changed', () => {
+ const {input} = setup()
+ fireEvent.change(input, {target: {value: '$23.0'}})
+ expect(input.value).toBe('$23.0')
+})
+
+test('It should not allow letters to be inputted', () => {
+ const {input} = setup()
+ expect(input.value).toBe('') // empty before
+ fireEvent.change(input, {target: {value: 'Good Day'}})
+ expect(input.value).toBe('') //empty after
+})
+
+test('It should allow the $ to be deleted', () => {
+ const {input} = setup()
+ fireEvent.change(input, {target: {value: '23'}})
+ expect(input.value).toBe('$23') // need to make a change so React registers "" as a change
+ fireEvent.change(input, {target: {value: ''}})
+ expect(input.value).toBe('')
+})
+```
diff --git a/docs/example-reach-router.md b/docs/example-reach-router.mdx
similarity index 62%
rename from docs/example-reach-router.md
rename to docs/example-reach-router.mdx
index 448d83bad..742e14739 100644
--- a/docs/example-reach-router.md
+++ b/docs/example-reach-router.mdx
@@ -5,7 +5,7 @@ title: Reach Router
```jsx
import React from 'react'
-import { render, cleanup } from '@testing-library/react'
+import {render} from '@testing-library/react'
import {
Router,
Link,
@@ -13,7 +13,7 @@ import {
createMemorySource,
LocationProvider,
} from '@reach/router'
-import 'jest-dom/extend-expect'
+import '@testing-library/jest-dom'
const About = () => You are on the about page
const Home = () => You are home
@@ -35,13 +35,11 @@ function App() {
// Ok, so here's what your tests might look like
-afterEach(cleanup)
-
// this is a handy function that I would utilize for any component
// that relies on the router being in context
function renderWithRouter(
ui,
- { route = '/', history = createHistory(createMemorySource(route)) } = {}
+ {route = '/', history = createHistory(createMemorySource(route))} = {},
) {
return {
...render({ui} ),
@@ -55,7 +53,7 @@ function renderWithRouter(
test('full app rendering/navigating', async () => {
const {
container,
- history: { navigate },
+ history: {navigate},
} = renderWithRouter( )
const appContainer = container
// normally I'd use a data-testid, but just wanted to show this is also possible
@@ -68,10 +66,41 @@ test('full app rendering/navigating', async () => {
})
test('landing on a bad page', () => {
- const { container } = renderWithRouter( , {
+ const {container} = renderWithRouter( , {
route: '/something-that-does-not-match',
})
// normally I'd use a data-testid, but just wanted to show this is also possible
expect(container.innerHTML).toMatch('No match')
})
+
+// If your route component has parameters, you'll have to change the render function a little bit
+// example of a route component with parameter
+const Routes = () => (
+
+
+
+)
+
+// render function with Router wrapper from @reach/router
+function renderWithRouterWrapper(
+ ui,
+ {route = '/', history = createHistory(createMemorySource(route))} = {},
+) {
+ return {
+ ...render(
+
+ {ui}
+ ,
+ ),
+ history,
+ }
+}
+
+test('renders the component with params', () => {
+ // you'll have to declare the path prop in the component, exactly like the route
+ renderWithRouterWrapper( , {
+ // and pass the parameter value on the route config
+ route: '/some-component/1',
+ })
+})
```
diff --git a/docs/example-react-context.md b/docs/example-react-context.md
deleted file mode 100644
index f215b5f2b..000000000
--- a/docs/example-react-context.md
+++ /dev/null
@@ -1,65 +0,0 @@
----
-id: example-react-context
-title: React Context
----
-
-```jsx
-import React from 'react'
-import { render, cleanup } from '@testing-library/react'
-import 'jest-dom/extend-expect'
-import { NameContext, NameProvider, NameConsumer } from '../react-context'
-
-afterEach(cleanup)
-
-/**
- * Test default values by rendering a context consumer without a
- * matching provider
- */
-test('NameConsumer shows default value', () => {
- const { getByText } = render( )
- expect(getByText(/^My Name Is:/)).toHaveTextContent('My Name Is: Unknown')
-})
-
-/**
- * To test a component tree that uses a context consumer but not the provider,
- * wrap the tree with a matching provider
- */
-test('NameConsumer shows value from provider', () => {
- const tree = (
-
-
-
- )
- const { getByText } = render(tree)
- expect(getByText(/^My Name Is:/)).toHaveTextContent('My Name Is: C3P0')
-})
-
-/**
- * To test a component that provides a context value, render a matching
- * consumer as the child
- */
-test('NameProvider composes full name from first, last', () => {
- const tree = (
-
-
- {value => Received: {value} }
-
-
- )
- const { getByText } = render(tree)
- expect(getByText(/^Received:/).textContent).toBe('Received: Boba Fett')
-})
-
-/**
- * A tree containing both a providers and consumer can be rendered normally
- */
-test('NameProvider/Consumer shows name of character', () => {
- const tree = (
-
-
-
- )
- const { getByText } = render(tree)
- expect(getByText(/^My Name Is:/).textContent).toBe('My Name Is: Leia Organa')
-})
-```
diff --git a/docs/example-react-context.mdx b/docs/example-react-context.mdx
new file mode 100644
index 000000000..212365e3c
--- /dev/null
+++ b/docs/example-react-context.mdx
@@ -0,0 +1,78 @@
+---
+id: example-react-context
+title: React Context
+---
+
+```jsx
+import React from 'react'
+import {render, screen} from '@testing-library/react'
+import '@testing-library/jest-dom'
+import {NameContext, NameProvider, NameConsumer} from '../react-context'
+
+/**
+ * Test default values by rendering a context consumer without a
+ * matching provider
+ */
+test('NameConsumer shows default value', () => {
+ render( )
+ expect(screen.getByText(/^My Name Is:/)).toHaveTextContent(
+ 'My Name Is: Unknown',
+ )
+})
+
+/**
+ * A custom render to setup providers. Extends regular
+ * render options with `providerProps` to allow injecting
+ * different scenarios to test with.
+ *
+ * @see https://testing-library.com/docs/react-testing-library/setup#custom-render
+ */
+const customRender = (ui, {providerProps, ...renderOptions}) => {
+ return render(
+ {ui} ,
+ renderOptions,
+ )
+}
+
+test('NameConsumer shows value from provider', () => {
+ const providerProps = {
+ value: 'C3PO',
+ }
+ customRender( , {providerProps})
+ expect(screen.getByText(/^My Name Is:/)).toHaveTextContent('My Name Is: C3P0')
+})
+
+/**
+ * To test a component that provides a context value, render a matching
+ * consumer as the child
+ */
+test('NameProvider composes full name from first, last', () => {
+ const providerProps = {
+ first: 'Boba',
+ last: 'Fett',
+ }
+ customRender(
+
+ {value => Received: {value} }
+ ,
+ {providerProps},
+ )
+ expect(screen.getByText(/^Received:/).textContent).toBe('Received: Boba Fett')
+})
+
+/**
+ * A tree containing both a providers and consumer can be rendered normally
+ */
+test('NameProvider/Consumer shows name of character', () => {
+ const wrapper = ({children}) => (
+
+ {children}
+
+ )
+
+ render( , {wrapper})
+ expect(screen.getByText(/^My Name Is:/).textContent).toBe(
+ 'My Name Is: Leia Organa',
+ )
+})
+```
diff --git a/docs/example-react-hooks-useReducer.mdx b/docs/example-react-hooks-useReducer.mdx
new file mode 100644
index 000000000..2ce3fb1b4
--- /dev/null
+++ b/docs/example-react-hooks-useReducer.mdx
@@ -0,0 +1,73 @@
+---
+id: example-react-hooks-useReducer
+title: useReducer
+---
+
+Basic example showing how to test the `useReducer` hook. The most important
+thing is that we aren't testing the reducer directly - it's an implementation
+detail of the component! Instead we are testing the component interface.
+
+The component we are testing changes some text depending on an `isConfirmed`
+state.
+
+```jsx
+// example.js
+
+import React, {useReducer} from 'react'
+
+const initialState = {
+ isConfirmed: false,
+}
+
+function reducer(state = initialState, action) {
+ switch (action.type) {
+ case 'SUCCESS':
+ return {
+ ...state,
+ isConfirmed: true,
+ }
+ default:
+ throw Error('unknown action')
+ }
+}
+
+const Example = () => {
+ const [state, dispatch] = useReducer(reducer, initialState)
+
+ return (
+
+
+ {state.isConfirmed ? (
+
Confirmed!
+ ) : (
+
Waiting for confirmation...
+ )}
+
+
dispatch({type: 'SUCCESS'})}>Confirm
+
+ )
+}
+
+export default Example
+```
+
+We are testing to see if we get the correct output in our JSX based on the
+reducer state.
+
+```jsx
+// example.test.js
+
+import React from 'react'
+import {render, fireEvent} from '@testing-library/react'
+import Example from './example.js'
+
+it('shows success message after confirm button is clicked', () => {
+ const {getByText} = render( )
+
+ expect(getByText(/waiting/i)).toBeInTheDocument()
+
+ fireEvent.click(getByText('Confirm'))
+
+ expect(getByText('Confirmed!')).toBeInTheDocument()
+})
+```
diff --git a/docs/example-react-intl.md b/docs/example-react-intl.md
deleted file mode 100644
index 40a5eb2ec..000000000
--- a/docs/example-react-intl.md
+++ /dev/null
@@ -1,82 +0,0 @@
----
-id: example-react-intl
-title: React Intl
----
-
-> **Note**
->
-> If you want to combine setupTests with another setup you should check
-> [`setup`](react-testing-library/setup.md)
-
-## Configuring React-Intl Polyfills
-
-If you're using React-Intl in your project, and you need to load a locale, you
-must load the Polyfills according to that language.
-
-In order to do so, you may use this small setup and/or combine it with other
-setups.
-
-```
-// src/setupTests.js
-import IntlPolyfill from 'intl'
-import 'intl/locale-data/jsonp/pt'
-
-const setupTests = () => {
- // https://formatjs.io/guides/runtime-environments/#server
- if (global.Intl) {
- Intl.NumberFormat = IntlPolyfill.NumberFormat
- Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat
- } else {
- global.Intl = IntlPolyfill
- }
-}
-
-setupTests();
-```
-
-A complete example:
-
-```jsx
-import React from 'react'
-import 'jest-dom/extend-expect'
-import { render, cleanup, getByTestId } from '@testing-library/react'
-import { IntlProvider, FormattedDate } from 'react-intl'
-import IntlPolyfill from 'intl'
-import 'intl/locale-data/jsonp/pt'
-
-const setupTests = () => {
- // https://formatjs.io/guides/runtime-environments/#server
- if (global.Intl) {
- Intl.NumberFormat = IntlPolyfill.NumberFormat
- Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat
- } else {
- global.Intl = IntlPolyfill
- }
-}
-
-const FormatDateView = () => {
- return (
-
-
-
- )
-}
-
-const renderWithReactIntl = component => {
- return render({component} )
-}
-
-setupTests()
-afterEach(cleanup)
-
-test('it should render FormattedDate and have a formated pt date', () => {
- const { container } = renderWithReactIntl( )
- expect(getByTestId(container, 'date-display')).toHaveTextContent('11/03/2019')
-})
-```
diff --git a/docs/example-react-intl.mdx b/docs/example-react-intl.mdx
new file mode 100644
index 000000000..f9ea45597
--- /dev/null
+++ b/docs/example-react-intl.mdx
@@ -0,0 +1,144 @@
+---
+id: example-react-intl
+title: React Intl
+---
+
+> **Note**
+>
+> If you want to combine setupTests with another setup you should check
+> [`setup`](react-testing-library/setup.mdx)
+
+## Configuring React-Intl Polyfills / Locales
+
+If you're using React-Intl in your project, and you **need** to load a locale,
+You have two options:
+
+1. When using Node 13 and higher, Intl support is now out of the box. The
+ default ICU (International Components for Unicode) option for Node is
+ `full-icu` meaning all ICU's.
+ All you need to do is embed the set of ICU data you need:
+
+ ```js
+ // test-utils.js
+
+ const hasFullICU = () => {
+ // That's the recommended way to test for ICU support according to Node.js docs
+ try {
+ const january = new Date(9e8)
+ const pt = new Intl.DateTimeFormat('pt', {month: 'long'})
+ return pt.format(january) === 'janeiro'
+ } catch (err) {
+ return false
+ }
+ }
+
+ export const setupTests = () => {
+ if (hasFullICU()) {
+ Intl.NumberFormat.format = new Intl.NumberFormat('pt').format
+ Intl.DateTimeFormat.format = new Intl.DateTimeFormat('pt').format
+ } else {
+ global.Intl = IntlPolyfill
+ }
+ }
+ ```
+
+2. When using Node with prior versions, the ICU default option is `small-icu`
+ meaning it includes a subset of ICU data (typically only the English
+ locale).
+ If you do need to load a locale you have two options:
+
+ 1. Load the Polyfills according to that language:
+
+ ```js
+ // test-utils.js
+ import IntlPolyfill from 'intl'
+ import 'intl/locale-data/jsonp/pt'
+
+ export const setupTests = () => {
+ // https://formatjs.io/docs/guides/runtime-requirements/#nodejs
+ if (global.Intl) {
+ Intl.NumberFormat = IntlPolyfill.NumberFormat
+ Intl.DateTimeFormat = IntlPolyfill.DateTimeFormat
+ } else {
+ global.Intl = IntlPolyfill
+ }
+ }
+ ```
+
+ 2. Load the ICU's at runtime:
+ Install the package
+ [full-icu](https://github.com/unicode-org/full-icu-npm) and inject it to
+ your test environment, you can do that by setting `NODE_ICU_DATA` before
+ calling jest: `NODE_ICU_DATA=node_modules/full-icu jest`. Doing that you
+ will give you full-icu support as shown in option 1.
+
+## Creating a custom render function
+
+To test our translated component we can create a custom `render` function using
+the `wrapper` option as explained in the
+[setup](react-testing-library/setup.mdx) page.
+Our custom `render` function can look like this:
+
+```jsx
+// test-utils.js
+import React from 'react'
+import {render as rtlRender} from '@testing-library/react'
+import {IntlProvider} from 'react-intl'
+
+function render(ui, {locale = 'pt', ...renderOptions} = {}) {
+ function Wrapper({children}) {
+ return {children}
+ }
+ return rtlRender(ui, {wrapper: Wrapper, ...renderOptions})
+}
+
+// re-export everything
+export * from '@testing-library/react'
+
+// override render method
+export {render}
+```
+
+## A complete example
+
+```jsx
+import React from 'react'
+import '@testing-library/jest-dom'
+// We're importing from our own created test-utils and not RTL's
+import {render, screen, setupTests} from '../test-utils.js'
+import {FormattedDate} from 'react-intl'
+
+const FormatDateView = () => {
+ return (
+
+
+
+ )
+}
+
+setupTests()
+
+test('it should render FormattedDate and have a formatted pt date', () => {
+ render( )
+ expect(screen.getByTestId('date-display')).toHaveTextContent('11/03/2019')
+})
+```
+
+## Translated components testing stategy
+
+When testing a translated component there can be different approaches for
+achieving the wanted coverage, where the aimed goal should be to allow testing
+the component in a way that will simulate the user behavior as much as posible.
+
+| Approach | Pros | Cons |
+| ------------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| Use strings from the default language | Test is easy to read, and asserts expected default output. If you have variables in your strings, you can test that they work properly with correct output. | 1. Strings hardcoded into tests mean you have to update both tests and code for any copy changes. 2. If multiple elements have the same string/substring text, find-and-replace may be hard to use reliably. |
+| Mock the translation library | If your library is difficult to use in the test environment, you can mock it so it is easier. For example, you can add the message ID as a data-attribute to the text so you can query by that. | Test code deviates from what runs in production. Tests may assert about message IDs but not enough about content, so errors are possible. |
+| Use translation library in tests | Decouples strings from tests, so you can update the message files in one place without worrying about breaking tests. Can run tests in another language or multiple languages. `const buttonText = getNodeText( );` | Overhead - it takes more lines of code to write the test, and you need to know the variables and message IDs to create the right strings. It's not obvious what the text actually is when you read the test code, making maintaining it harder. |
+| Use translation library + inline snapshots | Same as above, but by adding an inline snapshot of the string, you can read the test code and see what strings are in use, but easily update them with `jest --updateSnapshot` if the messages change. `expect(buttonText).toMatchInlineSnapshot("'My button text'")` | Tests are longer because of the extra lines. You can wrap up some of the translation-related code into a helper function to make it a little more inline-able and avoid repeating yourself, but you still need to know the message IDs and variables inside the test. |
diff --git a/docs/example-react-modal.mdx b/docs/example-react-modal.mdx
new file mode 100644
index 000000000..9bccf105a
--- /dev/null
+++ b/docs/example-react-modal.mdx
@@ -0,0 +1,55 @@
+---
+id: example-react-modal
+title: Modals
+---
+
+```jsx
+import React, {useEffect} from 'react'
+import ReactDOM from 'react-dom'
+import {render, fireEvent} from '@testing-library/react'
+
+const modalRoot = document.createElement('div')
+modalRoot.setAttribute('id', 'modal-root')
+document.body.appendChild(modalRoot)
+
+const Modal = ({onClose, children}) => {
+ const el = document.createElement('div')
+
+ useEffect(() => {
+ modalRoot.appendChild(el)
+
+ return () => modalRoot.removeChild(el)
+ })
+
+ return ReactDOM.createPortal(
+
+
e.stopPropagation()}>
+ {children}
+
+ Close
+
+
,
+ el,
+ )
+}
+
+test('modal shows the children and a close button', () => {
+ // Arrange
+ const handleClose = jest.fn()
+
+ // Act
+ const {getByText} = render(
+
+ test
+ ,
+ )
+ // Assert
+ expect(getByText('test')).toBeTruthy()
+
+ // Act
+ fireEvent.click(getByText(/close/i))
+
+ // Assert
+ expect(handleClose).toHaveBeenCalledTimes(1)
+})
+```
diff --git a/docs/example-react-redux.md b/docs/example-react-redux.md
deleted file mode 100644
index f5ff77c15..000000000
--- a/docs/example-react-redux.md
+++ /dev/null
@@ -1,113 +0,0 @@
----
-id: example-react-redux
-title: React Redux
----
-
-```jsx
-import React from 'react'
-import { createStore } from 'redux'
-import { Provider, connect } from 'react-redux'
-import { render, fireEvent, cleanup } from '@testing-library/react'
-
-// counter.js
-class Counter extends React.Component {
- increment = () => {
- this.props.dispatch({ type: 'INCREMENT' })
- }
-
- decrement = () => {
- this.props.dispatch({ type: 'DECREMENT' })
- }
-
- render() {
- return (
-
-
Counter
-
- -
- {this.props.count}
- +
-
-
- )
- }
-}
-
-// normally this would be:
-// export default connect(state => ({count: state.count}))(Counter)
-// but for this test we'll give it a variable name
-// because we're doing this all in one file
-const ConnectedCounter = connect(state => ({ count: state.count }))(Counter)
-
-// app.js
-function reducer(state = { count: 0 }, action) {
- switch (action.type) {
- case 'INCREMENT':
- return {
- count: state.count + 1,
- }
- case 'DECREMENT':
- return {
- count: state.count - 1,
- }
- default:
- return state
- }
-}
-
-// normally here you'd do:
-// const store = createStore(reducer)
-// ReactDOM.render(
-//
-//
-// ,
-// document.getElementById('root'),
-// )
-// but for this test we'll umm... not do that :)
-
-// Now here's what your test will look like:
-
-afterEach(cleanup)
-
-// this is a handy function that I normally make available for all my tests
-// that deal with connected components.
-// you can provide initialState or the entire store that the ui is rendered with
-function renderWithRedux(
- ui,
- { initialState, store = createStore(reducer, initialState) } = {}
-) {
- return {
- ...render({ui} ),
- // adding `store` to the returned utilities to allow us
- // to reference it in our tests (just try to avoid using
- // this to test implementation details).
- store,
- }
-}
-
-test('can render with redux with defaults', () => {
- const { getByTestId, getByText } = renderWithRedux( )
- fireEvent.click(getByText('+'))
- expect(getByTestId('count-value').textContent).toBe('1')
-})
-
-test('can render with redux with custom initial state', () => {
- const { getByTestId, getByText } = renderWithRedux( , {
- initialState: { count: 3 },
- })
- fireEvent.click(getByText('-'))
- expect(getByTestId('count-value').textContent).toBe('2')
-})
-
-test('can render with redux with custom store', () => {
- // this is a silly store that can never be changed
- const store = createStore(() => ({ count: 1000 }))
- const { getByTestId, getByText } = renderWithRedux( , {
- store,
- })
- fireEvent.click(getByText('+'))
- expect(getByTestId('count-value').textContent).toBe('1000')
- fireEvent.click(getByText('-'))
- expect(getByTestId('count-value').textContent).toBe('1000')
-})
-```
diff --git a/docs/example-react-redux.mdx b/docs/example-react-redux.mdx
new file mode 100644
index 000000000..a0a01ca5f
--- /dev/null
+++ b/docs/example-react-redux.mdx
@@ -0,0 +1,7 @@
+---
+id: example-react-redux
+title: React Redux
+---
+
+Moved to
+[Writing Tests | Redux](https://redux.js.org/recipes/writing-tests#connected-components)
diff --git a/docs/example-react-router.md b/docs/example-react-router.md
deleted file mode 100644
index 85e8e5431..000000000
--- a/docs/example-react-router.md
+++ /dev/null
@@ -1,81 +0,0 @@
----
-id: example-react-router
-title: React Router
----
-
-```jsx
-import React from 'react'
-import { withRouter } from 'react-router'
-import { Link, Route, Router, Switch } from 'react-router-dom'
-import { createMemoryHistory } from 'history'
-import { render, fireEvent, cleanup } from '@testing-library/react'
-
-const About = () => You are on the about page
-const Home = () => You are home
-const NoMatch = () => No match
-
-const LocationDisplay = withRouter(({ location }) => (
- {location.pathname}
-))
-
-function App() {
- return (
-
- Home
- About
-
-
-
-
-
-
-
- )
-}
-
-// Ok, so here's what your tests might look like
-
-afterEach(cleanup)
-
-// this is a handy function that I would utilize for any component
-// that relies on the router being in context
-function renderWithRouter(
- ui,
- {
- route = '/',
- history = createMemoryHistory({ initialEntries: [route] }),
- } = {}
-) {
- return {
- ...render({ui} ),
- // adding `history` to the returned utilities to allow us
- // to reference it in our tests (just try to avoid using
- // this to test implementation details).
- history,
- }
-}
-
-test('full app rendering/navigating', () => {
- const { container, getByText } = renderWithRouter( )
- // normally I'd use a data-testid, but just wanted to show this is also possible
- expect(container.innerHTML).toMatch('You are home')
- const leftClick = { button: 0 }
- fireEvent.click(getByText(/about/i), leftClick)
- // normally I'd use a data-testid, but just wanted to show this is also possible
- expect(container.innerHTML).toMatch('You are on the about page')
-})
-
-test('landing on a bad page', () => {
- const { container } = renderWithRouter( , {
- route: '/something-that-does-not-match',
- })
- // normally I'd use a data-testid, but just wanted to show this is also possible
- expect(container.innerHTML).toMatch('No match')
-})
-
-test('rendering a component that uses withRouter', () => {
- const route = '/some-route'
- const { getByTestId } = renderWithRouter( , { route })
- expect(getByTestId('location-display').textContent).toBe(route)
-})
-```
diff --git a/docs/example-react-router.mdx b/docs/example-react-router.mdx
new file mode 100644
index 000000000..76875f88c
--- /dev/null
+++ b/docs/example-react-router.mdx
@@ -0,0 +1,216 @@
+---
+id: example-react-router
+title: React Router
+---
+
+This example demonstrates React Router v6. For previous versions see below.
+
+```jsx
+// app.js
+import React from 'react'
+import {Link, Route, Routes, useLocation} from 'react-router-dom'
+
+const About = () => You are on the about page
+const Home = () => You are home
+const NoMatch = () => No match
+
+export const LocationDisplay = () => {
+ const location = useLocation()
+
+ return {location.pathname}
+}
+
+export const App = () => (
+
+ Home
+
+ About
+
+
+ } />
+
+ } />
+
+ } />
+
+
+
+
+)
+```
+
+```jsx
+// app.test.js
+import {render, screen} from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+import React from 'react'
+import '@testing-library/jest-dom'
+import {App, LocationDisplay} from './app'
+import {BrowserRouter, MemoryRouter} from 'react-router-dom'
+
+test('full app rendering/navigating', async () => {
+ render( , {wrapper: BrowserRouter})
+ const user = userEvent.setup()
+
+ // verify page content for default route
+ expect(screen.getByText(/you are home/i)).toBeInTheDocument()
+
+ // verify page content for expected route after navigating
+ await user.click(screen.getByText(/about/i))
+ expect(screen.getByText(/you are on the about page/i)).toBeInTheDocument()
+})
+
+test('landing on a bad page', () => {
+ const badRoute = '/some/bad/route'
+
+ // use when you want to manually control the history
+ render(
+
+
+ ,
+ )
+
+ // verify navigation to "no match" route
+ expect(screen.getByText(/no match/i)).toBeInTheDocument()
+})
+
+test('rendering a component that uses useLocation', () => {
+ const route = '/some-route'
+
+ // use when you want to manually control the history
+ render(
+
+
+ ,
+ )
+
+ // verify location display is rendered
+ expect(screen.getByTestId('location-display')).toHaveTextContent(route)
+})
+```
+
+## Reducing boilerplate
+
+1. If you find yourself adding Router components to your tests a lot, you may
+ want to create a helper function that wraps around `render`.
+
+```jsx
+// test utils file
+const renderWithRouter = (ui, {route = '/'} = {}) => {
+ window.history.pushState({}, 'Test page', route)
+
+ return {
+ user: userEvent.setup(),
+ ...render(ui, {wrapper: BrowserRouter}),
+ }
+}
+```
+
+```jsx
+// app.test.js
+test('full app rendering/navigating', async () => {
+ const {user} = renderWithRouter( )
+ expect(screen.getByText(/you are home/i)).toBeInTheDocument()
+
+ await user.click(screen.getByText(/about/i))
+
+ expect(screen.getByText(/you are on the about page/i)).toBeInTheDocument()
+})
+
+test('landing on a bad page', () => {
+ renderWithRouter( , {route: '/something-that-does-not-match'})
+
+ expect(screen.getByText(/no match/i)).toBeInTheDocument()
+})
+
+test('rendering a component that uses useLocation', () => {
+ const route = '/some-route'
+ renderWithRouter( , {route})
+
+ expect(screen.getByTestId('location-display')).toHaveTextContent(route)
+})
+```
+
+## Testing Library and React Router v5
+
+```jsx
+// app.js
+import React from 'react'
+import {Link, Route, Switch, useLocation} from 'react-router-dom'
+
+const About = () => You are on the about page
+const Home = () => You are home
+const NoMatch = () => No match
+
+export const LocationDisplay = () => {
+ const location = useLocation()
+
+ return {location.pathname}
+}
+
+export const App = () => (
+
+ Home
+
+ About
+
+
+
+
+
+
+
+
+
+
+
+)
+```
+
+In your tests, pass the history object as a whole to the Router component.
+**Note:** React Router v5
+[only works with History v4](https://github.com/remix-run/history#documentation),
+so make sure you have the correct version of `history` installed.
+
+```jsx
+// app.test.js
+import {render, screen} from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+import {createMemoryHistory} from 'history'
+import React from 'react'
+import {Router} from 'react-router-dom'
+import '@testing-library/jest-dom'
+import {App} from './app'
+
+// React Router v5
+
+test('full app rendering/navigating', async () => {
+ const history = createMemoryHistory()
+ render(
+
+
+ ,
+ )
+ const user = userEvent.setup()
+ // verify page content for expected route
+ // often you'd use a data-testid or role query, but this is also possible
+ expect(screen.getByText(/you are home/i)).toBeInTheDocument()
+
+ await user.click(screen.getByText(/about/i))
+
+ // check that the content changed to the new page
+ expect(screen.getByText(/you are on the about page/i)).toBeInTheDocument()
+})
+
+test('landing on a bad page', () => {
+ const history = createMemoryHistory()
+ history.push('/some/bad/route')
+ render(
+
+
+ ,
+ )
+
+ expect(screen.getByText(/no match/i)).toBeInTheDocument()
+})
+```
diff --git a/docs/example-react-transition-group.md b/docs/example-react-transition-group.md
deleted file mode 100644
index 6b9773158..000000000
--- a/docs/example-react-transition-group.md
+++ /dev/null
@@ -1,116 +0,0 @@
----
-id: example-react-transition-group
-title: React Transition Group
----
-
-## Mock
-
-```jsx
-import React from 'react'
-import { CSSTransition } from 'react-transition-group'
-import { render, fireEvent, cleanup } from '@testing-library/react'
-
-function Fade({ children, ...props }) {
- return (
-
- {children}
-
- )
-}
-
-class HiddenMessage extends React.Component {
- state = { show: this.props.initialShow || false }
- toggle = () => {
- this.setState(({ show }) => ({ show: !show }))
- }
- render() {
- return (
-
-
Toggle
-
- Hello world
-
-
- )
- }
-}
-
-afterEach(cleanup)
-
-jest.mock('react-transition-group', () => {
- const FakeTransition = jest.fn(({ children }) => children)
- const FakeCSSTransition = jest.fn(props =>
- props.in ? {props.children} : null
- )
- return { CSSTransition: FakeCSSTransition, Transition: FakeTransition }
-})
-
-test('you can mock things with jest.mock', () => {
- const { getByText, queryByText } = render(
-
- )
- expect(getByText('Hello world')).toBeTruthy() // we just care it exists
- // hide the message
- fireEvent.click(getByText('Toggle'))
- // in the real world, the CSSTransition component would take some time
- // before finishing the animation which would actually hide the message.
- // So we've mocked it out for our tests to make it happen instantly
- expect(queryByText('Hello World')).toBeNull() // we just care it doesn't exist
-})
-```
-
-## Shallow
-
-```jsx
-import React from 'react'
-import { CSSTransition } from 'react-transition-group'
-import { render, fireEvent, cleanup } from '@testing-library/react'
-
-function Fade({ children, ...props }) {
- return (
-
- {children}
-
- )
-}
-
-class HiddenMessage extends React.Component {
- state = { show: this.props.initialShow || false }
- toggle = () => {
- this.setState(({ show }) => ({ show: !show }))
- }
- render() {
- return (
-
-
Toggle
-
- Hello world
-
-
- )
- }
-}
-
-afterEach(cleanup)
-
-jest.mock('react-transition-group', () => {
- const FakeCSSTransition = jest.fn(() => null)
- return { CSSTransition: FakeCSSTransition }
-})
-
-test('you can mock things with jest.mock', () => {
- const { getByText } = render( )
- const context = expect.any(Object)
- const children = expect.any(Object)
- const defaultProps = { children, timeout: 1000, className: 'fade' }
- expect(CSSTransition).toHaveBeenCalledWith(
- { in: true, ...defaultProps },
- context
- )
- fireEvent.click(getByText(/toggle/i))
- expect(CSSTransition).toHaveBeenCalledWith(
- { in: true, ...defaultProps },
- expect.any(Object)
- )
-})
-```
diff --git a/docs/example-react-transition-group.mdx b/docs/example-react-transition-group.mdx
new file mode 100644
index 000000000..d22e3f3cd
--- /dev/null
+++ b/docs/example-react-transition-group.mdx
@@ -0,0 +1,102 @@
+---
+id: example-react-transition-group
+title: React Transition Group
+---
+
+## Mock
+
+```jsx
+import React, {useState} from 'react'
+import {CSSTransition} from 'react-transition-group'
+import {render, fireEvent} from '@testing-library/react'
+
+function Fade({children, ...props}) {
+ return (
+
+ {children}
+
+ )
+}
+
+function HiddenMessage({initialShow}) {
+ const [show, setShow] = useState(initialShow || false)
+ const toggle = () => setShow(prevState => !prevState)
+ return (
+
+
Toggle
+
+ Hello world
+
+
+ )
+}
+
+jest.mock('react-transition-group', () => {
+ const FakeTransition = jest.fn(({children}) => children)
+ const FakeCSSTransition = jest.fn(props =>
+ props.in ? {props.children} : null,
+ )
+ return {CSSTransition: FakeCSSTransition, Transition: FakeTransition}
+})
+
+test('you can mock things with jest.mock', () => {
+ const {getByText, queryByText} = render( )
+ expect(getByText('Hello world')).toBeTruthy() // we just care it exists
+ // hide the message
+ fireEvent.click(getByText('Toggle'))
+ // in the real world, the CSSTransition component would take some time
+ // before finishing the animation which would actually hide the message.
+ // So we've mocked it out for our tests to make it happen instantly
+ expect(queryByText('Hello World')).toBeNull() // we just care it doesn't exist
+})
+```
+
+## Shallow
+
+```jsx
+import React, {useState} from 'react'
+import {CSSTransition} from 'react-transition-group'
+import {render, fireEvent} from '@testing-library/react'
+
+function Fade({children, ...props}) {
+ return (
+
+ {children}
+
+ )
+}
+
+function HiddenMessage({initialShow}) {
+ const [show, setShow] = useState(initialShow || false)
+ const toggle = () => setShow(prevState => !prevState)
+ return (
+
+
Toggle
+
+ Hello world
+
+
+ )
+}
+
+jest.mock('react-transition-group', () => {
+ const FakeCSSTransition = jest.fn(() => null)
+ return {CSSTransition: FakeCSSTransition}
+})
+
+test('you can mock things with jest.mock', () => {
+ const {getByText} = render( )
+ const context = expect.any(Object)
+ const children = expect.any(Object)
+ const defaultProps = {children, timeout: 1000, className: 'fade'}
+ expect(CSSTransition).toHaveBeenCalledWith(
+ {in: true, ...defaultProps},
+ context,
+ )
+ fireEvent.click(getByText(/toggle/i))
+ expect(CSSTransition).toHaveBeenCalledWith(
+ {in: false, ...defaultProps},
+ expect.any(Object),
+ )
+})
+```
diff --git a/docs/example-update-props.md b/docs/example-update-props.md
deleted file mode 100644
index 9fcf8cec5..000000000
--- a/docs/example-update-props.md
+++ /dev/null
@@ -1,41 +0,0 @@
----
-id: example-update-props
-title: Update Props
-sidebar_label: Update Props
----
-
-```jsx
-// This is an example of how to update the props of a rendered component.
-// the basic idea is to simply call `render` again and provide the same container
-// that your first call created for you.
-
-import React from 'react'
-import { render, cleanup } from '@testing-library/react'
-
-let idCounter = 1
-
-class NumberDisplay extends React.Component {
- id = idCounter++ // to ensure we don't remount a different instance
- render() {
- return (
-
- {this.props.number}
- {this.id}
-
- )
- }
-}
-
-afterEach(cleanup)
-
-test('calling render with the same component on the same container does not remount', () => {
- const { getByTestId, rerender } = render( )
- expect(getByTestId('number-display').textContent).toBe('1')
-
- // re-render the same component with different props
- rerender( )
- expect(getByTestId('number-display').textContent).toBe('2')
-
- expect(getByTestId('instance-id').textContent).toBe('1')
-})
-```
diff --git a/docs/example-update-props.mdx b/docs/example-update-props.mdx
new file mode 100644
index 000000000..26d8388cf
--- /dev/null
+++ b/docs/example-update-props.mdx
@@ -0,0 +1,38 @@
+---
+id: example-update-props
+title: Update Props
+sidebar_label: Update Props
+---
+
+```jsx
+// This is an example of how to update the props of a rendered component.
+// the basic idea is to simply call `render` again and provide the same container
+// that your first call created for you.
+
+import React, {useRef} from 'react'
+import {render, screen} from '@testing-library/react'
+
+let idCounter = 1
+
+const NumberDisplay = ({number}) => {
+ const id = useRef(idCounter++) // to ensure we don't remount a different instance
+
+ return (
+
+ {number}
+ {id.current}
+
+ )
+}
+
+test('calling render with the same component on the same container does not remount', () => {
+ const {rerender} = render( )
+ expect(screen.getByTestId('number-display')).toHaveTextContent('1')
+
+ // re-render the same component with different props
+ rerender( )
+ expect(screen.getByTestId('number-display')).toHaveTextContent('2')
+
+ expect(screen.getByTestId('instance-id')).toHaveTextContent('1')
+})
+```
diff --git a/docs/guide-disappearance.md b/docs/guide-disappearance.mdx
similarity index 69%
rename from docs/guide-disappearance.md
rename to docs/guide-disappearance.mdx
index 92182fa73..b74130a6a 100644
--- a/docs/guide-disappearance.md
+++ b/docs/guide-disappearance.mdx
@@ -10,19 +10,30 @@ vice versa.
If you need to wait for an element to appear, the [async wait
utilities][async-api] allow you to wait for an assertion to be satisfied before
-proceeding. The wait utilities retry until the query passes or times out.
+proceeding. The wait utilities retry until the query passes or times out. _The
+async methods return a Promise, so you must always use `await` or `.then(done)`
+when calling them._
+
+### 1. Using `findBy` Queries
```jsx
test('movie title appears', async () => {
// element is initially not present...
+ // wait for appearance and return the element
+ const movie = await findByText('the lion king')
+})
+```
+
+### 2. Using `waitFor`
- // wait for appearance
- await wait(() => {
+```jsx
+test('movie title appears', async () => {
+ // element is initially not present...
+
+ // wait for appearance inside an assertion
+ await waitFor(() => {
expect(getByText('the lion king')).toBeInTheDocument()
})
-
- // wait for appearance and return the element
- const movie = await waitForElement(() => getByText('the lion king'))
})
```
@@ -41,18 +52,18 @@ test('movie title no longer present in DOM', async () => {
Using
[`MutationObserver`](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver)
-is more efficient than polling the DOM at regular intervals with `wait`.
+is more efficient than polling the DOM at regular intervals with `waitFor`.
-The `wait` [async helper][async-api] function retries until the wrapped function
-stops throwing an error. This can be used to assert that an element disappears
-from the page.
+The `waitFor` [async helper][async-api] function retries until the wrapped
+function stops throwing an error. This can be used to assert that an element
+disappears from the page.
```jsx
test('movie title goes away', async () => {
// element is initially present...
// note use of queryBy instead of getBy to return null
// instead of throwing in the query itself
- await wait(() => {
+ await waitFor(() => {
expect(queryByText('i, robot')).not.toBeInTheDocument()
})
})
@@ -65,7 +76,7 @@ if you want to make an assertion that an element is _not_ present in the DOM,
you can use `queryBy` APIs instead:
```javascript
-const submitButton = queryByText(container, 'submit')
+const submitButton = screen.queryByText('submit')
expect(submitButton).toBeNull() // it doesn't exist
```
@@ -74,22 +85,22 @@ array can be useful for assertions after elements are added or removed from the
DOM.
```javascript
-const submitButtons = queryAllByText(container, 'submit')
-expect(submitButtons).toHaveLength(2) // expect 2 elements
+const submitButtons = screen.queryAllByText('submit')
+expect(submitButtons).toHaveLength(0) // expect no elements
```
### `not.toBeInTheDocument`
-The [`jest-dom`](ecosystem-jest-dom.md) utility library provides the
+The [`jest-dom`](ecosystem-jest-dom.mdx) utility library provides the
`.toBeInTheDocument()` matcher, which can be used to assert that an element is
in the body of the document, or not. This can be more meaningful than asserting
a query result is `null`.
```javascript
-import 'jest-dom/extend-expect'
+import '@testing-library/jest-dom'
// use `queryBy` to avoid throwing an error with `getBy`
-const submitButton = queryByText(container, 'submit')
+const submitButton = screen.queryByText('submit')
expect(submitButton).not.toBeInTheDocument()
```
-[async-api]: dom-testing-library/api-async.md
+[async-api]: dom-testing-library/api-async.mdx
diff --git a/docs/guide-events.mdx b/docs/guide-events.mdx
new file mode 100644
index 000000000..1c2e520b4
--- /dev/null
+++ b/docs/guide-events.mdx
@@ -0,0 +1,71 @@
+---
+id: guide-events
+title: Considerations for fireEvent
+---
+
+## Interactions vs. events
+
+Based on [the Guiding Principles](guiding-principles.mdx), your test should
+resemble how users interact with your code (component, page, etc.) as much as
+possible. With this in mind, you should know that `fireEvent` isn't _exactly_
+how the user interacts with your application, but it's close enough for most
+scenarios.
+
+Consider `fireEvent.click`, which creates a click event and dispatches that
+event on the given DOM node. This works properly for most situations when you
+simply want to test what happens when your element is clicked, but when the
+_user_ actually clicks your element, these are the events that are typically
+fired (in order):
+
+- fireEvent.mouseOver(element)
+- fireEvent.mouseMove(element)
+- fireEvent.mouseDown(element)
+- element.focus() (if that element is focusable)
+- fireEvent.mouseUp(element)
+- fireEvent.click(element)
+
+And then, if that element happens to be a child of a `label`, then it will also
+move focus to the form control that the label is labeling. So even though all
+you really are trying to test is the click handler, by simply using
+`fireEvent.click` you're missing out on several other potentially important
+events the user is firing along the way.
+
+Again, most of the time this isn't critical for your tests and the trade-off of
+simply using `fireEvent.click` is worth it.
+
+## Alternatives
+
+We will describe a couple of simple adjustments to your tests that will increase
+your confidence in the interactive behavior of your components. For other
+interactions you may want to either consider using
+[`user-event`](user-event/intro.mdx) or testing your components in a real
+environment (e.g. manually, automatic with cypress, etc.).
+
+### Keydown
+
+[A keydown is dispatched on the currently focused element, the body element or the document element](https://w3c.github.io/uievents/#events-keyboard-event-order).
+Following this you should prefer
+
+```diff
+- fireEvent.keyDown(getByText('click me'));
++ getByText('click me').focus();
++ fireEvent.keyDown(document.activeElement || document.body);
+```
+
+This will also test that the element in question can even receive keyboard
+events.
+
+### Focus/Blur
+
+If an element is focused, a focus event is dispatched, the active element in the
+document changes, and the previously focused element is blurred. To simulate
+this behavior you can simply replace `fireEvent` with imperative focus:
+
+```diff
+- fireEvent.focus(getByText('focus me'));
++ getByText('focus me').focus();
+```
+
+A nice side-effect of this approach is that any assertion on fired focus events
+will fail if the element is not focusable. This is especially important if you
+follow-up with a keydown event.
diff --git a/docs/guide-which-query.md b/docs/guide-which-query.md
deleted file mode 100644
index 46b360172..000000000
--- a/docs/guide-which-query.md
+++ /dev/null
@@ -1,51 +0,0 @@
----
-id: guide-which-query
-title: Which query should I use?
----
-
-## Priority
-
-Based on [the Guiding Principles](guiding-principles.md), your test should
-resemble how users interact with your code (component, page, etc.) as much as
-possible. With this in mind, we recommend this order of priority:
-
-1. **Queries Accessible to Everyone** queries that reflect the experience of
- visual/mouse users as well as those that use assistive technology
- 1. `getByLabelText`: Only really good for form fields, but this is the number
- one method a user finds those elements, so it should be your top
- preference.
- 1. `getByPlaceholderText`:
- [A placeholder is not a substitute for a label](https://www.nngroup.com/articles/form-design-placeholders/).
- But if that's all you have, then it's better than alternatives.
- 1. `getByText`: Not useful for forms, but this is the number 1 method a user
- finds other elements (like buttons to click), so it should be your top
- preference for non-form elements.
- 1. `getByDisplayValue`: The current value of a form element can be useful
- when navigating a page with filled-in values.
-1. **Semantic Queries** HTML5 and ARIA compliant selectors. Note that the user
- experience of interacting with these attributes varies greatly across
- browsers and assistive technology.
- 1. `getByAltText`: If your element is one which supports `alt` text (`img`,
- `area`, and `input`), then you can use this to find that element.
- 1. `getByTitle`: The title attribute is not consistently read by
- screenreaders, and is not visible by default for sighted users
- 1. `getByRole`: This can be used to select dialog boxes and other
- difficult-to-capture elements in a more semantic way
-1. **Test IDs**
- 1. `getByTestId`: The user cannot see (or hear) these, so this is only
- recommended for cases where you can't match by text or it doesn't make
- sense (the text is dynamic).
-
-## Manual Queries
-
-On top of the queries provided by the testing library, you can use the regular
-[`querySelector` DOM API](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
-to query elements. Note that using this as an escape hatch to query by class or
-id is a bad practice because users can't see or identify these attributes. Use a
-testid if you have to.
-
-```jsx
-// @testing-library/react
-const { container } = render( )
-const foo = container.querySelector('[data-foo="bar"]')
-```
diff --git a/docs/guides-using-fake-timers.mdx b/docs/guides-using-fake-timers.mdx
new file mode 100644
index 000000000..bc7ab201e
--- /dev/null
+++ b/docs/guides-using-fake-timers.mdx
@@ -0,0 +1,56 @@
+---
+id: using-fake-timers
+title: Using Fake Timers
+sidebar_label: Using Fake Timers
+---
+
+In some cases, when your code uses timers (`setTimeout`, `setInterval`,
+`clearTimeout`, `clearInterval`), your tests may become unpredictable, slow and
+flaky.
+
+To solve these problems, or if you need to rely on specific timestamps in your
+code, most testing frameworks offer the option to replace the real timers in
+your tests with fake ones. This has a side effect - when using fake timers in
+your tests, _all_ of the code inside your test uses fake timers.
+
+The common pattern to setup fake timers is usually within the `beforeEach`, for
+example:
+
+```js
+// Fake timers using Jest
+beforeEach(() => {
+ jest.useFakeTimers()
+})
+```
+
+Since fake timers are mocking native timer functions, it is necessary to restore
+the timers after your test runs, just like regular mocks. This prevents fake
+timers leaking into other test cases and cleanup functions, where real timers
+are expected.
+
+For that you usually call `useRealTimers` in `afterEach`.
+
+It's important to also call `runOnlyPendingTimers` before switching to real
+timers. This will ensure you flush all the pending timers before you switch to
+real timers. If you don't progress the timers and just switch to real timers,
+the scheduled tasks won't get executed and you'll get an unexpected behavior.
+This is mostly important for 3rd parties that schedule tasks without you being
+aware of it.
+
+Here's an example of doing that using jest:
+
+```js
+// Running all pending timers and switching to real timers using Jest
+afterEach(() => {
+ jest.runOnlyPendingTimers()
+ jest.useRealTimers()
+})
+```
+
+:::note
+
+Combining fake timers with `user-event` may cause test timeouts. Refer to
+[`advanceTimers`](user-event/options.mdx#advancetimers) option to prevent this
+issue.
+
+:::
diff --git a/docs/guiding-principles.md b/docs/guiding-principles.mdx
similarity index 98%
rename from docs/guiding-principles.md
rename to docs/guiding-principles.mdx
index 0c6cef950..1825f345b 100644
--- a/docs/guiding-principles.md
+++ b/docs/guiding-principles.mdx
@@ -13,7 +13,7 @@ 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, then it should deal with DOM nodes
+1. If it relates to rendering components, then it should deal with DOM nodes
rather than component instances, and it should not encourage dealing with
component instances.
2. It should be generally useful for testing the application components in the
diff --git a/docs/intro.md b/docs/intro.mdx
similarity index 57%
rename from docs/intro.md
rename to docs/intro.mdx
index ee02e4ff7..7bf8ffb23 100644
--- a/docs/intro.md
+++ b/docs/intro.mdx
@@ -1,12 +1,13 @@
---
-id: intro
+id: introduction
title: Introduction
+slug: /
---
-The [`@testing-library`][npm] family of packages helps you test UI components in a
-user-centric way.
+The [`@testing-library`][npm] family of packages helps you test UI components in
+a user-centric way.
-> [The more your tests resemble the way your software is used, the more confidence they can give you.](guiding-principles.md)
+> [The more your tests resemble the way your software is used, the more confidence they can give you.](guiding-principles.mdx)
## The problem
@@ -16,7 +17,7 @@ tests to avoid including implementation details 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 solution
The core library, [`DOM Testing Library`][dom], is a light-weight solution for
testing web pages by querying and interacting with DOM nodes (whether simulated
@@ -29,7 +30,7 @@ it.
The core library has been wrapped to provide ergonomic APIs for several
frameworks, including [React][react], [Angular][angular], and [Vue][vue]. There
is also a plugin to use testing-library queries for end-to-end tests in
-[Cypress][cypress] and an implementation for [React Native][native].
+[Cypress][cypress] and an implementation for [React Native][react-native].
### What this library is not
@@ -37,14 +38,30 @@ is also a plugin to use testing-library queries for end-to-end tests in
2. Specific to a testing framework
`DOM Testing Library` works with any environment that provides DOM APIs, such as
-Jest, Mocha + JSDOM, or a real browser
+Jest, Mocha + JSDOM, or a real browser.
+
+### What you should avoid with Testing Library
+
+Testing Library encourages you to avoid testing
+[implementation details](https://kentcdodds.com/blog/testing-implementation-details)
+like the internals of a component you're testing (though it's still possible).
+[The Guiding Principles](/docs/guiding-principles) of this library emphasize a
+focus on tests that closely resemble how your web pages are interacted by the
+users.
+
+You may want to avoid the following implementation details:
+
+1. Internal state of a component
+1. Internal methods of a component
+1. Lifecycle methods of a component
+1. Child components
[jest]: https://jestjs.io
[jsdom]: https://github.com/jsdom/jsdom
-[dom]: dom-testing-library/intro.md
-[react]: react-testing-library/intro.md
-[angular]: angular-testing-library/intro.md
-[vue]: vue-testing-library/intro.md
-[cypress]: cypress-testing-library/intro.md
-[native]: ecosystem-native-testing-library.md
+[dom]: dom-testing-library/intro.mdx
+[react]: react-testing-library/intro.mdx
+[angular]: angular-testing-library/intro.mdx
+[vue]: vue-testing-library/intro.mdx
+[cypress]: cypress-testing-library/intro.mdx
+[react-native]: react-native-testing-library/intro.mdx
[npm]: https://www.npmjs.com/org/testing-library
diff --git a/docs/learning.md b/docs/learning.mdx
similarity index 51%
rename from docs/learning.md
rename to docs/learning.mdx
index c95b1a0da..6ddeb75b1 100644
--- a/docs/learning.md
+++ b/docs/learning.mdx
@@ -8,6 +8,9 @@ sidebar_label: Learning Material
about testing JavaScript applications (See also the
[course material with many examples using React Testing Library](https://github.com/kentcdodds/react-testing-library-course))
by [Kent C. Dodds](https://github.com/kentcdodds)
+- [Kent C. Dodds's testing related blog posts are all relevant](https://kentcdodds.com/blog?q=test)
+ (especially
+ [Common mistakes with React Testing Library](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library))
- [Migrating from Enzyme shallow rendering to explicit component mocks](https://www.youtube.com/watch?v=LHUdxkThTM0&list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u)
- [Confident React](https://www.youtube.com/watch?v=qXRPHRgcXJ0&list=PLV5CVI1eNcJgNqzNwcs4UKrlJdhfDjshf)
- [Test Driven Development with React Testing Library](https://www.youtube.com/watch?v=kCR3JAR7CHE&list=PLV5CVI1eNcJgCrPH_e6d57KRUTiDZgs0u)
@@ -18,8 +21,6 @@ sidebar_label: Learning Material
by [@iwilsonq](https://github.com/iwilsonq)
- [A clear way to unit testing React JS components using Jest and React Testing Library](https://www.richardkotze.com/coding/react-testing-library-jest)
by [Richard Kotze](https://github.com/rkotze)
-- [Intro to React Testing Library](https://chrisnoring.gitbooks.io/react/content/testing/react-testing-library.html)
- by [Chris Noring](https://github.com/softchris)
- [Integration testing in React](https://medium.com/@jeffreyrussom/integration-testing-in-react-21f92a55a894)
by [Jeffrey Russom](https://github.com/qswitcher)
- [React Testing Library have fantastic testing 🐐](https://medium.com/yazanaabed/react-testing-library-have-a-fantastic-testing-198b04699237)
@@ -30,8 +31,41 @@ sidebar_label: Learning Material
- [A sample repo using React Testing Library to test a Relay Modern GraphQL app](https://github.com/zth/relay-modern-flow-jest-example)
- [Creating Readable Tests Using React Testing Library](https://medium.com/flatiron-labs/creating-readable-tests-using-react-testing-library-2bd03c49c284)
by [Lukeghenco](https://github.com/Lukeghenco)
-- [My Experience moving from Enzyme to React Testing Library](https://medium.com/@boyney123/my-experience-moving-from-enzyme-to-react-testing-library-5ac65d992ce) by [David Boyne](https://github.com/boyney123)
-- [Testing Formik with React Testing Library](https://scottsauber.com/2019/05/25/testing-formik-with-react-testing-library/) by [Scott Sauber](https://github.com/scottsauber)
-- [How to Test Asynchronous Methods](https://www.polvara.me/posts/how-to-test-asynchronous-methods/) by [Gpx](https://twitter.com/Gpx)
+- [My Experience moving from Enzyme to React Testing Library](https://medium.com/@boyney123/my-experience-moving-from-enzyme-to-react-testing-library-5ac65d992ce)
+ by [David Boyne](https://github.com/boyney123)
+- [Testing Formik with React Testing Library](https://scottsauber.com/2019/05/25/testing-formik-with-react-testing-library/)
+ by [Scott Sauber](https://github.com/scottsauber)
+- [How to Test Asynchronous Methods](https://www.polvara.me/posts/how-to-test-asynchronous-methods/)
+ by [Gpx](https://twitter.com/Gpx)
+- [Writing better tests with react-testing-library](https://www.youtube.com/watch?v=O0VxvRqgm7g)
+ by [Billy Mathews](https://twitter.com/BillRMathews)
+- [React Hooks broke my tests, now what?](https://youtu.be/p3WS9GmfX_Q) by
+ [Daniel Afonso](https://twitter.com/danieljcafonso)
+- [Testing Apollo Components Using react-testing-library](https://www.arahansen.com/testing-apollo-components-using-react-testing-library/)
+ by [Andrew Hansen](https://twitter.com/arahansen)
+- [Enzyme vs React Testing Library: A Migration Guide](https://claritydev.net/blog/enzyme-vs-react-testing-library-migration-guide)
+ by [Alex Khomenko](https://claritydev.net)
+- [Testing React Hook Form With React Testing Library](https://claritydev.net/blog/testing-react-hook-form-with-react-testing-library)
+ by [Alex Khomenko](https://claritydev.net)
+- [Improving React Testing Library Tests](https://claritydev.net/blog/improving-react-testing-library-tests)
+ by [Alex Khomenko](https://claritydev.net)
+- [Testing Angular Challenges](https://angular-challenges.vercel.app/challenges/testing/)
+ by [Thomas Laforge](https://twitter.com/laforge_toma)
+
+## Portuguese 🇧🇷
+
+- [Do Enzyme ao Testing Library](https://www.infoq.com/br/presentations/enzyme-para-react-testing-library/)
+ by [Pablo Dinella](https://github.com/PabloDinella)
+- [React Hooks broke my tests, now what? (Portuguese)](https://youtu.be/t46n7REGswQ?t=99)
+ by [Daniel Afonso](https://twitter.com/danieljcafonso)
+- [Escrevendo testes eficientes de verdade no Angular](https://andrewrosario.medium.com/escrevendo-testes-eficientes-de-verdade-no-angular-263a9d03cecf)
+ by [Andrew Rosário](https://github.com/andrewarosario)
+
+## Spanish 🇪🇸
+
+- [TESTING en REACT 🧪 ¡Aprende DESDE CERO! Con react-testing-library y Jest](https://www.youtube.com/watch?v=KYjjtRgg_H0)
+ by [Midudev](https://twitter.com/midudev)
+- [Buenas prácticas con Angular Testing Library](https://dev.to/danyparedes/buenas-practicas-con-angular-testing-library-59lp)
+ by [Danywalls](https://twitter.com/danywalls)
Feel free to contribute more!
diff --git a/docs/marko-testing-library/api.mdx b/docs/marko-testing-library/api.mdx
new file mode 100644
index 000000000..0a3e91044
--- /dev/null
+++ b/docs/marko-testing-library/api.mdx
@@ -0,0 +1,302 @@
+---
+id: api
+title: API
+---
+
+`Marko Testing Library` re-exports everything from `DOM Testing Library` as well
+as these methods:
+
+- [`render`](#render)
+ - [`render` Options](#render-options)
+ - [`container`](#container)
+- [`render` Result](#render-result)
+ - [`...queries`](#queries)
+ - [`debug`](#debug)
+ - [`rerender`](#rerender)
+ - [`emitted`](#emitted)
+ - [`cleanup`](#cleanup)
+ - [`container`](#container-1)
+- [`cleanup`](#cleanup-1)
+
+---
+
+## `render`
+
+```javascript
+function render(
+ template, // A Marko template to render
+ input, // Input for the above template
+ options // You won't often use this, expand below for docs on options
+)
+```
+
+Render into a container which is appended to `document.body`.
+
+```javascript
+import {render} from '@marko/testing-library'
+import MyTemplate from './my-template.marko'
+
+render(MyTemplate)
+```
+
+```javascript
+import {render, screen} from '@marko/testing-library'
+import Greeting from './greeting.marko'
+
+test('renders a message', async () => {
+ const {container} = await render(Greeting, {name: 'Marko'})
+ expect(screen.getByText(/Marko/)).toBeInTheDocument()
+ expect(container.firstChild).toMatchInlineSnapshot(`
+ Hello, Marko!
+ `)
+})
+```
+
+### `render` Options
+
+You won't often need to specify options, but if you ever do, here are the
+available options which you can provide as the third argument to `render`.
+
+#### `container`
+
+By default for client-side tests, `Marko Testing Library` will create a `div`
+and append that `div` to the `document.body` and this is where your component
+will be rendered. If you provide your own HTMLElement `container` via this
+option, it will not be appended to the `document.body` automatically.
+
+For example: If you are unit testing a `tablebody` element, it cannot be a child
+of a `div`. In this case, you can specify a `table` as the render `container`.
+
+```javascript
+const table = document.createElement('table')
+
+const {container} = await render(MyTableBody, null, {
+ container: document.body.appendChild(table),
+})
+```
+
+## `render` Result
+
+The `render` method returns a promise which resolves with an object that has a
+few properties:
+
+### `...queries`
+
+The most important feature of `render` is that the queries from
+[the Core API](queries/about.mdx) are automatically returned with their first
+argument bound to the results of rendering your component.
+
+See [Queries](queries/about.mdx#types-of-queries) for a complete list.
+
+**Example**
+
+```javascript
+const {getByLabelText, queryAllByTestId} = await render(MyTemplate)
+```
+
+Alternatively, you can use the
+[top-level `screen` method](queries/about.mdx#screen) to query into all
+currently rendered components in the `document.body`, eg:
+
+```javascript
+import { render, screen } from "@marko/testing-library"
+
+await render(MyTemplate)
+const el = screen.getByText(...)
+```
+
+### `debug`
+
+This method is a shortcut for logging the `prettyDOM` for all children inside of
+the `container`.
+
+```javascript
+import {render} from '@marko/testing-library'
+import Greeting from './greeting.marko'
+
+const {debug} = await render(Greeting, {name: 'World'})
+debug()
+
+// Hello World
+// you can also pass an element: debug(getByTestId('messages'))
+```
+
+This is a simple wrapper around `prettyDOM` which is also exposed and comes from
+[`DOM Testing Library`](dom-testing-library/api-debugging.mdx#prettydom).
+
+### `rerender`
+
+A Marko components `input` can change at any time from a parent component.
+Although often this input is passed through your component declaratively,
+sometimes it is necessary to ensure that your components react appropriately to
+new data. You can simulate your component receiving new `input` by passing new
+data to the `rerender` helper.
+
+```javascript
+import {render} from '@marko/testing-library'
+import Greeting from './greeting.marko'
+
+const {rerender, debug} = await render(Greeting, {name: 'World'})
+
+// re-render the same component with different props
+await rerender({name: 'Marko'})
+
+debug()
+// Hello Marko
+```
+
+### `emitted`
+
+Marko components also communicate with their parents through events. It is
+recommended to also test that your components emit the right events at the right
+time.
+
+The `emitted` helper does just that. Calling the helper will return all emitted
+events since the last call to the helper. You can also pass in an event type to
+filter the results.
+
+```javascript
+import {render, fireEvent} from '@marko/testing-library'
+import Counter from './counter.marko'
+
+const {getByText, emitted} = await render(Counter)
+
+const button = getByText('Increment')
+
+await fireEvent.click(button)
+await fireEvent.click(button)
+
+// Assuming the `Counter` component forwards these button clicks as `increment` events
+expect(emitted('increment')).toHaveProperty('length', 2)
+
+await fireEvent.click(button)
+
+// Note: the tracked events are cleared every time you read them.
+// Below we are snapshoting the events after our last assertion,
+// the return value will include an array with all of the arguments for each increment event.
+expect(emitted('increment')).toMatchInlineSnapshot(`
+ Array [
+ Array [
+ Object {
+ "count": 3,
+ },
+ ],
+ ]
+`)
+
+// Without an event type will give you all events with their type and arguments.
+expect(emitted()).toMatchInlineSnapshot(`
+ Array [
+ Object {
+ "args": Array [
+ Object {
+ "count": 0,
+ },
+ ],
+ "type": "increment",
+ },
+ Object {
+ "args": Array [
+ Object {
+ "count": 1,
+ },
+ ],
+ "type": "increment",
+ },
+ Object {
+ "args": Array [
+ Object {
+ "count": 3,
+ },
+ ],
+ "type": "increment",
+ }
+ ]
+ `)
+```
+
+### `cleanup`
+
+Like the [top-level cleanup method](#cleanup-1), this allows you to remove and
+destroy the currently rendered component before the test has been completed.
+
+This can be useful to validate that a component properly cleans up any DOM
+mutations once it has been destroyed.
+
+```javascript
+import {render, screen, getRoles} from '@marko/testing-library'
+import Main from './main.marko'
+import Dialog from './dialog.marko'
+
+await render(Main)
+
+const main = screen.getByRole('main')
+expect(main).not.toHaveAttribute('aria-hidden')
+
+const {cleanup} = await render(Dialog)
+expect(main).toHaveAttribute('aria-hidden') // assert added attribute
+
+cleanup() // destroy the dialog
+
+expect(main).not.toHaveAttribute('aria-hidden') // assert attribute removed
+```
+
+### `container`
+
+The containing DOM node of your rendered Marko Component. For server-side tests
+this is a [JSDOM.fragment](https://github.com/jsdom/jsdom#fragment), and for
+client-side tests this will be whatever is passed as the `container` render
+option.
+
+> Tip: To get the root element of your rendered element, use
+> `container.firstChild`.
+
+> 🚨 If you find yourself using `container` to query for rendered elements then
+> you should reconsider! The other queries are designed to be more resilient to
+> changes that will be made to the component you're testing. Avoid using
+> `container` to query for elements!
+
+## `fireEvent`
+
+Because Marko batches DOM updates to avoid unnecessary re-renders, the
+[fireEvent](dom-testing-library/api-events.mdx) helpers are re-exported as
+`async` functions. Awaiting this ensures that the DOM has properly updated in
+response to the event triggered in the test.
+
+```js
+await fireEvent.click(getByText('Click me'))
+```
+
+## `cleanup`
+
+With client-side tests your components are rendered into a placeholder
+HTMLElement. To ensure that your components are properly removed, and destroyed,
+after each test the `cleanup` method is called for you automatically by hooking
+into `afterEach` in supported test frameworks. You can also manually call
+`cleanup` at any time which will remove all attached components.
+
+```javascript
+import {render, cleanup, screen} from '@marko/testing-library'
+import Greeting from './greeting.marko'
+
+await render(Greeting, {name: 'Marko'})
+
+expect(screen.getByText(/Marko/)).toBeInTheDocument()
+
+// manually cleanup the component before the test is finished
+cleanup()
+expect(screen.queryByText(/Marko/)).toBeNull()
+```
+
+You can turn off the automatic test cleanup by importing the following module:
+
+```javascript
+import '@marko/testing-library/dont-cleanup-after-each'
+```
+
+With mocha you can use
+`mocha --require @marko/testing-library/dont-cleanup-after-each` as a shorthand.
+
+If you are using Jest, you can include
+`setupFilesAfterEnv: ["@marko/testing-library/dont-cleanup-after-each"]` in your
+Jest config to avoid doing this in each file.
diff --git a/docs/marko-testing-library/intro.mdx b/docs/marko-testing-library/intro.mdx
new file mode 100644
index 000000000..6ab36a3eb
--- /dev/null
+++ b/docs/marko-testing-library/intro.mdx
@@ -0,0 +1,54 @@
+---
+id: intro
+title: Marko Testing Library
+sidebar_label: Introduction
+---
+
+[`Marko Testing Library`](https://github.com/marko-js/testing-library) builds on
+top of [`DOM Testing Library`](dom-testing-library/intro.mdx) by adding APIs for
+working with Marko components.
+
+```bash npm2yarn
+npm install --save-dev @marko/testing-library
+```
+
+## The problem
+
+You want to write maintainable tests for your Marko 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 test suite 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 `@marko/testing-library` is a very lightweight solution for testing Marko
+components. It provides light utility functions on top of
+[`@testing-library/dom`](dom-testing-library/intro.mdx) 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-principles.mdx)
+
+So rather than dealing with instances of rendered Marko components, your tests
+will work with actual DOM nodes. The utilities this library provides facilitate
+querying the DOM in the same way the user would. Finding for elements by their
+label text (just like a user would), finding links and buttons from their text
+(like a user would). It contains a small targeted API and can get out of your
+way if needed with some built-in escape hatches.
+
+This library encourages your applications to be more accessible and allows you
+to get your tests closer to using your components the way a user will, which
+allows your tests to give you more confidence that your application will work
+when a real user uses it.
+
+**What this library is not**:
+
+1. A test runner or framework
+2. Specific to a testing framework, you can [use it with Jest](setup.mdx#jest),
+ [mocha](setup.mdx#mocha), or other test runners.
+
+> NOTE: This library is built on top of
+> [`DOM Testing Library`](dom-testing-library/intro.mdx) which is where most of
+> the logic behind the queries is.
diff --git a/docs/marko-testing-library/setup.mdx b/docs/marko-testing-library/setup.mdx
new file mode 100644
index 000000000..37b74e8dc
--- /dev/null
+++ b/docs/marko-testing-library/setup.mdx
@@ -0,0 +1,93 @@
+---
+id: setup
+title: Setup
+sidebar_label: Setup
+---
+
+`Marko Testing Library` is not dependent on any test runner. However, it is
+dependent on the test environment. This package works for testing both server
+side, and client-side Marko templates and provides a slightly different
+implementation optimized for each. This is done using a
+[browser shim](https://github.com/defunctzombie/package-browser-field-spec),
+just like in Marko.
+
+The [browser shim](https://github.com/defunctzombie/package-browser-field-spec)
+is picked up by many tools, including all bundlers and some test runners.
+
+Below is some example configurations to test both server and browser components
+with some popular test runners.
+
+### [Jest](http://jestjs.io)
+
+For Jest to understand Marko templates you must first
+[install the @marko/jest preset](https://github.com/marko-js/jest#installation).
+This allows your Marko templates to be imported into your tests.
+
+In Jest there is a
+[browser option](https://jestjs.io/docs/en/configuration#browser-boolean) which
+will tell Jest to resolve the
+[browser shim](https://github.com/defunctzombie/package-browser-field-spec)
+version of all modules as mentioned above.
+
+To test components rendered on the client-side, be sure to enable both the
+`browser` option and the preset and you are good to go!
+
+```js title="jest.config.js"
+module.exports = {
+ preset: '@marko/jest/preset/browser',
+}
+```
+
+For testing components rendered on the server-side we can instead use
+`@marko/jest/preset/node` as our jest preset.
+
+```js title="jest.config.js"
+module.exports = {
+ preset: '@marko/jest/preset/node',
+}
+```
+
+A Jest configuration can also have multiple
+[projects](https://jestjs.io/docs/en/configuration#projects-array-string-projectconfig)
+which we can use to create a combined configuration for server-side tests, and
+client-side tests, like so:
+
+```js title="jest.config.js"
+module.exports = {
+ projects: [
+ {
+ displayName: 'server',
+ preset: '@marko/jest/preset/node',
+ testRegex: '/__tests__/[^.]+\\.server\\.js$',
+ },
+ {
+ displayName: 'browser',
+ preset: '@marko/jest/preset/browser',
+ testRegex: '/__tests__/[^.]+\\.browser\\.js$',
+ },
+ ],
+}
+```
+
+### [Mocha](https://mochajs.org)
+
+Mocha also works great for testing Marko components. Mocha, however, has no
+understanding of
+[browser shims](https://github.com/defunctzombie/package-browser-field-spec)
+which means out of the box it can only work with server-side Marko components.
+
+To run server-side Marko tests with `mocha` you can simply run the following
+command:
+
+```console
+mocha --require marko/node-require
+```
+
+This enables the
+[Marko require hook](https://markojs.com/docs/installing/#require-marko-views)
+and allows you to require server-side Marko templates directly in your tests.
+
+For client-side testing of your components with Mocha often you will use a
+bundler to build your tests (this will properly resolve the browser shims
+mentioned above) and then you can load these tests in some kind of browser
+context.
diff --git a/docs/native-testing-library/api.md b/docs/native-testing-library/api.md
deleted file mode 100644
index 20bf4ab8a..000000000
--- a/docs/native-testing-library/api.md
+++ /dev/null
@@ -1,171 +0,0 @@
----
-id: api
-title: API
-sidebar_label: API
----
-
-Here you'll find a high level summary of the most frequently used parts of
-Native Testing Library. For a more comprehensive look at what's possible, check
-out the [main docs site](https://native-testing-library.com).
-
-- [`render`](#render)
-- [`act`](#act)
-
-## `render`
-
-```typescript
-function render(
- ui: React.ReactElement,
- options?: {
- /* You won't often use this, expand below for docs on options */
- }
-): RenderResult
-```
-
-Create a `ReactTestRenderer` Instance.
-
-```jsx
-import { render } from '@testing-library/react-native'
-
-render( )
-```
-
-```javascript
-import { render } from '@testing-library/react-native'
-
-test('renders a message', () => {
- const { container, getByText } = render(Hello, World! )
- expect(getByText('Hello, world!')).toBeTruthy()
- expect(container.toJSON()).toMatchInlineSnapshot(`
- Hello, World!
- `)
-})
-```
-
-## `render` Options
-
-Most of the time you won't need to pass any options to `render`, but when you
-do, you will pass them as the second parameter. There are some important key
-differences between this and `React Testing Library` that you will want to be
-aware of.
-
-### `wrapper`
-
-Pass a React Component as the `wrapper` option to have it rendered around the
-inner element. This is most useful for creating reusable custom render functions
-for common data providers. See [setup](setup.md#custom-render) for examples.
-
-### `queries`
-
-Queries to bind. Overrides the default set from `Native Testing Library` unless
-merged.
-
-```js
-// Example, a function to traverse table contents
-import * as tableQueries from 'my-table-query-libary'
-import queries from '@testing-library/react-native'
-
-const { getByRowColumn, getByText } = render( , {
- queries: { ...queries, ...tableQueries },
-})
-```
-
-See [helpers](https://www.native-testing-library.com/docs/api-helpers) for
-guidance on using utility functions to create custom queries.
-
-Custom queries can also be added globally by following the
-[custom render guide](setup.md#custom-render).
-
-## `render` Result
-
-The `render` method returns an object that has a few properties:
-
-### `...queries`
-
-The most important feature of `render` is that the
-[default queries](https://www.native-testing-library.com/docs/api-queries) are
-automatically returned with their first argument bound to the `baseElement`.
-
-**Example**
-
-```javascript
-const { getByText, getByTestId /* etc */ } = render( )
-```
-
-### `container`
-
-The `ReactTestRendererInstance` result from your render. This has helpful
-methods like `toTree()` and `toJSON()`.
-
-> You should rarely use the container. There are very few instances where you
-> need to access the container itself to do something you'd need to in a test.
-
-### `baseElement`
-
-This is the root element of your render result. By default, this is what all of
-your queries will be run on, and you could also use it to do any custom
-searching logic you wanted to..
-
-### `debug`
-
-This method is a shortcut for `console.log(prettyPrint(container.toJSON()))`.
-
-```javascript
-import { render } from '@testing-library/react-native'
-
-const { debug } = render(
-
- Hello World
-
-)
-debug()
-//
-//
-// Hello World
-//
-//
-```
-
-This is a simple wrapper around `prettyPrint` which is also exported.
-
-### `rerender`
-
-Although its likely better to test updating your props the way a user would
-(through events and interaction), this method will allow you to re-render your
-entire tree at the base with new props.
-
-```jsx
-import { render } from '@testing-library/react-native'
-
-const { rerender } = render( )
-
-// re-render the same component with different props
-rerender( )
-```
-
-[See the examples page](example-update-props.md)
-
-### `unmount`
-
-This will cause the rendered component to be unmounted. This is useful for
-testing what happens when your component is removed from the page (like testing
-that you don't leave event handlers hanging around causing memory leaks). Note
-that you don't need to call this `afterEach` like you do in react testing
-library because these elements aren't being added to a DOM. Use it only as
-necessary.
-
-> This method is a wrapper around ReactTestRenderer.unmount()
-
-```javascript
-import { render } from '@testing-library/react-native'
-
-const { unmount } = render( )
-unmount()
-```
-
-## `act`
-
-This is a light wrapper around the
-[`react-test-renderer` `act` function](https://reactjs.org/docs/test-renderer.html).
-All it does is forward all arguments to the act function if your version of
-react supports `act`.
diff --git a/docs/native-testing-library/cheatsheet.md b/docs/native-testing-library/cheatsheet.md
deleted file mode 100644
index 1627524bf..000000000
--- a/docs/native-testing-library/cheatsheet.md
+++ /dev/null
@@ -1,15 +0,0 @@
----
-id: cheat-sheet
-title: Printable Cheat Sheet
-sidebar_label: Cheat Sheet
----
-
-There is a printable one-page cheat sheet available for you to download. It is
-intended to be a quick reference for `Native Testing Library`, but is not a
-complete API glossary or guide. Keep a copy of it on your desk to quickly take a
-peek at the most commonly used functionality!
-
-[Download the cheat sheet][cheatsheet]
-
-[cheatsheet]:
- https://github.com/testing-library/native-testing-library/raw/master/other/cheat-sheet.pdf
diff --git a/docs/native-testing-library/intro.md b/docs/native-testing-library/intro.md
deleted file mode 100644
index a7b3dcba0..000000000
--- a/docs/native-testing-library/intro.md
+++ /dev/null
@@ -1,74 +0,0 @@
----
-id: intro
-title: Introduction
-sidebar_label: Intro
----
-
-Native Testing Library is a testing library for **React Native** inspired by
-`DOM Testing Library`. Because React Native does not run in a browser
-environment, the core queries are implemented independently, unlike other
-wrappers that use `DOM Testing Library` as the base. You'll find much more
-information about the library, including examples, on the project sites:
-
-- [Docs][docs]
-- [Docs repo][docs-repo]
-- [Project repo][gh]
-
-## Quickstart
-
-```
-npm install --save-dev @testing-library/react-native
-```
-
-- [Native Testing Library on GitHub][gh]
-
-[docs]: https://native-testing-library.com
-[gh]: https://github.com/testing-library/native-testing-library
-[docs-repo]: https://github.com/testing-library/native-testing-library-docs
-
-## The problem
-
-You want to write maintainable tests for your React Native application. You love
-testing library, and you want to be able to write maintainable tests for your
-React Native application also. You don't want to use a library that renders
-components to a fake DOM, and you've had a hard time finding what you need to
-write tests using that philosophy in React Native.
-
-## This solution
-
-`Native Testing Library` is an implementation of the well-known
-`testing-library` API that works for React Native. The primary goal is to mimic
-the testing library API as closely as possible while still accounting for the
-differences in the platforms. Accomplishing this is no small feat because of the
-differences between the two platforms. Although most framework implementations
-like `React Testing Library` are thin layers over `DOM Testing Library`, this
-library needed to have its own base implementation as well as a user-facing API.
-This library uses the
-[react-test-renderer](https://reactjs.org/docs/test-renderer.html), whereas
-`DOM Testing Library` uses ReactDOM and JSDOM. The main philosophy is that you
-should find elements that are on the "screen" the way users would. This approach
-is meant to give you confidence that your app is working as a cohesive unit.
-Just like the `DOM Testing Library`, `Native 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-principles.md)
-
-This library gives you the tools you need to find things in your application the
-way your users would. You can do things like find elements by text, input value,
-and accessibility labels -- the types of things your users can see. We also give
-you the ability to search for elements by `testID`, but you should consider it a
-last resort because users can't see a testID and it can introduce unpredictable
-behavior in your tests.
-
-It will also encourage you to build more accessible apps. For example, your
-users can't see your icon touchable that doesn't have an accessibility label, so
-neither can your tests. We believe that writing good tests using this library
-will force you to give more consideration to whether what you're doing is an
-accessible experience for all of your users.
-
-This library should, in theory, work with any any testing framework like Jest or
-Mocha, but we do recommend you use Jest with the `react-native` Jest preset. We
-have not tested it in any other environment, and likely won't be able to support
-usage with another library.
-
-[jest]: https://jestjs.io
diff --git a/docs/nightwatch-testing-library/intro.mdx b/docs/nightwatch-testing-library/intro.mdx
new file mode 100644
index 000000000..4a9ceaf69
--- /dev/null
+++ b/docs/nightwatch-testing-library/intro.mdx
@@ -0,0 +1,140 @@
+---
+id: intro
+title: Nightwatch Testing Library
+---
+
+[`nightwatch-testing-library`][gh] allows the use of dom-testing-library queries
+in [Nightwatch](https://nightwatchjs.org) for end-to-end web testing.
+
+## Install
+
+> Be sure to follow the
+> [Nightwatch install & config instructions first](https://nightwatchjs.org/gettingstarted/installation/)
+
+then just
+
+```bash npm2yarn
+npm install --save-dev @testing-library/nightwatch
+```
+
+- [nightwatch-testing-library on GitHub][gh]
+
+## READ THIS FIRST
+
+At it's core, `nightwatch-testing-library` translates between
+dom-testing-library queries and css selectors. This is due to the fact that
+Nightwatch adheres to the WebDriver standards for
+[locator strategies](https://www.w3.org/TR/webdriver/#locator-strategies). For
+now, this means that the logging will have some very detailed css paths. PRs
+welcome for a
+[custom reporter](https://nightwatchjs.org/guide/extending-nightwatch/#custom-reporter)
+to solve this problem 🤗.
+
+**So remember, the results from NWTL queries are WebDriver locators, not DOM
+nodes.**
+
+> Note, in NWTL, all queries must be `await`ed.
+
+## Usage
+
+```javascript
+const {getQueriesFrom} = require('@testing-library/nightwatch')
+
+module.exports = {
+ beforeEach(browser, done) {
+ browser.url('http://localhost:13370/')
+ done()
+ },
+
+ async getByLabelText(browser) {
+ const {getByLabelText} = getQueriesFrom(browser)
+
+ const input = await getByLabelText('Label Text')
+ browser.setValue(input, '@TL FTW')
+
+ browser.expect.element(input).value.to.equal('@TL FTW')
+ },
+
+ async getByAltText(browser) {
+ const {getByAltText} = getQueriesFrom(browser)
+ const image = await getByAltText('Image Alt Text')
+
+ browser.click(image)
+ browser.expect
+ .element(image)
+ .to.have.css('border')
+ .which.equals('5px solid rgb(255, 0, 0)')
+ },
+}
+```
+
+### `AllBy` queries
+
+The results of `AllBy` queries have an additional function added to them: `nth`
+which can be used in nightwatch functions as well as in the `within` function of
+NWTL.
+
+```javascript
+
+ async 'getAllByText - regex'(browser) {
+ const { getAllByText } = getQueriesFrom(browser);
+ const chans = await getAllByText(/Jackie Chan/)
+
+
+ browser.expect.elements(chans).count.to.equal(2)
+
+ const firstChan = chans.nth(0);
+ const secondChan = chans.nth(1);
+
+
+ browser.click(chans.nth(0));
+ browser.click(chans.nth(1));
+
+ browser.expect.element(secondChan).text.to.equal('Jackie Kicked');
+ browser.expect.element(firstChan).text.to.equal('Jackie Kicked');
+
+ },
+
+```
+
+## Configure
+
+You can customize the testIdAttribute using the `configure` function just like
+[dom-testing-library][config]:
+
+```javascript
+const {configure} = require('@testing-library/nightwatch')
+
+configure({testIdAttribute: 'data-automation-id'})
+```
+
+## Containers
+
+By default the queries come pre-bound to `document.body`, so no need to provide
+a container. However, if you want to restrict your query using a container, you
+can use `within`.
+
+### Examples using `within`
+
+```javascript
+const {getQueriesFrom, within} = require('@testing-library/nightwatch')
+
+module.exports = {
+ beforeEach(browser, done) {
+ browser.url('http://localhost:13370/')
+ done()
+ },
+ async 'getByText within container'(browser) {
+ const {getByTestId} = getQueriesFrom(browser)
+
+ const nested = await getByTestId('nested')
+ const button = await within(nested).getByText('Button Text')
+
+ browser.click(button)
+ browser.expect.element(button).text.to.equal('Button Clicked')
+ },
+}
+```
+
+[config]: ../dom-testing-library/api-configuration.mdx
+[gh]: https://github.com/testing-library/nightwatch-testing-library
diff --git a/docs/pptr-testing-library/intro.md b/docs/pptr-testing-library/intro.mdx
similarity index 88%
rename from docs/pptr-testing-library/intro.md
rename to docs/pptr-testing-library/intro.mdx
index b3956eedb..ba8ea2562 100644
--- a/docs/pptr-testing-library/intro.md
+++ b/docs/pptr-testing-library/intro.mdx
@@ -6,7 +6,7 @@ title: Puppeteer Testing Library
[`pptr-testing-library`][gh] is a lightweight adapter allowing
`DOM Testing Library` to be used with [`puppeteer`][ghpuppeteer].
-```
+```bash npm2yarn
npm install --save-dev puppeteer pptr-testing-library
```
@@ -16,9 +16,9 @@ npm install --save-dev puppeteer pptr-testing-library
```js
const puppeteer = require('puppeteer')
-const { getDocument, queries, wait } = require('pptr-testing-library')
+const {getDocument, queries, waitFor} = require('pptr-testing-library')
-const { getByTestId, getByLabelText } = queries
+const {getByTestId, getByLabelText} = queries
const browser = await puppeteer.launch()
const page = await browser.newPage()
@@ -32,7 +32,7 @@ const $email = await getByLabelText($form, 'Email')
// interact with puppeteer like usual
await $email.type('pptr@example.com')
// waiting works too!
-await wait(() => getByText('Loading...'))
+await waitFor(() => getByText('Loading...'))
```
A little too un-puppeteer for you? You can attach all the `DOM Testing Library`
@@ -49,8 +49,8 @@ const page = await browser.newPage()
const $document = await page.getDocument()
// query methods are added directly to prototype of ElementHandle
const $form = await $document.getByTestId('my-form')
-// destructing works if you explicitly call getQueriesForElement
-const { getByLabelText } = $form.getQueriesForElement()
+// destructuring works if you explicitly call getQueriesForElement
+const {getByLabelText} = $form.getQueriesForElement()
// ...
const $email = await getByLabelText('Email')
```
diff --git a/docs/preact-testing-library/api.mdx b/docs/preact-testing-library/api.mdx
new file mode 100644
index 000000000..18dd43131
--- /dev/null
+++ b/docs/preact-testing-library/api.mdx
@@ -0,0 +1,163 @@
+---
+id: api
+title: API
+sidebar_label: API
+---
+
+- [`@testing-library/dom`](#testing-library-dom)
+- [`render`](#render)
+- [`cleanup`](#cleanup)
+- [`act`](#act)
+- [`fireEvent`](#fireevent)
+
+---
+
+## `@testing-library/dom`
+
+This library re-exports everything from the DOM Testing Library
+(`@testing-library/dom`). See the [documentation](queries/about.mdx) to see what
+goodies you can use.
+
+## `render`
+
+```jsx
+import {render} from '@testing-library/preact'
+
+const {results} = render( , {options})
+```
+
+### Options
+
+| Option | Description | Default |
+| ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- |
+| `container` | The HTML element the component is mounted to. | baseElement |
+| `baseElement` | The root HTML element to which the container is appended to. | document.body |
+| `queries` | Queries to bind to the baseElement. See [within](dom-testing-library/api-within.mdx). | null |
+| `hydrate` | Used when the component has already been mounted and requires a rerender. Not needed for most people. The rerender function passed back to you does this already. | false |
+| `wrapper` | A parent component to wrap YourComponent. | null |
+
+### Results
+
+| Result | Description |
+| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `container` | The HTML element the component is mounted to. |
+| `baseElement` | The root HTML element to which the container is appended to. |
+| `debug` | Logs the baseElement using [prettyDom](dom-testing-library/api-debugging.mdx#prettydom). |
+| `unmount` | Unmounts the component from the container. |
+| `rerender` | Calls render again passing in the original arguments and sets hydrate to true. |
+| `asFragment` | Returns the innerHTML of the container. |
+| `...queries` | Returns all [query functions](queries/about.mdx) to be used on the baseElement. If you pass in `query` arguments than this will be those, otherwise all. |
+
+## `cleanup`
+
+Unmounts the component from the container and destroys the container.
+
+> This is called automatically if your testing framework (such as mocha, Jest or
+> Jasmine) injects a global `afterEach()` function into the testing environment.
+> If not, you will need to call `cleanup()` after each test.
+
+If you'd like to disable this then set `process.env.PTL_SKIP_AUTO_CLEANUP` to
+true when running your tests.
+
+```jsx
+import {render, cleanup} from '@testing-library/preact'
+
+afterEach(() => {
+ cleanup()
+}) // Default on import: runs it after each test.
+
+render( )
+
+cleanup() // Or like this for more control.
+```
+
+## `act`
+
+Just a convenience export that points to preact/test-utils/act. All renders and
+events being fired are wrapped in `act`, so you don't really need this. It's
+responsible for flushing all effects and rerenders after invoking it.
+
+📝 If you'd love to learn more, checkout
+[this explanation](https://github.com/threepointone/react-act-examples/blob/master/sync.md).
+Even though it's for React, it gives you an idea of why it's needed.
+
+## `fireEvent`
+
+Passes it to the @testing-library/dom
+[fireEvent](dom-testing-library/api-events.mdx). It's also wrapped in `act` so
+you don't need to worry about doing it.
+
+📝 Keep in mind mainly when using `h / Preact.createElement` that React uses a
+Synthetic event system, and Preact uses the browser's native `addEventListener`
+for event handling. This means events like `onChange` and `onDoubleClick` in
+React, are `onInput` and `onDblClick` in Preact. The double click example will
+give you an idea of how to test using those functions.
+
+### Example 1
+
+```jsx
+const cb = jest.fn()
+
+function Counter() {
+ useEffect(cb)
+
+ const [count, setCount] = useState(0)
+
+ return setCount(count + 1)}>{count}
+}
+
+const {
+ container: {firstChild: buttonNode},
+} = render( )
+
+// Clear the first call to useEffect that the initial render triggers.
+cb.mockClear()
+
+// Fire event Option 1.
+fireEvent.click(buttonNode)
+
+// Fire event Option 2.
+fireEvent(
+ buttonNode,
+ new MouseEvent('click', {
+ bubbles: true,
+ cancelable: true,
+ button: 0,
+ }),
+)
+
+expect(buttonNode).toHaveTextContent('1')
+expect(cb).toHaveBeenCalledTimes(1)
+```
+
+### Example 2
+
+```jsx
+const handler = jest.fn()
+
+const {
+ container: {firstChild: input},
+} = render( )
+
+fireEvent.input(input, {target: {value: 'a'}})
+
+expect(handler).toHaveBeenCalledTimes(1)
+```
+
+### Example 3
+
+```jsx
+const ref = createRef()
+const spy = jest.fn()
+
+render(
+ h(elementType, {
+ onDblClick: spy,
+ ref,
+ }),
+)
+
+fireEvent['onDblClick'](ref.current)
+
+expect(spy).toHaveBeenCalledTimes(1)
+```
diff --git a/docs/preact-testing-library/example.mdx b/docs/preact-testing-library/example.mdx
new file mode 100644
index 000000000..609f26aca
--- /dev/null
+++ b/docs/preact-testing-library/example.mdx
@@ -0,0 +1,60 @@
+---
+id: example
+title: Example
+sidebar_label: Example
+---
+
+> Preact Testing Library works with both Preact Hooks and Classes. Your tests
+> will be the same however you write your components.
+
+## Component
+
+```jsx
+function HiddenMessage({children}) {
+ const [showMessage, setShowMessage] = useState(false)
+
+ return (
+
+ Show Message
+ setShowMessage(e.target.checked)}
+ checked={showMessage}
+ />
+ {showMessage ? children : null}
+
+ )
+}
+```
+
+## Test
+
+```jsx
+// NOTE: jest-dom adds handy assertions to Jest and it is recommended, but not required.
+import '@testing-library/jest-dom'
+
+import {h} from 'preact'
+import {render, fireEvent} from '@testing-library/preact'
+
+import HiddenMessage from '../hidden-message'
+
+test('shows the children when the checkbox is checked', () => {
+ const testMessage = 'Test Message'
+
+ const {queryByText, getByLabelText, getByText} = render(
+ {testMessage} ,
+ )
+
+ // 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(queryByText(testMessage)).toBeNull()
+
+ // The queries can accept a regex to make your selectors more resilient to content tweaks and changes.
+ fireEvent.click(getByLabelText(/show/i))
+
+ // .toBeInTheDocument() is an assertion that comes from jest-dom.
+ // Otherwise you could use .toBeDefined().
+ expect(getByText(testMessage)).toBeInTheDocument()
+})
+```
diff --git a/docs/preact-testing-library/intro.mdx b/docs/preact-testing-library/intro.mdx
new file mode 100644
index 000000000..dfda61344
--- /dev/null
+++ b/docs/preact-testing-library/intro.mdx
@@ -0,0 +1,46 @@
+---
+id: intro
+title: Intro
+sidebar_label: Introduction
+---
+
+[Preact Testing Library on GitHub][gh]
+
+[gh]: https://github.com/testing-library/preact-testing-library
+
+```bash npm2yarn
+npm install --save-dev @testing-library/preact
+```
+
+> This library is built on top of
+> [`DOM Testing Library`](dom-testing-library/intro.mdx) which is where most of
+> the logic behind the queries is.
+
+## The Problem
+
+You want to write tests for your Preact components so that they avoid including
+implementation details, and are maintainable in the long run.
+
+## This Solution
+
+The Preact Testing Library is a very lightweight solution for testing Preact
+components. It provides light utility functions on top of `preact/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.](https://twitter.com/kentcdodds/status/977018512689455106)
+
+See the [Dom introduction][dom-solution-explainer] and [React
+introduction][react-solution-explainer] for a more in-depth explanation.
+
+[dom-solution-explainer]: ../dom-testing-library/intro.mdx#this-solution
+[react-solution-explainer]: ../react-testing-library/intro.mdx#this-solution
+
+**What this library is not**:
+
+1. A test runner or framework.
+2. Specific to a testing framework.
+
+We recommend Jest as our preference. You can checkout
+[Using Without Jest](react-testing-library/setup.mdx#using-without-jest) if
+you're looking to use another framework.
diff --git a/docs/preact-testing-library/learn.mdx b/docs/preact-testing-library/learn.mdx
new file mode 100644
index 000000000..f16af57c9
--- /dev/null
+++ b/docs/preact-testing-library/learn.mdx
@@ -0,0 +1,33 @@
+---
+id: learn
+title: Learn
+sidebar_label: Learn
+---
+
+Due to the similarities between React and Preact (including the testing
+libraries), you should be able to comfortably use any React based examples,
+docs, answers on stack overflow etc.
+
+Take note of the
+[differences between React and Preact](https://preactjs.com/guide/v10/differences-to-react).
+
+If you're still hungry for more at this point than checkout:
+
+- The dom-testing-library:
+ - [Introduction](intro.mdx)
+ - [Queries](queries/about.mdx)
+ - [Firing Events](dom-testing-library/api-events.mdx)
+ - [Async Utilities](dom-testing-library/api-async.mdx)
+ - [Helpers](dom-testing-library/api-custom-queries.mdx)
+ - [FAQ](dom-testing-library/faq.mdx)
+- The react-testing-library:
+ - [API](react-testing-library/api.mdx)
+ - [Example](react-testing-library/example-intro.mdx)
+ - [Migrate from Enzyme](react-testing-library/migrate-from-enzyme.mdx)
+ - [Sandbox](https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples)
+ - [FAQ](react-testing-library/faq.mdx)
+- This YouTube video by LevelUpTuts called
+ [What is React Testing Library?](https://youtu.be/JKOwJUM4_RM)
+- Extending Jest with
+ [custom matchers](https://github.com/testing-library/jest-dom) to test the
+ state of the DOM.
diff --git a/docs/queries/about.mdx b/docs/queries/about.mdx
new file mode 100644
index 000000000..53de0b1f0
--- /dev/null
+++ b/docs/queries/about.mdx
@@ -0,0 +1,402 @@
+---
+id: about
+title: About Queries
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+## Overview
+
+Queries are the methods that Testing Library gives you to find elements on the
+page. There are several [types of queries](#types-of-queries) ("get", "find",
+"query"); the difference between them is whether the query will throw an error
+if no element is found or if it will return a Promise and retry. Depending on
+what page content you are selecting, different queries may be more or less
+appropriate. See the [priority guide](#priority) for recommendations on how to
+make use of semantic queries to test your page in the most accessible way.
+
+After selecting an element, you can use the
+[Events API](dom-testing-library/api-events.mdx) or
+[user-event](user-event/intro.mdx) to fire events and simulate user interactions
+with the page, or use Jest and [jest-dom](ecosystem-jest-dom.mdx) to make
+assertions about the element.
+
+There are Testing Library helper methods that work with queries. As elements
+appear and disappear in response to actions,
+[Async APIs](dom-testing-library/api-async.mdx) like
+[`waitFor`](dom-testing-library/api-async.mdx#waitfor) or
+[`findBy` queries](dom-testing-library/api-async.mdx#findby-queries) can be used
+to await the changes in the DOM. To find only elements that are children of a
+specific element, you can use [`within`](dom-testing-library/api-within.mdx). If
+necessary, there are also a few options you can
+[configure](dom-testing-library/api-configuration.mdx), like the timeout for
+retries and the default testID attribute.
+
+## Example
+
+```jsx
+import {render, screen} from '@testing-library/react' // (or /dom, /vue, ...)
+
+test('should show login form', () => {
+ render( )
+ const input = screen.getByLabelText('Username')
+ // Events and assertions...
+})
+```
+
+## Types of Queries
+
+- Single Elements
+ - `getBy...`: Returns the matching node for a query, and throw a descriptive
+ error if no elements match _or_ if more than one match is found (use
+ `getAllBy` instead if more than one element is expected).
+ - `queryBy...`: Returns the matching node for a query, and return `null` if no
+ elements match. This is useful for asserting an element that is not present.
+ Throws an error if more than one match is found (use `queryAllBy` instead if
+ this is OK).
+ - `findBy...`: Returns a Promise which resolves when an element is found which
+ matches the given query. The promise is rejected if no element is found or
+ if more than one element is found after a default timeout of 1000ms. If you
+ need to find more than one element, use `findAllBy`.
+- Multiple Elements
+ - `getAllBy...`: Returns an array of all matching nodes for a query, and
+ throws an error if no elements match.
+ - `queryAllBy...`: Returns an array of all matching nodes for a query, and
+ return an empty array (`[]`) if no elements match.
+ - `findAllBy...`: Returns a promise which resolves to an array of elements
+ when any elements are found which match the given query. The promise is
+ rejected if no elements are found after a default timeout of `1000`ms.
+ - `findBy` methods are a combination of `getBy*` queries and
+ [`waitFor`](../dom-testing-library/api-async.mdx#waitfor). They accept the
+ `waitFor` options as the last argument (i.e.
+ `await screen.findByText('text', queryOptions, waitForOptions)`)
+
+
+
+Summary Table
+
+
+
+| Type of Query | 0 Matches | 1 Match | >1 Matches | Retry (Async/Await) |
+| --------------------- | ------------- | -------------- | ------------ | :-----------------: |
+| **Single Element** | | | | |
+| `getBy...` | Throw error | Return element | Throw error | No |
+| `queryBy...` | Return `null` | Return element | Throw error | No |
+| `findBy...` | Throw error | Return element | Throw error | Yes |
+| **Multiple Elements** | | | | |
+| `getAllBy...` | Throw error | Return array | Return array | No |
+| `queryAllBy...` | Return `[]` | Return array | Return array | No |
+| `findAllBy...` | Throw error | Return array | Return array | Yes |
+
+
+
+## Priority
+
+Based on [the Guiding Principles](guiding-principles.mdx), your test should
+resemble how users interact with your code (component, page, etc.) as much as
+possible. With this in mind, we recommend this order of priority:
+
+1. **Queries Accessible to Everyone** Queries that reflect the experience of
+ visual/mouse users as well as those that use assistive technology.
+ 1. `getByRole`: This can be used to query every element that is exposed in
+ the
+ [accessibility tree](https://developer.mozilla.org/en-US/docs/Glossary/AOM).
+ With the `name` option you can filter the returned elements by their
+ [accessible name](https://www.w3.org/TR/accname-1.1/). This should be your
+ top preference for just about everything. There's not much you can't get
+ with this (if you can't, it's possible your UI is inaccessible). Most
+ often, this will be used with the `name` option like so:
+ `getByRole('button', {name: /submit/i})`. Check the
+ [list of roles](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques#Roles).
+ 1. `getByLabelText`: This method is really good for form fields. When
+ navigating through a website form, users find elements using label text.
+ This method emulates that behavior, so it should be your top preference.
+ 1. `getByPlaceholderText`:
+ [A placeholder is not a substitute for a label](https://www.nngroup.com/articles/form-design-placeholders/).
+ But if that's all you have, then it's better than alternatives.
+ 1. `getByText`: Outside of forms, text content is the main way users find
+ elements. This method can be used to find non-interactive elements (like
+ divs, spans, and paragraphs).
+ 1. `getByDisplayValue`: The current value of a form element can be useful
+ when navigating a page with filled-in values.
+1. **Semantic Queries** HTML5 and ARIA compliant selectors. Note that the user
+ experience of interacting with these attributes varies greatly across
+ browsers and assistive technology.
+ 1. `getByAltText`: If your element is one which supports `alt` text (`img`,
+ `area`, `input`, and any custom element), then you can use this to find
+ that element.
+ 1. `getByTitle`: The title attribute is not consistently read by
+ screenreaders, and is not visible by default for sighted users
+1. **Test IDs**
+ 1. `getByTestId`: The user cannot see (or hear) these, so this is only
+ recommended for cases where you can't match by role or text or it doesn't
+ make sense (e.g. the text is dynamic).
+
+## Using Queries
+
+The base queries from DOM Testing Library require you to pass a `container` as
+the first argument. Most framework-implementations of Testing Library provide a
+pre-bound version of these queries when you render your components with them
+which means you _do not have to provide a container_. In addition, if you just
+want to query `document.body` then you can use the [`screen`](#screen) export as
+demonstrated below (using `screen` is recommended).
+
+The primary argument to a query can be a _string_, _regular expression_, or
+_function_. There are also options to adjust how node text is parsed. See
+[TextMatch](#textmatch) for documentation on what can be passed to a query.
+
+Given the following DOM elements (which can be rendered by React, Vue, Angular,
+or plain HTML code):
+
+```js
+
+
+ Username
+
+
+
+```
+
+You can use a query to find an element (byLabelText, in this case):
+
+```js
+import {screen, getByLabelText} from '@testing-library/dom'
+
+// With screen:
+const inputNode1 = screen.getByLabelText('Username')
+
+// Without screen, you need to provide a container:
+const container = document.querySelector('#app')
+const inputNode2 = getByLabelText(container, 'Username')
+```
+
+### `queryOptions`
+
+You can pass a `queryOptions` object with the query type. See the docs for each
+query type to see available options, e.g. [byRole API](queries/byrole.mdx#api).
+
+### `screen`
+
+All of the queries exported by DOM Testing Library accept a `container` as the
+first argument. Because querying the entire `document.body` is very common, DOM
+Testing Library also exports a `screen` object which has every query that is
+pre-bound to `document.body` (using the
+[`within`](dom-testing-library/api-within.mdx) functionality). Wrappers such as
+React Testing Library re-export `screen` so you can use it the same way.
+
+Here's how you use it:
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+document.body.innerHTML = `
+ Example
+
+`
+
+const exampleInput = screen.getByLabelText('Example')
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render(
+
+ Example
+
+
,
+)
+
+const exampleInput = screen.getByLabelText('Example')
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(`
+
+ Example
+
+
+`)
+
+const exampleInput = screen.getByLabelText('Example')
+```
+
+
+
+
+```js
+cy.findByLabelText('Example').should('exist')
+```
+
+
+
+
+> **Note**
+>
+> You need a global DOM environment to use `screen`. If you're using jest, with
+> the
+> [testEnvironment](https://jestjs.io/docs/en/configuration#testenvironment-string)
+> set to `jsdom`, a global DOM environment will be available for you.
+>
+> If you're loading your test with a `script` tag, make sure it comes after the
+> `body`. An example can be seen
+> [here](https://github.com/testing-library/dom-testing-library/issues/700#issuecomment-692218886).
+
+## `TextMatch`
+
+Most of the query APIs take a `TextMatch` as an argument, which means the
+argument can be either a _string_, _regex_, or a _function_ of signature
+`(content?: string, element?: Element | null) => boolean` which returns `true`
+for a match and `false` for a mismatch.
+
+### TextMatch Examples
+
+Given the following HTML:
+
+```html
+Hello World
+```
+
+**_Will_ find the div:**
+
+```javascript
+// Matching a string:
+screen.getByText('Hello World') // full string match
+screen.getByText('llo Worl', {exact: false}) // substring match
+screen.getByText('hello world', {exact: false}) // ignore case
+
+// Matching a regex:
+screen.getByText(/World/) // substring match
+screen.getByText(/world/i) // substring match, ignore case
+screen.getByText(/^hello world$/i) // full string match, ignore case
+screen.getByText(/Hello W?oRlD/i) // substring match, ignore case, searches for "hello world" or "hello orld"
+
+// Matching with a custom function:
+screen.getByText((content, element) => content.startsWith('Hello'))
+```
+
+**_Will not_ find the div:**
+
+```javascript
+// full string does not match
+screen.getByText('Goodbye World')
+
+// case-sensitive regex with different case
+screen.getByText(/hello world/)
+
+// function looking for a span when it's actually a div:
+screen.getByText((content, element) => {
+ return element.tagName.toLowerCase() === 'span' && content.startsWith('Hello')
+})
+```
+
+### Precision
+
+Queries that take a `TextMatch` also accept an object as the final argument that
+can contain options that affect the precision of string matching:
+
+- `exact`: Defaults to `true`; matches full strings, case-sensitive. When false,
+ matches substrings and is not case-sensitive.
+ - it has no effect when used together with `regex` or `function` arguments.
+ - in most cases, using a regex instead of a string combined with `{ exact: false }`
+ gives you more control over fuzzy matching so it should be preferred.
+- `normalizer`: An optional function which overrides normalization behavior. See
+ [`Normalization`](#normalization).
+
+### Normalization
+
+Before running any matching logic against text in the DOM, `DOM Testing Library`
+automatically normalizes that text. By default, normalization consists of
+trimming whitespace from the start and end of text, and **collapsing multiple
+adjacent whitespace characters within the string into a single space**.
+
+If you want to prevent that normalization, or provide alternative normalization
+(e.g. to remove Unicode control characters), you can provide a `normalizer`
+function in the options object. This function will be given a string and is
+expected to return a normalized version of that string.
+
+> **Note**
+>
+> Specifying a value for `normalizer` _replaces_ the built-in normalization, but
+> you can call `getDefaultNormalizer` to obtain a built-in normalizer, either to
+> adjust that normalization or to call it from your own normalizer.
+
+`getDefaultNormalizer` takes an options object which allows the selection of
+behaviour:
+
+- `trim`: Defaults to `true`. Trims leading and trailing whitespace
+- `collapseWhitespace`: Defaults to `true`. Collapses inner whitespace
+ (newlines, tabs, repeated spaces) into a single space.
+
+#### Normalization Examples
+
+To perform a match against text without trimming:
+
+```javascript
+screen.getByText('text', {
+ normalizer: getDefaultNormalizer({trim: false}),
+})
+```
+
+To override normalization to remove some Unicode characters whilst keeping some
+(but not all) of the built-in normalization behavior:
+
+```javascript
+screen.getByText('text', {
+ normalizer: str =>
+ getDefaultNormalizer({trim: false})(str).replace(/[\u200E-\u200F]*/g, ''),
+})
+```
+
+## Manual Queries
+
+On top of the queries provided by the testing library, you can use the regular
+[`querySelector` DOM API](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
+to query elements. Note that using this as an escape hatch to query by class or
+id is not recommended because they are invisible to the user. Use a testid if
+you have to, to make your intention to fall back to non-semantic queries clear
+and establish a stable API contract in the HTML.
+
+```jsx
+// @testing-library/react
+const {container} = render( )
+const foo = container.querySelector('[data-foo="bar"]')
+```
+
+## Browser extension
+
+Do you still have problems knowing how to use Testing Library queries?
+
+There is a very cool Browser extension for Chrome named
+[Testing Playground](https://chrome.google.com/webstore/detail/testing-playground/hejbmebodbijjdhflfknehhcgaklhano),
+and it helps you find the best queries to select elements. It allows you to
+inspect the element hierarchies in the Browser's Developer Tools, and provides
+you with suggestions on how to select them, while encouraging good testing
+practices.
+
+## Playground
+
+If you want to get more familiar with these queries, you can try them out on
+[testing-playground.com](https://testing-playground.com). Testing Playground is
+an interactive sandbox where you can run different queries against your own
+html, and get visual feedback matching the rules mentioned above.
diff --git a/docs/queries/byalttext.mdx b/docs/queries/byalttext.mdx
new file mode 100644
index 000000000..7c1b850bf
--- /dev/null
+++ b/docs/queries/byalttext.mdx
@@ -0,0 +1,89 @@
+---
+id: byalttext
+title: ByAltText
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+> getByAltText, queryByAltText, getAllByAltText, queryAllByAltText,
+> findByAltText, findAllByAltText
+
+## API
+
+```typescript
+getByAltText(
+ // If you're using `screen`, then skip the container argument:
+ container: HTMLElement,
+ text: TextMatch,
+ options?: {
+ exact?: boolean = true,
+ normalizer?: NormalizerFn,
+ }): HTMLElement
+```
+
+This will return the element (normally an ` `) that has the given `alt`
+text. Note that it only supports elements which accept an `alt` attribute or
+[custom elements](https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_custom_elements)
+(since we don't know if a custom element implements `alt` or not):
+[` `](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img),
+[` `](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input),
+and [` `](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
+(intentionally excluding
+[``](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/applet)
+as it's deprecated).
+
+```html
+
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const incrediblesPosterImg = screen.getByAltText(/incredibles.*? poster/i)
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+const incrediblesPosterImg = screen.getByAltText(/incredibles.*? poster/i)
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(MyComponent)
+const incrediblesPosterImg = screen.getByAltText(/incredibles.*? poster/i)
+```
+
+
+
+
+```js
+cy.findByAltText(/incredibles.*? poster/i).should('exist')
+```
+
+
+
+
+## Options
+
+[TextMatch](queries/about.mdx#textmatch) options
diff --git a/docs/queries/bydisplayvalue.mdx b/docs/queries/bydisplayvalue.mdx
new file mode 100644
index 000000000..519af79db
--- /dev/null
+++ b/docs/queries/bydisplayvalue.mdx
@@ -0,0 +1,205 @@
+---
+id: bydisplayvalue
+title: ByDisplayValue
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+> getByDisplayValue, queryByDisplayValue, getAllByDisplayValue,
+> queryAllByDisplayValue, findByDisplayValue, findAllByDisplayValue
+
+## API
+
+```typescript
+getByDisplayValue(
+ // If you're using `screen`, then skip the container argument:
+ container: HTMLElement,
+ value: TextMatch,
+ options?: {
+ exact?: boolean = true,
+ normalizer?: NormalizerFn,
+ }): HTMLElement
+```
+
+Returns the `input`, `textarea`, or `select` element that has the matching
+display value.
+
+### `input` tags
+
+```html
+
+```
+
+```js
+document.getElementById('lastName').value = 'Norris'
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const lastNameInput = screen.getByDisplayValue('Norris')
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+const lastNameInput = screen.getByDisplayValue('Norris')
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(MyComponent)
+const lastNameInput = screen.getByDisplayValue('Norris')
+```
+
+
+
+
+```js
+cy.findByDisplayValue('Norris').should('exist')
+```
+
+
+
+
+### `textarea` tags
+
+```html
+
+```
+
+```js
+document.getElementById('messageTextArea').value = 'Hello World'
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const messageTextArea = screen.getByDisplayValue('Hello World')
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+const messageTextArea = screen.getByDisplayValue('Hello World')
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(MyComponent)
+const messageTextArea = screen.getByDisplayValue('Hello World')
+```
+
+
+
+
+```js
+cy.findByDisplayValue('Hello World').should('exist')
+```
+
+
+
+
+### `select` tags
+
+In case of `select`, this will search for a `` whose selected ``
+matches the given [`TextMatch`](queries/about.mdx#textmatch).
+
+```html
+
+ State
+ Alabama
+ Alaska
+ Arizona
+
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const selectElement = screen.getByDisplayValue('Alaska')
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+const selectElement = screen.getByDisplayValue('Alaska')
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(MyComponent)
+const selectElement = screen.getByDisplayValue('Alaska')
+```
+
+
+
+
+```js
+cy.findByDisplayValue('Alaska').should('exist')
+```
+
+
+
+
+## Options
+
+[TextMatch](queries/about.mdx#textmatch) options
diff --git a/docs/queries/bylabeltext.mdx b/docs/queries/bylabeltext.mdx
new file mode 100644
index 000000000..e532f50d7
--- /dev/null
+++ b/docs/queries/bylabeltext.mdx
@@ -0,0 +1,150 @@
+---
+id: bylabeltext
+title: ByLabelText
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+> getByLabelText, queryByLabelText, getAllByLabelText, queryAllByLabelText,
+> findByLabelText, findAllByLabelText
+
+## API
+
+```typescript
+getByLabelText(
+ // If you're using `screen`, then skip the container argument:
+ container: HTMLElement,
+ text: TextMatch,
+ options?: {
+ selector?: string = '*',
+ exact?: boolean = true,
+ normalizer?: NormalizerFn,
+ }): HTMLElement
+```
+
+This will search for the label that matches the given
+[`TextMatch`](queries/about.mdx#textmatch), then find the element associated
+with that label.
+
+The example below will find the input node for the following DOM structures:
+
+```js
+// for/htmlFor relationship between label and form element id
+Username
+
+
+// The aria-labelledby attribute with form elements
+Username
+
+
+// Wrapper labels
+Username
+
+// Wrapper labels where the label text is in another child element
+
+ Username
+
+
+
+// aria-label attributes
+// Take care because this is not a label that users can see on the page,
+// so the purpose of your input must be obvious to visual users.
+
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const inputNode = screen.getByLabelText('Username')
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+
+const inputNode = screen.getByLabelText('Username')
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(Login)
+
+const inputNode = screen.getByLabelText('Username')
+```
+
+
+
+
+```js
+cy.findByLabelText('Username').should('exist')
+```
+
+
+
+
+## Options
+
+### `name`
+
+The example above does NOT find the input node for label text broken up by
+elements. You can use `getByRole('textbox', { name: 'Username' })` instead which
+is robust against switching to `aria-label` or `aria-labelledby`.
+
+### `selector`
+
+If it is important that you query a specific element (e.g. an ` `) you can
+provide a `selector` in the options:
+
+```js
+// Multiple elements labelled via aria-labelledby
+Username
+
+Please enter your username
+
+// Multiple labels with the same text
+
+ Username
+
+
+
+ Username
+
+
+```
+
+```js
+const inputNode = screen.getByLabelText('Username', {selector: 'input'})
+```
+
+> **Note**
+>
+> `getByLabelText` will not work in the case where a `for` attribute on a
+> `` element matches an `id` attribute on a non-form element.
+
+```js
+// This case is not valid
+// for/htmlFor between label and an element that is not a form element
+
+```
diff --git a/docs/queries/byplaceholdertext.mdx b/docs/queries/byplaceholdertext.mdx
new file mode 100644
index 000000000..d70f847b2
--- /dev/null
+++ b/docs/queries/byplaceholdertext.mdx
@@ -0,0 +1,86 @@
+---
+id: byplaceholdertext
+title: ByPlaceholderText
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+> getByPlaceholderText, queryByPlaceholderText, getAllByPlaceholderText,
+> queryAllByPlaceholderText, findByPlaceholderText, findAllByPlaceholderText
+
+## API
+
+```typescript
+getByPlaceholderText(
+ // If you're using `screen`, then skip the container argument:
+ container: HTMLElement,
+ text: TextMatch,
+ options?: {
+ exact?: boolean = true,
+ normalizer?: NormalizerFn,
+ }): HTMLElement
+```
+
+This will search for all elements with a placeholder attribute and find one that
+matches the given [`TextMatch`](queries/about.mdx#textmatch).
+
+```html
+
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const inputNode = screen.getByPlaceholderText('Username')
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+const inputNode = screen.getByPlaceholderText('Username')
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(MyComponent)
+const inputNode = screen.getByPlaceholderText('Username')
+```
+
+
+
+
+```js
+cy.findByPlaceholderText('Username').should('exist')
+```
+
+
+
+
+> **Note**
+>
+> A placeholder is not a good substitute for a label so you should generally use
+> `getByLabelText` instead.
+
+## Options
+
+[TextMatch](queries/about.mdx#textmatch) options
diff --git a/docs/queries/byrole.mdx b/docs/queries/byrole.mdx
new file mode 100644
index 000000000..53ecd33be
--- /dev/null
+++ b/docs/queries/byrole.mdx
@@ -0,0 +1,489 @@
+---
+id: byrole
+title: ByRole
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+> getByRole, queryByRole, getAllByRole, queryAllByRole, findByRole,
+> findAllByRole
+
+## API
+
+```typescript
+getByRole(
+ // If you're using `screen`, then skip the container argument:
+ container: HTMLElement,
+ role: string,
+ options?: {
+ hidden?: boolean = false,
+ name?: TextMatch,
+ description?: TextMatch,
+ selected?: boolean,
+ busy?: boolean,
+ checked?: boolean,
+ pressed?: boolean,
+ suggest?: boolean,
+ current?: boolean | string,
+ expanded?: boolean,
+ queryFallbacks?: boolean,
+ level?: number,
+ value?: {
+ min?: number,
+ max?: number,
+ now?: number,
+ text?: TextMatch,
+ }
+ }): HTMLElement
+```
+
+Queries for elements with the given role (and it also accepts a
+[`TextMatch`](queries/about.mdx#textmatch)). Default roles are taken into
+consideration e.g. ` ` has the `button` role without explicitly setting
+the `role` attribute. Here you can see
+[a table of HTML elements with their default and desired roles](https://www.w3.org/TR/html-aria/#docconformance).
+
+Please note that setting a `role` and/or `aria-*` attribute that matches the
+implicit ARIA semantics is unnecessary and is **not recommended** as these
+properties are already set by the browser, and we must not use the `role` and
+`aria-*` attributes in a manner that conflicts with the semantics described. For
+example, a `button` element can't have the `role` attribute of `heading`,
+because the `button` element has default characteristics that conflict with the
+`heading` role.
+
+> Roles are matched literally by string equality, without inheriting from the
+> ARIA role hierarchy. As a result, querying a superclass role like `checkbox`
+> will not include elements with a subclass role like `switch`.
+
+You can query the returned element(s) by their
+[accessible name or description](https://www.w3.org/TR/accname-1.1/). The
+accessible name is for simple cases equal to e.g. the label of a form element,
+or the text content of a button, or the value of the `aria-label` attribute. It
+can be used to query a specific element if multiple elements with the same role
+are present on the rendered content. For an in-depth guide check out
+["What is an accessible name?" from TPGi](https://www.tpgi.com/what-is-an-accessible-name/).
+If you only query for a single element with `getByText('The name')` it's
+oftentimes better to use `getByRole(expectedRole, { name: 'The name' })`. The
+accessible name query does not replace other queries such as `*ByAlt` or
+`*ByTitle`. While the accessible name can be equal to these attributes, it does
+not replace the functionality of these attributes. For example
+` ` will be returned for
+`getByRole('img', { name: 'fancy image' })`. However, the image will not display
+its description if `fancy.jpg` could not be loaded. Whether you want to assert
+this functionality in your test or not is up to you.
+
+:::tip input type password
+
+Unfortunately, the spec defines that ` ` has no implicit
+role. This means that in order to query this type of element we must fallback to
+a less powerful query such as [`ByLabelText`](queries/bylabeltext.mdx).
+
+:::
+
+## Options
+
+### `hidden`
+
+If you set `hidden` to `true` elements that are normally excluded from the
+accessibility tree are considered for the query as well. The default behavior
+follows https://www.w3.org/TR/wai-aria-1.2/#tree_exclusion with the exception of
+`role="none"` and `role="presentation"` which are considered in the query in any
+case. For example in
+
+```html
+
+
+ Open dialog
+
+
+ Close dialog
+
+
+```
+
+`getByRole('button')` would only return the `Close dialog`-button. To make
+assertions about the `Open dialog`-button you would need to use
+`getAllByRole('button', { hidden: true })`.
+
+The default value for `hidden` can
+[be configured](../dom-testing-library/api-configuration.mdx#configuration).
+
+### `selected`
+
+You can filter the returned elements by their selected state by setting
+`selected: true` or `selected: false`.
+
+For example in
+
+```html
+
+
+ Native
+ React
+ Cypress
+
+
+```
+
+you can get the "Native"-tab by calling `getByRole('tab', { selected: true })`.
+To learn more about the selected state and which elements can have this state
+see [ARIA `aria-selected`](https://www.w3.org/TR/wai-aria-1.2/#aria-selected).
+
+### `busy`
+
+You can filter the returned elements by their busy state by setting `busy: true`
+or `busy: false`.
+
+For example in
+
+```html
+
+
+ Login failed
+ Error: Loading message...
+
+
+```
+
+you can get the "Login failed" alert by calling
+`getByRole('alert', { busy: false })`. To learn more about the busy state see
+[ARIA `aria-busy`](https://www.w3.org/TR/wai-aria-1.2/#aria-busy) and
+[MDN `aria-busy` attribute](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-busy).
+
+### `checked`
+
+You can filter the returned elements by their checked state by setting
+`checked: true` or `checked: false`.
+
+For example in
+
+```html
+
+
+ Sugar
+ Gummy bears
+ Whipped cream
+
+
+```
+
+you can get the "Sugar" option by calling
+`getByRole('checkbox', { checked: true })`. To learn more about the checked
+state and which elements can have this state see
+[ARIA `aria-checked`](https://www.w3.org/TR/wai-aria-1.2/#aria-checked).
+
+> **Note**
+>
+> Checkboxes have a "mixed" state, which is considered neither checked nor
+> unchecked (details [here](https://www.w3.org/TR/html-aam-1.0/#details-id-56)).
+
+### `current`
+
+You can filter the returned elements by their current state by setting
+`current: boolean | string`. Note that no `aria-current` attribute will match
+`current: false` since `false` is the default value for `aria-current`.
+
+For example in
+
+```html
+
+
+ 👍
+ 👎
+
+
+```
+
+you can get the "👍" link by calling `getByRole('link', { current: 'page' })`
+and the "👎" by calling `getByRole('link', { current: false })`. To learn more
+about the current state see
+[ARIA `aria-current`](https://www.w3.org/TR/wai-aria-1.2/#aria-current).
+
+### `pressed`
+
+Buttons can have a pressed state. You can filter the returned elements by their
+pressed state by setting `pressed: true` or `pressed: false`.
+
+For example in
+
+```html
+
+
+
+```
+
+you can get the "👍" button by calling `getByRole('button', { pressed: true })`.
+To learn more about the pressed state see
+[ARIA `aria-pressed`](https://www.w3.org/TR/wai-aria-1.2/#aria-pressed).
+
+### `suggest`
+
+You can disable the ability to
+[throw suggestions](../dom-testing-library/api-configuration.mdx#throwsuggestions-experimental)
+for a specific query by setting this value to `false`.
+Setting this value to `true` will throw suggestions for the specific query.
+
+### `expanded`
+
+You can filter the returned elements by their expanded state by setting
+`expanded: true` or `expanded: false`.
+
+For example in
+
+```html
+
+
+
+
+
+```
+
+you can get the "Expandable Menu Item" link by calling
+`getByRole('link', { expanded: false })`. To learn more about the expanded state
+and which elements can have this state see
+[ARIA `aria-expanded`](https://www.w3.org/TR/wai-aria-1.2/#aria-expanded).
+
+```html
+...
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const dialogContainer = screen.getByRole('dialog')
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+const dialogContainer = screen.getByRole('dialog')
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(MyComponent)
+const dialogContainer = screen.getByRole('dialog')
+```
+
+
+
+
+```js
+cy.findByRole('dialog').should('exist')
+```
+
+
+
+
+### `queryFallbacks`
+
+By default, it's assumed that the first role of each element is supported, so
+only the first role can be queried. If you need to query an element by any of
+its fallback roles instead, you can use `queryFallbacks: true`.
+
+For example, `getByRole('switch')` would always match
+`
` because it's the first role, while
+`getByRole('checkbox')` would not. However,
+`getByRole('checkbox', { queryFallbacks: true })` would enable all fallback
+roles and therefore match the same element.
+
+> An element doesn't have multiple roles in a given environment. It has a single
+> one. Multiple roles in the attribute are evaluated from left to right until
+> the environment finds the first role it understands. This is useful when new
+> roles get introduced and you want to start supporting those as well as older
+> environments that don't understand that role (yet).
+
+### `level`
+
+An element with the `heading` role can be queried by any heading level
+`getByRole('heading')` or by a specific heading level using the `level` option
+`getByRole('heading', { level: 2 })`.
+
+The `level` option queries the element(s) with the `heading` role matching the
+indicated level determined by the semantic HTML heading elements `-` or
+matching the `aria-level` attribute.
+
+Given the example below,
+
+```html
+
+
+ Heading Level One
+ First Heading Level Two
+ Heading Level Three
+ Second Heading Level Two
+
+
+```
+
+you can query the `Heading Level Three` heading using
+`getByRole('heading', { level: 3 })`.
+
+```js
+getByRole('heading', {level: 1})
+// Heading Level One
+
+getAllByRole('heading', {level: 2})
+// [
+// First Heading Level Two ,
+// Second Heading Level Two
+// ]
+```
+
+While it is possible to explicitly set `role="heading"` and `aria-level`
+attribute on an element, it is **strongly encouraged** to use the semantic HTML
+headings `-`.
+
+To learn more about the `aria-level` property, see
+[ARIA `aria-level`](https://www.w3.org/TR/wai-aria-1.2/#aria-level).
+
+> The `level` option is _only_ applicable to the `heading` role. An error will
+> be thrown when used with any other role.
+
+### `value`
+
+A range widget can be queried by any value `getByRole('spinbutton')` or by a
+specific value using the `level` option
+`getByRole('spinbutton', { value: { now: 5, min: 0, max: 10, text: 'medium' } })`.
+
+Note that you don't have to specify all properties in `value`. A subset is
+sufficient e.g.
+`getByRole('spinbutton', { value: { now: 5, text: 'medium' } })`.
+
+Given the example below,
+
+```html
+
+
+
+ Volume
+
+
+ Pitch
+
+
+
+```
+
+you can query specific spinbutton(s) with the following queries,
+
+```js
+getByRole('spinbutton', {value: {now: 5}})
+// Volume
+
+getAllByRole('spinbutton', {value: {min: 0}})
+// [
+// Volume ,
+// Pitch
+// ]
+```
+
+> Every specified property in `value` must match. For example, if you query for
+> `{value: {min: 0, now: 3}}` `aria-valuemin` must be equal to 0 **AND** >
+> `aria-valuenow` must be equal to 3
+
+> The `value` option is _only_ applicable to certain roles (check the linked MDN
+> pages below for applicable roles). An error will be thrown when used with any
+> other role.
+
+To learn more about the `aria-value*` properties, see
+[MDN `aria-valuemin`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-valuemin),
+[MDN `aria-valuemax`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-valuemax),
+[MDN `aria-valuenow`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-valuenow),
+[MDN `aria-valuetext`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-valuetext).
+
+### `description`
+
+You can filter the returned elements by their
+[accessible description](https://www.w3.org/TR/accname-1.1/#mapping_additional_nd_description)
+for those cases where you have several elements with the same role and they
+don't have an accessible name but they do have a description. This would
+be the case for elements with
+[alertdialog](https://www.w3.org/TR/wai-aria-1.1/#alertdialog) role, where the
+`aria-describedby` attribute is used to describe the element's content.
+
+For example in
+
+```html
+
+
+
+```
+
+You can query a specific element like this
+
+```js
+getByRole('alertdialog', {description: 'Your session is about to expire'})
+```
+
+## Performance
+
+`getByRole` is the most preferred query to use as it most closely resembles the
+user experience, however the calculations it must perform to provide this
+confidence can be expensive (particularly with large DOM trees).
+
+Where test performance is a concern it may be desirable to trade some of this
+confidence for improved performance.
+
+`getByRole` performance can be improved by setting the option
+[`hidden`](#hidden) to `true` and thereby avoid expensive visibility checks.
+Note that in doing so inaccessible elements will now be included in the result.
+
+Another option may be to substitute `getByRole` for simpler `getByLabelText` and
+`getByText` queries which can be significantly faster though less robust
+alternatives.
diff --git a/docs/queries/bytestid.mdx b/docs/queries/bytestid.mdx
new file mode 100644
index 000000000..5f0a3f82a
--- /dev/null
+++ b/docs/queries/bytestid.mdx
@@ -0,0 +1,100 @@
+---
+id: bytestid
+title: ByTestId
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+> getByTestId, queryByTestId, getAllByTestId, queryAllByTestId, findByTestId,
+> findAllByTestId
+
+## API
+
+```typescript
+getByTestId(
+ // If you're using `screen`, then skip the container argument:
+ container: HTMLElement,
+ text: TextMatch,
+ options?: {
+ exact?: boolean = true,
+ normalizer?: NormalizerFn,
+ }): HTMLElement
+```
+
+A shortcut to `` container.querySelector(`[data-testid="${yourId}"]`) `` (and it
+also accepts a [`TextMatch`](queries/about.mdx#textmatch)).
+
+```html
+
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const element = screen.getByTestId('custom-element')
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+const element = screen.getByTestId('custom-element')
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(MyComponent)
+const element = screen.getByTestId('custom-element')
+```
+
+
+
+
+```js
+cy.findByTestId('custom-element').should('exist')
+```
+
+
+
+
+> In the spirit of [the guiding principles](guiding-principles.mdx), it is
+> recommended to use this only after the other queries don't work for your use
+> case. Using data-testid attributes do not resemble how your software is used
+> and should be avoided if possible. That said, they are _way_ better than
+> querying based on DOM structure or styling css class names. Learn more about
+> `data-testid`s from the blog post
+> ["Making your UI tests resilient to change"](https://kentcdodds.com/blog/making-your-ui-tests-resilient-to-change)
+
+## Options
+
+[TextMatch](queries/about.mdx#textmatch) options
+
+## Overriding `data-testid`
+
+The `...ByTestId` functions in `DOM Testing Library` use the attribute
+`data-testid` by default, following the precedent set by
+[React Native Web](https://github.com/testing-library/react-testing-library/issues/1)
+which uses a `testID` prop to emit a `data-testid` attribute on the element, and
+we recommend you adopt that attribute where possible. But if you already have an
+existing codebase that uses a different attribute for this purpose, you can
+override this value via
+`configure({testIdAttribute: 'data-my-test-attribute'})`.
diff --git a/docs/queries/bytext.mdx b/docs/queries/bytext.mdx
new file mode 100644
index 000000000..7c754764f
--- /dev/null
+++ b/docs/queries/bytext.mdx
@@ -0,0 +1,107 @@
+---
+id: bytext
+title: ByText
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+> getByText, queryByText, getAllByText, queryAllByText, findByText,
+> findAllByText
+
+## API
+
+```typescript
+getByText(
+ // If you're using `screen`, then skip the container argument:
+ container: HTMLElement,
+ text: TextMatch,
+ options?: {
+ selector?: string = '*',
+ exact?: boolean = true,
+ ignore?: string|boolean = 'script, style',
+ normalizer?: NormalizerFn,
+ }): HTMLElement
+```
+
+This will search for all elements that have a text node with `textContent`
+matching the given [`TextMatch`](queries/about.mdx#textmatch).
+
+```html
+About ℹ️
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const aboutAnchorNode = screen.getByText(/about/i)
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+const aboutAnchorNode = screen.getByText(/about/i)
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(MyComponent)
+const aboutAnchorNode = screen.getByText(/about/i)
+```
+
+
+
+
+```js
+cy.findByText(/about/i).should('exist')
+```
+
+
+
+
+It also works with `input`s whose `type` attribute is either `submit` or
+`button`:
+
+```js
+
+```
+
+## Options
+
+[TextMatch](queries/about.mdx#textmatch) options, plus the following:
+
+### `selector`
+
+> **Note**
+>
+> See [`getByLabelText`](queries/bylabeltext.mdx#selector) for more details on
+> how and when to use the `selector` option
+
+### `ignore`
+
+The `ignore` option accepts a query selector. If the
+[`node.matches`](https://developer.mozilla.org/en-US/docs/Web/API/Element/matches)
+returns true for that selector, the node will be ignored. This defaults to
+`'script, style'` because generally you don't want to select these tags, but if
+your content is in an inline script file, then the script tag could be returned.
+
+If you'd rather disable this behavior, set `ignore` to `false`.
diff --git a/docs/queries/bytitle.mdx b/docs/queries/bytitle.mdx
new file mode 100644
index 000000000..5c4481138
--- /dev/null
+++ b/docs/queries/bytitle.mdx
@@ -0,0 +1,90 @@
+---
+id: bytitle
+title: ByTitle
+---
+
+import Tabs from '@theme/Tabs'
+import TabItem from '@theme/TabItem'
+
+> getByTitle, queryByTitle, getAllByTitle, queryAllByTitle, findByTitle,
+> findAllByTitle
+
+## API
+
+```typescript
+getByTitle(
+ // If you're using `screen`, then skip the container argument:
+ container: HTMLElement,
+ title: TextMatch,
+ options?: {
+ exact?: boolean = true,
+ normalizer?: NormalizerFn,
+ }): HTMLElement
+```
+
+Returns the element that has the matching `title` attribute.
+
+Will also find a `title` element within an SVG.
+
+```html
+
+
+ Close
+
+
+```
+
+
+
+
+```js
+import {screen} from '@testing-library/dom'
+
+const deleteElement = screen.getByTitle('Delete')
+const closeElement = screen.getByTitle('Close')
+```
+
+
+
+
+```jsx
+import {render, screen} from '@testing-library/react'
+
+render( )
+const deleteElement = screen.getByTitle('Delete')
+const closeElement = screen.getByTitle('Close')
+```
+
+
+
+
+```ts
+import {render, screen} from '@testing-library/angular'
+
+await render(MyComponent)
+const deleteElement = screen.getByTitle('Delete')
+const closeElement = screen.getByTitle('Close')
+```
+
+
+
+
+```js
+cy.findByTitle('Delete').should('exist')
+cy.findByTitle('Close').should('exist')
+```
+
+
+
+
+## Options
+
+[TextMatch](queries/about.mdx#textmatch) options
diff --git a/docs/qwik-testing-library/api.mdx b/docs/qwik-testing-library/api.mdx
new file mode 100644
index 000000000..09edcce59
--- /dev/null
+++ b/docs/qwik-testing-library/api.mdx
@@ -0,0 +1,203 @@
+---
+id: api
+title: API
+sidebar_label: API
+---
+
+`@noma.to/qwik-testing-library` re-exports everything from
+[`@testing-library/dom`][@testing-library/dom], as well as:
+
+- [`render`](#render)
+- [`cleanup`](#cleanup)
+
+[@testing-library/dom]: ../dom-testing-library/api.mdx
+
+## `render`
+
+Render your component to the DOM with Qwik. By default, when no options are
+provided, the component will be rendered into a `` appended to
+`document.body`.
+
+```tsx
+import {render} from '@noma.to/qwik-testing-library'
+import {MockProvider} from './MockProvider'
+import {MyComponent} from './MyComponent'
+
+const result = await render( , {
+ baseElement: document.body,
+ container: document.createElement('host'),
+ wrapper: MockProvider,
+})
+```
+
+### Render Options
+
+You may also customize how Qwik Testing Library renders your component. Most of
+the time, you shouldn't need to modify these options.
+
+| Option | Description | Default |
+| ------------- | --------------------------------------------------- | -------------------------------- |
+| `container` | The container in which the component is rendered. | `document.createElement('host')` |
+| `baseElement` | The base element for queries and [`debug`](#debug). | `document.body` |
+| `queries` | [Custom Queries][custom-queries]. | N/A |
+| `wrapper` | The wrapper to provide a context to the component. | N/A |
+
+[custom-queries]: ../dom-testing-library/api-custom-queries.mdx
+
+#### `wrapper`
+
+You can wrap your component into a wrapper to provide a context and other
+functionalities needed by the component under test.
+
+```tsx
+import {render} from '@noma.to/qwik-testing-library'
+import {QwikCityMockProvider} from '@builder.io/qwik-city'
+
+await render( , {wrapper: QwikCityMockProvider})
+```
+
+### Render Results
+
+| Result | Description |
+| ----------------------------- | ---------------------------------------------------------- |
+| [`baseElement`](#baseelement) | The base DOM element used for queries. |
+| [`container`](#container) | The DOM element the component is mounted to. |
+| [`asFragment`](#asFragment) | Convert the DOM element to a `DocumentFragment`. |
+| [`debug`](#debug) | Log elements using [`prettyDOM`][pretty-dom]. |
+| [`unmount`](#unmount) | Unmount and destroy the component. |
+| [`...queries`](#queries) | [Query functions][query-functions] bound to `baseElement`. |
+
+[pretty-dom]: ../dom-testing-library/api-debugging.mdx#prettydom
+[query-functions]: ../queries/about.mdx
+
+#### `baseElement`
+
+The base DOM element that queries are bound to. Corresponds to
+`renderOptions.baseElement`. If you do not use `renderOptions.baseElement`, this
+will be `document.body`.
+
+#### `container`
+
+The DOM element the component is mounted in. Corresponds to
+`renderOptions.container`. If you do not use `renderOptions.container`, this
+will be `document.createElement('host')`. In general, avoid using `container`
+directly to query for elements; use [testing-library queries][query-functions]
+instead.
+
+#### `asFragment`
+
+Returns a `DocumentFragment` of your rendered component. This can be useful if
+you need to avoid live bindings and see how your component reacts to events.
+
+```tsx
+import {component$} from '@builder.io/qwik';
+import {render} from '@testing-library/react';
+import {userEvent} from "@testing-library/user-event";
+
+const TestComponent = component$(() => {
+ const count = useSignal(0);
+
+ return (
+ (count.value++))}>
+ Click to increase: {count}
+
+ )
+});
+
+const {getByText, asFragment} = await render( )
+const firstRender = asFragment()
+
+userEvent.click(getByText(/Click to increase/))
+
+// This will snapshot only the difference between the first render, and the
+// state of the DOM after the click event.
+// See https://github.com/jest-community/snapshot-diff
+expect(firstRender).toMatchDiffSnapshot(asFragment())
+```
+
+#### `debug`
+
+Log the `baseElement` or a given element using [`prettyDOM`][pretty-dom].
+
+:::tip
+
+If your `baseElement` is the default `document.body`, we recommend using
+[`screen.debug`][screen-debug].
+
+:::
+
+```tsx
+import {render, screen} from '@noma.to/qwik-testing-library'
+
+const {debug} = await render( )
+
+const button = screen.getByRole('button')
+
+// log `document.body`
+screen.debug()
+
+// log your custom the `baseElement`
+debug()
+
+// log a specific element
+screen.debug(button)
+debug(button)
+```
+
+[screen-debug]: ../dom-testing-library/api-debugging.mdx#screendebug
+
+#### `unmount`
+
+Unmount and destroy the Qwik component.
+
+```tsx
+const {unmount} = await render( )
+
+unmount()
+```
+
+#### Queries
+
+[Query functions][query-functions] bound to the [`baseElement`](#baseelement).
+If you passed [custom queries][custom-queries] to `render`, those will be
+available instead of the default queries.
+
+:::tip
+
+If your [`baseElement`](#baseelement) is the default `document.body`, we
+recommend using [`screen`][screen] rather than bound queries.
+
+:::
+
+```tsx
+import {render, screen} from '@noma.to/qwik-testing-library'
+
+const {getByRole} = await render( )
+
+// query `document.body`
+const button = screen.getByRole('button')
+
+// query using a custom `target` or `baseElement`
+const button = getByRole('button')
+```
+
+[screen]: ../queries/about.mdx#screen
+
+## `cleanup`
+
+Destroy all components and remove any elements added to `document.body` or
+`renderOptions.baseElement`.
+
+```tsx
+import {render, cleanup} from '@noma.to/qwik-testing-library'
+
+// Default: runs after each test
+afterEach(() => {
+ cleanup()
+})
+
+await render( )
+
+// Called manually for more control
+cleanup()
+```
diff --git a/docs/qwik-testing-library/example.mdx b/docs/qwik-testing-library/example.mdx
new file mode 100644
index 000000000..55e1b5f11
--- /dev/null
+++ b/docs/qwik-testing-library/example.mdx
@@ -0,0 +1,103 @@
+---
+id: example
+title: Example
+sidebar_label: Example
+---
+
+Below are some examples of how to use the Qwik Testing Library to test your
+Qwik components.
+
+You can also learn more about the [**queries**][tl-queries-docs] and [**user
+events**][tl-user-events-docs] to help you write your tests.
+
+[tl-queries-docs]: ../queries/about.mdx
+[tl-user-events-docs]: ../user-event/intro.mdx
+
+## Qwikstart
+
+This is a minimal setup to get you started, with line-by-line explanations.
+
+```tsx title="counter.spec.tsx"
+// import qwik-testing methods
+import {screen, render, waitFor} from '@noma.to/qwik-testing-library'
+// import the userEvent methods to interact with the DOM
+import {userEvent} from '@testing-library/user-event'
+// import the component to be tested
+import {Counter} from './counter'
+
+// describe the test suite
+describe(' ', () => {
+ // describe the test case
+ it('should increment the counter', async () => {
+ // setup user event
+ const user = userEvent.setup()
+ // render the component into the DOM
+ await render( )
+
+ // retrieve the 'increment count' button
+ const incrementBtn = screen.getByRole('button', {name: /increment count/})
+ // click the button twice
+ await user.click(incrementBtn)
+ await user.click(incrementBtn)
+
+ // assert that the counter is now 2
+ expect(await screen.findByText(/count:2/)).toBeInTheDocument()
+ })
+})
+```
+
+## Qwik City - `server$` calls
+
+If one of your Qwik components uses `server$` calls, your tests might fail with
+a rather cryptic message (e.g.
+`QWIK ERROR __vite_ssr_import_0__.myServerFunctionQrl is not a function` or
+`QWIK ERROR Failed to parse URL from ?qfunc=DNpotUma33o`).
+
+We're happy to discuss it on [Discord][discord], but we consider this failure to
+be a good thing: your components should be tested in isolation, so you will be
+forced to mock your server functions.
+
+[discord]: https://qwik.dev/chat
+
+Here is an example of how to test a component that uses `server$` calls:
+
+```ts title="~/server/blog-post.ts"
+import {server$} from '@builder.io/qwik-city'
+import {BlogPost} from '~/lib/blog-post'
+
+export const getLatestPosts$ = server$(function (): Promise {
+ // get the latest posts
+ return Promise.resolve([])
+})
+```
+
+```tsx title="~/components/latest-post-list.tsx"
+import {render, screen, waitFor} from '@noma.to/qwik-testing-library'
+import {LatestPostList} from './latest-post-list'
+
+vi.mock('~/server/blog-posts', () => ({
+ // the mocked function should end with `Qrl` instead of `$`
+ getLatestPostsQrl: () => {
+ return Promise.resolve([
+ {id: 'post-1', title: 'Post 1'},
+ {id: 'post-2', title: 'Post 2'},
+ ])
+ },
+}))
+
+describe(' ', () => {
+ it('should render the latest posts', async () => {
+ await render( )
+
+ expect(await screen.findAllByRole('listitem')).toHaveLength(2)
+ })
+})
+```
+
+Notice how the mocked function is ending with `Qrl` instead of `$`, despite
+being named as `getLatestPosts$`. This is caused by the Qwik optimizer renaming
+it to `Qrl`. So, we need to mock the `Qrl` function instead of the original `$`
+one.
+
+If your function doesn't end with `$`, the Qwik optimizer will not rename it to
+`Qrl`.
diff --git a/docs/qwik-testing-library/faq.mdx b/docs/qwik-testing-library/faq.mdx
new file mode 100644
index 000000000..5f380e025
--- /dev/null
+++ b/docs/qwik-testing-library/faq.mdx
@@ -0,0 +1,34 @@
+---
+id: faq
+title: FAQ
+sidebar_label: FAQ
+---
+
+- [How do I test file upload?](#how-do-i-test-file-upload)
+
+---
+
+## How do I test file upload?
+
+Use the [upload][] utility from `@testing-library/user-event`. It works well in
+both [jsdom][] and [happy-dom][].
+
+```tsx
+test('upload file', async () => {
+ const user = userEvent.setup()
+
+ await render( )
+ const file = new File(['hello'], 'hello.png', {type: 'image/png'})
+ const input = screen.getByLabelText(/upload file/i)
+
+ await user.upload(input, file)
+
+ expect(input.files[0]).toBe(file)
+ expect(input.files.item(0)).toBe(file)
+ expect(input.files).toHaveLength(1)
+})
+```
+
+[upload]: ../user-event/api-utility.mdx#upload
+[jsdom]: https://github.com/jsdom/jsdom
+[happy-dom]: https://github.com/capricorn86/happy-dom
diff --git a/docs/qwik-testing-library/intro.mdx b/docs/qwik-testing-library/intro.mdx
new file mode 100644
index 000000000..2af36492b
--- /dev/null
+++ b/docs/qwik-testing-library/intro.mdx
@@ -0,0 +1,32 @@
+---
+id: intro
+title: Intro
+sidebar_label: Introduction
+---
+
+[Qwik Testing Library on GitHub][gh]
+
+[gh]: https://github.com/ianlet/qwik-testing-library
+
+## The Problem
+
+You want to write maintainable tests for your [Qwik][qwik] components.
+
+[qwik]: https://qwik.dev/
+
+## This Solution
+
+The Qwik Testing Library is a lightweight library for testing Qwik components.
+It provides functions on top of `qwik` and `@testing-library/dom` so you can
+mount Qwik components and query their rendered output in the DOM. 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]
+
+[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106
+
+**What this library is not**:
+
+1. A test runner or framework.
+2. Specific to a testing framework.
diff --git a/docs/qwik-testing-library/setup.mdx b/docs/qwik-testing-library/setup.mdx
new file mode 100644
index 000000000..c05ff934f
--- /dev/null
+++ b/docs/qwik-testing-library/setup.mdx
@@ -0,0 +1,153 @@
+---
+id: setup
+title: Setup
+sidebar_label: Setup
+---
+
+This module is distributed via [npm][npm] which is bundled with [node][node] and
+should be installed as one of your project's `devDependencies`:
+
+```bash npm2yarn
+npm install --save-dev @noma.to/qwik-testing-library @testing-library/dom
+```
+
+This library supports `qwik` versions `1.7.2` and above and
+`@testing-library/dom` versions `10.1.0` and above.
+
+You may also be interested in installing `@testing-library/jest-dom` and
+`@testing-library/user-event` so you can use [the custom jest
+matchers][jest-dom] and [the user event library][user-event] to test
+interactions with the DOM.
+
+```bash npm2yarn
+npm install --save-dev @testing-library/jest-dom @testing-library/user-event
+```
+
+Finally, we need a DOM environment to run the tests in. This library was tested
+(for now) only with `jsdom` so we recommend using it:
+
+```bash npm2yarn
+npm install --save-dev jsdom
+```
+
+[npm]: https://www.npmjs.com/
+[node]: https://nodejs.org
+[jest-dom]: https://github.com/testing-library/jest-dom
+[user-event]: https://github.com/testing-library/user-event
+
+## Vitest Configuration
+
+We recommend using `@noma.to/qwik-testing-library` with [Vitest][vitest] as your
+test runner.
+
+If you haven't done so already, add vitest to your project using Qwik CLI:
+
+```bash npm2yarn
+npm run qwik add vitest
+```
+
+After that, we need to configure Vitest so it can run your tests. For this,
+create a _separate_ `vitest.config.ts` so you don't have to modify your
+project's `vite.config.ts`:
+
+```ts title="vitest.config.ts"
+import {defineConfig, mergeConfig} from 'vitest/config'
+import viteConfig from './vite.config'
+
+export default defineConfig(configEnv =>
+ mergeConfig(
+ viteConfig(configEnv),
+ defineConfig({
+ // qwik-testing-library needs to consider your project as a Qwik lib
+ // if it's already a Qwik lib, you can remove this section
+ build: {
+ target: 'es2020',
+ lib: {
+ entry: './src/index.ts',
+ formats: ['es', 'cjs'],
+ fileName: (format, entryName) =>
+ `${entryName}.qwik.${format === 'es' ? 'mjs' : 'cjs'}`,
+ },
+ },
+ // configure your test environment
+ test: {
+ environment: 'jsdom',
+ setupFiles: ['./vitest.setup.ts'],
+ globals: true,
+ },
+ }),
+ ),
+)
+```
+
+For now, `qwik-testing-library` needs to consider your project as a lib. Hence,
+the `build.lib` section in the config above.
+
+As the build will try to use `./src/index.ts` as the entry point, we need to
+create it:
+
+```ts title="src/index.ts"
+/**
+ * DO NOT DELETE THIS FILE
+ *
+ * This entrypoint is needed by @noma.to/qwik-testing-library to run your tests
+ */
+```
+
+Then, create the `vitest.setup.ts` file:
+
+```ts title="vitest.setup.ts"
+// Configure DOM matchers to work in Vitest
+import '@testing-library/jest-dom/vitest'
+
+// This has to run before qdev.ts loads. `beforeAll` is too late
+globalThis.qTest = false // Forces Qwik to run as if it was in a Browser
+globalThis.qRuntimeQrl = true
+globalThis.qDev = true
+globalThis.qInspector = false
+```
+
+This setup will make sure that Qwik is properly configured. It also loads
+`@testing-library/jest-dom/vitest` in your test runner so you can use matchers
+like `expect(...).toBeInTheDocument()`.
+
+By default, Qwik Testing Library cleans everything up automatically for you. You
+can opt out of this by setting the environment variable `QTL_SKIP_AUTO_CLEANUP`
+to `true`. Then in your tests, you can call the `cleanup` function when needed.
+For example:
+
+```ts
+import {cleanup} from '@noma.to/qwik-testing-library'
+import {afterEach} from 'vitest'
+
+afterEach(cleanup)
+```
+
+Now, edit your `tsconfig.json` to declare the following global types:
+
+```diff title="tsconfig.json"
+{
+ "compilerOptions": {
+ "types": [
++ "vitest/globals",
++ "@testing-library/jest-dom/vitest"
+ ]
+ },
+ "include": ["src"]
+}
+```
+
+[vitest]: https://vitest.dev/
+
+Finally, you can add test scripts to your `package.json` to run the tests with
+Vitest
+
+```json title="package.json"
+{
+ "scripts": {
+ "test": "vitest run",
+ "test:ui": "vitest --ui",
+ "test:watch": "vitest"
+ }
+}
+```
diff --git a/docs/native-testing-library/example-intro.md b/docs/react-native-testing-library/example-intro.mdx
similarity index 51%
rename from docs/native-testing-library/example-intro.md
rename to docs/react-native-testing-library/example-intro.mdx
index ffb54fde5..e6724ebd1 100644
--- a/docs/native-testing-library/example-intro.md
+++ b/docs/react-native-testing-library/example-intro.mdx
@@ -4,10 +4,10 @@ title: Example
sidebar_label: Example
---
-```javascript
-import React from 'react'
-import { Button, Text, TextInput, View } from 'react-native'
-import { fireEvent, render, wait } from '@testing-library/react-native'
+```jsx
+import * as React from 'react'
+import {Button, Text, TextInput, View} from 'react-native'
+import {render, screen, fireEvent} from '@testing-library/react-native'
function Example() {
const [name, setUser] = React.useState('')
@@ -22,7 +22,7 @@ function Example() {
// let's pretend this is making a server request, so it's async
// (you'd want to mock this imaginary request in your unit tests)...
setTimeout(() => {
- setShow(!show)
+ setShow(true)
}, Math.floor(Math.random() * 200))
}}
/>
@@ -32,22 +32,19 @@ function Example() {
}
test('examples of some things', async () => {
- const { getByTestId, getByText, queryByTestId, baseElement } = render(
-
- )
- const famousWomanInHistory = 'Ada Lovelace'
+ const expectedUsername = 'Ada Lovelace'
- const input = getByTestId('input')
- fireEvent.changeText(input, famousWomanInHistory)
+ render( )
- const button = getByText('Print Username')
- fireEvent.press(button)
+ fireEvent.changeText(screen.getByTestId('input'), expectedUsername)
+ fireEvent.press(screen.getByText('Print Username'))
- await wait(() => expect(queryByTestId('printed-username')).toBeTruthy())
+ // Using `findBy` query to wait for asynchronous operation to finish
+ const usernameOutput = await screen.findByTestId('printed-username')
- expect(getByTestId('printed-username').props.children).toBe(
- famousWomanInHistory
- )
- expect(baseElement).toMatchSnapshot()
+ // Using `toHaveTextContent` matcher from `@testing-library/jest-native` package.
+ expect(usernameOutput).toHaveTextContent(expectedUsername)
+
+ expect(screen.toJSON()).toMatchSnapshot()
})
```
diff --git a/docs/react-native-testing-library/intro.mdx b/docs/react-native-testing-library/intro.mdx
new file mode 100644
index 000000000..6af2119e5
--- /dev/null
+++ b/docs/react-native-testing-library/intro.mdx
@@ -0,0 +1,56 @@
+---
+id: intro
+title: Introduction
+sidebar_label: Introduction
+---
+
+React Native Testing Library is a testing library for **React Native** inspired
+by `React Testing Library`. Because React Native does not run in a browser
+environment, the core queries are implemented independently, unlike other
+wrappers that use `DOM Testing Library` as the base. You'll find much more
+information about the library, including examples, on the project sites:
+
+- [Docs][docs]
+- [Project repo][gh]
+
+The project is maintained by
+[Callstack](https://callstack.com/open-source/?utm_source=testing-library.com&utm_medium=referral&utm_campaign=react-native-testing-library)
+which is one of the React Native
+[Partners](https://github.com/facebook/react-native/blob/d48f7ba748a905818e8c64fe70fe5b24aa098b05/ECOSYSTEM.md#partners),
+active throughout the React Native Community.
+
+## Quickstart
+
+```bash npm2yarn
+npm install --save-dev @testing-library/react-native
+```
+
+- [React Native Testing Library on GitHub][gh]
+
+[docs]: https://callstack.github.io/react-native-testing-library/
+[gh]: https://github.com/callstack/react-native-testing-library
+
+## The problem
+
+You want to write maintainable tests for your React Native 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.
+
+## This solution
+
+The React Native Testing Library (RNTL) is a lightweight solution for testing
+React Native components. It provides light utility functions on top of
+`react-test-renderer`, 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.
+
+This Project is tested to work with Jest, but it should work with other test
+runners as well.
+
+[jest]: https://jestjs.io
diff --git a/docs/native-testing-library/setup.md b/docs/react-native-testing-library/setup.mdx
similarity index 53%
rename from docs/native-testing-library/setup.md
rename to docs/react-native-testing-library/setup.mdx
index a395cf709..d6d69b3e2 100644
--- a/docs/native-testing-library/setup.md
+++ b/docs/react-native-testing-library/setup.mdx
@@ -6,43 +6,33 @@ sidebar_label: Setup
## Setting up your project
-The `Native Testing Library` API should work out of the box for most tests. All
-of the snippets you'll find throughout the website work without any additional
-configuration assuming you use Jest and a moderately recent version of React
-Native.
-
-We strongly encourage you to use Jest with the `Native Testing Library` preset.
-The `react-native` preset should also work, but you'll be getting the best
-experience when using our preset. There may be some additional mocks you need to
-provide to such as mocks for `react-native-gesture-handler` when using
-`react-navigation`.
+The `React Native Testing Library` API should work out of the box for most
+tests. All of the snippets you'll find throughout the website work without any
+additional configuration assuming you use Jest and a moderately recent version
+of React Native.
## Custom Render
It's often useful to define a custom render method that includes things like
global context providers, data stores, etc. To make this available globally, one
approach is to define a utility file that re-exports everything from
-`Native Testing Library`. You can replace `Native Testing Library` with this
-file in all your imports. See [below](#configuring-jest-with-test-utils) for a
-way to make your test util file accessible without using relative paths.
-
-The example below sets up data providers using the
-[`wrapper`](api-render.md#render-options) option to `render`.
+`React Native Testing Library`. You can replace `React Native Testing Library`
+with this file in all your imports. See
+[below](#configuring-jest-with-test-utils) for a way to make your test util file
+accessible without using relative paths.
-```diff
-// my-component.test.js
+```diff title="my-component.test.js"
- import { render, fireEvent } from '@testing-library/react-native';
+ import { render, fireEvent } from '../test-utils';
```
-```js
-// test-utils.js
-import { render } from '@testing-library/react-native'
-import { ThemeProvider } from 'my-ui-lib'
-import { TranslationProvider } from 'my-i18n-lib'
+```jsx title="test-utils.js"
+import {render} from '@testing-library/react-native'
+import {ThemeProvider} from 'my-ui-lib'
+import {TranslationProvider} from 'my-i18n-lib'
import defaultStrings from 'i18n/en-x-default'
-const AllTheProviders = ({ children }) => {
+const AllTheProviders = ({children}) => {
return (
@@ -53,13 +43,13 @@ const AllTheProviders = ({ children }) => {
}
const customRender = (ui, options) =>
- render(ui, { wrapper: AllTheProviders, ...options })
+ render(ui, {wrapper: AllTheProviders, ...options})
// re-export everything
export * from '@testing-library/react-native'
// override render method
-export { customRender as render }
+export {customRender as render}
```
### Configuring Jest with Test Utils
@@ -71,14 +61,12 @@ Jest `moduleDirectories` option.
This will make all the `.js` files in the test-utils directory importable
without `../`.
-```diff
-// my-component.test.js
+```diff title="my-component.test.js"
- import { render, fireEvent } from '../test-utils';
+ import { render, fireEvent } from 'test-utils';
```
-```diff
-// jest.config.js
+```diff title="jest.config.js"
module.exports = {
moduleDirectories: [
'node_modules',
diff --git a/docs/react-testing-library/api.md b/docs/react-testing-library/api.md
deleted file mode 100644
index cd8a0f7a2..000000000
--- a/docs/react-testing-library/api.md
+++ /dev/null
@@ -1,294 +0,0 @@
----
-id: api
-title: API
----
-
-`React Testing Library` re-exports everything from `DOM Testing Library` as well
-as these methods:
-
-- [`render`](#render)
-- [`cleanup`](#cleanup)
-- [`act`](#act)
-
----
-
-## `render`
-
-```typescript
-function render(
- ui: React.ReactElement,
- options?: {
- /* You won't often use this, expand below for docs on options */
- }
-): RenderResult
-```
-
-Render into a container which is appended to `document.body`.
-
-```jsx
-import { render } from '@testing-library/react'
-
-render(
)
-```
-
-```jsx
-import { render, cleanup } from '@testing-library/react'
-import 'jest-dom/extend-expect'
-afterEach(cleanup)
-
-test('renders a message', () => {
- const { container, getByText } = render( )
- expect(getByText('Hello, world!')).toBeInTheDocument()
- expect(container.firstChild).toMatchInlineSnapshot(`
- Hello, World!
- `)
-})
-```
-
-> Note
->
-> The [cleanup](#cleanup) function should be called between tests to remove the
-> created DOM nodes and keep the tests isolated.
-
-## `render` Options
-
-You wont often need to specify options, but if you ever do, here are the
-available options which you could provide as a second argument to `render`.
-
-### `container`
-
-By default, `React Testing Library` will create a `div` and append that div to
-the `document.body` and this is where your react component will be rendered. If
-you provide your own HTMLElement `container` via this option, it will not be
-appended to the `document.body` automatically.
-
-For Example: If you are unit testing a `tablebody` element, it cannot be a child
-of a `div`. In this case, you can specify a `table` as the render `container`.
-
-```jsx
-const table = document.createElement('table')
-
-const { container } = render( , {
- container: document.body.appendChild(table),
-})
-```
-
-### `baseElement`
-
-If the `container` is specified, then this defaults to that, otherwise this
-defaults to `document.documentElement`. This is used as the base element for the
-queries as well as what is printed when you use `debug()`.
-
-### `hydrate`
-
-If hydrate is set to true, then it will render with
-[ReactDOM.hydrate](https://reactjs.org/docs/react-dom.html#hydrate). This may be
-useful if you are using server-side rendering and use ReactDOM.hydrate to mount
-your components.
-
-### `wrapper`
-
-Pass a React Component as the `wrapper` option to have it rendered around the
-inner element. This is most useful for creating reusable custom render functions
-for common data providers. See [setup](setup.md#custom-render) for examples.
-
-### `queries`
-
-Queries to bind. Overrides the default set from `DOM Testing Library` unless
-merged.
-
-```js
-// Example, a function to traverse table contents
-import * as tableQueries from 'my-table-query-libary'
-import queries from '@testing-library/react'
-
-const { getByRowColumn, getByText } = render( , {
- queries: { ...queries, ...tableQueries },
-})
-```
-
-See [helpers](dom-testing-library/api-helpers.md) for guidance on using utility
-functions to create custom queries.
-
-Custom queries can also be added globally by following the
-[custom render guide](./setup#custom-render).
-
-## `render` Result
-
-The `render` method returns an object that has a few properties:
-
-### `...queries`
-
-The most important feature of `render` is that the queries from
-[DOM Testing Library](dom-testing-library/api-queries.md) are automatically
-returned with their first argument bound to the [baseElement](#baseelement),
-which defaults to `document.body`.
-
-See [Queries](dom-testing-library/api-queries.md) for a complete list.
-
-**Example**
-
-```jsx
-const { getByLabelText, queryAllByTestId } = render( )
-```
-
-### `container`
-
-The containing DOM node of your rendered React Element (rendered using
-`ReactDOM.render`). It's a `div`. This is a regular DOM node, so you can call
-`container.querySelector` etc. to inspect the children.
-
-> Tip: To get the root element of your rendered element, use
-> `container.firstChild`.
->
-> NOTE: When that root element is a
-> [React Fragment](https://reactjs.org/docs/fragments.html),
-> `container.firstChild` will only get the first child of that Fragment, not the
-> Fragment itself.
-
-> 🚨 If you find yourself using `container` to query for rendered elements then
-> you should reconsider! The other queries are designed to be more resiliant to
-> changes that will be made to the component you're testing. Avoid using
-> `container` to query for elements!
-
-### `baseElement`
-
-The containing DOM node where your React Element is rendered in the container.
-If you don't specify the `baseElement` in the options of `render`, it will
-default to `document.body`.
-
-This is useful when the component you want to test renders something outside the
-container div, e.g. when you want to snapshot test your portal component which
-renders its HTML directly in the body.
-
-> Note: the queries returned by the `render` looks into baseElement, so you can
-> use queries to test your portal component without the baseElement.
-
-### `debug`
-
-This method is a shortcut for `console.log(prettyDOM(baseElement))`.
-
-```jsx
-import React from 'react'
-import { render } from '@testing-library/react'
-
-const HelloWorld = () => Hello World
-const { debug } = render( )
-debug()
-//
-//
Hello World
-//
-// you can also pass an element: debug(getByTestId('messages'))
-```
-
-This is a simple wrapper around `prettyDOM` which is also exposed and comes from
-[`DOM Testing Library`](https://github.com/testing-library/dom-testing-library/blob/master/README.md#prettydom).
-
-### `rerender`
-
-It'd probably be better if you test the component that's doing the prop updating
-to ensure that the props are being updated correctly (see
-[the Guiding Principles section](guiding-principles.md)). That said, if you'd
-prefer to update the props of a rendered component in your test, this function
-can be used to update props of the rendered component.
-
-```jsx
-import { render } from '@testing-library/react'
-
-const { rerender } = render( )
-
-// re-render the same component with different props
-rerender( )
-```
-
-[See the examples page](example-update-props.md)
-
-### `unmount`
-
-This will cause the rendered component to be unmounted. This is useful for
-testing what happens when your component is removed from the page (like testing
-that you don't leave event handlers hanging around causing memory leaks).
-
-> This method is a pretty small abstraction over
-> `ReactDOM.unmountComponentAtNode`
-
-```jsx
-import { render } from '@testing-library/react'
-
-const { container, unmount } = render( )
-unmount()
-// your component has been unmounted and now: container.innerHTML === ''
-```
-
-### `asFragment`
-
-Returns a `DocumentFragment` of your rendered component. This can be useful if
-you need to avoid live bindings and see how your component reacts to events.
-
-```jsx
-import { render, fireEvent } from '@testing-library/react'
-
-class TestComponent extends React.Component {
- constructor() {
- super()
- this.state = { count: 0 }
- }
-
- render() {
- const { count } = this.state
-
- return (
- this.setState({ count: count + 1 })}>
- Click to increase: {count}
-
- )
- }
-}
-
-const { getByText, asFragment } = render( )
-const firstRender = asFragment()
-
-fireEvent.click(getByText(/Click to increase/))
-
-// This will snapshot only the difference between the first render, and the
-// state of the DOM after the click event.
-// See https://github.com/jest-community/snapshot-diff
-expect(firstRender).toMatchDiffSnapshot(asFragment())
-```
-
----
-
-## `cleanup`
-
-Unmounts React trees that were mounted with [render](#render).
-
-```jsx
-import { cleanup, render } from '@testing-library/react'
-
-afterEach(cleanup) // <-- add this
-
-test('renders into document', () => {
- render(
)
- // ...
-})
-
-// ... more tests ...
-```
-
-Failing to call `cleanup` when you've called `render` could result in a memory
-leak and tests which are not "idempotent" (which can lead to difficult to debug
-errors in your tests).
-
-**If you don't want to add this to _every single test file_** then we recommend
-that you configure your test framework to run a file before your tests which
-does this automatically. See the [setup](./setup) section for guidance on how to
-set up your framework.
-
----
-
-## `act`
-
-This is a light wrapper around the
-[`react-dom/test-utils` `act` function](https://reactjs.org/docs/test-utils.html#act).
-All it does is forward all arguments to the act function if your version of
-react supports `act`.
diff --git a/docs/react-testing-library/api.mdx b/docs/react-testing-library/api.mdx
new file mode 100644
index 000000000..449c76f52
--- /dev/null
+++ b/docs/react-testing-library/api.mdx
@@ -0,0 +1,519 @@
+---
+id: api
+title: API
+---
+
+`React Testing Library` re-exports everything from `DOM Testing Library` as well
+as these methods:
+
+- [`render`](#render)
+- [`render` Options](#render-options)
+ - [`container`](#container)
+ - [`baseElement`](#baseelement)
+ - [`hydrate`](#hydrate)
+ - [`legacyRoot`](#legacyroot)
+ - [`onCaughtError`](#oncaughterror)
+ - [`onRecoverableError`](#onrecoverableerror)
+ - [`wrapper`](#wrapper)
+ - [`queries`](#queries)
+ - [`reactStrictMode`](#render-options-reactstrictmode)
+- [`render` Result](#render-result)
+ - [`...queries`](#queries-1)
+ - [`container`](#container-1)
+ - [`baseElement`](#baseelement-1)
+ - [`debug`](#debug)
+ - [`rerender`](#rerender)
+ - [`unmount`](#unmount)
+ - [`asFragment`](#asfragment)
+- [`cleanup`](#cleanup)
+- [`act`](#act)
+- [`renderHook`](#renderhook)
+- [`renderHook` Options](#renderhook-options)
+ - [`initialProps`](#initialprops)
+ - [`onCaughtError`](#oncaughterror)
+ - [`onRecoverableError`](#onrecoverableerror)
+ - [`wrapper`](#renderhook-options-wrapper)
+ - [`reactStrictMode`](#renderhook-options-reactstrictmode)
+- [`renderHook` Result](#renderhook-result)
+ - [`result`](#result)
+ - [`rerender`](#rerender-1)
+ - [`unmount`](#unmount-1)
+- [`configure`](#configure)
+- [`configure` Options](#configure-options)
+
+---
+
+## `render`
+
+```typescript
+function render(
+ ui: React.ReactElement,
+ options?: {
+ /* You won't often use this, expand below for docs on options */
+ },
+): RenderResult
+```
+
+Render into a container which is appended to `document.body`.
+
+```jsx
+import {render} from '@testing-library/react'
+
+render(
)
+```
+
+```jsx
+import {render} from '@testing-library/react'
+import '@testing-library/jest-dom'
+
+test('renders a message', () => {
+ const {asFragment, getByText} = render( )
+ expect(getByText('Hello, world!')).toBeInTheDocument()
+ expect(asFragment()).toMatchInlineSnapshot(`
+ Hello, World!
+ `)
+})
+```
+
+## `render` Options
+
+You won't often need to specify options, but if you ever do, here are the
+available options which you could provide as a second argument to `render`.
+
+### `container`
+
+By default, `React Testing Library` will create a `div` and append that `div` to
+the `document.body` and this is where your React component will be rendered. If
+you provide your own HTMLElement `container` via this option, it will not be
+appended to the `document.body` automatically.
+
+For example: If you are unit testing a `tablebody` element, it cannot be a child
+of a `div`. In this case, you can specify a `table` as the render `container`.
+
+```jsx
+const table = document.createElement('table')
+
+const {container} = render( , {
+ container: document.body.appendChild(table),
+})
+```
+
+### `baseElement`
+
+If the `container` is specified, then this defaults to that, otherwise this
+defaults to `document.body`. This is used as the base element for the queries as
+well as what is printed when you use `debug()`.
+
+### `hydrate`
+
+If hydrate is set to true, then it will render with
+[ReactDOM.hydrate](https://react.dev/reference/react-dom/hydrate#hydrate). This may be
+useful if you are using server-side rendering and use ReactDOM.hydrate to mount
+your components.
+
+### `legacyRoot`
+
+:::warning
+
+This option is only available when tests run with React 18 and earlier.
+
+:::
+
+By default we'll render with support for concurrent features (i.e.
+[`ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot)).
+However, if you're dealing with a legacy app that requires rendering like in
+React 17 (i.e.
+[`ReactDOM.render`](https://react.dev/reference/react-dom/render)) then you
+should enable this option by setting `legacyRoot: true`.
+
+### `onCaughtError`
+
+Callback called when React catches an error in an Error Boundary.
+Behaves the same as [`onCaughtError` in `ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot#parameters).
+
+### `onRecoverableError`
+
+Callback called when React automatically recovers from errors.
+Behaves the same as [`onRecoverableError` in `ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot#parameters).
+
+### `wrapper`
+
+Pass a React Component as the `wrapper` option to have it rendered around the
+inner element. This is most useful for creating reusable custom render functions
+for common data providers. See [setup](setup.mdx#custom-render) for examples.
+
+### `queries`
+
+Queries to bind. Overrides the default set from `DOM Testing Library` unless
+merged.
+
+```jsx
+// Example, a function to traverse table contents
+import * as tableQueries from 'my-table-query-library'
+import {queries} from '@testing-library/react'
+
+const {getByRowColumn, getByText} = render( , {
+ queries: {...queries, ...tableQueries},
+})
+```
+
+See [helpers](dom-testing-library/api-custom-queries.mdx) for guidance on using
+utility functions to create custom queries.
+
+Custom queries can also be added globally by following the
+[custom render guide](setup.mdx#custom-render).
+
+### `render` Options `reactStrictMode`
+
+When enabled, [``](https://react.dev/reference/react/StrictMode) is rendered around the inner element.
+If defined, overrides the value of `reactStrictMode` set in [`configure`](https://testing-library.com/docs/react-testing-library/api/#configure-options).
+
+## `render` Result
+
+The `render` method returns an object that has a few properties:
+
+### `...queries`
+
+The most important feature of `render` is that the queries from
+[DOM Testing Library](queries/about.mdx) are automatically returned with their
+first argument bound to the [baseElement](#baseelement), which defaults to
+`document.body`.
+
+See [Queries](queries/about.mdx) for a complete list.
+
+**Example**
+
+```jsx
+const {getByLabelText, queryAllByTestId} = render( )
+```
+
+### `container`
+
+The containing DOM node of your rendered React Element (rendered using
+`ReactDOM.render`). It's a `div`. This is a regular DOM node, so you can call
+`container.querySelector` etc. to inspect the children.
+
+> Tip: To get the root element of your rendered element, use
+> `container.firstChild`.
+>
+> NOTE: When that root element is a
+> [React Fragment](https://react.dev/reference/react/Fragment),
+> `container.firstChild` will only get the first child of that Fragment, not the
+> Fragment itself.
+
+> 🚨 If you find yourself using `container` to query for rendered elements then
+> you should reconsider! The other queries are designed to be more resilient to
+> changes that will be made to the component you're testing. Avoid using
+> `container` to query for elements!
+
+### `baseElement`
+
+The containing DOM node where your React Element is rendered in the container.
+If you don't specify the `baseElement` in the options of `render`, it will
+default to `document.body`.
+
+This is useful when the component you want to test renders something outside the
+container div, e.g. when you want to snapshot test your portal component which
+renders its HTML directly in the body.
+
+> Note: the queries returned by the `render` looks into baseElement, so you can
+> use queries to test your portal component without the baseElement.
+
+### `debug`
+
+> NOTE: It's recommended to use [`screen.debug`](queries/about.mdx#screendebug)
+> instead.
+
+This method is a shortcut for `console.log(prettyDOM(baseElement))`.
+
+```jsx
+import React from 'react'
+import {render} from '@testing-library/react'
+
+const HelloWorld = () => Hello World
+const {debug} = render( )
+debug()
+//
+//
Hello World
+//
+// you can also pass an element: debug(getByTestId('messages'))
+// and you can pass all the same arguments to debug as you can
+// to prettyDOM:
+// const maxLengthToPrint = 10000
+// debug(getByTestId('messages'), maxLengthToPrint, {highlight: false})
+```
+
+This is a simple wrapper around `prettyDOM` which is also exposed and comes from
+[`DOM Testing Library`](dom-testing-library/api-debugging.mdx#prettydom).
+
+### `rerender`
+
+It'd probably be better if you test the component that's doing the prop updating
+to ensure that the props are being updated correctly (see
+[the Guiding Principles section](guiding-principles.mdx)). That said, if you'd
+prefer to update the props of a rendered component in your test, this function
+can be used to update props of the rendered component.
+
+```jsx
+import {render} from '@testing-library/react'
+
+const {rerender} = render( )
+
+// re-render the same component with different props
+rerender( )
+```
+
+[See the examples page](example-update-props.mdx)
+
+### `unmount`
+
+This will cause the rendered component to be unmounted. This is useful for
+testing what happens when your component is removed from the page (like testing
+that you don't leave event handlers hanging around causing memory leaks).
+
+> This method is a pretty small abstraction over
+> `ReactDOM.unmountComponentAtNode`
+
+```jsx
+import {render} from '@testing-library/react'
+
+const {container, unmount} = render( )
+unmount()
+// your component has been unmounted and now: container.innerHTML === ''
+```
+
+### `asFragment`
+
+Returns a `DocumentFragment` of your rendered component. This can be useful if
+you need to avoid live bindings and see how your component reacts to events.
+
+```jsx
+import React, {useState} from 'react'
+import {render, fireEvent} from '@testing-library/react'
+
+const TestComponent = () => {
+ const [count, setCounter] = useState(0)
+
+ return (
+ setCounter(count => count + 1)}>
+ Click to increase: {count}
+
+ )
+}
+
+const {getByText, asFragment} = render( )
+const firstRender = asFragment()
+
+fireEvent.click(getByText(/Click to increase/))
+
+// This will snapshot only the difference between the first render, and the
+// state of the DOM after the click event.
+// See https://github.com/jest-community/snapshot-diff
+expect(firstRender).toMatchDiffSnapshot(asFragment())
+```
+
+---
+
+## `cleanup`
+
+Unmounts React trees that were mounted with [render](#render).
+
+> This is called automatically if your testing framework (such as mocha, Jest or
+> Jasmine) injects a global `afterEach()` function into the testing environment.
+> If not, you will need to call `cleanup()` after each test.
+
+For example, if you're using the [ava](https://github.com/avajs/ava) testing
+framework, then you would need to use the `test.afterEach` hook like so:
+
+```jsx
+import {cleanup, render} from '@testing-library/react'
+import test from 'ava'
+
+test.afterEach(cleanup)
+
+test('renders into document', () => {
+ render(
)
+ // ...
+})
+
+// ... more tests ...
+```
+
+Failing to call `cleanup` when you've called `render` could result in a memory
+leak and tests which are not "idempotent" (which can lead to difficult to debug
+errors in your tests).
+
+---
+
+## `act`
+
+This is a light wrapper around the
+[`react` `act` function](https://react.dev/reference/react/act).
+All it does is forward all arguments to the act function if your version of
+react supports `act`. It is recommended to use the import from
+`@testing-library/react` over `react` for consistency reasons.
+
+## `renderHook`
+
+This is a convenience wrapper around `render` with a custom test component. The
+API emerged from a popular testing pattern and is mostly interesting for
+libraries publishing hooks. You should prefer `render` since a custom test
+component results in more readable and robust tests since the thing you want to
+test is not hidden behind an abstraction.
+
+```typescript
+function renderHook<
+ Result,
+ Props,
+ Q extends Queries = typeof queries,
+ Container extends Element | DocumentFragment = HTMLElement,
+ BaseElement extends Element | DocumentFragment = Container
+>(
+ render: (initialProps: Props) => Result,
+ options?: RenderHookOptions,
+): RenderHookResult
+```
+
+Example:
+
+```jsx
+import {renderHook} from '@testing-library/react'
+
+test('returns logged in user', () => {
+ const {result} = renderHook(() => useLoggedInUser())
+ expect(result.current).toEqual({name: 'Alice'})
+})
+```
+
+## `renderHook` Options
+
+### `renderHook` Options `initialProps`
+
+Declares the props that are passed to the render-callback when first invoked.
+These will **not** be passed if you call `rerender` without props.
+
+```jsx
+import {renderHook} from '@testing-library/react'
+
+test('returns logged in user', () => {
+ const {result, rerender} = renderHook((props = {}) => props, {
+ initialProps: {name: 'Alice'},
+ })
+ expect(result.current).toEqual({name: 'Alice'})
+ rerender()
+ expect(result.current).toEqual({name: undefined})
+})
+```
+
+> NOTE: When using `renderHook` in conjunction with the `wrapper` and
+> `initialProps` options, the `initialProps` are not passed to the `wrapper`
+> component. To provide props to the `wrapper` component, consider a solution
+> like this:
+>
+> ```js
+> const createWrapper = (Wrapper, props) => {
+> return function CreatedWrapper({ children }) {
+> return {children} ;
+> };
+> };
+>
+> ...
+>
+> {
+> wrapper: createWrapper(Wrapper, { value: 'foo' }),
+> }
+> ```
+
+### `onCaughtError`
+
+Callback called when React catches an error in an Error Boundary.
+Behaves the same as [`onCaughtError` in `ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot#parameters).
+
+### `onRecoverableError`
+
+Callback called when React automatically recovers from errors.
+Behaves the same as [`onRecoverableError` in `ReactDOMClient.createRoot`](https://react.dev/reference/react-dom/client/createRoot#parameters).
+
+### `renderHook` Options `wrapper`
+
+See [`wrapper` option for `render`](#wrapper)
+
+
+### `renderHook` Options `reactStrictMode`
+
+See [`reactStrictMode` option for `render`](#render-options-reactstrictmode)
+
+## `renderHook` Result
+
+The `renderHook` method returns an object that has a few properties:
+
+### `result`
+
+Holds the value of the most recently **committed** return value of the
+render-callback:
+
+```jsx
+import {renderHook} from '@testing-library/react'
+
+const {result} = renderHook(() => {
+ const [name, setName] = useState('')
+ React.useEffect(() => {
+ setName('Alice')
+ }, [])
+
+ return name
+})
+
+expect(result.current).toBe('Alice')
+```
+
+Note that the value is held in `result.current`. Think of `result` as a
+[ref](https://react.dev/learn/referencing-values-with-refs) for the most recently
+**committed** value.
+
+### `rerender`
+
+Renders the previously rendered render-callback with the new props:
+
+```jsx
+import {renderHook} from '@testing-library/react'
+
+const {rerender} = renderHook(({name = 'Alice'} = {}) => name)
+
+// re-render the same hook with different props
+rerender({name: 'Bob'})
+```
+
+### `unmount`
+
+Unmounts the test hook.
+
+```jsx
+import {renderHook} from '@testing-library/react'
+
+const {unmount} = renderHook(({name = 'Alice'} = {}) => name)
+
+unmount()
+```
+
+## `configure`
+
+Changes global options. Basic usage can be seen at
+[Configuration Options](dom-testing-library/api-configuration.mdx).
+
+React Testing Library also has dedicated options.
+
+```typescript
+import {configure} from '@testing-library/react'
+
+configure({reactStrictMode: true})
+```
+
+## `configure` Options
+
+### `reactStrictMode`
+
+When enabled, [``](https://react.dev/reference/react/StrictMode) is
+rendered around the inner element. Defaults to `false`.
+
+This setting can be changed for a single test by providing `reactStrictMode` in the options argument of the [`render`](#render-options-reactstrictmode) function.
\ No newline at end of file
diff --git a/docs/react-testing-library/cheatsheet.md b/docs/react-testing-library/cheatsheet.mdx
similarity index 68%
rename from docs/react-testing-library/cheatsheet.md
rename to docs/react-testing-library/cheatsheet.mdx
index 5335d1853..a50d82543 100644
--- a/docs/react-testing-library/cheatsheet.md
+++ b/docs/react-testing-library/cheatsheet.mdx
@@ -8,22 +8,23 @@ title: Cheatsheet
A short guide to all the exported functions in `React Testing Library`
- **render** `const {/* */} = render(Component)` returns:
- - all the queries from `DOM Testing Library`, bound to the document so there
- is no need to pass a node as the first argument
- `unmount` function to unmount the component
- `container` reference to the DOM node where the component is mounted
+ - all the queries from `DOM Testing Library`, bound to the document so there
+ is no need to pass a node as the first argument (usually, you can use the
+ `screen` import instead)
```jsx
-import { render, fireEvent } from '@testing-library/react'
+import {render, fireEvent, screen} from '@testing-library/react'
test('loads items eventually', async () => {
- const { getByText, findByText } = render( )
+ render( )
// Click button
- fireEvent.click(getByText('Load'))
+ fireEvent.click(screen.getByText('Load'))
// Wait for page to update with query text
- const items = await findByText(/Item #[0-9]: /)
+ const items = await screen.findAllByText(/Item #[0-9]: /)
expect(items).toHaveLength(10)
})
```
@@ -36,7 +37,7 @@ test('loads items eventually', async () => {
> `DOM Testing Library` except they have the first argument bound to the
> document, so instead of `getByText(node, 'text')` you do `getByText('text')`
-See [Which query should I use?](guide-which-query.md)
+See [Which query should I use?](queries/about.mdx#priority)
| | No Match | 1 Match | 1+ Match | Await? |
| -------------- | -------- | ------- | -------- | ------ |
@@ -106,45 +107,41 @@ See [Which query should I use?](guide-which-query.md)
## Async
-See [dom-testing-library Async API](dom-testing-library/api-async.md)
+The [dom-testing-library Async API](dom-testing-library/api-async.mdx) is
+re-exported from React Testing Library.
-- **wait** (Promise) retry the function within until it stops throwing or times
- out
-- **waitForElement** (Promise) retry the function until it returns an element or
- an array of elements
- - `findBy` and `findAllBy` queries are async and retry until either a timeout
- or if the query returns successfully; they wrap `waitForElement`
-- **waitForDomChange** (Promise) retry the function each time the DOM is changed
+- **waitFor** (Promise) retry the function within until it stops throwing or
+ times out
- **waitForElementToBeRemoved** (Promise) retry the function until it no longer
returns a DOM node
-> Remember to `await` or `.then()` the result of async functions in your tests!
-
## Events
-See [Events API](dom-testing-library/api-events.md)
+See [Events API](dom-testing-library/api-events.mdx)
- **fireEvent** trigger DOM event: `fireEvent(node, event)`
- **fireEvent.\*** helpers for default event types
- **click** `fireEvent.click(node)`
- - [See all supported events](https://github.com/testing-library/dom-testing-library/blob/master/src/events.js)
+ - [See all supported events](https://github.com/testing-library/dom-testing-library/blob/master/src/event-map.js)
- **act** wrapper around
- [react-dom/test-utils act](https://reactjs.org/docs/test-utils.html#act);
+ [react act](https://react.dev/reference/react/act);
React Testing Library wraps render and fireEvent in a call to `act` already so
most cases should not require using it manually
## Other
-See [Helpers API](dom-testing-library/api-helpers.md),
-[Config API](dom-testing-library/api-configuration.md)
+See [Querying Within Elements](dom-testing-library/api-within.mdx),
+[Config API](react-testing-library/api.mdx#configure),
+[Cleanup](react-testing-library/api.mdx#cleanup),
- **within** take a node and return an object with all the queries bound to the
node (used to return the queries from `React Testing Library`'s render
method): `within(node).getByText("hello")`
- **configure** change global options:
`configure({testIdAttribute: 'my-data-test-id'})`
-- **cleanup** clears the DOM ([use with `afterEach`](setup.md#cleanup) to reset
- DOM between tests)
+- **cleanup** clears the DOM
+ ([use with `afterEach`](react-testing-library/api.mdx#cleanup) to reset DOM
+ between tests)
## Text Match Options
@@ -158,21 +155,21 @@ Given the following HTML:
```javascript
// Matching a string:
-getByText(container, 'Hello World') // full string match
-getByText(container, 'llo Worl', { exact: false }) // substring match
-getByText(container, 'hello world', { exact: false }) // ignore case
+getByText('Hello World') // full string match
+getByText('llo Worl', {exact: false}) // substring match
+getByText('hello world', {exact: false}) // ignore case
// Matching a regex:
-getByText(container, /World/) // substring match
-getByText(container, /world/i) // substring match, ignore case
-getByText(container, /^hello world$/i) // full string match, ignore case
-getByText(container, /Hello W?oRlD/i) // advanced regex
+getByText(/World/) // substring match
+getByText(/world/i) // substring match, ignore case
+getByText(/^hello world$/i) // full string match, ignore case
+getByText(/Hello W?oRlD/i) // advanced regex
// Matching with a custom function:
-getByText(container, (content, element) => content.startsWith('Hello'))
+getByText((content, element) => content.startsWith('Hello'))
```
**[Get the printable cheat sheet][cheatsheet]**
[cheatsheet]:
- https://github.com/testing-library/react-testing-library/raw/master/other/cheat-sheet.pdf
+ https://github.com/testing-library/react-testing-library/raw/main/other/cheat-sheet.pdf
diff --git a/docs/react-testing-library/example-intro.md b/docs/react-testing-library/example-intro.md
deleted file mode 100644
index 9292a2188..000000000
--- a/docs/react-testing-library/example-intro.md
+++ /dev/null
@@ -1,135 +0,0 @@
----
-id: example-intro
-title: Example
-sidebar_label: Example
----
-
-## Full Example
-
-See the following sections for a detailed breakdown of the test
-
-```jsx
-// __tests__/fetch.test.js
-import React from 'react'
-import {
- render,
- fireEvent,
- cleanup,
- waitForElement,
-} from '@testing-library/react'
-import 'jest-dom/extend-expect'
-import axiosMock from 'axios'
-import Fetch from '../fetch'
-
-afterEach(cleanup)
-
-test('loads and displays greeting', async () => {
- const url = '/greeting'
- const { getByText, getByTestId } = render( )
-
- axiosMock.get.mockResolvedValueOnce({
- data: { greeting: 'hello there' },
- })
-
- fireEvent.click(getByText('Load Greeting'))
-
- const greetingTextNode = await waitForElement(() =>
- getByTestId('greeting-text')
- )
-
- expect(axiosMock.get).toHaveBeenCalledTimes(1)
- expect(axiosMock.get).toHaveBeenCalledWith(url)
- expect(getByTestId('greeting-text')).toHaveTextContent('hello there')
- expect(getByTestId('ok-button')).toHaveAttribute('disabled')
-})
-```
-
----
-
-## Step-By-Step
-
-### Imports
-
-```jsx
-// import dependencies
-import React from 'react'
-
-// import react-testing methods
-import {
- render,
- fireEvent,
- cleanup,
- waitForElement,
-} from '@testing-library/react'
-
-// add custom jest matchers from jest-dom
-import 'jest-dom/extend-expect'
-
-// the axios mock is in __mocks__/
-// see https://jestjs.io/docs/en/manual-mocks
-import axiosMock from 'axios'
-
-// the component to test
-import Fetch from '../fetch'
-```
-
-```jsx
-// automatically unmount and cleanup DOM after the test is finished.
-afterEach(cleanup)
-
-test('loads and displays greeting', async () => {
- // Arrange
- // Act
- // Assert
-})
-```
-
-### Arrange
-
-The [`render`](./api#render) method renders a React element into the DOM and
-returns utility functions for testing the component.
-
-```jsx
-const url = '/greeting'
-const { getByText, getByTestId, container, asFragment } = render(
-
-)
-```
-
-### Act
-
-The [`fireEvent`](dom-testing-library/api-events.md) method allows you to fire
-events to simulate user actions.
-
-```jsx
-axiosMock.get.mockResolvedValueOnce({
- data: { greeting: 'hello there' },
-})
-
-fireEvent.click(getByText('Load Greeting'))
-
-// Wait until the mocked `get` request promise resolves and
-// the component calls setState and re-renders.
-// `waitForElement` waits until the callback doesn't throw an error
-
-const greetingTextNode = await waitForElement(() =>
- // getByTestId throws an error if it cannot find an element
- getByTestId('greeting-text')
-)
-```
-
-### Assert
-
-```jsx
-expect(axiosMock.get).toHaveBeenCalledTimes(1)
-expect(axiosMock.get).toHaveBeenCalledWith(url)
-expect(getByTestId('greeting-text')).toHaveTextContent('hello there')
-expect(getByTestId('ok-button')).toHaveAttribute('disabled')
-
-// snapshots work great with regular DOM nodes!
-expect(container.firstChild).toMatchSnapshot()
-
-// you can also use get a `DocumentFragment`,
-// which is useful if you want to compare nodes across render
-expect(asFragment()).toMatchSnapshot()
-```
diff --git a/docs/react-testing-library/example-intro.mdx b/docs/react-testing-library/example-intro.mdx
new file mode 100644
index 000000000..66722d223
--- /dev/null
+++ b/docs/react-testing-library/example-intro.mdx
@@ -0,0 +1,296 @@
+---
+id: example-intro
+title: Example
+sidebar_label: Example
+---
+
+## Quickstart
+
+This is a minimal setup to get you started. If you want to see a description of
+what each line does, scroll down to the
+[annotated version](#quickstart-annotated). Scroll down to
+[Full Example](#full-example) to see a more advanced test setup.
+
+```jsx
+import {render, screen} from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
+import '@testing-library/jest-dom'
+import Fetch from './fetch'
+
+test('loads and displays greeting', async () => {
+ // ARRANGE
+ render( )
+
+ // ACT
+ await userEvent.click(screen.getByText('Load Greeting'))
+ await screen.findByRole('heading')
+
+ // ASSERT
+ expect(screen.getByRole('heading')).toHaveTextContent('hello there')
+ expect(screen.getByRole('button')).toBeDisabled()
+})
+```
+
+
+ Quickstart (Annotated Example)
+
+```jsx
+// import react-testing methods
+import {render, screen} from '@testing-library/react'
+// userEvent library simulates user interactions by dispatching the events that would happen if the interaction took place in a browser.
+import userEvent from '@testing-library/user-event'
+// add custom jest matchers from jest-dom
+import '@testing-library/jest-dom'
+// the component to test
+import Fetch from './fetch'
+
+test('loads and displays greeting', async () => {
+ // Render a React element into the DOM
+ render( )
+
+ await userEvent.click(screen.getByText('Load Greeting'))
+ // wait before throwing an error if it cannot find an element
+ await screen.findByRole('heading')
+
+ // assert that the alert message is correct using
+ // toHaveTextContent, a custom matcher from jest-dom.
+ expect(screen.getByRole('heading')).toHaveTextContent('hello there')
+ expect(screen.getByRole('button')).toBeDisabled()
+})
+```
+
+
+
+## Full Example
+
+See the following sections for a detailed breakdown of the test
+
+:::note
+
+We recommend using the
+[Mock Service Worker (MSW)](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.
+
+:::
+
+:::note
+
+Our example here uses axios to make its API calls. If your application uses [`fetch()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/fetch) to make its API calls, then be aware that by default JSDOM does not include fetch. If you are using vitest as your test runner, it will be included for you. For jest you may wish to manually polyfill `fetch()` or use the [jest-fixed-jsdom](https://github.com/mswjs/jest-fixed-jsdom) environment which includes fetch.
+
+:::
+
+```jsx title="__tests__/fetch.test.jsx"
+import React from 'react'
+import {http, HttpResponse} from 'msw'
+import {setupServer} from 'msw/node'
+import {render, fireEvent, screen} from '@testing-library/react'
+import '@testing-library/jest-dom'
+import Fetch from '../fetch'
+
+const server = setupServer(
+ http.get('/greeting', () => {
+ return HttpResponse.json({greeting: 'hello there'})
+ }),
+)
+
+beforeAll(() => server.listen())
+afterEach(() => server.resetHandlers())
+afterAll(() => server.close())
+
+test('loads and displays greeting', async () => {
+ render( )
+
+ fireEvent.click(screen.getByText('Load Greeting'))
+
+ await screen.findByRole('heading')
+
+ expect(screen.getByRole('heading')).toHaveTextContent('hello there')
+ expect(screen.getByRole('button')).toBeDisabled()
+})
+
+test('handles server error', async () => {
+ server.use(
+ http.get('/greeting', () => {
+ return new HttpResponse(null, {status: 500})
+ }),
+ )
+
+ render( )
+
+ fireEvent.click(screen.getByText('Load Greeting'))
+
+ await screen.findByRole('alert')
+
+ expect(screen.getByRole('alert')).toHaveTextContent('Oops, failed to fetch!')
+ expect(screen.getByRole('button')).not.toBeDisabled()
+})
+```
+
+---
+
+## Step-By-Step
+
+### Imports
+
+```jsx
+// import dependencies
+import React from 'react'
+
+// import API mocking utilities from Mock Service Worker
+import {http, HttpResponse} from 'msw'
+import {setupServer} from 'msw/node'
+
+// import react-testing methods
+import {render, fireEvent, screen} from '@testing-library/react'
+
+// add custom jest matchers from jest-dom
+import '@testing-library/jest-dom'
+// the component to test
+import Fetch from '../fetch'
+```
+
+```jsx
+test('loads and displays greeting', async () => {
+ // Arrange
+ // Act
+ // Assert
+})
+```
+
+### Mock
+
+Use the `setupServer` function from `msw` to mock an API request that our tested
+component makes.
+
+```js
+// declare which API requests to mock
+const server = setupServer(
+ // capture "GET /greeting" requests
+ http.get('/greeting', (req, res, ctx) => {
+ // respond using a mocked JSON body
+ return HttpResponse.json({greeting: 'hello there'})
+ }),
+)
+
+// establish API mocking before all tests
+beforeAll(() => server.listen())
+// reset any request handlers that are declared as a part of our tests
+// (i.e. for testing one-time error scenarios)
+afterEach(() => server.resetHandlers())
+// clean up once the tests are done
+afterAll(() => server.close())
+
+// ...
+
+test('handles server error', async () => {
+ server.use(
+ // override the initial "GET /greeting" request handler
+ // to return a 500 Server Error
+ http.get('/greeting', (req, res, ctx) => {
+ return new HttpResponse(null, {status: 500})
+ }),
+ )
+
+ // ...
+})
+```
+
+### Arrange
+
+The [`render`](api.mdx#render) method renders a React element into the DOM.
+
+```jsx
+render( )
+```
+
+### Act
+
+The [`fireEvent`](dom-testing-library/api-events.mdx) method allows you to fire
+events to simulate user actions.
+
+```jsx
+fireEvent.click(screen.getByText('Load Greeting'))
+
+// wait until the `get` request promise resolves and
+// the component calls setState and re-renders,
+// throwing an error if it cannot find an element
+await screen.findByRole('heading')
+```
+
+### Assert
+
+```jsx
+// assert that the alert message is correct using
+// toHaveTextContent, a custom matcher from jest-dom.
+expect(screen.getByRole('alert')).toHaveTextContent('Oops, failed to fetch!')
+
+// assert that the button is not disabled using
+// toBeDisabled, a custom matcher from jest-dom.
+expect(screen.getByRole('button')).not.toBeDisabled()
+```
+
+### System Under Test
+
+```jsx title="fetch.jsx"
+import React, {useState, useReducer} from 'react'
+import axios from 'axios'
+
+const initialState = {
+ error: null,
+ greeting: null,
+}
+
+function greetingReducer(state, action) {
+ switch (action.type) {
+ case 'SUCCESS': {
+ return {
+ error: null,
+ greeting: action.greeting,
+ }
+ }
+ case 'ERROR': {
+ return {
+ error: action.error,
+ greeting: null,
+ }
+ }
+ default: {
+ return state
+ }
+ }
+}
+
+export default function Fetch({url}) {
+ const [{error, greeting}, dispatch] = useReducer(
+ greetingReducer,
+ initialState,
+ )
+ const [buttonClicked, setButtonClicked] = useState(false)
+
+ const fetchGreeting = async url =>
+ axios
+ .get(url)
+ .then(response => {
+ const {data} = response
+ const {greeting} = data
+ dispatch({type: 'SUCCESS', greeting})
+ setButtonClicked(true)
+ })
+ .catch(error => {
+ dispatch({type: 'ERROR', error})
+ })
+
+ const buttonText = buttonClicked ? 'Ok' : 'Load Greeting'
+
+ return (
+
+
fetchGreeting(url)} disabled={buttonClicked}>
+ {buttonText}
+
+ {greeting &&
{greeting} }
+ {error &&
Oops, failed to fetch!
}
+
+ )
+}
+```
diff --git a/docs/react-testing-library/faq.md b/docs/react-testing-library/faq.md
deleted file mode 100644
index 93e909db3..000000000
--- a/docs/react-testing-library/faq.md
+++ /dev/null
@@ -1,192 +0,0 @@
----
-id: faq
-title: FAQ
----
-
-See also the [main FAQ](dom-testing-library/faq.md) for questions not specific
-to React testing
-
-
-
-How do I test input onChange handlers?
-
-TL;DR:
-[Go to the `on-change.js` example](https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples/tree/master/?module=%2Fsrc%2F__tests__%2Fon-change.js&previewwindow=tests)
-
-In summary:
-
-```javascript
-import React from 'react'
-import '@testing-library/react/cleanup-after-each'
-import { render, fireEvent } from '@testing-library/react'
-
-test('change values via the fireEvent.change method', () => {
- const handleChange = jest.fn()
- const { container } = render( )
- const input = container.firstChild
- fireEvent.change(input, { target: { value: 'a' } })
- expect(handleChange).toHaveBeenCalledTimes(1)
- expect(input.value).toBe('a')
-})
-
-test('checkboxes (and radios) must use fireEvent.click', () => {
- const handleChange = jest.fn()
- const { container } = render(
-
- )
- const checkbox = container.firstChild
- fireEvent.click(checkbox)
- expect(handleChange).toHaveBeenCalledTimes(1)
- expect(checkbox.checked).toBe(true)
-})
-```
-
-If you've used enzyme or React's TestUtils, you may be accustomed to changing
-inputs like so:
-
-```javascript
-input.value = 'a'
-Simulate.change(input)
-```
-
-We can't do this with React Testing Library because React actually keeps track
-of any time you assign the `value` property on an `input` and so when you fire
-the `change` event, React thinks that the value hasn't actually been changed.
-
-This works for Simulate because they use internal APIs to fire special simulated
-events. With React Testing Library, we try to avoid implementation details to
-make your tests more resiliant.
-
-So we have it worked out for the change event handler to set the property for
-you in a way that's not trackable by React. This is why you must pass the value
-as part of the `change` method call.
-
-
-
-
-
-Can I write unit tests with this library?
-
-Definitely yes! You can write unit and integration tests with this library. See
-below for more on how to mock dependencies (because this library intentionally
-does NOT support shallow rendering) if you want to unit test a high level
-component. The tests in this project show several examples of unit testing with
-this library.
-
-As you write your tests, keep in mind:
-
-> The more your tests resemble the way your software is used, the more
-> confidence they can give you. - [17 Feb 2018][guiding-principle]
-
-
-
-
-
-If I can't use shallow rendering, how do I mock out components in tests?
-
-In general, you should avoid mocking out components (see
-[the Guiding Principles section](guiding-principles.md)). However if you need
-to, then it's pretty trivial using
-[Jest's mocking feature](https://facebook.github.io/jest/docs/en/manual-mocks.html).
-One case that I've found mocking to be especially useful is for animation
-libraries. I don't want my tests to wait for animations to end.
-
-```javascript
-jest.mock('react-transition-group', () => {
- const FakeTransition = jest.fn(({ children }) => children)
- const FakeCSSTransition = jest.fn(props =>
- props.in ? {props.children} : null
- )
- return { CSSTransition: FakeCSSTransition, Transition: FakeTransition }
-})
-
-test('you can mock things with jest.mock', () => {
- const { getByTestId, queryByTestId } = render(
-
- )
- expect(queryByTestId('hidden-message')).toBeTruthy() // we just care it exists
- // hide the message
- fireEvent.click(getByTestId('toggle-message'))
- // in the real world, the CSSTransition component would take some time
- // before finishing the animation which would actually hide the message.
- // So we've mocked it out for our tests to make it happen instantly
- expect(queryByTestId('hidden-message')).toBeNull() // we just care it doesn't exist
-})
-```
-
-Note that because they're Jest mock functions (`jest.fn()`), you could also make
-assertions on those as well if you wanted.
-
-[Open full test](example-react-transition-group.md) for the full example.
-
-This looks like more work that shallow rendering (and it is), but it gives you
-more confidence so long as your mock resembles the thing you're mocking closely
-enough.
-
-If you want to make things more like shallow rendering, then you could do
-something more [like this](example-react-transition-group.md).
-
-Learn more about how Jest mocks work from my blog post:
-["But really, what is a JavaScript mock?"](https://blog.kentcdodds.com/but-really-what-is-a-javascript-mock-10d060966f7d)
-
-
-
-
-
-What about enzyme is "bloated with complexity and features" and "encourage
-poor testing practices"?
-
-Most of the damaging features have to do with encouraging testing implementation
-details. Primarily, these are
-[shallow rendering](http://airbnb.io/enzyme/docs/api/shallow.html), APIs which
-allow selecting rendered elements by component constructors, and APIs which
-allow you to get and interact with component instances (and their
-state/properties) (most of enzyme's wrapper APIs allow this).
-
-The guiding principle for this library is:
-
-> The more your tests resemble the way your software is used, the more
-> confidence they can give you. - [17 Feb 2018][guiding-principle]
-
-Because users can't directly interact with your app's component instances,
-assert on their internal state or what components they render, or call their
-internal methods, doing those things in your tests reduce the confidence they're
-able to give you.
-
-That's not to say that there's never a use case for doing those things, so they
-should be possible to accomplish, just not the default and natural way to test
-react components.
-
-
-
-
-
-Why isn't snapshot diffing working?
-
-If you use the [snapshot-diff](https://github.com/jest-community/snapshot-diff)
-library to save snapshot diffs, it won't work out of the box because this
-library uses the DOM which is mutable. Changes don't return new objects so
-snapshot-diff will think it's the same object and avoid diffing it.
-
-Luckily there's an easy way to make it work: clone the DOM when passing it into
-snapshot-diff. It looks like this:
-
-```js
-const firstVersion = container.cloneNode(true)
-// Do some changes
-snapshotDiff(firstVersion, container.cloneNode(true))
-```
-
-
-
-
-
-
-
-[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106
-[data-testid-blog-post]: https://blog.kentcdodds.com/making-your-ui-tests-resilient-to-change-d37a6ee37269
-[dom-testing-lib-textmatch]: https://github.com/testing-library/dom-testing-library#textmatch
-
-
diff --git a/docs/react-testing-library/faq.mdx b/docs/react-testing-library/faq.mdx
new file mode 100644
index 000000000..b939e8e61
--- /dev/null
+++ b/docs/react-testing-library/faq.mdx
@@ -0,0 +1,353 @@
+---
+id: faq
+title: FAQ
+---
+
+See also the [main FAQ](dom-testing-library/faq.mdx) for questions not specific
+to React testing.
+
+
+
+How do I test input onChange handlers?
+
+TL;DR:
+[Go to the `on-change.js` example](https://codesandbox.io/s/github/kentcdodds/react-testing-library-examples/tree/main/?module=%2Fsrc%2F__tests__%2Fon-change.js&previewwindow=tests)
+
+In summary:
+
+```jsx
+import React from 'react'
+import {render, fireEvent} from '@testing-library/react'
+
+test('change values via the fireEvent.change method', () => {
+ const handleChange = jest.fn()
+ const {container} = render( )
+ const input = container.firstChild
+ fireEvent.change(input, {target: {value: 'a'}})
+ expect(handleChange).toHaveBeenCalledTimes(1)
+ expect(input.value).toBe('a')
+})
+
+test('select drop-downs must use the fireEvent.change', () => {
+ const handleChange = jest.fn()
+ const {container} = render(
+
+ 1
+ 2
+ ,
+ )
+ const select = container.firstChild
+ const option1 = container.getElementsByTagName('option').item(0)
+ const option2 = container.getElementsByTagName('option').item(1)
+
+ fireEvent.change(select, {target: {value: '2'}})
+
+ expect(handleChange).toHaveBeenCalledTimes(1)
+ expect(option1.selected).toBe(false)
+ expect(option2.selected).toBe(true)
+})
+
+test('checkboxes (and radios) must use fireEvent.click', () => {
+ const handleChange = jest.fn()
+ const {container} = render( )
+ const checkbox = container.firstChild
+ fireEvent.click(checkbox)
+ expect(handleChange).toHaveBeenCalledTimes(1)
+ expect(checkbox.checked).toBe(true)
+})
+```
+
+If you've used enzyme or React's TestUtils, you may be accustomed to changing
+inputs like so:
+
+```javascript
+input.value = 'a'
+Simulate.change(input)
+```
+
+We can't do this with React Testing Library because React actually keeps track
+of any time you assign the `value` property on an `input` and so when you fire
+the `change` event, React thinks that the value hasn't actually been changed.
+
+This works for Simulate because they use internal APIs to fire special simulated
+events. With React Testing Library, we try to avoid implementation details to
+make your tests more resilient.
+
+So we have it worked out for the change event handler to set the property for
+you in a way that's not trackable by React. This is why you must pass the value
+as part of the `change` method call.
+
+
+
+
+
+How do I test error boundaries
+
+To test if an error boundary successfully catches an error, you should make sure that if the fallback of the boundary is displayed when a child threw.
+
+Here's an example of how you can test an error boundary:
+
+```jsx
+import React from 'react'
+import {render, screen} from '@testing-library/react'
+
+class ErrorBoundary extends React.Component {
+ state = {error: null}
+ static getDerivedStateFromError(error) {
+ return {error}
+ }
+ render() {
+ const {error} = this.state
+ if (error) {
+ return Something went wrong
+ }
+ return this.props.children
+ }
+}
+
+test('error boundary catches error', () => {
+ const {container} = render(
+
+
+ ,
+ )
+ expect(container.textContent).toEqual('Something went wrong.')
+})
+```
+
+If the error boundary did not catch the error, the test would fail since the `render` call would throw the error the Component produced.
+
+
+:::info
+
+React 18 will call `console.error` with an extended error message.
+React 19 will call `console.warn` with an extended error message.
+
+To disable the additional `console.warn` call in React 19, you can provide a custom `onCaughtError` callback e.g. `render( , {onCaughtError: () => {}})`
+`onCaughtError` is not supported in React 18.
+
+:::
+
+
+
+
+
+Can I write unit tests with this library?
+
+Definitely yes! You can write unit and integration tests with this library. See
+below for more on how to mock dependencies (because this library intentionally
+does NOT support shallow rendering) if you want to unit test a high level
+component. The tests in this project show several examples of unit testing with
+this library.
+
+As you write your tests, keep in mind:
+
+> The more your tests resemble the way your software is used, the more
+> confidence they can give you. - [17 Feb 2018][guiding-principle]
+
+
+
+
+
+How do I test thrown errors in a Component or Hook?
+
+If a component throws during render, the origin of the state update will throw if wrapped in `act`.
+By default, `render` and `fireEvent` are wrapped in `act`.
+You can just wrap it in a try-catch or use dedicated matchers if your test runner supports these.
+For example, in Jest you can use `toThrow`:
+
+```jsx
+function Thrower() {
+ throw new Error('I throw')
+}
+
+test('it throws', () => {
+ expect(() => render( )).toThrow('I throw')
+})
+```
+
+The same applies to Hooks and `renderHook`:
+
+```jsx
+function useThrower() {
+ throw new Error('I throw')
+}
+
+test('it throws', () => {
+ expect(() => renderHook(useThrower)).toThrow('I throw')
+})
+```
+
+:::info
+
+React 18 will call `console.error` with an extended error message.
+React 19 will call `console.warn` with an extended error message unless the state update is wrapped in `act`.
+`render`, `renderHook` and `fireEvent` are wrapped in `act` by default.
+
+:::
+
+
+
+
+
+
+ If I can't use shallow rendering, how do I mock out components in tests?
+
+
+In general, you should avoid mocking out components (see
+[the Guiding Principles section](guiding-principles.mdx)). However, if you need
+to, then try to use
+[Jest's mocking feature](https://facebook.github.io/jest/docs/en/manual-mocks.html).
+One case where I've found mocking to be especially useful is for animation
+libraries. I don't want my tests to wait for animations to end.
+
+```jsx
+jest.mock('react-transition-group', () => {
+ const FakeTransition = jest.fn(({children}) => children)
+ const FakeCSSTransition = jest.fn(props =>
+ props.in ? {props.children} : null,
+ )
+ return {CSSTransition: FakeCSSTransition, Transition: FakeTransition}
+})
+
+test('you can mock things with jest.mock', () => {
+ const {getByTestId, queryByTestId} = render(
+ ,
+ )
+ expect(queryByTestId('hidden-message')).toBeTruthy() // we just care it exists
+ // hide the message
+ fireEvent.click(getByTestId('toggle-message'))
+ // in the real world, the CSSTransition component would take some time
+ // before finishing the animation which would actually hide the message.
+ // So we've mocked it out for our tests to make it happen instantly
+ expect(queryByTestId('hidden-message')).toBeNull() // we just care it doesn't exist
+})
+```
+
+Note that because they're Jest mock functions (`jest.fn()`), you could also make
+assertions on those as well if you wanted.
+
+[Open full test](example-react-transition-group.mdx) for the full example.
+
+This looks like more work than shallow rendering (and it is), but it gives you
+more confidence so long as your mock resembles the thing you're mocking closely
+enough.
+
+If you want to make things more like shallow rendering, then you could do
+something more [like this](example-react-transition-group.mdx).
+
+Learn more about how Jest mocks work from my blog post:
+["But really, what is a JavaScript mock?"](https://kentcdodds.com/blog/but-really-what-is-a-javascript-mock)
+
+
+
+
+
+
+ What about enzyme is "bloated with complexity and features" and "encourage
+ poor testing practices"?
+
+
+Most of the damaging features have to do with encouraging testing implementation
+details. Primarily, these are
+[shallow rendering](http://airbnb.io/enzyme/docs/api/shallow.html), APIs which
+allow selecting rendered elements by component constructors, and APIs which
+allow you to get and interact with component instances (and their
+state/properties) (most of enzyme's wrapper APIs allow this).
+
+The guiding principle for this library is:
+
+> The more your tests resemble the way your software is used, the more
+> confidence they can give you. - [17 Feb 2018][guiding-principle]
+
+Because users can't directly interact with your app's component instances,
+assert on their internal state or what components they render, or call their
+internal methods, doing those things in your tests reduce the confidence they're
+able to give you.
+
+That's not to say that there's never a use case for doing those things, so they
+should be possible to accomplish, just not the default and natural way to test
+react components.
+
+
+
+
+
+Why isn't snapshot diffing working?
+
+If you use the [snapshot-diff](https://github.com/jest-community/snapshot-diff)
+library to save snapshot diffs, it won't work out of the box because this
+library uses the DOM which is mutable. Changes don't return new objects so
+snapshot-diff will think it's the same object and avoid diffing it.
+
+Luckily there's an easy way to make it work: clone the DOM when passing it into
+snapshot-diff. It looks like this:
+
+```js
+const firstVersion = container.cloneNode(true)
+// Do some changes
+snapshotDiff(firstVersion, container.cloneNode(true))
+```
+
+
+
+
+
+How do I fix "an update was not wrapped in act(...)" warnings?
+
+This warning is usually caused by an async operation causing an update after the
+test has already finished. There are 2 approaches to resolve it:
+
+1. Wait for the result of the operation in your test by using one of
+ [the async utilities](dom-testing-library/api-async.mdx) like
+ [waitFor](dom-testing-library/api-async.mdx#waitfor) or a
+ [`find*` query](queries/about.mdx#types-of-queries). For example:
+ `const userAddress = await findByLabel(/address/i)`.
+2. Mocking out the asynchronous operation so that it doesn't trigger state
+ updates.
+
+Generally speaking, approach 1 is preferred since it better matches the
+expectations of a user interacting with your app.
+
+In addition, you may find
+[this blog post](https://kentcdodds.com/blog/write-fewer-longer-tests) helpful
+as you consider how best to write tests that give you confidence and avoid these
+warnings.
+
+
+
+
+
+What level of a component tree should I test? Children, parents, or both?
+
+Following the guiding principle of this library, it is useful to break down how
+tests are organized around how the user experiences and interacts with
+application functionality rather than around specific components themselves. In
+some cases, for example for reusable component libraries, it might be useful to
+include developers in the list of users to test for and test each of the
+reusable components individually. Other times, the specific break down of a
+component tree is just an implementation detail and testing every component
+within that tree individually can cause issues (see
+https://kentcdodds.com/blog/avoid-the-test-user).
+
+In practice this means that it is often preferable to test high enough up the
+component tree to simulate realistic user interactions. The question of whether
+it is worth additionally testing at a higher or lower level on top of this comes
+down to a question of tradeoffs and what will provide enough value for the cost
+(see https://kentcdodds.com/blog/unit-vs-integration-vs-e2e-tests on more info
+on different levels of testing).
+
+For a more in-depth discussion of this topic see
+[this video](https://youtu.be/0qmPdcV-rN8).
+
+
+
+
+
+
+
+[guiding-principle]: https://twitter.com/kentcdodds/status/977018512689455106
+
+
diff --git a/docs/react-testing-library/intro.md b/docs/react-testing-library/intro.mdx
similarity index 68%
rename from docs/react-testing-library/intro.md
rename to docs/react-testing-library/intro.mdx
index b7add1f87..7d8d026c4 100644
--- a/docs/react-testing-library/intro.md
+++ b/docs/react-testing-library/intro.mdx
@@ -7,11 +7,23 @@ sidebar_label: Introduction
[`React Testing Library`][gh] builds on top of `DOM Testing Library` by adding
APIs for working with React components.
+## Installation
+
+To get started with `React Testing Library`, you'll need to install it together
+with its peerDependency `@testing-library/dom`:
+
+```bash npm2yarn
+npm install --save-dev @testing-library/react @testing-library/dom
```
-npm install --save-dev @testing-library/react
-```
-- [React Testing Library on GitHub][gh]
+### With TypeScript
+
+To get full type coverage, you need to install the types for `react` and `react-dom` as
+well:
+
+```bash npm2yarn
+npm install --save-dev @testing-library/react @testing-library/dom @types/react @types/react-dom
+```
[gh]: https://github.com/testing-library/react-testing-library
@@ -32,11 +44,11 @@ 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-principles.md)
+> [The more your tests resemble the way your software is used, the more confidence they can give you.](guiding-principles.mdx)
-So rather than dealing with instances of rendered react components, your tests
+So rather than dealing with instances of rendered React components, your tests
will work with actual DOM nodes. The utilities this library provides facilitate
-querying the DOM in the same way the user would. Finding for elements by their
+querying the DOM in the same way the user would. Finding form elements by their
label text (just like a user would), finding links and buttons from their text
(like a user would). It also exposes a recommended way to find elements by a
`data-testid` as an "escape hatch" for elements where the text content and label
@@ -51,24 +63,32 @@ This library is a replacement for [Enzyme](http://airbnb.io/enzyme/). While you
_can_ follow these guidelines using Enzyme itself, enforcing this is harder
because of all the extra utilities that Enzyme provides (utilities which
facilitate testing implementation details). Read more about this in
-[the FAQ](./faq).
+[the FAQ](react-testing-library/faq.mdx).
**What this library is not**:
1. A test runner or framework
2. Specific to a testing framework (though we recommend Jest as our preference,
the library works with any framework. See
- [Using Without Jest](./setup#using-without-jest))
+ [Using Without Jest](setup.mdx#using-without-jest))
> NOTE: This library is built on top of
-> [`DOM Testing Library`](dom-testing-library/intro.md) which is where most of
+> [`DOM Testing Library`](dom-testing-library/intro.mdx) which is where most of
> the logic behind the queries is.
-## Video
+## Tutorials
Have a look at the "What is React Testing library?" video below for an
introduction to the library.
-
+
+
+Also, don't miss this
+[tutorial for React Testing Library](https://www.robinwieruch.de/react-testing-library).
diff --git a/docs/react-testing-library/migrate-from-enzyme.mdx b/docs/react-testing-library/migrate-from-enzyme.mdx
new file mode 100644
index 000000000..cf04d9298
--- /dev/null
+++ b/docs/react-testing-library/migrate-from-enzyme.mdx
@@ -0,0 +1,368 @@
+---
+id: migrate-from-enzyme
+title: Migrate from Enzyme
+sidebar_label: Migrate from Enzyme
+---
+
+This page is intended for developers who have experience with Enzyme and are
+trying to understand how to migrate to React Testing Library. It does not go
+into great detail about how to migrate all types of tests, but it does have some
+helpful information for those who are comparing Enzyme with React Testing
+Library.
+
+## What is React Testing Library?
+
+React Testing Library is part of an open-source project named
+[Testing Library](https://github.com/testing-library). There are several other
+helpful tools and libraries in the Testing Library project which you can use to
+write more concise and useful tests. Besides React Testing Library, here are
+some of the project's other libraries that can help you along the way:
+
+- **[@testing-library/jest-dom](https://github.com/testing-library/jest-dom)**:
+ `jest-dom` provides a set of custom Jest matchers that you can use to extend
+ Jest. These make your tests more declarative, clearer to read, and easier to
+ maintain.
+
+- **[@testing-library/user-event](https://github.com/testing-library/user-event):**
+ `user-event` tries to simulate the real events that happen in the browser as
+ the user interacts with elements on the page. For example,
+ `userEvent.click(checkbox)` would change the state of the checkbox.
+
+## Why should I use React Testing Library?
+
+Enzyme is a powerful test library, and its contributors did a lot for the
+JavaScript community. In fact, many of the React Testing Library maintainers
+used and contributed to Enzyme for years before developing and working on React
+Testing Library. So we want to say thank you to the contributors of Enzyme!
+
+The primary purpose of React Testing Library is to increase confidence in your
+tests by testing your components in the way a user would use them. Users don't
+care what happens behind the scenes, they just see and interact with the output.
+Instead of accessing the components' internal APIs or evaluating their `state`,
+you'll get more confidence by writing your tests based on the component output.
+
+React Testing Library aims to solve the problem that many developers face when
+writing tests with Enzyme, which allows (and encourages) developers to test
+[implementation details](https://kentcdodds.com/blog/testing-implementation-details).
+Tests which do this ultimately prevent you from modifying and refactoring the
+component without changing its tests. As a result, the tests slow down
+development speed and productivity. Every small change may require rewriting
+some part of your tests, even if the change does not affect the component's
+output.
+
+Rewriting your tests in React Testing library is worthwhile because you'll be
+trading tests that slow you down for tests that give you more confidence and
+increase your productivity in the long run.
+
+## How to migrate from Enzyme to React Testing Library?
+
+To ensure a successful migration, we recommend doing it incrementally by running
+the two test libraries side by side in the same application, porting your Enzyme
+tests to React Testing Library one by one. That makes it possible to migrate
+even large and complex applications without disrupting other business because
+the work can be done collaboratively and spread out over time.
+
+## Install React Testing Library
+
+First, install React Testing Library and the `jest-dom` helper library (you can
+check [this page](setup.mdx) for the complete installation and setup guide).
+
+```bash npm2yarn
+npm install --save-dev @testing-library/react @testing-library/jest-dom
+```
+
+## Import React Testing Library to your test
+
+If you're using Jest (you can use other test frameworks), then you only need to
+import the following modules into your test file:
+
+```jsx
+// import React so you can use JSX (React.createElement) in your test
+import React from 'react'
+
+/**
+ * render: lets us render the component as React would
+ * screen: a utility for finding elements the same way the user does
+ */
+import {render, screen} from '@testing-library/react'
+```
+
+The test structure can be the same as you would write with Enzyme:
+
+```jsx
+test('test title', () => {
+ // Your tests come here...
+})
+```
+
+> Note: you can also use `describe` and `it` blocks with React Testing Library.
+> React Testing Library doesn't replace Jest, just Enzyme. We recommend `test`
+> because it helps with this:
+> [Avoid Nesting When You're Testing](https://kentcdodds.com/blog/avoid-nesting-when-youre-testing).
+
+## Basic Enzyme to React Testing Library migration examples
+
+One thing to keep in mind is that there's not a one-to-one mapping of Enzyme
+features to React Testing Library features. Many Enzyme features result in
+inefficient tests anyway, so some of the features you're accustomed to with
+Enzyme need to be left behind (no more need for a `wrapper` variable or
+`wrapper.update()` calls, etc.).
+
+React Testing Library has helpful queries which let you access your component's
+elements and their properties. We'll show some typical Enzyme tests along with
+alternatives using React Testing Library.
+
+Let's say we have a `Welcome` component which shows a welcome message. We will
+have a look at both Enzyme and React Testing Library tests to learn how we can
+test this component:
+
+**React Component**
+
+The following component gets a `name` from `props` and shows a welcome message
+in an `h1` element. It also has a text input which users can change to a
+different name, and the template updates accordingly. Check the live version on
+[CodeSandbox](https://codesandbox.io/s/ecstatic-hellman-fh7in).
+
+```jsx
+const Welcome = props => {
+ const [values, setValues] = useState({
+ firstName: props.firstName,
+ lastName: props.lastName,
+ })
+
+ const handleChange = event => {
+ setValues({...values, [event.target.name]: event.target.value})
+ }
+
+ return (
+
+
+ Welcome, {values.firstName} {values.lastName}
+
+
+
+
+ )
+}
+
+export default Welcome
+```
+
+### Test 1: Render the component, and check if the `h1` value is correct
+
+**Enzyme test**
+
+```jsx
+test('has correct welcome text', () => {
+ const wrapper = shallow( )
+ expect(wrapper.find('h1').text()).toEqual('Welcome, John Doe')
+})
+```
+
+**React Testing library**
+
+```jsx
+test('has correct welcome text', () => {
+ render( )
+ expect(screen.getByRole('heading')).toHaveTextContent('Welcome, John Doe')
+})
+```
+
+As you can see, the tests are pretty similar. Enzyme's `shallow` renderer
+doesn't render sub-components, so React Testing Library's `render` method is
+more similar to Enzyme's `mount` method.
+
+In React Testing Library, you don't need to assign the `render` result to a
+variable (i.e. `wrapper`). You can simply access the rendered output by calling
+functions on the `screen` object. The other good thing to know is that React
+Testing Library automatically cleans up the environment after each test so you
+don't need to call `cleanup` in an `afterEach` or `beforeEach` function.
+
+The other thing that you might notice is `getByRole` which has `'heading'` as
+its argument. `'heading'` is the accessible role of the `h1` element. You can
+learn more about them on the [queries documentation page](queries/byrole.mdx).
+One of the things people quickly learn to love about Testing Library is how it
+encourages you to write more accessible applications (because if it's not
+accessible, then it's harder to test).
+
+### Test 2: Input texts must have correct value
+
+In the component above, the input values are initialized with the
+`props.firstName` and `props.lastName` values. We need to check whether the
+value is correct or not.
+
+**Enzyme**
+
+```jsx
+test('has correct input value', () => {
+ const wrapper = shallow( )
+ expect(wrapper.find('input[name="firstName"]').value).toEqual('John')
+ expect(wrapper.find('input[name="lastName"]').value).toEqual('Doe')
+})
+```
+
+**React Testing Library**
+
+```jsx
+test('has correct input value', () => {
+ render( )
+ expect(screen.getByRole('form')).toHaveFormValues({
+ firstName: 'John',
+ lastName: 'Doe',
+ })
+})
+```
+
+Cool! It's pretty simple and handy, and the tests are clear enough that we don't
+need to talk much about them. Something that you might notice is that the
+`