-
Notifications
You must be signed in to change notification settings - Fork 278
Expand file tree
/
Copy pathrender.tsx
More file actions
119 lines (98 loc) · 3.18 KB
/
render.tsx
File metadata and controls
119 lines (98 loc) · 3.18 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import * as React from 'react';
import {
createRoot,
type HostElement,
type JsonElement,
type Root,
type RootOptions,
} from 'universal-test-renderer';
import { act } from './act';
import { addToCleanupQueue } from './cleanup';
import { getConfig } from './config';
import type { DebugOptions } from './helpers/debug';
import { debug } from './helpers/debug';
import { HOST_TEXT_NAMES } from './helpers/host-component-names';
import { setRenderResult } from './screen';
import { getQueriesForElement } from './within';
export interface RenderOptions {
/**
* Pass a React Component as the wrapper option to have it rendered around the inner element. This is most useful for creating
* reusable custom render functions for common data providers.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
wrapper?: React.ComponentType<any>;
createNodeMock?: (element: React.ReactElement) => object;
}
export type RenderResult = Awaited<ReturnType<typeof render>>;
/**
* Renders test component deeply using React Test Renderer and exposes helpers
* to assert on the output.
*/
export async function render<T>(element: React.ReactElement<T>, options: RenderOptions = {}) {
const { wrapper: Wrapper, createNodeMock } = options || {};
const rendererOptions: RootOptions = {
textComponents: HOST_TEXT_NAMES,
createNodeMock,
};
const wrap = (element: React.ReactElement) => (Wrapper ? <Wrapper>{element}</Wrapper> : element);
const renderer = createRoot(rendererOptions);
await act(() => {
renderer.render(wrap(element));
});
const container = renderer.container;
const rerender = async (component: React.ReactElement) => {
await act(() => {
renderer.render(wrap(component));
});
};
const unmount = async () => {
await act(() => {
renderer.unmount();
});
};
const toJSON = (): JsonElement | null => {
const json = renderer.container.toJSON();
if (json?.children?.length === 0) {
return null;
}
if (json?.children?.length === 1 && typeof json.children[0] !== 'string') {
return json.children[0];
}
return json;
};
addToCleanupQueue(unmount);
const result = {
...getQueriesForElement(renderer.container),
rerender,
update: rerender, // alias for `rerender`
unmount,
toJSON,
debug: makeDebug(renderer),
get container(): HostElement {
return renderer.container;
},
get root(): HostElement | null {
const firstChild = container.children[0];
if (typeof firstChild === 'string') {
throw new Error(
'Invariant Violation: Root element must be a host element. Detected attempt to render a string within the root element.',
);
}
return firstChild;
},
};
setRenderResult(result);
return result;
}
export type DebugFunction = (options?: DebugOptions) => void;
function makeDebug(renderer: Root): DebugFunction {
function debugImpl(options?: DebugOptions) {
const { defaultDebugOptions } = getConfig();
const debugOptions = { ...defaultDebugOptions, ...options };
const json = renderer.container.toJSON();
if (json) {
return debug(json, debugOptions);
}
}
return debugImpl;
}