Skip to content

Commit 1980c56

Browse files
committed
Add tests for error boundaries
1 parent e65667e commit 1980c56

File tree

1 file changed

+137
-0
lines changed

1 file changed

+137
-0
lines changed
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import React, { Component } from 'react'
2+
import renderPrepass from '..'
3+
4+
it('returns to the next componentDidCatch boundary on erroring', () => {
5+
const Throw = jest.fn(() => {
6+
throw new Error()
7+
})
8+
const Inner = jest.fn(() => null)
9+
10+
class Outer extends Component {
11+
constructor() {
12+
super()
13+
this.state = { error: false }
14+
}
15+
16+
componentDidCatch(error) {
17+
this.setState({ error: true })
18+
}
19+
20+
render() {
21+
return this.state.error ? <Inner /> : <Throw />
22+
}
23+
}
24+
25+
const render$ = renderPrepass(<Outer />)
26+
expect(Throw).toHaveBeenCalledTimes(1)
27+
expect(Inner).not.toHaveBeenCalled()
28+
29+
return render$.then(() => {
30+
expect(Inner).toHaveBeenCalledTimes(1)
31+
})
32+
})
33+
34+
it('returns to the next getDerivedStateFromError boundary on erroring', () => {
35+
const Throw = jest.fn(() => {
36+
throw new Error()
37+
})
38+
const Inner = jest.fn(() => null)
39+
40+
class Outer extends Component {
41+
static getDerivedStateFromProps() {
42+
return { error: false }
43+
}
44+
45+
static getDerivedStateFromError() {
46+
return { error: true }
47+
}
48+
49+
render() {
50+
return this.state.error ? <Inner /> : <Throw />
51+
}
52+
}
53+
54+
const render$ = renderPrepass(<Outer />)
55+
expect(Throw).toHaveBeenCalledTimes(1)
56+
expect(Inner).not.toHaveBeenCalled()
57+
58+
return render$.then(() => {
59+
expect(Inner).toHaveBeenCalledTimes(1)
60+
})
61+
})
62+
63+
it('guards against infinite render loops', () => {
64+
const Throw = jest.fn(() => {
65+
throw new Error()
66+
})
67+
68+
class Outer extends Component {
69+
componentDidCatch() {} // NOTE: This doesn't actually recover from errors
70+
render() {
71+
return <Throw />
72+
}
73+
}
74+
75+
return renderPrepass(<Outer />).then(() => {
76+
expect(Throw).toHaveBeenCalledTimes(25)
77+
})
78+
})
79+
80+
it('always returns to the correct error boundary', () => {
81+
const values = []
82+
83+
const Inner = jest.fn(({ value, depth }) => {
84+
values.push({ value, depth })
85+
return value
86+
})
87+
88+
const Throw = jest.fn(({ value }) => {
89+
throw new Error('' + value)
90+
})
91+
92+
class Outer extends Component {
93+
static getDerivedStateFromProps(props) {
94+
return { value: null }
95+
}
96+
97+
static getDerivedStateFromError(error) {
98+
return { value: error.message }
99+
}
100+
101+
render() {
102+
return [
103+
this.state.value ? (
104+
<Inner value={this.state.value} depth={this.props.depth} />
105+
) : (
106+
<Throw value={this.props.depth} />
107+
),
108+
this.props.depth < 4 ? <Outer depth={this.props.depth + 1} /> : null
109+
]
110+
}
111+
}
112+
113+
return renderPrepass(<Outer depth={1} />).then(() => {
114+
expect(Throw).toHaveBeenCalledTimes(4)
115+
expect(Inner).toHaveBeenCalledTimes(4)
116+
expect(values).toMatchInlineSnapshot(`
117+
Array [
118+
Object {
119+
"depth": 1,
120+
"value": "1",
121+
},
122+
Object {
123+
"depth": 2,
124+
"value": "2",
125+
},
126+
Object {
127+
"depth": 3,
128+
"value": "3",
129+
},
130+
Object {
131+
"depth": 4,
132+
"value": "4",
133+
},
134+
]
135+
`)
136+
})
137+
})

0 commit comments

Comments
 (0)