New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to properly mock Select and Option Component with jest. #21080
Comments
Please follow jest doc for more detail. It's not antd issue. |
@gwuah Do you found a solution to mock the |
@alibenmessaoud no bro. I ended up using enzyme to test. It never found out how to mock both the select and option component properly |
Here is how to mock Select and Option. jest.mock('antd', () => {
const antd = jest.requireActual('antd');
const Select = ({ children, onChange }) => {
return <select onChange={e => onChange(e.target.value)}>{children}</select>;
};
Select.Option = ({ children, ...otherProps }) => {
return <option {...otherProps}>{children}</option>;
}
return {
...antd,
Select,
}
} |
nice solution from @wildan2711 !!! |
It worked great @wildan2711 . Thanks. Do you know by any change how to put this under |
Based on @wildan2711 nice solution (thanks !), this is a more complete one, supporting multiple selects, OptGroup and a few antd props : jest.mock('antd', () => {
const antd = jest.requireActual('antd');
const Select = (props) => {
const multiple = ['tags', 'multiple'].includes(props.mode);
return (
<select
value={props.value}
defaultValue={props.defaultValue}
multiple={multiple}
disabled={props.disabled}
data-testid={props['data-testid']}
className={props.className}
onChange={(e) =>
props.onChange(multiple ? Array.from(e.target.selectedOptions).map((option) => option.value) : e.target.value)
}
>
{props.children}
</select>
);
};
Select.Option = ({ children, ...otherProps }) => <option {...otherProps}>{children}</option>;
Select.OptGroup = ({ children, ...otherProps }) => <optgroup {...otherProps}>{children}</optgroup>;
return { ...antd, Select };
}); It is worth noting that you can easily disable this mock by adding |
@florianMo This solution looks perfect but I just can't get it to work. I am have my custom component that renders a whole bunch of stuff, and among the stuff in there is a Select with many Select.Option children. In my test, I am just ensuring that certain events are triggered when the person selects something from the list and then clicks OK. However, the above stub just doesn't replace the Select. I can log the HTML out and see it's still the same antd markup with the hidden options (I am also confused why this is like this, how can a screen reader work with this?). jest.mock("antd", () => {
const antd = jest.requireActual("antd");
const Select = (props: any) => {
const multiple = ["tags", "multiple"].includes(props.mode);
return (
<select
value={props.value}
defaultValue={props.defaultValue}
multiple={multiple}
disabled={props.disabled}
data-testid={props["data-testid"]}
className={props.className}
onChange={(e) =>
props.onChange(
multiple
? Array.from(e.target.selectedOptions).map(
(option) => option.value
)
: e.target.value
)
}
>
{props.children}
</select>
);
};
Select.Option = ({ children, ...otherProps }: any) => (
<option {...otherProps}>{children}</option>
);
Select.OptGroup = ({ children, ...otherProps }: any) => (
<optgroup {...otherProps}>{children}</optgroup>
);
return { ...antd, Select };
});
const handleRedeem = sinon.mock();
const component = render(
<GiftManagementPanel
onRedeemedConfirmed={handleRedeem}
gifts={[gift]}
businesses={[business]}
/>
);
// Ensure both the gift "type" and redeem button are present
await screen.findByTestId(
`gift-${gift.id}-${gift.data.properties.validFor}`
);
const redeemButton = await screen.findByLabelText("Redeem");
userEvent.click(redeemButton);
const modal = await screen.findByRole("document");
const selectMenu = await findByRole(modal, "combobox"); // Finds this, but it's not the stubbed version above
const options = Array.from(selectMenu.querySelectorAll("option")).map((o) =>
o.getAttribute("value")
);
expect(options).toEqual(["HAHAHA"]);
expect(selectMenu).toHaveValue("HAHAHA"); Is this because my component is initializing with the original select first? Where should I put this stub in a normal CRA where it is loaded first? |
@Gibbo3771 in my case (regular CRA app), I have this mock in I did a quick test, it works fine if I add my mock at the top of my test file : jest.mock("antd", () => { ... });
describe('...', () => {
test(...);
}); But it does not work if I add it in a specific test : jest.mock("antd", () => { ... });
describe('...', () => {
test('something', () => {
jest.mock("antd", () => { ... });
expect(...);
});
}); |
@florianMo Thanks for the very fast response. Hoisting it to the top of the file, outside of the test does indeed fix it. I am probably going to assume this is why my sinon stubs were not working as well. I am considering wrapping this selector in it's own component and stubbing that instead, that allows me to stub it at any point in the test rather than only at the top. |
You can also mock List from antd like this - `jest.mock('antd', () => {
})` |
Based on everybody replies this Dragger (from antd Upload) should work?
This is placed outside of the tests but does not appear to have any effect |
Adding a test input to support tag mode / search behaviour : jest.mock('antd', () => {
const React = require('react');
const antd = jest.requireActual('antd');
// <Select />
// Testing mode="tags" : see OdLinkedRelation.test.tsx
const Select = (props) => {
const [text, setText] = React.useState('');
const multiple = ['tags', 'multiple'].includes(props.mode);
const handleKeyDown = (event) => {
if (event.key === 'Enter') {
props.onChange([text]);
setText('');
}
};
return (
<>
<select
// add value in custom attribute to handle async selector, where no option exists on load (need to type to fetch option)
data-value={props.value || undefined}
value={props.value || undefined}
defaultValue={props.defaultValue || undefined}
multiple={multiple || undefined}
disabled={props.disabled || undefined}
data-testid={props['data-testid']}
className={props.className}
onChange={(e) =>
props.onChange(
multiple ? Array.from(e.target.selectedOptions).map((option) => option.value) : e.target.value
)
}
>
{props.children}
</select>
{props.mode === 'tags' && (
<input
data-testid={props['data-testid'] + 'Input'}
type="text"
value={text}
onChange={(e) => setText(e.target.value)}
onKeyDown={handleKeyDown}
/>
)}
</>
);
};
Select.Option = ({ children, ...otherProps }) => <option {...otherProps}>{children}</option>;
Select.OptGroup = ({ children, ...otherProps }) => <optgroup {...otherProps}>{children}</optgroup>;
return { ...antd, Select };
}); |
Reproduction link
Steps to reproduce
I want to be able to mock my select and option component. The code below is my best attempt, but it's still not working.
What is expected?
I expect my custom select component and option component to be rendered instead of the default antd component.
What is actually happening?
I expect both Option and Select to be mocked properly, but on the contrary, it isn't working.
Message :
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object
I'm using react-testing-library to write my tests
The text was updated successfully, but these errors were encountered: