Repository: mui/material-ui (TypeScript)
Date: 2018-04-20 23:26:09+00:00
Task type: Testing

Issue:
Select component is not rendering children when using enzyme
<!--- Provide a general summary of the issue in the Title above -->

The Select component is not rendering it's children when using enzyme's mount function.

<!--
    Thank you very much for contributing to Material-UI by creating an issue! ❤️
    To avoid duplicate issues we ask you to check off the following list.
-->

<!-- Checked checkbox should look like this: [x] -->
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

The following test should pass:

```js
    test('it renders all datasets', () => {
        let datasets = [
            new DataSet({id: 'ds-1', name: 'ds 1', sequences: []}),
            new DataSet({id: 'ds-2', name: 'ds 2', sequences: []}),
            new DataSet({id: 'ds-3', name: 'ds 3', sequences: []}),
        ];

        let newProps = {
            ...props,
            datasets: datasets,
            currentDataSet: datasets[0],
            filters: {selectedDataSet: datasets[0].getId()}
        };

        let wrappedComponent = mount(<DataSetSelect {...newProps}/>);

        expect(wrappedComponent.find('MenuItem').length).toBe(3);
    });
```

## Current Behavior

The test fails with:

```bash
    expect(received).toBe(expected)

    Expected value to be (using ===):
      3
    Received:
      0
```


## Context

The component itself renders like this:
```js
render() {
        let {classes} = this.props;

        return (
            <div className={classes.root}>
                <Grid container className={classes.root}>
                    <Grid item xs={1}>
                        <p>Select a dataset</p>
                    </Grid>
                    <Grid item xs={3}>
                        <form>
                            <FormControl className={classes.formControl}>
                                <Select onChange={(event) => this.handleChange(event)}
                                        value={this.props.filters.selectedDataSet}
                                        inputProps={{
                                            name: 'selectedDataSet',
                                            id: 'selected-dataSet',
                                        }}>
                                    {this.props.datasets.map((dataSet) => <MenuItem key={dataSet.getId()}
                                                                                    value={dataSet.getId()}>{dataSet.getName()}</MenuItem>)}
                                </Select>
                            </FormControl>

                        </form>

                    </Grid>
                    <Grid item xs={2}>
                        <p>
                            Dataset name: {this.props.currentDataSet.getName()}
                        </p>
                    </Grid>
                </Grid>
            </div>
        );
    }
```


## Your Environment
<!--- Include as many relevant details about the environment with which you experienced the bug. -->

| Tech         | Version |
|--------------|---------|
| Material-UI  |    1.0.0-beta.35     |
| React        |     16.2.0    |
| Jest        |     20.0.4    |




Issue discussion:
Same here. Im trying to read `MenuItem` which is inside `SelectField`
```
<SomeComponent /> // which has Table, TableRowColumn, SelectField

const mountWithContext = (node) => mount(node, {context: {muiTheme}, childContextTypes: {muiTheme: PropTypes.object}});

const wrapper = mountWithContext(<Goals goal={goal} onChange={() => 'test'} />);

const unitOptions = wrapper.find('SelectField#unit MenuItem'); //which has 2 items

console.log("unitOptions: ", unitOptions); // prints ReactWrapper { length: 0 }

```
@yoanisgil I fear it's a limitation of enzyme. The Select component portals it's children to the body. I would encourage you to have a look at our tests:
https://github.com/mui-org/material-ui/blob/6e8ffbfe6d0317763b57022f745cb89895fed1e6/packages/material-ui/src/Select/SelectInput.test.js#L10
@oliviertassinari  Im using `SelectField` inside a `component`. So I face issue while testing that component since Im not able to find `MenuItem` inside SelectField.

Can you suggest me any workaround.
@steelx `SelectField ` is a v0.x component, my comment only targets v1+.
@oliviertassinari I was looking at those tests but they are for SelectInput and I'm using Select. I was also looking at this:

https://github.com/mui-org/material-ui/blob/6e8ffbfe6d0317763b57022f745cb89895fed1e6/packages/material-ui/src/Select/Select.test.js#L37

and initially I would have expected to see a test around making sure that all children are rendered. But maybe is because the issue that we are discussing here? 
@yoanisgil We have another test:
https://github.com/mui-org/material-ui/blob/6e8ffbfe6d0317763b57022f745cb89895fed1e6/packages/material-ui/test/integration/Select.test.js#L8
@oliviertassinari I think none of the children component gets rendered if we use enzyme. I have observed similar behavior with Dialog.
 [https://github.com/mui-org/material-ui/issues/9200](url)
I think the issue is that they get rendered but not as children of the Select component so that's why they can't be found. At least that's what I see when I inspect the DOM while my application, that contains a Select, is running.
@talmukund @yoanisgil What version of enzyme are you using? The portal part was an important pain point when I upgraded from React 15.x to React 16.x. We have removed our "hack" recently as enzyme better support the Portal API. Maybe, it's still in need for you guys: #10208
If that works, we can consider adding an option to the portal to disable the behavior for testing purposes.
Using:

    "enzyme": "^3.3.0",
    "enzyme-adapter-react-16": "^1.1.1"

I'm not sure I understand the previous comment :(.
@oliviertassinari I have also observed issue comes only when component is part of portal. I will check same issue with react 15.x.
Im not using Portal though Im using React 16.2.0

Solution patch:
diff --git a/.size-limit b/.size-limit
--- a/.size-limit
+++ b/.size-limit
@@ -9,7 +9,7 @@
     "name": "The size of all the modules of material-ui.",
     "webpack": true,
     "path": "packages/material-ui/build/index.js",
-    "limit": "101.0 KB"
+    "limit": "101.1 KB"
   },
   {
     "name": "The main bundle of the docs",
diff --git a/docs/src/pages/guides/composition/composition.md b/docs/src/pages/guides/composition/composition.md
--- a/docs/src/pages/guides/composition/composition.md
+++ b/docs/src/pages/guides/composition/composition.md
@@ -89,6 +89,6 @@ class ListItemLink extends React.Component {
 }
 ```
 
-`renderLink` will now always reference the same component. Here is a demo:
+`renderLink` will now always reference the same component. Here is a demo with react-router:
 
 {{"demo": "pages/guides/composition/ComponentProperty.js"}}
diff --git a/docs/src/pages/utils/portal/portal.md b/docs/src/pages/utils/portal/portal.md
--- a/docs/src/pages/utils/portal/portal.md
+++ b/docs/src/pages/utils/portal/portal.md
@@ -15,3 +15,9 @@ You have to wait for the client side reconciliation to see the children.
 ## Simple Portal
 
 {{"demo": "pages/utils/portal/SimplePortal.js"}}
+
+## Portal & tests
+
+The portal behavior can be challenging for testing libraries, like [enzyme](https://github.com/airbnb/enzyme/issues/252), to handle.
+We provide a global option to disable the behavior: `global.__MUI_PORTAL_DISABLE__`.
+When set to `true`, the portal will behave as a pass-through component.
diff --git a/packages/material-ui/src/Portal/Portal.js b/packages/material-ui/src/Portal/Portal.js
--- a/packages/material-ui/src/Portal/Portal.js
+++ b/packages/material-ui/src/Portal/Portal.js
@@ -1,3 +1,5 @@
+/* eslint-disable no-underscore-dangle */
+
 import React from 'react';
 import ReactDOM from 'react-dom';
 import PropTypes from 'prop-types';
@@ -44,13 +46,30 @@ class Portal extends React.Component {
    * @public
    */
   getMountNode = () => {
+    if (this.disable) {
+      return ReactDOM.findDOMNode(this);
+    }
+
     return this.mountNode;
   };
 
+  // Hack waiting for https://github.com/airbnb/enzyme/issues/252 to be solved.
+  // When `global.__MUI_PORTAL_DISABLE__` is set to `true`,
+  // the portal will behave as a pass-through component.
+  disable = typeof global !== 'undefined' && global.__MUI_PORTAL_DISABLE__;
+
   render() {
     const { children } = this.props;
 
-    return this.mountNode ? ReactDOM.createPortal(children, this.mountNode) : null;
+    if (this.mountNode) {
+      if (this.disable) {
+        return children;
+      }
+
+      return ReactDOM.createPortal(children, this.mountNode);
+    }
+
+    return null;
   }
 }
 


Test patch:
diff --git a/packages/material-ui/src/Portal/Portal.test.js b/packages/material-ui/src/Portal/Portal.test.js
--- a/packages/material-ui/src/Portal/Portal.test.js
+++ b/packages/material-ui/src/Portal/Portal.test.js
@@ -1,3 +1,4 @@
+/* eslint-disable no-underscore-dangle */
 /* eslint-disable react/no-multi-comp */
 
 import React from 'react';
@@ -24,6 +25,29 @@ describe('<Portal />', () => {
     mount.cleanUp();
   });
 
+  describe('disable portal for tests', () => {
+    const Portal = NewPortal;
+
+    before(() => {
+      global.__MUI_PORTAL_DISABLE__ = true;
+    });
+
+    after(() => {
+      global.__MUI_PORTAL_DISABLE__ = false;
+    });
+
+    it('should disable the portal feature', () => {
+      const wrapper = mount(
+        <Portal>
+          <h1 className="woofPortal">Foo</h1>
+        </Portal>,
+      );
+      assert.strictEqual(wrapper.children().length, 1, 'should have one children');
+      const instance = wrapper.instance();
+      assert.strictEqual(instance.getMountNode().nodeName.toLowerCase(), 'h1');
+    });
+  });
+
   versions.map(verion => {
     describe(verion, () => {
       let Portal;