Skip to content

Commit a543597

Browse files
committed
test: add tests for checking strict mode works and can be combine with wrapper
1 parent f8cb04b commit a543597

File tree

3 files changed

+283
-164
lines changed

3 files changed

+283
-164
lines changed

src/__tests__/__snapshots__/render.js.snap

Lines changed: 1 addition & 1 deletion
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__/render.js

Lines changed: 192 additions & 140 deletions
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
})

0 commit comments

Comments
 (0)