Skip to content

Commit 0490c45

Browse files
authored
Add guides for tricky topics (#34)
1 parent d3743fb commit 0490c45

File tree

8 files changed

+155
-144
lines changed

8 files changed

+155
-144
lines changed

docs/api-async.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
---
22
id: api-async
3-
title: Async
3+
title: Async Utilities
44
---
55

6+
Several utilities are provided for dealing with asynchronous code. These can be
7+
useful to wait for an element to appear or disappear in response to an action.
8+
(See the [guide to testing disappearance](guide-disappearance.md).)
9+
610
### `wait`
711

812
```typescript

docs/api-events.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
id: api-events
3-
title: Events
3+
title: Firing Events
44
---
55

66
## `fireEvent`

docs/faq.md

+9-24
Original file line numberDiff line numberDiff line change
@@ -7,36 +7,21 @@ title: FAQ
77

88
<summary>Which get method should I use?</summary>
99

10-
Based on [the Guiding Principles](guiding-principles.md), your test should
11-
resemble how your code (component, page, etc.) as much as possible. With this in
12-
mind, we recommend this order of priority:
13-
14-
1. `getByLabelText`: Only really good for form fields, but this is the number 1
15-
method a user finds those elements, so it should be your top preference.
16-
2. `getByPlaceholderText`:
17-
[A placeholder is not a substitute for a label](https://www.nngroup.com/articles/form-design-placeholders/).
18-
But if that's all you have, then it's better than alternatives.
19-
3. `getByText`: Not useful for forms, but this is the number 1 method a user
20-
finds other elements (like buttons to click), so it should be your top
21-
preference for non-form elements.
22-
4. `getByAltText`: If your element is one which supports `alt` text (`img`,
23-
`area`, and `input`), then you can use this to find that element.
24-
5. `getByTestId`: The user cannot see (or hear) these, so this is only
25-
recommended for cases where you can't match by text or it doesn't make sense
26-
(the text is dynamic).
27-
28-
Other than that, you can also use the `container` to query the rendered
29-
component as well (using the regular
30-
[`querySelector` API](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)).
10+
See [Which Query Should I Use](guide-which-query.md)
3111

3212
</details>
3313

3414
<details>
3515

3616
<summary>Can I write unit tests with this library?</summary>
3717

38-
Definitely yes! You can write unit, integration, functional, and end-to-end
39-
tests with this library.
18+
Definitely yes! You can write unit, integration, and end-to-end tests with this
19+
library.
20+
21+
As you write your tests, keep in mind:
22+
23+
> The more your tests resemble the way your software is used, the more
24+
> confidence they can give you. - [17 Feb 2018][guiding-principle]
4025
4126
</details>
4227

@@ -47,7 +32,7 @@ tests with this library.
4732
This is fairly common. Our first bit of advice is to try to get the default text
4833
used in your tests. That will make everything much easier (more than just using
4934
this utility). If that's not possible, then you're probably best to just stick
50-
with `data-testid`s (which is not too bad anyway).
35+
with `data-testid`s (which is not bad anyway).
5136

5237
</details>
5338

docs/guide-disappearance.md

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
---
2+
id: guide-disappearance
3+
title: Appearance and Disappearance
4+
---
5+
6+
Sometimes you need to test that an element is present and then disappears or
7+
vice versa.
8+
9+
## Waiting for appearance
10+
11+
If you need to wait for an element to appear, the
12+
[async wait utilities](api-async.md) allow you to wait for an assertion to be
13+
satisfied before proceeding. The wait utilities retry until the query passes or
14+
times out.
15+
16+
```jsx
17+
test('movie title appears', async () => {
18+
// element is initially not present...
19+
20+
// wait for appearance
21+
await wait(() => {
22+
expect(getByText('the lion king')).toBeInTheDocument()
23+
})
24+
25+
// wait for appearance and return the element
26+
const movie = await waitForElement(() => getByText('the lion king'))
27+
})
28+
```
29+
30+
## Waiting for disappearance
31+
32+
The `wait` [async helper](api-async.md) function retries until the wrapped
33+
function stops throwing an error. This can be used to assert that an element
34+
disappears from the page.
35+
36+
```jsx
37+
test('movie title goes away', async () => {
38+
// element is initially present...
39+
// note use of queryBy instead of getBy to return null
40+
// instead of throwing in the query itself
41+
await wait(() => {
42+
expect(queryByText('i, robot')).not.toBeInTheDocument()
43+
})
44+
})
45+
```
46+
47+
## Asserting elements are not present
48+
49+
The standard `getBy` methods throw an error when they can't find an element, so
50+
if you want to make an assertion that an element is _not_ present in the DOM,
51+
you can use `queryBy` APIs instead:
52+
53+
```javascript
54+
const submitButton = queryByText(container, 'submit')
55+
expect(submitButton).toBeNull() // it doesn't exist
56+
```
57+
58+
The `queryAll` APIs version return an array of matching nodes. The length of the
59+
array can be useful for assertions after elements are added or removed from the
60+
DOM.
61+
62+
```javascript
63+
const submitButtons = queryAllByText(container, 'submit')
64+
expect(submitButtons).toHaveLength(2) // expect 2 elements
65+
```
66+
67+
### `not.toBeInTheDocument`
68+
69+
The [`jest-dom`](ecosystem-jest-dom.md) utility library provides the
70+
`.toBeInTheDocument()` matcher, which can be used to assert that an element is
71+
in the body of the document, or not. This can be more meaningful than asserting
72+
a query result is `null`.
73+
74+
```javascript
75+
import 'jest-dom/extend-expect'
76+
// use `queryBy` to avoid throwing an error with `getBy`
77+
const submitButton = queryByText(container, 'submit')
78+
expect(submitButton).not.toBeInTheDocument()
79+
```

docs/guide-which-query.md

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
---
2+
id: guide-which-query
3+
title: Which query should I use?
4+
---
5+
6+
## Priority
7+
8+
Based on [the Guiding Principles](guiding-principles.md), your test should
9+
resemble how users interact with your code (component, page, etc.) as much as
10+
possible. With this in mind, we recommend this order of priority:
11+
12+
1. **Queries Accessible to Everyone** queries that reflect the experience of
13+
visual/mouse users as well as those that use assistive technology
14+
1. `getByLabelText`: Only really good for form fields, but this is the number
15+
one method a user finds those elements, so it should be your top
16+
preference.
17+
1. `getByPlaceholderText`:
18+
[A placeholder is not a substitute for a label](https://www.nngroup.com/articles/form-design-placeholders/).
19+
But if that's all you have, then it's better than alternatives.
20+
1. `getByText`: Not useful for forms, but this is the number 1 method a user
21+
finds other elements (like buttons to click), so it should be your top
22+
preference for non-form elements.
23+
1. `getByDisplayValue`: The current value of a form element can be useful
24+
when navigating a page with filled-in values.
25+
1. **Semantic Queries** HTML5 and ARIA compliant selectors
26+
1. `getByAltText`: If your element is one which supports `alt` text (`img`,
27+
`area`, and `input`), then you can use this to find that element.
28+
1. `getByTitle`: The title attribute is usually more accessible by screen
29+
readers than mouse/visual users
30+
1. `getByRole`: This can be used to select dialog boxes and other
31+
difficult-to-capture elements in a more semantic way
32+
1. **Test IDs**
33+
1. `getByTestId`: The user cannot see (or hear) these, so this is only
34+
recommended for cases where you can't match by text or it doesn't make
35+
sense (the text is dynamic).
36+
37+
## Manual Queries
38+
39+
On top of the queries provided by the testing library, you can using the regular
40+
[`querySelector` DOM API](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
41+
to query elements. Note that using this as an escape hatch to query by class or
42+
id is a bad practice because users can't see or identify these attributes. Use a
43+
testid if you have to.
44+
45+
```jsx
46+
// react-testing-library
47+
const { container } = render(<MyComponent />)
48+
const foo = container.querySelector(['data-foo="bar"'])
49+
```

docs/react-testing-library/faq.md

+2-113
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ id: faq
33
title: FAQ
44
---
55

6+
See also the [main FAQ](/docs/faq) for questions not specific to React testing
7+
68
<details>
79

810
<summary>How do I test input onChange handlers?</summary>
@@ -62,34 +64,6 @@ as part of the `change` method call.
6264

6365
<details>
6466

65-
<summary>Which get method should I use?</summary>
66-
67-
Based on [the Guiding Principles](./guiding-principles), your test should
68-
resemble how your code (component, page, etc.) is used as much as possible. With
69-
this in mind, we recommend this order of priority:
70-
71-
1. `getByLabelText`: Only really good for form fields, but this is the number 1
72-
method a user finds those elements, so it should be your top preference.
73-
2. `getByPlaceholderText`:
74-
[A placeholder is not a substitute for a label](https://www.nngroup.com/articles/form-design-placeholders/).
75-
But if that's all you have, then it's better than alternatives.
76-
3. `getByText`: Not useful for forms, but this is the number 1 method a user
77-
finds other elements (like buttons to click), so it should be your top
78-
preference for non-form elements.
79-
4. `getByAltText`: If your element is one which supports `alt` text (`img`,
80-
`area`, and `input`), then you can use this to find that element.
81-
5. `getByTestId`: The user cannot see (or hear) these, so this is only
82-
recommended for cases where you can't match by text or it doesn't make sense
83-
(the text is dynamic).
84-
85-
Other than that, you can also use the `container` to query the rendered
86-
component as well (using the regular
87-
[`querySelector` API](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)).
88-
89-
</details>
90-
91-
<details>
92-
9367
<summary>Can I write unit tests with this library?</summary>
9468

9569
Definitely yes! You can write unit and integration tests with this library. See
@@ -107,17 +81,6 @@ As you write your tests, keep in mind:
10781

10882
<details>
10983

110-
<summary>What if my app is localized and I don't have access to the text in test?</summary>
111-
112-
This is fairly common. Our first bit of advice is to try to get the default text
113-
used in your tests. That will make everything much easier (more than just using
114-
this utility). If that's not possible, then you're probably best to just stick
115-
with `data-testid`s (which is not bad anyway).
116-
117-
</details>
118-
119-
<details>
120-
12184
<summary>If I can't use shallow rendering, how do I mock out components in tests?</summary>
12285

12386
In general, you should avoid mocking out components (see
@@ -169,80 +132,6 @@ Learn more about how Jest mocks work from my blog post:
169132

170133
<details>
171134

172-
<summary>What if I want to verify that an element does NOT exist?</summary>
173-
174-
You typically will get access to rendered elements using the `getByTestId`
175-
utility. However, that function will throw an error if the element isn't found.
176-
If you want to specifically test for the absence of an element, then you should
177-
use the `queryByTestId` utility which will return the element if found or `null`
178-
if not.
179-
180-
```javascript
181-
expect(queryByTestId('thing-that-does-not-exist')).toBeNull()
182-
```
183-
184-
</details>
185-
186-
<details>
187-
188-
<summary>I really don't like data-testids, but none of the other queries make sense. Do I have to use a data-testid?</summary>
189-
190-
Definitely not. That said, a common reason people don't like the `data-testid`
191-
attribute is they're concerned about shipping that to production. I'd suggest
192-
that you probably want some simple E2E tests that run in production on occasion
193-
to make certain that things are working smoothly. In that case the `data-testid`
194-
attributes will be very useful. Even if you don't run these in production, you
195-
may want to run some E2E tests that run on the same code you're about to ship to
196-
production. In that case, the `data-testid` attributes will be valuable there as
197-
well.
198-
199-
All that said, if you really don't want to ship `data-testid` attributes, then
200-
you can use
201-
[this simple babel plugin](https://www.npmjs.com/package/babel-plugin-react-remove-properties)
202-
to remove them.
203-
204-
If you don't want to use them at all, then you can simply use regular DOM
205-
methods and properties to query elements off your container.
206-
207-
```javascript
208-
const firstLiInDiv = container.querySelector('div li')
209-
const allLisInDiv = container.querySelectorAll('div li')
210-
const rootElement = container.firstChild
211-
```
212-
213-
</details>
214-
215-
<details>
216-
217-
<summary>What if I’m iterating over a list of items that I want to put the data-testid="item" attribute on. How do I distinguish them from each other?</summary>
218-
219-
You can make your selector just choose the one you want by including :nth-child
220-
in the selector.
221-
222-
```javascript
223-
const thirdLiInUl = container.querySelector('ul > li:nth-child(3)')
224-
```
225-
226-
Or you could include the index or an ID in your attribute:
227-
228-
```javascript
229-
<li data-testid={`item-${item.id}`}>{item.text}</li>
230-
```
231-
232-
And then you could use the `getByTestId` utility:
233-
234-
```javascript
235-
const items = [
236-
/* your items */
237-
]
238-
const { getByTestId } = render(/* your component with the items */)
239-
const thirdItem = getByTestId(`item-${items[2].id}`)
240-
```
241-
242-
</details>
243-
244-
<details>
245-
246135
<summary>What about enzyme is "bloated with complexity and features" and "encourage
247136
poor testing practices"?</summary>
248137

docs/react-testing-library/setup.md

+9-5
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ the setup and teardown of tests in individual files. For example, you can ensure
1818
[`cleanup`](./api#cleanup) is called after each test and import additional
1919
assertions.
2020

21-
To do this with Jest 24, you can add the
21+
To do this with Jest 24 and up, you can add the
2222
[`setupFilesAfterEnv`](https://jestjs.io/docs/en/configuration.html#setupfilesafterenv-array)
2323
option to your Jest config.
2424

@@ -33,12 +33,14 @@ module.exports = {
3333
}
3434
```
3535

36-
### Jest 23
36+
### Older versions of Jest
3737

38-
Jest 23 uses the
38+
<details>
39+
40+
Jest versions 23 and below use the
3941
[`setupTestFrameworkScriptFile`](https://jestjs.io/docs/en/23.x/configuration#setuptestframeworkscriptfile-string)
40-
option in your Jest config. This setup file can be anywhere, for example
41-
`jest.setup.js` or `./utils/setupTests.js`.
42+
option in your Jest config instead of `setupFilesAfterEnv`. This setup file can
43+
be anywhere, for example `jest.setup.js` or `./utils/setupTests.js`.
4244

4345
If you are using the default setup from create-react-app, this option is set to
4446
`src/setupTests.js`. You should create this file if it doesn't exist and put the
@@ -62,6 +64,8 @@ import 'jest-dom/extend-expect'
6264
import 'react-testing-library/cleanup-after-each'
6365
```
6466

67+
</details>
68+
6569
## Custom Render
6670

6771
It's often useful to define a custom render method that includes things like

website/sidebars.json

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"example-external"
2020
],
2121
"API": ["api-queries", "api-events", "api-async", "api-helpers"],
22+
"Guides": ["guide-which-query", "guide-disappearance"],
2223
"Wrappers": [
2324
{
2425
"type": "subcategory",

0 commit comments

Comments
 (0)