@@ -210,3 +210,358 @@ test('logbox: can recover from a component error', async () => {
210
210
211
211
await cleanup ( )
212
212
} )
213
+
214
+ // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137262
215
+ test ( 'render error not shown right after syntax error' , async ( ) => {
216
+ const [ session , cleanup ] = await sandbox ( )
217
+
218
+ // Starting here:
219
+ await session . patch (
220
+ 'index.js' ,
221
+ `
222
+ class ClassDefault extends React.Component {
223
+ render() {
224
+ return <h1>Default Export</h1>;
225
+ }
226
+ }
227
+
228
+ export default ClassDefault;
229
+ `
230
+ )
231
+
232
+ expect (
233
+ await session . evaluate ( ( ) => document . querySelector ( 'h1' ) . textContent )
234
+ ) . toBe ( 'Default Export' )
235
+
236
+ // Break it with a syntax error:
237
+ await session . patch (
238
+ 'index.js' ,
239
+ `
240
+ import * as React from 'react';
241
+
242
+ class ClassDefault extends React.Component {
243
+ render()
244
+ return <h1>Default Export</h1>;
245
+ }
246
+ }
247
+
248
+ export default ClassDefault;
249
+ `
250
+ )
251
+ expect ( await session . hasRedbox ( true ) ) . toBe ( true )
252
+
253
+ // Now change the code to introduce a runtime error without fixing the syntax error:
254
+ await session . patch (
255
+ 'index.js' ,
256
+ `
257
+ import * as React from 'react';
258
+
259
+ class ClassDefault extends React.Component {
260
+ render()
261
+ throw new Error('nooo');
262
+ return <h1>Default Export</h1>;
263
+ }
264
+ }
265
+
266
+ export default ClassDefault;
267
+ `
268
+ )
269
+ expect ( await session . hasRedbox ( true ) ) . toBe ( true )
270
+
271
+ // Now fix the syntax error:
272
+ await session . patch (
273
+ 'index.js' ,
274
+ `
275
+ import * as React from 'react';
276
+
277
+ class ClassDefault extends React.Component {
278
+ render() {
279
+ throw new Error('nooo');
280
+ return <h1>Default Export</h1>;
281
+ }
282
+ }
283
+
284
+ export default ClassDefault;
285
+ `
286
+ )
287
+ expect ( await session . hasRedbox ( true ) ) . toBe ( true )
288
+ expect ( await session . getRedboxSource ( ) ) . toMatchInlineSnapshot ( `
289
+ "index.js (6:16) @ ClassDefault.render
290
+
291
+ 4 | class ClassDefault extends React.Component {
292
+ 5 | render() {
293
+ > 6 | throw new Error('nooo');
294
+ | ^
295
+ 7 | return <h1>Default Export</h1>;
296
+ 8 | }
297
+ 9 | }"
298
+ ` )
299
+
300
+ await cleanup ( )
301
+ } )
302
+
303
+ // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554137807
304
+ test ( 'module init error not shown' , async ( ) => {
305
+ // Start here:
306
+ const [ session , cleanup ] = await sandbox ( )
307
+
308
+ // We start here.
309
+ await session . patch (
310
+ 'index.js' ,
311
+ `
312
+ import * as React from 'react';
313
+ class ClassDefault extends React.Component {
314
+ render() {
315
+ return <h1>Default Export</h1>;
316
+ }
317
+ }
318
+ export default ClassDefault;
319
+ `
320
+ )
321
+
322
+ expect (
323
+ await session . evaluate ( ( ) => document . querySelector ( 'h1' ) . textContent )
324
+ ) . toBe ( 'Default Export' )
325
+
326
+ // Add a throw in module init phase:
327
+ await session . patch (
328
+ 'index.js' ,
329
+ `
330
+ // top offset for snapshot
331
+ import * as React from 'react';
332
+ throw new Error('no')
333
+ class ClassDefault extends React.Component {
334
+ render() {
335
+ return <h1>Default Export</h1>;
336
+ }
337
+ }
338
+ export default ClassDefault;
339
+ `
340
+ )
341
+
342
+ expect ( await session . hasRedbox ( true ) ) . toBe ( true )
343
+ expect ( await session . getRedboxSource ( ) ) . toMatchInlineSnapshot ( `
344
+ "index.js (4:12) @ Module../index.js
345
+
346
+ 2 | // top offset for snapshot
347
+ 3 | import * as React from 'react';
348
+ > 4 | throw new Error('no')
349
+ | ^
350
+ 5 | class ClassDefault extends React.Component {
351
+ 6 | render() {
352
+ 7 | return <h1>Default Export</h1>;"
353
+ ` )
354
+
355
+ await cleanup ( )
356
+ } )
357
+
358
+ // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554144016
359
+ test ( 'stuck error' , async ( ) => {
360
+ const [ session , cleanup ] = await sandbox ( )
361
+
362
+ // We start here.
363
+ await session . patch (
364
+ 'index.js' ,
365
+ `
366
+ import * as React from 'react';
367
+
368
+ function FunctionDefault() {
369
+ return <h1>Default Export Function</h1>;
370
+ }
371
+
372
+ export default FunctionDefault;
373
+ `
374
+ )
375
+
376
+ // We add a new file. Let's call it Foo.js.
377
+ await session . write (
378
+ 'Foo.js' ,
379
+ `
380
+ // intentionally skips export
381
+ export default function Foo() {
382
+ return React.createElement('h1', null, 'Foo');
383
+ }
384
+ `
385
+ )
386
+
387
+ // We edit our first file to use it.
388
+ await session . patch (
389
+ 'index.js' ,
390
+ `
391
+ import * as React from 'react';
392
+ import Foo from './Foo';
393
+ function FunctionDefault() {
394
+ return <Foo />;
395
+ }
396
+ export default FunctionDefault;
397
+ `
398
+ )
399
+
400
+ // We get an error because Foo didn't import React. Fair.
401
+ expect ( await session . hasRedbox ( true ) ) . toBe ( true )
402
+ expect ( await session . getRedboxSource ( ) ) . toMatchInlineSnapshot ( `
403
+ "Foo.js (4:8) @ Foo
404
+
405
+ 2 | // intentionally skips export
406
+ 3 | export default function Foo() {
407
+ > 4 | return React.createElement('h1', null, 'Foo');
408
+ | ^
409
+ 5 | }
410
+ 6 | "
411
+ ` )
412
+
413
+ // Let's add that to Foo.
414
+ await session . patch (
415
+ 'Foo.js' ,
416
+ `
417
+ import * as React from 'react';
418
+ export default function Foo() {
419
+ return React.createElement('h1', null, 'Foo');
420
+ }
421
+ `
422
+ )
423
+
424
+ // Expected: this fixes the problem
425
+ expect ( await session . hasRedbox ( ) ) . toBe ( false )
426
+
427
+ await cleanup ( )
428
+ } )
429
+
430
+ // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554150098
431
+ test ( 'syntax > runtime error' , async ( ) => {
432
+ const [ session , cleanup ] = await sandbox ( )
433
+
434
+ // Start here.
435
+ await session . patch (
436
+ 'index.js' ,
437
+ `
438
+ import * as React from 'react';
439
+
440
+ export default function FunctionNamed() {
441
+ return <div />
442
+ }
443
+ `
444
+ )
445
+ // TODO: this acts weird without above step
446
+ await session . patch (
447
+ 'index.js' ,
448
+ `
449
+ import * as React from 'react';
450
+ let i = 0
451
+ setInterval(() => {
452
+ i++
453
+ throw Error('no ' + i)
454
+ }, 1000)
455
+ export default function FunctionNamed() {
456
+ return <div />
457
+ }
458
+ `
459
+ )
460
+
461
+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) )
462
+ expect ( await session . hasRedbox ( true ) ) . toBe ( true )
463
+ expect ( await session . getRedboxSource ( ) ) . toMatchInlineSnapshot ( `
464
+ "index.js (6:14) @ eval
465
+
466
+ 4 | setInterval(() => {
467
+ 5 | i++
468
+ > 6 | throw Error('no ' + i)
469
+ | ^
470
+ 7 | }, 1000)
471
+ 8 | export default function FunctionNamed() {
472
+ 9 | return <div />"
473
+ ` )
474
+
475
+ // Make a syntax error.
476
+ await session . patch (
477
+ 'index.js' ,
478
+ `
479
+ import * as React from 'react';
480
+ let i = 0
481
+ setInterval(() => {
482
+ i++
483
+ throw Error('no ' + i)
484
+ }, 1000)
485
+ export default function FunctionNamed() {
486
+ `
487
+ )
488
+
489
+ await new Promise ( resolve => setTimeout ( resolve , 1000 ) )
490
+ expect ( await session . hasRedbox ( true ) ) . toBe ( true )
491
+ expect ( await session . getRedboxSource ( ) ) . toMatch ( 'SyntaxError' )
492
+
493
+ // Test that runtime error does not take over:
494
+ await new Promise ( resolve => setTimeout ( resolve , 2000 ) )
495
+ expect ( await session . hasRedbox ( true ) ) . toBe ( true )
496
+ expect ( await session . getRedboxSource ( ) ) . toMatch ( 'SyntaxError' )
497
+
498
+ await cleanup ( )
499
+ } )
500
+
501
+ // https://github.com/pmmmwh/react-refresh-webpack-plugin/pull/3#issuecomment-554152127
502
+ test ( 'boundaries' , async ( ) => {
503
+ const [ session , cleanup ] = await sandbox ( )
504
+
505
+ await session . write (
506
+ 'FunctionDefault.js' ,
507
+ `
508
+ export default function FunctionDefault() {
509
+ return <h2>hello</h2>
510
+ }
511
+ `
512
+ )
513
+ await session . patch (
514
+ 'index.js' ,
515
+ `
516
+ import FunctionDefault from './FunctionDefault.js'
517
+ class ErrorBoundary extends React.Component {
518
+ constructor() {
519
+ super()
520
+ this.state = { hasError: false, error: null };
521
+ }
522
+ static getDerivedStateFromError(error) {
523
+ return {
524
+ hasError: true,
525
+ error
526
+ };
527
+ }
528
+ render() {
529
+ if (this.state.hasError) {
530
+ return this.props.fallback;
531
+ }
532
+ return this.props.children;
533
+ }
534
+ }
535
+ function App() {
536
+ return (
537
+ <ErrorBoundary fallback={<h2>error</h2>}>
538
+ <FunctionDefault />
539
+ </ErrorBoundary>
540
+ );
541
+ }
542
+ export default App;
543
+ `
544
+ )
545
+
546
+ expect (
547
+ await session . evaluate ( ( ) => document . querySelector ( 'h2' ) . textContent )
548
+ ) . toBe ( 'hello' )
549
+
550
+ await session . write (
551
+ 'FunctionDefault.js' ,
552
+ `export default function FunctionDefault() { throw new Error('no'); }`
553
+ )
554
+
555
+ expect ( await session . hasRedbox ( true ) ) . toBe ( true )
556
+ expect ( await session . getRedboxSource ( ) ) . toMatchInlineSnapshot ( `
557
+ "FunctionDefault.js (1:50) @ FunctionDefault
558
+
559
+ > 1 | export default function FunctionDefault() { throw new Error('no'); }
560
+ | ^"
561
+ ` )
562
+ expect (
563
+ await session . evaluate ( ( ) => document . querySelector ( 'h2' ) . textContent )
564
+ ) . toBe ( 'error' )
565
+
566
+ await cleanup ( )
567
+ } )
0 commit comments