使用 react-testing-library 测试材料 ui popper 时如何修复“TypeError:document.createRange 不是函数”错误?
Posted
技术标签:
【中文标题】使用 react-testing-library 测试材料 ui popper 时如何修复“TypeError:document.createRange 不是函数”错误?【英文标题】:How to fix `TypeError: document.createRange is not a function` error while testing material ui popper with react-testing-library? 【发布时间】:2020-06-05 13:25:03 【问题描述】:我有一个 Material-ui TextField
,它在焦点上打开一个 Popper
。我正在尝试使用 react-testing-library 测试这种行为。
组件:
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>
);
测试:
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();
);
当我运行测试时,我收到以下错误 - 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();
我该如何进行这项工作?
【问题讨论】:
【参考方案1】:参考this github issue,我发现我们可以通过将以下代码放入测试设置文件来修复错误。
(global as any).document.createRange = () => (
setStart: () => ,
setEnd: () => ,
commonAncestorContainer:
nodeName: 'BODY',
ownerDocument: document,
,
);
【讨论】:
你知道怎么打吗?我在打字稿中收到此警告:Type ' nodeName: string; ownerDocument:文档; ' 缺少“Node”类型的以下属性:baseURI、childNodes、firstChild、isConnected 和另外 45 个 @jean182 我和你有同样的问题。你找到解决办法了吗? @jean182 您可以使用global as any
来修复类型问题。我更新了答案。
@kylejw2 嗨,我更新了答案以修复类型问题。你可以使用global as any
【参考方案2】:
问题在于 PopperJS 的底层实现在没有可供调用的 DOM API 时调用 document.createRange
函数。解决方案是模拟 PopperJS。
// __mocks__/popper.js.js
import PopperJs from 'popper.js';
export default class Popper
constructor()
this.placements = PopperJs.placements;
return
update: () => ,
destroy: () => ,
scheduleUpdate: () =>
;
jest 将自动选择 /__mocks__
目录中的任何模拟,因此只需添加此文件即可解决您的问题
【讨论】:
这是一件好事,但是使用 Enzyme 而不是 React-Testing-Library,我不必模拟 Popper.js 来超过测试。你知道 Enzyme 与 React-Testing-Library 的区别吗? 我正在使用 jest 和酵素,这对我有用。 ***.com/a/60616862/2060913以上是关于使用 react-testing-library 测试材料 ui popper 时如何修复“TypeError:document.createRange 不是函数”错误?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 React-Testing-Library 测试 mapDispatchToProps?
如何使用 Jest 和 react-testing-library 测试 useRef?
react-testing-library - 屏幕与渲染查询
如何使用 react-testing-library 测试由其他组件组成的组件?