Files
react-native/Libraries/Animated/__tests__/AnimatedNative-test.js
T
Andrei Shikov ef0db95300 Back out "Adjust animation batch numbers to be consistent when controlled by native"
Summary:
This change caused crashes in animations on some surfaces.

Changelog: [Internal]

Reviewed By: JoshuaGross

Differential Revision: D28013969

fbshipit-source-id: 95845c69d6e67d59582ea14ad08cbf42fd3e2f8f
2021-04-27 04:00:16 -07:00

922 lines
28 KiB
JavaScript

/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
* @emails oncall+react_native
*/
jest
.clearAllMocks()
.mock('../../BatchedBridge/NativeModules', () => ({
NativeAnimatedModule: {},
PlatformConstants: {
getConstants() {
return {};
},
},
}))
.mock('../NativeAnimatedModule')
.mock('../../EventEmitter/NativeEventEmitter')
// findNodeHandle is imported from ReactNative so mock that whole module.
.setMock('../../Renderer/shims/ReactNative', {findNodeHandle: () => 1});
import TestRenderer from 'react-test-renderer';
import * as React from 'react';
const Animated = require('../Animated');
const NativeAnimatedHelper = require('../NativeAnimatedHelper');
describe('Native Animated', () => {
const NativeAnimatedModule = require('../NativeAnimatedModule').default;
beforeEach(() => {
Object.assign(NativeAnimatedModule, {
getValue: jest.fn(),
addAnimatedEventToView: jest.fn(),
connectAnimatedNodes: jest.fn(),
connectAnimatedNodeToView: jest.fn(),
createAnimatedNode: jest.fn(),
disconnectAnimatedNodeFromView: jest.fn(),
disconnectAnimatedNodes: jest.fn(),
dropAnimatedNode: jest.fn(),
extractAnimatedNodeOffset: jest.fn(),
flattenAnimatedNodeOffset: jest.fn(),
removeAnimatedEventFromView: jest.fn(),
restoreDefaultValues: jest.fn(),
setAnimatedNodeOffset: jest.fn(),
setAnimatedNodeValue: jest.fn(),
startAnimatingNode: jest.fn(),
startListeningToAnimatedNodeValue: jest.fn(),
stopAnimation: jest.fn(),
stopListeningToAnimatedNodeValue: jest.fn(),
});
});
describe('Animated Value', () => {
it('proxies `setValue` correctly', () => {
const opacity = new Animated.Value(0);
const ref = React.createRef(null);
Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
TestRenderer.create(<Animated.View ref={ref} style={{opacity}} />);
expect(ref.current).not.toBeNull();
jest.spyOn(ref.current, 'setNativeProps');
opacity.setValue(0.5);
expect(NativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(
expect.any(Number),
0.5,
);
expect(ref.current.setNativeProps).not.toHaveBeenCalled();
});
it('should set offset', () => {
const opacity = new Animated.Value(0);
opacity.setOffset(10);
opacity.__makeNative();
TestRenderer.create(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 10},
);
opacity.setOffset(20);
expect(NativeAnimatedModule.setAnimatedNodeOffset).toBeCalledWith(
expect.any(Number),
20,
);
});
it('should flatten offset', () => {
const opacity = new Animated.Value(0);
opacity.__makeNative();
TestRenderer.create(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 0},
);
opacity.flattenOffset();
expect(NativeAnimatedModule.flattenAnimatedNodeOffset).toBeCalledWith(
expect.any(Number),
);
});
it('should save value on unmount', () => {
NativeAnimatedModule.getValue = jest.fn((tag, saveCallback) => {
saveCallback(1);
});
const opacity = new Animated.Value(0);
opacity.__makeNative();
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
const tag = opacity.__getNativeTag();
root.unmount();
expect(NativeAnimatedModule.getValue).toBeCalledWith(
tag,
expect.any(Function),
);
expect(opacity.__getValue()).toBe(1);
});
it('should extract offset', () => {
const opacity = new Animated.Value(0);
opacity.__makeNative();
TestRenderer.create(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 0},
);
opacity.extractOffset();
expect(NativeAnimatedModule.extractAnimatedNodeOffset).toBeCalledWith(
expect.any(Number),
);
});
});
describe('Animated Listeners', () => {
it('should get updates', () => {
const value1 = new Animated.Value(0);
value1.__makeNative();
const listener = jest.fn();
const id = value1.addListener(listener);
expect(
NativeAnimatedModule.startListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 42,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(1);
expect(listener).toBeCalledWith({value: 42});
expect(value1.__getValue()).toBe(42);
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 7,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(2);
expect(listener).toBeCalledWith({value: 7});
expect(value1.__getValue()).toBe(7);
value1.removeListener(id);
expect(
NativeAnimatedModule.stopListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 1492,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(2);
expect(value1.__getValue()).toBe(7);
});
it('should removeAll', () => {
const value1 = new Animated.Value(0);
value1.__makeNative();
const listener = jest.fn();
[1, 2, 3, 4].forEach(() => value1.addListener(listener));
expect(
NativeAnimatedModule.startListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 42,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(4);
expect(listener).toBeCalledWith({value: 42});
value1.removeAllListeners();
expect(
NativeAnimatedModule.stopListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
value: 7,
tag: value1.__getNativeTag(),
});
expect(listener).toHaveBeenCalledTimes(4);
});
});
describe('Animated Events', () => {
it('should map events', () => {
const value = new Animated.Value(0);
value.__makeNative();
const event = Animated.event([{nativeEvent: {state: {foo: value}}}], {
useNativeDriver: true,
});
const root = TestRenderer.create(<Animated.View onTouchMove={event} />);
expect(NativeAnimatedModule.addAnimatedEventToView).toBeCalledWith(
expect.any(Number),
'onTouchMove',
{
nativeEventPath: ['state', 'foo'],
animatedValueTag: value.__getNativeTag(),
},
);
expect(
NativeAnimatedModule.removeAnimatedEventFromView,
).not.toHaveBeenCalled();
root.unmount();
expect(NativeAnimatedModule.removeAnimatedEventFromView).toBeCalledWith(
expect.any(Number),
'onTouchMove',
value.__getNativeTag(),
);
});
it('should throw on invalid event path', () => {
const value = new Animated.Value(0);
value.__makeNative();
const event = Animated.event([{notNativeEvent: {foo: value}}], {
useNativeDriver: true,
});
jest.spyOn(console, 'error').mockImplementationOnce((...args) => {
if (args[0].startsWith('The above error occurred in the')) {
return;
}
console.errorDebug(...args);
});
expect(() => {
TestRenderer.create(<Animated.View onTouchMove={event} />);
}).toThrowError(/nativeEvent/);
expect(NativeAnimatedModule.addAnimatedEventToView).not.toBeCalled();
console.error.mockRestore();
});
it('should call listeners', () => {
const value = new Animated.Value(0);
value.__makeNative();
const listener = jest.fn();
const event = Animated.event([{nativeEvent: {foo: value}}], {
useNativeDriver: true,
listener,
});
const handler = event.__getHandler();
handler({nativeEvent: {foo: 42}});
expect(listener).toHaveBeenCalledTimes(1);
expect(listener).toBeCalledWith({nativeEvent: {foo: 42}});
});
});
describe('Animated Graph', () => {
it('creates and detaches nodes', () => {
const opacity = new Animated.Value(0);
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.createAnimatedNode).toHaveBeenCalledTimes(3);
expect(NativeAnimatedModule.connectAnimatedNodes).toHaveBeenCalledTimes(
2,
);
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'frames',
frames: expect.any(Array),
toValue: expect.any(Number),
iterations: 1,
},
expect.any(Function),
);
expect(
NativeAnimatedModule.disconnectAnimatedNodes,
).not.toHaveBeenCalled();
expect(NativeAnimatedModule.dropAnimatedNode).not.toHaveBeenCalled();
root.unmount();
expect(
NativeAnimatedModule.disconnectAnimatedNodes,
).toHaveBeenCalledTimes(2);
expect(NativeAnimatedModule.dropAnimatedNode).toHaveBeenCalledTimes(3);
});
it('sends a valid description for value, style and props nodes', () => {
const opacity = new Animated.Value(0);
TestRenderer.create(<Animated.View style={{opacity}} />);
Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 0},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'style', style: {opacity: expect.any(Number)}},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'props', props: {style: expect.any(Number)}},
);
});
it('sends a valid graph description for Animated.add nodes', () => {
const first = new Animated.Value(1);
const second = new Animated.Value(2);
first.__makeNative();
second.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.add(first, second)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'addition', input: expect.any(Array)},
);
const additionCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'addition',
);
expect(additionCalls.length).toBe(1);
const additionCall = additionCalls[0];
const additionNodeTag = additionCall[0];
const additionConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === additionNodeTag,
);
expect(additionConnectionCalls.length).toBe(2);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
additionCall[1].input[0],
{
type: 'value',
value: 1,
offset: 0,
},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
additionCall[1].input[1],
{
type: 'value',
value: 2,
offset: 0,
},
);
});
it('sends a valid graph description for Animated.subtract nodes', () => {
const first = new Animated.Value(2);
const second = new Animated.Value(1);
first.__makeNative();
second.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.subtract(first, second)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'subtraction', input: expect.any(Array)},
);
const subtractionCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'subtraction',
);
expect(subtractionCalls.length).toBe(1);
const subtractionCall = subtractionCalls[0];
const subtractionNodeTag = subtractionCall[0];
const subtractionConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === subtractionNodeTag,
);
expect(subtractionConnectionCalls.length).toBe(2);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
subtractionCall[1].input[0],
{
type: 'value',
value: 2,
offset: 0,
},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
subtractionCall[1].input[1],
{
type: 'value',
value: 1,
offset: 0,
},
);
});
it('sends a valid graph description for Animated.multiply nodes', () => {
const first = new Animated.Value(2);
const second = new Animated.Value(1);
first.__makeNative();
second.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.multiply(first, second)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'multiplication', input: expect.any(Array)},
);
const multiplicationCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'multiplication',
);
expect(multiplicationCalls.length).toBe(1);
const multiplicationCall = multiplicationCalls[0];
const multiplicationNodeTag = multiplicationCall[0];
const multiplicationConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === multiplicationNodeTag,
);
expect(multiplicationConnectionCalls.length).toBe(2);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
multiplicationCall[1].input[0],
{
type: 'value',
value: 2,
offset: 0,
},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
multiplicationCall[1].input[1],
{
type: 'value',
value: 1,
offset: 0,
},
);
});
it('sends a valid graph description for Animated.divide nodes', () => {
const first = new Animated.Value(4);
const second = new Animated.Value(2);
first.__makeNative();
second.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.divide(first, second)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'division', input: expect.any(Array)},
);
const divisionCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'division',
);
expect(divisionCalls.length).toBe(1);
const divisionCall = divisionCalls[0];
const divisionNodeTag = divisionCall[0];
const divisionConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === divisionNodeTag,
);
expect(divisionConnectionCalls.length).toBe(2);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
divisionCall[1].input[0],
{
type: 'value',
value: 4,
offset: 0,
},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
divisionCall[1].input[1],
{
type: 'value',
value: 2,
offset: 0,
},
);
});
it('sends a valid graph description for Animated.modulo nodes', () => {
const value = new Animated.Value(4);
value.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.modulo(value, 4)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'modulus', modulus: 4, input: expect.any(Number)},
);
const moduloCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'modulus',
);
expect(moduloCalls.length).toBe(1);
const moduloCall = moduloCalls[0];
const moduloNodeTag = moduloCall[0];
const moduloConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === moduloNodeTag,
);
expect(moduloConnectionCalls.length).toBe(1);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
moduloCall[1].input,
{
type: 'value',
value: 4,
offset: 0,
},
);
});
it('sends a valid graph description for interpolate() nodes', () => {
const value = new Animated.Value(10);
value.__makeNative();
TestRenderer.create(
<Animated.View
style={{
opacity: value.interpolate({
inputRange: [10, 20],
outputRange: [0, 1],
}),
}}
/>,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 10, offset: 0},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{
type: 'interpolation',
inputRange: [10, 20],
outputRange: [0, 1],
extrapolateLeft: 'extend',
extrapolateRight: 'extend',
},
);
const interpolationNodeTag = NativeAnimatedModule.createAnimatedNode.mock.calls.find(
call => call[1].type === 'interpolation',
)[0];
const valueNodeTag = NativeAnimatedModule.createAnimatedNode.mock.calls.find(
call => call[1].type === 'value',
)[0];
expect(NativeAnimatedModule.connectAnimatedNodes).toBeCalledWith(
valueNodeTag,
interpolationNodeTag,
);
});
it('sends a valid graph description for transform nodes', () => {
const translateX = new Animated.Value(0);
translateX.__makeNative();
TestRenderer.create(
<Animated.View style={{transform: [{translateX}, {scale: 2}]}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{
type: 'transform',
transforms: [
{
nodeTag: expect.any(Number),
property: 'translateX',
type: 'animated',
},
{
value: 2,
property: 'scale',
type: 'static',
},
],
},
);
});
it('sends a valid graph description for Animated.diffClamp nodes', () => {
const value = new Animated.Value(2);
value.__makeNative();
TestRenderer.create(
<Animated.View style={{opacity: Animated.diffClamp(value, 0, 20)}} />,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'diffclamp', input: expect.any(Number), max: 20, min: 0},
);
const diffClampCalls = NativeAnimatedModule.createAnimatedNode.mock.calls.filter(
call => call[1].type === 'diffclamp',
);
expect(diffClampCalls.length).toBe(1);
const diffClampCall = diffClampCalls[0];
const diffClampNodeTag = diffClampCall[0];
const diffClampConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === diffClampNodeTag,
);
expect(diffClampConnectionCalls.length).toBe(1);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
diffClampCall[1].input,
{
type: 'value',
value: 2,
offset: 0,
},
);
});
it("doesn't call into native API if useNativeDriver is set to false", () => {
const opacity = new Animated.Value(0);
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: false,
}).start();
root.unmount();
expect(NativeAnimatedModule.createAnimatedNode).not.toBeCalled();
});
it('fails when trying to run non-native animation on native node', () => {
const opacity = new Animated.Value(0);
const ref = React.createRef(null);
TestRenderer.create(<Animated.View ref={ref} style={{opacity}} />);
// Necessary to simulate the native animation.
expect(ref.current).not.toBeNull();
ref.current.setNativeProps = jest.fn();
Animated.timing(opacity, {
toValue: 10,
duration: 50,
useNativeDriver: true,
}).start();
jest.runAllTimers();
Animated.timing(opacity, {
toValue: 4,
duration: 500,
useNativeDriver: false,
}).start();
try {
process.env.NODE_ENV = 'development';
expect(jest.runAllTimers).toThrow(
'Attempting to run JS driven animation on animated node that has ' +
'been moved to "native" earlier by starting an animation with ' +
'`useNativeDriver: true`',
);
} finally {
process.env.NODE_ENV = 'test';
}
});
it('fails for unsupported styles', () => {
const left = new Animated.Value(0);
TestRenderer.create(<Animated.View style={{left}} />);
const animation = Animated.timing(left, {
toValue: 10,
duration: 50,
useNativeDriver: true,
});
expect(animation.start).toThrowError(/left/);
});
it('works for any `static` props and styles', () => {
// Passing "unsupported" props should work just fine as long as they are not animated
const opacity = new Animated.Value(0);
opacity.__makeNative();
TestRenderer.create(
<Animated.View
removeClippedSubviews={true}
style={{left: 10, opacity, top: 20}}
/>,
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'style', style: {opacity: expect.any(Number)}},
);
expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'props', props: {style: expect.any(Number)}},
);
});
});
describe('Animations', () => {
it('sends a valid timing animation description', () => {
const anim = new Animated.Value(0);
Animated.timing(anim, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'frames',
frames: expect.any(Array),
toValue: expect.any(Number),
iterations: 1,
},
expect.any(Function),
);
});
it('sends a valid spring animation description', () => {
const anim = new Animated.Value(0);
Animated.spring(anim, {
toValue: 10,
friction: 5,
tension: 164,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'spring',
stiffness: 679.08,
damping: 16,
mass: 1,
initialVelocity: 0,
overshootClamping: false,
restDisplacementThreshold: 0.001,
restSpeedThreshold: 0.001,
toValue: 10,
iterations: 1,
},
expect.any(Function),
);
Animated.spring(anim, {
toValue: 10,
stiffness: 1000,
damping: 500,
mass: 3,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'spring',
stiffness: 1000,
damping: 500,
mass: 3,
initialVelocity: 0,
overshootClamping: false,
restDisplacementThreshold: 0.001,
restSpeedThreshold: 0.001,
toValue: 10,
iterations: 1,
},
expect.any(Function),
);
Animated.spring(anim, {
toValue: 10,
bounciness: 8,
speed: 10,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'spring',
damping: 23.05223140901191,
initialVelocity: 0,
overshootClamping: false,
restDisplacementThreshold: 0.001,
restSpeedThreshold: 0.001,
stiffness: 299.61882352941177,
mass: 1,
toValue: 10,
iterations: 1,
},
expect.any(Function),
);
});
it('sends a valid decay animation description', () => {
const anim = new Animated.Value(0);
Animated.decay(anim, {
velocity: 10,
deceleration: 0.1,
useNativeDriver: true,
}).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 1},
expect.any(Function),
);
});
it('works with Animated.loop', () => {
const anim = new Animated.Value(0);
Animated.loop(
Animated.decay(anim, {
velocity: 10,
deceleration: 0.1,
useNativeDriver: true,
}),
{iterations: 10},
).start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 10},
expect.any(Function),
);
});
it('sends stopAnimation command to native', () => {
const value = new Animated.Value(0);
const animation = Animated.timing(value, {
toValue: 10,
duration: 50,
useNativeDriver: true,
});
animation.start();
expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
type: 'frames',
frames: expect.any(Array),
toValue: expect.any(Number),
iterations: 1,
},
expect.any(Function),
);
const animationId =
NativeAnimatedModule.startAnimatingNode.mock.calls[0][0];
animation.stop();
expect(NativeAnimatedModule.stopAnimation).toBeCalledWith(animationId);
});
});
describe('Animated Components', () => {
it('Should restore default values on prop updates only', () => {
const opacity = new Animated.Value(0);
opacity.__makeNative();
const root = TestRenderer.create(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.restoreDefaultValues).not.toHaveBeenCalled();
root.update(<Animated.View style={{opacity}} />);
expect(NativeAnimatedModule.restoreDefaultValues).toHaveBeenCalledWith(
expect.any(Number),
);
root.unmount();
// Make sure it doesn't get called on unmount.
expect(NativeAnimatedModule.restoreDefaultValues).toHaveBeenCalledTimes(
1,
);
});
});
});