Skip to content

Add guides for tricky topics #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 13, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion docs/api-async.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
---
id: api-async
title: Async
title: Async Utilities
---

Several utilities are provided for dealing with asynchronous code. These can be
useful to wait for an element to appear or disappear in response to an action.
(See the [guide to testing disappearance](guide-disappearance.md).)

### `wait`

```typescript
Expand Down
2 changes: 1 addition & 1 deletion docs/api-events.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
id: api-events
title: Events
title: Firing Events
---

## `fireEvent`
Expand Down
33 changes: 9 additions & 24 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,21 @@ title: FAQ

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

Based on [the Guiding Principles](guiding-principles.md), your test should
resemble how your code (component, page, etc.) as much as possible. With this in
mind, we recommend this order of priority:

1. `getByLabelText`: Only really good for form fields, but this is the number 1
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
finds other elements (like buttons to click), so it should be your top
preference for non-form elements.
4. `getByAltText`: If your element is one which supports `alt` text (`img`,
`area`, and `input`), then you can use this to find that element.
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).

Other than that, you can also use the `container` to query the rendered
component as well (using the regular
[`querySelector` API](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)).
See [Which Query Should I Use](guide-which-query.md)

</details>

<details>

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

Definitely yes! You can write unit, integration, functional, and end-to-end
tests with this library.
Definitely yes! You can write unit, integration, and end-to-end tests with this
library.

As you write your tests, keep in mind:

> The more your tests resemble the way your software is used, the more
> confidence they can give you. - [17 Feb 2018][guiding-principle]

</details>

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

</details>

Expand Down
79 changes: 79 additions & 0 deletions docs/guide-disappearance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
id: guide-disappearance
title: Appearance and Disappearance
---

Sometimes you need to test that an element is present and then disappears or
vice versa.

## Waiting for appearance

If you need to wait for an element to appear, the
[async wait utilities](api-async.md) allow you to wait for an assertion to be
satisfied before proceeding. The wait utilities retry until the query passes or
times out.

```jsx
test('movie title appears', async () => {
// element is initially not present...

// wait for appearance
await wait(() => {
expect(getByText('the lion king')).toBeInTheDocument()
})

// wait for appearance and return the element
const movie = await waitForElement(() => getByText('the lion king'))
})
```

## Waiting for disappearance

The `wait` [async helper](api-async.md) function retries until the wrapped
function stops throwing an error. This can be used to assert that an element
disappears from the page.

```jsx
test('movie title goes away', async () => {
// element is initially present...
// note use of queryBy instead of getBy to return null
// instead of throwing in the query itself
await wait(() => {
expect(queryByText('i, robot')).not.toBeInTheDocument()
})
})
```

## Asserting elements are not present

The standard `getBy` methods throw an error when they can't find an element, so
if you want to make an assertion that an element is _not_ present in the DOM,
you can use `queryBy` APIs instead:

```javascript
const submitButton = queryByText(container, 'submit')
expect(submitButton).toBeNull() // it doesn't exist
```

The `queryAll` APIs version return an array of matching nodes. The length of the
array can be useful for assertions after elements are added or removed from the
DOM.

```javascript
const submitButtons = queryAllByText(container, 'submit')
expect(submitButtons).toHaveLength(2) // expect 2 elements
```

### `not.toBeInTheDocument`

The [`jest-dom`](ecosystem-jest-dom.md) utility library provides the
`.toBeInTheDocument()` matcher, which can be used to assert that an element is
in the body of the document, or not. This can be more meaningful than asserting
a query result is `null`.

```javascript
import 'jest-dom/extend-expect'
// use `queryBy` to avoid throwing an error with `getBy`
const submitButton = queryByText(container, 'submit')
expect(submitButton).not.toBeInTheDocument()
```
49 changes: 49 additions & 0 deletions docs/guide-which-query.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
id: guide-which-query
title: Which query should I use?
---

## Priority

Based on [the Guiding Principles](guiding-principles.md), your test should
resemble how users interact with your code (component, page, etc.) as much as
possible. With this in mind, we recommend this order of priority:

1. **Queries Accessible to Everyone** queries that reflect the experience of
visual/mouse users as well as those that use assistive technology
1. `getByLabelText`: Only really good for form fields, but this is the number
one method a user finds those elements, so it should be your top
preference.
1. `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.
1. `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.
1. `getByDisplayValue`: The current value of a form element can be useful
when navigating a page with filled-in values.
1. **Semantic Queries** HTML5 and ARIA compliant selectors
1. `getByAltText`: If your element is one which supports `alt` text (`img`,
`area`, and `input`), then you can use this to find that element.
1. `getByTitle`: The title attribute is usually more accessible by screen
readers than mouse/visual users
1. `getByRole`: This can be used to select dialog boxes and other
difficult-to-capture elements in a more semantic way
1. **Test IDs**
1. `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).

## Manual Queries

On top of the queries provided by the testing library, you can using the regular
[`querySelector` DOM API](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)
to query elements. Note that using this as an escape hatch to query by class or
id is a bad practice because users can't see or identify these attributes. Use a
testid if you have to.

```jsx
// react-testing-library
const { container } = render(<MyComponent />)
const foo = container.querySelector(['data-foo="bar"'])
```
115 changes: 2 additions & 113 deletions docs/react-testing-library/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ id: faq
title: FAQ
---

See also the [main FAQ](/docs/faq) for questions not specific to React testing

<details>

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

<details>

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

Based on [the Guiding Principles](./guiding-principles), your test should
resemble how your code (component, page, etc.) is used as much as possible. With
this in mind, we recommend this order of priority:

1. `getByLabelText`: Only really good for form fields, but this is the number 1
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
finds other elements (like buttons to click), so it should be your top
preference for non-form elements.
4. `getByAltText`: If your element is one which supports `alt` text (`img`,
`area`, and `input`), then you can use this to find that element.
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).

Other than that, you can also use the `container` to query the rendered
component as well (using the regular
[`querySelector` API](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector)).

</details>

<details>

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

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

<details>

<summary>What if my app is localized and I don't have access to the text in test?</summary>

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

</details>

<details>

<summary>If I can't use shallow rendering, how do I mock out components in tests?</summary>

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

<details>

<summary>What if I want to verify that an element does NOT exist?</summary>

You typically will get access to rendered elements using the `getByTestId`
utility. However, that function will throw an error if the element isn't found.
If you want to specifically test for the absence of an element, then you should
use the `queryByTestId` utility which will return the element if found or `null`
if not.

```javascript
expect(queryByTestId('thing-that-does-not-exist')).toBeNull()
```

</details>

<details>

<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>

Definitely not. That said, a common reason people don't like the `data-testid`
attribute is they're concerned about shipping that to production. I'd suggest
that you probably want some simple E2E tests that run in production on occasion
to make certain that things are working smoothly. In that case the `data-testid`
attributes will be very useful. Even if you don't run these in production, you
may want to run some E2E tests that run on the same code you're about to ship to
production. In that case, the `data-testid` attributes will be valuable there as
well.

All that said, if you really don't want to ship `data-testid` attributes, then
you can use
[this simple babel plugin](https://www.npmjs.com/package/babel-plugin-react-remove-properties)
to remove them.

If you don't want to use them at all, then you can simply use regular DOM
methods and properties to query elements off your container.

```javascript
const firstLiInDiv = container.querySelector('div li')
const allLisInDiv = container.querySelectorAll('div li')
const rootElement = container.firstChild
```

</details>

<details>

<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>

You can make your selector just choose the one you want by including :nth-child
in the selector.

```javascript
const thirdLiInUl = container.querySelector('ul > li:nth-child(3)')
```

Or you could include the index or an ID in your attribute:

```javascript
<li data-testid={`item-${item.id}`}>{item.text}</li>
```

And then you could use the `getByTestId` utility:

```javascript
const items = [
/* your items */
]
const { getByTestId } = render(/* your component with the items */)
const thirdItem = getByTestId(`item-${items[2].id}`)
```

</details>

<details>

<summary>What about enzyme is "bloated with complexity and features" and "encourage
poor testing practices"?</summary>

Expand Down
14 changes: 9 additions & 5 deletions docs/react-testing-library/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ the setup and teardown of tests in individual files. For example, you can ensure
[`cleanup`](./api#cleanup) is called after each test and import additional
assertions.

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

Expand All @@ -33,12 +33,14 @@ module.exports = {
}
```

### Jest 23
### Older versions of Jest

Jest 23 uses the
<details>

Jest versions 23 and below use the
[`setupTestFrameworkScriptFile`](https://jestjs.io/docs/en/23.x/configuration#setuptestframeworkscriptfile-string)
option in your Jest config. This setup file can be anywhere, for example
`jest.setup.js` or `./utils/setupTests.js`.
option in your Jest config instead of `setupFilesAfterEnv`. This setup file can
be anywhere, for example `jest.setup.js` or `./utils/setupTests.js`.

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

</details>

## Custom Render

It's often useful to define a custom render method that includes things like
Expand Down
1 change: 1 addition & 0 deletions website/sidebars.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"example-external"
],
"API": ["api-queries", "api-events", "api-async", "api-helpers"],
"Guides": ["guide-which-query", "guide-disappearance"],
"Wrappers": [
{
"type": "subcategory",
Expand Down