Skip to content

Commit e9b603d

Browse files
author
Kent C. Dodds
committed
feat(getByAltText): add a new query utility
1 parent 66b5a2c commit e9b603d

File tree

5 files changed

+60
-7
lines changed

5 files changed

+60
-7
lines changed

README.md

+21-4
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,20 @@ const inputNode = getByPlaceholderText('Username')
215215
> NOTE: a placeholder is not a good substitute for a label so you should
216216
> generally use `getByLabelText` instead.
217217
218+
#### `getByAltText(text: TextMatch): HTMLElement`
219+
220+
This will return the element (normally an `<img>`) that has the given `alt`
221+
text. Note that it only supports elements which accept an `alt` attribute:
222+
[`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img),
223+
[`<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input),
224+
and [`<area>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
225+
(intentionally excluding [`<applet>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/applet) as it's deprecated).
226+
227+
```javascript
228+
// <img alt="Incredibles 2 Poster" src="/incredibles-2.png" />
229+
const incrediblesPosterImg = getByText(/incredibles.*poster/)
230+
```
231+
218232
#### `getByText(text: TextMatch): HTMLElement`
219233

220234
This will search for all elements that have a text node with `textContent`
@@ -225,9 +239,10 @@ matching the given [`TextMatch`](#textmatch).
225239
const aboutAnchorNode = getByText('about')
226240
```
227241

228-
#### `getByTestId`
242+
#### `getByTestId(text: TextMatch): HTMLElement`
229243

230-
A shortcut to `` container.querySelector(`[data-testid="${yourId}"]`) ``.
244+
A shortcut to `` container.querySelector(`[data-testid="${yourId}"]`) `` (and it
245+
also accepts a [`TextMatch`](#textmatch)).
231246

232247
```javascript
233248
// <input data-testid="username-input" />
@@ -392,10 +407,12 @@ in mind, we recommend this order of priority:
392407
method a user finds those elements, so it should be your top preference.
393408
2. `getByPlaceholderText`: [A placeholder is not a substitute for a label](https://www.nngroup.com/articles/form-design-placeholders/).
394409
But if that's all you have, then it's better than alternatives.
395-
3. `getByText`: Not useful for forms, but this is the number 1 method a user
410+
3. `getByAltText`: If your element is one which supports `alt` text
411+
(`img`, `area`, and `input`), then you can use this to find that element.
412+
4. `getByText`: Not useful for forms, but this is the number 1 method a user
396413
finds other elements (like buttons to click), so it should be your top
397414
preference for non-form elements.
398-
4. `getByTestId`: The user cannot see (or hear) these, so this is only
415+
5. `getByTestId`: The user cannot see (or hear) these, so this is only
399416
recommended for cases where you can't match by text or it doesn't make sense
400417
(the text is dynamic).
401418

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

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ exports[`get throws a useful error message 3`] = `"Unable to find an element wit
88

99
exports[`get throws a useful error message 4`] = `"Unable to find an element by: [data-testid=\\"LucyRicardo\\"]"`;
1010

11+
exports[`get throws a useful error message 5`] = `"Unable to find an element with the alt text: LucyRicardo"`;
12+
1113
exports[`label with no form control 1`] = `"Found a label with the text of: alone, however no form control was found associated to that label. Make sure you're using the \\"for\\" attribute or \\"aria-labelledby\\" attribute correctly."`;
1214

1315
exports[`totally empty label 1`] = `"Found a label with the text of: , however no form control was found associated to that label. Make sure you're using the \\"for\\" attribute or \\"aria-labelledby\\" attribute correctly."`;

src/__tests__/element-queries.js

+17-3
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,30 @@ test('query can return null', () => {
88
queryByPlaceholderText,
99
queryByText,
1010
queryByTestId,
11+
queryByAltText,
1112
} = render(<div />)
1213
expect(queryByTestId('LucyRicardo')).toBeNull()
1314
expect(queryByLabelText('LucyRicardo')).toBeNull()
1415
expect(queryByPlaceholderText('LucyRicardo')).toBeNull()
1516
expect(queryByText('LucyRicardo')).toBeNull()
17+
expect(queryByAltText('LucyRicardo')).toBeNull()
1618
})
1719

1820
test('get throws a useful error message', () => {
19-
const {getByLabelText, getByPlaceholderText, getByText, getByTestId} = render(
20-
<div />,
21-
)
21+
const {
22+
getByLabelText,
23+
getByPlaceholderText,
24+
getByText,
25+
getByTestId,
26+
getByAltText,
27+
} = render(<div />)
2228
expect(() => getByLabelText('LucyRicardo')).toThrowErrorMatchingSnapshot()
2329
expect(() =>
2430
getByPlaceholderText('LucyRicardo'),
2531
).toThrowErrorMatchingSnapshot()
2632
expect(() => getByText('LucyRicardo')).toThrowErrorMatchingSnapshot()
2733
expect(() => getByTestId('LucyRicardo')).toThrowErrorMatchingSnapshot()
34+
expect(() => getByAltText('LucyRicardo')).toThrowErrorMatchingSnapshot()
2835
})
2936

3037
test('get can get form controls by label text', () => {
@@ -67,6 +74,13 @@ test('totally empty label', () => {
6774
expect(() => getByLabelText('')).toThrowErrorMatchingSnapshot()
6875
})
6976

77+
test('get element by its alt text', () => {
78+
const {getByAltText} = render(
79+
<img alt="finding nemo poster" src="/finding-nemo.png" />,
80+
)
81+
expect(getByAltText(/fin.*nem.*poster$/i).src).toBe('/finding-nemo.png')
82+
})
83+
7084
test('using jest helpers to assert element states', () => {
7185
const {queryByTestId} = render(<span data-testid="count-value">2</span>)
7286

src/queries.js

+18
Original file line numberDiff line numberDiff line change
@@ -121,13 +121,31 @@ function getByText(container, text, ...rest) {
121121
return el
122122
}
123123

124+
function queryByAltText(container, alt) {
125+
return (
126+
Array.from(container.querySelectorAll('img,input,area')).find(node =>
127+
matches(node.getAttribute('alt'), node, alt),
128+
) || null
129+
)
130+
}
131+
132+
function getByAltText(container, alt) {
133+
const el = queryByAltText(container, alt)
134+
if (!el) {
135+
throw new Error(`Unable to find an element with the alt text: ${alt}`)
136+
}
137+
return el
138+
}
139+
124140
export {
125141
queryByPlaceholderText,
126142
getByPlaceholderText,
127143
queryByText,
128144
getByText,
129145
queryByLabelText,
130146
getByLabelText,
147+
queryByAltText,
148+
getByAltText,
131149
queryByTestId,
132150
getByTestId,
133151
}

typings/index.d.ts

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ interface RenderResult {
1212
getByPlaceholderText: (id: string) => HTMLElement
1313
queryByLabelText: (id: string) => HTMLElement | null
1414
getByLabelText: (id: string) => HTMLElement
15+
queryByAltText: (text: string) => HTMLElement | null
16+
getByAltText: (text: string) => HTMLElement
1517
}
1618

1719
export function render(

0 commit comments

Comments
 (0)