diff --git a/Libraries/Animated/src/__tests__/Animated-test.js b/Libraries/Animated/src/__tests__/Animated-test.js
index f119806e16c..061fd449925 100644
--- a/Libraries/Animated/src/__tests__/Animated-test.js
+++ b/Libraries/Animated/src/__tests__/Animated-test.js
@@ -95,51 +95,33 @@ describe('Animated tests', () => {
});
it('does not detach on updates', () => {
- const anim = new Animated.Value(0);
- anim.__detach = jest.fn();
+ const opacity = new Animated.Value(0);
+ opacity.__detach = jest.fn();
- const c = new Animated.View();
- c.props = {
- style: {
- opacity: anim,
- },
- };
- c.UNSAFE_componentWillMount();
+ const root = TestRenderer.create();
+ expect(opacity.__detach).not.toBeCalled();
- expect(anim.__detach).not.toBeCalled();
- c._component = {};
- c.UNSAFE_componentWillReceiveProps({
- style: {
- opacity: anim,
- },
- });
- expect(anim.__detach).not.toBeCalled();
+ root.update();
+ expect(opacity.__detach).not.toBeCalled();
- c.componentWillUnmount();
- expect(anim.__detach).toBeCalled();
+ root.unmount();
+ expect(opacity.__detach).toBeCalled();
});
it('stops animation when detached', () => {
- const anim = new Animated.Value(0);
+ const opacity = new Animated.Value(0);
const callback = jest.fn();
- const c = new Animated.View();
- c.props = {
- style: {
- opacity: anim,
- },
- };
- c.UNSAFE_componentWillMount();
+ const root = TestRenderer.create();
- Animated.timing(anim, {
+ Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: false,
}).start(callback);
- c._component = {};
- c.componentWillUnmount();
- expect(callback).toBeCalledWith({finished: false});
+ root.unmount();
+
expect(callback).toBeCalledWith({finished: false});
});
@@ -198,7 +180,7 @@ describe('Animated tests', () => {
,
);
- expect(testRenderer.toJSON()).toMatchSnapshot();
+ expect(testRenderer.toJSON().props.style.opacity).toEqual(0);
Animated.timing(opacity, {
toValue: 1,
@@ -206,7 +188,7 @@ describe('Animated tests', () => {
useNativeDriver: false,
}).start();
- expect(testRenderer.toJSON()).toMatchSnapshot();
+ expect(testRenderer.toJSON().props.style.opacity).toEqual(1);
});
it('warns if `useNativeDriver` is missing', () => {
diff --git a/Libraries/Animated/src/__tests__/AnimatedNative-test.js b/Libraries/Animated/src/__tests__/AnimatedNative-test.js
index 09b087ce5d5..b6e34047f6c 100644
--- a/Libraries/Animated/src/__tests__/AnimatedNative-test.js
+++ b/Libraries/Animated/src/__tests__/AnimatedNative-test.js
@@ -10,18 +10,8 @@
'use strict';
-const ClassComponentMock = class {};
-ClassComponentMock.prototype.isReactComponent = true;
-
jest
.clearAllMocks()
- .setMock('../../../Text/Text', ClassComponentMock)
- .setMock('../../../Components/View/View', ClassComponentMock)
- .setMock('../../../Image/Image', ClassComponentMock)
- .setMock('../../../Components/ScrollView/ScrollView', ClassComponentMock)
- .setMock('../../../Lists/FlatList', ClassComponentMock)
- .setMock('../../../Lists/SectionList', ClassComponentMock)
- .setMock('react', {Component: class {}})
.mock('../../../BatchedBridge/NativeModules', () => ({
NativeAnimatedModule: {},
PlatformConstants: {
@@ -35,128 +25,107 @@ jest
// 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');
-function createAndMountComponent(ComponentClass, props) {
- const component = new ComponentClass();
- component.props = props;
- component.UNSAFE_componentWillMount();
- // Simulate that refs were set.
- component._component = {};
- component.componentDidMount();
- return component;
-}
-
describe('Native Animated', () => {
- const nativeAnimatedModule = require('../NativeAnimatedModule').default;
+ const NativeAnimatedModule = require('../NativeAnimatedModule').default;
beforeEach(() => {
- nativeAnimatedModule.addAnimatedEventToView = jest.fn();
- nativeAnimatedModule.connectAnimatedNodes = jest.fn();
- nativeAnimatedModule.connectAnimatedNodeToView = jest.fn();
- nativeAnimatedModule.createAnimatedNode = jest.fn();
- nativeAnimatedModule.disconnectAnimatedNodeFromView = jest.fn();
- nativeAnimatedModule.disconnectAnimatedNodes = jest.fn();
- nativeAnimatedModule.dropAnimatedNode = jest.fn();
- nativeAnimatedModule.extractAnimatedNodeOffset = jest.fn();
- nativeAnimatedModule.flattenAnimatedNodeOffset = jest.fn();
- nativeAnimatedModule.removeAnimatedEventFromView = jest.fn();
- nativeAnimatedModule.setAnimatedNodeOffset = jest.fn();
- nativeAnimatedModule.setAnimatedNodeValue = jest.fn();
- nativeAnimatedModule.startAnimatingNode = jest.fn();
- nativeAnimatedModule.startListeningToAnimatedNodeValue = jest.fn();
- nativeAnimatedModule.stopAnimation = jest.fn();
- nativeAnimatedModule.stopListeningToAnimatedNodeValue = jest.fn();
+ Object.assign(NativeAnimatedModule, {
+ 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(),
+ 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 anim = new Animated.Value(0);
- Animated.timing(anim, {
+ const opacity = new Animated.Value(0);
+ const ref = React.createRef(null);
+
+ Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
- const c = createAndMountComponent(Animated.View, {
- style: {
- opacity: anim,
- },
- });
+ TestRenderer.create();
- // We expect `setValue` not to propagate down to `setNativeProps`, otherwise it may try to access `setNativeProps`
- // via component refs table that we override here.
- c.refs = {
- node: {
- setNativeProps: jest.fn(),
- },
- };
+ expect(ref.current).not.toBeNull();
+ jest.spyOn(ref.current, 'setNativeProps');
- anim.setValue(0.5);
+ opacity.setValue(0.5);
- expect(nativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(
+ expect(NativeAnimatedModule.setAnimatedNodeValue).toBeCalledWith(
expect.any(Number),
0.5,
);
- expect(c.refs.node.setNativeProps).not.toHaveBeenCalled();
+ expect(ref.current.setNativeProps).not.toHaveBeenCalled();
});
it('should set offset', () => {
- const anim = new Animated.Value(0);
- anim.setOffset(10);
- anim.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: anim,
- },
- });
+ const opacity = new Animated.Value(0);
+ opacity.setOffset(10);
+ opacity.__makeNative();
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ TestRenderer.create();
+
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 10},
);
- anim.setOffset(20);
- expect(nativeAnimatedModule.setAnimatedNodeOffset).toBeCalledWith(
+ opacity.setOffset(20);
+ expect(NativeAnimatedModule.setAnimatedNodeOffset).toBeCalledWith(
expect.any(Number),
20,
);
});
it('should flatten offset', () => {
- const anim = new Animated.Value(0);
- anim.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: anim,
- },
- });
+ const opacity = new Animated.Value(0);
+ opacity.__makeNative();
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ TestRenderer.create();
+
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 0},
);
- anim.flattenOffset();
- expect(nativeAnimatedModule.flattenAnimatedNodeOffset).toBeCalledWith(
+ opacity.flattenOffset();
+ expect(NativeAnimatedModule.flattenAnimatedNodeOffset).toBeCalledWith(
expect.any(Number),
);
});
it('should extract offset', () => {
- const anim = new Animated.Value(0);
- anim.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: anim,
- },
- });
+ const opacity = new Animated.Value(0);
+ opacity.__makeNative();
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ TestRenderer.create();
+
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 0},
);
- anim.extractOffset();
- expect(nativeAnimatedModule.extractAnimatedNodeOffset).toBeCalledWith(
+ opacity.extractOffset();
+ expect(NativeAnimatedModule.extractAnimatedNodeOffset).toBeCalledWith(
expect.any(Number),
);
});
@@ -169,7 +138,7 @@ describe('Native Animated', () => {
const listener = jest.fn();
const id = value1.addListener(listener);
expect(
- nativeAnimatedModule.startListeningToAnimatedNodeValue,
+ NativeAnimatedModule.startListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
@@ -190,7 +159,7 @@ describe('Native Animated', () => {
value1.removeListener(id);
expect(
- nativeAnimatedModule.stopListeningToAnimatedNodeValue,
+ NativeAnimatedModule.stopListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
@@ -207,7 +176,7 @@ describe('Native Animated', () => {
const listener = jest.fn();
[1, 2, 3, 4].forEach(() => value1.addListener(listener));
expect(
- nativeAnimatedModule.startListeningToAnimatedNodeValue,
+ NativeAnimatedModule.startListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
@@ -219,7 +188,7 @@ describe('Native Animated', () => {
value1.removeAllListeners();
expect(
- nativeAnimatedModule.stopListeningToAnimatedNodeValue,
+ NativeAnimatedModule.stopListeningToAnimatedNodeValue,
).toHaveBeenCalledWith(value1.__getNativeTag());
NativeAnimatedHelper.nativeEventEmitter.emit('onAnimatedValueUpdate', {
@@ -237,8 +206,9 @@ describe('Native Animated', () => {
const event = Animated.event([{nativeEvent: {state: {foo: value}}}], {
useNativeDriver: true,
});
- const c = createAndMountComponent(Animated.View, {onTouchMove: event});
- expect(nativeAnimatedModule.addAnimatedEventToView).toBeCalledWith(
+
+ const root = TestRenderer.create();
+ expect(NativeAnimatedModule.addAnimatedEventToView).toBeCalledWith(
expect.any(Number),
'onTouchMove',
{
@@ -247,8 +217,11 @@ describe('Native Animated', () => {
},
);
- c.componentWillUnmount();
- expect(nativeAnimatedModule.removeAnimatedEventFromView).toBeCalledWith(
+ expect(
+ NativeAnimatedModule.removeAnimatedEventFromView,
+ ).not.toHaveBeenCalled();
+ root.unmount();
+ expect(NativeAnimatedModule.removeAnimatedEventFromView).toBeCalledWith(
expect.any(Number),
'onTouchMove',
value.__getNativeTag(),
@@ -261,10 +234,20 @@ describe('Native Animated', () => {
const event = Animated.event([{notNativeEvent: {foo: value}}], {
useNativeDriver: true,
});
- expect(() =>
- createAndMountComponent(Animated.View, {onTouchMove: event}),
- ).toThrowError(/nativeEvent/);
- expect(nativeAnimatedModule.addAnimatedEventToView).not.toBeCalled();
+
+ jest.spyOn(console, 'error').mockImplementationOnce((...args) => {
+ if (args[0].startsWith('The above error occurred in the')) {
+ return;
+ }
+ console.errorDebug(...args);
+ });
+
+ expect(() => {
+ TestRenderer.create();
+ }).toThrowError(/nativeEvent/);
+ expect(NativeAnimatedModule.addAnimatedEventToView).not.toBeCalled();
+
+ console.error.mockRestore();
});
it('should call listeners', () => {
@@ -284,27 +267,21 @@ describe('Native Animated', () => {
describe('Animated Graph', () => {
it('creates and detaches nodes', () => {
- const anim = new Animated.Value(0);
- const c = createAndMountComponent(Animated.View, {
- style: {
- opacity: anim,
- },
- });
+ const opacity = new Animated.Value(0);
+ const root = TestRenderer.create();
- Animated.timing(anim, {
+ Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
- c.componentWillUnmount();
-
- expect(nativeAnimatedModule.createAnimatedNode).toHaveBeenCalledTimes(3);
- expect(nativeAnimatedModule.connectAnimatedNodes).toHaveBeenCalledTimes(
+ expect(NativeAnimatedModule.createAnimatedNode).toHaveBeenCalledTimes(3);
+ expect(NativeAnimatedModule.connectAnimatedNodes).toHaveBeenCalledTimes(
2,
);
- expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
+ expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
@@ -317,34 +294,37 @@ describe('Native Animated', () => {
);
expect(
- nativeAnimatedModule.disconnectAnimatedNodes,
+ NativeAnimatedModule.disconnectAnimatedNodes,
+ ).not.toHaveBeenCalled();
+ expect(NativeAnimatedModule.dropAnimatedNode).not.toHaveBeenCalled();
+
+ root.unmount();
+
+ expect(
+ NativeAnimatedModule.disconnectAnimatedNodes,
).toHaveBeenCalledTimes(2);
- expect(nativeAnimatedModule.dropAnimatedNode).toHaveBeenCalledTimes(3);
+ expect(NativeAnimatedModule.dropAnimatedNode).toHaveBeenCalledTimes(3);
});
it('sends a valid description for value, style and props nodes', () => {
- const anim = new Animated.Value(0);
- createAndMountComponent(Animated.View, {
- style: {
- opacity: anim,
- },
- });
+ const opacity = new Animated.Value(0);
+ TestRenderer.create();
- Animated.timing(anim, {
+ Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: true,
}).start();
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 0, offset: 0},
);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'style', style: {opacity: expect.any(Number)}},
);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'props', props: {style: expect.any(Number)}},
);
@@ -356,31 +336,29 @@ describe('Native Animated', () => {
first.__makeNative();
second.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: Animated.add(first, second),
- },
- });
+ TestRenderer.create(
+ ,
+ );
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'addition', input: expect.any(Array)},
);
- const additionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
+ 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(
+ const additionConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === additionNodeTag,
);
expect(additionConnectionCalls.length).toBe(2);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
additionCall[1].input[0],
{type: 'value', value: 1, offset: 0},
);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
additionCall[1].input[1],
{type: 'value', value: 2, offset: 0},
);
@@ -392,31 +370,29 @@ describe('Native Animated', () => {
first.__makeNative();
second.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: Animated.subtract(first, second),
- },
- });
+ TestRenderer.create(
+ ,
+ );
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'subtraction', input: expect.any(Array)},
);
- const subtractionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
+ 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(
+ const subtractionConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === subtractionNodeTag,
);
expect(subtractionConnectionCalls.length).toBe(2);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
subtractionCall[1].input[0],
{type: 'value', value: 2, offset: 0},
);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
subtractionCall[1].input[1],
{type: 'value', value: 1, offset: 0},
);
@@ -428,31 +404,29 @@ describe('Native Animated', () => {
first.__makeNative();
second.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: Animated.multiply(first, second),
- },
- });
+ TestRenderer.create(
+ ,
+ );
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'multiplication', input: expect.any(Array)},
);
- const multiplicationCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
+ 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(
+ const multiplicationConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === multiplicationNodeTag,
);
expect(multiplicationConnectionCalls.length).toBe(2);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
multiplicationCall[1].input[0],
{type: 'value', value: 2, offset: 0},
);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
multiplicationCall[1].input[1],
{type: 'value', value: 1, offset: 0},
);
@@ -464,31 +438,29 @@ describe('Native Animated', () => {
first.__makeNative();
second.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: Animated.divide(first, second),
- },
- });
+ TestRenderer.create(
+ ,
+ );
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'division', input: expect.any(Array)},
);
- const divisionCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
+ 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(
+ const divisionConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === divisionNodeTag,
);
expect(divisionConnectionCalls.length).toBe(2);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
divisionCall[1].input[0],
{type: 'value', value: 4, offset: 0},
);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
divisionCall[1].input[1],
{type: 'value', value: 2, offset: 0},
);
@@ -498,27 +470,25 @@ describe('Native Animated', () => {
const value = new Animated.Value(4);
value.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: Animated.modulo(value, 4),
- },
- });
+ TestRenderer.create(
+ ,
+ );
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'modulus', modulus: 4, input: expect.any(Number)},
);
- const moduloCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
+ 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(
+ const moduloConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === moduloNodeTag,
);
expect(moduloConnectionCalls.length).toBe(1);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
moduloCall[1].input,
{type: 'value', value: 4, offset: 0},
);
@@ -528,20 +498,22 @@ describe('Native Animated', () => {
const value = new Animated.Value(10);
value.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: value.interpolate({
- inputRange: [10, 20],
- outputRange: [0, 1],
- }),
- },
- });
+ TestRenderer.create(
+ ,
+ );
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'value', value: 10, offset: 0},
);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{
type: 'interpolation',
@@ -551,29 +523,27 @@ describe('Native Animated', () => {
extrapolateRight: 'extend',
},
);
- const interpolationNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find(
+ const interpolationNodeTag = NativeAnimatedModule.createAnimatedNode.mock.calls.find(
call => call[1].type === 'interpolation',
)[0];
- const valueNodeTag = nativeAnimatedModule.createAnimatedNode.mock.calls.find(
+ const valueNodeTag = NativeAnimatedModule.createAnimatedNode.mock.calls.find(
call => call[1].type === 'value',
)[0];
- expect(nativeAnimatedModule.connectAnimatedNodes).toBeCalledWith(
+ expect(NativeAnimatedModule.connectAnimatedNodes).toBeCalledWith(
valueNodeTag,
interpolationNodeTag,
);
});
it('sends a valid graph description for transform nodes', () => {
- const value = new Animated.Value(0);
- value.__makeNative();
+ const translateX = new Animated.Value(0);
+ translateX.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- transform: [{translateX: value}, {scale: 2}],
- },
- });
+ TestRenderer.create(
+ ,
+ );
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{
type: 'transform',
@@ -597,86 +567,86 @@ describe('Native Animated', () => {
const value = new Animated.Value(2);
value.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- opacity: Animated.diffClamp(value, 0, 20),
- },
- });
+ TestRenderer.create(
+ ,
+ );
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'diffclamp', input: expect.any(Number), max: 20, min: 0},
);
- const diffClampCalls = nativeAnimatedModule.createAnimatedNode.mock.calls.filter(
+ 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(
+ const diffClampConnectionCalls = NativeAnimatedModule.connectAnimatedNodes.mock.calls.filter(
call => call[1] === diffClampNodeTag,
);
expect(diffClampConnectionCalls.length).toBe(1);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ 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 anim = new Animated.Value(0);
+ const opacity = new Animated.Value(0);
- const c = createAndMountComponent(Animated.View, {
- style: {
- opacity: anim,
- },
- });
+ const root = TestRenderer.create();
- Animated.timing(anim, {
+ Animated.timing(opacity, {
toValue: 10,
duration: 1000,
useNativeDriver: false,
}).start();
- c.componentWillUnmount();
+ root.unmount();
- expect(nativeAnimatedModule.createAnimatedNode).not.toBeCalled();
+ expect(NativeAnimatedModule.createAnimatedNode).not.toBeCalled();
});
it('fails when trying to run non-native animation on native node', () => {
- const anim = new Animated.Value(0);
+ const opacity = new Animated.Value(0);
+ const ref = React.createRef(null);
- createAndMountComponent(Animated.View, {
- style: {
- opacity: anim,
- },
- });
+ TestRenderer.create();
- Animated.timing(anim, {
+ // 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(anim, {
+ Animated.timing(opacity, {
toValue: 4,
duration: 500,
useNativeDriver: false,
}).start();
- expect(jest.runAllTimers).toThrow();
+ 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 anim = new Animated.Value(0);
+ const left = new Animated.Value(0);
- createAndMountComponent(Animated.View, {
- style: {
- left: anim,
- },
- });
+ TestRenderer.create();
- const animation = Animated.timing(anim, {
+ const animation = Animated.timing(left, {
toValue: 10,
duration: 50,
useNativeDriver: true,
@@ -686,23 +656,21 @@ describe('Native Animated', () => {
it('works for any `static` props and styles', () => {
// Passing "unsupported" props should work just fine as long as they are not animated
- const value = new Animated.Value(0);
- value.__makeNative();
+ const opacity = new Animated.Value(0);
+ opacity.__makeNative();
- createAndMountComponent(Animated.View, {
- style: {
- left: 10,
- top: 20,
- opacity: value,
- },
- removeClippedSubviews: true,
- });
+ TestRenderer.create(
+ ,
+ );
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'style', style: {opacity: expect.any(Number)}},
);
- expect(nativeAnimatedModule.createAnimatedNode).toBeCalledWith(
+ expect(NativeAnimatedModule.createAnimatedNode).toBeCalledWith(
expect.any(Number),
{type: 'props', props: {style: expect.any(Number)}},
);
@@ -718,7 +686,7 @@ describe('Native Animated', () => {
useNativeDriver: true,
}).start();
- expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
+ expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
@@ -739,7 +707,7 @@ describe('Native Animated', () => {
tension: 164,
useNativeDriver: true,
}).start();
- expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
+ expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
@@ -764,7 +732,7 @@ describe('Native Animated', () => {
mass: 3,
useNativeDriver: true,
}).start();
- expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
+ expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
@@ -788,7 +756,7 @@ describe('Native Animated', () => {
speed: 10,
useNativeDriver: true,
}).start();
- expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
+ expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
@@ -815,7 +783,7 @@ describe('Native Animated', () => {
useNativeDriver: true,
}).start();
- expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
+ expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 1},
@@ -834,7 +802,7 @@ describe('Native Animated', () => {
{iterations: 10},
).start();
- expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
+ expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{type: 'decay', deceleration: 0.1, velocity: 10, iterations: 10},
@@ -851,7 +819,7 @@ describe('Native Animated', () => {
});
animation.start();
- expect(nativeAnimatedModule.startAnimatingNode).toBeCalledWith(
+ expect(NativeAnimatedModule.startAnimatingNode).toBeCalledWith(
expect.any(Number),
expect.any(Number),
{
@@ -863,10 +831,10 @@ describe('Native Animated', () => {
expect.any(Function),
);
const animationId =
- nativeAnimatedModule.startAnimatingNode.mock.calls[0][0];
+ NativeAnimatedModule.startAnimatingNode.mock.calls[0][0];
animation.stop();
- expect(nativeAnimatedModule.stopAnimation).toBeCalledWith(animationId);
+ expect(NativeAnimatedModule.stopAnimation).toBeCalledWith(animationId);
});
});
});
diff --git a/Libraries/Animated/src/__tests__/__snapshots__/Animated-test.js.snap b/Libraries/Animated/src/__tests__/__snapshots__/Animated-test.js.snap
deleted file mode 100644
index 721292188b3..00000000000
--- a/Libraries/Animated/src/__tests__/__snapshots__/Animated-test.js.snap
+++ /dev/null
@@ -1,21 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`Animated tests Animated bypasses \`setNativeProps\` in test environments 1`] = `
-
-`;
-
-exports[`Animated tests Animated bypasses \`setNativeProps\` in test environments 2`] = `
-
-`;
diff --git a/Libraries/Animated/src/createAnimatedComponent.js b/Libraries/Animated/src/createAnimatedComponent.js
index 8401f63c806..4c0dc34c0f4 100644
--- a/Libraries/Animated/src/createAnimatedComponent.js
+++ b/Libraries/Animated/src/createAnimatedComponent.js
@@ -15,6 +15,7 @@ const AnimatedProps = require('./nodes/AnimatedProps');
const React = require('react');
const invariant = require('invariant');
+const setAndForwardRef = require('../../Utilities/setAndForwardRef');
export type AnimatedComponentType = React.AbstractComponent<
any,
@@ -116,20 +117,26 @@ function createAnimatedComponent(
oldPropsAnimated && oldPropsAnimated.__detach();
}
- _setComponentRef = c => {
- this._prevComponent = this._component;
- this._component = c;
- };
+ _setComponentRef = setAndForwardRef({
+ getForwardedRef: () => this.props.forwardedRef,
+ setLocalRef: ref => {
+ this._prevComponent = this._component;
+ this._component = ref;
- // A third party library can use getNode()
- // to get the node reference of the decorated component
- getNode() {
- return this._component;
- }
-
- setNativeProps(props) {
- this._component.setNativeProps(props);
- }
+ // TODO: Delete this in a future release.
+ if (ref != null && ref.getNode == null) {
+ ref.getNode = () => {
+ console.warn(
+ '%s: Calling `getNode()` on the ref of an Animated component ' +
+ 'is no longer necessary. You can now directly use the ref ' +
+ 'instead. This method will be removed in a future release.',
+ ref.constructor.name ?? '<>',
+ );
+ return ref;
+ };
+ }
+ },
+ });
render() {
const props = this._propsAnimated.__getValue();
@@ -182,7 +189,14 @@ function createAnimatedComponent(
}
}
- return AnimatedComponent;
+ return React.forwardRef(function AnimatedComponentWrapper(props, ref) {
+ return (
+
+ );
+ });
}
module.exports = createAnimatedComponent;
diff --git a/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorSourceMapStatus-test.js.snap b/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorSourceMapStatus-test.js.snap
index 1877e27ac7c..dcb78ed5130 100644
--- a/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorSourceMapStatus-test.js.snap
+++ b/Libraries/LogBox/UI/__tests__/__snapshots__/LogBoxInspectorSourceMapStatus-test.js.snap
@@ -27,7 +27,7 @@ exports[`LogBoxInspectorSourceMapStatus should render for failed 1`] = `
}
}
>
-
- {
}
}
+function TouchableNativeMethodChecker<
+ T: React.AbstractComponent,
+>(props: {|Component: T, name: string|}): React.Node {
+ const [status, setStatus] = useState(null);
+ const ref = useRef>(null);
+
+ useEffect(() => {
+ setStatus(ref.current != null && typeof ref.current.measure === 'function');
+ }, []);
+
+ return (
+
+
+
+
+
+ {props.name + ': '}
+ {status == null
+ ? 'Missing Ref!'
+ : status === true
+ ? 'Native Methods Exist'
+ : 'Native Methods Missing!'}
+
+
+ );
+}
+
+function TouchableNativeMethods() {
+ return (
+
+
+
+
+ );
+}
+
class TouchableDisabled extends React.Component<{}> {
render() {
return (
@@ -551,6 +595,13 @@ exports.examples = [
return ;
},
},
+ {
+ title: 'Touchable Native Methods',
+ description: ('Some components expose native methods like `measure`.': string),
+ render: function(): React.Element {
+ return ;
+ },
+ },
{
title: 'Disabled Touchable*',
description: (' components accept disabled prop which prevents ' +