are there anyone see the problem here? If I put checked in state on change it works. But I don't want to duplicate the checked state data and watch it every single props change.
Parent components gets the latest checked information and put it in own state and if parent isChecked change, FormCheckBox isChecked changes. I think, it works async and when I reach latest line of my test code, parent update does not finish, so I see the stale isChecked.
export default function FormCheckBox(props: IFormCheckBoxProps) {
const { onChange, label, isChecked, error, errorModelLabel = '' } = props
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event.currentTarget.checked)
}
const errorMsg = getFormErrorMsg(error, errorModelLabel)
return (
<FormGroup
as={Row}
className={cx('mb-3', { 'is-invalid': errorMsg })}
controlId="catalogDescription"
data-testid="slvy-formcheckbox"
>
<FormLabel column className="d-flex align-items-center justify-content-end fw-bold" sm={2}>
{label}
</FormLabel>
<Col className="d-flex align-items-center" sm={10}>
<input
checked={isChecked}
className={cx('checkbox', { 'is-invalid': errorMsg })}
type="checkbox"
onChange={handleChange}
/>
<Form.Control.Feedback type="invalid">{errorMsg}</Form.Control.Feedback>
</Col>
</FormGroup>
)
}
it('checkbox must use fireEvent.click', async () => {
const handleChange = jest.fn()
props.isChecked = false
props.onChange = handleChange
const { container } = render(<FormCheckBox {...props} />)
const checkbox = container.querySelectorAll("input[type='checkbox']")[0] as HTMLInputElement
fireEvent.click(checkbox)
expect(handleChange).toHaveBeenCalledTimes(1)
expect(checkbox.checked).toBe(true)
})
CodePudding user response:
Since the FormCheckBox
is a controlled component, the input’s value is always driven by the React state. So you need a wrapper component to provide this state. In your test case, even though the mock handleChange
function is called, we need to update the isChecked
state so that the FormCheckBox
component will re-render with this new state.
E.g.
index.tsx
:
import React from 'react';
export default function FormCheckBox({ onChange, isChecked }) {
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
onChange(event.currentTarget.checked);
};
return <input checked={isChecked} type="checkbox" onChange={handleChange} />;
}
To keep things simple. I removed the irrelevant code.
index.test.tsx
:
import { fireEvent, render } from '@testing-library/react';
import React, { useState } from 'react';
import FormCheckBox from './';
describe('73184212', () => {
test('should pass', () => {
const Wrap = () => {
const [isChecked, setIsChecked] = useState(false);
return <FormCheckBox isChecked={isChecked} onChange={() => setIsChecked(!isChecked)} />;
};
const { container } = render(<Wrap />);
const checkbox = container.querySelectorAll("input[type='checkbox']")[0] as HTMLInputElement;
fireEvent.click(checkbox);
expect(checkbox.checked).toBe(true);
});
});
Test result:
PASS stackoverflow/73184212/index.test.tsx (10.998 s)
73184212
✓ should pass (30 ms)
-----------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-----------|---------|----------|---------|---------|-------------------
All files | 100 | 100 | 100 | 100 |
index.tsx | 100 | 100 | 100 | 100 |
-----------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 11.607 s