Skip to content

Commit 0880eba

Browse files
yinmeps1lon
andauthoredJan 30, 2024
feat: add reactStrictMode option to enable strict mode render (#1241)
* feat: add `reactStrictMode` option and override `getConfig` and `configure` functions from DTL * feat: update types for overridden `getConfig` and `configure` functions * test: add tests for checking configure APIs support RTL option and do not degrade * refactor: use a wrapper option for simplicity * refactor: use same function for wrapping UI if needed * feat: enable strict mode render if `reactStrictMode` option is true * test: add tests for checking strict mode works and can be combine with wrapper --------- Co-authored-by: Sebastian Silbermann <silbermann.sebastian@gmail.com>
1 parent 03a301f commit 0880eba

File tree

8 files changed

+443
-174
lines changed

8 files changed

+443
-174
lines changed
 

‎src/__tests__/__snapshots__/render.js.snap

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`supports fragments 1`] = `
3+
exports[`render API supports fragments 1`] = `
44
<DocumentFragment>
55
<div>
66
<code>

‎src/__tests__/config.js

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import {configure, getConfig} from '../'
2+
3+
describe('configuration API', () => {
4+
let originalConfig
5+
beforeEach(() => {
6+
// Grab the existing configuration so we can restore
7+
// it at the end of the test
8+
configure(existingConfig => {
9+
originalConfig = existingConfig
10+
// Don't change the existing config
11+
return {}
12+
})
13+
})
14+
15+
afterEach(() => {
16+
configure(originalConfig)
17+
})
18+
19+
describe('DTL options', () => {
20+
test('configure can set by a plain JS object', () => {
21+
const testIdAttribute = 'not-data-testid'
22+
configure({testIdAttribute})
23+
24+
expect(getConfig().testIdAttribute).toBe(testIdAttribute)
25+
})
26+
27+
test('configure can set by a function', () => {
28+
// setup base option
29+
const baseTestIdAttribute = 'data-testid'
30+
configure({testIdAttribute: baseTestIdAttribute})
31+
32+
const modifiedPrefix = 'modified-'
33+
configure(existingConfig => ({
34+
testIdAttribute: `${modifiedPrefix}${existingConfig.testIdAttribute}`,
35+
}))
36+
37+
expect(getConfig().testIdAttribute).toBe(
38+
`${modifiedPrefix}${baseTestIdAttribute}`,
39+
)
40+
})
41+
})
42+
43+
describe('RTL options', () => {
44+
test('configure can set by a plain JS object', () => {
45+
configure({reactStrictMode: true})
46+
47+
expect(getConfig().reactStrictMode).toBe(true)
48+
})
49+
50+
test('configure can set by a function', () => {
51+
configure(existingConfig => ({
52+
reactStrictMode: !existingConfig.reactStrictMode,
53+
}))
54+
55+
expect(getConfig().reactStrictMode).toBe(true)
56+
})
57+
})
58+
59+
test('configure can set DTL and RTL options at once', () => {
60+
const testIdAttribute = 'not-data-testid'
61+
configure({testIdAttribute, reactStrictMode: true})
62+
63+
expect(getConfig().testIdAttribute).toBe(testIdAttribute)
64+
expect(getConfig().reactStrictMode).toBe(true)
65+
})
66+
})

‎src/__tests__/render.js

+192-140
Original file line numberDiff line numberDiff line change
@@ -1,84 +1,100 @@
11
import * as React from 'react'
22
import ReactDOM from 'react-dom'
33
import ReactDOMServer from 'react-dom/server'
4-
import {fireEvent, render, screen} from '../'
4+
import {fireEvent, render, screen, configure} from '../'
5+
6+
describe('render API', () => {
7+
let originalConfig
8+
beforeEach(() => {
9+
// Grab the existing configuration so we can restore
10+
// it at the end of the test
11+
configure(existingConfig => {
12+
originalConfig = existingConfig
13+
// Don't change the existing config
14+
return {}
15+
})
16+
})
517

6-
test('renders div into document', () => {
7-
const ref = React.createRef()
8-
const {container} = render(<div ref={ref} />)
9-
expect(container.firstChild).toBe(ref.current)
10-
})
18+
afterEach(() => {
19+
configure(originalConfig)
20+
})
1121

12-
test('works great with react portals', () => {
13-
class MyPortal extends React.Component {
14-
constructor(...args) {
15-
super(...args)
16-
this.portalNode = document.createElement('div')
17-
this.portalNode.dataset.testid = 'my-portal'
18-
}
19-
componentDidMount() {
20-
document.body.appendChild(this.portalNode)
21-
}
22-
componentWillUnmount() {
23-
this.portalNode.parentNode.removeChild(this.portalNode)
24-
}
25-
render() {
26-
return ReactDOM.createPortal(
27-
<Greet greeting="Hello" subject="World" />,
28-
this.portalNode,
29-
)
30-
}
31-
}
32-
33-
function Greet({greeting, subject}) {
34-
return (
35-
<div>
36-
<strong>
37-
{greeting} {subject}
38-
</strong>
39-
</div>
40-
)
41-
}
42-
43-
const {unmount} = render(<MyPortal />)
44-
expect(screen.getByText('Hello World')).toBeInTheDocument()
45-
const portalNode = screen.getByTestId('my-portal')
46-
expect(portalNode).toBeInTheDocument()
47-
unmount()
48-
expect(portalNode).not.toBeInTheDocument()
49-
})
22+
test('renders div into document', () => {
23+
const ref = React.createRef()
24+
const {container} = render(<div ref={ref} />)
25+
expect(container.firstChild).toBe(ref.current)
26+
})
5027

51-
test('returns baseElement which defaults to document.body', () => {
52-
const {baseElement} = render(<div />)
53-
expect(baseElement).toBe(document.body)
54-
})
28+
test('works great with react portals', () => {
29+
class MyPortal extends React.Component {
30+
constructor(...args) {
31+
super(...args)
32+
this.portalNode = document.createElement('div')
33+
this.portalNode.dataset.testid = 'my-portal'
34+
}
35+
componentDidMount() {
36+
document.body.appendChild(this.portalNode)
37+
}
38+
componentWillUnmount() {
39+
this.portalNode.parentNode.removeChild(this.portalNode)
40+
}
41+
render() {
42+
return ReactDOM.createPortal(
43+
<Greet greeting="Hello" subject="World" />,
44+
this.portalNode,
45+
)
46+
}
47+
}
5548

56-
test('supports fragments', () => {
57-
class Test extends React.Component {
58-
render() {
49+
function Greet({greeting, subject}) {
5950
return (
6051
<div>
61-
<code>DocumentFragment</code> is pretty cool!
52+
<strong>
53+
{greeting} {subject}
54+
</strong>
6255
</div>
6356
)
6457
}
65-
}
6658

67-
const {asFragment} = render(<Test />)
68-
expect(asFragment()).toMatchSnapshot()
69-
})
59+
const {unmount} = render(<MyPortal />)
60+
expect(screen.getByText('Hello World')).toBeInTheDocument()
61+
const portalNode = screen.getByTestId('my-portal')
62+
expect(portalNode).toBeInTheDocument()
63+
unmount()
64+
expect(portalNode).not.toBeInTheDocument()
65+
})
7066

71-
test('renders options.wrapper around node', () => {
72-
const WrapperComponent = ({children}) => (
73-
<div data-testid="wrapper">{children}</div>
74-
)
67+
test('returns baseElement which defaults to document.body', () => {
68+
const {baseElement} = render(<div />)
69+
expect(baseElement).toBe(document.body)
70+
})
71+
72+
test('supports fragments', () => {
73+
class Test extends React.Component {
74+
render() {
75+
return (
76+
<div>
77+
<code>DocumentFragment</code> is pretty cool!
78+
</div>
79+
)
80+
}
81+
}
7582

76-
const {container} = render(<div data-testid="inner" />, {
77-
wrapper: WrapperComponent,
83+
const {asFragment} = render(<Test />)
84+
expect(asFragment()).toMatchSnapshot()
7885
})
7986

80-
expect(screen.getByTestId('wrapper')).toBeInTheDocument()
81-
expect(container.firstChild).toMatchInlineSnapshot(`
87+
test('renders options.wrapper around node', () => {
88+
const WrapperComponent = ({children}) => (
89+
<div data-testid="wrapper">{children}</div>
90+
)
91+
92+
const {container} = render(<div data-testid="inner" />, {
93+
wrapper: WrapperComponent,
94+
})
95+
96+
expect(screen.getByTestId('wrapper')).toBeInTheDocument()
97+
expect(container.firstChild).toMatchInlineSnapshot(`
8298
<div
8399
data-testid=wrapper
84100
>
@@ -87,102 +103,138 @@ test('renders options.wrapper around node', () => {
87103
/>
88104
</div>
89105
`)
90-
})
106+
})
91107

92-
test('flushes useEffect cleanup functions sync on unmount()', () => {
93-
const spy = jest.fn()
94-
function Component() {
95-
React.useEffect(() => spy, [])
96-
return null
97-
}
98-
const {unmount} = render(<Component />)
99-
expect(spy).toHaveBeenCalledTimes(0)
108+
test('renders options.wrapper around node when reactStrictMode is true', () => {
109+
configure({reactStrictMode: true})
100110

101-
unmount()
111+
const WrapperComponent = ({children}) => (
112+
<div data-testid="wrapper">{children}</div>
113+
)
114+
const {container} = render(<div data-testid="inner" />, {
115+
wrapper: WrapperComponent,
116+
})
102117

103-
expect(spy).toHaveBeenCalledTimes(1)
104-
})
118+
expect(screen.getByTestId('wrapper')).toBeInTheDocument()
119+
expect(container.firstChild).toMatchInlineSnapshot(`
120+
<div
121+
data-testid=wrapper
122+
>
123+
<div
124+
data-testid=inner
125+
/>
126+
</div>
127+
`)
128+
})
129+
130+
test('renders twice when reactStrictMode is true', () => {
131+
configure({reactStrictMode: true})
105132

106-
test('can be called multiple times on the same container', () => {
107-
const container = document.createElement('div')
133+
const spy = jest.fn()
134+
function Component() {
135+
spy()
136+
return null
137+
}
108138

109-
const {unmount} = render(<strong />, {container})
139+
render(<Component />)
140+
expect(spy).toHaveBeenCalledTimes(2)
141+
})
110142

111-
expect(container).toContainHTML('<strong></strong>')
143+
test('flushes useEffect cleanup functions sync on unmount()', () => {
144+
const spy = jest.fn()
145+
function Component() {
146+
React.useEffect(() => spy, [])
147+
return null
148+
}
149+
const {unmount} = render(<Component />)
150+
expect(spy).toHaveBeenCalledTimes(0)
112151

113-
render(<em />, {container})
152+
unmount()
114153

115-
expect(container).toContainHTML('<em></em>')
154+
expect(spy).toHaveBeenCalledTimes(1)
155+
})
116156

117-
unmount()
157+
test('can be called multiple times on the same container', () => {
158+
const container = document.createElement('div')
118159

119-
expect(container).toBeEmptyDOMElement()
120-
})
160+
const {unmount} = render(<strong />, {container})
121161

122-
test('hydrate will make the UI interactive', () => {
123-
function App() {
124-
const [clicked, handleClick] = React.useReducer(n => n + 1, 0)
162+
expect(container).toContainHTML('<strong></strong>')
125163

126-
return (
127-
<button type="button" onClick={handleClick}>
128-
clicked:{clicked}
129-
</button>
130-
)
131-
}
132-
const ui = <App />
133-
const container = document.createElement('div')
134-
document.body.appendChild(container)
135-
container.innerHTML = ReactDOMServer.renderToString(ui)
164+
render(<em />, {container})
136165

137-
expect(container).toHaveTextContent('clicked:0')
166+
expect(container).toContainHTML('<em></em>')
138167

139-
render(ui, {container, hydrate: true})
168+
unmount()
140169

141-
fireEvent.click(container.querySelector('button'))
170+
expect(container).toBeEmptyDOMElement()
171+
})
142172

143-
expect(container).toHaveTextContent('clicked:1')
144-
})
173+
test('hydrate will make the UI interactive', () => {
174+
function App() {
175+
const [clicked, handleClick] = React.useReducer(n => n + 1, 0)
145176

146-
test('hydrate can have a wrapper', () => {
147-
const wrapperComponentMountEffect = jest.fn()
148-
function WrapperComponent({children}) {
149-
React.useEffect(() => {
150-
wrapperComponentMountEffect()
151-
})
177+
return (
178+
<button type="button" onClick={handleClick}>
179+
clicked:{clicked}
180+
</button>
181+
)
182+
}
183+
const ui = <App />
184+
const container = document.createElement('div')
185+
document.body.appendChild(container)
186+
container.innerHTML = ReactDOMServer.renderToString(ui)
152187

153-
return children
154-
}
155-
const ui = <div />
156-
const container = document.createElement('div')
157-
document.body.appendChild(container)
158-
container.innerHTML = ReactDOMServer.renderToString(ui)
188+
expect(container).toHaveTextContent('clicked:0')
159189

160-
render(ui, {container, hydrate: true, wrapper: WrapperComponent})
190+
render(ui, {container, hydrate: true})
161191

162-
expect(wrapperComponentMountEffect).toHaveBeenCalledTimes(1)
163-
})
192+
fireEvent.click(container.querySelector('button'))
164193

165-
test('legacyRoot uses legacy ReactDOM.render', () => {
166-
expect(() => {
167-
render(<div />, {legacyRoot: true})
168-
}).toErrorDev(
169-
[
170-
"Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot",
171-
],
172-
{withoutStack: true},
173-
)
174-
})
194+
expect(container).toHaveTextContent('clicked:1')
195+
})
196+
197+
test('hydrate can have a wrapper', () => {
198+
const wrapperComponentMountEffect = jest.fn()
199+
function WrapperComponent({children}) {
200+
React.useEffect(() => {
201+
wrapperComponentMountEffect()
202+
})
203+
204+
return children
205+
}
206+
const ui = <div />
207+
const container = document.createElement('div')
208+
document.body.appendChild(container)
209+
container.innerHTML = ReactDOMServer.renderToString(ui)
175210

176-
test('legacyRoot uses legacy ReactDOM.hydrate', () => {
177-
const ui = <div />
178-
const container = document.createElement('div')
179-
container.innerHTML = ReactDOMServer.renderToString(ui)
180-
expect(() => {
181-
render(ui, {container, hydrate: true, legacyRoot: true})
182-
}).toErrorDev(
183-
[
184-
"Warning: ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot",
185-
],
186-
{withoutStack: true},
187-
)
211+
render(ui, {container, hydrate: true, wrapper: WrapperComponent})
212+
213+
expect(wrapperComponentMountEffect).toHaveBeenCalledTimes(1)
214+
})
215+
216+
test('legacyRoot uses legacy ReactDOM.render', () => {
217+
expect(() => {
218+
render(<div />, {legacyRoot: true})
219+
}).toErrorDev(
220+
[
221+
"Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot",
222+
],
223+
{withoutStack: true},
224+
)
225+
})
226+
227+
test('legacyRoot uses legacy ReactDOM.hydrate', () => {
228+
const ui = <div />
229+
const container = document.createElement('div')
230+
container.innerHTML = ReactDOMServer.renderToString(ui)
231+
expect(() => {
232+
render(ui, {container, hydrate: true, legacyRoot: true})
233+
}).toErrorDev(
234+
[
235+
"Warning: ReactDOM.hydrate is no longer supported in React 18. Use hydrateRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot",
236+
],
237+
{withoutStack: true},
238+
)
239+
})
188240
})

‎src/__tests__/rerender.js

+90-23
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,98 @@
11
import * as React from 'react'
2-
import {render} from '../'
3-
4-
test('rerender will re-render the element', () => {
5-
const Greeting = props => <div>{props.message}</div>
6-
const {container, rerender} = render(<Greeting message="hi" />)
7-
expect(container.firstChild).toHaveTextContent('hi')
8-
rerender(<Greeting message="hey" />)
9-
expect(container.firstChild).toHaveTextContent('hey')
10-
})
2+
import {render, configure} from '../'
3+
4+
describe('rerender API', () => {
5+
let originalConfig
6+
beforeEach(() => {
7+
// Grab the existing configuration so we can restore
8+
// it at the end of the test
9+
configure(existingConfig => {
10+
originalConfig = existingConfig
11+
// Don't change the existing config
12+
return {}
13+
})
14+
})
15+
16+
afterEach(() => {
17+
configure(originalConfig)
18+
})
19+
20+
test('rerender will re-render the element', () => {
21+
const Greeting = props => <div>{props.message}</div>
22+
const {container, rerender} = render(<Greeting message="hi" />)
23+
expect(container.firstChild).toHaveTextContent('hi')
24+
rerender(<Greeting message="hey" />)
25+
expect(container.firstChild).toHaveTextContent('hey')
26+
})
27+
28+
test('hydrate will not update props until next render', () => {
29+
const initialInputElement = document.createElement('input')
30+
const container = document.createElement('div')
31+
container.appendChild(initialInputElement)
32+
document.body.appendChild(container)
33+
34+
const firstValue = 'hello'
35+
initialInputElement.value = firstValue
1136

12-
test('hydrate will not update props until next render', () => {
13-
const initialInputElement = document.createElement('input')
14-
const container = document.createElement('div')
15-
container.appendChild(initialInputElement)
16-
document.body.appendChild(container)
37+
const {rerender} = render(<input value="" onChange={() => null} />, {
38+
container,
39+
hydrate: true,
40+
})
1741

18-
const firstValue = 'hello'
19-
initialInputElement.value = firstValue
42+
expect(initialInputElement).toHaveValue(firstValue)
2043

21-
const {rerender} = render(<input value="" onChange={() => null} />, {
22-
container,
23-
hydrate: true,
44+
const secondValue = 'goodbye'
45+
rerender(<input value={secondValue} onChange={() => null} />)
46+
expect(initialInputElement).toHaveValue(secondValue)
2447
})
2548

26-
expect(initialInputElement).toHaveValue(firstValue)
49+
test('re-renders options.wrapper around node when reactStrictMode is true', () => {
50+
configure({reactStrictMode: true})
2751

28-
const secondValue = 'goodbye'
29-
rerender(<input value={secondValue} onChange={() => null} />)
30-
expect(initialInputElement).toHaveValue(secondValue)
52+
const WrapperComponent = ({children}) => (
53+
<div data-testid="wrapper">{children}</div>
54+
)
55+
const Greeting = props => <div>{props.message}</div>
56+
const {container, rerender} = render(<Greeting message="hi" />, {
57+
wrapper: WrapperComponent,
58+
})
59+
60+
expect(container.firstChild).toMatchInlineSnapshot(`
61+
<div
62+
data-testid=wrapper
63+
>
64+
<div>
65+
hi
66+
</div>
67+
</div>
68+
`)
69+
70+
rerender(<Greeting message="hey" />)
71+
expect(container.firstChild).toMatchInlineSnapshot(`
72+
<div
73+
data-testid=wrapper
74+
>
75+
<div>
76+
hey
77+
</div>
78+
</div>
79+
`)
80+
})
81+
82+
test('re-renders twice when reactStrictMode is true', () => {
83+
configure({reactStrictMode: true})
84+
85+
const spy = jest.fn()
86+
function Component() {
87+
spy()
88+
return null
89+
}
90+
91+
const {rerender} = render(<Component />)
92+
expect(spy).toHaveBeenCalledTimes(2)
93+
94+
spy.mockClear()
95+
rerender(<Component />)
96+
expect(spy).toHaveBeenCalledTimes(2)
97+
})
3198
})

‎src/config.js

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import {
2+
getConfig as getConfigDTL,
3+
configure as configureDTL,
4+
} from '@testing-library/dom'
5+
6+
let configForRTL = {
7+
reactStrictMode: false,
8+
}
9+
10+
function getConfig() {
11+
return {
12+
...getConfigDTL(),
13+
...configForRTL,
14+
}
15+
}
16+
17+
function configure(newConfig) {
18+
if (typeof newConfig === 'function') {
19+
// Pass the existing config out to the provided function
20+
// and accept a delta in return
21+
newConfig = newConfig(getConfig())
22+
}
23+
24+
const {reactStrictMode, ...configForDTL} = newConfig
25+
26+
configureDTL(configForDTL)
27+
28+
configForRTL = {
29+
...configForRTL,
30+
reactStrictMode,
31+
}
32+
}
33+
34+
export {getConfig, configure}

‎src/pure.js

+25-10
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import act, {
1111
setReactActEnvironment,
1212
} from './act-compat'
1313
import {fireEvent} from './fire-event'
14+
import {getConfig, configure} from './config'
1415

1516
function jestFakeTimersAreEnabled() {
1617
/* istanbul ignore else */
@@ -76,6 +77,18 @@ const mountedContainers = new Set()
7677
*/
7778
const mountedRootEntries = []
7879

80+
function strictModeIfNeeded(innerElement) {
81+
return getConfig().reactStrictMode
82+
? React.createElement(React.StrictMode, null, innerElement)
83+
: innerElement
84+
}
85+
86+
function wrapUiIfNeeded(innerElement, wrapperComponent) {
87+
return wrapperComponent
88+
? React.createElement(wrapperComponent, null, innerElement)
89+
: innerElement
90+
}
91+
7992
function createConcurrentRoot(
8093
container,
8194
{hydrate, ui, wrapper: WrapperComponent},
@@ -85,7 +98,7 @@ function createConcurrentRoot(
8598
act(() => {
8699
root = ReactDOMClient.hydrateRoot(
87100
container,
88-
WrapperComponent ? React.createElement(WrapperComponent, null, ui) : ui,
101+
strictModeIfNeeded(wrapUiIfNeeded(ui, WrapperComponent)),
89102
)
90103
})
91104
} else {
@@ -129,16 +142,17 @@ function renderRoot(
129142
ui,
130143
{baseElement, container, hydrate, queries, root, wrapper: WrapperComponent},
131144
) {
132-
const wrapUiIfNeeded = innerElement =>
133-
WrapperComponent
134-
? React.createElement(WrapperComponent, null, innerElement)
135-
: innerElement
136-
137145
act(() => {
138146
if (hydrate) {
139-
root.hydrate(wrapUiIfNeeded(ui), container)
147+
root.hydrate(
148+
strictModeIfNeeded(wrapUiIfNeeded(ui, WrapperComponent)),
149+
container,
150+
)
140151
} else {
141-
root.render(wrapUiIfNeeded(ui), container)
152+
root.render(
153+
strictModeIfNeeded(wrapUiIfNeeded(ui, WrapperComponent)),
154+
container,
155+
)
142156
}
143157
})
144158

@@ -157,10 +171,11 @@ function renderRoot(
157171
})
158172
},
159173
rerender: rerenderUi => {
160-
renderRoot(wrapUiIfNeeded(rerenderUi), {
174+
renderRoot(rerenderUi, {
161175
container,
162176
baseElement,
163177
root,
178+
wrapper: WrapperComponent,
164179
})
165180
// Intentionally do not return anything to avoid unnecessarily complicating the API.
166181
// folks can use all the same utilities we return in the first place that are bound to the container
@@ -276,6 +291,6 @@ function renderHook(renderCallback, options = {}) {
276291

277292
// just re-export everything from dom-testing-library
278293
export * from '@testing-library/dom'
279-
export {render, renderHook, cleanup, act, fireEvent}
294+
export {render, renderHook, cleanup, act, fireEvent, getConfig, configure}
280295

281296
/* eslint func-name-matching:0 */

‎types/index.d.ts

+13
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,25 @@ import {
55
Queries,
66
BoundFunction,
77
prettyFormat,
8+
Config as ConfigDTL,
89
} from '@testing-library/dom'
910
import {Renderer} from 'react-dom'
1011
import {act as reactAct} from 'react-dom/test-utils'
1112

1213
export * from '@testing-library/dom'
1314

15+
export interface Config extends ConfigDTL {
16+
reactStrictMode: boolean
17+
}
18+
19+
export interface ConfigFn {
20+
(existingConfig: Config): Partial<Config>
21+
}
22+
23+
export function configure(configDelta: ConfigFn | Partial<Config>): void
24+
25+
export function getConfig(): Config
26+
1427
export type RenderResult<
1528
Q extends Queries = typeof queries,
1629
Container extends Element | DocumentFragment = HTMLElement,

‎types/test.tsx

+22
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,28 @@ export function testFireEvent() {
6262
fireEvent.click(container)
6363
}
6464

65+
export function testConfigure() {
66+
// test for DTL's config
67+
pure.configure({testIdAttribute: 'foobar'})
68+
pure.configure(existingConfig => ({
69+
testIdAttribute: `modified-${existingConfig.testIdAttribute}`,
70+
}))
71+
72+
// test for RTL's config
73+
pure.configure({reactStrictMode: true})
74+
pure.configure(existingConfig => ({
75+
reactStrictMode: !existingConfig.reactStrictMode,
76+
}))
77+
}
78+
79+
export function testGetConfig() {
80+
// test for DTL's config
81+
pure.getConfig().testIdAttribute
82+
83+
// test for RTL's config
84+
pure.getConfig().reactStrictMode
85+
}
86+
6587
export function testDebug() {
6688
const {debug, getAllByTestId} = render(
6789
<>

0 commit comments

Comments
 (0)
Please sign in to comment.