Reputation: 53531
I have two tests :
import { render, screen, act } from '@testing-library/react'
describe('Test', () => {
it('should CSR', async () => {
await act(async () => {
render(<div data-testid="output">Test</div>);
});
expect(screen.getByTestId('output')).toHaveTextContent('Test');
});
it('should SSR', async () => {
await act(async () => {
render(<div data-testid="output">Test</div>, { hydrate: true });
});
expect(screen.getByTestId('output')).toHaveTextContent('Test');
});
});
The first test, CSR, is fine, no problem whatsoever!
The second test, SSR, spouts a bunch of errors :
console.error
Warning: Expected server HTML to contain a matching <div> in <div>.
at div
console.error
Error: Uncaught [Error: Hydration failed because the initial UI does not match what was rendered on the server.]
at reportException (/path/to/project/node_modules/jsdom/lib/jsdom/living/helpers/runtime-script-errors.js:66:24)
... a LOT more stacktrace
console.error
Warning: An error occurred during hydration. The server HTML was replaced with client content in <div>.
... etc.
Basically, I need to test a component through SSR. How do I do that with the test above?
My jest.config.json
looks like this:
{
"bail": 1,
"verbose": true,
"preset": "ts-jest",
"testEnvironment": "jsdom",
"setupFilesAfterEnv": ["@testing-library/jest-dom", "@testing-library/jest-dom/extend-expect"],
"transform": {
"^.+\\.(ts|tsx)$": "ts-jest"
},
"transformIgnorePatterns": ["<rootDir>/node_modules/"]
}
Upvotes: 1
Views: 3644
Reputation: 102207
When setting the hydrate: true
, RTL will call ReactDOM.hydrate()
method to render the component. See source code v11.2.7/src/pure.js#L59:
act(() => {
if (hydrate) {
ReactDOM.hydrate(wrapUiIfNeeded(ui), container);
} else {
ReactDOM.render(wrapUiIfNeeded(ui), container);
}
});
The warning Expected server HTML to contain a matching XXX
was thrown by the ReactDOM.hydrate()
method, not RTL.
The hydrate(reactNode, domNode)
accepts two parameters:
reactNode
: The “React node” used to render the existing HTML. This will usually be a piece of JSX like which was rendered with a ReactDOM Server method such as renderToString() in React 17.
domNode
: A DOM element that was rendered as the root element on the server.
The div
is the React Node and the container.innerHTML
is the DOM node in your case.
We often use the below code to get the root DOM node and hydrate. The HTML was rendered on the server and sent to the client. The client will download the SPA bundle js file which contains below code:
const app = document.getElementById( "app" );
ReactDOM.hydrate( <App />, app )
The test should be:
import { render, screen, act } from '@testing-library/react';
import ReactDOMServer from 'react-dom/server';
import '@testing-library/jest-dom';
import React from 'react';
describe('Test', () => {
it('should CSR', () => {
render(<div data-testid="output">Test</div>);
expect(screen.getByTestId('output')).toHaveTextContent('Test');
});
it('should SSR', () => {
const ui = <div data-testid="output">Test</div>;
const container = document.createElement('div');
document.body.appendChild(container);
container.innerHTML = ReactDOMServer.renderToString(ui);
// hydrate the React Component and DOM node rendered on the server-side.
render(ui, { hydrate: true, container });
expect(screen.getByTestId('output')).toHaveTextContent('Test');
});
});
Test result:
PASS stackoverflow/75376392/index.test.tsx (9.475 s)
Test
✓ should CSR (19 ms)
✓ should SSR (7 ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 0 total
Time: 10.288 s
jest.config.js
:
module.exports = {
preset: 'ts-jest/presets/js-with-ts',
testEnvironment: 'jsdom'
}
package versions:
"react": "^16.14.0",
"react-dom": "^16.14.0",
"@testing-library/jest-dom": "^5.11.6",
"@testing-library/react": "^11.2.7",
P.S. Check this React test case renders over an existing text child without throwing
See hydrate#caveats
hydrate
expects the rendered content to be identical with the server-rendered content. React can patch up differences in text content, but you should treat mismatches as bugs and fix them.
Upvotes: 1