@@ -103,6 +103,30 @@ describe('FakeTimers', () => {
103103 timers . useFakeTimers ( ) ;
104104 expect ( global . clearImmediate ) . not . toBe ( origClearImmediate ) ;
105105 } ) ;
106+
107+ it ( 'mocks requestAnimationFrame if it exists on global' , ( ) => {
108+ const global = {
109+ Date,
110+ clearTimeout,
111+ requestAnimationFrame : ( ) => - 1 ,
112+ setTimeout,
113+ } as unknown as typeof globalThis ;
114+ const timers = new FakeTimers ( { config : makeProjectConfig ( ) , global} ) ;
115+ timers . useFakeTimers ( ) ;
116+ expect ( global . requestAnimationFrame ) . toBeDefined ( ) ;
117+ } ) ;
118+
119+ it ( 'mocks cancelAnimationFrame if it exists on global' , ( ) => {
120+ const global = {
121+ Date,
122+ cancelAnimationFrame : ( ) => { } ,
123+ clearTimeout,
124+ setTimeout,
125+ } as unknown as typeof globalThis ;
126+ const timers = new FakeTimers ( { config : makeProjectConfig ( ) , global} ) ;
127+ timers . useFakeTimers ( ) ;
128+ expect ( global . cancelAnimationFrame ) . toBeDefined ( ) ;
129+ } ) ;
106130 } ) ;
107131
108132 describe ( 'runAllTicks' , ( ) => {
@@ -570,6 +594,182 @@ describe('FakeTimers', () => {
570594 } ) ;
571595 } ) ;
572596
597+ describe ( 'runToFrame' , ( ) => {
598+ it ( 'runs scheduled animation frames in order' , ( ) => {
599+ const global = {
600+ Date,
601+ clearTimeout,
602+ process,
603+ requestAnimationFrame : ( ) => - 1 ,
604+ setTimeout,
605+ } as unknown as typeof globalThis ;
606+
607+ const timers = new FakeTimers ( { config : makeProjectConfig ( ) , global} ) ;
608+ timers . useFakeTimers ( ) ;
609+
610+ const runOrder : Array < string > = [ ] ;
611+ const mock1 = jest . fn ( ( ) => runOrder . push ( 'mock1' ) ) ;
612+ const mock2 = jest . fn ( ( ) => runOrder . push ( 'mock2' ) ) ;
613+ const mock3 = jest . fn ( ( ) => runOrder . push ( 'mock3' ) ) ;
614+
615+ global . requestAnimationFrame ( mock1 ) ;
616+ global . requestAnimationFrame ( mock2 ) ;
617+ global . requestAnimationFrame ( mock3 ) ;
618+
619+ timers . runToFrame ( ) ;
620+
621+ expect ( runOrder ) . toEqual ( [ 'mock1' , 'mock2' , 'mock3' ] ) ;
622+ } ) ;
623+
624+ it ( 'should only run currently scheduled animation frames' , ( ) => {
625+ const global = {
626+ Date,
627+ clearTimeout,
628+ process,
629+ requestAnimationFrame : ( ) => - 1 ,
630+ setTimeout,
631+ } as unknown as typeof globalThis ;
632+
633+ const timers = new FakeTimers ( { config : makeProjectConfig ( ) , global} ) ;
634+ timers . useFakeTimers ( ) ;
635+
636+ const runOrder : Array < string > = [ ] ;
637+ function run ( ) {
638+ runOrder . push ( 'first-frame' ) ;
639+
640+ // scheduling another animation frame in the first frame
641+ global . requestAnimationFrame ( ( ) => runOrder . push ( 'second-frame' ) ) ;
642+ }
643+
644+ global . requestAnimationFrame ( run ) ;
645+
646+ // only the first frame should be executed
647+ timers . runToFrame ( ) ;
648+
649+ expect ( runOrder ) . toEqual ( [ 'first-frame' ] ) ;
650+
651+ timers . runToFrame ( ) ;
652+
653+ expect ( runOrder ) . toEqual ( [ 'first-frame' , 'second-frame' ] ) ;
654+ } ) ;
655+
656+ it ( 'should allow cancelling of scheduled animation frames' , ( ) => {
657+ const global = {
658+ Date,
659+ cancelAnimationFrame : ( ) => { } ,
660+ clearTimeout,
661+ process,
662+ requestAnimationFrame : ( ) => - 1 ,
663+ setTimeout,
664+ } as unknown as typeof globalThis ;
665+
666+ const timers = new FakeTimers ( { config : makeProjectConfig ( ) , global} ) ;
667+ timers . useFakeTimers ( ) ;
668+
669+ const runOrder : Array < string > = [ ] ;
670+ const callback = ( ) => runOrder . push ( 'frame' ) ;
671+
672+ const timerId = global . requestAnimationFrame ( callback ) ;
673+ global . cancelAnimationFrame ( timerId ) ;
674+
675+ // no frames should be executed
676+ timers . runToFrame ( ) ;
677+
678+ expect ( runOrder ) . toEqual ( [ ] ) ;
679+ } ) ;
680+
681+ it ( 'should only advance as much time is needed to get to the next frame' , ( ) => {
682+ const global = {
683+ Date,
684+ cancelAnimationFrame : ( ) => { } ,
685+ clearTimeout,
686+ process,
687+ requestAnimationFrame : ( ) => - 1 ,
688+ setTimeout,
689+ } as unknown as typeof globalThis ;
690+
691+ const timers = new FakeTimers ( { config : makeProjectConfig ( ) , global} ) ;
692+ timers . useFakeTimers ( ) ;
693+
694+ const runOrder : Array < string > = [ ] ;
695+ const start = global . Date . now ( ) ;
696+
697+ const callback = ( ) => runOrder . push ( 'frame' ) ;
698+ global . requestAnimationFrame ( callback ) ;
699+
700+ // Advancing timers less than a frame (which is 16ms)
701+ timers . advanceTimersByTime ( 6 ) ;
702+ expect ( global . Date . now ( ) ) . toEqual ( start + 6 ) ;
703+
704+ // frame not yet executed
705+ expect ( runOrder ) . toEqual ( [ ] ) ;
706+
707+ // move timers forward to execute frame
708+ timers . runToFrame ( ) ;
709+
710+ // frame has executed as time has moved forward 10ms to get to the 16ms frame time
711+ expect ( runOrder ) . toEqual ( [ 'frame' ] ) ;
712+ expect ( global . Date . now ( ) ) . toEqual ( start + 16 ) ;
713+ } ) ;
714+
715+ it ( 'should execute any timers on the way to the animation frame' , ( ) => {
716+ const global = {
717+ Date,
718+ cancelAnimationFrame : ( ) => { } ,
719+ clearTimeout,
720+ process,
721+ requestAnimationFrame : ( ) => - 1 ,
722+ setTimeout,
723+ } as unknown as typeof globalThis ;
724+
725+ const timers = new FakeTimers ( { config : makeProjectConfig ( ) , global} ) ;
726+ timers . useFakeTimers ( ) ;
727+
728+ const runOrder : Array < string > = [ ] ;
729+
730+ global . requestAnimationFrame ( ( ) => runOrder . push ( 'frame' ) ) ;
731+
732+ // scheduling a timeout that will be executed on the way to the frame
733+ global . setTimeout ( ( ) => runOrder . push ( 'timeout' ) , 10 ) ;
734+
735+ // move timers forward to execute frame
736+ timers . runToFrame ( ) ;
737+
738+ expect ( runOrder ) . toEqual ( [ 'timeout' , 'frame' ] ) ;
739+ } ) ;
740+
741+ it ( 'should not execute any timers scheduled inside of a frame' , ( ) => {
742+ const global = {
743+ Date,
744+ cancelAnimationFrame : ( ) => { } ,
745+ clearTimeout,
746+ process,
747+ requestAnimationFrame : ( ) => - 1 ,
748+ setTimeout,
749+ } as unknown as typeof globalThis ;
750+
751+ const timers = new FakeTimers ( { config : makeProjectConfig ( ) , global} ) ;
752+ timers . useFakeTimers ( ) ;
753+
754+ const runOrder : Array < string > = [ ] ;
755+
756+ global . requestAnimationFrame ( ( ) => {
757+ runOrder . push ( 'frame' ) ;
758+ // scheduling a timer inside of a frame
759+ global . setTimeout ( ( ) => runOrder . push ( 'timeout' ) , 1 ) ;
760+ } ) ;
761+
762+ timers . runToFrame ( ) ;
763+
764+ // timeout not yet executed
765+ expect ( runOrder ) . toEqual ( [ 'frame' ] ) ;
766+
767+ // validating that the timer will still be executed
768+ timers . advanceTimersByTime ( 1 ) ;
769+ expect ( runOrder ) . toEqual ( [ 'frame' , 'timeout' ] ) ;
770+ } ) ;
771+ } ) ;
772+
573773 describe ( 'reset' , ( ) => {
574774 it ( 'resets all pending setTimeouts' , ( ) => {
575775 const global = {
@@ -649,6 +849,25 @@ describe('FakeTimers', () => {
649849 timers . advanceTimersByTime ( 50 ) ;
650850 expect ( mock1 ) . toHaveBeenCalledTimes ( 0 ) ;
651851 } ) ;
852+
853+ it ( 'resets all scheduled animation frames' , ( ) => {
854+ const global = {
855+ Date,
856+ clearTimeout,
857+ process,
858+ requestAnimationFrame : ( ) => - 1 ,
859+ setTimeout,
860+ } as unknown as typeof globalThis ;
861+ const timers = new FakeTimers ( { config : makeProjectConfig ( ) , global} ) ;
862+ timers . useFakeTimers ( ) ;
863+
864+ const mock1 = jest . fn ( ) ;
865+ global . requestAnimationFrame ( mock1 ) ;
866+
867+ timers . reset ( ) ;
868+ timers . runAllTimers ( ) ;
869+ expect ( mock1 ) . toHaveBeenCalledTimes ( 0 ) ;
870+ } ) ;
652871 } ) ;
653872
654873 describe ( 'runOnlyPendingTimers' , ( ) => {
0 commit comments