Material-ui: Tooltip issue using Jest: TypeError document.createRange is not a function

Created on 16 May 2019  Â·  14Comments  Â·  Source: mui-org/material-ui

Hi, I'm using Jest and Enzyme to test my component.

import React, { Component, Fragment } from "react";
import equal from "fast-deep-equal";
import { TextField, Tooltip, Zoom, withStyles } from "@material-ui/core";
import { checkValue, setValue } from "./PickersFunction";
import { customVariant } from "../../MuiTheme";
import {
  valueVerificationPropType,
  cellValPropType,
  classesPropType,
  typePropType
} from "../../../proptypes";

export class TextFieldWrapper extends Component {
  constructor(props) {
    super(props);
    this.state = {
      tooltipOpen: false,
      message: "",
      error: false
    };
  }

  componentDidMount() {
    const { valueVerification } = this.props;
    if (valueVerification) {
      const newState = checkValue({
        ...this.props,
        mounting: true
      });
      if (!equal(this.state, newState)) {
        this.setState(newState);
      }
    }
  }

  onValueChange = e => {
    const { value } = e.target;
    const newState = setValue({
      ...this.props,
      value
    });
    if (!equal(this.state, newState)) {
      this.setState(newState);
    }
  };

  toggleCloseTooltip = open => {
    const { error } = this.state;
    if (error) {
      this.setState({ tooltipOpen: open });
    }
  };

  render() {
    const { type, cellVal, classes } = this.props;
    const { tooltipOpen, message, error } = this.state;

    return (
      <Tooltip
        open={tooltipOpen}
        classes={{
          tooltip: classes.errorTooltip
        }}
        title={message}
        TransitionComponent={Zoom}
        interactive
      >
        <Fragment>
          <TextField
            value={cellVal}
            error={error}
            onFocus={() => this.toggleCloseTooltip(true)}
            onBlur={() => this.setState({ tooltipOpen: false })}
            onChange={this.onValueChange}
            type={type}
            fullWidth
          />
        </Fragment>
      </Tooltip>
    );
  }
}

TextFieldWrapper.propTypes = {
  cellVal: cellValPropType.isRequired,
  classes: classesPropType.isRequired,
  type: typePropType.isRequired,
  valueVerification: valueVerificationPropType
};

export default withStyles(customVariant)(TextFieldWrapper);

My test :

it("should render a Tootltip and Textfield", () => {
    const wrapper = mount(
      <TextFieldWrapperPureComponent
        cellVal={10}
        type="number"
        columnId="age"
        rowId={rowId}
        valueVerification={valueVerification}
        setRowEdited={setRowEdited}
        classes={{ customVariant }}
      />
    );
    wrapper.instance().onValueChange({ target: { value: 105 } });
  });

Result: ```
TypeError: document.createRange is not a function

  41 |     });
  42 |     if (!equal(this.state, newState)) {
> 43 |       this.setState(newState);
     |            ^
  44 |     }
  45 |   };
  46 |

...
console.error node_modules/react-dom/cjs/react-dom.development.js:17117
The above error occurred in the component:
in Portal (created by Popper)
in Popper (created by Tooltip)
in Tooltip (created by WithStyles(Tooltip))
in WithStyles(Tooltip) (created by TextFieldWrapper)
in TextFieldWrapper (created by WrapperComponent)
in WrapperComponent

```

Any ideas ?

Tooltip question test

Most helpful comment

@MorganDbs See https://github.com/jsdom/jsdom/issues/317. We use the following patch for our tests:

  global.document.createRange = () => ({
    setStart: () => {},
    setEnd: () => {},
    commonAncestorContainer: {
      nodeName: 'BODY',
      ownerDocument: document,
    },
  });

Does it fix your problem?

Update, no longer required since Jest v26.0.0 and https://github.com/facebook/jest/pull/9606.

All 14 comments

Please create a minimal repro. It looks like your test setup is incomplete i.e. no DOM is available.

@MorganDbs See https://github.com/jsdom/jsdom/issues/317. We use the following patch for our tests:

  global.document.createRange = () => ({
    setStart: () => {},
    setEnd: () => {},
    commonAncestorContainer: {
      nodeName: 'BODY',
      ownerDocument: document,
    },
  });

Does it fix your problem?

Update, no longer required since Jest v26.0.0 and https://github.com/facebook/jest/pull/9606.

@oliviertassinari Thanks you it's working now !! If I'm understanding well, my test wasn't attached to the BODY, right ?

@oliviertassinari adding the mock for createRange seem to help, my test is passing, but I get the following console.error:

√ without consent, cannot go further (190ms)

  console.error node_modules/react-dom/cjs/react-dom.development.js:506
    Warning: An update to ForwardRef(Popper) inside a test was not wrapped in act(...).        
    When testing, code that causes React state updates should be wrapped into act(...):
        act(() => {
      /* fire events that update state */
    });
    /* assert on the output */
        This ensures that you're testing the behavior the user would see in the browser. Learn more at https://fb.me/react-wrap-tests-with-act
        in ForwardRef(Popper) (created by Tooltip)
        in Tooltip (created by WithStyles(Tooltip))
      ....

My test (using @testing-library/react):


it('without consent, cannot go further', () => {
  const {getByText} = render(<Registration/>)
  const loginButton = getByText(/Log in/)
  act(() => {
    fireEvent.mouseEnter(loginButton)
  })
  expect(getByText(/Please accept our terms and conditions/)).toBeInTheDocument()
})

@balazsorban44 There are timeouts started when hovering. You should either let all timers run out inside your act() or set the timeouts to 0 via props.

@eps1lon Trying like this:

+ jest.useFakeTimers()
it('without consent, cannot go further', () => {
  const {getByText} = render(<Registration/>)
  const loginButton = getByText(/Log in/)
  act(() => {
    fireEvent.mouseEnter(loginButton)
+   jest.runAllTimers()
  })
  expect(getByText(/Please accept our terms and conditions/)).toBeInTheDocument()
})

Still the same. 🤔 What do I do wrong? I would prefer this solution.
Or if not that, what do you mean by

set timeouts to 0 via props

?

Yeah sorry this was just a quick guess. Could you ask the question on stackoverflow and link it from here? I'll take a look later.

@eps1lon

Yeah sorry this was just a quick guess. Could you ask the question on stackoverflow and link it from here? I'll take a look later.

https://stackoverflow.com/questions/56837949/act-warning-when-testing-component-with-material-ui-tooltip

UPDATE:

The warning goes away in [email protected], don't even need the timers!

Hey! I just found this thread. I am still reproducing this issue on [email protected], so I guess it did not go way?

The issue should have been solved in [email protected] now.

I stumbled on this thread as well. As @oliviertassinari suggested, using jest-environment-jsdom-sixteen solved the issue for me. Upgrading jest to v26 works as well as jest uses jsdom v16 as of v26 https://jestjs.io/blog/2020/05/05/jest-26

@tedwardmah Updating to jest-environment-jsdom-sixteen fixed the issue for me

@MorganDbs See jsdom/jsdom#317. We use the following patch for our tests:

  global.document.createRange = () => ({
    setStart: () => {},
    setEnd: () => {},
    commonAncestorContainer: {
      nodeName: 'BODY',
      ownerDocument: document,
    },
  });

Does it fix your problem?

I tried this and fixed my problem, thanks

Update, since Jest v26.0.0 (https://github.com/facebook/jest/pull/9606), the workaround in https://github.com/mui-org/material-ui/issues/15726#issuecomment-493124813 is no longer required.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  Â·  3Comments

FranBran picture FranBran  Â·  3Comments

mb-copart picture mb-copart  Â·  3Comments

pola88 picture pola88  Â·  3Comments

rbozan picture rbozan  Â·  3Comments