Jest on React, test a prop is called - reactjs

I have a component with a props that is an action called changeReason()
And then, I have a function called when I press a button. The function is:
const handleNext = React.useCallback(() => {
if (!reason) {
changeReason({
code: defaultReason,
value: defaultReason
});
}
}
I want to test that changeReason() prop is called, I'm trying to do a test like this:
const changeReason = jest.fn();
const rendered = render(
<TransactionReason
changeReason={changeReason}
reason={null}
/>
);
// When some button is pressed...
const reasonNext = rendered.getByTestId('reasonButton');
// it must fire an event
fireEvent.press(reasonNext);
const FakeFun = jest.spyOn(rendered, 'changeReason');
expect(FakeFun).toHaveBeenCalled();
I'm getting:
Cannot spy the handleNext property because it is not a function; undefined given instead
How could I test that I'm calling that action?

Related

How to test child components props method in the parent component test

I'm writing a test for my parent class which contains a child component. The code coverage reports that I haven't covered test for child components props method. Below is my structure of my components
export const CreateSRFullScreenPanel: React.FC<Props> = ({
interPluginContext,
srType,
errorMessage,
}: Props) => {
const [disableButton, setDisableButton] = React.useState(false);
const [submitSR, setSubmitSR] = React.useState(false);
const returnToHomePage = (): void => {
getRouteClient()
.changeRoute(getActiveRegionBaseUrl(state.regionName));
};
const cancelOp = interPluginContext.isValid()
? CancelAction
: returnToHomePage;
...
<childSR disableButton={disableButton} onSubmitComplete={() =>
setSubmitSR(false)}/>
<Button
type={ButtonType.Submit}
buttonStyle={ButtonStyle.Primary}
onClick={cancelOp}
>
Cancel
</Button>
};
When I wrote a test like below I was getting undefined for method calls.
it("check props method gets called", () => {
const wrapper = mount(
<ParentSR {...props} />
);
console.log(wrapper.find(CreateSR).props().onSubmitComplete()); // undefined
console.log(wrapper.find(CreateSR).props().disableButton()); // true
});
Also, when I click on a cancel button cancelOp method gets called. How do I mock returnToHomePage method calls?

Jest function call for functional component gives error on stimulate click

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.

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.

Jest/Enzyme Shallow testing RFC - not firing jest.fn()

I'm trying to test the onChange prop (and the value) of an input on an RFC. On the tests, trying to simulate the event doesn't fire the jest mock function.
The actual component is connected (with redux) but I'm exporting it also as an unconnected component so I can do a shallow unit test. I'm also using some react-spring hooks for animation.
I've also tried to mount instead of shallow the component but I still get the same problem.
MY Component
export const UnconnectedSearchInput: React.FC<INT.IInputProps> = ({ scrolled, getUserInputRequest }): JSX.Element => {
const [change, setChange] = useState<string>('')
const handleChange = (e: InputVal): void => {
setChange(e.target.value)
}
const handleKeyUp = (): void => {
getUserInputRequest(change)
}
return (
<animated.div
className="search-input"
data-test="component-search-input"
style={animateInputContainer}>
<animated.input
type="text"
name="search"
className="search-input__inp"
data-test="search-input"
style={animateInput}
onChange={handleChange}
onKeyUp={handleKeyUp}
value={change}
/>
</animated.div>
)
}
export default connect(null, { getUserInputRequest })(UnconnectedSearchInput);
My Tests
Here you can see the test that is failing. Commented out code is other things that I-ve tried so far without any luck.
describe('test input and dispatch action', () => {
let changeValueMock
let wrapper
const userInput = 'matrix'
beforeEach(() => {
changeValueMock = jest.fn()
const props = {
handleChange: changeValueMock
}
wrapper = shallow(<UnconnectedSearchInput {...props} />).dive()
// wrapper = mount(<UnconnectedSearchInput {...props} />)
})
test('should update input value', () => {
const input = findByTestAttr(wrapper, 'search-input').dive()
// const component = findByTestAttr(wrapper, 'search-input').last()
expect(input.name()).toBe('input')
expect(changeValueMock).not.toHaveBeenCalled()
input.props().onChange({ target: { value: userInput } }) // not geting called
// input.simulate('change', { target: { value: userInput } })
// used with mount
// act(() => {
// input.props().onChange({ target: { value: userInput } })
// })
// wrapper.update()
expect(changeValueMock).toBeCalledTimes(1)
// expect(input.prop('value')).toBe(userInput);
})
})
Test Error
Nothing too special here.
expect(jest.fn()).toBeCalledTimes(1)
Expected mock function to have been called one time, but it was called zero times.
71 | // wrapper.update()
72 |
> 73 | expect(changeValueMock).toBeCalledTimes(1)
Any help would be greatly appreciated since it's been 2 days now and I cn't figure this out.
you don't have to interact with component internals; instead better use public interface: props and render result
test('should update input value', () => {
expect(findByTestAttr(wrapper, 'search-input').dive().props().value).toEqual('');
findByTestAttr(wrapper, 'search-input').dive().props().onChange({ target: {value: '_test_'} });
expect(findByTestAttr(wrapper, 'search-input').dive().props().value).toEqual('_test_');
}
See you don't need to check if some internal method has been called, what's its name or argument. If you get what you need - and you require to have <input> with some expected value - it does not matter how it happened.
But if function is passed from the outside(through props) you will definitely want to verify if it's called at some expected case
test('should call getUserInputRequest prop on keyUp event', () => {
const getUserInputRequest = jest.fn();
const mockedEvent = { target: { key: 'A' } };
const = wrapper = shallow(<UnconnectedSearchInput getUserInputRequest={getUserInputRequest } />).dive()
findByTestAttr(wrapper, 'search-input').dive().props().onKeyUp(mockedEvent)
expect(getUserInputRequest).toHaveBeenCalledTimes(1);
expect(getUserInputRequest).toHaveBeenCalledWith(mockedEvent);
}
[UPD] seems like caching selector in interm variable like
const input = findByTestAttr(wrapper, 'search-input').dive();
input.props().onChange({ target: {value: '_test_'} });
expect(input.props().value).toEqual('_test_');
does not pass since input refers to stale old object where value does not update.
At enzyme's github I've been answered that it's expected behavior:
This is intended behavior in enzyme v3 - see https://github.com/airbnb/enzyme/blob/master/docs/guides/migration-from-2-to-3.md#calling-props-after-a-state-change.
So yes, exactly - everything must be re-found from the root if anything has changed.

Spy a method that has been called when onPress on an Alert

I am trying to test a removeService method that gets called when onPress 'ing inside an Alert like this
Alert.alert(
[...],
onPress: () => removeService();
);
}
this is the test:
const spyAlert = jest.spyOn(Alert,
"alert");
const spyRemoveService = jest.fn();
const wrapper = shallow(
<Component removeService={spyRemoveService} />
);
wrapper
.find("RemoveServiceButton")
.props()
.handleRemoveService();
// Click onPress
spyAlert.mock.calls[0][2][1].onPress();
expect(spyRemoveService).toHaveBeenCalledTimes(1);
However, when I run the test I get TypeError: removeService is not a function
why?

Resources