I have a parent component where a piece of state is defined with useImmer like this:
const [example, setExample] = useImmer({
submitterId: {
value: EMPTY_STRING,
label: 'Submitter ID',
disabled: false,
status: null,
schema: submitterIdSchema
},
submitterName: {
value: EMPTY_STRING,
label: 'Submitter Name',
disabled: false,
status: null,
schema: submitterNameSchema
}});
These states are passed down as props to a child component which I am trying to write jest tests for. This is a simplified example.
export function ChildComponent({
example,
setExample,
}) {
const handleInputChange = async (e, fieldName) => {
const valueToUpdateWith = e.target.value;
const schema = example[fieldName].yupSchema;
const status = await validate(valueToUpdateWith, schema);
setExample((draft) => {
draft[fieldName].status = status;
draft[fieldName].value = valueToUpdateWith;
});
};
return (
<div id="exampleContainer">
<ConnectedFieldGenerator // this just loops through keys in example and generates label/input field groups for them
fieldKeys={Object.keys(example)}
handleInputChange={handleInputChange}
/>
</div>
);
}
I have written the below test in order to cover the handleInputChange function, but in my coverage report the logic inside of setExample is notcovered.
const props = {
example: EXAMPLE_INITIAL_FIELD_STATE,
setExample: jest.fn(),
};
describe('Search Table Renders', () => {
it('input change of example', async () => {
const { container } = render(<ChildComponent{...props} />);
await enterInputValue(container, '#submitterId-input', 'Submitter ID');
expect(props.setBasicSearchState).toHaveBeenCalled();
});
});
How would I cover the below lines in handleInputChange with my tests?
setExample((draft) => {
draft[fieldName].status = status; // NOT COVERED
draft[fieldName].value = valueToUpdateWith; // NOT COVERED
});
You don't need to mock the setExample
function, create a parent component for testing purposes. Keep it as simple as possible, retaining enough logic for a single test case. Then, pass the real setExample
function returned by the useImmer
hook to the <ChildComponent/>
component. Finally, assert the latest state the component has rendered.
E.g.
Child.jsx
:
import React from 'react';
export function ChildComponent({ example, setExample }) {
const handleInputChange = async (e, fieldName) => {
const valueToUpdateWith = e.target.value;
const status = 'ok';
setExample((draft) => {
draft[fieldName].status = status;
draft[fieldName].value = valueToUpdateWith;
});
};
return (
<div id="exampleContainer">
<input type="text" onChange={(e) => handleInputChange(e, 'submitterId')} />
<div>example.submitterId: {example.submitterId.value}</div>
<div>example.submitterId.status: {example.submitterId.status}</div>
</div>
);
}
Child.test.jsx
:
import React from 'react';
import { fireEvent, render, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import { ChildComponent } from './Child';
import { useImmer } from 'use-immer';
describe('ChildComponent', () => {
test('should pass', () => {
// Component for testing purposes, keep it simple.
const ParentComp = () => {
const [example, setExample] = useImmer({
submitterId: {
value: '',
label: 'Submitter ID',
status: null,
},
submitterName: {
value: '',
label: 'Submitter Name',
status: null,
},
});
return <ChildComponent example={example} setExample={setExample} />;
};
render(<ParentComp />);
const textbox = screen.getByRole('textbox');
fireEvent.change(textbox, { target: { value: 'test' } });
expect(screen.getByText('example.submitterId: test')).toBeInTheDocument();
});
});
Test result:
PASS stackoverflow/77582125/Child.test.tsx
ChildComponent
√ should pass (48 ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
Child.tsx | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 1.736 s
Ran all test suites related to changed files.