@@ -453,4 +453,153 @@ describe('ReactIncrementalUpdates', () => {
453453 } ) ;
454454 expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'derived state' ) ] ) ;
455455 } ) ;
456+
457+ it ( 'flushes all expired updates in a single batch' , ( ) => {
458+ class Foo extends React . Component {
459+ componentDidUpdate ( ) {
460+ ReactNoop . yield ( 'Commit: ' + this . props . prop ) ;
461+ }
462+ componentDidMount ( ) {
463+ ReactNoop . yield ( 'Commit: ' + this . props . prop ) ;
464+ }
465+ render ( ) {
466+ ReactNoop . yield ( 'Render: ' + this . props . prop ) ;
467+ return < span prop = { this . props . prop } /> ;
468+ }
469+ }
470+
471+ // First, as a sanity check, assert what happens when four low pri
472+ // updates in separate batches are all flushed in the same callback
473+ ReactNoop . render ( < Foo prop = "" /> ) ;
474+ ReactNoop . expire ( 1000 ) ;
475+ jest . advanceTimersByTime ( 1000 ) ;
476+ ReactNoop . render ( < Foo prop = "he" /> ) ;
477+ ReactNoop . expire ( 1000 ) ;
478+ jest . advanceTimersByTime ( 1000 ) ;
479+ ReactNoop . render ( < Foo prop = "hell" /> ) ;
480+ ReactNoop . expire ( 1000 ) ;
481+ jest . advanceTimersByTime ( 1000 ) ;
482+ ReactNoop . render ( < Foo prop = "hello" /> ) ;
483+
484+ // There should be a separate render and commit for each update
485+ expect ( ReactNoop . flush ( ) ) . toEqual ( [
486+ 'Render: ' ,
487+ 'Commit: ' ,
488+ 'Render: he' ,
489+ 'Commit: he' ,
490+ 'Render: hell' ,
491+ 'Commit: hell' ,
492+ 'Render: hello' ,
493+ 'Commit: hello' ,
494+ ] ) ;
495+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'hello' ) ] ) ;
496+
497+ // Now do the same thing, except this time expire all the updates
498+ // before flushing them.
499+ ReactNoop . render ( < Foo prop = "" /> ) ;
500+ ReactNoop . expire ( 1000 ) ;
501+ jest . advanceTimersByTime ( 1000 ) ;
502+ ReactNoop . render ( < Foo prop = "go" /> ) ;
503+ ReactNoop . expire ( 1000 ) ;
504+ jest . advanceTimersByTime ( 1000 ) ;
505+ ReactNoop . render ( < Foo prop = "good" /> ) ;
506+ ReactNoop . expire ( 1000 ) ;
507+ jest . advanceTimersByTime ( 1000 ) ;
508+ ReactNoop . render ( < Foo prop = "goodbye" /> ) ;
509+
510+ ReactNoop . advanceTime ( 10000 ) ;
511+ jest . advanceTimersByTime ( 10000 ) ;
512+
513+ // All the updates should render and commit in a single batch.
514+ expect ( ReactNoop . flush ( ) ) . toEqual ( [ 'Render: goodbye' , 'Commit: goodbye' ] ) ;
515+ expect ( ReactNoop . getChildren ( ) ) . toEqual ( [ span ( 'goodbye' ) ] ) ;
516+ } ) ;
517+
518+ it ( 'flushes all expired updates in a single batch across multiple roots' , ( ) => {
519+ // Same as previous test, but with two roots.
520+ class Foo extends React . Component {
521+ componentDidUpdate ( ) {
522+ ReactNoop . yield ( 'Commit: ' + this . props . prop ) ;
523+ }
524+ componentDidMount ( ) {
525+ ReactNoop . yield ( 'Commit: ' + this . props . prop ) ;
526+ }
527+ render ( ) {
528+ ReactNoop . yield ( 'Render: ' + this . props . prop ) ;
529+ return < span prop = { this . props . prop } /> ;
530+ }
531+ }
532+
533+ // First, as a sanity check, assert what happens when four low pri
534+ // updates in separate batches are all flushed in the same callback
535+ ReactNoop . renderToRootWithID ( < Foo prop = "" /> , 'a' ) ;
536+ ReactNoop . renderToRootWithID ( < Foo prop = "" /> , 'b' ) ;
537+
538+ ReactNoop . expire ( 1000 ) ;
539+ jest . advanceTimersByTime ( 1000 ) ;
540+ ReactNoop . renderToRootWithID ( < Foo prop = "he" /> , 'a' ) ;
541+ ReactNoop . renderToRootWithID ( < Foo prop = "he" /> , 'b' ) ;
542+
543+ ReactNoop . expire ( 1000 ) ;
544+ jest . advanceTimersByTime ( 1000 ) ;
545+ ReactNoop . renderToRootWithID ( < Foo prop = "hell" /> , 'a' ) ;
546+ ReactNoop . renderToRootWithID ( < Foo prop = "hell" /> , 'b' ) ;
547+
548+ ReactNoop . expire ( 1000 ) ;
549+ jest . advanceTimersByTime ( 1000 ) ;
550+ ReactNoop . renderToRootWithID ( < Foo prop = "hello" /> , 'a' ) ;
551+ ReactNoop . renderToRootWithID ( < Foo prop = "hello" /> , 'b' ) ;
552+
553+ // There should be a separate render and commit for each update
554+ expect ( ReactNoop . flush ( ) ) . toEqual ( [
555+ 'Render: ' ,
556+ 'Commit: ' ,
557+ 'Render: ' ,
558+ 'Commit: ' ,
559+ 'Render: he' ,
560+ 'Commit: he' ,
561+ 'Render: he' ,
562+ 'Commit: he' ,
563+ 'Render: hell' ,
564+ 'Commit: hell' ,
565+ 'Render: hell' ,
566+ 'Commit: hell' ,
567+ 'Render: hello' ,
568+ 'Commit: hello' ,
569+ 'Render: hello' ,
570+ 'Commit: hello' ,
571+ ] ) ;
572+ expect ( ReactNoop . getChildren ( 'a' ) ) . toEqual ( [ span ( 'hello' ) ] ) ;
573+ expect ( ReactNoop . getChildren ( 'b' ) ) . toEqual ( [ span ( 'hello' ) ] ) ;
574+
575+ // Now do the same thing, except this time expire all the updates
576+ // before flushing them.
577+ ReactNoop . renderToRootWithID ( < Foo prop = "" /> , 'a' ) ;
578+ ReactNoop . renderToRootWithID ( < Foo prop = "" /> , 'b' ) ;
579+ ReactNoop . expire ( 1000 ) ;
580+ jest . advanceTimersByTime ( 1000 ) ;
581+ ReactNoop . renderToRootWithID ( < Foo prop = "go" /> , 'a' ) ;
582+ ReactNoop . renderToRootWithID ( < Foo prop = "go" /> , 'b' ) ;
583+ ReactNoop . expire ( 1000 ) ;
584+ jest . advanceTimersByTime ( 1000 ) ;
585+ ReactNoop . renderToRootWithID ( < Foo prop = "good" /> , 'a' ) ;
586+ ReactNoop . renderToRootWithID ( < Foo prop = "good" /> , 'b' ) ;
587+ ReactNoop . expire ( 1000 ) ;
588+ jest . advanceTimersByTime ( 1000 ) ;
589+ ReactNoop . renderToRootWithID ( < Foo prop = "goodbye" /> , 'a' ) ;
590+ ReactNoop . renderToRootWithID ( < Foo prop = "goodbye" /> , 'b' ) ;
591+
592+ ReactNoop . advanceTime ( 10000 ) ;
593+ jest . advanceTimersByTime ( 10000 ) ;
594+
595+ // All the updates should render and commit in a single batch.
596+ expect ( ReactNoop . flush ( ) ) . toEqual ( [
597+ 'Render: goodbye' ,
598+ 'Commit: goodbye' ,
599+ 'Render: goodbye' ,
600+ 'Commit: goodbye' ,
601+ ] ) ;
602+ expect ( ReactNoop . getChildren ( 'a' ) ) . toEqual ( [ span ( 'goodbye' ) ] ) ;
603+ expect ( ReactNoop . getChildren ( 'b' ) ) . toEqual ( [ span ( 'goodbye' ) ] ) ;
604+ } ) ;
456605} ) ;
0 commit comments