Jest function call for functional component gives error on stimulate click - reactjs

I have imported a table component in a different component file and I am passing props form the parent component.
TableWrapper.js
const handleRowClick = rowData => {
// function data
}
<TableRender onRowClick={handleRowClick} id={'AUDIT'} />
I am writing test cases for this kind of function as I want this function to be covered and pass the data to it rowData
testFile.js
import React from 'react';
import { shallow } from 'enzyme';
it('handle row click is called', () => {
const handleRowClick = jest.fn();
const wrapper = shallow(<TableWrapper {...props} onRowClick={handleRowClick} />);
const rowClickFunction = wrapper.find('.ra--audit-table__content');
rowClickFunction.simulate('handleRowClick');
expect(handleRowClick).toBeTruthy();
})
If I do this then it passes the test case but does not cover the function in coverage.
testFile.js
import React from 'react';
import { shallow } from 'enzyme';
it('handle row click is called', () => {
const handleRowClick = jest.fn();
const wrapper = shallow(<TableWrapper {...props} onRowClick={handleRowClick} />);
const rowClickFunction = wrapper.find('.ra--audit-table__content');
rowClickFunction.simulate('handleRowClick');
expect(rowClick).toHaveBeenCalledTimes(1);
})
If I do this change it gives me an error:-
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
I want this function to b covered.
Any Idea for this?

you are simulating wrong event. you should simulate click.
change:
const rowClickFunction = wrapper.find('.ra--audit-table__content');
rowClickFunction.simulate('handleRowClick');
to:
const elem = wrapper.find('.ra--audit-table__content');
elem.simulate('click');
(also make sure the clickable element has a .ra--audit-table__content class)
you are creating wrong component. you should create a TableRender, not TableWrapper.
change:
const wrapper = shallow(<TableWrapper {...props} onRowClick={handleRowClick} />);
to:
const wrapper = shallow(<TableRender onRowClick={handleRowClick} />);
Code (testFile.js) :
it('handle row click is called', () => {
const handleRowClick = jest.fn((i) => {console.log(`row ${i} clicked`)});
const wrapper = shallow(<TableRender onRowClick={handleRowClick} />);
const elem = wrapper.find('.ra--audit-table__content');
elem.simulate('click');
// expect(handleRowClick).toBeTruthy();
expect(handleRowClick).toHaveBeenCalledTimes(1);
})
Ps: i tested it and it works fine.

Related

How can I test an input with Jest

I've been trying to figure out how to test different input methods but since I am new to this test methodology, I cannot get even close to the answer. Here is what I have:
const App = (props) => {
const newGame = props.newGame;
const [typeracertext, setTyperacertext] = useState(props.typeracertext);
const [wholeText, setWholeText] = useState("");
const onChange = (e) => {
//here I have code that read the input and is comparing it with variable - typeracertext and if so, it sets the property wholeText to that value
};
return (
<input ref={(node) => this.textInput = node} placeholder="Message..." onChange={onChange}></input>
);
}
so what I am trying to figure out is a test that should set the typeracertext to a certain value (for example "This is a test), and set the input value to "This" so if it passes the onChange() check it should set wholeText to "This". I hope that makes sense.
This is the best I could get and I don't have an idea what should I write on "expect".
test('Test the input value', () => {
const node = this.textInput;
node.value = 'This';
ReactTestUtils.Simulate.change(node);
expect()
});
Since this is a react app, I'll advice you take advantage of react testing library to make this easy
import React from 'react';
import { fireEvent, render, screen } from '#testing-library/react';
import userEvent from '#testing-library/user-event';
// In describe block
test('Test input component', () => {
const onChange = jest.fn();
render(<InputComponent onChange={onChange} data-test-id="input" />);
const input = screen.getByTestId('input');
fireEvent.change(input, { target: { value: 'a value' } });
// You can also do this with userEvent
userEvent.type(input, 'test')
// Check if change event was fired
expect((input as HTMLInputElement).onchange).toHaveBeenCalled();
});
See documentation here

How can I test a click event which changes a useState state with enzyme?

I have the following component:
import React, { useState } from "react";
import { Button, ThirteenBold } from "#selfdecode/sd-component-library";
import { PlayIcon } from "assets/icons";
import { TourButtonProps } from "./interfaces";
import { WelcomeVideoModal } from "../../modals/welcome-video-modal";
/**
* The tour button.
*/
export const TourButton: React.FC<TourButtonProps> = (props) => {
const [isIntroVideoShowing, setIsIntroVideoShowing] = useState(false);
return (
<>
<WelcomeVideoModal
isOpen={isIntroVideoShowing}
onClickX={() => setIsIntroVideoShowing(false)}
data-test="tour-button-welcome-video"
/>
<Button
{...props}
width={["max-content"]}
variant="tour"
onClick={() => setIsIntroVideoShowing(true)}
data-test="tour-button"
>
<ThirteenBold
mr={["12px"]}
color="cl_blue"
width={["max-content"]}
letterSpacing={["1px"]}
display={["none", "block"]}
textTransform="uppercase"
>
welcome tour
</ThirteenBold>
<PlayIcon style={{ height: "30px", fill: "#4568F9" }} />
</Button>
</>
);
};
And the test coverage report is complaining that I am not testing both of the onClick events, which change the state.
I've tried two approaches, and both fail.
Approach one was to mock the useState and see if it gets called as I'd have expected it.
This was the test I tried:
const setState = jest.fn();
const useStateMock: any = (initState: any) => [initState, setState];
jest.spyOn(React, "useState").mockImplementation(useStateMock);
const button = wrapper.find(`[data-test="tour-button"]`);
expect(button).toHaveLength(1);
button.simulate("click");
expect(setState).toHaveBeenCalled();
This shouldn't even be the final test, as it doesn't check what was the valuee it was called with, but still, it failed because useState wasn't even called.
The second approach I've tried was to check the prop value on this component:
<WelcomeVideoModal
isOpen={isIntroVideoShowing}
onClickX={() => setIsIntroVideoShowing(false)}
data-test="tour-button-welcome-video"
/>
And this is the test I've tried
test("Check the isIntroVideoShowing changes to true on buton click", () => {
jest.spyOn(React, "useState").mockImplementation(useStateMock);
const button = wrapper.find(`[data-test="tour-button"]`);
const welcomeVideo = wrapper.find(
`[data-test="tour-button-welcome-video"]`
);
expect(button).toHaveLength(1);
expect(welcomeVideo.prop("isOpen")).toEqual(false);
button.simulate("click");
expect(welcomeVideo.prop("isOpen")).toEqual(true);
});
This one failed claiming it was called with false even after the click.
Is there a way to make these work? Or a different approach to cover these?
You need to give wrapper.update for updating the template with state changes after simulating the click event.
test("Check the isIntroVideoShowing changes to true on buton click", () => {
jest.spyOn(React, "useState").mockImplementation(useStateMock);
const button = wrapper.find(`[data-test="tour-button"]`);
const welcomeVideo = wrapper.find(
`[data-test="tour-button-welcome-video"]`
);
expect(button).toHaveLength(1);
expect(welcomeVideo.prop("isOpen")).toEqual(false);
button.simulate("click");
wrapper.update();
expect(welcomeVideo.prop("isOpen")).toEqual(true);
});
Reference - https://enzymejs.github.io/enzyme/docs/api/ShallowWrapper/update.html

Test useRef onError Fn, with React-Testing-Library and Jest

I have this simple fallbackImage Component:
export interface ImageProps {
srcImage: string;
classNames?: string;
fallbackImage?: FallbackImages;
}
const Image = ({
srcImage,
classNames,
fallbackImage = FallbackImages.FALLBACK
}: ImageProps) => {
const imgToSourceFrom = srcImage;
const imgToFallbackTo = fallbackImage;
const imageRef = useRef(null);
const whenImageIsMissing = () => {
imageRef.current.src = imgToFallbackTo;
imageRef.current.onerror = () => {};
};
return (
<img ref={imageRef} src={imgToSourceFrom} className={classNames} onError={whenImageIsMissing} />
);
};
export default Image;
It works perfectly. I have test running for it with Jest and React-Testing-Library. I have tested all but one scenario. This one:
const whenImageIsMissing = () => {
imageRef.current.src = imgToFallbackTo;
imageRef.current.onerror = () => {}; // This line.
};
This line basically prevents an infinite Loop in case both images are missing
The Problem:
I want to test that my onerror function has been called exactly one time. Which I am really stuck on how to do it. Here is the test...
const { container } = render(<Image srcImage={undefined} fallbackImage={undefined} />);
const assertion = container.querySelector('img').onerror;
fireEvent.error(container.firstElementChild);
console.log(container.firstElementChild);
expect(container.firstElementChild.ref.current.onerror).toHaveBeenCalledTimes(1);
// This though has no reference to a real value. Is an example of what I want to get at.
The Question:
How to access the ref callback function and check how many times has my function been called?
Any ideas on this. I am at a loss, I tried mocking refs, I tried mocking and spying on the component. I tried using act and async/await, in case it was called after. I really need some help on this..
You should check if your function is called or not, that's called testing implementation details, rather you should check if your img element have correct src.
Even you should add some alt and user getByAltText to select image element
const { getByAltText } = render(<Image srcImage={undefined} fallbackImage={undefined} />);
const imageElement = getByAltText('Image Alt');
fireEvent.error(imageElement);
expect(imageElement.src).toEqual(imgToFallbackTo);
You have 2 options:
Add a callback to your props that will be called when whenImageIsMissing is called:
export interface ImageProps {
srcImage: string;
classNames?: string;
fallbackImage?: FallbackImages;
onImageMissing?:();
}
const Image = ({
srcImage,
classNames,
onImageMissing,
fallbackImage = FallbackImages.FALLBACK
}: ImageProps) => {
const imgToSourceFrom = srcImage;
const imgToFallbackTo = fallbackImage;
const imageRef = useRef(null);
const whenImageIsMissing = () => {
imageRef.current.src = imgToFallbackTo;
imageRef.current.onerror = () => {};
if (onImageMissing) onImageMissing();
};
return (
<img ref={imageRef} src={imgToSourceFrom} className={classNames} onError={whenImageIsMissing} />
);
};
and then insert jest.fn in your test and check how many times it was called.
The other option is to take the implementation of whenImageIsMissing and put it inside image.util file and then use jest.spy to get number of calls. Since you are using a function component there is no way to access this function directly.
Hope this helps.

Testing if button changes state, or if component appears (React)

I have a component with a button and a form. When button is visible, the form is hidden and the opposite - when we click button it dissapears and form is shown. I would like to test it either with enzyme or testing-library, but all my tests fail.
import React, { useState } from 'react';
import Form from './Form';
const FormComponent = () => {
const [isFormVisible, setFormVisibility] = useState(false);
function toggleFormVisibility() {
setFormVisibility(!isFormVisible);
}
return (
<section>
{!isFormVisible && (
<button
id='toggle-form-button'
data-testid='toggle-form-button'
onClick={toggleFormVisibility}
>
Show form
</button>
)}
{isFormVisible && <Form onCancel={toggleFormVisibility} />}
</section>
);
};
export default FormComponent;
My test:
describe('Form component', () => {
it('should fire toggle form action on button click', () => {
const setState = jest.fn();
const useStateSpy = jest.spyOn(React, 'useState');
useStateSpy.mockImplementation(() => [undefined, setState]);
const component = render(
<Form />
);
const showFormButton = component.getByTestId('toggle-form-button');
Simulate.click(showFormButton);
expect(showFormButton).toBeCalled();
});
});
and another one:
it('should fire toggle form action on button click', () => {
const toggleFormVisibility = jest.fn();
const component = render(
<Form />
);
const showFormButton = component.getByTestId('toggle-form-button');
Simulate.click(showFormButton);
expect(toggleFormVisibility).toBeCalled();
});
It looks like in your tests, you are trying to render the <Form> instead of the <FormComponent>, that might be causing the problem in your test.
Also in your 2nd test, you are not setting up the toggleFormVisibility mocked function with your component, so that wouldn't be invoked at all, the answer above is pretty reasonable, you might want to consider giving that a shot, not sure why it gets downvoted.
testing-library may make this test easier:
import { render, fireEvent } from '#testing-library/react'
render(<Form />);
fireEvent.click(screen.getByLabelText('Show form'));

Function call inside useEffect not firing in jest test

I have a component that, on button click sends the updated value to parent via props.OnValChange. This is implemented in the useEffect hook.
If I console log the useEffect I can see it being called. But in my test when I do expect(prop.OnValChange).toHaveBeenCalledTimes(1); it says it was called 0 times.
Component:
const MyComp = ({OnValChange}) => {
const [ val, setVal ] = useState(0);
useEffect(() => {
console.log("before");
OnValChange(val);
console.log("after");
}, [val]);
return (
<button onClick={() => setVal(val + 1)}>Count</button>
)
}
Test:
it("Sends val to parent when button is clicked", () => {
const prop = {
OnValChange: jest.fn();
}
const control = mount(<MyComp {...prop} />);
expect(prop.OnValChange).toHaveBeenCalledTimes(0);
control.find(button).simulate("click");
expect(prop.OnValChange).toHaveBeenCalledTimes(1);
})
useEffect will always be called once when the component is initially mounted, and will be called a second time when you trigger a button click, so the correct test should be like this
it("Sends val to parent when button is clicked", () => {
const prop = {
OnValChange: jest.fn();
}
const control = mount(<MyComp {...prop} />);
expect(prop.OnValChange).toHaveBeenCalledTimes(1);
control.find(button).simulate("click");
expect(prop.OnValChange).toHaveBeenCalledTimes(2);
})
If you are always 0 times, I suspect that it is a problem with the version of enzyme-adapter-react-16. When I switch the version to 1.13.0, there will be the same problem as you, you can try enzyme -adapter-react-16 updated to the latest version.

Resources