Skip to content

Commit c14851b

Browse files
committed
Add workaround for @testing-library missing server rendering utils
See testing-library/react-testing-library#1120 (comment)
1 parent 1c19702 commit c14851b

File tree

4 files changed

+73
-6
lines changed

4 files changed

+73
-6
lines changed

test-utils.tsx

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import type { ReactNode } from 'react';
2+
import { hydrateRoot } from 'react-dom/client';
3+
import { renderToString } from 'react-dom/server';
4+
import { act } from 'react-dom/test-utils';
5+
6+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
7+
export const renderHookServer = <Hook extends () => any>(
8+
useHook: Hook,
9+
{
10+
wrapper: Wrapper,
11+
}: {
12+
wrapper?: ({ children }: { children: ReactNode }) => JSX.Element;
13+
} = {},
14+
): {
15+
result: { current: ReturnType<Hook> | undefined };
16+
hydrate: () => void;
17+
} => {
18+
// Store hook return values
19+
const results: ReturnType<Hook>[] = [];
20+
21+
const result = {
22+
get current() {
23+
return results.slice(-1)[0];
24+
},
25+
};
26+
27+
const setValue = (value: ReturnType<Hook>) => {
28+
results.push(value);
29+
};
30+
31+
const Component = ({ useHook }: { useHook: Hook }) => {
32+
setValue(useHook());
33+
return null;
34+
};
35+
36+
const component = Wrapper ? (
37+
<Wrapper>
38+
<Component useHook={useHook} />
39+
</Wrapper>
40+
) : (
41+
<Component useHook={useHook} />
42+
);
43+
44+
// Render hook on server
45+
const serverOutput = renderToString(component);
46+
47+
// Render hook on client
48+
const hydrate = () => {
49+
const root = document.createElement('div');
50+
root.innerHTML = serverOutput;
51+
act(() => {
52+
hydrateRoot(root, component);
53+
});
54+
};
55+
56+
return {
57+
result,
58+
hydrate: hydrate,
59+
};
60+
};

tsconfig.json

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"declaration": true,
44
"esModuleInterop": true,
55
"isolatedModules": true,
6+
"jsx": "react-jsx",
67
"module": "nodenext",
78
"noEmit": true,
89
"noUncheckedIndexedAccess": true,

vitest.config.node.ts

+1-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
import { defineConfig } from 'vitest/config';
22

33
export default defineConfig({
4-
resolve: {
5-
alias: {
6-
'@testing-library/react': '@testing-library/react/server',
7-
},
8-
},
94
test: {
105
environment: 'node',
11-
setupFiles: 'vitest.setup.ts',
6+
setupFiles: ['vitest.setup.ts', 'vitest.setup.node.ts'],
127
watch: false,
138
},
149
});

vitest.setup.node.ts

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { vi } from 'vitest';
2+
import { renderHookServer } from './test-utils.js';
3+
4+
vi.mock('@testing-library/react', async () => {
5+
const actualTestingLibraryReact = await vi.importActual('@testing-library/react');
6+
7+
return {
8+
...actualTestingLibraryReact,
9+
renderHook: renderHookServer,
10+
};
11+
});

0 commit comments

Comments
 (0)