Skip to content

Commit 4adbc1d

Browse files
lgandeckiKent C. Dodds
authored and
Kent C. Dodds
committed
feat(waitForExpect): add waitForExpect (#25)
* added waitForExpect with test * added typescript and simplified version of the waitForExpect, used and exports its typings * added initial notes about waitForExpect * fixed styling * minor stylistic change * updated tests to remove the nesting * updated readme * fixed d .md syntax * improved style Closes #21
1 parent b13b1c1 commit 4adbc1d

File tree

6 files changed

+122
-4
lines changed

6 files changed

+122
-4
lines changed

.all-contributorsrc

+10
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,16 @@
9696
"contributions": [
9797
"doc"
9898
]
99+
},
100+
{
101+
"login": "lgandecki",
102+
"name": "Łukasz Gandecki",
103+
"avatar_url": "https://avatars1.githubusercontent.com/u/4002543?v=4",
104+
"profile": "http://team.thebrain.pro",
105+
"contributions": [
106+
"code",
107+
"test"
108+
]
99109
}
100110
]
101111
}

README.md

+64-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
[![downloads][downloads-badge]][npmtrends]
1717
[![MIT License][license-badge]][license]
1818

19-
[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors)
19+
[![All Contributors](https://img.shields.io/badge/all_contributors-10-orange.svg?style=flat-square)](#contributors)
2020
[![PRs Welcome][prs-badge]][prs]
2121
[![Code of Conduct][coc-badge]][coc]
2222

@@ -78,8 +78,11 @@ facilitate testing implementation details). Read more about this in
7878
* [Usage](#usage)
7979
* [`Simulate`](#simulate)
8080
* [`flushPromises`](#flushpromises)
81+
* [`waitForExpect`](#waitforexpect)
8182
* [`render`](#render)
8283
* [Custom Jest Matchers](#custom-jest-matchers)
84+
* [`toBeInTheDOM`](#tobeinthedom)
85+
* [`toHaveTextContent`](#tohavetextcontent)
8386
* [`TextMatch`](#textmatch)
8487
* [`query` APIs](#query-apis)
8588
* [Examples](#examples)
@@ -151,6 +154,35 @@ you make your test function an `async` function and use
151154

152155
See an example in the section about `render` below.
153156

157+
### `waitForExpect`
158+
159+
Defined as:
160+
161+
```javascript
162+
waitForExpect(expectation: () => void, timeout?: number, interval?: number) => Promise<{}>;
163+
```
164+
165+
When in need to wait for non-deterministic periods of time you can use waitForExpect,
166+
to wait for your expectations to pass. Take a look at [`Is there a different way to wait for things to happen?`](#waitForExpect) part of the FAQ,
167+
or the function documentation here: [`wait-for-expect`](https://github.com/TheBrainFamily/wait-for-expect)
168+
or just take a look at this simple example:
169+
170+
```javascript
171+
...
172+
await waitForExpect(() => expect(queryByLabelText('username')).not.toBeNull())
173+
getByLabelText('username').value = 'chucknorris'
174+
...
175+
```
176+
177+
Another advantage of waitForExpect in comparison to flushPromises, is that
178+
flushPromises will not flush promises that have not been queued up already,
179+
for example, if they will queue up as a result of the initial promises.
180+
In consequence of that, you might have to call flushPromises multiple times to get your components
181+
to your desired state.
182+
183+
This can happen for example, when you integration test your apollo-connected react components
184+
that go a couple level deep, with queries fired up in consequent components.
185+
154186
### `render`
155187

156188
In the example above, the `render` method returns an object that has a few
@@ -591,6 +623,36 @@ that this is only effective if you've mocked out your async requests to resolve
591623
immediately (like the `axios` mock we have in the examples). It will not `await`
592624
for promises that are not already resolved by the time you attempt to flush them.
593625

626+
In case this doesn't work for you the way you would expect, take a look at the
627+
waitForExpect function that should be much more intuitive to use.
628+
629+
</details>
630+
631+
<details>
632+
633+
<summary><a name="waitForExpectFAQ"></a>Is there a different way to wait for things to happen? For example for end to end or contract tests?</summary>
634+
Definitely! There is an abstraction called `waitForExpect` that will keep
635+
calling your expectations until a timeout or the expectation passes - whatever happens first.
636+
637+
Please take a look at this example (taken from [`here`](https://github.com/kentcdodds/react-testing-library/blob/master/src/__tests__/end-to-end.js)):
638+
639+
```javascript
640+
import {render, waitForExpect} from 'react-testing-library'
641+
test('it waits for the data to be loaded', async () => {
642+
const {queryByText, queryByTestId} = render(<ComponentWithLoader />)
643+
644+
// Initially the loader shows
645+
expect(queryByText('Loading...')).toBeTruthy()
646+
647+
// This will pass when the state of the component changes once the data is available
648+
// the loader will disappear, and the data will be shown
649+
await waitForExpect(() => expect(queryByText('Loading...')).toBeNull())
650+
expect(queryByTestId('message').textContent).toMatch(/Hello World/)
651+
})
652+
```
653+
654+
For consistency and making your tests easier to understand, you can use it instead of flushPromises.
655+
594656
</details>
595657

596658
## Other Solutions
@@ -634,7 +696,7 @@ Thanks goes to these people ([emoji key][emojis]):
634696
<!-- prettier-ignore -->
635697
| [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub><b>Kent C. Dodds</b></sub>](https://kentcdodds.com)<br />[💻](https://github.com/kentcdodds/react-testing-library/commits?author=kentcdodds "Code") [📖](https://github.com/kentcdodds/react-testing-library/commits?author=kentcdodds "Documentation") [🚇](#infra-kentcdodds "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/kentcdodds/react-testing-library/commits?author=kentcdodds "Tests") | [<img src="https://avatars1.githubusercontent.com/u/2430381?v=4" width="100px;"/><br /><sub><b>Ryan Castner</b></sub>](http://audiolion.github.io)<br />[📖](https://github.com/kentcdodds/react-testing-library/commits?author=audiolion "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/8008023?v=4" width="100px;"/><br /><sub><b>Daniel Sandiego</b></sub>](https://www.dnlsandiego.com)<br />[💻](https://github.com/kentcdodds/react-testing-library/commits?author=dnlsandiego "Code") | [<img src="https://avatars2.githubusercontent.com/u/12592677?v=4" width="100px;"/><br /><sub><b>Paweł Mikołajczyk</b></sub>](https://github.com/Miklet)<br />[💻](https://github.com/kentcdodds/react-testing-library/commits?author=Miklet "Code") | [<img src="https://avatars3.githubusercontent.com/u/464978?v=4" width="100px;"/><br /><sub><b>Alejandro Ñáñez Ortiz</b></sub>](http://co.linkedin.com/in/alejandronanez/)<br />[📖](https://github.com/kentcdodds/react-testing-library/commits?author=alejandronanez "Documentation") | [<img src="https://avatars0.githubusercontent.com/u/1402095?v=4" width="100px;"/><br /><sub><b>Matt Parrish</b></sub>](https://github.com/pbomb)<br />[🐛](https://github.com/kentcdodds/react-testing-library/issues?q=author%3Apbomb "Bug reports") [💻](https://github.com/kentcdodds/react-testing-library/commits?author=pbomb "Code") [📖](https://github.com/kentcdodds/react-testing-library/commits?author=pbomb "Documentation") [⚠️](https://github.com/kentcdodds/react-testing-library/commits?author=pbomb "Tests") | [<img src="https://avatars1.githubusercontent.com/u/1288694?v=4" width="100px;"/><br /><sub><b>Justin Hall</b></sub>](https://github.com/wKovacs64)<br />[📦](#platform-wKovacs64 "Packaging/porting to new platform") |
636698
| :---: | :---: | :---: | :---: | :---: | :---: | :---: |
637-
| [<img src="https://avatars1.githubusercontent.com/u/1241511?s=460&v=4" width="100px;"/><br /><sub><b>Anto Aravinth</b></sub>](https://github.com/antoaravinth)<br />[💻](https://github.com/kentcdodds/react-testing-library/commits?author=antoaravinth "Code") [⚠️](https://github.com/kentcdodds/react-testing-library/commits?author=antoaravinth "Tests") [📖](https://github.com/kentcdodds/react-testing-library/commits?author=antoaravinth "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/3462296?v=4" width="100px;"/><br /><sub><b>Jonah Moses</b></sub>](https://github.com/JonahMoses)<br />[📖](https://github.com/kentcdodds/react-testing-library/commits?author=JonahMoses "Documentation") |
699+
| [<img src="https://avatars1.githubusercontent.com/u/1241511?s=460&v=4" width="100px;"/><br /><sub><b>Anto Aravinth</b></sub>](https://github.com/antoaravinth)<br />[💻](https://github.com/kentcdodds/react-testing-library/commits?author=antoaravinth "Code") [⚠️](https://github.com/kentcdodds/react-testing-library/commits?author=antoaravinth "Tests") [📖](https://github.com/kentcdodds/react-testing-library/commits?author=antoaravinth "Documentation") | [<img src="https://avatars2.githubusercontent.com/u/3462296?v=4" width="100px;"/><br /><sub><b>Jonah Moses</b></sub>](https://github.com/JonahMoses)<br />[📖](https://github.com/kentcdodds/react-testing-library/commits?author=JonahMoses "Documentation") | [<img src="https://avatars1.githubusercontent.com/u/4002543?v=4" width="100px;"/><br /><sub><b>Łukasz Gandecki</b></sub>](http://team.thebrain.pro)<br />[💻](https://github.com/kentcdodds/react-testing-library/commits?author=lgandecki "Code") [⚠️](https://github.com/kentcdodds/react-testing-library/commits?author=lgandecki "Tests") [📖](https://github.com/kentcdodds/react-testing-library/commits?author=lgandecki "Documentation") |
638700

639701
<!-- ALL-CONTRIBUTORS-LIST:END -->
640702

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
"keywords": [],
2626
"author": "Kent C. Dodds <kent@doddsfamily.us> (http://kentcdodds.com/)",
2727
"license": "MIT",
28-
"dependencies": {},
28+
"dependencies": {
29+
"wait-for-expect": "0.4.0"
30+
},
2931
"devDependencies": {
3032
"@types/react-dom": "^16.0.4",
3133
"axios": "^0.18.0",

src/__tests__/end-to-end.js

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React from 'react'
2+
import {render, waitForExpect} from '../'
3+
4+
const fetchAMessage = () =>
5+
new Promise(resolve => {
6+
// we are using random timeout here to simulate a real-time example
7+
// of an async operation calling a callback at a non-deterministic time
8+
const randomTimeout = Math.floor(Math.random() * 100)
9+
setTimeout(() => {
10+
resolve({returnedMessage: 'Hello World'})
11+
}, randomTimeout)
12+
})
13+
14+
class ComponentWithLoader extends React.Component {
15+
state = {loading: true}
16+
async componentDidMount() {
17+
const data = await fetchAMessage()
18+
this.setState({data, loading: false}) // eslint-disable-line
19+
}
20+
render() {
21+
if (this.state.loading) {
22+
return <div>Loading...</div>
23+
} else {
24+
return (
25+
<div data-testid="message">
26+
Loaded this message: {this.state.data.returnedMessage}!
27+
</div>
28+
)
29+
}
30+
}
31+
}
32+
33+
test('it waits for the data to be loaded', async () => {
34+
const {queryByText, queryByTestId} = render(<ComponentWithLoader />)
35+
36+
expect(queryByText('Loading...')).toBeTruthy()
37+
38+
await waitForExpect(() => expect(queryByText('Loading...')).toBeNull())
39+
expect(queryByTestId('message').textContent).toMatch(/Hello World/)
40+
})

src/index.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import ReactDOM from 'react-dom'
22
import {Simulate} from 'react-dom/test-utils'
3+
import waitForExpect from 'wait-for-expect'
34
import * as queries from './queries'
45

56
function render(ui, {container = document.createElement('div')} = {}) {
@@ -24,4 +25,4 @@ function flushPromises() {
2425
return new Promise(resolve => setImmediate(resolve))
2526
}
2627

27-
export {render, flushPromises, Simulate}
28+
export {render, flushPromises, Simulate, waitForExpect}

typings/index.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {Simulate as ReactSimulate} from 'react-dom/test-utils'
2+
import waitForExpect from 'wait-for-expect'
23

34
interface RenderResult {
45
container: HTMLDivElement
@@ -21,3 +22,5 @@ export function render(
2122
export function flushPromises(): Promise<void>
2223

2324
export const Simulate: typeof ReactSimulate
25+
26+
export function waitForExpect(): typeof waitForExpect

0 commit comments

Comments
 (0)