Skip to content

Commit 32daf10

Browse files
committed
move useWithoutAct into disableActEnvironment, reduce api size
1 parent 53d9f45 commit 32daf10

7 files changed

+134
-77
lines changed

src/__tests__/renderToRenderStream.test.tsx

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,9 @@
11
/* eslint-disable @typescript-eslint/no-use-before-define */
22
import {describe, test, expect} from '@jest/globals'
3-
import {
4-
renderToRenderStream,
5-
userEventWithoutAct,
6-
} from '@testing-library/react-render-stream'
7-
import {userEvent as baseUserEvent} from '@testing-library/user-event'
3+
import {renderToRenderStream} from '@testing-library/react-render-stream'
4+
import {userEvent} from '@testing-library/user-event'
85
import * as React from 'react'
96

10-
const userEvent = userEventWithoutAct(baseUserEvent)
11-
127
function CounterForm({
138
value,
149
onIncrement,

src/disableActEnvironment.ts

+113-5
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,39 @@
1+
import {getConfig} from '@testing-library/dom'
2+
13
const dispose: typeof Symbol.dispose =
24
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
35
Symbol.dispose ?? Symbol.for('nodejs.dispose')
46

7+
export interface DisableActEnvironmentOptions {
8+
/**
9+
* If `true`, all modifications of values set by `disableActEnvironment`
10+
* will be prevented until `cleanup` is called.
11+
*
12+
* @default true
13+
*/
14+
preventModification?: boolean
15+
16+
/**
17+
* If `true`, will change the configuration of the testing library to
18+
* prevent auto-wrapping e.g. `userEvent` calls in `act`.
19+
*
20+
* @default true
21+
*/
22+
adjustTestingLibConfig?: boolean
23+
}
24+
525
/**
626
* Helper to temporarily disable a React 18+ act environment.
727
*
28+
* By default, this also adjusts the configuration of @testing-library/dom
29+
* to prevent auto-wrapping of user events in `act`, as well as preventing
30+
* all modifications of values set by this method until `cleanup` is called
31+
* or the returned `Disposable` is disposed of.
32+
*
33+
* Both of these behaviors can be disabled with the option, of the defaults
34+
* can be changed for all calls to this method by modifying
35+
* `disableActEnvironment.defaultOptions`.
36+
*
837
* This returns a disposable and can be used in combination with `using` to
938
* automatically restore the state from before this method call after your test.
1039
*
@@ -35,16 +64,95 @@ const dispose: typeof Symbol.dispose =
3564
* For more context on what `act` is and why you shouldn't use it in renderStream tests,
3665
* https://github.com/reactwg/react-18/discussions/102 is probably the best resource we have.
3766
*/
38-
export function disableActEnvironment(): {cleanup: () => void} & Disposable {
39-
const anyThis = globalThis as any as {IS_REACT_ACT_ENVIRONMENT?: boolean}
40-
const prevActEnv = anyThis.IS_REACT_ACT_ENVIRONMENT
41-
anyThis.IS_REACT_ACT_ENVIRONMENT = false
67+
export function disableActEnvironment({
68+
preventModification = disableActEnvironment.defaultOptions
69+
.preventModification,
70+
adjustTestingLibConfig = disableActEnvironment.defaultOptions
71+
.adjustTestingLibConfig,
72+
}: DisableActEnvironmentOptions = {}): {cleanup: () => void} & Disposable {
73+
const typedGlobal = globalThis as any as {IS_REACT_ACT_ENVIRONMENT?: boolean}
74+
const cleanupFns: Array<() => void> = []
75+
76+
// core functionality
77+
{
78+
const previous = typedGlobal.IS_REACT_ACT_ENVIRONMENT
79+
cleanupFns.push(() => {
80+
Object.defineProperty(typedGlobal, 'IS_REACT_ACT_ENVIRONMENT', {
81+
value: previous,
82+
})
83+
})
84+
Object.defineProperty(
85+
typedGlobal,
86+
'IS_REACT_ACT_ENVIRONMENT',
87+
getNewPropertyDescriptor(false, preventModification),
88+
)
89+
}
90+
91+
if (adjustTestingLibConfig) {
92+
const config = getConfig()
93+
// eslint-disable-next-line @typescript-eslint/unbound-method
94+
const {asyncWrapper, eventWrapper} = config
95+
cleanupFns.push(() => {
96+
Object.defineProperty(config, 'asyncWrapper', {value: asyncWrapper})
97+
Object.defineProperty(config, 'eventWrapper', {value: eventWrapper})
98+
})
99+
100+
Object.defineProperty(
101+
config,
102+
'asyncWrapper',
103+
getNewPropertyDescriptor<typeof asyncWrapper>(
104+
fn => fn(),
105+
preventModification,
106+
),
107+
)
108+
Object.defineProperty(
109+
config,
110+
'eventWrapper',
111+
getNewPropertyDescriptor<typeof eventWrapper>(
112+
fn => fn(),
113+
preventModification,
114+
),
115+
)
116+
}
42117

43118
function cleanup() {
44-
anyThis.IS_REACT_ACT_ENVIRONMENT = prevActEnv
119+
while (cleanupFns.length > 0) {
120+
cleanupFns.pop()!()
121+
}
45122
}
46123
return {
47124
cleanup,
48125
[dispose]: cleanup,
49126
}
50127
}
128+
129+
/**
130+
* Default options for `disableActEnvironment`.
131+
*
132+
* This can be modified to change the default options for all calls to `disableActEnvironment`.
133+
*/
134+
disableActEnvironment.defaultOptions = {
135+
preventModification: true,
136+
adjustTestingLibConfig: true,
137+
} satisfies Required<DisableActEnvironmentOptions> as Required<DisableActEnvironmentOptions>
138+
139+
function getNewPropertyDescriptor<T>(
140+
value: T,
141+
preventModification: boolean,
142+
): PropertyDescriptor {
143+
return preventModification
144+
? {
145+
configurable: true,
146+
enumerable: true,
147+
get() {
148+
return value
149+
},
150+
set() {},
151+
}
152+
: {
153+
configurable: true,
154+
enumerable: true,
155+
writable: true,
156+
value,
157+
}
158+
}

src/pure.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export {
2424
cleanup,
2525
type RenderWithoutActAsync,
2626
} from './renderWithoutAct.js'
27-
export {userEventWithoutAct} from './useWithoutAct.js'
28-
export {disableActEnvironment} from './disableActEnvironment.js'
29-
export {withDisabledActEnvironment} from './withDisabledActEnvironment.js'
27+
export {
28+
disableActEnvironment,
29+
type DisableActEnvironmentOptions,
30+
} from './disableActEnvironment.js'

src/renderStream/__tests__/createRenderStream.test.tsx

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
/* eslint-disable @typescript-eslint/no-use-before-define */
22
import {jest, describe, test, expect} from '@jest/globals'
3-
import {
4-
createRenderStream,
5-
userEventWithoutAct,
6-
} from '@testing-library/react-render-stream'
7-
import {userEvent as baseUserEvent} from '@testing-library/user-event'
3+
import {createRenderStream} from '@testing-library/react-render-stream'
84
import * as React from 'react'
95
import {ErrorBoundary} from 'react-error-boundary'
6+
import {userEvent} from '@testing-library/user-event'
107
import {getExpectErrorMessage} from '../../__testHelpers__/getCleanedErrorMessage.js'
118

12-
const userEvent = userEventWithoutAct(baseUserEvent)
13-
149
function CounterForm({
1510
value,
1611
onIncrement,

src/renderWithoutAct.tsx

+13-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ import {
1111
} from '@testing-library/dom'
1212
import React from 'react'
1313
import {SyncQueries} from './renderStream/syncQueries.js'
14-
import {withDisabledActEnvironment} from './withDisabledActEnvironment.js'
14+
import {
15+
disableActEnvironment,
16+
DisableActEnvironmentOptions,
17+
} from './disableActEnvironment.js'
1518

1619
// Ideally we'd just use a WeakMap where containers are keys and roots are values.
1720
// We use two variables so that we can bail out in constant time when we render with a new container (most common use case)
@@ -209,7 +212,12 @@ export function cleanup() {
209212
// there is a good chance this happens outside of a test, where the user
210213
// has no control over enabling or disabling the React Act environment,
211214
// so we do it for them here.
212-
withDisabledActEnvironment(() => {
215+
216+
const disabledAct = disableActEnvironment({
217+
preventModification: false,
218+
adjustTestingLibConfig: false,
219+
} satisfies /* ensure that all possible options are passed here in case we add more in the future */ Required<DisableActEnvironmentOptions>)
220+
try {
213221
mountedRootEntries.forEach(({root, container}) => {
214222
root.unmount()
215223

@@ -219,5 +227,7 @@ export function cleanup() {
219227
})
220228
mountedRootEntries.length = 0
221229
mountedContainers.clear()
222-
})
230+
} finally {
231+
disabledAct.cleanup()
232+
}
223233
}

src/useWithoutAct.ts

-38
This file was deleted.

src/withDisabledActEnvironment.ts

-14
This file was deleted.

0 commit comments

Comments
 (0)