How to unit test addTodo in redux app? - reactjs

I am trying this example :
https://github.com/reactjs/redux/tree/master/examples/todomvc
Based in this solution I have created a unit test that looks like this:
it('should call addTodo if length of text is greater than 0', () => {
const props = {
addTodo: jest.fn()
}
let cb = shallow(<Header {...props} />)
expect(props.addTodo).not.toBeCalled()
cb.find('TodoTextInput').simulate("onSave",{text:"fsdhsd"});
//error starts here:
expect(props.addTodo).toBeCalled()
});
The result of this one is :
FAIL src/components/NewHeaderTest.spec.js
●
Header enzyme style › should call addTodo if length of text is greater than 0
expect(jest.fn()).toBeCalled()
Expected mock function to have been called.
at Object.it (src/components/NewHeaderTest.spec.js:46:27)
This is part of the component:
handleSave = text => {
console.log('handlesave');
if (text.length > 0) {
this.props.addTodo(text);
}
}
render = () => {
return (
<header className="header">
<TodoTextInput onSave={this.handleSave}>
</TodoTextInput>
</header>)
}
How can I fix the unittest or how to pass in an argument for the simulate statement: cb.find('TodoTextInput').simulate("onSave",{text:"fsdhsd"});

Related

How to find a component inside another component in jest?

I want to find a component inside another component in Jest. After finding the component The length should be 1 but I am getting 0 length.
Test cases code is as follows -
beforeEach(() => {
wrapper = enzyme.mount(
<Rest
onApplyClick={mockOnApplyClick}
translateFn={mockTranslateFn}
someData={mockSomeData}
/>
);
});
and then we have test case -
it('Test case', () => {
let Button= wrapper.find(Button);
Button.simulate('click');
expect(wrapper.find(StyledPopover).prop('open')).toBe(true);
act(() => {
let newWrapper = wrapper
.find(<NewComponent {...props} myData={mockMyData[1]} />)
expect(newWrapper.length).toBe(1);
console.log(newWrapper.length, "newWrapper.length")
});
});
I am getting 0 length in console.log(newWrapper.length) But if we are passing let newWrapper = wrapper.find(NewComponent) Then I am getting length 1. So how to resolve this issue as I want to pass props to NewComponent so that I can find some other elements inside NewComponent.

React Number Format unit test with isAllowed

I am using the react number format for inputting the number input fields.
<TargetRow>
<NumberFormat
type="text"
isNumericString={true}
decimalScale={2}
name="otbTarget"
value={otbTarget === null ? '' : otbTarget}
className="TargetRow__input TargetRow__otbTarget"
onBlur={this.updateTarget}
onChange={this.handleChangeOTBTarget}
isAllowed={this.limitInputValue}
/>
/>
function for the isAllowed, is to limit the length of values entered in the field.
limitInputValue = ({ value }: NumberFormatValues ) => value.length <= ALLOWED_INPUT_LENGTH;
I need to write a unit test for this functionality, to check if the values entered in the field with NumberFormat gets restricted by using the isAllowed method.
I tried using the enzyme simulate for change, but as it turns out it calls the handleChangeOTBTarget method, which is as expected.
I am not able to understand how the isAllowed will be triggered.
Unit test which I am writing is :
it('should restrict values with ALLOWED_INPUT_LENGTH', async () => {
// ALLOWED_INPUT_LENGTH = 3
const mockTarget = 1234512345;
component = shallow(<TargetRow {...defaultProps} otbTarget={mockTarget} />);
const targetInput = component.find('.TargetRow__otbTarget');
targetInput.simulate('change', { target: { value: mockTarget } });
targetInput.simulate('blur');
expect(targetInput.props().value).toBe( 123 );
});
As you can see there is way to simulate onchange, which is working as stated above. But I am not able to trigger the method which is on isAllowed
I would do something as the following:
it('should restrict values with ALLOWED_INPUT_LENGTH', async () => {
const isValid
// ALLOWED_INPUT_LENGTH = 3
const mockTarget = 1234512345;
component = shallow(<TargetRow {...defaultProps} otbTarget={mockTarget} />);
component.instance().limitInputValue = jest.fn(); // MOCK THE METHOD WE ADD TO isAllowed prop
const targetInput = component.find('.TargetRow__otbTarget');
targetInput.simulate('change', { target: { value: mockTarget } });
targetInput.simulate('blur');
expect(targetInput.props().value).toBe( 123 );
expect(component.instance().limitInputValue).toHaveBeenCalled() // Check if the method has been called at all
});

Test a component with useState and setTimeout

Code structure is as same as given below:
FunctionComponent.js
...
const [open, handler] = useState(false);
setTimeout(() => {handler(true);}, 2000);
...
return (
...
<div className={active ? 'open' : 'close'}>
)
comp.test.js
jest.useFakeTimers();
test('test case 1', () => {
expect(wrapper.find('open').length).toBe(0);
jest.advanceTimersByTime(2000);
expect(wrapper.find('open').length).toBe(1);
jest.useRealTimers();
});
The problem is that the expression written in bold in test is saying the length of open class is still 0, so actual and expected are not meeting.
You want to test the outcome of the hook and not the hook itself since that would be like testing React. You effectively want a test where you check for if the open class exists and then doesn't exist (or vice versa), which it looks like you're trying.
In short, to solve your issue you need to use ".open" when selecting the class. I would also suggest using the .exists() check on the class instead of ".length()" and then you can use ".toBeTruthy()" as well.
You could look into improve writing your tests in a Jest/Enzyme combined format as well:
import { shallow } from 'enzyme';
import { FunctionComponent } from './FunctionComponent.jsx';
jest.useFakeTimers();
describe('<FunctionCompnent />', () => {
const mockProps = { prop1: mockProp1, prop2: mockProp2, funcProp3: jest.fn() };
const wrapper = shallow(<FunctionComponent {...mockProps} />);
afterEach(() => {
jest.advanceTimersByTime(2000);
});
afterAll(() => {
jest.useRealTimers();
});
it('should render as closed initially', () => {
expect(wrapper.find('.close').exists()).toBeTruthy();
// you could also add the check for falsy of open if you wanted
// expect(wrapper.find('.open').exists()).toBeFalsy();
});
it('should change to open after 2 seconds (or more)', () => {
expect(wrapper.find('.open').exists()).toBeTruthy();
// you could also add the check for falsy of close if you wanted
// expect(wrapper.find('.close').exists()).toBeFalsy();
});
});
EDIT: Sorry realised I wrote the test backwards after checking your code again, they should be fixed now.

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.

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.

Resources