The following line of code is dangerous in a jest environment:
https://github.com/kmagiera/react-native-reanimated/blob/3f9f5cca3bd7867cf9e17147f5e9200567f0cdda/src/ReanimatedEventEmitter.js#L4
I upgraded react-navigation-tabs to a version that started including this library.
Immediately any of my unit tests that happened to import my navigation stack triggered crashes even though I wasn't actually using react-navigation-tabs in my tests.
Callstack
at invariant (node_modules/react-native/node_modules/fbjs/lib/invariant.js:40:15)
at new NativeEventEmitter (node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter.js:36:36)
at Object.<anonymous> (node_modules/react-native-reanimated/src/ReanimatedEventEmitter.js:4:1)
at Object.<anonymous> (node_modules/react-native-reanimated/src/core/AnimatedCall.js:1:824)
at Object.<anonymous> (node_modules/react-native-reanimated/src/base.js:8:44)
at Object.<anonymous> (node_modules/react-native-reanimated/src/Easing.js:1:411)
at Object.<anonymous> (node_modules/react-native-reanimated/src/Animated.js:2:38)
at Object.<anonymous> (node_modules/react-navigation-tabs/src/views/BottomTabBar.js:11:53)
In a jest environment, obviously NativeModules has nothing in it.
If we change the EventEmitter to lazily create on first access, this would be safer, possibly?
export default {
get emitter() {
delete this.emitter;
return this.emitter = new NativeEventEmitter(ReanimatedModule);
}
}
Thoughts?
I would love any advice you could provide to workaround this issue!
This would also require fixes to avoid global-scope touching of the NativeModule here:
https://github.com/kmagiera/react-native-reanimated/blob/3f9f5cca3bd7867cf9e17147f5e9200567f0cdda/src/ConfigHelper.js#L106-L126
I have similar issue, I am using rn-gesture-handler & reanimated.
And when running Jest test, I get error:
Test suite failed to run
Invariant Violation: Native module cannot be null.
at invariant (node_modules/react-native/node_modules/fbjs/lib/invariant.js:40:15)
at new NativeEventEmitter (node_modules/react-native/Libraries/EventEmitter/NativeEventEmitter.js:36:36)
at Object.<anonymous> (node_modules/react-native-reanimated/src/ReanimatedEventEmitter.js:4:1)
at Object.<anonymous> (node_modules/react-native-reanimated/src/core/AnimatedCall.js:1:181)
I have successfully mocked rn-gesture-handler with just:
jest.mock("react-native-gesture-handler", () => {});
But I cannot figure out how to mock reanimated properly.
I have tried to mock it like this:
jest.mock("react-native-reanimated", () => {
return {
Value: jest.fn(() => 0),
event: jest.fn(),
add: jest.fn(() => 0),
eq: jest.fn(() => true),
set: jest.fn(() => 0),
cond: jest.fn(),
interpolate: jest.fn(() => {}),
Extrapolate: { CLAMP: jest.fn() }
};
});
But then I get error saying:
```
Invariant Violation: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component fro
m the file it's defined in, or you might have mixed up default and named imports.
Check the render method of `BottomDrawer`.
at invariant (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:55:19)
at createFiberFromTypeAndProps (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2015:15)
at createFiberFromElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:2036:19)
at reconcileSingleElement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5980:27)
at reconcileChildFibers (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6037:39)
at reconcileChildren (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6404:32)
at updateHostComponent (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6749:7)
at beginWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7413:18)
at performUnitOfWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:10149:16)
at workLoop (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:10181:28
)
```
I have managed to mock it successfully 馃榿
The problem with mock from above is that I forgot to mock View. Working mock looks like:
import { View } from "react-native";
jest.mock("react-native-reanimated", () => {
return {
Value: jest.fn(),
event: jest.fn(),
add: jest.fn(),
eq: jest.fn(),
set: jest.fn(),
cond: jest.fn(),
interpolate: jest.fn(),
View: View,
Extrapolate: { CLAMP: jest.fn() }
};
});
I need to mock Transition for `react-navigation-animated-switch' so my version like this:
jest.mock('react-native-reanimated', () => {
const View = require('react-native').View;
return {
Value: jest.fn(),
event: jest.fn(),
add: jest.fn(),
eq: jest.fn(),
set: jest.fn(),
cond: jest.fn(),
interpolate: jest.fn(),
View: View,
Extrapolate: { CLAMP: jest.fn() },
Transition: {
Together: 'Together',
Out: 'Out',
In: 'In',
},
};
});
Hi @mralj and @EricMcRay I get the following error after using your mock solution:
Is this part of <Transition.Out> component?
Just for test I have added <Transition.Out> to my render function and got same error as you, but @EricMcRay solution worked for me (ie appending this to my code helped):
Transition: {
Out: 'Out',
},
So in my case whole thing now looks like:
jest.mock("react-native-reanimated", () => {
return {
Value: jest.fn(),
event: jest.fn(),
add: jest.fn(),
eq: jest.fn(),
set: jest.fn(),
cond: jest.fn(),
interpolate: jest.fn(),
View: mockView,
Extrapolate: { CLAMP: jest.fn() },
Clock: jest.fn(),
greaterThan: jest.fn(),
lessThan: jest.fn(),
startClock: jest.fn(),
stopClock: jest.fn(),
clockRunning: jest.fn(),
not: jest.fn(),
or: jest.fn(),
and: jest.fn(),
spring: jest.fn(),
decay: jest.fn(),
defined: jest.fn(),
call: jest.fn(),
Code: mockView,
block: jest.fn(),
abs: jest.fn(),
greaterOrEq: jest.fn(),
lessOrEq: jest.fn(),
debug: jest.fn(),
Transition: {
Out: "Out"
}
};
});
Cool. Thanks @mralj. I had it resolved by using react-navigation/tabs#128. There you'll find an indepth solution by @Satya164 at #276
If anyone has problems with Jest and Transitioning.View, I have solved it this way
import { View as mockView } from "react-native";
const React = require("React");
class MockedTransitioningView extends React.Component {
animateNextTransition() {}
render() {
return <mockView {...this.props}>{this.props.children}</mockView>;
}
}
and then:
jest.mock("react-native-reanimated", () => {
return {
...
Transitioning: {
View: MockedTransitioningView
}
};
});
It would be great if the necessary mocking was either automatically hooked up for Jest, or at least if there was a source-file or function we could execute to hook it up.
I was originally able to solve this by mocking NativeEventEmitter, but today we're upgrading to react-native 0.62, and I had to re-examine this ticket & thread.
For me, it looks like we needed @mralj's answer too: https://github.com/software-mansion/react-native-reanimated/issues/205#issuecomment-492689486
I feel foolish for not figuring this out before now, but the following lines of code in my beforeAll.js setup script were all that was required (as of the current version of the library:
jest.mock("react-native-reanimated", () =>
jest.requireActual("../../node_modules/react-native-reanimated/mock"),
);
Maybe we should add this line to the README or docs somewhere?
Most helpful comment
I feel foolish for not figuring this out before now, but the following lines of code in my
beforeAll.jssetup script were all that was required (as of the current version of the library:Maybe we should add this line to the README or docs somewhere?