Skip to content

Commit fe02135

Browse files
JeffBaumgardtKent C. Dodds
authored and
Kent C. Dodds
committed
feat(getByRole): add queries for dom attriburte role (#94)
**What**: Add queries for role dom attribute <!-- Why are these changes necessary? --> **Why**: As evidenced by [testing-library/jest-dom#55](testing-library/jest-dom#55) having a role attribute query would be helpful when attempting to get elements that don't fit the other queries. My example was a dialog who did not have any other matching queries and due to the associated library. I was able to add data-testid but that added the attribute too low in the generated dom not to the container root as expected, this is a problem with the library not dom-testing-library. <!-- How were these changes implemented? --> **How**: I added new query selectors for the node attribute. <!-- Have you done all of these things? --> **Checklist**: <!-- add "N/A" to the end of each line that's irrelevant to your changes --> <!-- to check an item, place an "x" in the box like so: "- [x] Documentation" --> - [x] Documentation - [x] Tests - [x] Ready to be merged <!-- In your opinion, is this ready to be merged as soon as it's reviewed? --> - [x] Added myself to contributors table <!-- this is optional, see the contributing guidelines for instructions --> <!-- feel free to add additional comments -->
1 parent 98c67e2 commit fe02135

File tree

6 files changed

+83
-4
lines changed

6 files changed

+83
-4
lines changed

.all-contributorsrc

+10
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,16 @@
314314
"contributions": [
315315
"code"
316316
]
317+
},
318+
{
319+
"login": "JeffBaumgardt",
320+
"name": "Jeff Baumgardt",
321+
"avatar_url": "https://avatars2.githubusercontent.com/u/777527?v=4",
322+
"profile": "https://github.com/JeffBaumgardt",
323+
"contributions": [
324+
"code",
325+
"doc"
326+
]
317327
}
318328
]
319329
}

README.md

+23-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
[![downloads][downloads-badge]][npmtrends]
1717
[![MIT License][license-badge]][license]
1818

19-
[![All Contributors](https://img.shields.io/badge/all_contributors-31-orange.svg?style=flat-square)](#contributors)
19+
[![All Contributors](https://img.shields.io/badge/all_contributors-32-orange.svg?style=flat-square)](#contributors)
2020
[![PRs Welcome][prs-badge]][prs]
2121
[![Code of Conduct][coc-badge]][coc]
2222

@@ -351,6 +351,27 @@ Returns the element that has the matching value.
351351
const lastNameInput = getByValue('Norris')
352352
```
353353

354+
### `getByRole`
355+
356+
```typescript
357+
getByRole(
358+
container: HTMLElement,
359+
text: TextMatch,
360+
options?: {
361+
exact?: boolean = true,
362+
collapseWhitespace?: boolean = false,
363+
trim?: boolean = true,
364+
}): HTMLElement`
365+
```
366+
367+
A shortcut to `` container.querySelector(`[role="${yourRole}"]`) `` (and it
368+
also accepts a [`TextMatch`](#textmatch)).
369+
370+
```javascript
371+
// <div role="dialog">...</div>
372+
const dialogContainer = getByTestrole(container, 'dialog')
373+
```
374+
354375
### `getByTestId`
355376

356377
```typescript
@@ -933,7 +954,7 @@ Thanks goes to these people ([emoji key][emojis]):
933954
| [<img src="https://avatars1.githubusercontent.com/u/1241511?s=460&v=4" width="100px;"/><br /><sub><b>Anto Aravinth</b></sub>](https://github.com/antoaravinth)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=antoaravinth "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=antoaravinth "Tests") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=antoaravinth "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/3462296?v=4" width="100px;"/><br /><sub><b>Jonah Moses</b></sub>](https://github.com/JonahMoses)<br />[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JonahMoses "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/4002543?v=4" width="100px;"/><br /><sub><b>Łukasz Gandecki</b></sub>](http://team.thebrain.pro)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=lgandecki "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=lgandecki "Tests") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=lgandecki "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/498274?v=4" width="100px;"/><br /><sub><b>Ivan Babak</b></sub>](https://sompylasar.github.io)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Asompylasar "Bug reports") [🤔](#ideas-sompylasar "Ideas, Planning, & Feedback") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=sompylasar "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=sompylasar "Documentation") | [<img src="https://avatars3.githubusercontent.com/u/4439618?v=4" width="100px;"/><br /><sub><b>Jesse Day</b></sub>](https://github.com/jday3)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=jday3 "Code") | [<img src="https://avatars0.githubusercontent.com/u/15199?v=4" width="100px;"/><br /><sub><b>Ernesto García</b></sub>](http://gnapse.github.io)<br />[💬](#question-gnapse "Answering Questions") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=gnapse "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=gnapse "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/2747424?v=4" width="100px;"/><br /><sub><b>Josef Maxx Blake</b></sub>](http://jomaxx.com)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=jomaxx "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=jomaxx "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=jomaxx "Tests") |
934955
| [<img src="https://avatars3.githubusercontent.com/u/725236?v=4" width="100px;"/><br /><sub><b>Alex Cook</b></sub>](https://github.com/alecook)<br />[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=alecook "Documentation") [💡](#example-alecook "Examples") | [<img src="https://avatars3.githubusercontent.com/u/10348212?v=4" width="100px;"/><br /><sub><b>Daniel Cook</b></sub>](https://github.com/dfcook)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=dfcook "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=dfcook "Documentation") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=dfcook "Tests") | [<img src="https://avatars2.githubusercontent.com/u/21194045?s=400&v=4" width="100px;"/><br /><sub><b>Thomas Chia</b></sub>](https://github.com/thchia)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Athchia "Bug reports") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=thchia "Code") | [<img src="https://avatars1.githubusercontent.com/u/28659384?v=4" width="100px;"/><br /><sub><b>Tim Deschryver</b></sub>](https://github.com/tdeschryver)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=tdeschryver "Tests") | [<img src="https://avatars3.githubusercontent.com/u/1571667?v=4" width="100px;"/><br /><sub><b>Alex Krolick</b></sub>](https://alexkrolick.com)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=alexkrolick "Code") | [<img src="https://avatars2.githubusercontent.com/u/2224291?v=4" width="100px;"/><br /><sub><b>Maddi Joyce</b></sub>](http://www.maddijoyce.com)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=maddijoyce "Code") | [<img src="https://avatars1.githubusercontent.com/u/25429764?v=4" width="100px;"/><br /><sub><b>Peter Kamps</b></sub>](https://github.com/npeterkamps)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Anpeterkamps "Bug reports") [💻](https://github.com/kentcdodds/dom-testing-library/commits?author=npeterkamps "Code") [⚠️](https://github.com/kentcdodds/dom-testing-library/commits?author=npeterkamps "Tests") |
935956
| [<img src="https://avatars2.githubusercontent.com/u/21689428?v=4" width="100px;"/><br /><sub><b>Jonathan Stoye</b></sub>](http://jonathanstoye.de)<br />[📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JonathanStoye "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/4126644?v=4" width="100px;"/><br /><sub><b>Sanghyeon Lee</b></sub>](https://github.com/yongdamsh)<br />[💡](#example-yongdamsh "Examples") | [<img src="https://avatars3.githubusercontent.com/u/8015514?v=4" width="100px;"/><br /><sub><b>Justice Mba </b></sub>](https://github.com/Dajust)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=Dajust "Documentation") [🤔](#ideas-Dajust "Ideas, Planning, & Feedback") | [<img src="https://avatars3.githubusercontent.com/u/340761?v=4" width="100px;"/><br /><sub><b>Wayne Crouch</b></sub>](https://github.com/wgcrouch)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wgcrouch "Code") | [<img src="https://avatars1.githubusercontent.com/u/4996462?v=4" width="100px;"/><br /><sub><b>Ben Elliott</b></sub>](http://benjaminelliott.co.uk)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=benelliott "Code") | [<img src="https://avatars3.githubusercontent.com/u/577921?v=4" width="100px;"/><br /><sub><b>Ruben Costa</b></sub>](http://nuances.co)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=rubencosta "Code") | [<img src="https://avatars2.githubusercontent.com/u/4982001?v=4" width="100px;"/><br /><sub><b>Robert Smith</b></sub>](http://rbrtsmith.com/)<br />[🐛](https://github.com/kentcdodds/dom-testing-library/issues?q=author%3Arbrtsmith "Bug reports") [🤔](#ideas-rbrtsmith "Ideas, Planning, & Feedback") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=rbrtsmith "Documentation") |
936-
| [<img src="https://avatars3.githubusercontent.com/u/881986?v=4" width="100px;"/><br /><sub><b>dadamssg</b></sub>](https://github.com/dadamssg)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=dadamssg "Code") | [<img src="https://avatars1.githubusercontent.com/u/186971?v=4" width="100px;"/><br /><sub><b>Neil Kistner</b></sub>](https://neilkistner.com/)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wyze "Code") | [<img src="https://avatars3.githubusercontent.com/u/1448597?v=4" width="100px;"/><br /><sub><b>Ben Chauvette</b></sub>](http://bdchauvette.net/)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=bdchauvette "Code") |
957+
| [<img src="https://avatars3.githubusercontent.com/u/881986?v=4" width="100px;"/><br /><sub><b>dadamssg</b></sub>](https://github.com/dadamssg)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=dadamssg "Code") | [<img src="https://avatars1.githubusercontent.com/u/186971?v=4" width="100px;"/><br /><sub><b>Neil Kistner</b></sub>](https://neilkistner.com/)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=wyze "Code") | [<img src="https://avatars3.githubusercontent.com/u/1448597?v=4" width="100px;"/><br /><sub><b>Ben Chauvette</b></sub>](http://bdchauvette.net/)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=bdchauvette "Code") | [<img src="https://avatars2.githubusercontent.com/u/777527?v=4" width="100px;"/><br /><sub><b>Jeff Baumgardt</b></sub>](https://github.com/JeffBaumgardt)<br />[💻](https://github.com/kentcdodds/dom-testing-library/commits?author=JeffBaumgardt "Code") [📖](https://github.com/kentcdodds/dom-testing-library/commits?author=JeffBaumgardt "Documentation") |
937958

938959
<!-- ALL-CONTRIBUTORS-LIST:END -->
939960

src/__tests__/__snapshots__/element-queries.js.snap

+8
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,14 @@ exports[`get throws a useful error message 7`] = `
5656
</div>"
5757
`;
5858

59+
exports[`get throws a useful error message 8`] = `
60+
"Unable to find an element by role=LucyRicardo
61+
62+
<div>
63+
<div />
64+
</div>"
65+
`;
66+
5967
exports[`get throws a useful error message without DOM in Cypress 1`] = `"Unable to find a label with the text of: LucyRicardo"`;
6068

6169
exports[`get throws a useful error message without DOM in Cypress 2`] = `"Unable to find an element with the placeholder text of: LucyRicardo"`;

src/__tests__/element-queries.js

+20-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ test('get throws a useful error message', () => {
2929
getByAltText,
3030
getByTitle,
3131
getByValue,
32+
getByRole,
3233
} = render('<div />')
3334
expect(() => getByLabelText('LucyRicardo')).toThrowErrorMatchingSnapshot()
3435
expect(() =>
@@ -39,6 +40,7 @@ test('get throws a useful error message', () => {
3940
expect(() => getByAltText('LucyRicardo')).toThrowErrorMatchingSnapshot()
4041
expect(() => getByTitle('LucyRicardo')).toThrowErrorMatchingSnapshot()
4142
expect(() => getByValue('LucyRicardo')).toThrowErrorMatchingSnapshot()
43+
expect(() => getByRole('LucyRicardo')).toThrowErrorMatchingSnapshot()
4244
})
4345

4446
test('can get elements by matching their text content', () => {
@@ -209,8 +211,9 @@ test('getAll* matchers return an array', () => {
209211
getAllByLabelText,
210212
getAllByPlaceholderText,
211213
getAllByText,
214+
getAllByRole,
212215
} = render(`
213-
<div>
216+
<div role="container">
214217
<img
215218
data-testid="poster"
216219
alt="finding nemo poster"
@@ -234,6 +237,7 @@ test('getAll* matchers return an array', () => {
234237
expect(getAllByPlaceholderText(/The Rock/)).toHaveLength(1)
235238
expect(getAllByLabelText('User Name')).toHaveLength(1)
236239
expect(getAllByText(/^where/i)).toHaveLength(1)
240+
expect(getAllByRole(/container/i)).toHaveLength(1)
237241
})
238242

239243
test('getAll* matchers throw for 0 matches', () => {
@@ -243,8 +247,9 @@ test('getAll* matchers throw for 0 matches', () => {
243247
getAllByLabelText,
244248
getAllByPlaceholderText,
245249
getAllByText,
250+
getAllByRole,
246251
} = render(`
247-
<div>
252+
<div role="container">
248253
<label>No Matches Please</label>
249254
</div>,
250255
`)
@@ -255,6 +260,7 @@ test('getAll* matchers throw for 0 matches', () => {
255260
expect(() => getAllByLabelText('no matches please')).toThrow()
256261
expect(() => getAllByPlaceholderText('nope')).toThrow()
257262
expect(() => getAllByText('nope')).toThrow()
263+
expect(() => getAllByRole('nope')).toThrow()
258264
})
259265

260266
test('queryAll* matchers return an array for 0 matches', () => {
@@ -264,6 +270,7 @@ test('queryAll* matchers return an array for 0 matches', () => {
264270
queryAllByLabelText,
265271
queryAllByPlaceholderText,
266272
queryAllByText,
273+
queryAllByRole,
267274
} = render(`
268275
<div>
269276
</div>,
@@ -273,6 +280,7 @@ test('queryAll* matchers return an array for 0 matches', () => {
273280
expect(queryAllByLabelText('nope')).toHaveLength(0)
274281
expect(queryAllByPlaceholderText('nope')).toHaveLength(0)
275282
expect(queryAllByText('nope')).toHaveLength(0)
283+
expect(queryAllByRole('nope')).toHaveLength(0)
276284
})
277285

278286
test('using jest helpers to assert element states', () => {
@@ -375,6 +383,16 @@ test('using jest helpers to check element class names', () => {
375383
).toThrowError()
376384
})
377385

386+
test('using jest helpers to check element role', () => {
387+
const {getByRole} = render(`
388+
<div role="dialog">
389+
<span>Contents</span>
390+
</div>
391+
`)
392+
393+
expect(getByRole('dialog')).toHaveTextContent('Contents')
394+
})
395+
378396
test('test the debug helper prints the dom state here', () => {
379397
const originalDebugPrintLimit = process.env.DEBUG_PRINT_LIMIT
380398
const Large = `<div>

src/queries.js

+18
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ const queryByTestId = queryByAttribute.bind(null, 'data-testid')
106106
const queryAllByTestId = queryAllByAttribute.bind(null, 'data-testid')
107107
const queryByValue = queryByAttribute.bind(null, 'value')
108108
const queryAllByValue = queryAllByAttribute.bind(null, 'value')
109+
const queryByRole = queryByAttribute.bind(null, 'role')
110+
const queryAllByRole = queryAllByAttribute.bind(null, 'role')
109111

110112
function queryAllByAltText(
111113
container,
@@ -241,6 +243,18 @@ function getByAltText(...args) {
241243
return firstResultOrNull(getAllByAltText, ...args)
242244
}
243245

246+
function getAllByRole(container, id, ...rest) {
247+
const els = queryAllByRole(container, id, ...rest)
248+
if (!els.length) {
249+
throw getElementError(`Unable to find an element by role=${id}`, container)
250+
}
251+
return els
252+
}
253+
254+
function getByRole(...args) {
255+
return firstResultOrNull(getAllByRole, ...args)
256+
}
257+
244258
export {
245259
queryByPlaceholderText,
246260
queryAllByPlaceholderText,
@@ -270,6 +284,10 @@ export {
270284
queryAllByValue,
271285
getByValue,
272286
getAllByValue,
287+
queryByRole,
288+
queryAllByRole,
289+
getAllByRole,
290+
getByRole,
273291
}
274292

275293
/* eslint complexity:["error", 14] */

typings/queries.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,7 @@ export const queryByValue: QueryByAttribute
5757
export const queryAllByValue: AllByAttribute
5858
export const getByValue: GetByAttribute
5959
export const getAllByValue: AllByAttribute
60+
export const queryByTRole: QueryByAttribute
61+
export const queryAllByTRole: AllByAttribute
62+
export const getByTRole: GetByAttribute
63+
export const getAllByTRole: AllByAttribute

0 commit comments

Comments
 (0)