How to check if a component disappears after click using jest & enzyme? - reactjs

I have a component,
function TestComponent() {
const [visible, setVisible] = useState(true);
return (
<React.Fragment>
{visible && <Container>
I'm Visible
<button onClick={() => setVisible(false)}>
click to close
</button>
</Container>}
</React.Fragment>
);
}
I'm trying to test that on clicking the button the component should be invisible.
And I have following test case to test that,
test('Random Test', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container')).toBeTruthy();
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container')).toBeFalsy();
});
Doesnt seem to work,
Getting error,
expect(received).toBeFalsy()
Received: {}
Any help would be appreciated?
I have a sneaky suspicion that this isnt the way to check if component is hidden. Would also appreciate if anyone could tell a better way.
Update #1:
expect(randomComponent.render().text()).toContain('I\'m Visible');
randomComponent.find('button').simulate('click');
expect(randomComponent.render().text()).toContain('');
Using the above testcases seem to work. Still looking for a better way.

Since .find() will always return an instance of ShallowWrapper class even if there is no node matched. See source code of .find() and .wrap(). It will NOT return a falsy value (null, undefined), so .toBeFalsy() assertion will always fail.
Use .exists([selector]) => Boolean
Returns whether or not any nodes exist in the wrapper. Or, if a selector is passed in, whether that selector has any matches in the wrapper.
import { shallow } from 'enzyme';
import React from 'react';
import { TestComponent } from './';
describe('68334346', () => {
it('should pass', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container').exists()).toBeTruthy();
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container').exists()).toBeFalsy();
});
});
Use .toHaveLength(number) matcher
import { shallow } from 'enzyme';
import React from 'react';
import { TestComponent } from './';
describe('68334346', () => {
it('should pass', () => {
const randomComponent = shallow(<TestComponent />);
expect(randomComponent.find('Container')).toHaveLength(1);
randomComponent.find('button').simulate('click');
expect(randomComponent.find('Container')).toHaveLength(0);
});
});

Related

Problems testing a Redux + React app with enzyme:

I have this component
import React, { useEffect } from 'react';
import './App.css';
import { connect } from 'react-redux';
import { CircularProgress } from '#material-ui/core';
import { loadPhones } from './redux/actions/actions.js';
import TablePhones from './Table.js';
const mapStateToProps = (state) => state;
function mapDispatchToProps(dispatch) {
return {
loadPhones: () => {
dispatch(loadPhones());
},
};
}
export function App(props) {
useEffect(() => {
props.loadPhones();
}, []);
if (props.phones.data) {
return (
<div className="App">
<div className="introductoryNav">Phones</div>
<TablePhones phones={props.phones.data} />
</div>
);
}
return (
<div className="gridLoadingContainer">
<CircularProgress color="secondary" iconStyle="width: 1000, height:1000" />
<p className="loadingText1">Loading...</p>
</div>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
For whom ive written
import React from 'react';
import { render } from '#testing-library/react';
import { Provider } from "react-redux";
import App from './App';
import { shallow, mount } from "enzyme";
import configureMockStore from "redux-mock-store";
const mockStore = configureMockStore();
const store = mockStore({});
describe('App comp testing', () => {
it("should render without throwing an error", () => {
const app = mount(
<Provider store={store}>
<App />
</Provider>
).dive()
expect(app.find('.introductoryNav').text()).toContain("Phones");
});
})
But that test keeps failing
ypeError: Cannot read property 'data' of undefined
I also tried importing App as {App} instead and using shallow testing, but no luck. It gives the same erros, so im left without access to the context, and I cant keep doing my tests
How can I solve this?
You could use the non-default export of your component here and shallow render test if you pass your component the props and don't try to mock the store (if I recall correctly).
I was thinking something like this might work, tesing the "pure" non-store connected version of the component. This seems to be a popular answer for this question as this was asked (in a different way) before here:
import React from 'react';
import { App } from './App';
import { shallow } from "enzyme";
// useful function that is reusable for desturcturing the returned
// wrapper and object of props inside of beforeAll etc...
const setupFunc = overrideProps => {
const props = {
phones: {
...phones, // replace with a mock example of a render of props.phones
data: {
...phoneData // replace with a mock example of a render of props.phones.data
},
},
loadPhones: jest.fn()
};
const wrapper = shallow(<App {...props} />);
return {
wrapper,
props
};
};
// this is just the way I personally write my inital describe, I find it the easiest way
// to describe the component being rendered. (alot of the things below will be opinios on test improvements as well).
describe('<App />', () => {
describe('When the component has intially rendered' () => {
beforeAll(() => {
const { props } = setupFunc();
});
it ('should call loadPhones after the component has initially rendered, () => {
expect(props.loadPhones).toHaveBeenCalled();
});
});
describe('When it renders WITH props present', () => {
// we should use describes to section our tests as per how the code is written
// 1. when it renders with the props present in the component
// 2. when it renders without the props
beforeAll(() => {
const { wrapper, props } = setupFunc();
});
// "render without throwing an error" sounds quite broad or more like
// how you would "describe" how it rendered before testing something
// inside of the render. We want to have our "it" represent what we're
// actually testing; that introductoryNave has rendered with text.
it("should render an introductoryNav with text", () => {
// toContain is a bit broad, toBe would be more specific
expect(wrapper.find('.introductoryNav').text()).toBe("Phones");
});
it("should render a TablePhones component with data from props", () => {
// iirc toEqual should work here, you might need toStrictEqual though.
expect(wrapper.find('TablePhones').prop('phones')).toEqual(props.phones);
});
});
describe('When it renders WITHOUT props present', () => {
it("should render with some loading components", () => {
expect(wrapper.find('.gridLoadingContainer').exists()).toBeTruthy();
expect(wrapper.find('CircularProgress').exists()).toBeTruthy();
expect(wrapper.find('.loadingText1').exists()).toBeTruthy();
});
});
});

React jest enzyme function called test

Hi I have a simple component I need to test:
MyComponent.js-----
import React from 'react';
const MyComponent = (props) => {
onClickHandler = () => {
console.log('clicked');
props.outsideClickHandler();
}
return (
<div>
<span className='some-button' onClick={onClickHandler}></span>
</div>
);
}
MyComponent.test.js----
import React from 'react';
import { shallow } from 'enzyme';
import MyComponent from './MyComponent';
describe('MyComponent', () => {
const onClickHandler = jest.fn();
it('calls click event', () => {
const wrapper = shallow(<MyComponent />);
wrapper.find('.some-button').simulate('click');
expect(onClickHandler.mock.calls.length).toEqual(1); // tried this first
expect(onClickHandler).toBeCalled(); // tried this next
});
});
Tried above two types of expect, my console log value is coming
console.log('clicked'); comes
but my test fails and I get this:
expect(received).toEqual(expected) // deep equality
Expected: 1
Received: 0
So, the problem with your code is when you simulate a click event, you expect a totally independent mock function to be called. You need to attach the mock function to the component. The best way is using prototype. Like this:
it('calls click event', () => {
MyComponent.prototype.onClickHandler = onClickHandler; // <-- add this line
const wrapper = shallow(<MyComponent />);
wrapper.find('.some-button').simulate('click');
expect(onClickHandler.mock.calls.length).toEqual(1);
expect(onClickHandler).toBeCalled();
expect(onClickHandler).toHaveBeenCalledTimes(1); // <-- try this as well
});
Refer to this issue for more potential solutions.

How do you use Enzyme to check for changes to a React Hooks component after onClick?

I am trying to write a simple integration test in my 100% React Hooks (React v16.12) project with Enzyme (v3.10), Jest (v24.0) and TypeScript where if I click a button component in my App container, another component displaying a counter will go up by one. The current value of the counter is stored in the state of the App container (see snippets below).
Basically, I mount the App component to render its children, then try to simulate a click on the button with Enzyme and check the props of the counter display component to see if its value has gone up. But nothing happens. Not only does the onClick handler not get called but I don't seem to be able to retrieve the value prop I pass to the PaperResource component. So basically I can't test the counter display changes when I click on the button in my Enzyme integration test! The test asserts that the value prop goes from 0 to 1, but this assertion fails without an error per seenter code here. Is this because Enzyme support for Hooks is still not there yet or am I doing something daft here? When I run the app on my browser, everything works as expected.
Here's my integration test
import React from 'react';
import App from './App';
import { mount, ReactWrapper } from 'enzyme';
import { act } from 'react-dom/test-utils';
import MakePaperButton from './components/MakePaperButton';
import PaperResource from './components/PaperResource';
describe('App', () => {
let wrapper: ReactWrapper;
beforeEach(() => {
act(() => {
wrapper = mount(<App />);
});
});
describe('when make paper button is clicked', () => {
beforeEach(() => {
act(() => {
wrapper.find('.make-paper__button').simulate('click');
});
});
it('should increase paper resource', () => {
expect(wrapper.find('.resources__paper').prop('value')).toEqual(1);
});
});
});
And here is my React code
import React, { useState } from 'react';
import './App.scss';
import MakePaperButton from './components/MakePaperButton';
import PaperResource from './components/PaperResource';
const App: React.FC = () => {
const [ resources, setResources ] = useState({
paper: 0,
});
const handleMakePaperButtonClick = () => {
setResources({
...resources,
paper: resources.paper + 1,
});
};
return (
<div className="App">
<MakePaperButton onClick={handleMakePaperButtonClick} />
<div className="resources">
<PaperResource value={resources.paper} />
</div>
</div>
);
}
export default App;
My components are very simple
// PaperResource.tsx
import React from 'react';
export default (props: { value: number }) => (
<div className="resources__paper">
<span>Paper: {props.value}</span>
</div>
);
// MakePaperButton.tsx
import React from 'react';
export default (props: { onClick: () => void }) => (
<div className="make-paper__button">
<button onClick={props.onClick}>Make Paper</button>
</div>
);
The only solution I've found so far is wrapping the expect statement in a setTimeout().
it('should increase paper resource', () => {
setTimeout(() => {
expect(wrapper.find('.resources__paper').prop('value')).toEqual(1);
}, 0);
});

Props aren't passing inside component in test cases written with Jest and Enzyme

This is my test case
import React from 'react';
import { mount } from 'enzyme';
import CustomForm from '../index';
describe('Custom Form mount testing', () => {
let props;
let mountedCustomForm;
beforeEach(() => {
props = {
nav_module_id: 'null',
};
mountedCustomForm = undefined;
});
const customform = () => {
if (!mountedCustomForm) {
mountedCustomForm = mount(
<CustomForm {...props} />
);
}
return mountedCustomForm;
};
it('always renders a div', () => {
const divs = customform().find('div');
console.log(divs);
});
});
Whenever I run the test case using yarn test. It gives the following error TypeError: Cannot read property 'nav_module_id' of undefined.
I have already placed console at multiple places in order to see the value of props. It is getting set. But it couldn't just pass through the components and give the error mentioned above.
Any help would be appreciated been stuck for almost 2-3 days now.
You have to wrap the component that you want to test in beforeEach method such that it becomes available for all the 'it' blocks, and also you have to take the mocked props that you think you are getting into the original component.
import React from 'react'
import {expect} from 'chai'
import {shallow} from 'enzyme'
import sinon from 'sinon'
import {Map} from 'immutable'
import {ClusterToggle} from '../../../src/MapView/components/ClusterToggle'
describe('component tests for ClusterToggle', () => {
let dummydata
let wrapper
let props
beforeEach(() => {
dummydata = {
showClusters: true,
toggleClustering: () => {}
}
wrapper = shallow(<ClusterToggle {...dummydata} />)
props = wrapper.props()
})
describe(`ClusterToggle component tests`, () => {
it(`1. makes sure that component exists`, () => {
expect(wrapper).to.exist
})
it('2. makes sure that cluster toggle comp has input and label', () => {
expect(wrapper.find('input').length).to.eql(1)
expect(wrapper.find('label').length).to.eql(1)
})
it('3. simulating onChange for the input element', () => {
const spy = sinon.spy()
const hct = sinon.spy()
hct(wrapper.props(), 'toggleClustering')
spy(wrapper.instance(), 'handleClusterToggle')
wrapper.find('input').simulate('change')
expect(spy.calledOnce).to.eql(true)
expect(hct.calledOnce).to.eql(true)
})
})
})

Wait for condition in Enzyme and Jest (eventual consistency, assert with timeout)

I have a simple test:
import React from 'react';
import GenericButton from 'components/buttons/GenericButton';
import { shallow } from 'enzyme';
import { shallowToJson } from 'enzyme-to-json';
describe('Generic Button', () => {
test('Button action called when clicked', () => {
var clicked = false;
const component = shallow(
<GenericButton action={() => {
clicked = true;
}}
id="testComponent"/>
);
component.find('testComponent').first().simulate('click');
expect(clicked).toBeTruthy();
});
});
However this will fail as the action is done eventually,
If i set the assertion to happen eventually (using setTimeout for example) it will work
however it would be better if i do something of the following before the assert (found this on examples using jasmine)
waitsFor(() => {
return clicked;
}, "the value to change", 1000);
What is the equivalent call for enzyme/jest?
Edit: Content of component
render() {
return <Button id={this.props.id}
key={this.props.id}
onClick={() => this.props.action()}
bsStyle={this.props.style}>
{this.props.text}
</Button>;
}
(Button is a 3rd party library component)
To test if a click handler was added correctly pass a spy into your comment, simulate the click and check that the spy was called. Doing this there is no need to use waitsFor.
import React from 'react';
import GenericButton from 'components/buttons/GenericButton';
import { shallow } from 'enzyme';
import { shallowToJson } from 'enzyme-to-json';
describe('Generic Button', () = > {
test('Button action called when clicked', () = > {
const action = jest.fn();
const component = shallow(
<GenericButton action={action}
id="testComponent"/>
);
component.find('Button').first().simulate('click');
expect(action).toHaveBeenCalled();
});
});

Resources