@@ -18,6 +18,7 @@ let ReactNoop;
1818let ReactNoopFlightServer ;
1919let ReactNoopFlightServerRuntime ;
2020let ReactNoopFlightClient ;
21+ let ErrorBoundary ;
2122
2223describe ( 'ReactFlight' , ( ) => {
2324 beforeEach ( ( ) => {
@@ -29,6 +30,27 @@ describe('ReactFlight', () => {
2930 ReactNoopFlightServerRuntime = require ( 'react-noop-renderer/flight-server-runtime' ) ;
3031 ReactNoopFlightClient = require ( 'react-noop-renderer/flight-client' ) ;
3132 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+ } ;
3254 } ) ;
3355
3456 function block ( render , load ) {
@@ -127,4 +149,103 @@ describe('ReactFlight', () => {
127149 expect ( ReactNoop ) . toMatchRenderedOutput ( < span > Hello, Seb Smith</ span > ) ;
128150 } ) ;
129151 }
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+ } ) ;
130251} ) ;
0 commit comments