Using custom mocks for components with jest - javascript

I'm writing a React-Native application in which I have a screen I need to test:
MyScreen.js
import React, { Component } from "react";
import CustomTable from "./CustomTable";
export default MyScreen extends Component {
render() {
return <CustomTable />;
}
}
CustomTable.ios.js
import React, { Component } from "react";
import { View } from "react-native";
import TableView from "react-native-tableview";
export default MyScreen extends Component {
render() {
return (
<View>
...some stuff
<TableView />
</View>
);
}
}
react-native-tableview calls some iOS specific code so I have mocked it out by simply returning the android version (CustomTable.android.js) in the __mocks__ folder
__mocks__/CustomTable.ios.js
import CustomAndroidTable from "../CustomTable.android";
export const CustomTable = CustomAndroidTable;
What I want to do is test MyScreen.js with Jest, but I want it to use the __mock__/CustomTable.ios. How do I go about getting it to do that? Is that even possible with Jest? My current test file looks like:
tests/MyScreen.test.js
import React from "react";
import renderer from "react-test-renderer";
import MyScreen from "../src/MyScreen";
describe("test", () => {
it("works", () => {
jest.mock("../src/CustomTable.ios");
const tree = renderer.create(
<MyScreen />,
).toJSON();
expect(tree).toMatchSnapshot();
});
But it still calls the original version of CustomTable.ios. What am i doing wrong?

You should call jest.mock outside of your suite test. It should be immediately after your imports.
import React from "react";
import renderer from "react-test-renderer";
import MyScreen from "../src/MyScreen";
jest.mock("../src/CustomTable.ios");
describe("test", () => {
it("works", () => {
const tree = renderer.create(
<MyScreen />,
).toJSON();
expect(tree).toMatchSnapshot();
});

Related

Get SVG icons dynamically in Next.js

I wanted to get SVG icons dynamically. I found a method to do this but it seems I made some mistakes. Where am I doing it wrong?
Icon.js
import React from "react";
import { ReactComponent as Bollards } from "./icons/bollards.svg";
import { ReactComponent as Earthquake } from "./icons/earthquake.svg";
import { ReactComponent as Fire } from "./icons/fire.svg";
import { ReactComponent as Healthy } from "./icons/heartbeat.svg";
import { ReactComponent as Home } from "./icons/home.svg";
import { ReactComponent as Planting } from "./icons/planting.svg";
import { ReactComponent as Business } from "./icons/suitcase.svg";
import { ReactComponent as Travel } from "./icons/airplane-around-earth.svg";
const iconTypes = {
bollards: Bollards,
earthQuake: Earthquake,
fire: Fire,
healthy: Healthy,
home: Home,
planting: Planting,
business: Business,
travel: Travel
};
const IconComponent = ({ name, ...props }) => {
let Icon = iconTypes[name];
return <Icon {...props} />;
};
export default IconComponent;
Feautures.js
import React from "react";
import Icon from "./icon";
export default function Features() {
return (
<div>
<Icon name="bollards" />
</div>
);
}
I get this error when trying to export icons.
error - ./components/icon.js
Attempted import error: 'ReactComponent' is not exported from './icons/bollards.svg' (imported as 'Bollards').
You can use SVGR that allows us to import SVGs into your React applications as components.
You need to add #svgr/webpack as a dependency and modify the next.config.js file like this.
next.config.js:
module.exports = {
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: ["#svgr/webpack"]
});
return config;
}
};
Then, you can simply import your icons without using ReactComponent.
Icon.js:
import React from "react";
import Bollards from './icons/bollards.svg';
import Earthquake from './icons/earthquake.svg';
import Fire from './icons/fire.svg';
import Healthy from './icons/heartbeat.svg';
import Home from './icons/home.svg';
import Planting from './icons/planting.svg';
import Business from './icons/suitcase.svg';
import Travel from './icons/airplane-around-earth.svg';
const iconTypes = {
bollards: Bollards,
earthQuake: Earthquake,
fire: Fire,
healthy: Healthy,
home: Home,
planting: Planting,
business: Business,
travel: Travel
};
const IconComponent = ({ name, ...props }) => {
let Icon = iconTypes[name];
return <Icon {...props} />;
};
export default IconComponent;
Working demo is available on CodeSandbox.

this.context returning an empty object

I'm setting up ContextApi for the first time in a production app, hoping to replace our current handling of our app configs with it. I've followed the official docs and consulted with similar issues other people are experiencing with the API, and gotten it to a point where I am able to correctly the config when I do Config.Consumer and a callback in render functions. However, I cannot get this.context to return anything other than an empty object.
Ideally, I would use this.context in lifecycle methods and to avoid callback hell, so help would be appreciated. I've double checked my React version and that I'm setting the contextType. Below is a representation of the code
config.js
import { createContext } from "react";
export default createContext();
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { Router, browserHistory } from "react-router";
import { syncHistoryWithStore } from "react-router-redux";
import Config from "../somePath/config";
// more imports
function init() {
const config = getConfig();
const routes = getRoutes(config);
const history = syncHistoryWithStore(browserHistory, appStore);
ReactDOM.render(
<Provider store={appStore}>
<Config.Provider value={config}>
<Router history={history} routes={routes} />
</Config.Provider>
</Provider>,
document.getElementById("app")
);
}
init();
someNestedComponent.js
import React, { Component } from "react";
import { connect } from "react-redux";
import Config from "../somePath/config";
#connect(
state => ({
someState: state.someState,
})
)
class someNestedComponent extends Component {
componentDidMount() {
console.log(this.context);
}
render() {
return (...someJSX);
}
}
someNestedComponent.contextType = Config;
export default someNestedComponent;
Currently running on:
React 16.8.6 (hopi to see error messages about circuitous code but
didn't get any warnings)
React-DOM 16.7.0
React-Redux 6.0.1
The problem is that someNestedComponent doesn't refer to the class where this.context is used:
someNestedComponent.contextType = Config;
It refers to functional component that wraps original class because it was decorated with #connect decorator, it is syntactic sugar for:
const someNestedComponent = connect(...)(class someNestedComponent extends Component {
...
});
someNestedComponent.contextType = Config;
Instead, it should be:
#connect(...)
class someNestedComponent extends Component {
static contextType = Config;
componentDidMount() {
console.log(this.context);
}
...
}
There are no callback hell problems with context API; this is conveniently solved with same higher-order component pattern as used in React Redux and can also benefit from decorator syntax:
const withConfig = Comp => props => (
<Config.Consumer>{config => <Comp config={config} {...props} />}</Config.Consumer>
);
#connect(...)
#withConfig
class someNestedComponent extends Component {
componentDidMount() {
console.log(this.props.config);
}
...
}
You didn't use a consumer to get the values
ref: https://reactjs.org/docs/context.html#contextconsumer

shallow rendering could not find component

hi everyone I am testing my react application using jest. While testing a component I found that a test breaks unexpectedly throwing error as
Method “props” is only meant to be run on a single node. 0 found instead.
test file
import React from 'react';
import {shallow} from 'enzyme';
import {AddLibraryItem} from '../../components/AddLibraryItem';
import libraryItems from '../fixtures/libraryItems';
let addLibraryItem, history, wrapper;
beforeEach(() => {
addLibraryItem = jest.fn();
history = {push: jest.fn()};
wrapper = shallow(<AddLibraryItem addLibraryItem={addLibraryItem} history={history}/>);
})
test('should execute on submit button successfully', () => {
console.log(wrapper);
wrapper.find('LibraryItemForm').prop('onSubmit')(libraryItems[0]);
expect(addLibraryItem).toHaveBeenLastCalledWith(libraryItems[0]);
expect(history.push).toHaveBeenLastCalledWith("/");
});
Component
import React from 'react';
import {connect} from 'react-redux';
import LibraryItemForm from './LibraryItemForm';
import {addLibraryItem} from '../actions/libraryA';
export class AddLibraryItem extends React.Component {
onSubmit = (libraryItem) => {
this.props.addLibraryItem(libraryItem);
this.props.history.push('/');
}
render () {
return (
<div>
<LibraryItemForm onSubmit={this.onSubmit} />
</div>
);
}
}
const mapDispatchToProps = (dispatch) => {
return {
addLibraryItem: (libraryItem) => dispatch(addLibraryItem(libraryItem))
}
}
const ConnectedAddLibraryItem = connect(undefined, mapDispatchToProps)(AddLibraryItem);
export default ConnectedAddLibraryItem;
The piece of test was earlier working very fine and test of 'LibraryItemForm' is also working fine and also rendering perfectly.
I am not getting what is wrong with it.
Is there any fix of it?
You probably forgot to dive():
wrapper.find(LibraryItemForm).dive().prop('onSubmit')(libraryItems[0]);
Enzyme documentation here.

<Provider> does not support changing `store` on the fly

I recently began learning how to use React-Native and Redux together. I got an error in the IOS simulator that I can't figure out how to fix, and I was wondering if anyone had seen this before.
Here's the error:
Provider does not support changing store on the fly. It is most likely that you see this error because you updated to Redux 2.x and React Redux 2.x which no longer hot reload reducers automatically. See https://github.com/reactjs/react-redux/releases/tag/v2.0.0 for the migration instructions.
I followed that link mentioned in the error message, but it seemed like it needed me to be using Webpack. In the link, where it references if(module.hot), I got the following error when trying to use this solution:
Unable to resolve module
So I'm not sure where to go from here. My project so far is very small. I have my index.ios.js, then an app folder containing a components folder, a store folder and a reducer folder. The structure looks like this:
index.ios.js
app
store
index.js
component
index.js
reducer
index.js
Here is my code:
index.ios.js
import React, { Component } from 'react';
import { Provider } from 'react-redux';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import {configureStore} from './app/store';
import Main from './app/components/Main';
export default class introToRedux extends Component {
render() {
return (
<Provider store={configureStore()}>
<Main />
</Provider>
);
}
}
AppRegistry.registerComponent('introToRedux', () => introToRedux);
components/Main.js
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
var Main = React.createClass({
render(){
return (
<View>
<Text>{this.props.text}</Text>
</View>
);
}
});
var mapStateToText = (state) => {
return {
text: state.text
}
}
module.exports = connect(mapStateToText)(Main);
reducer/index.js
module.exports = (state={}, action) => {
switch (action.type) {
default:
return state;
}
}
store/index.js
import {createStore} from 'redux';
import reducer from '../reducer';
var defaultState = {
text: "default text"
}
export var configureStore = (initialState=defaultState) => {
return createStore(reducer, initialState);
}
Any help on this would be awesome!
Why do you export configureStore()? You might as well
const initialState = {
text: "default text"
}
export default function reducer (state=initialState, action) {
switch (action.type) {
default:
return state;
}
}
createStore() should be executed once.
index.js
// import stuff
const store = createStore(reducer)
class IntroToRedux extends Component {
render() {
return (
<Provider store={store}>
<Main />
</Provider>
);
}
}
ReactDOM.render(IntroToRedux, document.getElementById('root'))

React Jest Test Failing - type.toUpperCase is not a function

I'm trying to write tests using Jest for React. However, I'm getting the following error:
TypeError: type.toUpperCase is not a function
React (images.js):
import React, { Component } from 'react';
export class Images extends Component {
render() {
return (
<div class="images">
</div>
);
}
}
Test (Jest):
jest.autoMockOff();
import React from 'react';
import TestUtils from 'react-addons-test-utils';
const ImagesComponent = require('../src/Components/images');
describe('ImagesComponent', () => {
it('Render instance of div class=images in DOM', () => {
const shallowRenderer = TestUtils.createRenderer();
shallowRenderer.render(<ImagesComponent className="images" />);
imagesDivComponent = shallowRenderer.getRenderOutput();
expect(imagesDivComponent.props.className).toEqual('images');
});
});
Redefine your React component from:
export class Images extends Component {...}
to:
var Images = React.createClass ({...)};
module.exports = Images;

Categories

Resources