Jest expected mock not called (redux component) - javascript

In React, I am testing that a button click inside a child component causes a function to be called in the parent component (onDeleteClick), via event bubbling.
For this test, I am using mount, as shallow will not allow us to trigger a function in a child component.
onDeleteClick, the function I am trying to check whether it was called or not, is a class property which in this case, is an arrow function.
I am mocking the onDeleteClick function, and passing it into my component via a Redux Provider when starting the test.
The problem I am having is that at the end of the test, when I perform a check to see if the mocked function was called, it returns 0.
expect(onDeleteClick.mock.calls.length).toBe(1);
If I put a console.log within onDeleteClick(), it's outputted during the test, so I know that the function is in fact being called.
I have researched this quite a bit and so far haven't gotten anything to work.
Some suggestions were to spy on my mocked function, and then call forceUpdate on the wrapper, but this didn't yield any positive results.
For this, I am using Jest with Enzyme.
Reference Code:
Parent.js
import { deleteAccount } from '../../actions/profileActions';
import ChildComponent from '../common/ChildComponent';
class ParentComponent extends Component {
onDeleteClick = () => {
console.log('onDeleteClick was executed during this test!')
this.props.deleteAccount();
}
render() {
let dashboardContent;
dashboardContent = (
<div>
<ChildComponent onDelete={this.onDeleteClick} />
</div>
);
return (
<div>
{dashboardContent}
</div>
);
}
}
// propTypes and mapStateToProps removed from this post
export default connect(
mapStateToProps,
{ deleteAccount }
)(ParentComponent);
__tests__/ParentComponent.js
import React from 'react';
import { mount } from 'enzyme';
import { BrowserRouter as Router } from 'react-router-dom';
import { Provider } from 'react-redux';
import configureStore from 'redux-mock-store';
import ParentComponent from '../ParentComponent';
import thunk from "redux-thunk";
const mockStore = configureStore([thunk]);
const deleteAccount = jest.fn();
const props = {
deleteAccount
}
const randomTestState = {
// some initial state, not important
};
const randomTestStore = mockStore(randomTestState);
describe('<ParentComponent />', () => {
it(`mounts the ParentComponent component and, when ChildComponent sends onDelete, then deleteAccount function is called once`, () => {
const wrapper = mount(
<Provider store={randomTestStore} props={props}>
<Router >
<ParentComponent />
</Router>
</Provider>
);
// Here, I grab an element in ChildComponent and simulate a click using Enzyme, then the event bubbles up, and deleteAccount() is called in the parent component.
// the console.log we expect to see from onDeleteClick is logged to console.
// the call does not seem to have registered though and the expect returns falsy
expect(deleteAccount.mock.calls.length).toBe(1);
})
});
Could the problem be that I am wrapping the component in a Provider?
I have a hunch, but I couldn't find any concrete examples of tests which use a Provider to wrap their component when running integration testing

The solution was that I needed to change my main ParentComponent file from
class ParentComponent extends Component {
to this:
extend class ParentComponent extends Component {
and then in my test file, import the component like so:
import { ParentComponent } from '../ParentComponent'; // non-default export should be wrapped in braces
and then update my test so that I assign the wrapper variable like so:
const wrapper = mount(<ParentComponent {...props} />);
This then allowed the test to pass
expect(deleteAccount.mock.calls.length).toBe(1);
It was recommended here in the Redux docs

Related

call react component-element as variable

Is it possible to call a React component element with a variable inside?
import React from "react"
/*React functional component*/
function someName() {
const someVar = "componentName"; //the name of the called component
return(
<{someVar}/>
)
}
export default someName;
I try to implement this in a router and to change the filenames(Sites) (in the element) dynamically with useState from fetched data.
I am open to all kind of help :)
There is no direct way to do that but you can use this approach.
import ComponentA from '...path';
import ComponentB from '...path';
...
const components = {
componentA: ComponentA,
componentB: ComponentB,
...
}
...
function App(props) {
const TargetComponent = components[props.componentName];
return <TargetComponent />;
}

Jest Enzyme how to shallow test for existence of wrapped component

I am testing a component that conditionally renders a wrapped component. I am using enzyme and jest and the root component is rendered through the shallow() method.
The issue is testing if the Root component contains the wrapped component.
How would I test if the wrapped component exists without using the mount() render method?
hoc.component.jsx
export function HOC(Component) {
render() {
return <Component />
}
}
wrapped.component.jsx
class WrappedComponent extends React.Component {
...
}
export default HOC(WrappedComponent)
root.component.jsx
class RootComponent extends React.Component {
render() {
return (
condition ? ... :
<WrappedComponent/>
)
}
}
When testing the root.component.jsx I would like to check if the WrappedComponent exists.
root.component.spec.js
import { WrappedComponent } from 'WrappedComponent'
const wrapper = shallow(<RootComponent {...props}/>);
wrapper.find(WrappedComponent).length => returns 0
If I log wrapper.debug() I see the following:
...<HOC(WrappedComponent) />
How would I test the existence of the WrappedComponent while testing the RootComponent?
It should be possible to assert the existence of a component, as long as WrappedComponent in tests is not original component class but a component wrapped in HOC, i.e. HOC(WrappedComponent).
If HOC(WrappedComponent) is default export, it should be:
import WrappedComponent from 'WrappedComponent'
...
const wrapper = shallow(<RootComponent {...props}/>);
expect(wrapper.find(WrappedComponent).length).toBe(1);
You can use the selector 'HOC(WrappedComponent)':
test('WrappedComponent is rendered', () => {
const wrapper = shallow(<RootComponent {...props}/>);
expect(wrapper.find('HOC(WrappedComponent)').length).toEqual(1);
}

Understanding React Higher-Order Components

Can someone please explain Higher-order components in React. I have read and re-read the documentation but cannot seem to get a better understanding. According to the documentation, HOCs help remove duplication by creating a primary function that returns a react component, by passing arguments to that function.
I have a few questions on that.
If HOCs create a new enhanced component, can it be possible not to pass in any component as argument at all?
In an example such as this, which is the higher order component, the Button or the EnhancedButton.
I tried creating one HOC like this:
// createSetup.js
import React from 'react';
export default function createSetup(options) {
return class extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.testFunction = this.testFunction.bind(this);
}
testFunction() {
console.log("This is a test function");
}
render() {
return <p>{options.name}</p>
}
}
}
// main.js
import React from 'react';
import {render} from 'react-dom';
import createSetup from './createSetup';
render((<div>{() => createSetup({name: 'name'})}</div>),
document.getElementById('root'););
Running this does not show the HOC, only the div
Can anyone help out with a better example than the ones given?
A HOC is a function that takes a Component as one of its parameters and enhances that component in some way.
If HOCs create a new enhanced component, can it be possible not to pass in any component as argument at all?
Nope, then it wouldn't be a HOC, because one of the conditions is that they take a component as one of the arguments and they return a new Component that has some added functionality.
In an example such as this, which is the higher order component, the Button or the EnhancedButton.
EnhanceButton is the HOC and FinalButton is the enhanced component.
I tried creating one HOC like this: ... Running this does not show the HOC, only the div
That's because your createSetup function is not a HOC... It's a function that returns a component, yes, but it does not take a component as an argument in order to enhance it.
Let's see an example of a basic HOC:
const renderWhen = (condition, Component) =>
props => condition(props)
? <Component {...props} />
: null
);
And you could use it like this:
const EnhancedLink = renderWhen(({invisible}) => !invisible, 'a');
Now your EnhancedLink will be like a a component but if you pass the property invisible set to true it won't render... So we have enhanced the default behaviour of the a component and you could do that with any other component.
In many cases HOC functions are curried and the Component arg goes last... Like this:
const renderWhen = condition => Component =>
props => condition(props)
? <Component {...props} />
: null
);
Like the connect function of react-redux... That makes composition easier. Have a look at recompose.
In short, If you assume functions are analogues to Components, Closure is analogous to HOC.
Try your createSetup.js with:
const createSetup = options => <p>{options.name}</p>;
and your main.js
const comp = createSetup({ name: 'name' });
render((<div>{comp}</div>),
document.getElementById('root'));
A higher-order component (HOC) is an advanced technique in React for reusing component logic. Concretely, a higher-order component is a function that takes a component and returns a new component.
A HOC is a pure function with zero side-effects.
Example: CONDITIONALLY RENDER COMPONENTS
Suppose we have a component that needs to be rendered only when a user is authenticated — it is a protected component. We can create a HOC named WithAuth() to wrap that protected component, and then do a check in the HOC that will render only that particular component if the user has been authenticated.
A basic withAuth() HOC, according to the example above, can be written as follows:
// withAuth.js
import React from "react";
export function withAuth(Component) {
return class AuthenticatedComponent extends React.Component {
isAuthenticated() {
return this.props.isAuthenticated;
}
/**
* Render
*/
render() {
const loginErrorMessage = (
<div>
Please login in order to view this part of the application.
</div>
);
return (
<div>
{ this.isAuthenticated === true ? <Component {...this.props} /> : loginErrorMessage }
</div>
);
}
};
}
export default withAuth;
The code above is a HOC named withAuth. It basically takes a component and returns a new component, named AuthenticatedComponent, that checks whether the user is authenticated. If the user is not authenticated, it returns the loginErrorMessage component; if the user is authenticated, it returns the wrapped component.
Note: this.props.isAuthenticated has to be set from your application’s
logic. (Or else use react-redux to retrieve it from the global state.)
To make use of our HOC in a protected component, we’d use it like so:
// MyProtectedComponent.js
import React from "react";
import {withAuth} from "./withAuth.js";
export class MyProectedComponent extends React.Component {
/**
* Render
*/
render() {
return (
<div>
This is only viewable by authenticated users.
</div>
);
}
}
// Now wrap MyPrivateComponent with the requireAuthentication function
export default withAuth(MyPrivateComponent);
Here, we create a component that is viewable only by users who are authenticated. We wrap that component in our withAuth HOC to protect the component from users who are not authenticated.
Source
// HIGHER ORDER COMPOENTS IN REACT
// Higher order components are JavaScript functions used for adding
// additional functionalities to the existing component.
// file 1: hoc.js (will write our higher order component logic) -- code start -->
const messageCheckHOC = (OriginalComponent) => {
// OriginalComponent is component passed to HOC
const NewComponent = (props) => {
// business logic of HOC
if (!props.isAllowedToView) {
return <b> Not Allowed To View The MSG </b>;
}
// here we can pass the props to component
return <OriginalComponent {...props} />;
};
// returning new Component with updated Props and UI
return NewComponent;
};
export default messageCheckHOC;
// file 1: hoc.js -- code end -->
// file 2: message.js -- code start -->
// this is the basic component we are wrapping with HOC
// to check the permission isAllowedToView msg if not display fallback UI
import messageCheckHOC from "./hoc";
const MSG = ({ name, msg }) => {
return (
<h3>
{name} - {msg}
</h3>
);
};
export default messageCheckHOC(MSG);
// file 2: message.js -- code end -->
// file 3 : App.js -- code start --->
import MSG from "./message.js";
export default function App() {
return (
<div className="App">
<h3>HOC COMPONENTS </h3>
<MSG name="Mac" msg="Heyy !!! " isAllowedToView={true} />
<MSG name="Robin" msg="Hello ! " isAllowedToView={true} />
<MSG name="Eyann" msg="How are you" isAllowedToView={false} />
</div>
);
}
// file 3 : App.js -- code end --->

Unit test: simulate the click event of child component in parent using enzyme

I have a parent component and a child component which is just a "label" element. When i click the child element, i need to call the function in parent component. I expect it to be called but the state doesnt change and when i saw the coverage file the function isnt being called.
**Updated:**The code works for development. It's just the unit test that fails.
Here is my parent component
parent.js
export default class Parent extends Component {
constructor(props) {
super(props)
this.state={clickedChild: false}
this.handleChildClick = this.handleChildClick.bind(this)
}
handleChildClick(index) {
this.setState({clickedChild:true})
}
render(){
const self = this
return(
const items = [{'id':1,'text':'hello'},{'id':2,'text':'world'}]
<div>
{items.map(function(item,index){
return <ChildComponent onChildClick ={ self.handleChildClick.bind(null,index)} childItem={item} />
})}
</div>
)}
}
child component
export default class ChildComponent extends Component {
constructor(props) { super(props)}
render(){
return(
<label onClick={this.props.onChildClick}>{this.props.childItem.text} </label>
)
}
}
unit test
import chai from 'chai'
import React from 'react'
import ReactDOM from 'react-dom'
import { mount, shallow } from 'enzyme';
import sinon from 'sinon'
import Parent from '../Parent'
import ChildComponent from '../ChildComponent'
let expect = chai.expect
describe('check click event on child()',()=>{
it('clicking menu item',()=>{
const items = [{'id':1,'text':'hello'},{'id':2,'text':'world'}]
const wrapper = mount(<Parent items={items} />)
console.log(wrapper.state('clickedChild')) // prints false
wrapper.find(ChildComponent).last().simulate('click',1)
// tried the following
// wrapper.find(ChildComponent).last().simulate('click')
console.log(wrapper.state('clickedChild')) // still prints false
})
})
I changed the binding in my parent component to
<ChildComponent onChildClick ={() => self.handleChildClick(index)} childItem={item} />
There was also a function i was calling in my parent component which was calling its method.(parent.js)
handleChildClick(index) {
this.setState({clickedChild:true})
this.props.handleClick(index) // i had forgotten to see the line.
}
Once i stubbed the above commented line in my test . everything worked as expected.
it('clicking menu item', () => {
const items = [{'id':1,'text':'hello'},{'id':2,'text':'world'}]
const handleClickStub = sinon.spy()
const wrapper = mount(<Parent items={items} handleClick={handleClickStub} />)
console.log(wrapper.state('clickedChild')) // prints false
wrapper.find(ChildComponent).last().simulate('click')
expect(handleClickStub.calledOnce).to.be.true // successful
console.log(wrapper.state('clickedChild')) // prints true
})

FindDOMNode flux/alt store

I'm trying to access a dom node from a store (using alt) in order to animate using velocity.js, however am only getting 'cannot read property of undefined'. Is it possible to use findDOMNode from an alt/flux store?
import React from 'react'
import alt from '../_alt';
import Actions from '../actions/actions';
import Velocity from 'velocity-animate/velocity';
import Body from '../modules/level_1/body/body1'
class Store {
constructor(){
this.bindListeners({
menuToggle: Actions.MENU_TOGGLE
});
this.menuStatus = false
}
menuToggle(){
if (!this.menuStatus){
this.menuStatus = true;
Velocity(React.findDOMNode(Body.refs.wrap),({ width: '50px' }), 50)
} else {
this.menuStatus = false;
}
}
}
export default alt.createStore(Store, 'Store');
Component:
import React from 'react';
import connectToStores from 'alt/utils/connectToStores';
import Store from '../../../stores/store'
import Actions from '../../../actions/actions';
import Styles from './body1.css';
import Hero from '../../objects/hero/full_width'
let image = ['../../../../assets/full_width1.jpg', 'image']
#connectToStores
export default class Body extends React.Component {
static getStores(){
return [Store];
}
static getPropsFromStores(){
return Store.getState();
}
render(){
return (
<div ref='wrap'>
<Hero content={image} />
</div>
);
}
}
Body is a react class, which does not have refs.
What you need is a react element (an instance of a react class) which is the "this" inside of render, componentDidMount, etc.
You will have to provide the react element to the store in some way (probably by calling menuToggle with the actual react element).
Alternatively you could use componentDidMount to set the ref on the Body class so that toggle could consume it.
A pattern that I have used with some success is to create an initialize action that takes as one of its arguments a React component. Then in componentDidMount() you can call that action, passing this as an argument. This allows your store to have a handle on that React element, as well as all of its associated properties so you can do things like React.findDOMNode(component.refs['someref']).

Categories

Resources