Skip to content

Commit 6207abf

Browse files
committed
Added .../pure import for skipping auto cleanup
1 parent 43782a8 commit 6207abf

File tree

5 files changed

+128
-115
lines changed

5 files changed

+128
-115
lines changed

docs/api-reference.md

+10-2
Original file line numberDiff line numberDiff line change
@@ -137,5 +137,13 @@ module.exports = {
137137
}
138138
```
139139
140-
Alternatively, setting the `RHTL_SKIP_AUTO_CLEANUP` environment variable to `true` before importing
141-
`@testing-library/react-hooks` will also disable this feature.
140+
Alternatively, you can change your test to import from `@testing-library/react-hooks/pure` instead
141+
of the regular imports.
142+
143+
```diff
144+
- import { renderHook, cleanup, act } from '@testing-library/react-hooks'
145+
+ import { renderHook, cleanup, act } from '@testing-library/react-hooks/pure'
146+
```
147+
148+
If neither of these approaches are suitable, setting the `RHTL_SKIP_AUTO_CLEANUP` environment
149+
variable to `true` before importing `@testing-library/react-hooks` will also disable this feature.

pure.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// makes it so people can import from '@testing-library/react-hooks/pure'
2+
module.exports = require('./lib/pure')

src/cleanup.js

-7
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,4 @@ function removeCleanup(callback) {
1616
cleanupCallbacks = cleanupCallbacks.filter((cb) => cb !== callback)
1717
}
1818

19-
// Automatically registers cleanup in supported testing frameworks
20-
if (typeof afterEach === 'function' && !process.env.RHTL_SKIP_AUTO_CLEANUP) {
21-
afterEach(async () => {
22-
await cleanup()
23-
})
24-
}
25-
2619
export { cleanup, addCleanup, removeCleanup }

src/index.js

+6-106
Original file line numberDiff line numberDiff line change
@@ -1,110 +1,10 @@
1-
import React, { Suspense } from 'react'
2-
import { act, create } from 'react-test-renderer'
3-
import { cleanup, addCleanup, removeCleanup } from './cleanup'
1+
import { cleanup } from './pure'
42

5-
function TestHook({ callback, hookProps, onError, children }) {
6-
try {
7-
children(callback(hookProps))
8-
} catch (err) {
9-
if (err.then) {
10-
throw err
11-
} else {
12-
onError(err)
13-
}
14-
}
15-
return null
16-
}
17-
18-
function Fallback() {
19-
return null
20-
}
21-
22-
function resultContainer() {
23-
let value = null
24-
let error = null
25-
const resolvers = []
26-
27-
const result = {
28-
get current() {
29-
if (error) {
30-
throw error
31-
}
32-
return value
33-
},
34-
get error() {
35-
return error
36-
}
37-
}
38-
39-
const updateResult = (val, err) => {
40-
value = val
41-
error = err
42-
resolvers.splice(0, resolvers.length).forEach((resolve) => resolve())
43-
}
44-
45-
return {
46-
result,
47-
addResolver: (resolver) => {
48-
resolvers.push(resolver)
49-
},
50-
setValue: (val) => updateResult(val),
51-
setError: (err) => updateResult(undefined, err)
52-
}
53-
}
54-
55-
function renderHook(callback, { initialProps, wrapper } = {}) {
56-
const { result, setValue, setError, addResolver } = resultContainer()
57-
const hookProps = { current: initialProps }
58-
59-
const wrapUiIfNeeded = (innerElement) =>
60-
wrapper ? React.createElement(wrapper, null, innerElement) : innerElement
61-
62-
const toRender = () =>
63-
wrapUiIfNeeded(
64-
<Suspense fallback={<Fallback />}>
65-
<TestHook callback={callback} hookProps={hookProps.current} onError={setError}>
66-
{setValue}
67-
</TestHook>
68-
</Suspense>
69-
)
70-
71-
let testRenderer
72-
act(() => {
73-
testRenderer = create(toRender())
3+
// Automatically registers cleanup in supported testing frameworks
4+
if (typeof afterEach === 'function' && !process.env.RHTL_SKIP_AUTO_CLEANUP) {
5+
afterEach(async () => {
6+
await cleanup()
747
})
75-
const { unmount, update } = testRenderer
76-
77-
function unmountHook() {
78-
act(() => {
79-
removeCleanup(unmountHook)
80-
unmount()
81-
})
82-
}
83-
84-
addCleanup(unmountHook)
85-
86-
let waitingForNextUpdate = null
87-
const resolveOnNextUpdate = (resolve) => {
88-
addResolver((...args) => {
89-
waitingForNextUpdate = null
90-
resolve(...args)
91-
})
92-
}
93-
94-
return {
95-
result,
96-
waitForNextUpdate: () => {
97-
waitingForNextUpdate = waitingForNextUpdate || act(() => new Promise(resolveOnNextUpdate))
98-
return waitingForNextUpdate
99-
},
100-
rerender: (newProps = hookProps.current) => {
101-
hookProps.current = newProps
102-
act(() => {
103-
update(toRender())
104-
})
105-
},
106-
unmount: unmountHook
107-
}
1088
}
1099

110-
export { renderHook, cleanup, act }
10+
export * from './pure'

src/pure.js

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import React, { Suspense } from 'react'
2+
import { act, create } from 'react-test-renderer'
3+
import { cleanup, addCleanup, removeCleanup } from './cleanup'
4+
5+
function TestHook({ callback, hookProps, onError, children }) {
6+
try {
7+
children(callback(hookProps))
8+
} catch (err) {
9+
if (err.then) {
10+
throw err
11+
} else {
12+
onError(err)
13+
}
14+
}
15+
return null
16+
}
17+
18+
function Fallback() {
19+
return null
20+
}
21+
22+
function resultContainer() {
23+
let value = null
24+
let error = null
25+
const resolvers = []
26+
27+
const result = {
28+
get current() {
29+
if (error) {
30+
throw error
31+
}
32+
return value
33+
},
34+
get error() {
35+
return error
36+
}
37+
}
38+
39+
const updateResult = (val, err) => {
40+
value = val
41+
error = err
42+
resolvers.splice(0, resolvers.length).forEach((resolve) => resolve())
43+
}
44+
45+
return {
46+
result,
47+
addResolver: (resolver) => {
48+
resolvers.push(resolver)
49+
},
50+
setValue: (val) => updateResult(val),
51+
setError: (err) => updateResult(undefined, err)
52+
}
53+
}
54+
55+
function renderHook(callback, { initialProps, wrapper } = {}) {
56+
const { result, setValue, setError, addResolver } = resultContainer()
57+
const hookProps = { current: initialProps }
58+
59+
const wrapUiIfNeeded = (innerElement) =>
60+
wrapper ? React.createElement(wrapper, null, innerElement) : innerElement
61+
62+
const toRender = () =>
63+
wrapUiIfNeeded(
64+
<Suspense fallback={<Fallback />}>
65+
<TestHook callback={callback} hookProps={hookProps.current} onError={setError}>
66+
{setValue}
67+
</TestHook>
68+
</Suspense>
69+
)
70+
71+
let testRenderer
72+
act(() => {
73+
testRenderer = create(toRender())
74+
})
75+
const { unmount, update } = testRenderer
76+
77+
function unmountHook() {
78+
act(() => {
79+
removeCleanup(unmountHook)
80+
unmount()
81+
})
82+
}
83+
84+
addCleanup(unmountHook)
85+
86+
let waitingForNextUpdate = null
87+
const resolveOnNextUpdate = (resolve) => {
88+
addResolver((...args) => {
89+
waitingForNextUpdate = null
90+
resolve(...args)
91+
})
92+
}
93+
94+
return {
95+
result,
96+
waitForNextUpdate: () => {
97+
waitingForNextUpdate = waitingForNextUpdate || act(() => new Promise(resolveOnNextUpdate))
98+
return waitingForNextUpdate
99+
},
100+
rerender: (newProps = hookProps.current) => {
101+
hookProps.current = newProps
102+
act(() => {
103+
update(toRender())
104+
})
105+
},
106+
unmount: unmountHook
107+
}
108+
}
109+
110+
export { renderHook, cleanup, act }

0 commit comments

Comments
 (0)