Skip to content
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

[test] Modal snapshot test fail "TypeError: parentInstance.children.indexOf is not a function" #9243

Closed
1 task done
RWOverdijk opened this issue Nov 20, 2017 · 22 comments
Closed
1 task done
Labels
component: modal This is the name of the generic UI component, not the React module! support: question Community support but can be turned into an improvement test

Comments

@RWOverdijk
Copy link

  • I have searched the issues of this repository and believe that this is not a duplicate.

Expected Behavior

I expect the tests to run without a problem.

Current Behavior

I get an error:

TypeError: parentInstance.children.indexOf is not a function

I think this is due to portals not working properly in combination with jest snapshots.

Steps to Reproduce (for bugs)

import React from 'react';
import { storiesOf } from '@storybook/react';
import AlertDialog from './AlertDialog';

storiesOf('AlertDialog', module)
  .add('basic', () => <AlertDialog bodyKey="test" onClick={() => {}} titleKey="test" open />);

Where <AlertDialog /> is a boring wrapper around <Dialog />.

I'm sorry for not including a code snippet. I don't know how to set up the tests to illustrate the issue, but I tried my best to provide as much relevant information as possible.

Context

I'm using storybooks with storysnaps (jest snapshots), and our tests are failing. I'm pretty sure it has something to do with enzymejs/enzyme#1150 and potentially reactjs/react-modal#553 could benefit from a solution as well.

Your Environment

Tech Version
Material-UI 1.0.0-beta.21
React 16.0.0
browser N/A

Additional

I have a feeling this is something that could be caught in storyshots but I'm not experienced enough with the material at hand. Any pointers, or better yet solutions would be greatly appreciated.

I'm not against fixing it and contributing either (if I get the right pointers to help me out).

@oliviertassinari oliviertassinari added the status: waiting for author Issue with insufficient information label Nov 20, 2017
@oliviertassinari

This comment has been minimized.

@oliviertassinari
Copy link
Member

oliviertassinari commented Nov 20, 2017

I'm sorry for not including a code snippet. I don't know how to set up the tests to illustrate the issue, but I tried my best to provide as much relevant information as possible.

The information provided aren't actionable on our end. We need a reproduction repository. Also, what is this AlertDialog component?

@RWOverdijk
Copy link
Author

@oliviertassinari

An error occured when fetching the sandbox:

Could not find package.json

@RWOverdijk
Copy link
Author

While I do understand you want to keep things tidy in here, I'm not spending too much time on this. I'll just comment out my dialog stories for now. If anyone else comes across this issue, maybe they're willing to make the example.

@oliviertassinari
Copy link
Member

oliviertassinari commented Nov 20, 2017

@RWOverdijk Thanks, I will have a look at what's going on with codesandbox!

Yes, same on our side. We have limited ressources, trying reproducing issues has a poor value/cost ratio in comparaison to other tasks. It's why we use a strict policy.

@rpk-red
Copy link

rpk-red commented Nov 22, 2017

Came across the same issue...
Environment

  • react 16.1.0
  • material-ui 1.0.0-beta.20
  • jest 21.2.1

Component

import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from 'material-ui/styles';
import Button from 'material-ui/Button';
import Dialog, {
  DialogActions,
  DialogContent,
  DialogContentText,
  DialogTitle,
} from 'material-ui/Dialog';


const styles = () => ({
  div: {
    display: 'block',
    textAlign: 'center'
  }
});
class Error extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      open: true

    };
  }

  handleRequestClose = () => {
    this.setState({ open: false });
  }

  render() {
    const { classes, errorMsg } = this.props;
    return (
      <div className={classes.div}>
        <Dialog ignoreBackdropClick ignoreEscapeKeyUp open={this.state.open} onRequestClose={this.handleRequestClose}>
          <DialogTitle>{"You got an error!"}</DialogTitle>
          <DialogContent>
            <DialogContentText>
              Error: {errorMsg}
            </DialogContentText>
          </DialogContent>
          <DialogActions>
            <Button onClick={this.handleRequestClose} color="accent" autoFocus>
              Dismiss
            </Button>
          </DialogActions>
        </Dialog>
      </div>
    );
  }
}

Error.propTypes = {
  classes: PropTypes.object.isRequired,
  errorMsg: PropTypes.string
};

export default withStyles(styles)(Error);

Test

import React from 'react';
import Error from '../shared/Error';
import renderer from 'react-test-renderer';

describe('Error component', () => {
  it('should render', () => {
    const tree = renderer.create(
      <Error />
    ).toJSON();
    expect(tree).toMatchSnapshot();
  });
});

Error message
● Error component › should render

    TypeError: parentInstance.children.indexOf is not a function

      at appendChild (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7146:39)
      at commitPlacement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4893:13)
      at commitAllHostEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5651:13)
      at HTMLUnknownElement.callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1584:14)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:219:27)
      at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:126:9)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:87:17)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:36:27)
      at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:61:35)
      at Object.invokeGuardedCallbackDev (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1623:16)
      at invokeGuardedCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1480:29)
      at commitRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5771:9)
      at performWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6730:42)
      at performWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6680:7)
      at requestWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6596:7)
      at scheduleWorkImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6479:11)
      at scheduleWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6441:12)
      at scheduleTopLevelUpdate (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6904:5)
      at Object.updateContainer (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6942:7)
      at Object.create (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7562:18)
      at Object.<anonymous> (src/components/pages/shared/Error.spec.js:7:44)

@Skaronator
Copy link
Contributor

Just run into the same issue. It has something to do with the Portals. I made a simple reproduce project here: https://github.com/Skaronator/material-ui-issue-9243
Just run:

yarn
yarn test

And you'll get the following error:

$ react-scripts test --env=jsdom
 FAIL  src\App.test.js
  ● should match Snapshot

    TypeError: parentInstance.children.indexOf is not a function

      at appendChild (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7218:39)
      at commitPlacement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:4904:13)
      at commitAllHostEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5678:13)
      at HTMLUnknownElement.callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1610:14)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:219:27)
      at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:126:9)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:87:17)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:36:27)
      at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:61:35)
      at Object.invokeGuardedCallbackDev (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1649:16)
      at invokeGuardedCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:1506:29)
      at commitRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5798:9)
      at performWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6800:42)
      at performWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6750:7)
      at requestWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6661:7)
      at scheduleWorkImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6515:11)
      at scheduleWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6472:12)
      at scheduleTopLevelUpdate (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6976:5)
      at Object.updateContainer (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7014:7)
      at Object.create (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7637:18)
      at Object.<anonymous>.it (src/App.test.js:8:46)
          at new Promise (<anonymous>)
      at Promise.resolve.then.el (node_modules/p-map/index.js:46:16)
          at <anonymous>
      at process._tickCallback (internal/process/next_tick.js:188:7)

  × should match Snapshot (61ms)

@oliviertassinari
Copy link
Member

@Skaronator Enzyme do not support the portal API of React: enzymejs/enzyme#252. We use a mockPortal.js for our tests internally.

@oliviertassinari oliviertassinari added test support: question Community support but can be turned into an improvement and removed status: waiting for author Issue with insufficient information labels Jan 11, 2018
@zxydaisy
Copy link

i have the same problem

@mattspeller
Copy link

I can confirm with @oliviertassinari that mockPortal.js solves this issue. (not all issues trying to snapshot a material-ui dialog, but this particular one)

@reyronald

This comment has been minimized.

@oliviertassinari
Copy link
Member

@Skaronator Enzyme do not support the portal API of React: enzymejs/enzyme#252. We use a mockPortal.js for our tests internally.

Enzyme now supports the portal API of React. We have removed our mockPortal.js module.

@oliviertassinari

This comment has been minimized.

@Skaronator
Copy link
Contributor

Enzyme now supports the portal API of React.

Oh cool - didn't know that. They didn't even close the issue on thier repo.

@ifier
Copy link

ifier commented Apr 20, 2018

package.json

"material-ui": "1.0.0-beta.42",
"enzyme": "3.3.0",
"enzyme-adapter-react-16": "1.1.1",
"react-test-renderer": "16.3.2",

Test

import React from 'react';
import renderer from 'react-test-renderer';
import { BtnIcon } from 'Components/buttons/btn-icon';

const buttonIcon = <i className="fa fa-close" />;

describe('BtnIcon should match Snapshot', () => {
  it('without hint', () => {
    const renderedValue = renderer.create(
      <BtnIcon>{buttonIcon}</BtnIcon>
    ).toJSON();
    expect(renderedValue).toMatchSnapshot();
  });
});

btn-icon.js

import React from 'react';
import PropTypes from 'prop-types';
import IconButton from 'material-ui/IconButton';
import Hint from 'Components/hint';

const BtnIcon = ({
  children, className, onClick, disabled, hint, hintPlacement, wrapperClassName
}) => (
  <Hint
    text={hint}
    placement={hintPlacement}
    className="tooltip-popper--btn-icon"
    wrapperClassName={wrapperClassName}
  >
    <div> {/* Little hack for disabled button inside */}
      <IconButton
        className={`btn btn-icon ${className} ${disabled ? 'disabled' : ''}`}
        onClick={onClick}
        disabled={disabled}
        tabIndex={-1}
      >
        {children}
      </IconButton>
    </div>
  </Hint>
);

BtnIcon.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object
  ]).isRequired,
  className: PropTypes.string,
  onClick: PropTypes.func,
  disabled: PropTypes.bool,
  hint: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object
  ]),
  hintPlacement: PropTypes.string,
  wrapperClassName: PropTypes.string
};

BtnIcon.defaultProps = {
  className: '',
  onClick: null,
  disabled: false,
  hint: '',
  wrapperClassName: '',
  hintPlacement: undefined
};

export { BtnIcon };

hint.js - tooltip

import React from 'react';
import PropTypes from 'prop-types';
import Tooltip from 'material-ui/Tooltip';
import './hint.scss';

const Hint = props => (
  <Tooltip
    title={props.text}
    placement={props.placement}
    classes={{
      root: `hint-root ${props.wrapperClassName}`,
      popper: `hint-popper ${props.className}`,
      tooltip: 'hint__text',
      open: 'opened',
      tooltipPlacementLeft: 'left',
      tooltipPlacementRight: 'right',
      tooltipPlacementTop: 'top',
      tooltipPlacementBottom: 'bottom'
    }}
  >
    {props.children}
  </Tooltip>
);

Hint.propTypes = {
  children: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object
  ]).isRequired,
  text: PropTypes.oneOfType([
    PropTypes.string,
    PropTypes.object
  ]),
  placement: PropTypes.string,
  className: PropTypes.string,
  wrapperClassName: PropTypes.string
};

Hint.defaultProps = {
  wrapperClassName: '',
  className: '',
  text: '',
  placement: 'top'
};

export default Hint;

And I'm getting the same error:

FAIL  __tests__/components/buttons/btn-icon/btn-icon.test.js
  BtnIcon should match Snapshot
    ✕ without hint (96ms)

  ● BtnIcon should match Snapshot › without hint

    TypeError: parentInstance.children.indexOf is not a function

at appendChild (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8565:39)
      at commitPlacement (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5779:13)
      at commitAllHostEffects (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:6946:13)
      at HTMLUnknownElement.callCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:906:14)
      at invokeEventListeners (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:193:27)
      at HTMLUnknownElementImpl._dispatch (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:119:9)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/events/EventTarget-impl.js:82:17)
      at HTMLUnknownElementImpl.dispatchEvent (node_modules/jsdom/lib/jsdom/living/nodes/HTMLElement-impl.js:30:27)
      at HTMLUnknownElement.dispatchEvent (node_modules/jsdom/lib/jsdom/living/generated/EventTarget.js:157:21)
      at Object.invokeGuardedCallbackDev (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:944:16)
      at invokeGuardedCallback (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:993:29)
      at commitRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7122:9)
      at completeRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8065:36)
      at performWorkOnRoot (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8015:11)
      at performWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7933:9)
      at performSyncWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7910:5)
      at requestWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7810:7)
      at scheduleWorkImpl (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7685:13)
      at scheduleWork (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:7645:12)
      at scheduleRootUpdate (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8273:5)
      at updateContainerAtExpirationTime (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8301:12)
      at Object.updateContainer (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:8328:14)
      at Object.create (node_modules/react-test-renderer/cjs/react-test-renderer.development.js:9009:18)
      at Object.<anonymous> (__tests__/components/buttons/btn-icon/btn-icon.test.js:27:53)

console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5206
    The above error occurred in the <Popper> component:
        in Popper (created by Tooltip)
        in Portal (created by Tooltip)
        in Manager (created by Tooltip)
        in Tooltip (created by WithStyles(Tooltip))
        in WithStyles(Tooltip) (created by Hint)
        in Hint (created by BtnIcon)
        in BtnIcon
    
    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

  console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5206
    The above error occurred in the <div> component:
        in div (created by Popper)
        in Popper (created by Tooltip)
        in Portal (created by Tooltip)
        in Manager (created by Tooltip)
        in Tooltip (created by WithStyles(Tooltip))
        in WithStyles(Tooltip) (created by Hint)
        in Hint (created by BtnIcon)
        in BtnIcon
    
    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

  console.error node_modules/react-test-renderer/cjs/react-test-renderer.development.js:5206
    The above error occurred in the <BtnIcon> component:
        in BtnIcon
    
    Consider adding an error boundary to your tree to customize error handling behavior.
    Visit https://fb.me/react-error-boundaries to learn more about error boundaries.

How can I solve this issue while we are waiting for a new enzyme (enzyme that will support portals)?

@ifier
Copy link

ifier commented Apr 20, 2018

Solved it with - import { createMount } from 'material-ui/test-utils'; I hope it will be helpful for someone.
Also you can use simple { mount } - import { mount } from 'enzyme';
But it will not render Tooltip at snapshot. Enzyme release new version with supporting Portals soon, I hope.

Example:

import React from 'react';
import { createMount } from 'material-ui/test-utils';
import { BtnIcon } from 'Components/buttons/btn-icon';

describe('BtnIcon should match Snapshot', () => {
  it('without hint', () => {
    const renderedValue = createMount()(
      // Code of this component - <BtnIcon /> you can find above
      <BtnIcon>{buttonIcon}</BtnIcon>
    );
    expect(renderedValue.html()).toMatchSnapshot();
  });
});

@ahummel25
Copy link

ahummel25 commented Mar 16, 2020

I still have this issue using material-ui and react-test-renderer 16.13.0.

Is the solution here to use createMount from material-ui/test-utils?

I would like to continue to use react-test-renderer.

const tree = renderer.create(<MyComp />);

@TomaszWaszczyk
Copy link

@exaucae
Copy link

exaucae commented May 3, 2021

I experience same issue when using mui Dialog and react-test-renderer. Here's the related sandbox.

@yinzixie

This comment has been minimized.

@oliviertassinari oliviertassinari changed the title TypeError when rendering for jest snapshots [test] Modal snapshot test fail "TypeError: parentInstance.children.indexOf is not a function" Sep 26, 2021
@oliviertassinari oliviertassinari added the component: modal This is the name of the generic UI component, not the React module! label Sep 26, 2021
@pingfengafei
Copy link

The root cause is ReactDOM.createPortal, which many Modal components relay on.

import ReactDOM from 'react-dom';

  beforeAll(() => {
    ReactDOM.createPortal = jest.fn((element, _node) => {
      return element;
    });
  });

  afterEach(() => {
    ReactDOM.createPortal.mockClear();
  });

@pihish
Copy link

pihish commented Jan 5, 2023

You need to mock out react-dom and the createPortal() method it returns with Jest

jest.mock('react-dom', () => {
  const original = jest.requireActual('react-dom');

  return {
    ...original,
    createPortal: (node) => node,
  };
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: modal This is the name of the generic UI component, not the React module! support: question Community support but can be turned into an improvement test
Projects
None yet
Development

No branches or pull requests