Skip to content

Commit 262ceba

Browse files
committed
make rerender wait for the render and return Promise<void>
1 parent 94f35e5 commit 262ceba

File tree

4 files changed

+72
-37
lines changed

4 files changed

+72
-37
lines changed

src/renderStream/__tests__/createRenderStream.test.tsx

+14
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,18 @@ Expected: 1
267267
Received: 2
268268
`)
269269
})
270+
271+
test('returned `rerender` returns a promise that resolves', async () => {
272+
function Component() {
273+
return null
274+
}
275+
276+
const {takeRender, render} = createRenderStream()
277+
const {rerender} = await render(<Component />)
278+
await takeRender()
279+
const promise: Promise<void> = rerender(<Component />)
280+
expect(promise).toBeInstanceOf(Promise)
281+
await promise
282+
await takeRender()
283+
})
270284
})

src/renderStream/createRenderStream.tsx

+9-1
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ export function createRenderStream<
251251
ui: React.ReactNode,
252252
options?: RenderOptions<any, any, any>,
253253
) => {
254-
const ret = renderWithoutAct(ui, {
254+
const ret = await renderWithoutAct(ui, {
255255
...options,
256256
wrapper: props => {
257257
const ParentWrapper = options?.wrapper ?? React.Fragment
@@ -265,6 +265,14 @@ export function createRenderStream<
265265
if (stream.renders.length === 0) {
266266
await stream.waitForNextRender()
267267
}
268+
const origRerender = ret.rerender
269+
ret.rerender = async function rerender(rerenderUi: React.ReactNode) {
270+
try {
271+
return await origRerender(rerenderUi)
272+
} finally {
273+
await stream.waitForNextRender()
274+
}
275+
}
268276
return ret
269277
}) as unknown as RenderWithoutActAsync // TODO
270278

src/renderToRenderStream.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
type ValidSnapshot,
1111
} from './renderStream/createRenderStream.js'
1212
import {SyncQueries} from './renderStream/syncQueries.js'
13+
import {AsyncRenderResult} from './renderWithoutAct.js'
1314

1415
type RenderOptions<
1516
Snapshot extends ValidSnapshot = void,
@@ -20,7 +21,7 @@ export interface RenderStreamWithRenderResult<
2021
Snapshot extends ValidSnapshot = void,
2122
Q extends Queries = SyncQueries,
2223
> extends RenderStream<Snapshot, Q> {
23-
utils: BaseResult<Q>
24+
utils: AsyncRenderResult<Q>
2425
}
2526

2627
/**

src/renderWithoutAct.tsx

+47-35
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
import * as ReactDOMClient from 'react-dom/client'
22
import * as ReactDOM from 'react-dom'
3+
import {type RenderOptions} from '@testing-library/react/pure.js'
34
import {
4-
type RenderOptions,
5-
type RenderResult,
6-
} from '@testing-library/react/pure.js'
7-
import {
5+
BoundFunction,
86
getQueriesForElement,
97
prettyDOM,
8+
prettyFormat,
109
type Queries,
1110
} from '@testing-library/dom'
1211
import React from 'react'
@@ -25,6 +24,26 @@ const mountedRootEntries: Array<{
2524
root: ReturnType<typeof createConcurrentRoot>
2625
}> = []
2726

27+
export type AsyncRenderResult<
28+
Q extends Queries = SyncQueries,
29+
Container extends ReactDOMClient.Container = HTMLElement,
30+
BaseElement extends ReactDOMClient.Container = Container,
31+
> = {
32+
container: Container
33+
baseElement: BaseElement
34+
debug: (
35+
baseElement?:
36+
| ReactDOMClient.Container
37+
| Array<ReactDOMClient.Container>
38+
| undefined,
39+
maxLength?: number | undefined,
40+
options?: prettyFormat.OptionsReceived | undefined,
41+
) => void
42+
rerender: (rerenderUi: React.ReactNode) => Promise<void>
43+
unmount: () => void
44+
asFragment: () => DocumentFragment
45+
} & {[P in keyof Q]: BoundFunction<Q[P]>}
46+
2847
function renderRoot(
2948
ui: React.ReactNode,
3049
{
@@ -38,7 +57,7 @@ function renderRoot(
3857
container: ReactDOMClient.Container
3958
root: ReturnType<typeof createConcurrentRoot>
4059
},
41-
): RenderResult<Queries, any, any> {
60+
): AsyncRenderResult<{}, any, any> {
4261
root.render(
4362
WrapperComponent ? React.createElement(WrapperComponent, null, ui) : ui,
4463
)
@@ -57,7 +76,7 @@ function renderRoot(
5776
unmount: () => {
5877
root.unmount()
5978
},
60-
rerender: rerenderUi => {
79+
rerender: async rerenderUi => {
6180
renderRoot(rerenderUi, {
6281
container,
6382
baseElement,
@@ -80,7 +99,7 @@ function renderRoot(
8099
}
81100
},
82101
...getQueriesForElement<Queries>(baseElement as HTMLElement, queries),
83-
} as RenderResult<Queries, any, any> // TODO clean up more
102+
}
84103
}
85104

86105
export type RenderWithoutActAsync = {
@@ -91,54 +110,41 @@ export type RenderWithoutActAsync = {
91110
>(
92111
this: any,
93112
ui: React.ReactNode,
94-
options: //Omit<
95-
RenderOptions<Q, Container, BaseElement>,
96-
//'hydrate' | 'legacyRoot' >,
97-
): Promise<RenderResult<Q, Container, BaseElement>>
113+
options: Pick<
114+
RenderOptions<Q, Container, BaseElement>,
115+
'container' | 'baseElement' | 'queries' | 'wrapper'
116+
>,
117+
): Promise<AsyncRenderResult<Q, Container, BaseElement>>
98118
(
99119
this: any,
100120
ui: React.ReactNode,
101121
options?:
102-
| Omit<RenderOptions, 'hydrate' | 'legacyRoot' | 'queries'>
122+
| Pick<RenderOptions, 'container' | 'baseElement' | 'wrapper'>
103123
| undefined,
104124
): Promise<
105-
RenderResult<
125+
AsyncRenderResult<
106126
SyncQueries,
107127
ReactDOMClient.Container,
108128
ReactDOMClient.Container
109129
>
110130
>
111131
}
112132

113-
export function renderWithoutAct<
114-
Q extends Queries = SyncQueries,
115-
Container extends ReactDOMClient.Container = HTMLElement,
116-
BaseElement extends ReactDOMClient.Container = Container,
117-
>(
118-
ui: React.ReactNode,
119-
options: //Omit<
120-
RenderOptions<Q, Container, BaseElement>,
121-
//'hydrate' | 'legacyRoot' >,
122-
): RenderResult<Q, Container, BaseElement>
123-
export function renderWithoutAct(
124-
ui: React.ReactNode,
125-
options?:
126-
| Omit<RenderOptions, 'hydrate' | 'legacyRoot' | 'queries'>
127-
| undefined,
128-
): RenderResult<Queries, ReactDOMClient.Container, ReactDOMClient.Container>
133+
export const renderWithoutAct =
134+
_renderWithoutAct as unknown as RenderWithoutActAsync
129135

130-
export function renderWithoutAct(
136+
async function _renderWithoutAct(
131137
ui: React.ReactNode,
132138
{
133139
container,
134140
baseElement = container,
135141
queries,
136142
wrapper,
137-
}: Omit<
138-
RenderOptions<Queries, ReactDOMClient.Container, ReactDOMClient.Container>,
139-
'hydrate' | 'legacyRoot'
143+
}: Pick<
144+
RenderOptions<SyncQueries>,
145+
'container' | 'baseElement' | 'wrapper' | 'queries'
140146
> = {},
141-
): RenderResult<any, ReactDOMClient.Container, ReactDOMClient.Container> {
147+
): Promise<AsyncRenderResult<{}>> {
142148
if (!baseElement) {
143149
// default to document.body instead of documentElement to avoid output of potentially-large
144150
// head elements (such as JSS style blocks) in debug output
@@ -172,7 +178,13 @@ export function renderWithoutAct(
172178
})
173179
}
174180

175-
return renderRoot(ui, {baseElement, container, queries, wrapper, root: root!})
181+
return renderRoot(ui, {
182+
baseElement,
183+
container,
184+
queries,
185+
wrapper,
186+
root: root!,
187+
})
176188
}
177189

178190
function createLegacyRoot(container: ReactDOMClient.Container) {

0 commit comments

Comments
 (0)