Description
react-hooks-testing-library
version: v8.0.0-alpha.1 - created a commit starting herereact
version: N/Areact-dom
version (if applicable): N/Areact-test-renderer
version (if applicable): N/Anode
version: v14.17.6npm
(oryarn
) version: v1.22.17
Relevant code or config:
See below
What you did:
I added a test that runs in a Node environment to see if #605 was fixed (sidenote: using this issue/solution in a TypeScript course I'm authoring).
What happened:
I thought the solution would allow renderHook
to be called in SSR environments but we get the same issue.
Summary of all failing tests
FAIL src/server/__tests__/ssr.test.ts
● ssr › should not throw ReferenceError
The error below may be caused by using the wrong test environment, see https://jestjs.io/docs/en/configuration#testenvironment-string.
Consider using the "jsdom" test environment.
ReferenceError: document is not defined
32 | throw new Error('The component can only be hydrated once')
33 | } else {
> 34 | container = document.createElement('div')
| ^
35 | container.innerHTML = serverOutput
36 | act(() => {
37 | ReactDOM.hydrate(testHarness(renderProps), container!)
at hydrate (src/server/pure.ts:34:9)
at Object.<anonymous> (src/server/__tests__/ssr.test.ts:20:5)
Test Suites: 1 failed, 61 passed, 62 total
Tests: 1 failed, 204 passed, 205 total
Snapshots: 0 total
Time: 7.859 s
Ran all test suites.
Note: I added the original author on Discord to try and contact them about this PR. I saw in the comments they said they tested in a Node environment so I may be doing something differently.
Reproduction:
git clone https://github.com/testing-library/react-hooks-testing-library.git
touch src/server/__tests__/ssr.test.ts
- Add this code:
/**
* @jest-environment node
*/
import { useState } from 'react'
import { renderHook, act } from '..'
// This verifies that renderHook can be called in
// a SSR-like environment.
describe('ssr', () => {
function useLoading() {
if (typeof window !== 'undefined') {
const [loading, setLoading] = useState(false)
return { loading, setLoading }
}
}
test('should not throw ReferenceError', () => {
const { result, hydrate } = renderHook(() => useLoading())
hydrate()
act(() => result?.current?.setLoading(true))
expect(result?.current?.loading).toBe(true)
})
})
- Run
yarn test --watch=false
Problem description:
Even though #607 modified src/server/pure.ts
to fill the container with the ReactDOM tree in hydrate()
, this still assumes document
is always available.
Suggested solution:
I see three potential solutions.
Option 1: add DOM to test before hydrate
Based on my understanding, hydrate
is supposed to be called on the client. So in our test, we could create a mock document
and make sure it's available globally before calling hydrate
. I see this more like a bandaid though.
Option 2: check if document
is defined
Add a check to see if document
is available before using it.
hydrate() {
if (container) {
throw new Error('The component can only be hydrated once')
} else {
if (typeof document !== 'undefined') {
container = document.createElement('div')
container.innerHTML = serverOutput
act(() => {
ReactDOM.hydrate(testHarness(renderProps), container!)
})
}
}
},
Option 3: pass in document
This is the approach I usually take, but it would be a breaking change and most people don't like passing in globals this way.
hydrate(_document: Document) {
// ...
}
// Then to call it
hydrate(document)