how to test unsubscribeFocus & unsubscribeBlur with Jest? - javascript

i got an error with this message :
TypeError: this.unsubscribeFocus is not a function Jest
what i want to test is this code
componentWillUnmount() {
// Remove the event listener
this.unsubscribeFocus();
this.unsubscribeBlur();
}
then on file test.js
test('componentWillUnmount as expected', async () => {
class Foo extends React.Component {
// constructor(props) {
// this.unsubscribeFocus = jest.fn();
// this.unsubscribeFocus = jest.fn();
// }
unsubscribeFocus = () => {};
unsubscribeBlur = () => {};
render() {
return (<ReservationNotFound />)
}
}
wrapper.unmount();
const componentWillUnmount = wrapper.instance().componentWillUnmount();
expect(componentWillUnmount).not.toEqual(null);
});
});
but at the wrapper.unmount i got an error that like this
so how to solve this unsubscribeFocus & unsubscribeBlur with jest when it will unmount?

Related

Not able to mock a constructor´s property: ReferenceError: EventSource is not defined (Jest, Enzyme)

In my test file I am mounting a Component and one of the nested Components is making me troubles. This is the Component:
class CacheHandler extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: true,
isLatestVersion: false,
refreshCacheAndReload: () => {
if (caches) {
caches.keys().then((names) => {
names.forEach((name) => {
caches.delete(name);
})
});
}
window.location.reload(true);
}
};
// ...some other code
}
render() {
const { loading, isLatestVersion, refreshCacheAndReload } = this.state;
return this.props.children({ loading, isLatestVersion, refreshCacheAndReload });
}
}
CacheHandler.propTypes = {
children: PropTypes.func.isRequired
};
export default CacheHandler;
I do not know how properly mock the constructor's refreshCacheAndReload property that gives me grey hair. It would be totally ok if it just does not do anything in the mock, but it shoud be found during the mounting process. At the moment, when I run my test, I get because of that part ReferenceError: EventSource is not defined
This is what I tried inside of my test but failed (Error: "CacheHandler" is read-only.):
const fakeCacheHandler = jest.fn(() => ({
constructor(props) {
//super(props);
this.state = {
loading: false,
isLatestVersion: false,
refreshCacheAndReload: () => { }
}},
render() {
const { loading, isLatestVersion, refreshCacheAndReload } = this.state;
return this.props.children({ loading, isLatestVersion, refreshCacheAndReload });
}
}))
CacheHandler = fakeCacheHandler;
I also tried to define the property directly in test but without success:
Object.defineProperty(CacheHandler, 'CacheHandler', {
value: jest.fn().mockImplementation(query => ({
loading: false,
isLatestVersion: false,
refreshCacheAndReload: () => {}
}))
})
I also tried to mock the whole module in the test like this:
jest.mock('../../components/utilities/CacheHandler', function() {
return jest.fn().mockImplementation(() => {
return {
refreshCacheAndReload: () => {},
render: () => {this.props.children({ loading:false, isLatestVersion:false, refreshCacheAndReload })},
}})
});
but still not successful.
jest.spyOn fails as well(Cannot spy the refreshCacheAndReload property because it is not a function; undefined given instead)
const fakeHandler = new CacheHandler();
const methodSpy = jest.spyOn(fakeHandler, "refreshCacheAndReload");
methodSpy.mockImplementation(() => {
console.log('test!');
})
This is how the test itself look like now:
it('renders MembersList', async () => {
const Component = withMemory(AdminAppCore, ROUTE_ADMIN_MEMBERS);
const result = mount(
<MockProvider stores={{ memberStore, programStore }}>
<Component />
</MockProvider>
);
console.log(result.debug());
await waitForState(result, state => state.loading === false);
expect(result.find(MembersList).length).toBe(1);
result.unmount();
});
I tried to mock the constructor of child element inside the test like this, but if failed(TypeError: this.props.children is not a function):
it('renders MembersList', async () => {
const Component = withMemory(AdminAppCore, ROUTE_ADMIN_MEMBERS);
const mockrefreshCacheAndReload = jest.fn(() => ({}));
const component = shallow(<CacheHandler/>);
component.setState({refreshCacheAndReload: mockrefreshCacheAndReload});
const result = mount(
<MockProvider stores={{ memberStore, programStore }}>
<Component />
</MockProvider>
);
console.log(result.debug());
await waitForState(result, state => state.loading === false);
expect(result.find(MembersList).length).toBe(1);
result.unmount();
});
So I am mounting the AdminAppCore, and inside of the AdminAppCore is the nested component that causes troubles.
Can anyone please explain me how can I mock the refreshCacheAndReload state inside of the nested Component constructor?
As I cannot post this in the comments, so please try this.
const mockrefreshCacheAndReload = jest.fn(() => ({}));
it('tests the method', () => {
const component = shallow(<CacheHandler/>);
component.setState({refreshCacheAndReload: mockrefreshCacheAndReload})
})

jest test call of method that is not mocked

I have a React Component MyComponent where I want to test behavior that should be triggered when a user rotates their phone.
Inside the Component:
export class MyComponent extends React.PureComponent<props> {
componentDidMount() {
window.addEventListener('orientationchange', this.onRotation)
}
componentWillUnmount() {
window.removeEventListener('orientationchange', this.onRotation)
}
onRotation = () => {
// do things
}
render() {
// ...
}
}
I found an article on medium that describes how to write tests for this here. However, that doesn't work for me.
describe('<MyComponent />', () => {
it('does things on rotation', () => {
const map : any = {}
window.addEventListener = jest.fn((event, cb) => {
map[event] = cb;
})
const wrapper : any = mount(<MyComponent />)
map.orientationchange()
expect(wrapper.onRotation).toHaveBeenCalled()
})
})
In the article this works, however I get an error:
"Matcher error: received value must be a mock or spy function
Received has value: undefined"
Using a spy also doesn't work:
it('does things on rotation', () => {
const map : any = {}
window.addEventListener = jest.fn((event, cb) => {
map[event] = cb;
})
const wrapper : any = mount(<MyComponent />)
const spy = jest.spyOn(wrapper.instance(), 'onRotation')
map.orientationchange()
expect(spy).toHaveBeenCalled()
})
It says:
"Expected mock function to have been called, but it was not called."
Spy on function inside onRotation.
import React from 'react';
class OrientationChange extends React.Component {
componentDidMount() {
window.addEventListener('orientationchange', this.onRotation)
}
componentWillUnmount() {
window.removeEventListener('orientationchange', this.onRotation)
}
handleRotation = () => {
console.log('inside handle rotation');
}
onRotation = (event) => {
this.handleRotation()
}
render() {
return (
<div>Testing</div>
)
}
}
export default OrientationChange;
describe('<OrientationChange /> orientation change test', () => {
it('does things on rotation', () => {
const map = {}
window.addEventListener = jest.fn((event, cb) => {
map[event] = cb;
})
const wrapper = mount(<OrientationChange />)
const spy = jest.spyOn(wrapper.instance(), 'handleRotation')
map.orientationchange();
expect(spy).toHaveBeenCalled()
})
})

How can I test a debounce function with Jest/Enzyme?

I have this component where test coverage says I need to test lines 24 and 25:
class TableToolbarComp extends Component {
state = {
shipmentId: '',
};
debouncedSetFilters = debounce(() => {
const { applyFilters } = this.props; // LINE 24
applyFilters(this.state); // LINE 25
}, 750);
updateShipmentId = ev => {
this.setState(
{
shipmentId: ev.target.value,
},
this.debouncedSetFilters,
);
};
render() {...}
}
And the test:
beforeEach(() => {
applyFilters: k => k,
});
...
it('should trigger button click', () => {
const wrapper = shallow(<TableToolbarComp {...props} />);
wrapper.instance().debouncedSetFilters(750);
wrapper.instance().updateShipmentId({ target: { shipmentId: '124' } });
wrapper.instance().props.applyFilters({ shipmentId: '124' });
});
And I am not getting any errors, it just says those 2 lines need coverage.
I already attempted to called debouncedSetFilters and applyFilters on the test but it's still returning those 2 lines as uncover.
What am I missing?
Function calls cannot be tested efficiently without spies. It should be:
beforeEach(() => {
applyFilters = jest.fn();
});
In order to test asynchronous time-sensitive function, timer mocks should be applied:
jest.useFakeTimers();
const wrapper = shallow(<TableToolbarComp applyFilters={applyFilters} />);
wrapper.instance().debouncedSetFilters();
wrapper.instance().debouncedSetFilters();
expect(applyFilters).not.toHaveBeenCalled();
jest.advanceTimersByTime(750);
expect(applyFilters).toHaveBeenCalledTimes(1);
Then debouncedSetFilters can be stubbed in updateShipmentId test.

Jest Expected mock function to have been called, but it was not called

I've looked at various suggestions to solve testing a class property with no success and was wondering if anyone could possibly cast a little more light on where I may be going wrong, here are the tests I've tried all with the error Expected mock function to have been called, but it was not called.
Search.jsx
import React, { Component } from 'react'
import { func } from 'prop-types'
import Input from './Input'
import Button from './Button'
class SearchForm extends Component {
static propTypes = {
toggleAlert: func.isRequired
}
constructor() {
super()
this.state = {
searchTerm: ''
}
this.handleSubmit = this.handleSubmit.bind(this)
}
handleSubmit = () => {
const { searchTerm } = this.state
const { toggleAlert } = this.props
if (searchTerm === 'mocky') {
toggleAlert({
alertType: 'success',
alertMessage: 'Success!!!'
})
this.setState({
searchTerm: ''
})
} else {
toggleAlert({
alertType: 'error',
alertMessage: 'Error!!!'
})
}
}
handleChange = ({ target: { value } }) => {
this.setState({
searchTerm: value
})
}
render() {
const { searchTerm } = this.state
const btnDisabled = (searchTerm.length === 0) === true
return (
<div className="well search-form soft push--bottom">
<ul className="form-fields list-inline">
<li className="flush">
<Input
id="search"
name="search"
type="text"
placeholder="Enter a search term..."
className="text-input"
value={searchTerm}
onChange={this.handleChange}
/>
<div className="feedback push-half--right" />
</li>
<li className="push-half--left">
<Button className="btn btn--positive" disabled={btnDisabled} onClick={this.handleSubmit}>
Search
</Button>
</li>
</ul>
</div>
)
}
}
export default SearchForm
First option:
it('should call handleSubmit function on submit', () => {
const wrapper = shallow(<Search toggleAlert={jest.fn()} />)
const spy = jest.spyOn(wrapper.instance(), 'handleSubmit')
wrapper.instance().forceUpdate()
wrapper.find('.btn').simulate('click')
expect(spy).toHaveBeenCalled()
spy.mockClear()
})
Second option:
it('should call handleSubmit function on submit', () => {
const wrapper = shallow(<Search toggleAlert={jest.fn()} />)
wrapper.instance().handleSubmit = jest.fn()
wrapper.update()
wrapper.find('.btn').simulate('click')
expect(wrapper.instance().handleSubmit).toHaveBeenCalled()
})
I get that with a class property the function is an instance of the class requiring the component to be updated in order to register the function, it looks however like the component handleSubmit function gets called instead of the mock?
Swapping out handleSubmit to be a class function as a method gives me access on the class prototype which passes the test when spying on Search.prototype but I'd really like to get a solution to the class property approach.
All suggestions and recommendations would be grateful!
I suppose the unit test should be robust enough to catch the error, if case of any undesirable code changes.
Please include strict assertions in your tests.
For the conditional statements, please cover the branches as well. E.g in case of if and else statement you will have to write two tests.
For user actions, you should try to simulate the actions rather than calling the function manually.
Please see the example below,
import React from 'react';
import { shallow } from 'enzyme';
import { SearchForm } from 'components/Search';
describe('Search Component', () => {
let wrapper;
const toggleAlert = jest.fn();
const handleChange = jest.fn();
const successAlert = {
alertType: 'success',
alertMessage: 'Success!!!'
}
const errorAlert = {
alertType: 'error',
alertMessage: 'Error!!!'
}
beforeEach(() => {
wrapper = shallow(<SearchForm toggleAlert={toggleAlert} />);
});
it('"handleSubmit" to have been called with "mocky"', () => {
expect(toggleAlert).not.toHaveBeenCalled();
expect(handleChange).not.toHaveBeenCalled();
wrapper.find('Input').simulate('change', { target: { value: 'mocky' } });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(wrapper.state().searchTerm).toBe('mocky');
wrapper.find('Button').simulate('click');
expect(toggleAlert).toHaveBeenCalledTimes(1);
expect(toggleAlert).toHaveBeenCalledWith(successAlert);
expect(wrapper.state().searchTerm).toBe('');
});
it('"handleSubmit" to have been called with "other than mocky"', () => {
expect(toggleAlert).not.toHaveBeenCalled();
expect(handleChange).not.toHaveBeenCalled();
wrapper.find('Input').simulate('change', { target: { value: 'Hello' } });
expect(handleChange).toHaveBeenCalledTimes(1);
expect(wrapper.state().searchTerm).toBe('Hello');
wrapper.find('Button').simulate('click');
expect(toggleAlert).toHaveBeenCalledTimes(1);
expect(toggleAlert).toHaveBeenCalledWith(errorAlert);
expect(wrapper.state().searchTerm).toBe('Hello');
});
});
So I've managed to create a working solution by first of all updating the wrapper instance and then updating the wrapper. Test now works.
Working test looks like:
it('should call handleSubmit function on submit', () => {
const wrapper = shallow(<Search toggleAlert={jest.fn()} />)
wrapper.instance().handleSubmit = jest.fn()
wrapper.instance().forceUpdate()
wrapper.update()
wrapper.find('.btn').simulate('click')
expect(wrapper.instance().handleSubmit).toHaveBeenCalled()
})
A solution that could work is;
Mock the function before shallow:
let handleSubmitMock = jest.fn();
LoginPage.prototype.handleSubmit = function() { handleSubmitMock() };
Use this to expect:
form.props.onSubmit();
expect(handleSubmitMock).toHaveBeenCalledTimes(1);
Try something like this
it('should call handleSubmit function on submit', () => {
const toggleAlert = jest.fn();
const wrapper = shallow(<Search toggleAlert={toggleAlert} />)
wrapper.setState({ searchText: 'mocky' });
wrapper.find('Button').at(0).simulate('click');
expect(toggleAlert).toHaveBeenLastCalledWith({
alertType: 'success',
alertMessage: 'Success!!!'
});
})
****Update
constructor(props) {
super(props) //you have to add props to access it this.props
this.state = {
searchTerm: ''
}
this.handleSubmit = this.handleSubmit.bind(this)
}
You shouldn't need to write unit tests for this scenario. You should be able to trust that the framework will fire the correct handlers that you've provided. A more useful test would be one which mocks the toggleAlert prop and tests the instance method handleSubmit. This is where the majority of custom logic will reside and consequently where we are most likely to find errors. Snapshot testing should be fine for anything that is part of the render function output.
A sensible test suite for this component would resemble something like the following:
describe('handleSubmit', () => {
let wrapper;
let spy;
describe('when searchTerm is "mocky"', () => {
beforeEach(() => {
spy = jest.fn();
wrapper = shallow(<SearchForm toggleAlert={spy} />);
wrapper.setState({ searchTerm: 'mocky' });
});
it('will fire spy with expected arguments', () => {
// verify that spy has not been fired prior to test
expect(spy).not.toBeCalled();
wrapper.instance().handleSubmit();
expect(spy).toBeCalled();
expect(spy).toBeCalledWith({
alertType: 'success',
alertMessage: 'Success!!!'
});
});
it('will set searchTerm to ""', () => {
expect(wrapper.state('searchTerm')).toBe('mocky');
wrapper.instance().handleSubmit();
expect(wrapper.state('searchTerm')).toBe('');
});
});
describe('when searchTerm is "something else"', () => {
beforeEach(() => {
spy = jest.fn();
wrapper = shallow(<SearchForm toggleAlert={spy} />);
wrapper.setState({ searchTerm: 'something else' });
});
it('will fire spy with expected arguments', () => {
// verify that spy has not been fired prior to test
expect(spy).not.toBeCalled();
wrapper.instance().handleSubmit();
expect(spy).toBeCalled();
expect(spy).toBeCalledWith({
alertType: 'error',
alertMessage: 'Error!!!'
});
});
});
});

How do I test methods of React components and include them in my Istanbul coverage?

I was wondering how I'm able to test the methods of my react components and include them in my Istanbul test coverage?
Edit: I'm using enzyme. Forgot to mention that.
For example, I have this component:
class SearchFormContainer extends Component {
static handleToggle = () => {
const filter = document.querySelector('.filter-container');
const root = document.getElementById('root');
if (filter.classList.contains('closed')) {
filter.classList.remove('closed');
filter.classList.add('opened');
root.classList.add('paused');
} else {
root.classList.remove('paused');
filter.classList.remove('opened');
filter.classList.add('closed');
}
};
updateQuantity = (e) => {
const { store } = this.props;
store.setQuantity(e.target.value);
}
updateStrength = (e) => {
const { store } = this.props;
store.setStrength(e.target.value);
}
updateCustomQuantity = (e) => {
const { store } = this.props;
let value = e.target.value || '';
if (!value) {
store.setPricingError('invalidquantity');
} else {
value = value.match(/\d+(\.)?(\d+)?/);
if (!value) {
value = '';
} else {
value = value[0];
}
if (parseFloat(value) <= 0) {
store.setPricingError('invalidquantity');
} else if (store.pricingError === 'invalidquantity') {
store.setPricingError(null);
}
}
store.setCustomQuantity(value);
}
render() {
const {
styleName,
openFilterLabel,
closeFilterLabel,
updateFilterLabel,
searchLabel,
quantityLabel,
strengthLabel,
zipLabel,
zipPlaceholder,
searchFormAnchor,
customQuantityPlaceholder,
store,
searchBar,
} = this.props;
const toggled = 'closed';
const { useCustomQuantity } = store;
let inputType = 'predefined';
if (useCustomQuantity) {
inputType = 'custom';
} else {
inputType = 'predefined';
}
const handleCustomInput = () => {
store.toggleUseCustomQuantity();
};
Here's a test I'm trying to run (note that I've assigned store and searchBar in the describe block.
it('calls upDateQuantity', () => {
sinon.spy(App.prototype, 'updateQuantity');
const updateQuantity = sinon.stub();
const component = shallow(<App
updateQuantity={updateQuantity}
store={store}
searchBar={searchBar}
openFilterLabel="Filter"
closeFilterLabel="Close"
updateFilterLabel="UPDATE"
searchLabel="Medication Name"
quantityLabel="Quantity"
strengthLabel="Strength"
zipLabel="ZIP code"
zipPlaceholder="11111"
searchFormAnchor="SearchForm"
name="search-field"
placeholder="Search drug name..."
customQuantityPlaceholder="Enter Custom Quantity"
/>);
component.find('#quantitySelector').simulate('click');
expect(App.updateQuantity.callCount).to.equal(1);
});
I'm not sure if this will even test the actual function, seems like it will just test to see if the event was fired? I'm getting the error:
TypeError: Attempted to wrap undefined property updateQuantity as function.
I'm not sure how to test certain methods above, such as handleToggle, updateQuantity, UpdateStrength, etc. My react testing skills are young, so any assistance is greatly appreciated. Thank you!
I would suggest using enzyme to render the react component within your test and proceed as follow. You can then test your component methods directly with:
const component = shallow(<MyComponent {...props} />)
component.instance().myMethod()
Or if you need to trigger an event on your component, you can do as follow:
import {shallow} from 'enzyme'
import ButtonControl from '../ButtonControl'
describe('ButtonControl component', () => {
it('handleClick', () => {
let onClickHandler = jest.fn()
let props = { handleClick: onClickHandler }
let component = shallow(<ButtonControl {...props} />)
component.find('button').first().props().onClick()
expect(onClickHandler).toHaveBeenCalled()
})
})
this test use jest plus code coverage. Enzyme is compatible with jasmine, it should be easy to adapt.

Categories

Resources