title: Testing React portals with enzyme
body: <p>So I'm having a hard time writing tests for a modal component using React fiber's portal. Because my modal mounts to a domNode on the root of the <code>&lt;body /&gt;</code> but because that domNode doesn't exist, the test fails.</p>
  
  <p>Some code to give, context: </p>
  
  <p><strong>index.html</strong></p>
  
  <pre><code>&lt;!DOCTYPE html&gt;
  &lt;html lang="en"&gt;
    &lt;head&gt;
      &lt;meta charset="utf-8"&gt;
      &lt;meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"&gt;
      &lt;meta name="theme-color" content="#000000"&gt;
      &lt;link rel="manifest" href="%PUBLIC_URL%/manifest.json"&gt;
      &lt;link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico"&gt;
      &lt;title&gt;React App&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
      &lt;noscript&gt;
        You need to enable JavaScript to run this app.
      &lt;/noscript&gt;
      &lt;div id="modal-root"&gt;&lt;/div&gt;
      &lt;div id="root"&gt;&lt;/div&gt;
    &lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
  <p><strong>App.js</strong></p>
  
  <pre><code>import React, { Component } from 'react';
  import logo from './logo.svg';
  import './App.css';
  import { Modal, ModalHeader } from './Modal';
  
  class App extends Component {
    constructor(props) {
      super(props);
      this.state = { show: false };
      this.toggleModal = this.toggleModal.bind(this);
    }
  
    toggleModal(show) {
      this.setState({ show: show !== undefined ? show : !this.state.show });
    }
  
    render() {
      return (
        &lt;div className="App"&gt;
          &lt;header className="App-header"&gt;
            &lt;img src={logo} className="App-logo" alt="logo" /&gt;
            &lt;h1 className="App-title"&gt;Welcome to React&lt;/h1&gt;
          &lt;/header&gt;
          &lt;p className="App-intro"&gt;
            To get started, edit &lt;code&gt;src/App.js&lt;/code&gt; and save to reload.
          &lt;/p&gt;
          &lt;button onClick={() =&gt; this.toggleModal()}&gt;show modal&lt;/button&gt;
          &lt;Modal toggle={this.toggleModal} show={this.state.show}&gt;
            &lt;ModalHeader&gt;
              &lt;span&gt;I'm a header&lt;/span&gt;
              &lt;button onClick={() =&gt; this.toggleModal(false)}&gt;
                &lt;span aria-hidden="true"&gt;&amp;times;&lt;/span&gt;
              &lt;/button&gt;
            &lt;/ModalHeader&gt;
            &lt;p&gt;Modal Body!!!&lt;/p&gt;
          &lt;/Modal&gt;
        &lt;/div&gt;
      );
    }
  }
  
  export default App;
  </code></pre>
  
  <p><strong>Modal.js</strong></p>
  
  <pre><code>import React, { Fragment } from 'react';
  import ReactDOM from 'react-dom';
  import PropTypes from 'prop-types';
  // the next components are styled components, they are just for adding style no logic at all
  import {
    ModalBackdrop,
    ModalContent,
    ModalDialog,
    ModalWrap,
  } from './components';
  
  class Modal extends React.Component {
    constructor(props) {
      super(props);
      this.el = document.createElement('div');
      this.modalRoot = document.getElementById('modal-root');
      this.outerClick = this.outerClick.bind(this);
    }
  
    componentDidMount() {
      this.modalRoot.appendChild(this.el);
      this.modalRoot.parentNode.style.overflow = '';
    }
  
    componentWillUpdate(nextProps) {
      if (this.props.show !== nextProps.show) {
        this.modalRoot.parentNode.style.overflow = nextProps.show ? 'hidden' : '';
      }
    }
  
    componentWillUnmount() {
      this.props.toggle(false);
      this.modalRoot.removeChild(this.el);
    }
  
    outerClick(event) {
      event.preventDefault();
      if (
        event.target === event.currentTarget ||
        event.target.nodeName.toLowerCase() === 'a'
      ) {
        this.props.toggle(false);
      }
    }
  
    render() {
      const ModalMarkup = (
        &lt;Fragment&gt;
          &lt;ModalBackdrop show={this.props.show} /&gt;
          &lt;ModalWrap show={this.props.show} onClick={this.outerClick}&gt;
            &lt;ModalDialog show={this.props.show}&gt;
              &lt;ModalContent&gt;{this.props.children}&lt;/ModalContent&gt;
            &lt;/ModalDialog&gt;
          &lt;/ModalWrap&gt;
        &lt;/Fragment&gt;
      );
      return ReactDOM.createPortal(ModalMarkup, this.el);
    }
  }
  
  Modal.defaultProps = {
    show: false,
    toggle: () =&gt; {},
  };
  
  Modal.propTypes = {
    children: PropTypes.node.isRequired,
    show: PropTypes.bool,
    toggle: PropTypes.func,
  };
  
  export default Modal;
  </code></pre>
  
  <p>And last but not least the test:
  <strong>Modal.test.js</strong></p>
  
  <pre><code>import React from 'react';
  import Modal from './Modal.component';
  import {
    ModalBackdrop,
    ModalContent,
    ModalDialog,
    ModalWrap,
  } from './components';
  
  describe('Modal component', () =&gt; {
    const Child = () =&gt; &lt;div&gt;Yolo&lt;/div&gt;;
  
    it('should render all the styled components and the children', () =&gt; {
      const component = mount(
        &lt;Modal&gt;
          &lt;Child /&gt;
        &lt;/Modal&gt;
      );
      expect(component.find(ModalBackdrop).exists()).toBeTruthy();
      expect(component.find(ModalWrap).exists()).toBeTruthy();
      expect(component.find(ModalWrap).contains(ModalDialog)).toBeTruthy();
      expect(component.find(ModalDialog).contains(ModalContent)).toBeTruthy();
      expect(component.find(ModalContent).contains(Child)).toBeTruthy();
    });
  });
  </code></pre>
  
  <p>A <a href="https://codesandbox.io/s/739ojqpzz6" rel="noreferrer">codesandbox</a> so you can see it in action</p>