diff --git a/packages/react-native-web/src/exports/Animated/__tests__/index-test.js b/packages/react-native-web/src/exports/Animated/__tests__/index-test.js index 0946c8a00a..ee708d9552 100644 --- a/packages/react-native-web/src/exports/Animated/__tests__/index-test.js +++ b/packages/react-native-web/src/exports/Animated/__tests__/index-test.js @@ -9,6 +9,7 @@ import Animated from '..'; import Easing from '../../Easing'; +import AnimatedImplementation from '../../../vendor/react-native/Animated/AnimatedImplementation'; const AnimatedInterpolation = Animated.Interpolation; @@ -329,4 +330,74 @@ describe('Animated', () => { expect(interpolation(2 / 3)).toBe('rgba(0, 0, 0, 0.667)'); }); }); + + describe('sequence and loop', () => { + it('supports restarting sequence after it was stopped during execution', () => { + const anim1 = { start: jest.fn(), stop: jest.fn() }; + const anim2 = { start: jest.fn(), stop: jest.fn() }; + const cb = jest.fn(); + + const seq = AnimatedImplementation.sequence([anim1, anim2]); + + seq.start(cb); + + anim1.start.mock.calls[0][0]({ finished: true }); + seq.stop(); + + // anim1 should be finished so anim2 should also start + expect(anim1.start).toHaveBeenCalledTimes(1); + expect(anim2.start).toHaveBeenCalledTimes(1); + + seq.start(cb); + + // after restart the sequence should resume from the anim2 + expect(anim1.start).toHaveBeenCalledTimes(1); + expect(anim2.start).toHaveBeenCalledTimes(2); + }); + + it('supports restarting sequence after it was finished without a reset', () => { + const anim1 = { start: jest.fn(), stop: jest.fn() }; + const anim2 = { start: jest.fn(), stop: jest.fn() }; + const cb = jest.fn(); + + const seq = AnimatedImplementation.sequence([anim1, anim2]); + + seq.start(cb); + anim1.start.mock.calls[0][0]({ finished: true }); + anim2.start.mock.calls[0][0]({ finished: true }); + + // sequence should be finished + expect(cb).toBeCalledWith({ finished: true }); + + seq.start(cb); + + // sequence should successfully restart from the anim1 + expect(anim1.start).toHaveBeenCalledTimes(2); + expect(anim2.start).toHaveBeenCalledTimes(1); + }); + + it('restarts sequence normally in a loop if resetBeforeIteration is false', () => { + const anim1 = { start: jest.fn(), stop: jest.fn() }; + const anim2 = { start: jest.fn(), stop: jest.fn() }; + const seq = AnimatedImplementation.sequence([anim1, anim2]); + + const loop = AnimatedImplementation.loop(seq, { + resetBeforeIteration: false + }); + + loop.start(); + + expect(anim1.start).toHaveBeenCalledTimes(1); + + anim1.start.mock.calls[0][0]({ finished: true }); + + expect(anim2.start).toHaveBeenCalledTimes(1); + + anim2.start.mock.calls[0][0]({ finished: true }); + + // after anim2 is finished, the sequence is finished, + // hence the loop iteration is finished, so the next iteration starts + expect(anim1.start).toHaveBeenCalledTimes(2); + }); + }); }); diff --git a/packages/react-native-web/src/vendor/react-native/Animated/AnimatedImplementation.js b/packages/react-native-web/src/vendor/react-native/Animated/AnimatedImplementation.js index 0fdbe5ace5..aee7cbdc39 100644 --- a/packages/react-native-web/src/vendor/react-native/Animated/AnimatedImplementation.js +++ b/packages/react-native-web/src/vendor/react-native/Animated/AnimatedImplementation.js @@ -318,6 +318,8 @@ const sequence = function ( current++; if (current === animations.length) { + // if the start is called, even without a reset, it should start from the beginning + current = 0; callback && callback(result); return; }