I'm trying to write a unit test for when a checkbox is checked, which would lead to a function being called. The React component is:
const changeExamBoards = (e, filterOptions) => {
if(e.target.checked && !(filterOptions.examBoards.includes(e.target.value))) {
setfilterOptions({ ...filterOptions, examBoards: [...filterOptions.examBoards, e.target.value] });
}
else {
setfilterOptions({ ...filterOptions, examBoards: [...filterOptions.examBoards.filter(item => item !== e.target.value)] });
}
};
<div className='option-container' onChange={e => changeExamBoards(e, filterOptions)}>
<div className='question-option-container'><input className='question-option' type="checkbox" name="AQA" value="AQA" /> AQA</div>
<div className='question-option-container'><input className='question-option' type="checkbox" name="Edexcel" value="Edexcel" /> Edexcel</div>
<div className='question-option-container'><input className='question-option' type="checkbox" name="OCR" value="OCR" /> OCR</div>
<div className='question-option-container'><input className='question-option' type="checkbox" name="WJEC" value="WJEC" /> WJEC</div>
</div>
The unit test I've been trying to run:
describe('QuestionFilter component', () => {
let store;
let wrapper;
let mockChangeExamBoards;
let mockRefreshPage;
let mockChangeDifficulty;
beforeEach(() => {
store = mockStore({
question: []
});
mockChangeExamBoards = jest.fn();
mockRefreshPage = jest.fn();
store.dispatch = jest.fn();
mockChangeDifficulty = jest.fn();
const mockProps = {
store,
refreshPage: mockRefreshPage,
changeExamBoards: mockChangeExamBoards,
changeDifficulty: mockChangeDifficulty
}
wrapper = mount(<QuestionFilter {...mockProps} />
)
});
it('expect changeExamBoards to be called', () => {
const eMock = { preventDefault: jest.fn(), target: 'fake target', checked: true };
expect(mockChangeExamBoards).not.toHaveBeenCalled();
wrapper.find('.question-option-container')
.at(0)
.simulate('change', eMock);
expect(mockChangeExamBoards).toHaveBeenCalled();
});
However no matter how I try I keep getting this error:
QuestionFilter component › expect change difficulty to be called
expect(jest.fn()).toHaveBeenCalled()
Expected number of calls: >= 1
Received number of calls: 0
I thought the simulate would call the mock function but nothing seems to actually call it?
Related
I've a jest test that is failing on addition of a new component to the page. The test is about showing of an error alert once error occurs. Code works in local environment but fails during commit.
Error Text:
TestingLibraryElementError: Unable to find an element with the text:
Student is unable to perform register/unregister activities.. This could be because
the text is broken up by multiple elements. In this case, you can
provide a function for your text matcher to make your matcher more
flexible.
Test:
jest.mock('react-query', () => ({
...jest.requireActual('react-query'),
useMutation: jest.fn((_key, cb) => {
cb();
return { data: null };
})
}));
const useMutation = useMutationHook as ReturnType<typeof jest.fn>;
describe('StatusAlert', () => {
beforeEach(() => {
useMutation.mockReturnValue({});
});
afterEach(() => {
jest.restoreAllMocks();
});
it('should show error', () => {
useMutation.mockReturnValueOnce({
isError: true
});
const { getByText } = render(
<StudentRegister
students={[studentStub, studentStub]}
onSuccess={jest.fn()}
/>
);
expect(getByText(ErrorDict.ErrorRequest)).toBeInTheDocument();
});
StudentRegister:
Adding this component is causing the above mentioned error:
interface Props {
selectedStudents: Array<Student>;
onSuccessCallback: () => void;
}
export const StudentSelectionBar: FC<Props> = ({
selectedStudents,
onSuccessCallback
}) => {
const [isOpenDropCourseModal, setisOpenDropCourseModal] =
useState(false);
const [studentIds, setStudentIds] = useState<string[]>([]);
useEffect(() => {
setStudentIds(selectedStudents.map((student) =>
student.id));
}, [selectedStudents]);
const onToggleOpenDropCourseModal = useCallback(() => {
setisOpenDropCourseModal(
(state) => !state
);
}, []);
const {
isError: isDropCourseError,
isSuccess: isDropCourseSuccess,
isLoading: isDropCourseLoading,
mutateAsync: DropCourseMutation,
error: DropCourseError
} = useMutation<void, ApiError>(
() => dropCourse(selectedStudents.map((student) =>
student.id)),
{
onSuccess() {
onToggleOpenDropCourseModal();
onSuccess();
}
}
);
return (
<>
<StatusAlert
isError={isDropCourseError}
isSuccess={isDropCourseSuccess}
errorMessage={
dropCourseError?.errorMessage ||
ErrorMessages.FailedPostRequest
}
successMessage="Students successfully dropped from
course"
/>
<StatusAlert
isError={registerMutation.isError}
isSuccess={registerMutation.isSuccess}
errorMessage={
registerMutation.error?.errorMessage ||
ErrorDict.ErrorRequest
}
successMessage="Students successfully registered"
/>
<StatusAlert
isError={isError}
isSuccess={isSuccess}
errorMessage={
error?.errorMessage ||
ErrorDict.ErrorRequest
}
successMessage="Students successfully unregistered"
/>
<Permissions scope={[DropCourseUsers]}>
<LoadingButton
color="error"
variant="contained"
onClick={onToggleDropCourseUserModal}
className={styles['action-button']}
loading={isDropCourseLoading}
loadingPosition="center"
disabled={registerMutation.isLoading || isLoading}
>
drop Course
</LoadingButton>
</Permissions>
<DropCourseModal
isOpen={isOpenDropCourseModal}
onCloseModal={onToggleOpenDropCourseModal}
onArchiveUsers={DropCourseMutation}
users={studentIds}
/>
</>
);
};
Update:
I've noticed that removing useEffect() hook from the component, makes it render correctly in the test. Its function is to update the state variable holding studentIds on every selection on the list.
Is there a way to mock following useEffect hook with dependency in the test?
const [studentIds, setStudentIds] = useState<string[]>([]);
useEffect(() => {
setStudentIds(selectedStudents.map((student) => student.id));
}, [selectedStudents]);
I need to assert that SearchInputActions.onSearchActivated(value) is called when something is written in an input. It is a callback which is in change handler handleChange. I've been trying to create to mocks - one for handleChange and one for search but it did not work either. I am using jest and enzyme for tests.
const SearchInput = () => {
const search = throttle(event => {
const value = event.target.value;
SearchInputActions.onSearchActivated(value);
});
const handleChange = event => {
event.persist();
search(event);
};
return (
<div>
<SomeChildComponent />
<input type="text" onChange={handleChange} />
</div>
)
}
Test:
it('should dispatch search action', async () => {
const tree = mount(<SearchInput />);
const spySearch = jest.spyOn(SearchInputActions, 'onSearchActivated');
SearchInputActions.onSearchActivated.mockImplementation(() => {})
tree.find('input').simulate('change', {target: value: 'test'}});
expect(spySearch).toBeCalled();
}
I figured it out: Because the callback function was throttled (lodash throttle) I needed to add jest.useFakeTimers(); Final code looks like this:
jest.useFakeTimers();
it('should dispatch search action', async () => {
const tree = mount(<SearchInput />);
const spySearch = jest.spyOn(SearchInputActions, 'onSearchActivated');
SearchInputActions.onSearchActivated.mockImplementation(() => {})
tree.find('input').simulate('change', {target: value: 'test'}});
expect(spySearch).not.toBeCalled();
jest.runAllTimers();
expect(spySearch).toBeCalled();
SearchInputActions.onSearchActivated.mockRestore();
}
I am trying to test a functional component. And the goal is to evaluate that the value change correctly.
I have only managed to carry out the test checking that it renders. But I can't find a way to pass the props to him
InputPassword
export default function InputPassword({ password, SetPassword }: any) {
return (
<input
type="password"
placeholder="password"
value={password ?? ''}
onChange={(event) => SetPassword(event.target.value)}
/>
);
}
Test:
test('Login InputPassword', () => {
render(<InputPassword />);
const username_input = screen.queryByPlaceholderText(/password/i);
});
Update final code
test('Login InputPassword', async () => {
const fakePass = '123';
const fakeCb = jest.fn();
render(<InputPassword password={fakePass} SetPassword={fakeCb} />);
const usernameInput = screen.queryByPlaceholderText(/password/i);
expect(usernameInput).toHaveValue(fakePass);
fireEvent.change(usernameInput, { target: { value: '000' } });
expect(fakeCb).toHaveBeenCalled();
});
Inside the render function you can pass props to the component just like you would pass props anywhere else.
test('Login InputPassword', () => {
render(<InputPassword password="123" />);
const username_input = screen.queryByPlaceholderText(/password/i);
});
Based on your comment:
test("Login InputPassword", async () => {
const fakePass = "123";
const fakeCb = jest.fn();
render(<InputPassword password={fakePass} setPassword={fakeCb} />);
const usernameInput = screen.queryByPlaceholderText(/password/i);
expect(usernameInput).toHaveValue(fakePass);
await userEvent.type(username_input, "changed");
expect(usernameInput).toHaveValue("changed");
expect(fakeCb).toHaveBeenCalledTimes(7);
});
On mount the input displays the password that is given to it via props. Then after the user provides a new password which calls the handler accordingly and the input's value is also updated.
enter image description here
Find the attached image
I am getting this following error, while running Unit test for my react file.
I couldn't able to set value for a state method.
TestFile.tsx
const [attr, setAttributes] = useState<any>(initialState);
const getDetail = async () => {
if (Id) {
const form: IForm = await getResponse(Id);
console.log("||" + form.attributes.sourceName)
setFormResponseAttributes(form.attributes);
console.log("***" + JSON.stringify(attr));
};
useEffect(() => {
getDetail();
}, []);
return(
{attr.sourceName === 'ECR' ?
<div className='fe_u_padding--right-medium'>
<Button
id='saveDraftButtonId'
text='Save as Draft'
onClick={() => saveForm(true)}
variant='secondary'
className='hide-while-printing'
/>
</div>
: null
);
}
export
TestFile.test.tsx
it('load form response', () => {
const getResponseSpy = jest.spyOn(ResponseApi, 'getResponse');
getResponseSpy.mockImplementation(() => Promise.resolve(testUtils.Response));
const setAppContextSpy = jest.fn();
let setResponseAttributes;
const wrapper = mount(
<BrowserRouter>
<RegisteredApplicationContext.Provider
value={{ appContext: testUtils.registeredApplication, setAppContext: setAppContextSpy }}
>
<FormResponse match={{ params: { formResponseId: 'formResponseId1' } }} />
</RegisteredApplicationContext.Provider>
</BrowserRouter>
);
wrapper.find(Button).find('#saveDraftButtonId').first().simulate('click', {});
wrapper.update();
expect(getFormResponseSpy).toBeCalled();
TestUtils
export const Response: IForm = {
id: uuidv4(),
attributes: {"contextId": "111","sourceName" : "ECR"}
}
I am getting error while reading the SaveDraftButtonId,
Method “simulate” is meant to be run on 1 node. 0 found instead.,
it could able to find that button coz it couldn't set the value for attr state value
Any inputs would be helpful
TIA
Unable to make the following test pass :
Using React JS / enzyme and jest
I already asked a similar question and try to apply the same method, but its not going through. Any reason ?? Substitute shallow = mount ? or add a dive() ?
file.test.js -
// jest mock functions (mocks this.props.func)
const updateSelectedFormJSON = jest.fn();
const closeModal = jest.fn();
const onClick = jest.fn();
const onSaveClick = jest.fn();
// defining this.props
const baseProps = {
selectedFormJSON :{
FORM_COLUMN:[],
},
updateSelectedFormJSON,
closeModal,
onClick,
onSaveClick,
describe('SpecifyBodyModal Test', () => {
let wrapper;
let tree;
beforeEach(() => wrapper = mount(<SpecifyBodyModal {...baseProps} />));
it('should call closeModal functions on button click', () => {
baseProps.closeModal.mockClear();
wrapper.setProps({
updateSelectedFormJSON :null
});
wrapper.find('.add-custom-field-close').at(0).simulate('click')
expect(baseProps.closeModal).toHaveBeenCalled();
});
the 2nd test is not passing: error Method “simulate” is meant to be run on 1 node. 0 found instead.
it('should call onSaveClick functions on button click', () => {
baseProps.onSaveClick.mockClear();
wrapper.setProps({
closeModal :null
});
wrapper.find('.tran-button specify-body-continue').at(1).simulate('click')
expect(baseProps.onSaveClick).toHaveBeenCalled();
here is the render file js.
onSaveClick = () => {
let json = Object.assign({}, this.props.selectedFormJSON);
for(let i in json.FORM_COLUMN) {
json.FORM_COLUMN[i].IncludeInBody = this.state[json.FORM_COLUMN[i].Name];
}
this.props.updateSelectedFormJSON(json);
this.props.closeModal();
render() {
return (
<div className='specify-grid-modal'>
<div className='fullmodal'>
<div className='fullmodal_title'>Specify Body</div>
<div title="Close Window" className="add-custom-field-close" onClick={() => this.props.closeModal()}><FontAwesome name='xbutton' className='fa-times preview-close' /></div>
</div>
<button className='tran-button specify-body-continue' onClick={() => {this.onSaveClick()}} >
Continue
</button>
<div className='specify-body-wrapper'>
{this.renderColumns()}
</div>
</div>
)
}
The error means that there are no matches for className.add-custom-field-close selector.
className is prop name and shouldn't be included into the selector:
wrapper.find('.add-custom-field-close').at(0).simulate('click')
The selector of to find the element looks wrong. Its className.add-custom-field-close but should be .add-custom-field-close
Thanks for the help
it('should call onSaveClick functions on button click', () => {
baseProps.closeModal.mockClear();
wrapper.setProps({
updateSelectedFormJSON :null
});
wrapper.find('.add-custom-field-close').at(0).simulate('click')
expect(baseProps.closeModal).toHaveBeenCalled();
});