Skip to content

Commit

Permalink
Fix Animate.sequence restart issues (facebook#44031)
Browse files Browse the repository at this point in the history
Summary:
Currently, if the `Animated.sequence` animation is finished, and then the `start()` method is called without a `reset()` method being invoked beforehand, the failure happens: ```undefined is not an object (evaluating 'animations[current].start')```.
Use cases:
- sequence animation started, finished, and then started again
- sequence animation is used in `Animation.loop` with `resetBeforeIteration` set to `false`, which essentially does the same as the previous case

Related issues:
- facebook#43120
- facebook#37611

## Changelog:

[General] [Fixed] - Fix sequence restart failure

Pull Request resolved: facebook#44031

Test Plan: Test cases are included: 1 for regression and 2 for mentioned use cases in the summary

Reviewed By: cipolleschi

Differential Revision: D56015346

Pulled By: dmytrorykun

fbshipit-source-id: 8b0f46c8a33397fece807634463ce630c89d28af
  • Loading branch information
asyler authored and facebook-github-bot committed May 28, 2024
1 parent 3c5a3e0 commit a93a15a
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,50 @@ describe('Animated tests', () => {

expect(cb).toBeCalledWith({finished: false});
});

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 = Animated.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 = Animated.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);
});
});

describe('Animated Loop', () => {
Expand Down Expand Up @@ -506,6 +550,28 @@ describe('Animated tests', () => {
expect(cb).not.toBeCalled();
});

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 = Animated.sequence([anim1, anim2]);

const loop = Animated.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);
});

describe('Animated Parallel', () => {
it('works with an empty parallel', () => {
const cb = jest.fn();
Expand Down

0 comments on commit a93a15a

Please sign in to comment.