Skip to content

Commit

Permalink
feat(getByAltText): add a new query utility
Browse files Browse the repository at this point in the history
  • Loading branch information
Kent C. Dodds committed Mar 29, 2018
1 parent 66b5a2c commit e9b603d
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 7 deletions.
25 changes: 21 additions & 4 deletions README.md
Expand Up @@ -215,6 +215,20 @@ const inputNode = getByPlaceholderText('Username')
> NOTE: a placeholder is not a good substitute for a label so you should
> generally use `getByLabelText` instead.
#### `getByAltText(text: TextMatch): HTMLElement`

This will return the element (normally an `<img>`) that has the given `alt`
text. Note that it only supports elements which accept an `alt` attribute:
[`<img>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img),
[`<input>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input),
and [`<area>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/area)
(intentionally excluding [`<applet>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/applet) as it's deprecated).

```javascript
// <img alt="Incredibles 2 Poster" src="/incredibles-2.png" />
const incrediblesPosterImg = getByText(/incredibles.*poster/)
```

#### `getByText(text: TextMatch): HTMLElement`

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

#### `getByTestId`
#### `getByTestId(text: TextMatch): HTMLElement`

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

```javascript
// <input data-testid="username-input" />
Expand Down Expand Up @@ -392,10 +407,12 @@ in mind, we recommend this order of priority:
method a user finds those elements, so it should be your top preference.
2. `getByPlaceholderText`: [A placeholder is not a substitute for a label](https://www.nngroup.com/articles/form-design-placeholders/).
But if that's all you have, then it's better than alternatives.
3. `getByText`: Not useful for forms, but this is the number 1 method a user
3. `getByAltText`: If your element is one which supports `alt` text
(`img`, `area`, and `input`), then you can use this to find that element.
4. `getByText`: Not useful for forms, but this is the number 1 method a user
finds other elements (like buttons to click), so it should be your top
preference for non-form elements.
4. `getByTestId`: The user cannot see (or hear) these, so this is only
5. `getByTestId`: The user cannot see (or hear) these, so this is only
recommended for cases where you can't match by text or it doesn't make sense
(the text is dynamic).
Expand Down
2 changes: 2 additions & 0 deletions src/__tests__/__snapshots__/element-queries.js.snap
Expand Up @@ -8,6 +8,8 @@ exports[`get throws a useful error message 3`] = `"Unable to find an element wit

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

exports[`get throws a useful error message 5`] = `"Unable to find an element with the alt text: LucyRicardo"`;

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."`;

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."`;
20 changes: 17 additions & 3 deletions src/__tests__/element-queries.js
Expand Up @@ -8,23 +8,30 @@ test('query can return null', () => {
queryByPlaceholderText,
queryByText,
queryByTestId,
queryByAltText,
} = render(<div />)
expect(queryByTestId('LucyRicardo')).toBeNull()
expect(queryByLabelText('LucyRicardo')).toBeNull()
expect(queryByPlaceholderText('LucyRicardo')).toBeNull()
expect(queryByText('LucyRicardo')).toBeNull()
expect(queryByAltText('LucyRicardo')).toBeNull()
})

test('get throws a useful error message', () => {
const {getByLabelText, getByPlaceholderText, getByText, getByTestId} = render(
<div />,
)
const {
getByLabelText,
getByPlaceholderText,
getByText,
getByTestId,
getByAltText,
} = render(<div />)
expect(() => getByLabelText('LucyRicardo')).toThrowErrorMatchingSnapshot()
expect(() =>
getByPlaceholderText('LucyRicardo'),
).toThrowErrorMatchingSnapshot()
expect(() => getByText('LucyRicardo')).toThrowErrorMatchingSnapshot()
expect(() => getByTestId('LucyRicardo')).toThrowErrorMatchingSnapshot()
expect(() => getByAltText('LucyRicardo')).toThrowErrorMatchingSnapshot()
})

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

test('get element by its alt text', () => {
const {getByAltText} = render(
<img alt="finding nemo poster" src="/finding-nemo.png" />,
)
expect(getByAltText(/fin.*nem.*poster$/i).src).toBe('/finding-nemo.png')
})

test('using jest helpers to assert element states', () => {
const {queryByTestId} = render(<span data-testid="count-value">2</span>)

Expand Down
18 changes: 18 additions & 0 deletions src/queries.js
Expand Up @@ -121,13 +121,31 @@ function getByText(container, text, ...rest) {
return el
}

function queryByAltText(container, alt) {
return (
Array.from(container.querySelectorAll('img,input,area')).find(node =>
matches(node.getAttribute('alt'), node, alt),
) || null
)
}

function getByAltText(container, alt) {
const el = queryByAltText(container, alt)
if (!el) {
throw new Error(`Unable to find an element with the alt text: ${alt}`)
}
return el
}

export {
queryByPlaceholderText,
getByPlaceholderText,
queryByText,
getByText,
queryByLabelText,
getByLabelText,
queryByAltText,
getByAltText,
queryByTestId,
getByTestId,
}
Expand Down
2 changes: 2 additions & 0 deletions typings/index.d.ts
Expand Up @@ -12,6 +12,8 @@ interface RenderResult {
getByPlaceholderText: (id: string) => HTMLElement
queryByLabelText: (id: string) => HTMLElement | null
getByLabelText: (id: string) => HTMLElement
queryByAltText: (text: string) => HTMLElement | null
getByAltText: (text: string) => HTMLElement
}

export function render(
Expand Down

0 comments on commit e9b603d

Please sign in to comment.