@@ -18,6 +18,7 @@ let ReactNoop;
18
18
let ReactNoopFlightServer ;
19
19
let ReactNoopFlightServerRuntime ;
20
20
let ReactNoopFlightClient ;
21
+ let ErrorBoundary ;
21
22
22
23
describe ( 'ReactFlight' , ( ) => {
23
24
beforeEach ( ( ) => {
@@ -29,6 +30,27 @@ describe('ReactFlight', () => {
29
30
ReactNoopFlightServerRuntime = require ( 'react-noop-renderer/flight-server-runtime' ) ;
30
31
ReactNoopFlightClient = require ( 'react-noop-renderer/flight-client' ) ;
31
32
act = ReactNoop . act ;
33
+
34
+ ErrorBoundary = class extends React . Component {
35
+ state = { hasError : false , error : null } ;
36
+ static getDerivedStateFromError ( error ) {
37
+ return {
38
+ hasError : true ,
39
+ error,
40
+ } ;
41
+ }
42
+ componentDidMount ( ) {
43
+ expect ( this . state . hasError ) . toBe ( true ) ;
44
+ expect ( this . state . error ) . toBeTruthy ( ) ;
45
+ expect ( this . state . error . message ) . toContain ( this . props . expectedMessage ) ;
46
+ }
47
+ render ( ) {
48
+ if ( this . state . hasError ) {
49
+ return this . state . error . message ;
50
+ }
51
+ return this . props . children ;
52
+ }
53
+ } ;
32
54
} ) ;
33
55
34
56
function block ( render , load ) {
@@ -127,4 +149,103 @@ describe('ReactFlight', () => {
127
149
expect ( ReactNoop ) . toMatchRenderedOutput ( < span > Hello, Seb Smith</ span > ) ;
128
150
} ) ;
129
151
}
152
+
153
+ it ( 'should error if a non-serializable value is passed to a host component' , ( ) => {
154
+ function EventHandlerProp ( ) {
155
+ return (
156
+ < div className = "foo" onClick = { function ( ) { } } >
157
+ Test
158
+ </ div >
159
+ ) ;
160
+ }
161
+ function FunctionProp ( ) {
162
+ return < div > { ( ) => { } } </ div > ;
163
+ }
164
+ function SymbolProp ( ) {
165
+ return < div foo = { Symbol ( 'foo' ) } /> ;
166
+ }
167
+
168
+ const event = ReactNoopFlightServer . render ( < EventHandlerProp /> ) ;
169
+ const fn = ReactNoopFlightServer . render ( < FunctionProp /> ) ;
170
+ const symbol = ReactNoopFlightServer . render ( < SymbolProp /> ) ;
171
+
172
+ function Client ( { transport} ) {
173
+ return ReactNoopFlightClient . read ( transport ) ;
174
+ }
175
+
176
+ act ( ( ) => {
177
+ ReactNoop . render (
178
+ < >
179
+ < ErrorBoundary expectedMessage = "Event handlers cannot be passed to client component props." >
180
+ < Client transport = { event } />
181
+ </ ErrorBoundary >
182
+ < ErrorBoundary expectedMessage = "Functions cannot be passed directly to client components because they're not serializable." >
183
+ < Client transport = { fn } />
184
+ </ ErrorBoundary >
185
+ < ErrorBoundary expectedMessage = "Symbol values (foo) cannot be passed to client components." >
186
+ < Client transport = { symbol } />
187
+ </ ErrorBoundary >
188
+ </ > ,
189
+ ) ;
190
+ } ) ;
191
+ } ) ;
192
+
193
+ it ( 'should warn in DEV if a toJSON instance is passed to a host component' , ( ) => {
194
+ expect ( ( ) => {
195
+ const transport = ReactNoopFlightServer . render (
196
+ < input value = { new Date ( ) } /> ,
197
+ ) ;
198
+ act ( ( ) => {
199
+ ReactNoop . render ( ReactNoopFlightClient . read ( transport ) ) ;
200
+ } ) ;
201
+ } ) . toErrorDev (
202
+ 'Only plain objects can be passed to client components from server components. ' ,
203
+ { withoutStack : true } ,
204
+ ) ;
205
+ } ) ;
206
+
207
+ it ( 'should warn in DEV if a special object is passed to a host component' , ( ) => {
208
+ expect ( ( ) => {
209
+ const transport = ReactNoopFlightServer . render ( < input value = { Math } /> ) ;
210
+ act ( ( ) => {
211
+ ReactNoop . render ( ReactNoopFlightClient . read ( transport ) ) ;
212
+ } ) ;
213
+ } ) . toErrorDev (
214
+ 'Only plain objects can be passed to client components from server components. ' +
215
+ 'Built-ins like Math are not supported.' ,
216
+ { withoutStack : true } ,
217
+ ) ;
218
+ } ) ;
219
+
220
+ it ( 'should warn in DEV if an object with symbols is passed to a host component' , ( ) => {
221
+ expect ( ( ) => {
222
+ const transport = ReactNoopFlightServer . render (
223
+ < input value = { { [ Symbol . iterator ] : { } } } /> ,
224
+ ) ;
225
+ act ( ( ) => {
226
+ ReactNoop . render ( ReactNoopFlightClient . read ( transport ) ) ;
227
+ } ) ;
228
+ } ) . toErrorDev (
229
+ 'Only plain objects can be passed to client components from server components. ' +
230
+ 'Objects with symbol properties like Symbol.iterator are not supported.' ,
231
+ { withoutStack : true } ,
232
+ ) ;
233
+ } ) ;
234
+
235
+ it ( 'should warn in DEV if a class instance is passed to a host component' , ( ) => {
236
+ class Foo {
237
+ method ( ) { }
238
+ }
239
+ expect ( ( ) => {
240
+ const transport = ReactNoopFlightServer . render (
241
+ < input value = { new Foo ( ) } /> ,
242
+ ) ;
243
+ act ( ( ) => {
244
+ ReactNoop . render ( ReactNoopFlightClient . read ( transport ) ) ;
245
+ } ) ;
246
+ } ) . toErrorDev (
247
+ 'Only plain objects can be passed to client components from server components. ' ,
248
+ { withoutStack : true } ,
249
+ ) ;
250
+ } ) ;
130
251
} ) ;
0 commit comments