【问题标题】:How to fix `TypeError: document.createRange is not a function` error while testing material ui popper with react-testing-library?使用 react-testing-library 测试材料 ui popper 时如何修复“TypeError:document.createRange 不是函数”错误?
【发布时间】: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();

我该如何进行这项工作?

【问题讨论】:

    标签: reactjs typescript material-ui react-testing-library


    【解决方案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 和酵素,这对我有用。 stackoverflow.com/a/60616862/2060913
    猜你喜欢
    • 2020-03-15
    • 2020-03-10
    • 2020-06-11
    • 2020-12-20
    • 2021-04-26
    • 2019-08-29
    • 2021-11-30
    • 2019-05-21
    • 2019-11-11
    相关资源
    最近更新 更多