How can I write a jest unit test for a react native component which contains a NavigationEvents subcomponent.
I've tried the solutions offered in questions here and here without success.
The error I am getting is
console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9215
The above error occurred in the <Context.Consumer> component:
in withNavigation(NavigationEvents) (created by MyComponent)
in View (created by View)
in View (created by MyComponent)
in MyComponent
Consider adding an error boundary to your tree to customize error handling behavior.
● MyComponent test › renders
Invariant Violation: withNavigation can only be used on a view hierarchy of a navigator. The wrapped component is unable to get access to navigation from props or context.
Below is a minimal example that causes the error.
import 'react-native';
import { View } from 'react-native';
import { NavigationEvents } from 'react-navigation';
import React from 'react';
import renderer from 'react-test-renderer';
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<View>
<NavigationEvents/>
</View>
);}
}
describe('MyComponent test', () => {
it('renders', () => {
jest.mock('react-navigation', () =>({
NavigationEvents: 'mockNavigationEvents',
withNavigation: component => component
}));
const navigation = { navigate: jest.fn() };
renderer.create(<MyComponent navigation={navigation}/>);
});
});
Edit
As requested in comments added package.json below:
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"eject": "expo eject",
"test": "node ./node_modules/jest/bin/jest.js --watchAll"
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"#expo/samples": "2.1.1",
"#expo/vector-icons": "^10.0.0",
"expo": "^35.0.0",
"expo-camera": "~7.0.0",
"expo-sqlite": "^7.0.0",
"moment": "^2.24.0",
"react": "16.8.3",
"react-native": "https://github.com/expo/react-native/archive/sdk-35.0.0.tar.gz",
"react-native-action-button": "^2.8.5",
"react-native-calendar-picker": "^6.0.0",
"react-native-dialog": "^5.6.0",
"react-native-dotenv": "^0.2.0",
"react-native-dynamic-search-bar": "^0.1.11",
"react-native-elements": "^1.1.0",
"react-native-gesture-handler": "~1.3.0",
"react-native-keyboard-aware-scroll-view": "^0.9.1",
"react-native-modal-datetime-picker": "^7.4.0",
"react-native-paper": "^2.15.2",
"react-native-progress-circle": "^2.1.0",
"react-native-search-bar": "^3.4.2",
"react-native-vector-icons": "^6.4.2",
"react-navigation": "^3.0.9",
"expo-constants": "~7.0.0",
"expo-file-system": "~7.0.0"
},
"devDependencies": {
"babel-preset-expo": "^7.0.0",
"jest-expo": "^35.0.0"
},
"private": true
}
expo-cli version
$ expo-cli -V
3.13.1
The problem is that jest.mock has to be outside of describe and it functions. You don't need to manually mock navigation object since you're mocking the withNavigation HOC function:
// Setup mocks outside describe and it functions
jest.mock('react-navigation', () =>({
NavigationEvents: 'mockNavigationEvents',
withNavigation: component => component
}));
class MyComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<View>
<NavigationEvents/>
</View>
);
}
}
describe('MyComponent test', () => {
it('renders', () => {
renderer.create(<MyComponent/>);
});
});
And the renders test passes:
I suggest you have all those mocks inside a file and set setupFilesAfterEnv in package.json:
package.json > jest:
"jest": {
"preset": "jest-expo"
"setupFilesAfterEnv": ["./path/to/jestInit.js"]
},
jestInit.js:
jest.mock('react-navigation', () =>({
NavigationEvents: 'mockNavigationEvents',
withNavigation: component => component
}));
This way, you'll have those mocks for every Jest test file without having to include the mocks inside each test file.
Related
I have installed react-router-native in my project using npm and I am getting an error saying:
E:/myapp/node_modules/react-router-native/NativeRouter.js,
11:9 Module parse failed: Unexpected token (11:9).... You may need an
appropriate loader to handle this file type, currently no loaders are
configured to process this file. See
https://webpack.js.org/concepts#loaders
package.json :
{
"main": "node_modules/expo/AppEntry.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"eject": "expo eject"
},
"dependencies": {
"#material-ui/core": "^4.11.2",
"expo": "~40.0.0",
"expo-status-bar": "~1.0.3",
"firebase": "^8.2.0",
"react": "16.13.1",
"react-dom": "16.13.1",
"react-native": "https://github.com/expo/react-native/archive/sdk-40.0.1.tar.gz",
"react-native-vector-icons": "^7.1.0",
"react-native-web": "~0.13.12",
"react-router-native": "^5.2.0"
},
"devDependencies": {
"#babel/core": "~7.9.0"
},
"private": true
}
The file where react-router-native is giving an error (NativeRouter.js):
import React from "react";
import { Alert } from "react-native";
import { MemoryRouter } from "react-router";
import PropTypes from "prop-types";
/**
* The public API for a <Router> designed for React Native. Gets
* user confirmations via Alert by default.
*/
function NativeRouter(props) {
return <MemoryRouter {...props} />;
}
NativeRouter.defaultProps = {
getUserConfirmation: (message, callback) => {
Alert.alert("Confirm", message, [
{ text: "Cancel", onPress: () => callback(false) },
{ text: "OK", onPress: () => callback(true) }
]);
}
};
const __DEV__ = true; // TODO
if (__DEV__) {
NativeRouter.propTypes = {
initialEntries: PropTypes.array,
initialIndex: PropTypes.number,
getUserConfirmation: PropTypes.func,
keyLength: PropTypes.number,
children: PropTypes.node
};
}
export default NativeRouter;
In a simple setup to get going with testing React components with Jest, I'm getting the error "TypeError: Cannot call a class as a function" on each test run, with npm test
The actual React page builds fine and renders with no errors.
I've looked at the other posts related to this error, but none that can help with this issue seem to be specific to Jest.
Thanks to anyone that can help.
Here's my setup:
package.json
{
"name": "test-bed",
"main": "index.js",
"dependencies": {
"react": "^16.10.2",
"react-dom": "^16.10.2"
},
"devDependencies": {
"babel-jest": "^24.9.0",
"jest": "^24.9.0",
"react-test-renderer": "^16.10.2",
"#babel/core": "^7.1.2",
"#babel/plugin-proposal-class-properties": "^7.1.0",
"#babel/preset-env": "^7.1.0",
"#babel/preset-react": "^7.0.0",
"babel-loader": "^8.0.4",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"html-webpack-plugin": "3.2.0",
"webpack": "4.22.0",
"webpack-cli": "3.1.2",
"webpack-dev-server": "3.1.10"
},
"scripts": {
"start": "webpack-dev-server --mode development",
"build": "webpack --mode production",
"test": "jest"
}
}
src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import TestComponent from './components/TestComponent';
ReactDOM.render(<TestComponent />, document.getElementById('app'));
src/components/TestComponent.js
export default class TestComponent extends React.Component {
render() {
return (
<p>I'm v0.0.1 of TestComponent</p>
)
}
}
__ tests __/test-component.snapshot.test.js
import React from 'react';
import renderer from 'react-test-renderer';
import TestComponent from '../src/components/TestComponent';
TestComponent('should display component version', () => {
const comp = renderer.create(<TestComponent/>);
let tree = comp.toJSON();
expect(tree).toMatchSnapshot();
});
The problem is this:
TestComponent('should display component version', () => {
const comp = renderer.create(<TestComponent/>);
let tree = comp.toJSON();
expect(tree).toMatchSnapshot();
});
It's interpreting this as you wanting to call the TestComponent() function
Change it to something like:
describe('TestComponent', () => {
it('should display component version', () => {
const comp = renderer.create(<TestComponent/>);
let tree = comp.toJSON();
expect(tree).toMatchSnapshot();
});
})
Iam currently building an application with react native and mobx. My problem is that the component is not re-rendered after a observable value from the store is changed. My code looks as following:
components/phoneNumberChange.js:
#inject('store')
#observer export class PhoneNumberChange extends React.Component{
renderPhoneInput = () => (
<React.Fragment>
<Button onPress={() => this.props.store.showCountryPicker()}>
<Text>Show picker</Text>
</Button>
</React.Fragment>
);
render = () => (
<React.Fragment>
<CountryCodePicker visible= {this.props.store.pickerVisible}/>
</React.Fragment>
);
}
screens/loginScreen.js:
import { Provider } from "mobx-react";
import { store } from "stores/phoneNumberChangeStore.js";
import { PhoneNumberChange } from "components/phoneNumberChange.js";
export class LoginScreen extends React.Component{
render = () => (
<Provider store={store}>
<PhoneNumberChange/>
</Provider
);
}
stores/phoneNumberChangeStore.js:
import { observable, action } from "mobx";
class PhoneNumberChangeStore {
#observable pickerVisible;
constructor(){
this.hideCountryPicker();
}
#action hideCountryPicker = () => {
this.pickerVisible = false;
}
#action showCountryPicker = () => {
this.pickerVisible = true;
}
}
export const store = new PhoneNumberChangeStore();
babel.config.js:
module.exports = function(api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
plugins: [
[
"#babel/plugin-proposal-decorators",
{
"legacy": true
}
]
]
};
};
package.json:
{
"name": "myapp",
"main": "app/app.js",
"scripts": {
"start": "expo start",
"android": "expo start --android",
"ios": "expo start --ios",
"eject": "expo eject"
},
"dependencies": {
"#babel/plugin-proposal-decorators": "^7.3.0",
"babel-plugin-transform-decorators-legacy": "^1.3.5",
"babel-preset-react-native": "^4.0.1",
"expo": "^32.0.0",
"mobx": "^5.9.0",
"mobx-react": "^5.4.3",
"prop-types": "^15.7.2",
"react": "16.5.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
"react-native-animatable": "^1.3.1",
"react-native-ui-kitten": "^3.1.2",
"react-navigation": "^3.3.2"
},
"devDependencies": {
"babel-preset-expo": "^5.0.0"
},
"private": true
}
The countryCodePicker gets shown or not when the visible prop changes. This is working. but when i change a value from the store it doesnt update and gets shown. I would appreciate any help!
Cheers
You are exporting your store singleton as default in phoneNumberChangeStore.js, but you are importing a named export PhoneNumberChangeStore in loginScreen.js.
Use the default export in loginScreen.js instead. The store property is also called pickerVisible, not countryCodePickerVisible.
Found the solution! I redefined render on the instance.
I changed render = () => {..} to render(){ return (...); }
cheers!
Hi I am creating an SPFX weather webpart and i am getting this error:
there are no errors when i run gulp build. i am not sure how to debug my issue. this is the snippet of the proptypes.shape() where i am getting my issue:
import * as React from 'react';
import PropTypes from 'prop-types';
export const Day: React.SFC<any> = props => {
const date = props.day.dt;
const icon = getIcon(props.day.weather[0].id);
const animate = true;
const iconSize = 64;
const iconColor = 'black';
return (
<div className={appClasses.dayContainer} onClick={props.onClick} role="link">
<h2 className={appClasses.date}>{(new Date(date * 1000)).toDateString()} - {(new Date(date * 1000)).toLocaleTimeString()}</h2>
<ReactAnimatedWeather
icon={icon}
color={iconColor}
size={iconSize}
animate={animate}
/>
</div>
);
};
Day.defaultProps = {
onClick: () => {},
};
Day.propTypes = {
day: PropTypes.shape({
dt: PropTypes.number.isRequired,
weather: PropTypes.array.isRequired,
}).isRequired,
onClick: PropTypes.func,
};
I'd like to note that i created the webpart first using react and it is working perfectly, but when i created an SPFX app, and transferred my existing codes into it. I had encountered these errors.
This is my package.json
{
"name": "spfx-weather-2",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"dependencies": {
"#microsoft/sp-core-library": "~1.1.0",
"#microsoft/sp-webpart-base": "~1.1.1",
"#types/react": "0.14.46",
"#types/react-addons-shallow-compare": "0.14.17",
"#types/react-addons-test-utils": "0.14.15",
"#types/react-addons-update": "0.14.14",
"#types/react-dom": "0.14.18",
"#types/webpack-env": ">=1.12.1 <1.14.0",
"prop-types": "^15.6.1",
"react": "15.4.2",
"react-animated-weather": "^1.0.3",
"react-dom": "15.4.2",
"react-router-dom": "^4.2.2"
},
"devDependencies": {
"#microsoft/sp-build-web": "~1.1.0",
"#microsoft/sp-module-interfaces": "~1.1.0",
"#microsoft/sp-webpart-workbench": "~1.1.0",
"gulp": "~3.9.1",
"#types/chai": ">=3.4.34 <3.6.0",
"#types/mocha": ">=2.2.33 <2.6.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
}
}
Check my answer here [SPLoaderError.loadComponentError]: ***Failed to load component
You very likely have the same issue. If you were to post your github link for me to clone I could confirm for you.
TL;DR
You likely have a circular reference between your factories. You need to move any code that is required from your top level factory to the bottom of said factory.
Let me know if you don't quite understand after reading my other answer.
My app uses CRNA and Expo, and my issue is that the Font.loadAsync() asynchronous function can't locate a .otf font file in the assets/fonts/ folder in my project directory. I am absolutely sure that the directory and file names are correct. I receive this error.
link to image of my error screen
Here is my code:
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Font, AppLoading } from 'expo'
import Root from './js/Root';
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
fontLoaded: false
}
}
async componentDidMount() {
await Font.loadAsync({
"Light": require('./assets/fonts/SemplicitaPro-Light.otf')
})
this.setState({ fontLoaded: true })
}
render() {
if (!this.state.fontLoaded) {
return <AppLoading />
}
return <Root />;
}
}
Here is my package.json:
{
"name": "Zumer",
"version": "0.1.0",
"private": true,
"devDependencies": {
"flow-bin": "^0.40.0",
"jest-expo": "^0.4.0",
"react-native-scripts": "0.0.28",
"react-test-renderer": "16.0.0-alpha.6"
},
"main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
"scripts": {
"start": "react-native-scripts start",
"eject": "react-native-scripts eject",
"android": "react-native-scripts android",
"ios": "react-native-scripts ios",
"test": "node node_modules/jest/bin/jest.js --watch",
"flow": "flow"
},
"packagerOpts": {
"assetExts": ["ttf", "otf"]
},
"jest": {
"preset": "jest-expo",
"tranformIgnorePatterns": [
"node_modules/(?!(jest-)?react-native|react-navigation)"
]
},
"dependencies": {
"#expo/vector-icons": "^4.0.0",
"expo": "^16.0.0",
"native-base": "^2.1.2",
"react": "16.0.0-alpha.6",
"react-native": "^0.43.4",
"react-native-elements": "^0.11.1",
"react-navigation": "^1.0.0-beta.8",
"react-redux": "^5.0.4",
"redux": "^3.6.0",
"redux-observable": "^0.14.1",
"redux-persist": "^4.6.0",
"rxjs": "^5.3.0"
}
}
Could it be that the .otf file format isn't supported by Expo?
I fixed this issue by converting the otf files to ttf
I had a similar issue and I was able to resolve it by removing the quotes around the key that requires the font source.
Before:
await Font.loadAsync({
"Light": require('./assets/fonts/SemplicitaPro-Light.otf')
})
After
await Font.loadAsync({
Light: require('./assets/fonts/SemplicitaPro-Light.otf')
})
And using it in the stylesheet like so
const styles = StyleSheet.create({
text: {
fontFamily: 'Light'
}
});
Everything worked fine after that.