diff --git a/.all-contributorsrc b/.all-contributorsrc index 9b34a8ad..b22c9414 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -1,12 +1,13 @@ { "projectName": "react-testing-library", - "projectOwner": "kentcdodds", + "projectOwner": "testing-library", "repoType": "github", "files": [ "README.md" ], "imageSize": 100, "commit": false, + "skipCi": false, "contributors": [ { "login": "kentcdodds", @@ -76,6 +77,1314 @@ "contributions": [ "platform" ] + }, + { + "login": "antoaravinth", + "name": "Anto Aravinth", + "avatar_url": "https://avatars1.githubusercontent.com/u/1241511?s=460&v=4", + "profile": "https://github.com/antoaravinth", + "contributions": [ + "code", + "test", + "doc" + ] + }, + { + "login": "JonahMoses", + "name": "Jonah Moses", + "avatar_url": "https://avatars2.githubusercontent.com/u/3462296?v=4", + "profile": "https://github.com/JonahMoses", + "contributions": [ + "doc" + ] + }, + { + "login": "lgandecki", + "name": "Łukasz Gandecki", + "avatar_url": "https://avatars1.githubusercontent.com/u/4002543?v=4", + "profile": "http://team.thebrain.pro", + "contributions": [ + "code", + "test", + "doc" + ] + }, + { + "login": "sompylasar", + "name": "Ivan Babak", + "avatar_url": "https://avatars2.githubusercontent.com/u/498274?v=4", + "profile": "https://sompylasar.github.io", + "contributions": [ + "bug", + "ideas" + ] + }, + { + "login": "jday3", + "name": "Jesse Day", + "avatar_url": "https://avatars3.githubusercontent.com/u/4439618?v=4", + "profile": "https://github.com/jday3", + "contributions": [ + "code" + ] + }, + { + "login": "gnapse", + "name": "Ernesto García", + "avatar_url": "https://avatars0.githubusercontent.com/u/15199?v=4", + "profile": "http://gnapse.github.io", + "contributions": [ + "question", + "code", + "doc" + ] + }, + { + "login": "jomaxx", + "name": "Josef Maxx Blake", + "avatar_url": "https://avatars2.githubusercontent.com/u/2747424?v=4", + "profile": "http://jomaxx.com", + "contributions": [ + "code", + "doc", + "test" + ] + }, + { + "login": "mbaranovski", + "name": "Michal Baranowski", + "avatar_url": "https://avatars1.githubusercontent.com/u/29602306?v=4", + "profile": "https://twitter.com/baranovskim", + "contributions": [ + "blog", + "tutorial" + ] + }, + { + "login": "aputhin", + "name": "Arthur Puthin", + "avatar_url": "https://avatars3.githubusercontent.com/u/13985684?v=4", + "profile": "https://github.com/aputhin", + "contributions": [ + "doc" + ] + }, + { + "login": "thchia", + "name": "Thomas Chia", + "avatar_url": "https://avatars2.githubusercontent.com/u/21194045?v=4", + "profile": "https://github.com/thchia", + "contributions": [ + "code", + "doc" + ] + }, + { + "login": "thiagopaiva99", + "name": "Thiago Galvani", + "avatar_url": "https://avatars3.githubusercontent.com/u/20430611?v=4", + "profile": "http://ilegra.com/", + "contributions": [ + "doc" + ] + }, + { + "login": "ChrisWcs", + "name": "Christian", + "avatar_url": "https://avatars1.githubusercontent.com/u/19828824?v=4", + "profile": "http://Chriswcs.github.io", + "contributions": [ + "test" + ] + }, + { + "login": "alexkrolick", + "name": "Alex Krolick", + "avatar_url": "https://avatars3.githubusercontent.com/u/1571667?v=4", + "profile": "https://alexkrolick.com", + "contributions": [ + "question", + "doc", + "example", + "ideas" + ] + }, + { + "login": "johann-sonntagbauer", + "name": "Johann Hubert Sonntagbauer", + "avatar_url": "https://avatars3.githubusercontent.com/u/1239401?v=4", + "profile": "https://github.com/johann-sonntagbauer", + "contributions": [ + "code", + "doc", + "test" + ] + }, + { + "login": "maddijoyce", + "name": "Maddi Joyce", + "avatar_url": "https://avatars2.githubusercontent.com/u/2224291?v=4", + "profile": "http://www.maddijoyce.com", + "contributions": [ + "code" + ] + }, + { + "login": "RyanAtViceSoftware", + "name": "Ryan Vice", + "avatar_url": "https://avatars2.githubusercontent.com/u/10080111?v=4", + "profile": "http://www.vicesoftware.com", + "contributions": [ + "doc" + ] + }, + { + "login": "iwilsonq", + "name": "Ian Wilson", + "avatar_url": "https://avatars1.githubusercontent.com/u/7942604?v=4", + "profile": "https://ianwilson.io", + "contributions": [ + "blog", + "tutorial" + ] + }, + { + "login": "InExtremaRes", + "name": "Daniel", + "avatar_url": "https://avatars2.githubusercontent.com/u/1635491?v=4", + "profile": "https://github.com/InExtremaRes", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "Gpx", + "name": "Giorgio Polvara", + "avatar_url": "https://avatars0.githubusercontent.com/u/767959?v=4", + "profile": "https://twitter.com/Gpx", + "contributions": [ + "bug", + "ideas" + ] + }, + { + "login": "jgoz", + "name": "John Gozde", + "avatar_url": "https://avatars2.githubusercontent.com/u/132233?v=4", + "profile": "https://github.com/jgoz", + "contributions": [ + "code" + ] + }, + { + "login": "SavePointSam", + "name": "Sam Horton", + "avatar_url": "https://avatars0.githubusercontent.com/u/8203211?v=4", + "profile": "https://twitter.com/SavePointSam", + "contributions": [ + "doc", + "example", + "ideas" + ] + }, + { + "login": "rkotze", + "name": "Richard Kotze (mobile)", + "avatar_url": "https://avatars2.githubusercontent.com/u/10452163?v=4", + "profile": "http://www.richardkotze.com", + "contributions": [ + "doc" + ] + }, + { + "login": "sotobuild", + "name": "Brahian E. Soto Mercedes", + "avatar_url": "https://avatars2.githubusercontent.com/u/10819833?v=4", + "profile": "https://github.com/sotobuild", + "contributions": [ + "doc" + ] + }, + { + "login": "bdelaforest", + "name": "Benoit de La Forest", + "avatar_url": "https://avatars2.githubusercontent.com/u/7151559?v=4", + "profile": "https://github.com/bdelaforest", + "contributions": [ + "doc" + ] + }, + { + "login": "thesalah", + "name": "Salah", + "avatar_url": "https://avatars3.githubusercontent.com/u/6624197?v=4", + "profile": "https://github.com/thesalah", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "icfantv", + "name": "Adam Gordon", + "avatar_url": "https://avatars2.githubusercontent.com/u/370054?v=4", + "profile": "http://gordonizer.com", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "silvenon", + "name": "Matija Marohnić", + "avatar_url": "https://avatars2.githubusercontent.com/u/471278?v=4", + "profile": "https://silvenon.com", + "contributions": [ + "doc" + ] + }, + { + "login": "Dajust", + "name": "Justice Mba", + "avatar_url": "https://avatars3.githubusercontent.com/u/8015514?v=4", + "profile": "https://github.com/Dajust", + "contributions": [ + "doc" + ] + }, + { + "login": "MarkPollmann", + "name": "Mark Pollmann", + "avatar_url": "https://avatars2.githubusercontent.com/u/5286559?v=4", + "profile": "https://markpollmann.com/", + "contributions": [ + "doc" + ] + }, + { + "login": "ehteshamkafeel", + "name": "Ehtesham Kafeel", + "avatar_url": "https://avatars1.githubusercontent.com/u/1213123?v=4", + "profile": "https://github.com/ehteshamkafeel", + "contributions": [ + "code", + "doc" + ] + }, + { + "login": "jpavon", + "name": "Julio Pavón", + "avatar_url": "https://avatars2.githubusercontent.com/u/1493505?v=4", + "profile": "http://jpavon.com", + "contributions": [ + "code" + ] + }, + { + "login": "duncanleung", + "name": "Duncan L", + "avatar_url": "https://avatars3.githubusercontent.com/u/1765048?v=4", + "profile": "http://www.duncanleung.com/", + "contributions": [ + "doc", + "example" + ] + }, + { + "login": "tyagow", + "name": "Tiago Almeida", + "avatar_url": "https://avatars1.githubusercontent.com/u/700778?v=4", + "profile": "https://www.linkedin.com/in/tyagow/?locale=en_US", + "contributions": [ + "doc" + ] + }, + { + "login": "rbrtsmith", + "name": "Robert Smith", + "avatar_url": "https://avatars2.githubusercontent.com/u/4982001?v=4", + "profile": "http://rbrtsmith.com/", + "contributions": [ + "bug" + ] + }, + { + "login": "zgreen", + "name": "Zach Green", + "avatar_url": "https://avatars0.githubusercontent.com/u/1700355?v=4", + "profile": "https://offbyone.tech", + "contributions": [ + "doc" + ] + }, + { + "login": "dadamssg", + "name": "dadamssg", + "avatar_url": "https://avatars3.githubusercontent.com/u/881986?v=4", + "profile": "https://github.com/dadamssg", + "contributions": [ + "doc" + ] + }, + { + "login": "YazanAabeed", + "name": "Yazan Aabed", + "avatar_url": "https://avatars0.githubusercontent.com/u/8734097?v=4", + "profile": "https://www.yaabed.com/", + "contributions": [ + "blog" + ] + }, + { + "login": "timbonicus", + "name": "Tim", + "avatar_url": "https://avatars0.githubusercontent.com/u/556258?v=4", + "profile": "https://github.com/timbonicus", + "contributions": [ + "bug", + "code", + "doc", + "test" + ] + }, + { + "login": "divyanshu013", + "name": "Divyanshu Maithani", + "avatar_url": "https://avatars3.githubusercontent.com/u/6682655?v=4", + "profile": "http://divyanshu.xyz", + "contributions": [ + "tutorial", + "video" + ] + }, + { + "login": "metagrover", + "name": "Deepak Grover", + "avatar_url": "https://avatars2.githubusercontent.com/u/9116042?v=4", + "profile": "https://www.linkedin.com/in/metagrover", + "contributions": [ + "tutorial", + "video" + ] + }, + { + "login": "eyalcohen4", + "name": "Eyal Cohen", + "avatar_url": "https://avatars0.githubusercontent.com/u/16276358?v=4", + "profile": "https://github.com/eyalcohen4", + "contributions": [ + "doc" + ] + }, + { + "login": "petermakowski", + "name": "Peter Makowski", + "avatar_url": "https://avatars3.githubusercontent.com/u/7452681?v=4", + "profile": "https://github.com/petermakowski", + "contributions": [ + "doc" + ] + }, + { + "login": "Michielnuyts", + "name": "Michiel Nuyts", + "avatar_url": "https://avatars2.githubusercontent.com/u/20361668?v=4", + "profile": "https://github.com/Michielnuyts", + "contributions": [ + "doc" + ] + }, + { + "login": "joeynimu", + "name": "Joe Ng'ethe", + "avatar_url": "https://avatars0.githubusercontent.com/u/1195863?v=4", + "profile": "https://github.com/joeynimu", + "contributions": [ + "code", + "doc" + ] + }, + { + "login": "Enikol", + "name": "Kate", + "avatar_url": "https://avatars3.githubusercontent.com/u/19998290?v=4", + "profile": "https://github.com/Enikol", + "contributions": [ + "doc" + ] + }, + { + "login": "SeanRParker", + "name": "Sean", + "avatar_url": "https://avatars1.githubusercontent.com/u/11980217?v=4", + "profile": "http://www.seanrparker.com", + "contributions": [ + "doc" + ] + }, + { + "login": "jlongster", + "name": "James Long", + "avatar_url": "https://avatars2.githubusercontent.com/u/17031?v=4", + "profile": "http://jlongster.com", + "contributions": [ + "ideas", + "platform" + ] + }, + { + "login": "hhagely", + "name": "Herb Hagely", + "avatar_url": "https://avatars1.githubusercontent.com/u/10118777?v=4", + "profile": "https://github.com/hhagely", + "contributions": [ + "example" + ] + }, + { + "login": "themostcolm", + "name": "Alex Wendte", + "avatar_url": "https://avatars2.githubusercontent.com/u/5779538?v=4", + "profile": "http://www.wendtedesigns.com/", + "contributions": [ + "example" + ] + }, + { + "login": "M0nica", + "name": "Monica Powell", + "avatar_url": "https://avatars0.githubusercontent.com/u/6998954?v=4", + "profile": "http://www.aboutmonica.com", + "contributions": [ + "doc" + ] + }, + { + "login": "sivkoff", + "name": "Vitaly Sivkov", + "avatar_url": "https://avatars1.githubusercontent.com/u/2699953?v=4", + "profile": "http://sivkoff.com", + "contributions": [ + "code" + ] + }, + { + "login": "weyert", + "name": "Weyert de Boer", + "avatar_url": "https://avatars3.githubusercontent.com/u/7049?v=4", + "profile": "https://github.com/weyert", + "contributions": [ + "ideas", + "review", + "design" + ] + }, + { + "login": "EstebanMarin", + "name": "EstebanMarin", + "avatar_url": "https://avatars3.githubusercontent.com/u/13613037?v=4", + "profile": "https://github.com/EstebanMarin", + "contributions": [ + "doc" + ] + }, + { + "login": "vctormb", + "name": "Victor Martins", + "avatar_url": "https://avatars2.githubusercontent.com/u/13953703?v=4", + "profile": "https://github.com/vctormb", + "contributions": [ + "doc" + ] + }, + { + "login": "RoystonS", + "name": "Royston Shufflebotham", + "avatar_url": "https://avatars0.githubusercontent.com/u/19773?v=4", + "profile": "https://github.com/RoystonS", + "contributions": [ + "bug", + "doc", + "example" + ] + }, + { + "login": "chrbala", + "name": "chrbala", + "avatar_url": "https://avatars0.githubusercontent.com/u/6834804?v=4", + "profile": "https://github.com/chrbala", + "contributions": [ + "code" + ] + }, + { + "login": "donavon", + "name": "Donavon West", + "avatar_url": "https://avatars3.githubusercontent.com/u/887639?v=4", + "profile": "http://donavon.com", + "contributions": [ + "code", + "doc", + "ideas", + "test" + ] + }, + { + "login": "maisano", + "name": "Richard Maisano", + "avatar_url": "https://avatars2.githubusercontent.com/u/689081?v=4", + "profile": "https://github.com/maisano", + "contributions": [ + "code" + ] + }, + { + "login": "marcobiedermann", + "name": "Marco Biedermann", + "avatar_url": "https://avatars0.githubusercontent.com/u/5244986?v=4", + "profile": "https://www.marcobiedermann.com", + "contributions": [ + "code", + "maintenance", + "test" + ] + }, + { + "login": "alexzherdev", + "name": "Alex Zherdev", + "avatar_url": "https://avatars3.githubusercontent.com/u/93752?v=4", + "profile": "https://github.com/alexzherdev", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "Andrewmat", + "name": "André Matulionis dos Santos", + "avatar_url": "https://avatars0.githubusercontent.com/u/5133846?v=4", + "profile": "https://twitter.com/Andrewmat", + "contributions": [ + "code", + "example", + "test" + ] + }, + { + "login": "FredyC", + "name": "Daniel K.", + "avatar_url": "https://avatars0.githubusercontent.com/u/1096340?v=4", + "profile": "https://github.com/FredyC", + "contributions": [ + "bug", + "code", + "ideas", + "test", + "review" + ] + }, + { + "login": "mohamedmagdy17593", + "name": "mohamedmagdy17593", + "avatar_url": "https://avatars0.githubusercontent.com/u/40938625?v=4", + "profile": "https://github.com/mohamedmagdy17593", + "contributions": [ + "code" + ] + }, + { + "login": "lorensr", + "name": "Loren ☺️", + "avatar_url": "https://avatars2.githubusercontent.com/u/251288?v=4", + "profile": "http://lorensr.me", + "contributions": [ + "doc" + ] + }, + { + "login": "MarkFalconbridge", + "name": "MarkFalconbridge", + "avatar_url": "https://avatars1.githubusercontent.com/u/20678943?v=4", + "profile": "https://github.com/MarkFalconbridge", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "viniciusavieira", + "name": "Vinicius", + "avatar_url": "https://avatars0.githubusercontent.com/u/2073019?v=4", + "profile": "https://github.com/viniciusavieira", + "contributions": [ + "doc", + "example" + ] + }, + { + "login": "pschyma", + "name": "Peter Schyma", + "avatar_url": "https://avatars2.githubusercontent.com/u/2489928?v=4", + "profile": "https://github.com/pschyma", + "contributions": [ + "code" + ] + }, + { + "login": "ianschmitz", + "name": "Ian Schmitz", + "avatar_url": "https://avatars1.githubusercontent.com/u/6355370?v=4", + "profile": "https://github.com/ianschmitz", + "contributions": [ + "doc" + ] + }, + { + "login": "joual", + "name": "Joel Marcotte", + "avatar_url": "https://avatars0.githubusercontent.com/u/157877?v=4", + "profile": "https://github.com/joual", + "contributions": [ + "bug", + "test", + "code" + ] + }, + { + "login": "aledustet", + "name": "Alejandro Dustet", + "avatar_url": "https://avatars3.githubusercontent.com/u/2413802?v=4", + "profile": "http://aledustet.com", + "contributions": [ + "bug" + ] + }, + { + "login": "bcarroll22", + "name": "Brandon Carroll", + "avatar_url": "https://avatars2.githubusercontent.com/u/11020406?v=4", + "profile": "https://github.com/bcarroll22", + "contributions": [ + "doc" + ] + }, + { + "login": "lucas0707", + "name": "Lucas Machado", + "avatar_url": "https://avatars1.githubusercontent.com/u/26284338?v=4", + "profile": "https://github.com/lucas0707", + "contributions": [ + "doc" + ] + }, + { + "login": "pascalduez", + "name": "Pascal Duez", + "avatar_url": "https://avatars3.githubusercontent.com/u/335467?v=4", + "profile": "http://pascalduez.me", + "contributions": [ + "platform" + ] + }, + { + "login": "NMinhNguyen", + "name": "Minh Nguyen", + "avatar_url": "https://avatars3.githubusercontent.com/u/2852660?v=4", + "profile": "https://twitter.com/minh_ngvyen", + "contributions": [ + "code" + ] + }, + { + "login": "LiaoJimmy", + "name": "LiaoJimmy", + "avatar_url": "https://avatars0.githubusercontent.com/u/11155585?v=4", + "profile": "http://iababy46.blogspot.tw/", + "contributions": [ + "doc" + ] + }, + { + "login": "threepointone", + "name": "Sunil Pai", + "avatar_url": "https://avatars2.githubusercontent.com/u/18808?v=4", + "profile": "https://github.com/threepointone", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "gaearon", + "name": "Dan Abramov", + "avatar_url": "https://avatars0.githubusercontent.com/u/810438?v=4", + "profile": "http://twitter.com/dan_abramov", + "contributions": [ + "review" + ] + }, + { + "login": "ChristianMurphy", + "name": "Christian Murphy", + "avatar_url": "https://avatars3.githubusercontent.com/u/3107513?v=4", + "profile": "https://github.com/ChristianMurphy", + "contributions": [ + "infra" + ] + }, + { + "login": "jeetiss", + "name": "Ivakhnenko Dmitry", + "avatar_url": "https://avatars1.githubusercontent.com/u/6726016?v=4", + "profile": "https://jeetiss.github.io/", + "contributions": [ + "code" + ] + }, + { + "login": "jamesgeorge007", + "name": "James George", + "avatar_url": "https://avatars2.githubusercontent.com/u/25279263?v=4", + "profile": "https://ghuser.io/jamesgeorge007", + "contributions": [ + "doc" + ] + }, + { + "login": "JSFernandes", + "name": "João Fernandes", + "avatar_url": "https://avatars1.githubusercontent.com/u/1075053?v=4", + "profile": "https://joaofernandes.me/", + "contributions": [ + "doc" + ] + }, + { + "login": "alejandroperea", + "name": "Alejandro Perea", + "avatar_url": "https://avatars3.githubusercontent.com/u/6084749?v=4", + "profile": "https://github.com/alejandroperea", + "contributions": [ + "review" + ] + }, + { + "login": "nickmccurdy", + "name": "Nick McCurdy", + "avatar_url": "https://avatars0.githubusercontent.com/u/927220?v=4", + "profile": "https://nickmccurdy.com/", + "contributions": [ + "review", + "question", + "infra" + ] + }, + { + "login": "eps1lon", + "name": "Sebastian Silbermann", + "avatar_url": "https://avatars3.githubusercontent.com/u/12292047?v=4", + "profile": "https://twitter.com/sebsilbermann", + "contributions": [ + "review" + ] + }, + { + "login": "afontcu", + "name": "Adrià Fontcuberta", + "avatar_url": "https://avatars0.githubusercontent.com/u/9197791?v=4", + "profile": "https://afontcu.dev", + "contributions": [ + "review", + "doc" + ] + }, + { + "login": "johnnyreilly", + "name": "John Reilly", + "avatar_url": "https://avatars0.githubusercontent.com/u/1010525?v=4", + "profile": "https://blog.johnnyreilly.com/", + "contributions": [ + "review" + ] + }, + { + "login": "MichaelDeBoey", + "name": "Michaël De Boey", + "avatar_url": "https://avatars3.githubusercontent.com/u/6643991?v=4", + "profile": "https://michaeldeboey.be", + "contributions": [ + "review", + "code" + ] + }, + { + "login": "cimbul", + "name": "Tim Yates", + "avatar_url": "https://avatars2.githubusercontent.com/u/927923?v=4", + "profile": "https://cimbul.com", + "contributions": [ + "review" + ] + }, + { + "login": "eventualbuddha", + "name": "Brian Donovan", + "avatar_url": "https://avatars3.githubusercontent.com/u/1938?v=4", + "profile": "https://github.com/eventualbuddha", + "contributions": [ + "code" + ] + }, + { + "login": "JaysQubeXon", + "name": "Noam Gabriel Jacobson", + "avatar_url": "https://avatars1.githubusercontent.com/u/18309230?v=4", + "profile": "https://github.com/JaysQubeXon", + "contributions": [ + "doc" + ] + }, + { + "login": "rvdkooy", + "name": "Ronald van der Kooij", + "avatar_url": "https://avatars1.githubusercontent.com/u/4119960?v=4", + "profile": "https://github.com/rvdkooy", + "contributions": [ + "test", + "code" + ] + }, + { + "login": "aayushrajvanshi", + "name": "Aayush Rajvanshi", + "avatar_url": "https://avatars0.githubusercontent.com/u/14968551?v=4", + "profile": "https://github.com/aayushrajvanshi", + "contributions": [ + "doc" + ] + }, + { + "login": "ely-alamillo", + "name": "Ely Alamillo", + "avatar_url": "https://avatars2.githubusercontent.com/u/24350492?v=4", + "profile": "https://elyalamillo.com", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "danieljcafonso", + "name": "Daniel Afonso", + "avatar_url": "https://avatars3.githubusercontent.com/u/35337607?v=4", + "profile": "https://github.com/danieljcafonso", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "LaurensBosscher", + "name": "Laurens Bosscher", + "avatar_url": "https://avatars0.githubusercontent.com/u/13363196?v=4", + "profile": "http://www.laurensbosscher.nl", + "contributions": [ + "code" + ] + }, + { + "login": "sakito21", + "name": "Sakito Mukai", + "avatar_url": "https://avatars1.githubusercontent.com/u/15010907?v=4", + "profile": "https://twitter.com/__sakito__", + "contributions": [ + "doc" + ] + }, + { + "login": "tteke", + "name": "Türker Teke", + "avatar_url": "https://avatars3.githubusercontent.com/u/12457162?v=4", + "profile": "http://turkerteke.com", + "contributions": [ + "doc" + ] + }, + { + "login": "zbrogz", + "name": "Zach Brogan", + "avatar_url": "https://avatars1.githubusercontent.com/u/319162?v=4", + "profile": "http://linkedin.com/in/zachbrogan", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "ryota-murakami", + "name": "Ryota Murakami", + "avatar_url": "https://avatars2.githubusercontent.com/u/5501268?v=4", + "profile": "https://ryota-murakami.github.io/", + "contributions": [ + "doc" + ] + }, + { + "login": "hottmanmichael", + "name": "Michael Hottman", + "avatar_url": "https://avatars3.githubusercontent.com/u/10534502?v=4", + "profile": "https://github.com/hottmanmichael", + "contributions": [ + "ideas" + ] + }, + { + "login": "stevenfitzpatrick", + "name": "Steven Fitzpatrick", + "avatar_url": "https://avatars0.githubusercontent.com/u/23268855?v=4", + "profile": "https://github.com/stevenfitzpatrick", + "contributions": [ + "bug" + ] + }, + { + "login": "juangl", + "name": "Juan Je García", + "avatar_url": "https://avatars0.githubusercontent.com/u/1887029?v=4", + "profile": "https://github.com/juangl", + "contributions": [ + "doc" + ] + }, + { + "login": "Ishaan28malik", + "name": "Championrunner", + "avatar_url": "https://avatars3.githubusercontent.com/u/27343592?v=4", + "profile": "https://ghuser.io/Ishaan28malik", + "contributions": [ + "doc" + ] + }, + { + "login": "samtsai", + "name": "Sam Tsai", + "avatar_url": "https://avatars0.githubusercontent.com/u/225526?v=4", + "profile": "https://github.com/samtsai", + "contributions": [ + "code", + "test", + "doc" + ] + }, + { + "login": "screendriver", + "name": "Christian Rackerseder", + "avatar_url": "https://avatars0.githubusercontent.com/u/149248?v=4", + "profile": "https://www.echooff.dev", + "contributions": [ + "code" + ] + }, + { + "login": "NiGhTTraX", + "name": "Andrei Picus", + "avatar_url": "https://avatars0.githubusercontent.com/u/485061?v=4", + "profile": "https://github.com/NiGhTTraX", + "contributions": [ + "bug", + "review" + ] + }, + { + "login": "kettanaito", + "name": "Artem Zakharchenko", + "avatar_url": "https://avatars3.githubusercontent.com/u/14984911?v=4", + "profile": "https://redd.one", + "contributions": [ + "doc" + ] + }, + { + "login": "michael-siek", + "name": "Michael", + "avatar_url": "https://avatars0.githubusercontent.com/u/45568605?v=4", + "profile": "http://michaelsiek.com", + "contributions": [ + "doc" + ] + }, + { + "login": "2dubbing", + "name": "Braden Lee", + "avatar_url": "https://avatars2.githubusercontent.com/u/15885679?v=4", + "profile": "http://2dubbing.tistory.com", + "contributions": [ + "doc" + ] + }, + { + "login": "kamranayub", + "name": "Kamran Ayub", + "avatar_url": "https://avatars1.githubusercontent.com/u/563819?v=4", + "profile": "http://kamranicus.com/", + "contributions": [ + "code", + "test" + ] + }, + { + "login": "MatanBobi", + "name": "Matan Borenkraout", + "avatar_url": "https://avatars2.githubusercontent.com/u/12711091?v=4", + "profile": "https://twitter.com/matanbobi", + "contributions": [ + "code" + ] + }, + { + "login": "radar", + "name": "Ryan Bigg", + "avatar_url": "https://avatars3.githubusercontent.com/u/2687?v=4", + "profile": "http://ryanbigg.com", + "contributions": [ + "maintenance" + ] + }, + { + "login": "antonhalim", + "name": "Anton Halim", + "avatar_url": "https://avatars1.githubusercontent.com/u/10498035?v=4", + "profile": "https://antonhalim.com", + "contributions": [ + "doc" + ] + }, + { + "login": "artem-malko", + "name": "Artem Malko", + "avatar_url": "https://avatars0.githubusercontent.com/u/1823689?v=4", + "profile": "http://artmalko.ru", + "contributions": [ + "code" + ] + }, + { + "login": "ljosberinn", + "name": "Gerrit Alex", + "avatar_url": "https://avatars1.githubusercontent.com/u/29307652?v=4", + "profile": "http://gerritalex.de", + "contributions": [ + "code" + ] + }, + { + "login": "karthick3018", + "name": "Karthick Raja", + "avatar_url": "https://avatars1.githubusercontent.com/u/47154512?v=4", + "profile": "https://github.com/karthick3018", + "contributions": [ + "code" + ] + }, + { + "login": "theashraf", + "name": "Abdelrahman Ashraf", + "avatar_url": "https://avatars1.githubusercontent.com/u/39750790?v=4", + "profile": "https://github.com/theashraf", + "contributions": [ + "code" + ] + }, + { + "login": "lidoravitan", + "name": "Lidor Avitan", + "avatar_url": "https://avatars0.githubusercontent.com/u/35113398?v=4", + "profile": "https://github.com/lidoravitan", + "contributions": [ + "doc" + ] + }, + { + "login": "ljharb", + "name": "Jordan Harband", + "avatar_url": "https://avatars1.githubusercontent.com/u/45469?v=4", + "profile": "https://github.com/ljharb", + "contributions": [ + "review", + "ideas" + ] + }, + { + "login": "marcosvega91", + "name": "Marco Moretti", + "avatar_url": "https://avatars2.githubusercontent.com/u/5365582?v=4", + "profile": "https://github.com/marcosvega91", + "contributions": [ + "code" + ] + }, + { + "login": "sanchit121", + "name": "sanchit121", + "avatar_url": "https://avatars2.githubusercontent.com/u/30828115?v=4", + "profile": "https://github.com/sanchit121", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "solufa", + "name": "Solufa", + "avatar_url": "https://avatars.githubusercontent.com/u/9402912?v=4", + "profile": "https://github.com/solufa", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "AriPerkkio", + "name": "Ari Perkkiö", + "avatar_url": "https://avatars.githubusercontent.com/u/14806298?v=4", + "profile": "https://codepen.io/ariperkkio/", + "contributions": [ + "test" + ] + }, + { + "login": "jhnns", + "name": "Johannes Ewald", + "avatar_url": "https://avatars.githubusercontent.com/u/781746?v=4", + "profile": "https://github.com/jhnns", + "contributions": [ + "code" + ] + }, + { + "login": "anpaopao", + "name": "Angus J. Pope", + "avatar_url": "https://avatars.githubusercontent.com/u/44686792?v=4", + "profile": "https://github.com/anpaopao", + "contributions": [ + "doc" + ] + }, + { + "login": "leschdom", + "name": "Dominik Lesch", + "avatar_url": "https://avatars.githubusercontent.com/u/62334278?v=4", + "profile": "https://github.com/leschdom", + "contributions": [ + "doc" + ] + }, + { + "login": "ImADrafter", + "name": "Marcos Gómez", + "avatar_url": "https://avatars.githubusercontent.com/u/44379989?v=4", + "profile": "https://github.com/ImADrafter", + "contributions": [ + "doc" + ] + }, + { + "login": "akashshyamdev", + "name": "Akash Shyam", + "avatar_url": "https://avatars.githubusercontent.com/u/56759828?v=4", + "profile": "https://www.akashshyam.online/", + "contributions": [ + "bug" + ] + }, + { + "login": "fmeum", + "name": "Fabian Meumertzheim", + "avatar_url": "https://avatars.githubusercontent.com/u/4312191?v=4", + "profile": "https://hen.ne.ke", + "contributions": [ + "code", + "bug" + ] + }, + { + "login": "Nokel81", + "name": "Sebastian Malton", + "avatar_url": "https://avatars.githubusercontent.com/u/8225332?v=4", + "profile": "https://github.com/Nokel81", + "contributions": [ + "bug", + "code" + ] + }, + { + "login": "mboettcher", + "name": "Martin Böttcher", + "avatar_url": "https://avatars.githubusercontent.com/u/2325337?v=4", + "profile": "https://github.com/mboettcher", + "contributions": [ + "code" + ] + }, + { + "login": "TkDodo", + "name": "Dominik Dorfmeister", + "avatar_url": "https://avatars.githubusercontent.com/u/1021430?v=4", + "profile": "http://tkdodo.eu", + "contributions": [ + "code" + ] + }, + { + "login": "stephensauceda", + "name": "Stephen Sauceda", + "avatar_url": "https://avatars.githubusercontent.com/u/1017723?v=4", + "profile": "https://stephensauceda.com", + "contributions": [ + "doc" + ] + }, + { + "login": "cmdcolin", + "name": "Colin Diesh", + "avatar_url": "https://avatars.githubusercontent.com/u/6511937?v=4", + "profile": "http://cmdcolin.github.io", + "contributions": [ + "doc" + ] + }, + { + "login": "yinm", + "name": "Yusuke Iinuma", + "avatar_url": "https://avatars.githubusercontent.com/u/13295106?v=4", + "profile": "http://yinm.info", + "contributions": [ + "code" + ] + }, + { + "login": "trappar", + "name": "Jeff Way", + "avatar_url": "https://avatars.githubusercontent.com/u/525726?v=4", + "profile": "https://github.com/trappar", + "contributions": [ + "code" + ] + }, + { + "login": "bernardobelchior", + "name": "Bernardo Belchior", + "avatar_url": "https://avatars.githubusercontent.com/u/12778398?v=4", + "profile": "http://belchior.me", + "contributions": [ + "code", + "doc" + ] } - ] + ], + "contributorsPerLine": 7, + "repoHost": "https://github.com", + "commitType": "docs", + "commitConvention": "angular" } diff --git a/.bundle.main.env b/.bundle.main.env new file mode 100644 index 00000000..669fe7e4 --- /dev/null +++ b/.bundle.main.env @@ -0,0 +1,2 @@ +BUILD_GLOBALS={"react-dom/test-utils":"ReactTestUtils","react":"React","react-dom":"ReactDOM"} + diff --git a/.bundle.pure.env b/.bundle.pure.env new file mode 100644 index 00000000..fed4df2e --- /dev/null +++ b/.bundle.pure.env @@ -0,0 +1,3 @@ +BUILD_FILENAME_SUFFIX=.pure +BUILD_INPUT=src/pure.js + diff --git a/.codesandbox/ci.json b/.codesandbox/ci.json new file mode 100644 index 00000000..002bafb4 --- /dev/null +++ b/.codesandbox/ci.json @@ -0,0 +1,5 @@ +{ + "installCommand": "install:csb", + "sandboxes": ["new", "github/kentcdodds/react-testing-library-examples"], + "node": "18" +} diff --git a/.gitattributes b/.gitattributes index 391f0a4e..6313b56c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1 @@ -* text=auto -*.js text eol=lf +* text=auto eol=lf diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 6a7b4bff..496c8563 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,23 +1,67 @@ -* `react-testing-library` version: -* `node` version: -* `npm` (or `yarn`) version: +- `@testing-library/react` version: +- Testing Framework and version: + +- DOM Environment: + + + Relevant code or config ```javascript + ``` What you did: @@ -30,9 +74,20 @@ Reproduction repository: Problem description: + + Suggested solution: + + diff --git a/.github/ISSUE_TEMPLATE/Bug_Report.md b/.github/ISSUE_TEMPLATE/Bug_Report.md new file mode 100644 index 00000000..c04bef38 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_Report.md @@ -0,0 +1,76 @@ +--- +name: 🐛 Bug Report +about: Bugs, missing documentation, or unexpected behavior 🤔. +--- + + + +- `@testing-library/react` version: +- Testing Framework and version: + +- DOM Environment: + + + + +### Relevant code or config: + +```js +var your => (code) => here; +``` + + + +### What you did: + + + +### What happened: + + + +### Reproduction: + + + +### Problem description: + + + +### Suggested solution: + + diff --git a/.github/ISSUE_TEMPLATE/Feature_Request.md b/.github/ISSUE_TEMPLATE/Feature_Request.md new file mode 100644 index 00000000..357d1df7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_Request.md @@ -0,0 +1,51 @@ +--- +name: 💡 Feature Request +about: I have a suggestion (and might want to implement myself 🙂)! +--- + + + +### Describe the feature you'd like: + + + +### Suggested implementation: + + + +### Describe alternatives you've considered: + + + +### Teachability, Documentation, Adoption, Migration Strategy: + + diff --git a/.github/ISSUE_TEMPLATE/Question.md b/.github/ISSUE_TEMPLATE/Question.md new file mode 100644 index 00000000..e625486b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Question.md @@ -0,0 +1,24 @@ +--- +name: ❓ Support Question +about: 🛑 If you have a question 💬, please check out our support channels! +--- + +-------------- 👆 Click "Preview"! + +Issues on GitHub are intended to be related to problems with the library itself +and feature requests so we recommend not using this medium to ask them here 😁. + +--- + +## ❓ Support Forums + +For questions related to using the library, please visit a support community +instead of filing an issue on GitHub. You can follow the instructions in this +codesandbox to make a reproduction of your issue: https://kcd.im/rtl-help + +- Discord https://discord.gg/testing-library +- Stack Overflow + https://stackoverflow.com/questions/tagged/react-testing-library +- Documentation: https://github.com/testing-library/testing-library-docs + +**ISSUES WHICH ARE QUESTIONS WILL BE CLOSED** diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index aa0dc2b8..f5000e21 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -34,9 +34,11 @@ merge of your pull request! -* [ ] Documentation -* [ ] Tests -* [ ] Ready to be merged -* [ ] Added myself to contributors table +- [ ] Documentation added to the + [docs site](https://github.com/testing-library/testing-library-docs) +- [ ] Tests +- [ ] TypeScript definitions updated +- [ ] Ready to be merged + diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml new file mode 100644 index 00000000..f239c717 --- /dev/null +++ b/.github/workflows/validate.yml @@ -0,0 +1,117 @@ +name: validate +on: + push: + branches: + # Match SemVer major release branches + # e.g. "12.x" or "8.x" + - '[0-9]+.x' + - 'main' + - 'next' + - 'next-major' + - 'beta' + - 'alpha' + - '!all-contributors/**' + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + actions: write # to cancel/stop running workflows (styfle/cancel-workflow-action) + contents: read # to fetch code (actions/checkout) + +jobs: + main: + continue-on-error: ${{ matrix.react != 'latest' }} + # ignore all-contributors PRs + if: ${{ !contains(github.head_ref, 'all-contributors') }} + strategy: + fail-fast: false + matrix: + node: [18, 20] + react: ['18.x', latest, canary, experimental] + runs-on: ubuntu-latest + steps: + - name: ⬇️ Checkout repo + uses: actions/checkout@v4 + + - name: ⎔ Setup node + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + + - name: 📥 Download deps + uses: bahmutov/npm-install@v1 + with: + useLockFile: false + + # TODO: Can be removed if https://github.com/kentcdodds/kcd-scripts/pull/146 is released + - name: Verify format (`npm run format` committed?) + run: npm run format -- --check --no-write + + # as requested by the React team :) + # https://reactjs.org/blog/2019/10/22/react-release-channels.html#using-the-next-channel-for-integration-testing + - name: ⚛️ Setup react + run: npm install react@${{ matrix.react }} react-dom@${{ matrix.react }} + + - name: ⚛️ Setup react types + if: ${{ matrix.react != 'canary' && matrix.react != 'experimental' }} + run: + npm install @types/react@${{ matrix.react }} @types/react-dom@${{ + matrix.react }} + + - name: ▶️ Run validate script + run: npm run validate + + - name: ⬆️ Upload coverage report + uses: codecov/codecov-action@v5 + with: + fail_ci_if_error: true + flags: ${{ matrix.react }} + token: ${{ secrets.CODECOV_TOKEN }} + + release: + permissions: + actions: write # to cancel/stop running workflows (styfle/cancel-workflow-action) + contents: write # to create release tags (cycjimmy/semantic-release-action) + issues: write # to post release that resolves an issue (cycjimmy/semantic-release-action) + + needs: main + runs-on: ubuntu-latest + if: + ${{ github.repository == 'testing-library/react-testing-library' && + github.event_name == 'push' }} + steps: + - name: ⬇️ Checkout repo + uses: actions/checkout@v4 + + - name: ⎔ Setup node + uses: actions/setup-node@v4 + with: + node-version: 14 + + - name: 📥 Download deps + uses: bahmutov/npm-install@v1 + with: + useLockFile: false + + - name: 🏗 Run build script + run: npm run build + + - name: 🚀 Release + uses: cycjimmy/semantic-release-action@v2 + with: + semantic_version: 17 + branches: | + [ + '+([0-9])?(.{+([0-9]),x}).x', + 'main', + 'next', + 'next-major', + {name: 'beta', prerelease: true}, + {name: 'alpha', prerelease: true} + ] + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NPM_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/.gitignore b/.gitignore index 09048d22..8e0c70cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,7 @@ node_modules coverage dist -.opt-in -.opt-out .DS_Store -.eslintcache # these cause more harm than good # when working with contributors diff --git a/.huskyrc.js b/.huskyrc.js new file mode 100644 index 00000000..5e45c45d --- /dev/null +++ b/.huskyrc.js @@ -0,0 +1 @@ +module.exports = require('kcd-scripts/husky') diff --git a/.npmrc b/.npmrc index d2722898..1df2a6d8 100644 --- a/.npmrc +++ b/.npmrc @@ -1,2 +1,2 @@ -registry=http://registry.npmjs.org/ +registry=https://registry.npmjs.org/ package-lock=false diff --git a/.prettierignore b/.prettierignore index 30117ea2..9c628283 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,3 @@ -package.json node_modules -dist coverage +dist diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index fb31ee19..00000000 --- a/.prettierrc +++ /dev/null @@ -1,10 +0,0 @@ -{ - "printWidth": 80, - "tabWidth": 2, - "useTabs": false, - "semi": false, - "singleQuote": true, - "trailingComma": "all", - "bracketSpacing": false, - "jsxBracketSameLine": false -} diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 00000000..4679d9bf --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1 @@ +module.exports = require('kcd-scripts/prettier') diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 08be7ec0..00000000 --- a/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -sudo: false -language: node_js -cache: - directories: - - ~/.npm -notifications: - email: false -node_js: '8' -install: npm install -script: npm run validate -after_success: kcd-scripts travis-after-success -branches: - only: master diff --git a/CHANGELOG.md b/CHANGELOG.md index 06d221aa..2a675299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # CHANGELOG -The changelog is automatically updated using [semantic-release](https://github.com/semantic-release/semantic-release). -You can see it on the [releases page](../../releases). +The changelog is automatically updated using +[semantic-release](https://github.com/semantic-release/semantic-release). You +can see it on the [releases page](../../releases). diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index afe24327..47681ae0 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -2,73 +2,127 @@ ## Our Pledge -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, gender identity and expression, level of experience, +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible 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 +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. -Examples of unacceptable behavior by participants include: +## Our Standards -* 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 +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a professional setting -## Our Responsibilities +## Enforcement Responsibilities -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. +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior 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. +Community leaders 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, and will communicate reasons for moderation +decisions when appropriate. ## Scope -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the project team at kent+coc@doddsfamily.us. 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 to the community leaders responsible for enforcement at +me+coc@kentcdodds.com. All complaints will be reviewed and investigated promptly +and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. -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 -members of the project's leadership. +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. ## Attribution -This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, -available at [http://contributor-covenant.org/version/1/4][version] +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2cb6bdbf..e16e9d61 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,8 +2,8 @@ Thanks for being willing to contribute! -**Working on your first Pull Request?** You can learn how from this _free_ series -[How to Contribute to an Open Source Project on GitHub][egghead] +**Working on your first Pull Request?** You can learn how from this _free_ +series [How to Contribute to an Open Source Project on GitHub][egghead] ## Project setup @@ -11,54 +11,36 @@ Thanks for being willing to contribute! 2. Run `npm run setup -s` to install dependencies and run validation 3. Create a branch for your PR with `git checkout -b pr/your-branch-name` -> Tip: Keep your `master` branch pointing at the original repository and make -> pull requests from branches on your fork. To do this, run: +> Tip: Keep your `main` branch pointing at the original repository and make pull +> requests from branches on your fork. To do this, run: > > ``` -> git remote add upstream https://github.com/kentcdodds/react-testing-library.git +> git remote add upstream https://github.com/testing-library/react-testing-library.git > git fetch upstream -> git branch --set-upstream-to=upstream/master master +> git branch --set-upstream-to=upstream/main main > ``` > -> This will add the original repository as a "remote" called "upstream," -> Then fetch the git information from that remote, then set your local `master` -> branch to use the upstream master branch whenever you run `git pull`. -> Then you can make all of your pull request branches based on this `master` -> branch. Whenever you want to update your version of `master`, do a regular -> `git pull`. - -## Add yourself as a contributor - -This project follows the [all contributors][all-contributors] specification. -To add yourself to the table of contributors on the `README.md`, please use the -automated script as part of your PR: - -```console -npm run add-contributor -``` - -Follow the prompt and commit `.all-contributorsrc` and `README.md` in the PR. -If you've already added yourself to the list and are making -a new type of contribution, you can run it again and select the added -contribution type. +> This will add the original repository as a "remote" called "upstream," Then +> fetch the git information from that remote, then set your local `main` branch +> to use the upstream main branch whenever you run `git pull`. Then you can make +> all of your pull request branches based on this `main` branch. Whenever you +> want to update your version of `main`, do a regular `git pull`. ## Committing and Pushing changes Please make sure to run the tests before you commit your changes. You can run -`npm run test:update` which will update any snapshots that need updating. -Make sure to include those changes (if they exist) in your commit. - -### opt into git hooks +`npm run test:update` which will update any snapshots that need updating. Make +sure to include those changes (if they exist) in your commit. -There are git hooks set up with this project that are automatically installed -when you install dependencies. They're really handy, but are turned off by -default (so as to not hinder new contributors). You can opt into these by -creating a file called `.opt-in` at the root of the project and putting this -inside: +### Update Typings -``` -pre-commit -``` +If your PR introduced some changes in the API, you are more than welcome to +modify the TypeScript type definition to reflect those changes. Just modify the +`/types/index.d.ts` file accordingly. If you have never seen TypeScript +definitions before, you can read more about it in its +[documentation pages](https://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html). +Though this library itself is not written in TypeScript we use +[dtslint](https://github.com/microsoft/dtslint) to lint our typings. ## Help needed @@ -67,6 +49,6 @@ Please checkout the [the open issues][issues] Also, please watch the repo and respond to questions/bug reports/feature requests! Thanks! -[egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github -[all-contributors]: https://github.com/kentcdodds/all-contributors -[issues]: https://github.com/kentcdodds/react-testing-library/issues +[egghead]: + https://egghead.io/courses/how-to-contribute-to-an-open-source-project-on-github +[issues]: https://github.com/testing-library/react-testing-library/issues diff --git a/LICENSE b/LICENSE index 4c43675b..ca399d57 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ The MIT License (MIT) -Copyright (c) 2017 Kent C. Dodds +Copyright (c) 2017-Present Kent C. Dodds Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 41f85a7d..7e18d5dd 100644 --- a/README.md +++ b/README.md @@ -1,569 +1,647 @@
Simple and complete React DOM testing utilities that encourage good testing practices.
+Simple and complete React DOM testing utilities that encourage good testing +practices.
+ +
+ DocumentFragment
+
+ is pretty cool!
+ Loading..
+ } + + return ( +Loading..
+ } + + return ( +DocumentFragment
is pretty cool!
+ } + +/** @deprecated */ +export type BaseRenderOptions< + Q extends Queries, + Container extends RendererableContainer | HydrateableContainer, + BaseElement extends RendererableContainer | HydrateableContainer, +> = RenderOptions+ +type RendererableContainer = ReactDOMClient.Container +type HydrateableContainer = Parameters[0] +/** @deprecated */ +export interface ClientRenderOptions< + Q extends Queries, + Container extends RendererableContainer, + BaseElement extends RendererableContainer = Container, +> extends BaseRenderOptions { + /** + * If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side + * rendering and use ReactDOM.hydrate to mount your components. + * + * @see https://testing-library.com/docs/react-testing-library/api/#hydrate) + */ + hydrate?: false | undefined +} +/** @deprecated */ +export interface HydrateOptions< + Q extends Queries, + Container extends HydrateableContainer, + BaseElement extends HydrateableContainer = Container, +> extends BaseRenderOptions{ + /** + * If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side + * rendering and use ReactDOM.hydrate to mount your components. + * + * @see https://testing-library.com/docs/react-testing-library/api/#hydrate) + */ + hydrate: true +} + +export interface RenderOptions< + Q extends Queries = typeof queries, + Container extends RendererableContainer | HydrateableContainer = HTMLElement, + BaseElement extends RendererableContainer | HydrateableContainer = Container, +> { + /** + * By default, React Testing Library will create a div and append that div to the document.body. Your React component will be rendered in the created div. 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 `` element, it cannot be a child of a div. In this case, you can + * specify a table as the render container. + * + * @see https://testing-library.com/docs/react-testing-library/api/#container + */ + container?: Container | undefined + /** + * Defaults to the container if the container is specified. Otherwise `document.body` is used for the default. This is used as + * the base element for the queries as well as what is printed when you use `debug()`. + * + * @see https://testing-library.com/docs/react-testing-library/api/#baseelement + */ + baseElement?: BaseElement | undefined + /** + * If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side + * rendering and use ReactDOM.hydrate to mount your components. + * + * @see https://testing-library.com/docs/react-testing-library/api/#hydrate) + */ + hydrate?: boolean | undefined + /** + * Only works if used with React 18. + * Set to `true` if you want to force synchronous `ReactDOM.render`. + * Otherwise `render` will default to concurrent React if available. + */ + legacyRoot?: boolean | undefined + /** + * Only supported in React 19. + * Callback called when React catches an error in an Error Boundary. + * Called with the error caught by the Error Boundary, and an `errorInfo` object containing the `componentStack`. + * + * @see {@link https://react.dev/reference/react-dom/client/createRoot#parameters createRoot#options} + */ + onCaughtError?: ReactDOMClient.RootOptions extends { + onCaughtError: infer OnCaughtError + } + ? OnCaughtError + : never + /** + * Callback called when React automatically recovers from errors. + * Called with an error React throws, and an `errorInfo` object containing the `componentStack`. + * Some recoverable errors may include the original error cause as `error.cause`. + * + * @see {@link https://react.dev/reference/react-dom/client/createRoot#parameters createRoot#options} + */ + onRecoverableError?: ReactDOMClient.RootOptions['onRecoverableError'] + /** + * Not supported at the moment + */ + onUncaughtError?: never + /** + * Queries to bind. Overrides the default set from DOM Testing Library unless merged. + * + * @see https://testing-library.com/docs/react-testing-library/api/#queries + */ + queries?: Q | undefined + /** + * 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 for examples. + * + * @see https://testing-library.com/docs/react-testing-library/api/#wrapper + */ + wrapper?: React.JSXElementConstructor<{children: React.ReactNode}> | undefined + /** + * When enabled,is rendered around the inner element. + * If defined, overrides the value of `reactStrictMode` set in `configure`. + */ + reactStrictMode?: boolean +} + +type Omit = Pick > + +/** + * Render into a container which is appended to document.body. It should be used with cleanup. + */ +export function render< + Q extends Queries = typeof queries, + Container extends RendererableContainer | HydrateableContainer = HTMLElement, + BaseElement extends RendererableContainer | HydrateableContainer = Container, +>( + ui: React.ReactNode, + options: RenderOptions , +): RenderResult+export function render( + ui: React.ReactNode, + options?: Omit| undefined, +): RenderResult + +export interface RenderHookResult { + /** + * Triggers a re-render. The props will be passed to your renderHook callback. + */ + rerender: (props?: Props) => void + /** + * This is a stable reference to the latest value returned by your renderHook + * callback + */ + result: { + /** + * The value returned by your renderHook callback + */ + current: Result + } + /** + * Unmounts the test component. This is useful for when you need to test + * any cleanup your useEffects have. + */ + unmount: () => void +} + +/** @deprecated */ +export type BaseRenderHookOptions< + Props, + Q extends Queries, + Container extends RendererableContainer | HydrateableContainer, + BaseElement extends Element | DocumentFragment, +> = RenderHookOptions + +/** @deprecated */ +export interface ClientRenderHookOptions< + Props, + Q extends Queries, + Container extends Element | DocumentFragment, + BaseElement extends Element | DocumentFragment = Container, +> extends BaseRenderHookOptions { + /** + * If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side + * rendering and use ReactDOM.hydrate to mount your components. + * + * @see https://testing-library.com/docs/react-testing-library/api/#hydrate) + */ + hydrate?: false | undefined +} + +/** @deprecated */ +export interface HydrateHookOptions< + Props, + Q extends Queries, + Container extends Element | DocumentFragment, + BaseElement extends Element | DocumentFragment = Container, +> extends BaseRenderHookOptions { + /** + * If `hydrate` is set to `true`, then it will render with `ReactDOM.hydrate`. This may be useful if you are using server-side + * rendering and use ReactDOM.hydrate to mount your components. + * + * @see https://testing-library.com/docs/react-testing-library/api/#hydrate) + */ + hydrate: true +} + +export interface RenderHookOptions< + Props, + Q extends Queries = typeof queries, + Container extends RendererableContainer | HydrateableContainer = HTMLElement, + BaseElement extends RendererableContainer | HydrateableContainer = Container, +> extends BaseRenderOptions { + /** + * The argument passed to the renderHook callback. Can be useful if you plan + * to use the rerender utility to change the values passed to your hook. + */ + initialProps?: Props | undefined +} + +/** + * Allows you to render a hook within a test React component without having to + * create that component yourself. + */ +export function renderHook< + Result, + Props, + Q extends Queries = typeof queries, + Container extends RendererableContainer | HydrateableContainer = HTMLElement, + BaseElement extends RendererableContainer | HydrateableContainer = Container, +>( + render: (initialProps: Props) => Result, + options?: RenderHookOptions| undefined, +): RenderHookResult + +/** + * Unmounts React trees that were mounted with render. + */ +export function cleanup(): void + +/** + * Simply calls React.act(cb) + * If that's not available (older version of react) then it + * simply calls the deprecated version which is ReactTestUtils.act(cb) + */ +// IfAny from https://stackoverflow.com/a/61626123/3406963 +export const act: 0 extends 1 & typeof reactAct + ? typeof reactDeprecatedAct + : typeof reactAct diff --git a/types/pure.d.ts b/types/pure.d.ts new file mode 100644 index 00000000..7b527195 --- /dev/null +++ b/types/pure.d.ts @@ -0,0 +1 @@ +export * from './' diff --git a/types/test.tsx b/types/test.tsx new file mode 100644 index 00000000..825d5699 --- /dev/null +++ b/types/test.tsx @@ -0,0 +1,317 @@ +import * as React from 'react' +import {render, fireEvent, screen, waitFor, renderHook} from '.' +import * as pure from './pure' + +export async function testRender() { + const view = render() + + // single queries + view.getByText('foo') + view.queryByText('foo') + await view.findByText('foo') + + // multiple queries + view.getAllByText('bar') + view.queryAllByText('bar') + await view.findAllByText('bar') + + // helpers + const {container, rerender, debug} = view + expectType (container) + return {container, rerender, debug} +} + +export async function testPureRender() { + const view = pure.render() + + // single queries + view.getByText('foo') + view.queryByText('foo') + await view.findByText('foo') + + // multiple queries + view.getAllByText('bar') + view.queryAllByText('bar') + await view.findAllByText('bar') + + // helpers + const {container, rerender, debug} = view + expectType (container) + return {container, rerender, debug} +} + +export function testRenderOptions() { + const container = document.createElement('div') + const options = {container} + const {container: returnedContainer} = render(, options) + expectType (returnedContainer) + + render(, {wrapper: () => null}) +} + +export function testSVGRenderOptions() { + const container = document.createElementNS( + 'http://www.w3.org/2000/svg', + 'svg', + ) + const options = {container} + const {container: returnedContainer} = render( , options) + expectType (returnedContainer) +} + +export function testFireEvent() { + const {container} = render() + fireEvent.click(container) +} + +export function testConfigure() { + // test for DTL's config + pure.configure({testIdAttribute: 'foobar'}) + pure.configure(existingConfig => ({ + testIdAttribute: `modified-${existingConfig.testIdAttribute}`, + })) + + // test for RTL's config + pure.configure({reactStrictMode: true}) + pure.configure(existingConfig => ({ + reactStrictMode: !existingConfig.reactStrictMode, + })) +} + +export function testGetConfig() { + // test for DTL's config + pure.getConfig().testIdAttribute + + // test for RTL's config + pure.getConfig().reactStrictMode +} + +export function testDebug() { + const {debug, getAllByTestId} = render( + <> + Hello World
+Hello World
+ >, + ) + debug(getAllByTestId('testid')) +} + +export async function testScreen() { + render() + + await screen.findByRole('button') +} + +export async function testWaitFor() { + const {container} = render() + fireEvent.click(container) + await waitFor(() => {}) +} + +export function testQueries() { + const {getByLabelText} = render( + , + ) + expectType>( + getByLabelText('Username'), + ) + + const container = document.createElement('div') + const options = {container} + const {getByText} = render( Hello World, options) + expectType>( + getByText('Hello World'), + ) +} + +export function wrappedRender( + ui: React.ReactNode, + options?: pure.RenderOptions, +) { + const Wrapper = ({ + children, + }: { + children: React.ReactNode + }): React.JSX.Element => { + return {children}+ } + + return pure.render(ui, { + wrapper: Wrapper, + // testing exactOptionalPropertyTypes comaptibility + hydrate: options?.hydrate, + ...options, + }) +} + +export function wrappedRenderB( + ui: React.ReactNode, + options?: pure.RenderOptions, +) { + const Wrapper: React.FunctionComponent<{children?: React.ReactNode}> = ({ + children, + }) => { + return{children}+ } + + return pure.render(ui, {wrapper: Wrapper, ...options}) +} + +export function wrappedRenderC( + ui: React.ReactNode, + options?: pure.RenderOptions, +) { + interface AppWrapperProps { + children?: React.ReactNode + userProviderProps?: {user: string} + } + const AppWrapperProps: React.FunctionComponent= ({ + children, + userProviderProps = {user: 'TypeScript'}, + }) => { + return {children}+ } + + return pure.render(ui, {wrapper: AppWrapperProps, ...options}) +} + +export function wrappedRenderHook( + hook: () => unknown, + options?: pure.RenderHookOptions , +) { + interface AppWrapperProps { + children?: React.ReactNode + userProviderProps?: {user: string} + } + const AppWrapperProps: React.FunctionComponent = ({ + children, + userProviderProps = {user: 'TypeScript'}, + }) => { + return {children}+ } + + return pure.renderHook(hook, {...options}) +} + +export function testBaseElement() { + const {baseElement: baseDefaultElement} = render() + expectType(baseDefaultElement) + + const container = document.createElement('input') + const {baseElement: baseElementFromContainer} = render(, {container}) + expectType ( + baseElementFromContainer, + ) + + const baseElementOption = document.createElement('input') + const {baseElement: baseElementFromOption} = render(, { + baseElement: baseElementOption, + }) + expectType ( + baseElementFromOption, + ) +} + +export function testRenderHook() { + const {result, rerender, unmount} = renderHook(() => React.useState(2)[0]) + + expectType (result.current) + + rerender() + + unmount() + + renderHook(() => null, {wrapper: () => null}) +} + +export function testRenderHookProps() { + const {result, rerender, unmount} = renderHook( + ({defaultValue}) => React.useState(defaultValue)[0], + {initialProps: {defaultValue: 2}}, + ) + + expectType (result.current) + + rerender() + + unmount() +} + +export function testContainer() { + render('a', {container: document.createElement('div')}) + render('a', {container: document.createDocumentFragment()}) + // Only allowed in React 19 + render('a', {container: document}) + render('a', {container: document.createElement('div'), hydrate: true}) + // Only allowed for createRoot but typing `render` appropriately makes it harder to compose. + render('a', {container: document.createDocumentFragment(), hydrate: true}) + render('a', {container: document, hydrate: true}) + + renderHook(() => null, {container: document.createElement('div')}) + renderHook(() => null, {container: document.createDocumentFragment()}) + // Only allowed in React 19 + renderHook(() => null, {container: document}) + renderHook(() => null, { + container: document.createElement('div'), + hydrate: true, + }) + // Only allowed for createRoot but typing `render` appropriately makes it harder to compose. + renderHook(() => null, { + container: document.createDocumentFragment(), + hydrate: true, + }) + renderHook(() => null, {container: document, hydrate: true}) +} + +export function testErrorHandlers() { + // React 19 types are not used in tests. Verify manually if this works with `"@types/react": "npm:types-react@rc"` + render(null, { + // Should work with React 19 types + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + onCaughtError: () => {}, + }) + render(null, { + // Should never work as it's not supported yet. + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + onUncaughtError: () => {}, + }) + render(null, { + onRecoverableError: (error, errorInfo) => { + console.error(error) + console.log(errorInfo.componentStack) + }, + }) +} + +/* +eslint + testing-library/prefer-explicit-assert: "off", + testing-library/no-wait-for-empty-callback: "off", + testing-library/prefer-screen-queries: "off" +*/ + +// https://stackoverflow.com/questions/53807517/how-to-test-if-two-types-are-exactly-the-same +type IfEquals = ( () => G extends T + ? 1 + : 2) extends () => G extends U ? 1 : 2 + ? Yes + : No + +/** + * Issues a type error if `Expected` is not identical to `Actual`. + * + * `Expected` should be declared when invoking `expectType`. + * `Actual` should almost always we be a `typeof value` statement. + * + * Source: https://github.com/mui-org/material-ui/blob/6221876a4b468a3330ffaafa8472de7613933b87/packages/material-ui-types/index.d.ts#L73-L84 + * + * @example `expectType (value)` + * TypeScript issues a type error since `value is not assignable to never`. + * This means `typeof value` is not identical to `number | string` + * @param actual + */ +declare function expectType ( + actual: IfEquals , +): void diff --git a/types/tsconfig.json b/types/tsconfig.json new file mode 100644 index 00000000..bad26af7 --- /dev/null +++ b/types/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../node_modules/kcd-scripts/shared-tsconfig.json", + "compilerOptions": { + "exactOptionalPropertyTypes": true, + "skipLibCheck": false + }, + "include": ["."] +} diff --git a/typings/index.d.ts b/typings/index.d.ts deleted file mode 100644 index 55c9963c..00000000 --- a/typings/index.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -import {Simulate as ReactSimulate} from 'react-dom/test-utils' - -interface RenderResult { - container: HTMLDivElement - unmount: VoidFunction - queryByTestId: (id: string) => HTMLElement | null - getByTestId: (id: string) => HTMLElement - queryByText: (id: string) => HTMLElement | null - getByText: (id: string) => HTMLElement - queryByPlaceholderText: (id: string) => HTMLElement | null - getByPlaceholderText: (id: string) => HTMLElement - queryByLabelText: (id: string) => HTMLElement | null - getByLabelText: (id: string) => HTMLElement -} - -export function render( - ui: React.ReactElement , - options?: {container: HTMLElement}, -): RenderResult - -export function flushPromises(): Promise - -export const Simulate: typeof ReactSimulate