@@ -35,6 +35,9 @@ describe('InspectedElement', () => {
35
let legacyRender ;
35
let legacyRender ;
36
let testRendererInstance ;
36
let testRendererInstance ;
37
37
38
+ let ErrorBoundary ;
39
+ let errorBoundaryInstance ;
40
+
38
beforeEach ( ( ) => {
41
beforeEach ( ( ) => {
39
utils = require ( './utils' ) ;
42
utils = require ( './utils' ) ;
40
utils . beforeEachProfiling ( ) ;
43
utils . beforeEachProfiling ( ) ;
@@ -69,6 +72,23 @@ describe('InspectedElement', () => {
69
testRendererInstance = TestRenderer . create ( null , {
72
testRendererInstance = TestRenderer . create ( null , {
70
unstable_isConcurrent : true ,
73
unstable_isConcurrent : true ,
71
} ) ;
74
} ) ;
75
+
76
+ errorBoundaryInstance = null ;
77
+
78
+ ErrorBoundary = class extends React . Component {
79
+ state = { error : null } ;
80
+ componentDidCatch ( error ) {
81
+ this . setState ( { error} ) ;
82
+ }
83
+ render ( ) {
84
+ errorBoundaryInstance = this ;
85
+
86
+ if ( this . state . error ) {
87
+ return null ;
88
+ }
89
+ return this . props . children ;
90
+ }
91
+ } ;
72
} ) ;
92
} ) ;
73
93
74
afterEach ( ( ) => {
94
afterEach ( ( ) => {
@@ -109,7 +129,11 @@ describe('InspectedElement', () => {
109
129
110
function noop ( ) { }
130
function noop ( ) { }
111
131
112
- async function inspectElementAtIndex ( index , useCustomHook = noop ) {
132
+ async function inspectElementAtIndex (
133
+ index ,
134
+ useCustomHook = noop ,
135
+ shouldThrow = false ,
136
+ ) {
113
let didFinish = false ;
137
let didFinish = false ;
114
let inspectedElement = null ;
138
let inspectedElement = null ;
115
139
@@ -124,17 +148,21 @@ describe('InspectedElement', () => {
124
148
125
await utils . actAsync ( ( ) => {
149
await utils . actAsync ( ( ) => {
126
testRendererInstance . update (
150
testRendererInstance . update (
151
+ < ErrorBoundary >
127
< Contexts
152
< Contexts
128
defaultSelectedElementID = { id }
153
defaultSelectedElementID = { id }
129
defaultSelectedElementIndex = { index } >
154
defaultSelectedElementIndex = { index } >
130
< React . Suspense fallback = { null } >
155
< React . Suspense fallback = { null } >
131
< Suspender id = { id } index = { index } />
156
< Suspender id = { id } index = { index } />
132
</ React . Suspense >
157
</ React . Suspense >
133
- </ Contexts > ,
158
+ </ Contexts >
159
+ </ ErrorBoundary > ,
134
) ;
160
) ;
135
} , false ) ;
161
} , false ) ;
136
162
163
+ if ( ! shouldThrow ) {
137
expect ( didFinish ) . toBe ( true ) ;
164
expect ( didFinish ) . toBe ( true ) ;
165
+ }
138
166
139
return inspectedElement ;
167
return inspectedElement ;
140
}
168
}
@@ -2069,6 +2097,37 @@ describe('InspectedElement', () => {
2069
expect ( inspectedElement . rootType ) . toMatchInlineSnapshot ( `"createRoot()"` ) ;
2097
expect ( inspectedElement . rootType ) . toMatchInlineSnapshot ( `"createRoot()"` ) ;
2070
} ) ;
2098
} ) ;
2071
2099
2100
+ it ( 'should gracefully surface backend errors on the frontend rather than timing out' , async ( ) => {
2101
+ spyOn ( console , 'error' ) ;
2102
+
2103
+ let shouldThrow = false ;
2104
+
2105
+ const Example = ( ) => {
2106
+ const [ count ] = React . useState ( 0 ) ;
2107
+
2108
+ if ( shouldThrow ) {
2109
+ throw Error ( 'Expected' ) ;
2110
+ } else {
2111
+ return count ;
2112
+ }
2113
+ } ;
2114
+
2115
+ await utils . actAsync ( ( ) => {
2116
+ const container = document . createElement ( 'div' ) ;
2117
+ ReactDOM . createRoot ( container ) . render ( < Example /> ) ;
2118
+ } , false ) ;
2119
+
2120
+ shouldThrow = true ;
2121
+
2122
+ const value = await inspectElementAtIndex ( 0 , noop , true ) ;
2123
+
2124
+ expect ( value ) . toBe ( null ) ;
2125
+
2126
+ const error = errorBoundaryInstance . state . error ;
2127
+ expect ( error . message ) . toBe ( 'Expected' ) ;
2128
+ expect ( error . stack ) . toContain ( 'inspectHooksOfFiber' ) ;
2129
+ } ) ;
2130
+
2072
describe ( '$r' , ( ) => {
2131
describe ( '$r' , ( ) => {
2073
it ( 'should support function components' , async ( ) => {
2132
it ( 'should support function components' , async ( ) => {
2074
const Example = ( ) => {
2133
const Example = ( ) => {
@@ -2656,7 +2715,7 @@ describe('InspectedElement', () => {
2656
2715
2657
describe ( 'error boundary' , ( ) => {
2716
describe ( 'error boundary' , ( ) => {
2658
it ( 'can toggle error' , async ( ) => {
2717
it ( 'can toggle error' , async ( ) => {
2659
- class ErrorBoundary extends React . Component < any > {
2718
+ class LocalErrorBoundary extends React . Component < any > {
2660
state = { hasError : false } ;
2719
state = { hasError : false } ;
2661
static getDerivedStateFromError ( error ) {
2720
static getDerivedStateFromError ( error ) {
2662
return { hasError : true } ;
2721
return { hasError : true } ;
@@ -2666,13 +2725,14 @@ describe('InspectedElement', () => {
2666
return hasError ? 'has-error' : this . props . children ;
2725
return hasError ? 'has-error' : this . props . children ;
2667
}
2726
}
2668
}
2727
}
2728
+
2669
const Example = ( ) = > 'example' ;
2729
const Example = ( ) = > 'example' ;
2670
2730
2671
await utils . actAsync ( ( ) =>
2731
await utils . actAsync ( ( ) =>
2672
legacyRender (
2732
legacyRender (
2673
- < ErrorBoundary >
2733
+ < LocalErrorBoundary >
2674
< Example />
2734
< Example />
2675
- </ ErrorBoundary > ,
2735
+ </ LocalErrorBoundary > ,
2676
document . createElement ( 'div' ) ,
2736
document . createElement ( 'div' ) ,
2677
) ,
2737
) ,
2678
) ;
2738
) ;
0 commit comments