Skip to content

Commit 9fb2dbf

Browse files
committed
Added nextUpdate promise for testing async hooks
1 parent e4853f3 commit 9fb2dbf

File tree

6 files changed

+92
-59
lines changed

6 files changed

+92
-59
lines changed

.babelrc

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"@babel/react"
55
],
66
"plugins": [
7+
"@babel/plugin-transform-runtime",
78
"@babel/proposal-object-rest-spread",
89
["module-resolver", { "alias": { "src": "./src" } }],
910
"@babel/transform-modules-commonjs"

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ const useTheme = (initialTheme) => {
7777
}
7878
return useMemo(() => ({ ...themes[theme], toggleTheme }), [theme])
7979
}
80+
```
8081

82+
```js
8183
// useTheme.test.js
8284
import { renderHook, cleanup, act } from 'react-hooks-testing-library'
8385

@@ -152,6 +154,7 @@ Renders a test component that will call the provided `callback`, including any h
152154

153155
- `result` (`object`)
154156
- `current` (`any`) - the return value of the `callback` function
157+
- `nextUpdate` (`function`) - returns a `Promise` that resolves the next time the hook renders, commonly when state is updated as the result of a asynchronous action.
155158
- `rerender` (`function([newProps])`) - function to rerender the test component including any hooks called in the `callback` function. If `newProps` are passed, the will replace the `initialProps` passed the the `callback` function for future renders.
156159
- `unmount` (`function()`) - function to unmount the test component, commonly used to trigger cleanup effects for `useEffect` hooks.
157160

package-lock.json

+15-55
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2-3
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@
2727
"contributors:add": "all-contributors add"
2828
},
2929
"dependencies": {
30+
"@babel/runtime": "^7.3.4",
3031
"react-testing-library": "^6.0.0"
3132
},
3233
"devDependencies": {
3334
"@babel/cli": "^7.2.3",
3435
"@babel/core": "^7.3.4",
3536
"@babel/plugin-proposal-object-rest-spread": "^7.3.4",
3637
"@babel/plugin-transform-modules-commonjs": "^7.2.0",
38+
"@babel/plugin-transform-runtime": "^7.3.4",
3739
"@babel/preset-env": "^7.3.4",
3840
"@babel/preset-react": "^7.0.0",
3941
"@types/react": "^16.8.5",
@@ -44,16 +46,13 @@
4446
"eslint": "^5.14.1",
4547
"eslint-config-prettier": "^4.0.0",
4648
"eslint-plugin-prettier": "^3.0.1",
47-
"fetch-mock": "^7.3.1",
4849
"husky": "^1.3.1",
4950
"jest": "^24.1.0",
5051
"lint-staged": "^8.1.4",
51-
"node-fetch": "^2.3.0",
5252
"prettier": "^1.16.4",
5353
"prettier-eslint": "^8.8.2",
5454
"prettier-eslint-cli": "^4.7.1",
5555
"react": "^16.8.3",
56-
"react-async": "^5.1.0",
5756
"react-dom": "^16.8.3",
5857
"typescript": "^3.3.3333",
5958
"typings-tester": "^0.3.2"

src/index.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,22 @@ function TestHook({ callback, hookProps, children }) {
77
}
88

99
function renderHook(callback, { initialProps, ...options } = {}) {
10-
const result = { current: null }
10+
const result = {
11+
current: null
12+
}
1113
const hookProps = { current: initialProps }
1214

15+
const resolvers = []
16+
const nextUpdate = () =>
17+
new Promise((resolve) => {
18+
resolvers.push(resolve)
19+
})
20+
1321
const toRender = () => (
1422
<TestHook callback={callback} hookProps={hookProps.current}>
1523
{(res) => {
1624
result.current = res
25+
resolvers.splice(0, resolvers.length).forEach((resolve) => resolve())
1726
}}
1827
</TestHook>
1928
)
@@ -22,6 +31,7 @@ function renderHook(callback, { initialProps, ...options } = {}) {
2231

2332
return {
2433
result,
34+
nextUpdate,
2535
unmount,
2636
rerender: (newProps = hookProps.current) => {
2737
hookProps.current = newProps

test/asyncHook.test.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { useState, useEffect } from 'react'
2+
import { renderHook, cleanup } from 'src'
3+
4+
describe('async hook tests', () => {
5+
const getSomeName = () => Promise.resolve('Betty')
6+
7+
const useName = (prefix) => {
8+
const [name, setName] = useState('nobody')
9+
10+
useEffect(() => {
11+
getSomeName().then((theName) => {
12+
setName(prefix ? `${prefix} ${theName}` : theName)
13+
})
14+
}, [prefix])
15+
16+
return name
17+
}
18+
19+
afterEach(cleanup)
20+
21+
test('should wait for next update', async () => {
22+
const { result, nextUpdate } = renderHook(() => useName())
23+
24+
expect(result.current).toBe('nobody')
25+
26+
await nextUpdate()
27+
28+
expect(result.current).toBe('Betty')
29+
})
30+
31+
test('should wait for multiple updates', async () => {
32+
const { result, nextUpdate, rerender } = renderHook(({ prefix }) => useName(prefix), {
33+
initialProps: { prefix: 'Mrs.' }
34+
})
35+
36+
expect(result.current).toBe('nobody')
37+
38+
await nextUpdate()
39+
40+
expect(result.current).toBe('Mrs. Betty')
41+
42+
rerender({ prefix: 'Ms.' })
43+
44+
await nextUpdate()
45+
46+
expect(result.current).toBe('Ms. Betty')
47+
})
48+
49+
test('should resolve all when updating', async () => {
50+
const { result, nextUpdate } = renderHook(({ prefix }) => useName(prefix), {
51+
initialProps: { prefix: 'Mrs.' }
52+
})
53+
54+
expect(result.current).toBe('nobody')
55+
56+
await Promise.all([nextUpdate(), nextUpdate(), nextUpdate()])
57+
58+
expect(result.current).toBe('Mrs. Betty')
59+
})
60+
})

0 commit comments

Comments
 (0)