Redux-Form : How to mock formValueSelector in Jest - reactjs

I have a component Demo whose Label depends on the current value of a field in the redux-form state. I am using formValueSelector to get the current value of "param" field from the form state. It works fine. However, while running npm test, the selector function always returns undefined. How can I mock it?
Please let me know if I am doing this in a wrong way.
I have a component like
class Sample extends React.Component {
render() {
const {param, state} = this.props;
const selector = formValueSelector('sampleform');
return (
<div>
<Demo
name="name"
label={selector(state, `${param}`)}
/>
</div>
);
}
}
export default Sample;
and, testing code is like
function setup() {
const spy = jest.fn();
const store = createStore(() => ({}));
const Decorated = reduxForm({ form: 'sampleform' })(Sample);
const props = {
"param":"keyOfState",
"state":{"keyOfState":"Label"}
}
const mockedComponent = <Provider store={store}>
<MuiThemeProvider muiTheme={MuiStyles()}>
<Decorated {...props}>
<span></span>
</Decorated>
</MuiThemeProvider>
</Provider>;
return {
props,
mockedComponent}
}
describe('Sample Component', () => {
it('should render the snapshot', () => {
const { mockedComponent } = setup()
const tree = renderer.create(
mockedComponent
).toJSON();
expect(tree).toMatchSnapshot();
});
});

You aren't providing the formValueSelector with an adequate mock for the state that the selector expects.
Solution: The selector expects the global state object provided by redux. The current mocked state doesn't reflect this. Changing the mock to the shape expected fixes the issue:
It is of this shape:
{
form: {
sampleform: {
values: {
keyOfState: "Label"
}
}
}
}
Note: the object stored at the sampleform key includes more entries, but they are irrelevant for the mock.
Here is a link to a reproduction that resolves your issue:https://github.com/mjsisley/reduxFormMockIssue
Please note: I was directed here by Matt Lowe. I am the developer that has worked with him on a number of other projects.

For anyone in the future - if for some reason you actually need to mock FormValueSelector, I just exported a wrapper for it from my Helpers module:
export const tableTypeSelector = formValueSelector('toggle')
and then mocked that:
import * as Helpers from 'helpers'
...
stub = sinon.stub(Helpers, 'tableTypeSelector').returns('charges')

Related

React-Redux: How do I access the store in Parent Component?

So I have a React Native application and recently added Redux to it.
Now I am stuck with following problem:
There is a child component (a number slider where you can set the height value of an item) which is called by a parent component. Every time the value of the number slider in the child component changes, I want to have a console.log of the updated value in the parent component.
Therefore, the parent component somehow must have access to the Redux store, but I can't figure out how to do this. I tried converting the parent component into a Class Component and call store.getState();, but this only gives the initial value of the store and is not updated at all.
So I went back to the parent being a Functional Component and implemented the desired behavior without Redux, but with a callback function. But what do I have Redux for when I'm not using it? In the future I will definitely need Redux in this project and therefore, it would be great to solve this issue using it.
Here is the code without the callback function:
Parent.tsx
imports ...
import Child from '../../atoms/Child/Child';
import store from '../../../../index.js';
const Parent: React.FC<Props> = () => {
// console.log('store: ', store.getState());
const renderChild= () => {
return (
<View>
<Child></Child>
</View>
);
};
const dataArray = [{content: renderChild()}];
return (
<Accordion
dataArray={dataArray}
/>
);
};
export default Parent;
Child.tsx
imports ...
import {connect} from 'react-redux';
import {RootState} from '../../../rootReducer/rootReducer';
import {setHeight} from '../../../store/child/actions';
import {HeightState} from '../../../store/child/types';
type Props = {};
const Child: React.FC<Props> = () => {
const [sliderValue, setSliderValue] = useState(65);
useEffect(() => {
setHeight({height: sliderValue});
}, []);
useEffect(() => {
setHeight({height: sliderValue});
}, [sliderValue]);
return (
<View>
<Text>Height is {sliderValue} inches</Text>
<Slider
step={1}
value={sliderValue}
onValueChange={sliderValue => setSliderValue(sliderValue)}
/>
</View>
);
};
function mapStateToProps(state: RootState) {
return {
height: state.heightResult.height,
};
}
const mapDispatchToProps = {
setHeight,
};
export default connect(mapStateToProps, mapDispatchToProps)(Child);
According Redux Devtools, the store is updated correctly and works fine.
Can you please help me?
You need to connect the parent to the store also. At the moment your Parent has no idea of the store and has no relationship with it. This is the whole point of redux, flexibility and scalability of the state.
const Parent: React.FC<Props> = ({height}) => {
console.log('height', height);
};
const mapStateToProps = ({ heightResult: { height }}: RootState) => ({ height });
export default connect(mapStateToProps)(Parent);

Is it possible to pass data up to withTracker directly from it's lower order component?

I'm working in an existing codebase that uses the React, Meteor, and react-meteor-data combo.
Everything has been going relatively fine up until I tried implementing a search feature using withTracker,
React Select,
and Meteor's subscription functionality.
import { CollectionAPI } from '../arbitrary_meteormongo_collection';
export const WriteableConnectionToCollection = withTracker(props => {
let connection = Meteor.subscribe('COLLECTION_NAME.searchByName', SEARCH_TEXT_HERE);
let isLoading = connection.ready();
return {
...props,
isLoading: isLoading,
collection: CollectionAPI.find().fetch()
}
})(PRESENTATIONAL_COMPONENT);
I've googled around and saw that a common solution for getting data to Meteor.subscribe is to use things like URL parameters, though as I am working in an existing codebase, this change would also need to be implemented in various locations.
Another way I have found is to pass the input field's value to the parent component by keeping track of the input field state in the parent component's state, though this is clearly breaking the principal of separation of concerns:
Parent Component
export const ParentComponent = React.createClass({
getInitialState() {
return {
inputFieldValue: undefined
}
},
onChange(change) {
this.setState(inputFieldValue);
},
render() {
return (
<Search
onChange={this.onChange}
inputFieldValue={this.state.inputFieldValue}
/>
}
}
withTracker HOC
import { CollectionAPI } from '../arbitrary_meteormongo_collection';
export const WriteableConnectionToCollection = withTracker(props => {
let connection = Meteor.subscribe('COLLECTION_NAME.searchByName', this.props.inputFieldValue);
let isLoading = connection.ready();
return {
...props,
isLoading: isLoading,
collection: CollectionAPI.find().fetch()
}
});
InputField Component
import { WriteableConnectionToCollection } from './connections/writeableconnection.js';
const InputFieldComponent = React.createClass({
render() {
<InputField
onInputChange={this.props.onChange}
/>
}
}
export default WritableConnectionToCollection(InputFieldComponent);
Is this the only way to do things with this particular package/framework combo or is there a simpler way that I'm just not seeing?
As Christian Fritz had mentioned in a comment under my original question, I can use ReactiveVar to be able to pass input in and out of my connection component:
export const WritableConnection = function (subscriptionName, collectionAPI) {
/**
* ReactiveVar must be outside of withTracker. If the it was inside withTracker's scope,
* anytime a user would use .set(ANY_VALUE), it would overwrite whatever was in it first,
* and then re-initialize.
**/
const input = new ReactiveVar(undefined);
return withTracker(props => {
const connection = Meteor.subscribe(subscriptionName, input.get());
const isLoading = connection.ready();
return {
...props,
isLoading: isLoading,
collection: collectionAPI.find().fetch(),
setSearchText: (text) => input.set(text),
getSearchText: () => input.get()
}
})
}

How enzyme testing the render

How write a test for checking render element or not? For example, component like this, which render depends on existing list of cards:
class ButtonMore extends Component {
render() {
if (!this.props.listCards.length) {
return null;
}
return (
<button onClick={this.props.onClick} className="buttons button-more">
More
</button>
);
}
}
How would check render depends to props?
function setup() {
const props = {
listCards: [1, 2]
};
const wrapper = shallow(
<Provider store={store}>
<ButtonMore {...props} />
</Provider>
);
return {
props,
wrapper
};
}
describe("ButtonMore component", () => {
const { wrapper } = setup();
it("should render button if cards length more then 0", () => {
expect(wrapper.prop("listCards").length).toBe(2); // that's ok
expect(wrapper.find("button").length).toBe(1); // received 0, not 1
});
});
Unfortunately, i don't finded solution in the enzyme documentation.
First of all: "!this.props.listCards.length" checks only that listCards has the property length. You have to ensure that listCards is an array and that the length is more than 0.
Now ensure that your wrapper contains the right data: you can do it with console.log(wrapper.debug());
At the end.. If you don't need the store you can even not use the Provider. Render just ButtonMore and pass onClick and listCards as props.

Testing input.focus() in Enzyme

How do I test input.focus() in enzyme. I am writing the script with react. My code is below:
public inputBox: any;
componentDidUpdate = () => {
setTimeout(() => {
this.inputBox.focus();
}, 200);
}
render() {
return (
<div>
<input
type = 'number'
ref = {element => this.inputBox = element } />
</div>
);
}
You can use mount instead of shallow.
Then you can compare document.activeElement and the input DOM node for equality.
const output = mount(<MyFocusingComponent/>);
assert(output.find('input').node === document.activeElement);
See https://github.com/airbnb/enzyme/issues/316 for more details.
Per React 16.3 updates... using createRef for anyone visiting this post today, if you rearrange the original component to use the new ref api
class InputBox extends PureComponent {
constructor(props) {
super(props);
this.inputRef = React.createRef();
}
componentDidMount() {
this.inputRef.current.focus();
}
render() {
return (
<input
ref={this.inputRef}
/>
);
}
}
Then in your test spec
it("Gives immediate focus on to name field on load", () => {
const wrapper = mount(<InputBox />);
const { inputRef } = wrapper.instance();
jest.spyOn(inputRef.current, "focus");
wrapper.instance().componentDidMount();
expect(inputRef.current.focus).toHaveBeenCalledTimes(1);
});
Notice the use of the inputRef.current attribute which references the currently assigned DOM node.
Other approach is to test if element gains focus, i.e. focus() is called on node element. To achieve this, focused element need to be referenced via ref tag like it takes place in your example – reference was assigned to this.inputBox. Consider example below:
const wrapper = mount(<FocusingInput />);
const element = wrapper.instance().inputBox; // This is your input ref
spyOn(element, 'focus');
wrapper.simulate('mouseEnter', eventStub());
setTimeout(() => expect(element.focus).toHaveBeenCalled(), 250);
This example uses Jasmine's spyOn, though you can use any spy you like.
I just had the same issue and solved using the following approach:
My setup is Jest (react-create-app) + Enzyme:
it('should set the focus after render', () => {
// If you don't create this element you can not access the
// document.activeElement or simply returns <body/>
document.body.innerHTML = '<div></div>'
// You have to tell Enzyme to attach the component to this
// newly created element
wrapper = mount(<MyTextFieldComponent />, {
attachTo: document.getElementsByName('div')[0]
})
// In my case was easy to compare using id
// than using the whole element
expect(wrapper.find('input').props().id).toEqual(
document.activeElement.id
)
})
This worked for me when using mount and useRef hook:
expect(wrapper.find('input').get(0).ref.current).toEqual(document.activeElement)
Focus on the particular element can be checked using selectors.
const wrapper = mount(<MyComponent />);
const input = wrapper.find('input');
expect(input.is(':focus')).toBe(true);
Selecting by data-test attribute or something similar was the most straight forward solution I could come up with.
import React, { Component } from 'react'
import { mount } from 'enzyme'
class MyComponent extends Component {
componentDidMount() {
if (this.inputRef) {
this.inputRef.focus()
}
}
render() {
return (
<input data-test="my-data-test" ref={input => { this.inputRef = input } } />
)
}
}
it('should set focus on mount', () => {
mount(<MyComponent />)
expect(document.activeElement.dataset.test).toBe('my-data-test')
})
This should work
const wrapper = mount(<MyComponent />);
const input = wrapper.find('input');
expect(input).toHaveFocus();

Way to test the order of elements in React

Just want to implement the unit test for my react component with using the Jest and Enzyme.
Is there a way to test the order? Let's say I have component Button, and I want to render icon and text at the same time.
And of course it's good to provide the alignment option to the user(Icon first or Children first).
Button.js
class Button extends React.Component {
constructor() {
super();
}
render() {
let content;
const icon = (<Icon type='search' />);
if (this.props.iconAlign === 'right') {
content = (<span>{this.props.children} {icon}</span>
} else {
content = (<span>{icon} {this.props.children}</span>
}
return (
<button>{content}</button>
);
}
}
How to test the iconAlign props with Jest and Enzyme?
Check on the type of the component
Check icon first
var button = TestUtils.renderIntoDocument(<Button />);
var buttonNode = ReactDOM.findDOMNode(button);
expect(buttonNode.props.children[0].type.name).toEqual("Icon")
You could use a shallow render and compare the output. I am not familiar with the Jest syntax so that side of my example may be incorrect (I quickly referred to their website):
import { shallow } from 'enzyme';
describe(`Button`, () => {
it(`should render the icon on the right`, () => {
const children = <div>foo</div>;
const actual = shallow(
<Button iconAlign="right" children={children} />
);
const expected = (
<button><span>{children} <Icon type='search' /></span></button>
);
expect(actual.matchesElement(expected)).toBeTruthy();
});
});
And then you could create another test for the "left" align.
The enzyme version of #pshoukry's answer.
describe(`Button`, () => {
it(`should render icon on the right`, () => {
const wrapper = shallow(
<Button iconAlign="right">
<div>foo</div>
</Button>
);
const iconIsOnRight = wrapper.find('span').childAt(1).is(Icon);
expect(iconIsOnRight).toBeTruthy();
});
});
For reference, here is the enzyme shallow rendering API documentation: https://github.com/airbnb/enzyme/blob/master/docs/api/shallow.md

Resources