I have a material-ui TextField
which on focus opens a Popper
. I am trying to test this behavior using react-testing-library.
Component:
import ClickAwayListener from '@material-ui/core/ClickAwayListener';
import Grow from '@material-ui/core/Grow';
import Paper from '@material-ui/core/Paper';
import Popper from '@material-ui/core/Popper';
import TextField from '@material-ui/core/TextField';
import React from 'react';
import { ItemType } from './types';
export type TextFieldDropDownProps = {
items: ItemType,
};
export function TextFieldDropDown(props: TextFieldDropDownProps) {
const [searchTerm, setSearchTerm] = React.useState('');
const [anchorEl, setAnchorEl] = React.useState(null);
const handleSearchTermChange = (event: any) => {
setSearchTerm(event.target.value);
};
const onFoucs = (event: any) => {
setAnchorEl(event.currentTarget);
};
const handleClose = (event: any) => {
setAnchorEl(null);
};
const popperTrans = ({ TransitionProps }: any) => {
return (
<Grow
{...TransitionProps}
style={{ transformOrigin: '0 0 0' }}
>
<Paper>
<ul />
</Paper>
</Grow>
);
};
const open = Boolean(anchorEl);
return (
<ClickAwayListener onClickAway={handleClose}>
<div>
<TextField
onChange={handleSearchTermChange}
onFocus={onFoucs}
value={searchTerm}
label='Search'
/>
<Popper
open={open}
anchorEl={anchorEl}
transition={true}
disablePortal={true}
placement='bottom-start'
style={{zIndex: 999, minWidth: '100%'}}
>
{popperTrans}
</Popper>
</div>
</ClickAwayListener>
);
}
Test:
import { fireEvent, render, wait } from '@testing-library/react';
import { getTestData } from 'test-data';
import React from 'react';
import { TextFieldDropDown } from './TextFieldDropDown';
test('that on focus on input field, the component shows a dropdown', async () => {
// Set up test data
const items: any = getTestData();
// Render component
const props = { items };
const { getByRole, queryByRole } = render(<TextFieldDropDown {...props} />, {});
await wait();
expect(queryByRole('list')).toBeNull();
// Fire event
const placeSelectInputField = getByRole('textbox') as HTMLInputElement;
fireEvent.focus(placeSelectInputField);
// Verify that dropdown is shown
expect(queryByRole('list')).toBeInTheDocument();
});
When I run the test, I get the following error - TypeError: document.createRange is not a function
.
The above error occurred in the <div> component:
in div (created by ForwardRef(Portal))
in ForwardRef(Portal) (created by ForwardRef(Popper))
in ForwardRef(Popper) (created by TextFieldDropDown)
in div (created by ForwardRef(ClickAwayListener))
in ForwardRef(ClickAwayListener) (created by TextFieldDropDown)
in TextFieldDropDown
in Provider (created by AllTheProviders)
in AllTheProviders
Consider adding an error boundary to your tree to customize error handling behavior.
The above error occurred in the <ForwardRef(Popper)> component:
in ForwardRef(Popper) (created by TextFieldDropDown)
in div (created by ForwardRef(ClickAwayListener))
in ForwardRef(ClickAwayListener) (created by TextFieldDropDown)
in TextFieldDropDown
in Provider (created by AllTheProviders)
in AllTheProviders
Consider adding an error boundary to your tree to customize error handling behavior.
● that on focus on input field, the component shows a dropdown
TypeError: document.createRange is not a function
46 | // Fire event
47 | const TextFieldComponent = getByRole('textbox') as HTMLInputElement;
> 48 | fireEvent.focus(TextFieldComponent);
| ^
49 |
50 | // Verify that dropdown is shown
51 | expect(queryByRole('list')).toBeInTheDocument();
How do I make this work?
Referring to this github issue, I found out that we can fix the error with following code put in test set up file.
(global as any).document.createRange = () => ({
setStart: () => {},
setEnd: () => {},
commonAncestorContainer: {
nodeName: 'BODY',
ownerDocument: document,
},
});
The issue is with the underlying implementation of PopperJS calling the document.createRange
function when there is no DOM API for it to call. The solution is to mock PopperJS.
// __mocks__/popper.js.js
import PopperJs from 'popper.js';
export default class Popper {
constructor() {
this.placements = PopperJs.placements;
return {
update: () => {},
destroy: () => {},
scheduleUpdate: () => {}
};
}
}
jest will pick any mocks in the /__mocks__
directory automatically, so simply adding this file should fix your issue
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With