Testing a react component that is wrapped in react hot keys - reactjs

Hi I am quite new to react. I have implemented key board shortcuts in my react application using react-hotkeys.
The plugin basically asks to wrap rendered code inside following tags
<Hotkeys>
// Component render output
</Hotkeys>
Now how to test this kind of components? I wrote a test case like this
it('Shows text passed', () => {
var expected = [1,2,3,0];
const wrapper = renderer.create(<HomePageRow title={'Home Row Title'}cards={[]}/>);
const inst = wrapper.getInstance();
expect(inst.render()).toMatchSnapshot();
});
It gives following error
Console
console.error node_modules\react-test-renderer\cjs\react-test-renderer.development.js:5530
The above error occurred in the <HotKeys> component:
in HotKeys (at homePageRow.js:68)
in HomePageRow (at home.test.js:14)
Consider adding an error boundary to your tree to customize error handling behavior.
What is this error boundary? How to ignore HOCs in testing? Did anyone implement react-hotkeys plugin and wrote tests to your component? If so can you please help me how to write them?

The WrappedComponent property of any Component can help you access component wrapped with HOC's without creating a mock for the HOC :
it('Shows text passed', () => {
var expected = [1,2,3,0];
const wrapper = renderer.create(<HomePageRow.WrappedComponent title={'Home Row Title'}cards={[]}/>);
const inst = wrapper.getInstance();
expect(inst.render()).toMatchSnapshot();
});
If it's not a HOC try just to mock Hotkeys :
jest.mock('../../../Hotkeys', () => (
jest.fn(() =>
<div>Hotkeys component</div>,
)
));

Related

How to write jest test case using jest for a function inside React JS Component

I have developed a component which consumes the mock data. Based on the Boolean in the mock data I am giving the style for the component. I need to write the jest test case for the same, but I am facing statement not covered and branch not covered issue. I am new to jest, tried multiple ways to achieve the result but I am struggling to get the coverage for branch and statement. I have added the React component code and test code. Any one can guide me how to achieve the coverage for the component I wrote. Thanks in advance!
I can sucessfully pass the data inside the test for the respective component but I am unable to cover the branch and statement coverage.
Function displayCustomerData is failing in statement coverage
flexStart={isCreating ? and flexFill={isCreating ? displayCustomerData() is failing in branch coverage
MockData:
const processingCustomerData = [{
id:'123',
display:"Abc",
status:{
type:'--'
},
Date:{
dispaly:'--'
},
},
]
React Component Code:
const customerDisplay=(props)=>{
const {display, isCreating} = props.customerData
const displayCustomerData = () => (
//Spacer is a imported component which accepts padding props
<Spacer paddingLeft="small" paddingRight="small" id="customer-display-text">
//Text is an imported component
<Text> {display}</Text>
</Spacer>
);
return (
//Arrange is an imported component which accepts two props namely flexStart, flexFill
<Arrange
flexStart={isCreating ? <IconSpinner id="spinner-Icon"/> : null}
flexFill={isCreating ? displayCustomerData() : display}
/>
)}
Jest Code
I am importing the above mock inside the jest test file also.
describe('Should render customer data with spinner when it is in isCreating', () => {
const customerDisplayView = jest.fn() //creating a mock function
const customerDisplayStatusView = mountWithIntl(
<customerDisplay
customerData={processingCustomerData}
/>
);
expect(customerDisplayStatusView.find('#spinner-Icon').toMatchSnapshot();
});
});

How to test react component with refs?

I have some react functional component:
export const Chat = props => {
..............
const messagesRef = useRef(null);
useEffect(() => messages && messagesRef.current.scrollTo(0, 99999), [messages]);
.............
return (
...........
<div className='chat-content__list' ref={messagesRef}>
</div>
............
)
}
I'm not good at testing and I just want to test the render of my component, the code is like this:
it('Render Messages chat', async () => {
const { container } = render(<Chat ...someProps />)
}
After running this test, I get this error: TypeError: messagesRef.current.scrollTo is not a function
How to work with refs during testing and how to fix the error in my case?
Jest React tests run in an abstraction of the browser called jsdom. Because it isn't a real browser not all functions are implemented. I suspect that the scrollTo is one such example.
For example, this answer suggests that scrollIntoView is also not implemented and to get around it you can provide a spy for it.
Jest supports having a setup file for setup code required by all tests. You can look into placing the mock there.

Firing a click event on a child component

I have been trying to write a test that will ensure that when a marker is clicked (from leaflet) further details will be displayed to the user. The Marker component is a child of the Map component. To start I am just wanting to see if the onClick function is called once when the marker is clicked.
The Map component returns the following structure
return(
<LeafletMap>
<Marker data-testid='marker' onClick={someFunc}/>
<TileLayer/>
<Popup/>
</LeafletMap>
)
In my test I attempt to render the Map component and find the marker via a data-testid:
const handleParcelClick = jest.fn()
it('get parcel details upon clicking the marker', () => {
const {getByTestId}= render(<Map lat={someNumber} lng={someNumber} zoom={14} parcels={fakeParcels} activeParcel={fakeDetails} onParcelClick={handleParcelClick} />)
const marker = getByTestId('marker')
fireEvent.click(marker)
expect(handleParcelClick).toBeCalledTimes(1)
});
When attempting to run I get the following error:
at getElementError (node_modules/#testing-library/dom/dist/query-helpers.js:22:10)
at args (node_modules/#testing-library/dom/dist/query-helpers.js:76:13)
at getByTestId (node_modules/#testing-library/dom/dist/query-helpers.js:59:17)
at Object.<anonymous>.it (src/ParcelDetails.test.tsx:58:20)
I have attempted using enzyme as well with no success. The data-testid in the actual code is unique for each marker, called marker above for simplicity. Am I going about this wrong? Should I be testing the Marker separately from the Map component?
Update: I have attempted to use enzyme as a solution; however, I receive the following error when trying to simulate a click
TypeError: Cannot read property '__reactInternalInstance$k2volvgmsgj' of null
There does not seem to be a consistent solution for this error and I am confused as to why I am getting it. I have ensured that marker is the component I am wanting to click and that it is not null.
Here is my updated code:
it('Loads parcel details on click', ()=> {
const mockClick = jest.fn();
const component = mount(<Map lat={n1} lng={n2} zoom={14} parcels={fakeParcels} activeParcel={fakeDetails} onParcelClick={mockClick} />);
const marker = component.find(Marker).first();
marker.simulate('click');
expect(mockClick).toBeCalledTimes(1);
});
The easiest way to fire a click event on an element in jest is to first find the element using dom selector and then simulate click on it, like this:
let element = document.getElementById('your-element-id');
element.simulate('click');
Hope this helps!!
I was able to get the desired behaviour via enzyme. Although it is not the best solution - it will do for now. I know shallow rendering is not the best practice.
Here is a snippet of my solution using shallow from enzyme:
it('Loads parcel details on click', ()=> {
const onParcelClick = jest.fn();
const component = shallow(<Map lat={n1} lng={n2} zoom={14} parcels={mockParcels} activeParcel={mockDetails} onParcelClick={onParcelClick} />);
const marker = component.find(Marker).first();
marker.simulate('click');
expect(onParcelClick).toBeCalledTimes(1);
});

How to create an unit test for UncontrolledTooltip from reactstrap that does not handle state management directly?

I implemented simple UncontrolledTooltip from reactstrap. The doc (https://reactstrap.github.io/components/tooltips/) says
uncontrolled component can provide the functionality wanted without the need to manage/control the state of the component
If I want to implement an unit test (e.g. jest + enzyme) for testing its state as either open or close, how can I create a unit test without manually tinkering with state value? Is this possible to achieve it? It seems only possible with regular Tooltip component but I like to hear advice from seasoned engineers.
[Update]:
Upon request I include here tooltip and unit test I am trying to execute. At the moment, I want to simulate hover on the tooltip however mockHover.mock.calls.length returns as 0 which I interpret as mock function was not triggered.
Here is my Tooltip.
import React from 'react';
import { UncontrolledTooltip } from 'reactstrap';
export default class MyTooltip extends React.Component {
render() {
const { metaData, wg } = this.props;
return (
<div>
<UncontrolledTooltip placement="bottom" trigger={'hover'} target={wg}>
{metaData}
</UncontrolledTooltip>
</div>
);
}
}
Here is my unit test that use jest and enzyme:
describe('<MyTooltip />', () => {
it('Tooltip unit test', () => {
const mockHover = jest.fn();
const wrapper = shallow(<MyTooltip trigger={mockHover} />);
expect(wrapper.find(UncontrolledTooltip));
wrapper.find(UncontrolledTooltip).simulate('hover');
expect(mockHover.mock.calls.length).toEqual(1);
});
});
There are few important things to start from:
UncontrolledTooltip is part of 3rd party package so you won't test it explicitly.
Instead you better focus on testing your wrapper around UncontrolledTooltip.
simulate is nothing related to events browser's system. It's just a syntax sugar to do props().onHover(...). So if target component has such a prop - and it's a callback-function - it will be called. If there is no such a prop - it would be up to defaultProps what's going on. Anyway nothing like 'emulating mouse cursor over the element'.
shallow() will stop rendering at level of UncontrolledTooltip(its internals will not be rendered)
Keeping that in mind I see you able only:
your component finally renders UncontrolledTooltip with expected constant prop values
both metaData and wg props are passed down to UncontrolledTooltip
it('renders UncontrolledTooltips under the hood', () => {
const wg = '1';
const metaData = (<span>2</span>);
const wrapper = shallow(<MyTooltip wg={wg} metaData={metaData} />);
const innerTooltip = wrapper.find(UncontrolledTooltip);
/*
I don't validate `find(UncontrolledTooltip).toHaveLength(1)`
since assertion on `.find(..).props()` would throw exception otherwise
*/
expect(innerTooltip.props().placement).toEqual('bottom');
expect(innerTooltip.props().trigger).toEqual('hover');
expect(innerTooltip.props().wg).toEqual(wg);
expect(innerTooltip.props().metaData).toEqual(metaData);
});

React + Enzyme error: Invariant Violation: dangerouslyRenderMarkup(...): Cannot render markup in a worker thread

I am testing a react component using Enzyme and I'm getting the following error:
Invariant Violation: dangerouslyRenderMarkup(...): Cannot render markup in a worker thread. Make sure window and document are available globally before requiring React when unit testing or use ReactDOMServer.renderToString for server rendering
I added the following setup for jsdom, before requiring 'enzyme' (as I read in few places):
const baseMarkup = '<!DOCTYPE html><html><head><title></title></head><body></body></html>';
const window = require('jsdom').jsdom(baseMarkup).defaultView;
global.window = window;
global.document = window.document;
global.navigator = window.navigator;
const React = require('react');
const {mount} = require('enzyme');
const sinon = require('sinon');
const SortableInput = require('../../../src/components/sortableInput/sortableInput').default;
What am I doing wrong here?
EDIT
I don't think it is related to server side rendering. The message is general about unit testing and server side rendering.
Answering my own question, in case someone have the same issue. This was what eventually worked for me:
import 'jsdom-global/register';
describe('My awesome component test', () => {
let cleanup;
beforeEach(() => cleanup = require('jsdom-global')());
afterEach(() => cleanup());
....
})
In one of my project, this is the code to initialise JSDOM, and it is working fine.
import { jsdom } from 'jsdom';
before(() => {
global.document = jsdom('');
global.window = document.defaultView;
});
The before() is a root hook for Mocha. It runs before the beginning of all tests.
One more not so obvious reason for the same error is the describeWithDOM method from Enzyme:
describeWithDOM('<Slider />', () => {
describe('render', () => {
it('should render the slider with one handle by default', () => {
// and so on
According to enzyme guide now it is better to avoid this method:
In previous versions of enzyme, there was a public describeWithDOM API
which loaded in a new JSDOM document into the global namespace before
every test, ensuring that tests were deterministic and did not have
side-effects.
This approach is no longer recommended. React's source code makes
several assumptions about the environment it is running in, and one of
them is that the global.document that is found at "require time" is
going to be the one and only document it ever needs to worry about. As
a result, this type of "reloading" ends up causing more pain than it
prevents.
It is important, however, to make sure that your tests using the
global DOM APIs do not have leaky side-effects which could change the
results of other tests. Until there is a better option, this is left
to you to ensure.

Resources