Skip to content

Commit a98dc9f

Browse files
eps1lonkentcdodds
andauthored
feat(byRole): Add name filter (#408)
* feat(byRole): Add `name` filter * Use common place to define defaults * feat(byRole): Implement TextMatch for `name` option * chore(lint): fix warnings * Final dap review * describe test better * Reuse matches in name filter * Include accname by default in logRoles * Update src/role-helpers.js * remove includeName option Co-authored-by: Kent C. Dodds <[email protected]>
1 parent ed36447 commit a98dc9f

File tree

5 files changed

+208
-5
lines changed

5 files changed

+208
-5
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@sheerun/mutationobserver-shim": "^0.3.2",
4545
"@types/testing-library__dom": "^6.0.0",
4646
"aria-query": "3.0.0",
47+
"dom-accessibility-api": "^0.3.0",
4748
"pretty-format": "^24.9.0",
4849
"wait-for-expect": "^3.0.0"
4950
},

src/__tests__/__snapshots__/role-helpers.js.snap

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
exports[`logRoles calls console.log with output from prettyRoles 1`] = `
44
"region:
55
6+
Name "":
67
<section
78
data-testid="a-section"
89
/>
910
1011
--------------------------------------------------
1112
link:
1213
14+
Name "link":
1315
<a
1416
data-testid="a-link"
1517
href="http://whatever.com"
@@ -18,135 +20,159 @@ link:
1820
--------------------------------------------------
1921
navigation:
2022
23+
Name "":
2124
<nav
2225
data-testid="a-nav"
2326
/>
2427
2528
--------------------------------------------------
2629
heading:
2730
31+
Name "Main Heading":
2832
<h1
2933
data-testid="a-h1"
3034
/>
3135
36+
Name "Sub Heading":
3237
<h2
3338
data-testid="a-h2"
3439
/>
3540
41+
Name "Tertiary Heading":
3642
<h3
3743
data-testid="a-h3"
3844
/>
3945
4046
--------------------------------------------------
4147
article:
4248
49+
Name "":
4350
<article
4451
data-testid="a-article"
4552
/>
4653
4754
--------------------------------------------------
4855
command:
4956
57+
Name "":
5058
<menuitem
5159
data-testid="a-menuitem-1"
5260
/>
5361
62+
Name "":
5463
<menuitem
5564
data-testid="a-menuitem-2"
5665
/>
5766
5867
--------------------------------------------------
5968
menuitem:
6069
70+
Name "":
6171
<menuitem
6272
data-testid="a-menuitem-1"
6373
/>
6474
75+
Name "":
6576
<menuitem
6677
data-testid="a-menuitem-2"
6778
/>
6879
6980
--------------------------------------------------
7081
list:
7182
83+
Name "":
7284
<ul
7385
data-testid="a-list"
7486
/>
7587
88+
Name "":
7689
<ul
7790
data-testid="b-list"
7891
/>
7992
8093
--------------------------------------------------
8194
listitem:
8295
96+
Name "":
8397
<li
8498
data-testid="a-list-item-1"
8599
/>
86100
101+
Name "":
87102
<li
88103
data-testid="a-list-item-2"
89104
/>
90105
106+
Name "":
91107
<li
92108
data-testid="b-list-item-1"
93109
/>
94110
111+
Name "":
95112
<li
96113
data-testid="b-list-item-2"
97114
/>
98115
99116
--------------------------------------------------
100117
table:
101118
119+
Name "":
102120
<table
103121
data-testid="a-table"
104122
/>
105123
106124
--------------------------------------------------
107125
rowgroup:
108126
127+
Name "":
109128
<tbody
110129
data-testid="a-tbody"
111130
/>
112131
113132
--------------------------------------------------
114133
row:
115134
135+
Name "Cell 1 Cell 2 Cell 3":
116136
<tr
117137
data-testid="a-row"
118138
/>
119139
120140
--------------------------------------------------
121141
cell:
122142
143+
Name "Cell 1":
123144
<td
124145
data-testid="a-cell-1"
125146
/>
126147
148+
Name "Cell 2":
127149
<td
128150
data-testid="a-cell-2"
129151
/>
130152
153+
Name "Cell 3":
131154
<td
132155
data-testid="a-cell-3"
133156
/>
134157
135158
--------------------------------------------------
136159
form:
137160
161+
Name "":
138162
<form
139163
data-testid="a-form"
140164
/>
141165
142166
--------------------------------------------------
143167
radio:
144168
169+
Name "":
145170
<input
146171
data-testid="a-radio-1"
147172
type="radio"
148173
/>
149174
175+
Name "":
150176
<input
151177
data-testid="a-radio-2"
152178
type="radio"
@@ -155,16 +181,19 @@ radio:
155181
--------------------------------------------------
156182
textbox:
157183
184+
Name "":
158185
<input
159186
data-testid="a-input-1"
160187
type="text"
161188
/>
162189
190+
Name "":
163191
<input
164192
data-testid="a-input-2"
165193
type="text"
166194
/>
167195
196+
Name "":
168197
<textarea
169198
data-testid="a-textarea"
170199
/>

src/__tests__/role.js

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import {configure, getConfig} from '../config'
2-
import {render} from './helpers/test-utils'
2+
import {getQueriesForElement} from '../get-queries-for-element'
3+
import {render, renderIntoDocument} from './helpers/test-utils'
34

45
test('by default logs accessible roles when it fails', () => {
56
const {getByRole} = render(`<h1>Hi</h1>`)
@@ -10,6 +11,7 @@ Here are the accessible roles:
1011
1112
heading:
1213
14+
Name "Hi":
1315
<h1 />
1416
1517
--------------------------------------------------
@@ -32,6 +34,7 @@ Here are the available roles:
3234
3335
heading:
3436
37+
Name "Hi":
3538
<h1 />
3639
3740
--------------------------------------------------
@@ -183,6 +186,144 @@ test('can include inaccessible roles', () => {
183186
expect(getByRole('list', {hidden: true})).not.toBeNull()
184187
})
185188

189+
test('can be filtered by accessible name', () => {
190+
const {getByRole} = renderIntoDocument(
191+
`
192+
<div>
193+
<h1>Order</h1>
194+
<h2>Delivery Adress</h2>
195+
<form aria-label="Delivery Adress">
196+
<label>
197+
<div>Street</div>
198+
<input type="text" />
199+
</label>
200+
<input type="submit" />
201+
</form>
202+
<h2>Invoice Adress</h2>
203+
<form aria-label="Invoice Adress">
204+
<label>
205+
<div>Street</div>
206+
<input type="text" />
207+
</label>
208+
<input type="submit" />
209+
</form>
210+
</div>`,
211+
)
212+
213+
const deliveryForm = getByRole('form', {name: 'Delivery Adress'})
214+
expect(deliveryForm).not.toBeNull()
215+
216+
expect(
217+
// TODO: upstream bug in `aria-query`; should be `button` role
218+
getQueriesForElement(deliveryForm).getByRole('textbox', {name: 'Submit'}),
219+
).not.toBeNull()
220+
221+
const invoiceForm = getByRole('form', {name: 'Delivery Adress'})
222+
expect(invoiceForm).not.toBeNull()
223+
224+
expect(
225+
getQueriesForElement(invoiceForm).getByRole('textbox', {name: 'Street'}),
226+
).not.toBeNull()
227+
})
228+
229+
test('accessible name comparison is case sensitive', () => {
230+
const {getByRole} = render(`<h1>Sign <em>up</em></h1>`)
231+
232+
// actual: "Sign up",
233+
// queried: "Sign Up"
234+
expect(() => getByRole('heading', {name: 'Sign Up'}))
235+
.toThrowErrorMatchingInlineSnapshot(`
236+
"Unable to find an accessible element with the role "heading" and name "Sign Up"
237+
238+
Here are the accessible roles:
239+
240+
heading:
241+
242+
Name "Sign up":
243+
<h1 />
244+
245+
--------------------------------------------------
246+
247+
<div>
248+
<h1>
249+
Sign
250+
<em>
251+
up
252+
</em>
253+
</h1>
254+
</div>"
255+
`)
256+
})
257+
258+
test('accessible name filter implements TextMatch', () => {
259+
const {getByRole} = render(
260+
`<h1>Sign <em>up</em></h1><h2>Details</h2><h2>Your Signature</h2>`,
261+
)
262+
263+
// subset via regex
264+
expect(getByRole('heading', {name: /gn u/})).not.toBeNull()
265+
// regex
266+
expect(getByRole('heading', {name: /^sign/i})).not.toBeNull()
267+
// function
268+
expect(
269+
getByRole('heading', {
270+
name: (name, element) => {
271+
return element.nodeName === 'H2' && name === 'Your Signature'
272+
},
273+
}),
274+
).not.toBeNull()
275+
})
276+
277+
test('TextMatch serialization in error message', () => {
278+
const {getByRole} = render(`<h1>Sign <em>up</em></h1>`)
279+
280+
expect(() => getByRole('heading', {name: /Login/}))
281+
.toThrowErrorMatchingInlineSnapshot(`
282+
"Unable to find an accessible element with the role "heading" and name \`/Login/\`
283+
284+
Here are the accessible roles:
285+
286+
heading:
287+
288+
Name "Sign up":
289+
<h1 />
290+
291+
--------------------------------------------------
292+
293+
<div>
294+
<h1>
295+
Sign
296+
<em>
297+
up
298+
</em>
299+
</h1>
300+
</div>"
301+
`)
302+
303+
expect(() => getByRole('heading', {name: () => false}))
304+
.toThrowErrorMatchingInlineSnapshot(`
305+
"Unable to find an accessible element with the role "heading" and name \`() => false\`
306+
307+
Here are the accessible roles:
308+
309+
heading:
310+
311+
Name "Sign up":
312+
<h1 />
313+
314+
--------------------------------------------------
315+
316+
<div>
317+
<h1>
318+
Sign
319+
<em>
320+
up
321+
</em>
322+
</h1>
323+
</div>"
324+
`)
325+
})
326+
186327
describe('configuration', () => {
187328
let originalConfig
188329
beforeEach(() => {

0 commit comments

Comments
 (0)