From 790e65afe9de3ccb7c43229ed5ee6ec8a12cfa29 Mon Sep 17 00:00:00 2001
From: gzhappysky <451417726@qq.com>
Date: Thu, 20 Jul 2017 01:51:44 +0800
Subject: [PATCH 01/19] fix(*): fix renderToString fails with array type
 children when react-dom/server render

---
 src/renderers/shared/server/ReactPartialRenderer.js | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/src/renderers/shared/server/ReactPartialRenderer.js b/src/renderers/shared/server/ReactPartialRenderer.js
index ffd1e408239d..56bd3328cbc2 100644
--- a/src/renderers/shared/server/ReactPartialRenderer.js
+++ b/src/renderers/shared/server/ReactPartialRenderer.js
@@ -473,6 +473,15 @@ class ReactDOMServerRenderer {
       if (child === null || child === false) {
         return '';
       } else {
+        if (Array.isArray(child)) {
+          this.stack.push({
+            children: child,
+            childIndex: 0,
+            context: context,
+            footer: '',
+          });
+          return '';
+        }
         return this.renderDOM(child, context);
       }
     }

From 6fd78e5e49ea6a7d14969f711b0e280bf3438ac9 Mon Sep 17 00:00:00 2001
From: gzhappysky <451417726@qq.com>
Date: Thu, 20 Jul 2017 15:35:12 +0800
Subject: [PATCH 02/19] Add an integration test that verify renderToString with
 array type children

---
 .../dom/shared/__tests__/ReactDOMServerIntegration-test.js       | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 220c71f9aba7..ce6037a88701 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -328,6 +328,7 @@ describe('ReactDOMServerIntegration', () => {
       expect(e.childNodes.length).toBe(1);
       expect(e.firstChild.tagName).toBe('BR');
     });
+    
   });
 
   describe('property to attribute mapping', function() {

From 4b28bc1918d5f2e8d14356b38084c6cb6a2cd67c Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Fri, 21 Jul 2017 00:36:27 +0800
Subject: [PATCH 03/19] Add integration test to renderToString with array type
 child

---
 .../__tests__/ReactDOMServerIntegration-test.js | 17 +++++++++++++++++
 .../shared/server/ReactPartialRenderer.js       | 13 +++++++++++++
 2 files changed, 30 insertions(+)

diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 220c71f9aba7..9425ccea4927 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -20,6 +20,7 @@ let ReactDOM;
 let ReactDOMServer;
 let ReactDOMNodeStream;
 let ReactTestUtils;
+let ReactFeatureFlags;
 
 const stream = require('stream');
 
@@ -288,6 +289,8 @@ function expectMarkupMismatch(serverElement, clientElement) {
 let onAfterResetModules = null;
 function resetModules() {
   jest.resetModuleRegistry();
+  ReactFeatureFlags = require('ReactFeatureFlags');
+  ReactFeatureFlags.disableNewFiberFeatures = false;
   PropTypes = require('prop-types');
   React = require('react');
   ReactDOM = require('react-dom');
@@ -328,6 +331,20 @@ describe('ReactDOMServerIntegration', () => {
       expect(e.childNodes.length).toBe(1);
       expect(e.firstChild.tagName).toBe('BR');
     });
+
+    if (ReactDOMFeatureFlags.useFiber) {
+      itRenders('a array type children as a child', async render => {
+        class AppComponent extends React.Component {
+          render() {
+            return [<div key={1}>text1</div>, <div key={2}>text2</div>];
+          }
+        }
+        const e = await render(<AppComponent />);
+        const parentNode = e.parentNode;
+        expect(parentNode.childNodes[0].tagName).toBe('DIV');
+        expect(parentNode.childNodes[1].tagName).toBe('DIV');
+      });
+    }
   });
 
   describe('property to attribute mapping', function() {
diff --git a/src/renderers/shared/server/ReactPartialRenderer.js b/src/renderers/shared/server/ReactPartialRenderer.js
index ecb3b0600d04..951cced31661 100644
--- a/src/renderers/shared/server/ReactPartialRenderer.js
+++ b/src/renderers/shared/server/ReactPartialRenderer.js
@@ -484,6 +484,19 @@ class ReactDOMServerRenderer {
       if (child === null || child === false) {
         return '';
       } else {
+        if (Array.isArray(child)) {
+          var frame = {
+            children: child,
+            childIndex: 0,
+            context: context,
+            footer: '',
+          };
+          if (__DEV__) {
+            frame.debugElementStack = [];
+          }
+          this.stack.push(frame);
+          return '';
+        }
         return this.renderDOM(child, context);
       }
     }

From deb11af24fab7b8e0172636ab913987ece67a108 Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Fri, 21 Jul 2017 01:21:39 +0800
Subject: [PATCH 04/19] Update integration test to renderToString with array
 type child

---
 .../dom/shared/__tests__/ReactDOMServerIntegration-test.js    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 9425ccea4927..cd27085c0295 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -336,13 +336,13 @@ describe('ReactDOMServerIntegration', () => {
       itRenders('a array type children as a child', async render => {
         class AppComponent extends React.Component {
           render() {
-            return [<div key={1}>text1</div>, <div key={2}>text2</div>];
+            return [<div key={1}>text1</div>, <p key={2}>text2</p>];
           }
         }
         const e = await render(<AppComponent />);
         const parentNode = e.parentNode;
         expect(parentNode.childNodes[0].tagName).toBe('DIV');
-        expect(parentNode.childNodes[1].tagName).toBe('DIV');
+        expect(parentNode.childNodes[1].tagName).toBe('P');
       });
     }
   });

From 62c042e707402646943fe56524499cc03a419c73 Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Fri, 21 Jul 2017 03:05:37 +0800
Subject: [PATCH 05/19] fix renderToString fails with array type children when
 react-dom/server render

---
 src/renderers/dom/ReactDOMStringRenderer.js   |  4 +--
 .../ReactDOMServerIntegration-test.js         | 25 +++++++++----------
 .../shared/server/ReactPartialRenderer.js     |  4 +--
 3 files changed, 15 insertions(+), 18 deletions(-)

diff --git a/src/renderers/dom/ReactDOMStringRenderer.js b/src/renderers/dom/ReactDOMStringRenderer.js
index 7995ac3581bc..6f0363c993d6 100644
--- a/src/renderers/dom/ReactDOMStringRenderer.js
+++ b/src/renderers/dom/ReactDOMStringRenderer.js
@@ -22,7 +22,7 @@ var ReactPartialRenderer = require('ReactPartialRenderer');
  */
 function renderToString(element) {
   invariant(
-    React.isValidElement(element),
+    Array.isArray(element) || React.isValidElement(element),
     'renderToString(): You must pass a valid ReactElement.',
   );
   var renderer = new ReactPartialRenderer(element, false);
@@ -37,7 +37,7 @@ function renderToString(element) {
  */
 function renderToStaticMarkup(element) {
   invariant(
-    React.isValidElement(element),
+    Array.isArray(element) || React.isValidElement(element),
     'renderToStaticMarkup(): You must pass a valid ReactElement.',
   );
   var renderer = new ReactPartialRenderer(element, true);
diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index cd27085c0295..4ec037570485 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -20,7 +20,6 @@ let ReactDOM;
 let ReactDOMServer;
 let ReactDOMNodeStream;
 let ReactTestUtils;
-let ReactFeatureFlags;
 
 const stream = require('stream');
 
@@ -289,8 +288,6 @@ function expectMarkupMismatch(serverElement, clientElement) {
 let onAfterResetModules = null;
 function resetModules() {
   jest.resetModuleRegistry();
-  ReactFeatureFlags = require('ReactFeatureFlags');
-  ReactFeatureFlags.disableNewFiberFeatures = false;
   PropTypes = require('prop-types');
   React = require('react');
   ReactDOM = require('react-dom');
@@ -333,16 +330,18 @@ describe('ReactDOMServerIntegration', () => {
     });
 
     if (ReactDOMFeatureFlags.useFiber) {
-      itRenders('a array type children as a child', async render => {
-        class AppComponent extends React.Component {
-          render() {
-            return [<div key={1}>text1</div>, <p key={2}>text2</p>];
-          }
-        }
-        const e = await render(<AppComponent />);
-        const parentNode = e.parentNode;
-        expect(parentNode.childNodes[0].tagName).toBe('DIV');
-        expect(parentNode.childNodes[1].tagName).toBe('P');
+      it('a array type children as a child', () => {
+        spyOn(console, 'error');
+        let markup = ReactDOMServer.renderToString([
+          <div key={1}>text1</div>,
+          <p key={2}>text2</p>,
+        ]);
+
+        const root = document.createElement('div');
+        root.innerHTML = markup;
+        expect(root.childNodes[0].tagName).toBe('DIV');
+        expect(root.childNodes[1].tagName).toBe('P');
+        expectDev(console.error.calls.count()).toBe(0);
       });
     }
   });
diff --git a/src/renderers/shared/server/ReactPartialRenderer.js b/src/renderers/shared/server/ReactPartialRenderer.js
index 951cced31661..25773a652343 100644
--- a/src/renderers/shared/server/ReactPartialRenderer.js
+++ b/src/renderers/shared/server/ReactPartialRenderer.js
@@ -307,8 +307,6 @@ function createOpenTagMarkup(
 }
 
 function resolve(child, context) {
-  // TODO: We'll need to support Arrays (and strings) after Fiber is rolled out
-  invariant(!Array.isArray(child), 'Did not expect to receive an Array child');
   while (React.isValidElement(child)) {
     if (__DEV__) {
       pushElementToDebugStack(child);
@@ -416,7 +414,7 @@ function resolve(child, context) {
 class ReactDOMServerRenderer {
   constructor(element, makeStaticMarkup) {
     var topFrame = {
-      children: [element],
+      children: Array.isArray() ? element : [element],
       childIndex: 0,
       context: emptyObject,
       footer: '',

From 6b4eb0e32a5e6692a14b2748f981bb900638f7be Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Fri, 21 Jul 2017 10:17:05 +0800
Subject: [PATCH 06/19] Update integration test to renderToString with array
 type child

---
 .../dom/ReactDOMNodeStreamRenderer.js         |  4 ---
 .../ReactDOMServerIntegration-test.js         | 30 +++++++++++--------
 2 files changed, 18 insertions(+), 16 deletions(-)

diff --git a/src/renderers/dom/ReactDOMNodeStreamRenderer.js b/src/renderers/dom/ReactDOMNodeStreamRenderer.js
index 890538bb6fbd..689d339d81ef 100644
--- a/src/renderers/dom/ReactDOMNodeStreamRenderer.js
+++ b/src/renderers/dom/ReactDOMNodeStreamRenderer.js
@@ -40,10 +40,6 @@ class ReactMarkupReadableStream extends Readable {
  * See https://facebook.github.io/react/docs/react-dom-stream.html#rendertostream
  */
 function renderToStream(element) {
-  invariant(
-    React.isValidElement(element),
-    'renderToStream(): You must pass a valid ReactElement.',
-  );
   return new ReactMarkupReadableStream(element, false);
 }
 
diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 4ec037570485..3b47d070bd09 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -313,6 +313,19 @@ describe('ReactDOMServerIntegration', () => {
   });
 
   describe('basic rendering', function() {
+    beforeEach(() => {
+      onAfterResetModules = () => {
+        const ReactFeatureFlags = require('ReactFeatureFlags');
+        ReactFeatureFlags.disableNewFiberFeatures = false;
+      };
+
+      resetModules();
+    });
+
+    afterEach(() => {
+      onAfterResetModules = null;
+    });
+
     itRenders('a blank div', async render => {
       const e = await render(<div />);
       expect(e.tagName).toBe('DIV');
@@ -330,18 +343,11 @@ describe('ReactDOMServerIntegration', () => {
     });
 
     if (ReactDOMFeatureFlags.useFiber) {
-      it('a array type children as a child', () => {
-        spyOn(console, 'error');
-        let markup = ReactDOMServer.renderToString([
-          <div key={1}>text1</div>,
-          <p key={2}>text2</p>,
-        ]);
-
-        const root = document.createElement('div');
-        root.innerHTML = markup;
-        expect(root.childNodes[0].tagName).toBe('DIV');
-        expect(root.childNodes[1].tagName).toBe('P');
-        expectDev(console.error.calls.count()).toBe(0);
+      itRenders('a array type children as a child', async render => {
+        let e = await render([<div key={1}>text1</div>, <p key={2}>text2</p>]);
+        let parent = e.parentNode;
+        expect(parent.childNodes[0].tagName).toBe('DIV');
+        expect(parent.childNodes[1].tagName).toBe('P');
       });
     }
   });

From e53e5526244d71274a1b04eb42652308d1621b66 Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Fri, 21 Jul 2017 11:54:18 +0800
Subject: [PATCH 07/19] Add to iterate that are not arrays

---
 src/renderers/shared/server/ReactPartialRenderer.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/renderers/shared/server/ReactPartialRenderer.js b/src/renderers/shared/server/ReactPartialRenderer.js
index 25773a652343..dda2462ab84a 100644
--- a/src/renderers/shared/server/ReactPartialRenderer.js
+++ b/src/renderers/shared/server/ReactPartialRenderer.js
@@ -414,7 +414,7 @@ function resolve(child, context) {
 class ReactDOMServerRenderer {
   constructor(element, makeStaticMarkup) {
     var topFrame = {
-      children: Array.isArray() ? element : [element],
+      children: toArray(element),
       childIndex: 0,
       context: emptyObject,
       footer: '',

From eafb4bdf4c5723230d12eb3061054ffa3230f998 Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Sat, 22 Jul 2017 03:51:46 +0800
Subject: [PATCH 08/19] Add the validation react element

---
 src/renderers/dom/ReactDOMStringRenderer.js   |  23 ++-
 .../__tests__/ReactServerRendering-test.js    | 164 +++++++++---------
 2 files changed, 100 insertions(+), 87 deletions(-)

diff --git a/src/renderers/dom/ReactDOMStringRenderer.js b/src/renderers/dom/ReactDOMStringRenderer.js
index 6f0363c993d6..7461c3297c83 100644
--- a/src/renderers/dom/ReactDOMStringRenderer.js
+++ b/src/renderers/dom/ReactDOMStringRenderer.js
@@ -14,6 +14,7 @@
 var invariant = require('fbjs/lib/invariant');
 var React = require('react');
 var ReactPartialRenderer = require('ReactPartialRenderer');
+var ReactFeatureFlags = require('ReactFeatureFlags');
 
 /**
  * Render a ReactElement to its initial HTML. This should only be used on the
@@ -21,10 +22,13 @@ var ReactPartialRenderer = require('ReactPartialRenderer');
  * See https://facebook.github.io/react/docs/react-dom-server.html#rendertostring
  */
 function renderToString(element) {
-  invariant(
-    Array.isArray(element) || React.isValidElement(element),
-    'renderToString(): You must pass a valid ReactElement.',
-  );
+  const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
+  if (disableNewFiberFeatures) {
+    invariant(
+      React.isValidElement(element),
+      'renderToString(): You must pass a valid ReactElement.',
+    );
+  }
   var renderer = new ReactPartialRenderer(element, false);
   var markup = renderer.read(Infinity);
   return markup;
@@ -36,10 +40,13 @@ function renderToString(element) {
  * See https://facebook.github.io/react/docs/react-dom-server.html#rendertostaticmarkup
  */
 function renderToStaticMarkup(element) {
-  invariant(
-    Array.isArray(element) || React.isValidElement(element),
-    'renderToStaticMarkup(): You must pass a valid ReactElement.',
-  );
+  const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
+  if (disableNewFiberFeatures) {
+    invariant(
+      React.isValidElement(element),
+      'renderToStaticMarkup(): You must pass a valid ReactElement.',
+    );
+  }
   var renderer = new ReactPartialRenderer(element, true);
   var markup = renderer.read(Infinity);
   return markup;
diff --git a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
index b768f3bef2bb..53d50e85ff04 100644
--- a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
@@ -20,6 +20,7 @@ var ReactMarkupChecksum;
 var ReactReconcileTransaction;
 var ReactTestUtils;
 var PropTypes;
+var ReactFeatureFlags;
 
 var ID_ATTRIBUTE_NAME;
 var ROOT_ATTRIBUTE_NAME;
@@ -34,6 +35,7 @@ describe('ReactDOMServer', () => {
     ReactMarkupChecksum = require('ReactMarkupChecksum');
     ReactReconcileTransaction = require('ReactReconcileTransaction');
     PropTypes = require('prop-types');
+    ReactFeatureFlags = require('ReactFeatureFlags');
 
     ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
     ExecutionEnvironment.canUseDOM = false;
@@ -50,14 +52,14 @@ describe('ReactDOMServer', () => {
       expect(response).toMatch(
         new RegExp(
           '<span ' +
-            ROOT_ATTRIBUTE_NAME +
-            '="" ' +
-            ID_ATTRIBUTE_NAME +
-            '="[^"]*"' +
-            (ReactDOMFeatureFlags.useFiber
-              ? ''
-              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-            '>hello world</span>',
+          ROOT_ATTRIBUTE_NAME +
+          '="" ' +
+          ID_ATTRIBUTE_NAME +
+          '="[^"]*"' +
+          (ReactDOMFeatureFlags.useFiber
+            ? ''
+            : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+          '>hello world</span>',
         ),
       );
     });
@@ -67,14 +69,14 @@ describe('ReactDOMServer', () => {
       expect(response).toMatch(
         new RegExp(
           '<img ' +
-            ROOT_ATTRIBUTE_NAME +
-            '="" ' +
-            ID_ATTRIBUTE_NAME +
-            '="[^"]*"' +
-            (ReactDOMFeatureFlags.useFiber
-              ? ''
-              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-            '/>',
+          ROOT_ATTRIBUTE_NAME +
+          '="" ' +
+          ID_ATTRIBUTE_NAME +
+          '="[^"]*"' +
+          (ReactDOMFeatureFlags.useFiber
+            ? ''
+            : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+          '/>',
         ),
       );
     });
@@ -84,14 +86,14 @@ describe('ReactDOMServer', () => {
       expect(response).toMatch(
         new RegExp(
           '<img data-attr="&gt;" ' +
-            ROOT_ATTRIBUTE_NAME +
-            '="" ' +
-            ID_ATTRIBUTE_NAME +
-            '="[^"]*"' +
-            (ReactDOMFeatureFlags.useFiber
-              ? ''
-              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-            '/>',
+          ROOT_ATTRIBUTE_NAME +
+          '="" ' +
+          ID_ATTRIBUTE_NAME +
+          '="[^"]*"' +
+          (ReactDOMFeatureFlags.useFiber
+            ? ''
+            : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+          '/>',
         ),
       );
     });
@@ -130,23 +132,23 @@ describe('ReactDOMServer', () => {
       expect(response).toMatch(
         new RegExp(
           '<div ' +
-            ROOT_ATTRIBUTE_NAME +
-            '="" ' +
-            ID_ATTRIBUTE_NAME +
-            '="[^"]*"' +
-            (ReactDOMFeatureFlags.useFiber
-              ? ''
-              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-            '>' +
-            '<span ' +
-            ID_ATTRIBUTE_NAME +
-            '="[^"]*">' +
-            (ReactDOMFeatureFlags.useFiber
-              ? 'My name is <!-- -->child'
-              : '<!-- react-text: [0-9]+ -->My name is <!-- /react-text -->' +
-                  '<!-- react-text: [0-9]+ -->child<!-- /react-text -->') +
-            '</span>' +
-            '</div>',
+          ROOT_ATTRIBUTE_NAME +
+          '="" ' +
+          ID_ATTRIBUTE_NAME +
+          '="[^"]*"' +
+          (ReactDOMFeatureFlags.useFiber
+            ? ''
+            : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+          '>' +
+          '<span ' +
+          ID_ATTRIBUTE_NAME +
+          '="[^"]*">' +
+          (ReactDOMFeatureFlags.useFiber
+            ? 'My name is <!-- -->child'
+            : '<!-- react-text: [0-9]+ -->My name is <!-- /react-text -->' +
+            '<!-- react-text: [0-9]+ -->child<!-- /react-text -->') +
+          '</span>' +
+          '</div>',
         ),
       );
     });
@@ -159,7 +161,7 @@ describe('ReactDOMServer', () => {
           constructor(props) {
             super(props);
             lifecycle.push('getInitialState');
-            this.state = {name: 'TestComponent'};
+            this.state = { name: 'TestComponent' };
           }
 
           componentWillMount() {
@@ -201,19 +203,19 @@ describe('ReactDOMServer', () => {
         expect(response).toMatch(
           new RegExp(
             '<span ' +
-              ROOT_ATTRIBUTE_NAME +
-              '="" ' +
-              ID_ATTRIBUTE_NAME +
-              '="[^"]*"' +
-              (ReactDOMFeatureFlags.useFiber
-                ? ''
-                : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-              '>' +
-              (ReactDOMFeatureFlags.useFiber
-                ? 'Component name: <!-- -->TestComponent'
-                : '<!-- react-text: [0-9]+ -->Component name: <!-- /react-text -->' +
-                    '<!-- react-text: [0-9]+ -->TestComponent<!-- /react-text -->') +
-              '</span>',
+            ROOT_ATTRIBUTE_NAME +
+            '="" ' +
+            ID_ATTRIBUTE_NAME +
+            '="[^"]*"' +
+            (ReactDOMFeatureFlags.useFiber
+              ? ''
+              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+            '>' +
+            (ReactDOMFeatureFlags.useFiber
+              ? 'Component name: <!-- -->TestComponent'
+              : '<!-- react-text: [0-9]+ -->Component name: <!-- /react-text -->' +
+              '<!-- react-text: [0-9]+ -->TestComponent<!-- /react-text -->') +
+            '</span>',
           ),
         );
         expect(lifecycle).toEqual([
@@ -375,7 +377,7 @@ describe('ReactDOMServer', () => {
           constructor(props) {
             super(props);
             lifecycle.push('getInitialState');
-            this.state = {name: 'TestComponent'};
+            this.state = { name: 'TestComponent' };
           }
 
           componentWillMount() {
@@ -429,21 +431,25 @@ describe('ReactDOMServer', () => {
       runTest();
     });
 
+
     it('should throw with silly args', () => {
-      expect(
-        ReactDOMServer.renderToStaticMarkup.bind(
-          ReactDOMServer,
-          'not a component',
-        ),
-      ).toThrowError(
-        'renderToStaticMarkup(): You must pass a valid ReactElement.',
-      );
+      const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
+      if (disableNewFiberFeatures) {
+        expect(
+          ReactDOMServer.renderToStaticMarkup.bind(
+            ReactDOMServer,
+            'not a component',
+          ),
+        ).toThrowError(
+          'renderToStaticMarkup(): You must pass a valid ReactElement.',
+        );
+      }
     });
 
     it('allows setState in componentWillMount without using DOM', () => {
       class Component extends React.Component {
         componentWillMount() {
-          this.setState({text: 'hello, world'});
+          this.setState({ text: 'hello, world' });
         }
 
         render() {
@@ -451,7 +457,7 @@ describe('ReactDOMServer', () => {
         }
       }
 
-      ReactReconcileTransaction.prototype.perform = function() {
+      ReactReconcileTransaction.prototype.perform = function () {
         // We shouldn't ever be calling this on the server
         throw new Error('Browser reconcile transaction should not be used');
       };
@@ -463,11 +469,11 @@ describe('ReactDOMServer', () => {
       class Component extends React.Component {
         constructor() {
           super();
-          this.state = {text: 'default state'};
+          this.state = { text: 'default state' };
         }
 
         componentWillMount() {
-          this.setState({text: 'hello, world'});
+          this.setState({ text: 'hello, world' });
         }
 
         render() {
@@ -475,7 +481,7 @@ describe('ReactDOMServer', () => {
         }
       }
 
-      ReactReconcileTransaction.prototype.perform = function() {
+      ReactReconcileTransaction.prototype.perform = function () {
         // We shouldn't ever be calling this on the server
         throw new Error('Browser reconcile transaction should not be used');
       };
@@ -545,13 +551,13 @@ describe('ReactDOMServer', () => {
               <img src="foo-bar.jpg" />
             </div>,
           );
-          return <div dangerouslySetInnerHTML={{__html: staticContent}} />;
+          return <div dangerouslySetInnerHTML={{ __html: staticContent }} />;
         }
       }
 
       class Component extends React.Component {
         componentWillMount() {
-          this.setState({text: 'hello, world'});
+          this.setState({ text: 'hello, world' });
         }
 
         render() {
@@ -574,13 +580,13 @@ describe('ReactDOMServer', () => {
   it('warns with a no-op when an async setState is triggered', () => {
     class Foo extends React.Component {
       componentWillMount() {
-        this.setState({text: 'hello'});
+        this.setState({ text: 'hello' });
         setTimeout(() => {
-          this.setState({text: 'error'});
+          this.setState({ text: 'error' });
         });
       }
       render() {
-        return <div onClick={() => {}}>{this.state.text}</div>;
+        return <div onClick={() => { }}>{this.state.text}</div>;
       }
     }
 
@@ -590,8 +596,8 @@ describe('ReactDOMServer', () => {
     expectDev(console.error.calls.count()).toBe(1);
     expectDev(console.error.calls.mostRecent().args[0]).toBe(
       'Warning: setState(...): Can only update a mounting component.' +
-        ' This usually means you called setState() outside componentWillMount() on the server.' +
-        ' This is a no-op.\n\nPlease check the code for the Foo component.',
+      ' This usually means you called setState() outside componentWillMount() on the server.' +
+      ' This is a no-op.\n\nPlease check the code for the Foo component.',
     );
     var markup = ReactDOMServer.renderToStaticMarkup(<Foo />);
     expect(markup).toBe('<div>hello</div>');
@@ -607,7 +613,7 @@ describe('ReactDOMServer', () => {
       }
 
       render() {
-        return <div onClick={() => {}} />;
+        return <div onClick={() => { }} />;
       }
     }
 
@@ -617,8 +623,8 @@ describe('ReactDOMServer', () => {
     expectDev(console.error.calls.count()).toBe(1);
     expectDev(console.error.calls.mostRecent().args[0]).toBe(
       'Warning: forceUpdate(...): Can only update a mounting component. ' +
-        'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
-        'This is a no-op.\n\nPlease check the code for the Baz component.',
+      'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
+      'This is a no-op.\n\nPlease check the code for the Baz component.',
     );
     var markup = ReactDOMServer.renderToStaticMarkup(<Baz />);
     expect(markup).toBe('<div></div>');

From 3df964dea7bd95e26254296a71395be003903de9 Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Sat, 22 Jul 2017 11:29:35 +0800
Subject: [PATCH 09/19] Improve an integration test of server renderToString
 with array type

---
 .../__tests__/ReactDOMServerIntegration-test.js      | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 3b47d070bd09..83363f951c68 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -344,10 +344,18 @@ describe('ReactDOMServerIntegration', () => {
 
     if (ReactDOMFeatureFlags.useFiber) {
       itRenders('a array type children as a child', async render => {
-        let e = await render([<div key={1}>text1</div>, <p key={2}>text2</p>]);
+        let Header = props => {
+          return <p>header</p>;
+        };
+        let e = await render([
+          <div key={1}>text1</div>,
+          <span key={2}>text2</span>,
+          <Header key={3} />,
+        ]);
         let parent = e.parentNode;
         expect(parent.childNodes[0].tagName).toBe('DIV');
-        expect(parent.childNodes[1].tagName).toBe('P');
+        expect(parent.childNodes[1].tagName).toBe('SPAN');
+        expect(parent.childNodes[2].tagName).toBe('P');
       });
     }
   });

From 02101d5d4acccce66b303535aeea5bb07d9a384f Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Sat, 22 Jul 2017 13:38:25 +0800
Subject: [PATCH 10/19] prettier

---
 .../__tests__/ReactServerRendering-test.js    | 143 +++++++++---------
 1 file changed, 71 insertions(+), 72 deletions(-)

diff --git a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
index 53d50e85ff04..9caf9196b23d 100644
--- a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
@@ -52,14 +52,14 @@ describe('ReactDOMServer', () => {
       expect(response).toMatch(
         new RegExp(
           '<span ' +
-          ROOT_ATTRIBUTE_NAME +
-          '="" ' +
-          ID_ATTRIBUTE_NAME +
-          '="[^"]*"' +
-          (ReactDOMFeatureFlags.useFiber
-            ? ''
-            : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-          '>hello world</span>',
+            ROOT_ATTRIBUTE_NAME +
+            '="" ' +
+            ID_ATTRIBUTE_NAME +
+            '="[^"]*"' +
+            (ReactDOMFeatureFlags.useFiber
+              ? ''
+              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+            '>hello world</span>',
         ),
       );
     });
@@ -69,14 +69,14 @@ describe('ReactDOMServer', () => {
       expect(response).toMatch(
         new RegExp(
           '<img ' +
-          ROOT_ATTRIBUTE_NAME +
-          '="" ' +
-          ID_ATTRIBUTE_NAME +
-          '="[^"]*"' +
-          (ReactDOMFeatureFlags.useFiber
-            ? ''
-            : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-          '/>',
+            ROOT_ATTRIBUTE_NAME +
+            '="" ' +
+            ID_ATTRIBUTE_NAME +
+            '="[^"]*"' +
+            (ReactDOMFeatureFlags.useFiber
+              ? ''
+              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+            '/>',
         ),
       );
     });
@@ -86,14 +86,14 @@ describe('ReactDOMServer', () => {
       expect(response).toMatch(
         new RegExp(
           '<img data-attr="&gt;" ' +
-          ROOT_ATTRIBUTE_NAME +
-          '="" ' +
-          ID_ATTRIBUTE_NAME +
-          '="[^"]*"' +
-          (ReactDOMFeatureFlags.useFiber
-            ? ''
-            : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-          '/>',
+            ROOT_ATTRIBUTE_NAME +
+            '="" ' +
+            ID_ATTRIBUTE_NAME +
+            '="[^"]*"' +
+            (ReactDOMFeatureFlags.useFiber
+              ? ''
+              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+            '/>',
         ),
       );
     });
@@ -132,23 +132,23 @@ describe('ReactDOMServer', () => {
       expect(response).toMatch(
         new RegExp(
           '<div ' +
-          ROOT_ATTRIBUTE_NAME +
-          '="" ' +
-          ID_ATTRIBUTE_NAME +
-          '="[^"]*"' +
-          (ReactDOMFeatureFlags.useFiber
-            ? ''
-            : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-          '>' +
-          '<span ' +
-          ID_ATTRIBUTE_NAME +
-          '="[^"]*">' +
-          (ReactDOMFeatureFlags.useFiber
-            ? 'My name is <!-- -->child'
-            : '<!-- react-text: [0-9]+ -->My name is <!-- /react-text -->' +
-            '<!-- react-text: [0-9]+ -->child<!-- /react-text -->') +
-          '</span>' +
-          '</div>',
+            ROOT_ATTRIBUTE_NAME +
+            '="" ' +
+            ID_ATTRIBUTE_NAME +
+            '="[^"]*"' +
+            (ReactDOMFeatureFlags.useFiber
+              ? ''
+              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+            '>' +
+            '<span ' +
+            ID_ATTRIBUTE_NAME +
+            '="[^"]*">' +
+            (ReactDOMFeatureFlags.useFiber
+              ? 'My name is <!-- -->child'
+              : '<!-- react-text: [0-9]+ -->My name is <!-- /react-text -->' +
+                  '<!-- react-text: [0-9]+ -->child<!-- /react-text -->') +
+            '</span>' +
+            '</div>',
         ),
       );
     });
@@ -161,7 +161,7 @@ describe('ReactDOMServer', () => {
           constructor(props) {
             super(props);
             lifecycle.push('getInitialState');
-            this.state = { name: 'TestComponent' };
+            this.state = {name: 'TestComponent'};
           }
 
           componentWillMount() {
@@ -203,19 +203,19 @@ describe('ReactDOMServer', () => {
         expect(response).toMatch(
           new RegExp(
             '<span ' +
-            ROOT_ATTRIBUTE_NAME +
-            '="" ' +
-            ID_ATTRIBUTE_NAME +
-            '="[^"]*"' +
-            (ReactDOMFeatureFlags.useFiber
-              ? ''
-              : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
-            '>' +
-            (ReactDOMFeatureFlags.useFiber
-              ? 'Component name: <!-- -->TestComponent'
-              : '<!-- react-text: [0-9]+ -->Component name: <!-- /react-text -->' +
-              '<!-- react-text: [0-9]+ -->TestComponent<!-- /react-text -->') +
-            '</span>',
+              ROOT_ATTRIBUTE_NAME +
+              '="" ' +
+              ID_ATTRIBUTE_NAME +
+              '="[^"]*"' +
+              (ReactDOMFeatureFlags.useFiber
+                ? ''
+                : ' ' + ReactMarkupChecksum.CHECKSUM_ATTR_NAME + '="[^"]+"') +
+              '>' +
+              (ReactDOMFeatureFlags.useFiber
+                ? 'Component name: <!-- -->TestComponent'
+                : '<!-- react-text: [0-9]+ -->Component name: <!-- /react-text -->' +
+                    '<!-- react-text: [0-9]+ -->TestComponent<!-- /react-text -->') +
+              '</span>',
           ),
         );
         expect(lifecycle).toEqual([
@@ -377,7 +377,7 @@ describe('ReactDOMServer', () => {
           constructor(props) {
             super(props);
             lifecycle.push('getInitialState');
-            this.state = { name: 'TestComponent' };
+            this.state = {name: 'TestComponent'};
           }
 
           componentWillMount() {
@@ -431,7 +431,6 @@ describe('ReactDOMServer', () => {
       runTest();
     });
 
-
     it('should throw with silly args', () => {
       const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
       if (disableNewFiberFeatures) {
@@ -449,7 +448,7 @@ describe('ReactDOMServer', () => {
     it('allows setState in componentWillMount without using DOM', () => {
       class Component extends React.Component {
         componentWillMount() {
-          this.setState({ text: 'hello, world' });
+          this.setState({text: 'hello, world'});
         }
 
         render() {
@@ -457,7 +456,7 @@ describe('ReactDOMServer', () => {
         }
       }
 
-      ReactReconcileTransaction.prototype.perform = function () {
+      ReactReconcileTransaction.prototype.perform = function() {
         // We shouldn't ever be calling this on the server
         throw new Error('Browser reconcile transaction should not be used');
       };
@@ -469,11 +468,11 @@ describe('ReactDOMServer', () => {
       class Component extends React.Component {
         constructor() {
           super();
-          this.state = { text: 'default state' };
+          this.state = {text: 'default state'};
         }
 
         componentWillMount() {
-          this.setState({ text: 'hello, world' });
+          this.setState({text: 'hello, world'});
         }
 
         render() {
@@ -481,7 +480,7 @@ describe('ReactDOMServer', () => {
         }
       }
 
-      ReactReconcileTransaction.prototype.perform = function () {
+      ReactReconcileTransaction.prototype.perform = function() {
         // We shouldn't ever be calling this on the server
         throw new Error('Browser reconcile transaction should not be used');
       };
@@ -551,13 +550,13 @@ describe('ReactDOMServer', () => {
               <img src="foo-bar.jpg" />
             </div>,
           );
-          return <div dangerouslySetInnerHTML={{ __html: staticContent }} />;
+          return <div dangerouslySetInnerHTML={{__html: staticContent}} />;
         }
       }
 
       class Component extends React.Component {
         componentWillMount() {
-          this.setState({ text: 'hello, world' });
+          this.setState({text: 'hello, world'});
         }
 
         render() {
@@ -580,13 +579,13 @@ describe('ReactDOMServer', () => {
   it('warns with a no-op when an async setState is triggered', () => {
     class Foo extends React.Component {
       componentWillMount() {
-        this.setState({ text: 'hello' });
+        this.setState({text: 'hello'});
         setTimeout(() => {
-          this.setState({ text: 'error' });
+          this.setState({text: 'error'});
         });
       }
       render() {
-        return <div onClick={() => { }}>{this.state.text}</div>;
+        return <div onClick={() => {}}>{this.state.text}</div>;
       }
     }
 
@@ -596,8 +595,8 @@ describe('ReactDOMServer', () => {
     expectDev(console.error.calls.count()).toBe(1);
     expectDev(console.error.calls.mostRecent().args[0]).toBe(
       'Warning: setState(...): Can only update a mounting component.' +
-      ' This usually means you called setState() outside componentWillMount() on the server.' +
-      ' This is a no-op.\n\nPlease check the code for the Foo component.',
+        ' This usually means you called setState() outside componentWillMount() on the server.' +
+        ' This is a no-op.\n\nPlease check the code for the Foo component.',
     );
     var markup = ReactDOMServer.renderToStaticMarkup(<Foo />);
     expect(markup).toBe('<div>hello</div>');
@@ -613,7 +612,7 @@ describe('ReactDOMServer', () => {
       }
 
       render() {
-        return <div onClick={() => { }} />;
+        return <div onClick={() => {}} />;
       }
     }
 
@@ -623,8 +622,8 @@ describe('ReactDOMServer', () => {
     expectDev(console.error.calls.count()).toBe(1);
     expectDev(console.error.calls.mostRecent().args[0]).toBe(
       'Warning: forceUpdate(...): Can only update a mounting component. ' +
-      'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
-      'This is a no-op.\n\nPlease check the code for the Baz component.',
+        'This usually means you called forceUpdate() outside componentWillMount() on the server. ' +
+        'This is a no-op.\n\nPlease check the code for the Baz component.',
     );
     var markup = ReactDOMServer.renderToStaticMarkup(<Baz />);
     expect(markup).toBe('<div></div>');

From 5f8a2ddaaa927733b1cb589cab80cfef739597ec Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Fri, 28 Jul 2017 11:33:50 +0800
Subject: [PATCH 11/19] prettier

---
 src/renderers/shared/server/ReactPartialRenderer.js | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/src/renderers/shared/server/ReactPartialRenderer.js b/src/renderers/shared/server/ReactPartialRenderer.js
index afefefabdd8f..4e8f33ec6f32 100644
--- a/src/renderers/shared/server/ReactPartialRenderer.js
+++ b/src/renderers/shared/server/ReactPartialRenderer.js
@@ -306,7 +306,7 @@ function createOpenTagMarkup(
   return ret;
 }
 
-function resolve(child, context) { 
+function resolve(child, context) {
   while (React.isValidElement(child)) {
     if (__DEV__) {
       pushElementToDebugStack(child);
@@ -482,9 +482,10 @@ class ReactDOMServerRenderer {
       if (child === null || child === false) {
         return '';
       } else {
-        if (Array.isArray(child)) {
+        var children = toArray(child);
+        if (children.length > 1) {
           var frame = {
-            children: child,
+            children,
             childIndex: 0,
             context: context,
             footer: '',

From cf005dd11f6b9dd3d9d75852bfbd40e86c272591 Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Sat, 29 Jul 2017 18:58:04 +0800
Subject: [PATCH 12/19] Make SSR can handle a single array element and nested
 array

---
 .../ReactDOMServerIntegration-test.js         | 265 +++++++++++-------
 .../shared/server/ReactPartialRenderer.js     |  10 +-
 2 files changed, 174 insertions(+), 101 deletions(-)

diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 408f63c64f5c..77c7b1302efe 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -25,6 +25,9 @@ const stream = require('stream');
 
 // Helper functions for rendering tests
 // ====================================
+function normalizeCodeLocInfo(str) {
+  return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
+}
 
 // promisified version of ReactDOM.render()
 function asyncReactDOMRender(reactElement, domElement) {
@@ -149,7 +152,7 @@ const clientRenderOnServerString = async (element, errorCount = 0) => {
   return clientElement;
 };
 
-function BadMarkupExpected() {}
+function BadMarkupExpected() { }
 
 const clientRenderOnBadMarkup = async (element, errorCount = 0) => {
   // First we render the top of bad mark up.
@@ -232,8 +235,8 @@ function itThrows(desc, testFn) {
     return testFn()
       .then(() =>
         expect(false).toBe('The promise resolved and should not have.'),
-      )
-      .catch(() => {});
+    )
+      .catch(() => { });
   });
 }
 
@@ -331,7 +334,7 @@ describe('ReactDOMServerIntegration', () => {
     ExecutionEnvironment.canUseDOM = false;
   });
 
-  describe('basic rendering', function() {
+  describe('basic rendering', function () {
     beforeEach(() => {
       onAfterResetModules = () => {
         const ReactFeatureFlags = require('ReactFeatureFlags');
@@ -376,11 +379,79 @@ describe('ReactDOMServerIntegration', () => {
         expect(parent.childNodes[1].tagName).toBe('SPAN');
         expect(parent.childNodes[2].tagName).toBe('P');
       });
+
+      itRenders('a single array element children as a child', async render => {
+        let e = await render([<div key={1}>text1</div>]);
+        let parent = e.parentNode;
+        expect(parent.childNodes[0].tagName).toBe('DIV');
+      });
+
+      itRenders('a nested array children as a child', async render => {
+        let e = await render([[<div key={1}>text1</div>],<span key={2}>text2</span>]);
+        let parent = e.parentNode;
+        expect(parent.childNodes[0].tagName).toBe('DIV');
+        expect(parent.childNodes[1].tagName).toBe('SPAN');
+      });
+
+      itRenders(
+        'throws if a plain object is used as a child on SSR',
+        async () => {
+          var children = {
+            x: <span />,
+            y: <span />,
+            z: <span />,
+          };
+          var element = <div>{[children]}</div>;
+          var ex;
+          try {
+            ReactDOMServer.renderToString(element);
+          } catch (e) {
+            ex = e;
+          }
+          expect(ex).toBeDefined();
+          expect(normalizeCodeLocInfo(ex.message)).toBe(
+            'Objects are not valid as a React child (found: object with keys ' +
+            '{x, y, z}). If you meant to render a collection of children, use ' +
+            'an array instead.' +
+            // Fiber gives a slightly better stack with the nearest host components
+            (ReactDOMFeatureFlags.useFiber ? '\n    in div (at **)' : ''),
+          );
+        },
+      );
+
+      it('throws if a plain object even if it is in an owner on SSR', async () => {
+        class Foo extends React.Component {
+          render() {
+            var children = {
+              a: <span />,
+              b: <span />,
+              c: <span />,
+            };
+            return <div>{[children]}</div>;
+          }
+        }
+        var container = document.createElement('div');
+        var ex;
+        try {
+          ReactDOMServer.renderToString(<Foo />, container);
+        } catch (e) {
+          ex = e;
+        }
+        expect(ex).toBeDefined();
+        expect(normalizeCodeLocInfo(ex.message)).toBe(
+          'Objects are not valid as a React child (found: object with keys ' +
+          '{a, b, c}). If you meant to render a collection of children, use ' +
+          'an array instead.\n' +
+          // Fiber gives a slightly better stack with the nearest host components
+          (ReactDOMFeatureFlags.useFiber ? '    in div (at **)\n' : '') +
+          '    in Foo (at **)',
+        );
+      });
     }
   });
 
-  describe('property to attribute mapping', function() {
-    describe('string properties', function() {
+  describe('property to attribute mapping', function () {
+    describe('string properties', function () {
       itRenders('simple numbers', async render => {
         const e = await render(<div width={30} />);
         expect(e.getAttribute('width')).toBe('30');
@@ -409,7 +480,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('boolean properties', function() {
+    describe('boolean properties', function () {
       itRenders('boolean prop with true value', async render => {
         const e = await render(<div hidden={true} />);
         expect(e.getAttribute('hidden')).toBe('');
@@ -447,7 +518,7 @@ describe('ReactDOMServerIntegration', () => {
 
       // this seems like it might mask programmer error, but it's existing behavior.
       itRenders('boolean prop with object value', async render => {
-        const e = await render(<div hidden={{foo: 'bar'}} />);
+        const e = await render(<div hidden={{ foo: 'bar' }} />);
         expect(e.getAttribute('hidden')).toBe('');
       });
 
@@ -469,7 +540,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('download property (combined boolean/string attribute)', function() {
+    describe('download property (combined boolean/string attribute)', function () {
       itRenders('download prop with true value', async render => {
         const e = await render(<a download={true} />);
         expect(e.getAttribute('download')).toBe('');
@@ -511,7 +582,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('className property', function() {
+    describe('className property', function () {
       itRenders('className prop with string value', async render => {
         const e = await render(<div className="myClassName" />);
         expect(e.getAttribute('class')).toBe('myClassName');
@@ -540,7 +611,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('htmlFor property', function() {
+    describe('htmlFor property', function () {
       itRenders('htmlFor with string value', async render => {
         const e = await render(<div htmlFor="myFor" />);
         expect(e.getAttribute('for')).toBe('myFor');
@@ -569,7 +640,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('numeric properties', function() {
+    describe('numeric properties', function () {
       itRenders(
         'positive numeric property with positive value',
         async render => {
@@ -592,7 +663,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('props with special meaning in React', function() {
+    describe('props with special meaning in React', function () {
       itRenders('no ref attribute', async render => {
         class RefComponent extends React.Component {
           render() {
@@ -615,15 +686,15 @@ describe('ReactDOMServerIntegration', () => {
 
       itRenders('no dangerouslySetInnerHTML attribute', async render => {
         const e = await render(
-          <div dangerouslySetInnerHTML={{__html: '<foo />'}} />,
+          <div dangerouslySetInnerHTML={{ __html: '<foo />' }} />,
         );
         expect(e.getAttribute('dangerouslySetInnerHTML')).toBe(null);
       });
     });
 
-    describe('inline styles', function() {
+    describe('inline styles', function () {
       itRenders('simple styles', async render => {
-        const e = await render(<div style={{color: 'red', width: '30px'}} />);
+        const e = await render(<div style={{ color: 'red', width: '30px' }} />);
         expect(e.style.color).toBe('red');
         expect(e.style.width).toBe('30px');
       });
@@ -646,7 +717,7 @@ describe('ReactDOMServerIntegration', () => {
       });
 
       itRenders('custom properties', async render => {
-        const e = await render(<div style={{'--foo': 5}} />);
+        const e = await render(<div style={{ '--foo': 5 }} />);
         // This seems like an odd way computed properties are exposed in jsdom.
         // In a real browser we'd read it with e.style.getPropertyValue('--foo')
         expect(e.style.Foo).toBe('5');
@@ -654,27 +725,27 @@ describe('ReactDOMServerIntegration', () => {
 
       itRenders('no undefined styles', async render => {
         const e = await render(
-          <div style={{color: undefined, width: '30px'}} />,
+          <div style={{ color: undefined, width: '30px' }} />,
         );
         expect(e.style.color).toBe('');
         expect(e.style.width).toBe('30px');
       });
 
       itRenders('no null styles', async render => {
-        const e = await render(<div style={{color: null, width: '30px'}} />);
+        const e = await render(<div style={{ color: null, width: '30px' }} />);
         expect(e.style.color).toBe('');
         expect(e.style.width).toBe('30px');
       });
 
       itRenders('no empty styles', async render => {
-        const e = await render(<div style={{color: null, width: null}} />);
+        const e = await render(<div style={{ color: null, width: null }} />);
         expect(e.style.color).toBe('');
         expect(e.style.width).toBe('');
         expect(e.hasAttribute('style')).toBe(false);
       });
     });
 
-    describe('aria attributes', function() {
+    describe('aria attributes', function () {
       itRenders('simple strings', async render => {
         const e = await render(<div aria-label="hello" />);
         expect(e.getAttribute('aria-label')).toBe('hello');
@@ -692,7 +763,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('unknown attributes', function() {
+    describe('unknown attributes', function () {
       itRenders('no unknown attributes', async render => {
         const e = await render(<div foo="bar" />, 1);
         expect(e.getAttribute('foo')).toBe(null);
@@ -747,14 +818,14 @@ describe('ReactDOMServerIntegration', () => {
     });
 
     itRenders('no HTML events', async render => {
-      const e = await render(<div onClick={() => {}} />);
+      const e = await render(<div onClick={() => { }} />);
       expect(e.getAttribute('onClick')).toBe(null);
       expect(e.getAttribute('onClick')).toBe(null);
       expect(e.getAttribute('click')).toBe(null);
     });
   });
 
-  describe('elements and children', function() {
+  describe('elements and children', function () {
     // helper functions.
     const TEXT_NODE_TYPE = 3;
     const COMMENT_NODE_TYPE = 8;
@@ -785,7 +856,7 @@ describe('ReactDOMServerIntegration', () => {
       expectNode(node, COMMENT_NODE_TYPE, / react-empty: [0-9]+ /);
     }
 
-    describe('text children', function() {
+    describe('text children', function () {
       itRenders('a div with text', async render => {
         const e = await render(<div>Text</div>);
         expect(e.tagName).toBe('DIV');
@@ -967,7 +1038,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('number children', function() {
+    describe('number children', function () {
       itRenders('a number as single child', async render => {
         const e = await render(<div>{3}</div>);
         expect(e.textContent).toBe('3');
@@ -1007,7 +1078,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('null, false, and undefined children', function() {
+    describe('null, false, and undefined children', function () {
       itRenders('null single child as blank', async render => {
         const e = await render(<div>{null}</div>);
         expect(e.childNodes.length).toBe(0);
@@ -1082,7 +1153,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('elements with implicit namespaces', function() {
+    describe('elements with implicit namespaces', function () {
       itRenders('an svg element', async render => {
         const e = await render(<svg />);
         expect(e.childNodes.length).toBe(0);
@@ -1128,7 +1199,7 @@ describe('ReactDOMServerIntegration', () => {
 
     itRenders('a div with dangerouslySetInnerHTML', async render => {
       const e = await render(
-        <div dangerouslySetInnerHTML={{__html: "<span id='child'/>"}} />,
+        <div dangerouslySetInnerHTML={{ __html: "<span id='child'/>" }} />,
       );
       expect(e.childNodes.length).toBe(1);
       expect(e.firstChild.tagName).toBe('SPAN');
@@ -1136,7 +1207,7 @@ describe('ReactDOMServerIntegration', () => {
       expect(e.firstChild.childNodes.length).toBe(0);
     });
 
-    describe('newline-eating elements', function() {
+    describe('newline-eating elements', function () {
       itRenders(
         'a newline-eating tag with content not starting with \\n',
         async render => {
@@ -1157,7 +1228,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('different component implementations', function() {
+    describe('different component implementations', function () {
       function checkFooDiv(e) {
         expect(e.childNodes.length).toBe(1);
         expectNode(e.firstChild, TEXT_NODE_TYPE, 'foo');
@@ -1180,7 +1251,7 @@ describe('ReactDOMServerIntegration', () => {
       itRenders('factory components', async render => {
         const FactoryComponent = () => {
           return {
-            render: function() {
+            render: function () {
               return <div>foo</div>;
             },
           };
@@ -1189,7 +1260,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('component hierarchies', async function() {
+    describe('component hierarchies', async function () {
       itRenders('single child hierarchies of components', async render => {
         const Component = props => <div>{props.children}</div>;
         let e = await render(
@@ -1318,7 +1389,7 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('escaping >, <, and &', function() {
+    describe('escaping >, <, and &', function () {
       itRenders('>,<, and & as single child', async render => {
         const e = await render(<div>{'<span>Text&quot;</span>'}</div>);
         expect(e.childNodes.length).toBe(1);
@@ -1354,7 +1425,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('components that throw errors', function() {
+    describe('components that throw errors', function () {
       itThrowsWhenRendering('a string component', async render => {
         const StringComponent = () => 'foo';
         await render(<StringComponent />, 1);
@@ -1378,10 +1449,10 @@ describe('ReactDOMServerIntegration', () => {
     });
   });
 
-  describe('form controls', function() {
-    describe('inputs', function() {
+  describe('form controls', function () {
+    describe('inputs', function () {
       itRenders('an input with a value and an onChange', async render => {
-        const e = await render(<input value="foo" onChange={() => {}} />);
+        const e = await render(<input value="foo" onChange={() => { }} />);
         expect(e.value).toBe('foo');
       });
 
@@ -1432,10 +1503,10 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('checkboxes', function() {
+    describe('checkboxes', function () {
       itRenders('a checkbox that is checked with an onChange', async render => {
         const e = await render(
-          <input type="checkbox" checked={true} onChange={() => {}} />,
+          <input type="checkbox" checked={true} onChange={() => { }} />,
         );
         expect(e.checked).toBe(true);
       });
@@ -1498,11 +1569,11 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('textareas', function() {
+    describe('textareas', function () {
       // textareas
       // ---------
       itRenders('a textarea with a value and an onChange', async render => {
-        const e = await render(<textarea value="foo" onChange={() => {}} />);
+        const e = await render(<textarea value="foo" onChange={() => { }} />);
         // textarea DOM elements don't have a value **attribute**, the text is
         // a child of the element and accessible via the .value **property**.
         expect(e.getAttribute('value')).toBe(null);
@@ -1559,9 +1630,9 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('selects', function() {
+    describe('selects', function () {
       var options;
-      beforeEach(function() {
+      beforeEach(function () {
         options = [
           <option key={1} value="foo" id="foo">Foo</option>,
           <option key={2} value="bar" id="bar">Bar</option>,
@@ -1590,7 +1661,7 @@ describe('ReactDOMServerIntegration', () => {
 
       itRenders('a select with a value and an onChange', async render => {
         const e = await render(
-          <select value="bar" onChange={() => {}}>{options}</select>,
+          <select value="bar" onChange={() => { }}>{options}</select>,
         );
         expectSelectValue(e, 'bar');
       });
@@ -1606,7 +1677,7 @@ describe('ReactDOMServerIntegration', () => {
         'a select with a multiple values and an onChange',
         async render => {
           const e = await render(
-            <select value={['bar', 'baz']} multiple={true} onChange={() => {}}>
+            <select value={['bar', 'baz']} multiple={true} onChange={() => { }}>
               {options}
             </select>,
           );
@@ -1665,7 +1736,7 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('user interaction', function() {
+    describe('user interaction', function () {
       let ControlledInput,
         ControlledTextArea,
         ControlledCheckbox,
@@ -1674,13 +1745,13 @@ describe('ReactDOMServerIntegration', () => {
         ControlledInput = class extends React.Component {
           constructor() {
             super();
-            this.state = {value: 'Hello'};
+            this.state = { value: 'Hello' };
           }
           handleChange(event) {
             if (this.props.onChange) {
               this.props.onChange(event);
             }
-            this.setState({value: event.target.value});
+            this.setState({ value: event.target.value });
           }
           render() {
             return (
@@ -1694,13 +1765,13 @@ describe('ReactDOMServerIntegration', () => {
         ControlledTextArea = class extends React.Component {
           constructor() {
             super();
-            this.state = {value: 'Hello'};
+            this.state = { value: 'Hello' };
           }
           handleChange(event) {
             if (this.props.onChange) {
               this.props.onChange(event);
             }
-            this.setState({value: event.target.value});
+            this.setState({ value: event.target.value });
           }
           render() {
             return (
@@ -1714,13 +1785,13 @@ describe('ReactDOMServerIntegration', () => {
         ControlledCheckbox = class extends React.Component {
           constructor() {
             super();
-            this.state = {value: true};
+            this.state = { value: true };
           }
           handleChange(event) {
             if (this.props.onChange) {
               this.props.onChange(event);
             }
-            this.setState({value: event.target.checked});
+            this.setState({ value: event.target.checked });
           }
           render() {
             return (
@@ -1735,13 +1806,13 @@ describe('ReactDOMServerIntegration', () => {
         ControlledSelect = class extends React.Component {
           constructor() {
             super();
-            this.state = {value: 'Hello'};
+            this.state = { value: 'Hello' };
           }
           handleChange(event) {
             if (this.props.onChange) {
               this.props.onChange(event);
             }
-            this.setState({value: event.target.value});
+            this.setState({ value: event.target.value });
           }
           render() {
             return (
@@ -1756,7 +1827,7 @@ describe('ReactDOMServerIntegration', () => {
         };
       });
 
-      describe('user interaction with controlled inputs', function() {
+      describe('user interaction with controlled inputs', function () {
         itClientRenders('a controlled text input', async render => {
           let changeCount = 0;
           const e = await render(
@@ -1822,7 +1893,7 @@ describe('ReactDOMServerIntegration', () => {
         });
       });
 
-      describe('user interaction with inputs before client render', function() {
+      describe('user interaction with inputs before client render', function () {
         // renders the element and changes the value **before** the client
         // code has a chance to render; this simulates what happens when a
         // user starts to interact with a server-rendered form before
@@ -1928,18 +1999,18 @@ describe('ReactDOMServerIntegration', () => {
     });
   });
 
-  describe('context', function() {
+  describe('context', function () {
     let PurpleContext, RedContext;
     beforeEach(() => {
       class Parent extends React.Component {
         getChildContext() {
-          return {text: this.props.text};
+          return { text: this.props.text };
         }
         render() {
           return this.props.children;
         }
       }
-      Parent.childContextTypes = {text: PropTypes.string};
+      Parent.childContextTypes = { text: PropTypes.string };
 
       PurpleContext = props => <Parent text="purple">{props.children}</Parent>;
       RedContext = props => <Parent text="red">{props.children}</Parent>;
@@ -1951,7 +2022,7 @@ describe('ReactDOMServerIntegration', () => {
           return <div>{this.context.text}</div>;
         }
       }
-      ClassChildWithContext.contextTypes = {text: PropTypes.string};
+      ClassChildWithContext.contextTypes = { text: PropTypes.string };
 
       const e = await render(
         <PurpleContext><ClassChildWithContext /></PurpleContext>,
@@ -1963,7 +2034,7 @@ describe('ReactDOMServerIntegration', () => {
       function StatelessChildWithContext(props, context) {
         return <div>{context.text}</div>;
       }
-      StatelessChildWithContext.contextTypes = {text: PropTypes.string};
+      StatelessChildWithContext.contextTypes = { text: PropTypes.string };
 
       const e = await render(
         <PurpleContext><StatelessChildWithContext /></PurpleContext>,
@@ -2004,7 +2075,7 @@ describe('ReactDOMServerIntegration', () => {
           return <div id="classWrongChild">{this.context.text}</div>;
         }
       }
-      ClassChildWithWrongContext.contextTypes = {foo: PropTypes.string};
+      ClassChildWithWrongContext.contextTypes = { foo: PropTypes.string };
 
       const e = await render(
         <PurpleContext><ClassChildWithWrongContext /></PurpleContext>,
@@ -2031,7 +2102,7 @@ describe('ReactDOMServerIntegration', () => {
       function Grandchild(props, context) {
         return <div>{context.text}</div>;
       }
-      Grandchild.contextTypes = {text: PropTypes.string};
+      Grandchild.contextTypes = { text: PropTypes.string };
 
       const Child = props => <Grandchild />;
 
@@ -2043,7 +2114,7 @@ describe('ReactDOMServerIntegration', () => {
       const Grandchild = (props, context) => {
         return <div>{context.text}</div>;
       };
-      Grandchild.contextTypes = {text: PropTypes.string};
+      Grandchild.contextTypes = { text: PropTypes.string };
 
       const e = await render(
         <PurpleContext><RedContext><Grandchild /></RedContext></PurpleContext>,
@@ -2054,23 +2125,23 @@ describe('ReactDOMServerIntegration', () => {
     itRenders('a child context merged with a parent context', async render => {
       class Parent extends React.Component {
         getChildContext() {
-          return {text1: 'purple'};
+          return { text1: 'purple' };
         }
         render() {
           return <Child />;
         }
       }
-      Parent.childContextTypes = {text1: PropTypes.string};
+      Parent.childContextTypes = { text1: PropTypes.string };
 
       class Child extends React.Component {
         getChildContext() {
-          return {text2: 'red'};
+          return { text2: 'red' };
         }
         render() {
           return <Grandchild />;
         }
       }
-      Child.childContextTypes = {text2: PropTypes.string};
+      Child.childContextTypes = { text2: PropTypes.string };
 
       const Grandchild = (props, context) => {
         return (
@@ -2095,21 +2166,21 @@ describe('ReactDOMServerIntegration', () => {
       async render => {
         class WillMountContext extends React.Component {
           getChildContext() {
-            return {text: this.state.text};
+            return { text: this.state.text };
           }
           componentWillMount() {
-            this.setState({text: 'foo'});
+            this.setState({ text: 'foo' });
           }
           render() {
             return <Child />;
           }
         }
-        WillMountContext.childContextTypes = {text: PropTypes.string};
+        WillMountContext.childContextTypes = { text: PropTypes.string };
 
         const Child = (props, context) => {
           return <div>{context.text}</div>;
         };
-        Child.contextTypes = {text: PropTypes.string};
+        Child.contextTypes = { text: PropTypes.string };
 
         const e = await render(<WillMountContext />);
         expect(e.textContent).toBe('foo');
@@ -2124,7 +2195,7 @@ describe('ReactDOMServerIntegration', () => {
             return <div />;
           }
           getChildContext() {
-            return {foo: 'bar'};
+            return { foo: 'bar' };
           }
         }
         return render(<Component />);
@@ -2139,16 +2210,16 @@ describe('ReactDOMServerIntegration', () => {
             return <div />;
           }
           getChildContext() {
-            return {value1: 'foo', value2: 'bar'};
+            return { value1: 'foo', value2: 'bar' };
           }
         }
-        Component.childContextTypes = {value1: PropTypes.string};
+        Component.childContextTypes = { value1: PropTypes.string };
         return render(<Component />);
       },
     );
   });
 
-  describe('refs', function() {
+  describe('refs', function () {
     it('should not run ref code on server', async () => {
       let refCount = 0;
       class RefsComponent extends React.Component {
@@ -2203,7 +2274,7 @@ describe('ReactDOMServerIntegration', () => {
     });
   });
 
-  describe('reconnecting to server markup', function() {
+  describe('reconnecting to server markup', function () {
     var EmptyComponent;
     beforeEach(() => {
       EmptyComponent = class extends React.Component {
@@ -2213,8 +2284,8 @@ describe('ReactDOMServerIntegration', () => {
       };
     });
 
-    describe('elements', function() {
-      describe('reconnecting different component implementations', function() {
+    describe('elements', function () {
+      describe('reconnecting different component implementations', function () {
         let ES6ClassComponent, PureComponent, bareElement;
         beforeEach(() => {
           // try each type of component on client and server.
@@ -2280,45 +2351,45 @@ describe('ReactDOMServerIntegration', () => {
         expectMarkupMismatch(<div id="foo" />, <div id="bar" />));
     });
 
-    describe('inline styles', function() {
+    describe('inline styles', function () {
       it('should error reconnecting missing style attribute', () =>
-        expectMarkupMismatch(<div style={{width: '1px'}} />, <div />));
+        expectMarkupMismatch(<div style={{ width: '1px' }} />, <div />));
 
       it('should error reconnecting added style attribute', () =>
-        expectMarkupMismatch(<div />, <div style={{width: '1px'}} />));
+        expectMarkupMismatch(<div />, <div style={{ width: '1px' }} />));
 
       it('should error reconnecting empty style attribute', () =>
         expectMarkupMismatch(
-          <div style={{width: '1px'}} />,
+          <div style={{ width: '1px' }} />,
           <div style={{}} />,
         ));
 
       it('should error reconnecting added style values', () =>
         expectMarkupMismatch(
           <div style={{}} />,
-          <div style={{width: '1px'}} />,
+          <div style={{ width: '1px' }} />,
         ));
 
       it('should error reconnecting different style values', () =>
         expectMarkupMismatch(
-          <div style={{width: '1px'}} />,
-          <div style={{width: '2px'}} />,
+          <div style={{ width: '1px' }} />,
+          <div style={{ width: '2px' }} />,
         ));
 
       it('should reconnect number and string versions of a number', () =>
         expectMarkupMatch(
-          <div style={{width: '1px', height: 2}} />,
-          <div style={{width: 1, height: '2px'}} />,
+          <div style={{ width: '1px', height: 2 }} />,
+          <div style={{ width: 1, height: '2px' }} />,
         ));
 
       it('should error reconnecting reordered style values', () =>
         expectMarkupMismatch(
-          <div style={{width: '1px', fontSize: '2px'}} />,
-          <div style={{fontSize: '2px', width: '1px'}} />,
+          <div style={{ width: '1px', fontSize: '2px' }} />,
+          <div style={{ fontSize: '2px', width: '1px' }} />,
         ));
     });
 
-    describe('text nodes', function() {
+    describe('text nodes', function () {
       it('should error reconnecting different text', () =>
         expectMarkupMismatch(<div>Text</div>, <div>Other Text</div>));
 
@@ -2338,7 +2409,7 @@ describe('ReactDOMServerIntegration', () => {
         ));
     });
 
-    describe('element trees and children', function() {
+    describe('element trees and children', function () {
       it('should error reconnecting missing children', () =>
         expectMarkupMismatch(<div><div /></div>, <div />));
 
@@ -2396,8 +2467,8 @@ describe('ReactDOMServerIntegration', () => {
     // Markup Mismatches: misc
     it('should error reconnecting a div with different dangerouslySetInnerHTML', () =>
       expectMarkupMismatch(
-        <div dangerouslySetInnerHTML={{__html: "<span id='child1'/>"}} />,
-        <div dangerouslySetInnerHTML={{__html: "<span id='child2'/>"}} />,
+        <div dangerouslySetInnerHTML={{ __html: "<span id='child1'/>" }} />,
+        <div dangerouslySetInnerHTML={{ __html: "<span id='child2'/>" }} />,
       ));
   });
 
@@ -2411,10 +2482,10 @@ describe('ReactDOMServerIntegration', () => {
       onAfterResetModules = () => {
         const DOMProperty = require('DOMProperty');
         DOMProperty.injection.injectDOMPropertyConfig({
-          isCustomAttribute: function(name) {
+          isCustomAttribute: function (name) {
             return name.indexOf('foo-') === 0;
           },
-          Properties: {foobar: null},
+          Properties: { foobar: null },
         });
       };
       resetModules();
diff --git a/src/renderers/shared/server/ReactPartialRenderer.js b/src/renderers/shared/server/ReactPartialRenderer.js
index 4e8f33ec6f32..1d49808286ec 100644
--- a/src/renderers/shared/server/ReactPartialRenderer.js
+++ b/src/renderers/shared/server/ReactPartialRenderer.js
@@ -413,8 +413,9 @@ function resolve(child, context) {
 
 class ReactDOMServerRenderer {
   constructor(element, makeStaticMarkup) {
+    var children = React.isValidElement(element) ? [element] : toArray(element);
     var topFrame = {
-      children: toArray(element),
+      children,
       childIndex: 0,
       context: emptyObject,
       footer: '',
@@ -482,8 +483,10 @@ class ReactDOMServerRenderer {
       if (child === null || child === false) {
         return '';
       } else {
-        var children = toArray(child);
-        if (children.length > 1) {
+        if (React.isValidElement(child)) {
+          return this.renderDOM(child, context);
+        } else {
+          var children = toArray(child);
           var frame = {
             children,
             childIndex: 0,
@@ -496,7 +499,6 @@ class ReactDOMServerRenderer {
           this.stack.push(frame);
           return '';
         }
-        return this.renderDOM(child, context);
       }
     }
   }

From 7f5c0a16dece5ef930df5ce7ef4ec96e81a68e95 Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Sat, 29 Jul 2017 19:02:47 +0800
Subject: [PATCH 13/19] prettier

---
 .../ReactDOMServerIntegration-test.js         | 248 +++++++++---------
 1 file changed, 124 insertions(+), 124 deletions(-)

diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 77c7b1302efe..88444ea274bc 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -152,7 +152,7 @@ const clientRenderOnServerString = async (element, errorCount = 0) => {
   return clientElement;
 };
 
-function BadMarkupExpected() { }
+function BadMarkupExpected() {}
 
 const clientRenderOnBadMarkup = async (element, errorCount = 0) => {
   // First we render the top of bad mark up.
@@ -235,8 +235,8 @@ function itThrows(desc, testFn) {
     return testFn()
       .then(() =>
         expect(false).toBe('The promise resolved and should not have.'),
-    )
-      .catch(() => { });
+      )
+      .catch(() => {});
   });
 }
 
@@ -334,7 +334,7 @@ describe('ReactDOMServerIntegration', () => {
     ExecutionEnvironment.canUseDOM = false;
   });
 
-  describe('basic rendering', function () {
+  describe('basic rendering', function() {
     beforeEach(() => {
       onAfterResetModules = () => {
         const ReactFeatureFlags = require('ReactFeatureFlags');
@@ -387,37 +387,37 @@ describe('ReactDOMServerIntegration', () => {
       });
 
       itRenders('a nested array children as a child', async render => {
-        let e = await render([[<div key={1}>text1</div>],<span key={2}>text2</span>]);
+        let e = await render([
+          [<div key={1}>text1</div>],
+          <span key={2}>text2</span>,
+        ]);
         let parent = e.parentNode;
         expect(parent.childNodes[0].tagName).toBe('DIV');
         expect(parent.childNodes[1].tagName).toBe('SPAN');
       });
 
-      itRenders(
-        'throws if a plain object is used as a child on SSR',
-        async () => {
-          var children = {
-            x: <span />,
-            y: <span />,
-            z: <span />,
-          };
-          var element = <div>{[children]}</div>;
-          var ex;
-          try {
-            ReactDOMServer.renderToString(element);
-          } catch (e) {
-            ex = e;
-          }
-          expect(ex).toBeDefined();
-          expect(normalizeCodeLocInfo(ex.message)).toBe(
-            'Objects are not valid as a React child (found: object with keys ' +
+      it('throws if a plain object is used as a child on SSR', async () => {
+        var children = {
+          x: <span />,
+          y: <span />,
+          z: <span />,
+        };
+        var element = <div>{[children]}</div>;
+        var ex;
+        try {
+          ReactDOMServer.renderToString(element);
+        } catch (e) {
+          ex = e;
+        }
+        expect(ex).toBeDefined();
+        expect(normalizeCodeLocInfo(ex.message)).toBe(
+          'Objects are not valid as a React child (found: object with keys ' +
             '{x, y, z}). If you meant to render a collection of children, use ' +
             'an array instead.' +
             // Fiber gives a slightly better stack with the nearest host components
             (ReactDOMFeatureFlags.useFiber ? '\n    in div (at **)' : ''),
-          );
-        },
-      );
+        );
+      });
 
       it('throws if a plain object even if it is in an owner on SSR', async () => {
         class Foo extends React.Component {
@@ -440,18 +440,18 @@ describe('ReactDOMServerIntegration', () => {
         expect(ex).toBeDefined();
         expect(normalizeCodeLocInfo(ex.message)).toBe(
           'Objects are not valid as a React child (found: object with keys ' +
-          '{a, b, c}). If you meant to render a collection of children, use ' +
-          'an array instead.\n' +
-          // Fiber gives a slightly better stack with the nearest host components
-          (ReactDOMFeatureFlags.useFiber ? '    in div (at **)\n' : '') +
-          '    in Foo (at **)',
+            '{a, b, c}). If you meant to render a collection of children, use ' +
+            'an array instead.\n' +
+            // Fiber gives a slightly better stack with the nearest host components
+            (ReactDOMFeatureFlags.useFiber ? '    in div (at **)\n' : '') +
+            '    in Foo (at **)',
         );
       });
     }
   });
 
-  describe('property to attribute mapping', function () {
-    describe('string properties', function () {
+  describe('property to attribute mapping', function() {
+    describe('string properties', function() {
       itRenders('simple numbers', async render => {
         const e = await render(<div width={30} />);
         expect(e.getAttribute('width')).toBe('30');
@@ -480,7 +480,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('boolean properties', function () {
+    describe('boolean properties', function() {
       itRenders('boolean prop with true value', async render => {
         const e = await render(<div hidden={true} />);
         expect(e.getAttribute('hidden')).toBe('');
@@ -518,7 +518,7 @@ describe('ReactDOMServerIntegration', () => {
 
       // this seems like it might mask programmer error, but it's existing behavior.
       itRenders('boolean prop with object value', async render => {
-        const e = await render(<div hidden={{ foo: 'bar' }} />);
+        const e = await render(<div hidden={{foo: 'bar'}} />);
         expect(e.getAttribute('hidden')).toBe('');
       });
 
@@ -540,7 +540,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('download property (combined boolean/string attribute)', function () {
+    describe('download property (combined boolean/string attribute)', function() {
       itRenders('download prop with true value', async render => {
         const e = await render(<a download={true} />);
         expect(e.getAttribute('download')).toBe('');
@@ -582,7 +582,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('className property', function () {
+    describe('className property', function() {
       itRenders('className prop with string value', async render => {
         const e = await render(<div className="myClassName" />);
         expect(e.getAttribute('class')).toBe('myClassName');
@@ -611,7 +611,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('htmlFor property', function () {
+    describe('htmlFor property', function() {
       itRenders('htmlFor with string value', async render => {
         const e = await render(<div htmlFor="myFor" />);
         expect(e.getAttribute('for')).toBe('myFor');
@@ -640,7 +640,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('numeric properties', function () {
+    describe('numeric properties', function() {
       itRenders(
         'positive numeric property with positive value',
         async render => {
@@ -663,7 +663,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('props with special meaning in React', function () {
+    describe('props with special meaning in React', function() {
       itRenders('no ref attribute', async render => {
         class RefComponent extends React.Component {
           render() {
@@ -686,15 +686,15 @@ describe('ReactDOMServerIntegration', () => {
 
       itRenders('no dangerouslySetInnerHTML attribute', async render => {
         const e = await render(
-          <div dangerouslySetInnerHTML={{ __html: '<foo />' }} />,
+          <div dangerouslySetInnerHTML={{__html: '<foo />'}} />,
         );
         expect(e.getAttribute('dangerouslySetInnerHTML')).toBe(null);
       });
     });
 
-    describe('inline styles', function () {
+    describe('inline styles', function() {
       itRenders('simple styles', async render => {
-        const e = await render(<div style={{ color: 'red', width: '30px' }} />);
+        const e = await render(<div style={{color: 'red', width: '30px'}} />);
         expect(e.style.color).toBe('red');
         expect(e.style.width).toBe('30px');
       });
@@ -717,7 +717,7 @@ describe('ReactDOMServerIntegration', () => {
       });
 
       itRenders('custom properties', async render => {
-        const e = await render(<div style={{ '--foo': 5 }} />);
+        const e = await render(<div style={{'--foo': 5}} />);
         // This seems like an odd way computed properties are exposed in jsdom.
         // In a real browser we'd read it with e.style.getPropertyValue('--foo')
         expect(e.style.Foo).toBe('5');
@@ -725,27 +725,27 @@ describe('ReactDOMServerIntegration', () => {
 
       itRenders('no undefined styles', async render => {
         const e = await render(
-          <div style={{ color: undefined, width: '30px' }} />,
+          <div style={{color: undefined, width: '30px'}} />,
         );
         expect(e.style.color).toBe('');
         expect(e.style.width).toBe('30px');
       });
 
       itRenders('no null styles', async render => {
-        const e = await render(<div style={{ color: null, width: '30px' }} />);
+        const e = await render(<div style={{color: null, width: '30px'}} />);
         expect(e.style.color).toBe('');
         expect(e.style.width).toBe('30px');
       });
 
       itRenders('no empty styles', async render => {
-        const e = await render(<div style={{ color: null, width: null }} />);
+        const e = await render(<div style={{color: null, width: null}} />);
         expect(e.style.color).toBe('');
         expect(e.style.width).toBe('');
         expect(e.hasAttribute('style')).toBe(false);
       });
     });
 
-    describe('aria attributes', function () {
+    describe('aria attributes', function() {
       itRenders('simple strings', async render => {
         const e = await render(<div aria-label="hello" />);
         expect(e.getAttribute('aria-label')).toBe('hello');
@@ -763,7 +763,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('unknown attributes', function () {
+    describe('unknown attributes', function() {
       itRenders('no unknown attributes', async render => {
         const e = await render(<div foo="bar" />, 1);
         expect(e.getAttribute('foo')).toBe(null);
@@ -818,14 +818,14 @@ describe('ReactDOMServerIntegration', () => {
     });
 
     itRenders('no HTML events', async render => {
-      const e = await render(<div onClick={() => { }} />);
+      const e = await render(<div onClick={() => {}} />);
       expect(e.getAttribute('onClick')).toBe(null);
       expect(e.getAttribute('onClick')).toBe(null);
       expect(e.getAttribute('click')).toBe(null);
     });
   });
 
-  describe('elements and children', function () {
+  describe('elements and children', function() {
     // helper functions.
     const TEXT_NODE_TYPE = 3;
     const COMMENT_NODE_TYPE = 8;
@@ -856,7 +856,7 @@ describe('ReactDOMServerIntegration', () => {
       expectNode(node, COMMENT_NODE_TYPE, / react-empty: [0-9]+ /);
     }
 
-    describe('text children', function () {
+    describe('text children', function() {
       itRenders('a div with text', async render => {
         const e = await render(<div>Text</div>);
         expect(e.tagName).toBe('DIV');
@@ -1038,7 +1038,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('number children', function () {
+    describe('number children', function() {
       itRenders('a number as single child', async render => {
         const e = await render(<div>{3}</div>);
         expect(e.textContent).toBe('3');
@@ -1078,7 +1078,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('null, false, and undefined children', function () {
+    describe('null, false, and undefined children', function() {
       itRenders('null single child as blank', async render => {
         const e = await render(<div>{null}</div>);
         expect(e.childNodes.length).toBe(0);
@@ -1153,7 +1153,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('elements with implicit namespaces', function () {
+    describe('elements with implicit namespaces', function() {
       itRenders('an svg element', async render => {
         const e = await render(<svg />);
         expect(e.childNodes.length).toBe(0);
@@ -1199,7 +1199,7 @@ describe('ReactDOMServerIntegration', () => {
 
     itRenders('a div with dangerouslySetInnerHTML', async render => {
       const e = await render(
-        <div dangerouslySetInnerHTML={{ __html: "<span id='child'/>" }} />,
+        <div dangerouslySetInnerHTML={{__html: "<span id='child'/>"}} />,
       );
       expect(e.childNodes.length).toBe(1);
       expect(e.firstChild.tagName).toBe('SPAN');
@@ -1207,7 +1207,7 @@ describe('ReactDOMServerIntegration', () => {
       expect(e.firstChild.childNodes.length).toBe(0);
     });
 
-    describe('newline-eating elements', function () {
+    describe('newline-eating elements', function() {
       itRenders(
         'a newline-eating tag with content not starting with \\n',
         async render => {
@@ -1228,7 +1228,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('different component implementations', function () {
+    describe('different component implementations', function() {
       function checkFooDiv(e) {
         expect(e.childNodes.length).toBe(1);
         expectNode(e.firstChild, TEXT_NODE_TYPE, 'foo');
@@ -1251,7 +1251,7 @@ describe('ReactDOMServerIntegration', () => {
       itRenders('factory components', async render => {
         const FactoryComponent = () => {
           return {
-            render: function () {
+            render: function() {
               return <div>foo</div>;
             },
           };
@@ -1260,7 +1260,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('component hierarchies', async function () {
+    describe('component hierarchies', async function() {
       itRenders('single child hierarchies of components', async render => {
         const Component = props => <div>{props.children}</div>;
         let e = await render(
@@ -1389,7 +1389,7 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('escaping >, <, and &', function () {
+    describe('escaping >, <, and &', function() {
       itRenders('>,<, and & as single child', async render => {
         const e = await render(<div>{'<span>Text&quot;</span>'}</div>);
         expect(e.childNodes.length).toBe(1);
@@ -1425,7 +1425,7 @@ describe('ReactDOMServerIntegration', () => {
       });
     });
 
-    describe('components that throw errors', function () {
+    describe('components that throw errors', function() {
       itThrowsWhenRendering('a string component', async render => {
         const StringComponent = () => 'foo';
         await render(<StringComponent />, 1);
@@ -1449,10 +1449,10 @@ describe('ReactDOMServerIntegration', () => {
     });
   });
 
-  describe('form controls', function () {
-    describe('inputs', function () {
+  describe('form controls', function() {
+    describe('inputs', function() {
       itRenders('an input with a value and an onChange', async render => {
-        const e = await render(<input value="foo" onChange={() => { }} />);
+        const e = await render(<input value="foo" onChange={() => {}} />);
         expect(e.value).toBe('foo');
       });
 
@@ -1503,10 +1503,10 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('checkboxes', function () {
+    describe('checkboxes', function() {
       itRenders('a checkbox that is checked with an onChange', async render => {
         const e = await render(
-          <input type="checkbox" checked={true} onChange={() => { }} />,
+          <input type="checkbox" checked={true} onChange={() => {}} />,
         );
         expect(e.checked).toBe(true);
       });
@@ -1569,11 +1569,11 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('textareas', function () {
+    describe('textareas', function() {
       // textareas
       // ---------
       itRenders('a textarea with a value and an onChange', async render => {
-        const e = await render(<textarea value="foo" onChange={() => { }} />);
+        const e = await render(<textarea value="foo" onChange={() => {}} />);
         // textarea DOM elements don't have a value **attribute**, the text is
         // a child of the element and accessible via the .value **property**.
         expect(e.getAttribute('value')).toBe(null);
@@ -1630,9 +1630,9 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('selects', function () {
+    describe('selects', function() {
       var options;
-      beforeEach(function () {
+      beforeEach(function() {
         options = [
           <option key={1} value="foo" id="foo">Foo</option>,
           <option key={2} value="bar" id="bar">Bar</option>,
@@ -1661,7 +1661,7 @@ describe('ReactDOMServerIntegration', () => {
 
       itRenders('a select with a value and an onChange', async render => {
         const e = await render(
-          <select value="bar" onChange={() => { }}>{options}</select>,
+          <select value="bar" onChange={() => {}}>{options}</select>,
         );
         expectSelectValue(e, 'bar');
       });
@@ -1677,7 +1677,7 @@ describe('ReactDOMServerIntegration', () => {
         'a select with a multiple values and an onChange',
         async render => {
           const e = await render(
-            <select value={['bar', 'baz']} multiple={true} onChange={() => { }}>
+            <select value={['bar', 'baz']} multiple={true} onChange={() => {}}>
               {options}
             </select>,
           );
@@ -1736,7 +1736,7 @@ describe('ReactDOMServerIntegration', () => {
       );
     });
 
-    describe('user interaction', function () {
+    describe('user interaction', function() {
       let ControlledInput,
         ControlledTextArea,
         ControlledCheckbox,
@@ -1745,13 +1745,13 @@ describe('ReactDOMServerIntegration', () => {
         ControlledInput = class extends React.Component {
           constructor() {
             super();
-            this.state = { value: 'Hello' };
+            this.state = {value: 'Hello'};
           }
           handleChange(event) {
             if (this.props.onChange) {
               this.props.onChange(event);
             }
-            this.setState({ value: event.target.value });
+            this.setState({value: event.target.value});
           }
           render() {
             return (
@@ -1765,13 +1765,13 @@ describe('ReactDOMServerIntegration', () => {
         ControlledTextArea = class extends React.Component {
           constructor() {
             super();
-            this.state = { value: 'Hello' };
+            this.state = {value: 'Hello'};
           }
           handleChange(event) {
             if (this.props.onChange) {
               this.props.onChange(event);
             }
-            this.setState({ value: event.target.value });
+            this.setState({value: event.target.value});
           }
           render() {
             return (
@@ -1785,13 +1785,13 @@ describe('ReactDOMServerIntegration', () => {
         ControlledCheckbox = class extends React.Component {
           constructor() {
             super();
-            this.state = { value: true };
+            this.state = {value: true};
           }
           handleChange(event) {
             if (this.props.onChange) {
               this.props.onChange(event);
             }
-            this.setState({ value: event.target.checked });
+            this.setState({value: event.target.checked});
           }
           render() {
             return (
@@ -1806,13 +1806,13 @@ describe('ReactDOMServerIntegration', () => {
         ControlledSelect = class extends React.Component {
           constructor() {
             super();
-            this.state = { value: 'Hello' };
+            this.state = {value: 'Hello'};
           }
           handleChange(event) {
             if (this.props.onChange) {
               this.props.onChange(event);
             }
-            this.setState({ value: event.target.value });
+            this.setState({value: event.target.value});
           }
           render() {
             return (
@@ -1827,7 +1827,7 @@ describe('ReactDOMServerIntegration', () => {
         };
       });
 
-      describe('user interaction with controlled inputs', function () {
+      describe('user interaction with controlled inputs', function() {
         itClientRenders('a controlled text input', async render => {
           let changeCount = 0;
           const e = await render(
@@ -1893,7 +1893,7 @@ describe('ReactDOMServerIntegration', () => {
         });
       });
 
-      describe('user interaction with inputs before client render', function () {
+      describe('user interaction with inputs before client render', function() {
         // renders the element and changes the value **before** the client
         // code has a chance to render; this simulates what happens when a
         // user starts to interact with a server-rendered form before
@@ -1999,18 +1999,18 @@ describe('ReactDOMServerIntegration', () => {
     });
   });
 
-  describe('context', function () {
+  describe('context', function() {
     let PurpleContext, RedContext;
     beforeEach(() => {
       class Parent extends React.Component {
         getChildContext() {
-          return { text: this.props.text };
+          return {text: this.props.text};
         }
         render() {
           return this.props.children;
         }
       }
-      Parent.childContextTypes = { text: PropTypes.string };
+      Parent.childContextTypes = {text: PropTypes.string};
 
       PurpleContext = props => <Parent text="purple">{props.children}</Parent>;
       RedContext = props => <Parent text="red">{props.children}</Parent>;
@@ -2022,7 +2022,7 @@ describe('ReactDOMServerIntegration', () => {
           return <div>{this.context.text}</div>;
         }
       }
-      ClassChildWithContext.contextTypes = { text: PropTypes.string };
+      ClassChildWithContext.contextTypes = {text: PropTypes.string};
 
       const e = await render(
         <PurpleContext><ClassChildWithContext /></PurpleContext>,
@@ -2034,7 +2034,7 @@ describe('ReactDOMServerIntegration', () => {
       function StatelessChildWithContext(props, context) {
         return <div>{context.text}</div>;
       }
-      StatelessChildWithContext.contextTypes = { text: PropTypes.string };
+      StatelessChildWithContext.contextTypes = {text: PropTypes.string};
 
       const e = await render(
         <PurpleContext><StatelessChildWithContext /></PurpleContext>,
@@ -2075,7 +2075,7 @@ describe('ReactDOMServerIntegration', () => {
           return <div id="classWrongChild">{this.context.text}</div>;
         }
       }
-      ClassChildWithWrongContext.contextTypes = { foo: PropTypes.string };
+      ClassChildWithWrongContext.contextTypes = {foo: PropTypes.string};
 
       const e = await render(
         <PurpleContext><ClassChildWithWrongContext /></PurpleContext>,
@@ -2102,7 +2102,7 @@ describe('ReactDOMServerIntegration', () => {
       function Grandchild(props, context) {
         return <div>{context.text}</div>;
       }
-      Grandchild.contextTypes = { text: PropTypes.string };
+      Grandchild.contextTypes = {text: PropTypes.string};
 
       const Child = props => <Grandchild />;
 
@@ -2114,7 +2114,7 @@ describe('ReactDOMServerIntegration', () => {
       const Grandchild = (props, context) => {
         return <div>{context.text}</div>;
       };
-      Grandchild.contextTypes = { text: PropTypes.string };
+      Grandchild.contextTypes = {text: PropTypes.string};
 
       const e = await render(
         <PurpleContext><RedContext><Grandchild /></RedContext></PurpleContext>,
@@ -2125,23 +2125,23 @@ describe('ReactDOMServerIntegration', () => {
     itRenders('a child context merged with a parent context', async render => {
       class Parent extends React.Component {
         getChildContext() {
-          return { text1: 'purple' };
+          return {text1: 'purple'};
         }
         render() {
           return <Child />;
         }
       }
-      Parent.childContextTypes = { text1: PropTypes.string };
+      Parent.childContextTypes = {text1: PropTypes.string};
 
       class Child extends React.Component {
         getChildContext() {
-          return { text2: 'red' };
+          return {text2: 'red'};
         }
         render() {
           return <Grandchild />;
         }
       }
-      Child.childContextTypes = { text2: PropTypes.string };
+      Child.childContextTypes = {text2: PropTypes.string};
 
       const Grandchild = (props, context) => {
         return (
@@ -2166,21 +2166,21 @@ describe('ReactDOMServerIntegration', () => {
       async render => {
         class WillMountContext extends React.Component {
           getChildContext() {
-            return { text: this.state.text };
+            return {text: this.state.text};
           }
           componentWillMount() {
-            this.setState({ text: 'foo' });
+            this.setState({text: 'foo'});
           }
           render() {
             return <Child />;
           }
         }
-        WillMountContext.childContextTypes = { text: PropTypes.string };
+        WillMountContext.childContextTypes = {text: PropTypes.string};
 
         const Child = (props, context) => {
           return <div>{context.text}</div>;
         };
-        Child.contextTypes = { text: PropTypes.string };
+        Child.contextTypes = {text: PropTypes.string};
 
         const e = await render(<WillMountContext />);
         expect(e.textContent).toBe('foo');
@@ -2195,7 +2195,7 @@ describe('ReactDOMServerIntegration', () => {
             return <div />;
           }
           getChildContext() {
-            return { foo: 'bar' };
+            return {foo: 'bar'};
           }
         }
         return render(<Component />);
@@ -2210,16 +2210,16 @@ describe('ReactDOMServerIntegration', () => {
             return <div />;
           }
           getChildContext() {
-            return { value1: 'foo', value2: 'bar' };
+            return {value1: 'foo', value2: 'bar'};
           }
         }
-        Component.childContextTypes = { value1: PropTypes.string };
+        Component.childContextTypes = {value1: PropTypes.string};
         return render(<Component />);
       },
     );
   });
 
-  describe('refs', function () {
+  describe('refs', function() {
     it('should not run ref code on server', async () => {
       let refCount = 0;
       class RefsComponent extends React.Component {
@@ -2274,7 +2274,7 @@ describe('ReactDOMServerIntegration', () => {
     });
   });
 
-  describe('reconnecting to server markup', function () {
+  describe('reconnecting to server markup', function() {
     var EmptyComponent;
     beforeEach(() => {
       EmptyComponent = class extends React.Component {
@@ -2284,8 +2284,8 @@ describe('ReactDOMServerIntegration', () => {
       };
     });
 
-    describe('elements', function () {
-      describe('reconnecting different component implementations', function () {
+    describe('elements', function() {
+      describe('reconnecting different component implementations', function() {
         let ES6ClassComponent, PureComponent, bareElement;
         beforeEach(() => {
           // try each type of component on client and server.
@@ -2351,45 +2351,45 @@ describe('ReactDOMServerIntegration', () => {
         expectMarkupMismatch(<div id="foo" />, <div id="bar" />));
     });
 
-    describe('inline styles', function () {
+    describe('inline styles', function() {
       it('should error reconnecting missing style attribute', () =>
-        expectMarkupMismatch(<div style={{ width: '1px' }} />, <div />));
+        expectMarkupMismatch(<div style={{width: '1px'}} />, <div />));
 
       it('should error reconnecting added style attribute', () =>
-        expectMarkupMismatch(<div />, <div style={{ width: '1px' }} />));
+        expectMarkupMismatch(<div />, <div style={{width: '1px'}} />));
 
       it('should error reconnecting empty style attribute', () =>
         expectMarkupMismatch(
-          <div style={{ width: '1px' }} />,
+          <div style={{width: '1px'}} />,
           <div style={{}} />,
         ));
 
       it('should error reconnecting added style values', () =>
         expectMarkupMismatch(
           <div style={{}} />,
-          <div style={{ width: '1px' }} />,
+          <div style={{width: '1px'}} />,
         ));
 
       it('should error reconnecting different style values', () =>
         expectMarkupMismatch(
-          <div style={{ width: '1px' }} />,
-          <div style={{ width: '2px' }} />,
+          <div style={{width: '1px'}} />,
+          <div style={{width: '2px'}} />,
         ));
 
       it('should reconnect number and string versions of a number', () =>
         expectMarkupMatch(
-          <div style={{ width: '1px', height: 2 }} />,
-          <div style={{ width: 1, height: '2px' }} />,
+          <div style={{width: '1px', height: 2}} />,
+          <div style={{width: 1, height: '2px'}} />,
         ));
 
       it('should error reconnecting reordered style values', () =>
         expectMarkupMismatch(
-          <div style={{ width: '1px', fontSize: '2px' }} />,
-          <div style={{ fontSize: '2px', width: '1px' }} />,
+          <div style={{width: '1px', fontSize: '2px'}} />,
+          <div style={{fontSize: '2px', width: '1px'}} />,
         ));
     });
 
-    describe('text nodes', function () {
+    describe('text nodes', function() {
       it('should error reconnecting different text', () =>
         expectMarkupMismatch(<div>Text</div>, <div>Other Text</div>));
 
@@ -2409,7 +2409,7 @@ describe('ReactDOMServerIntegration', () => {
         ));
     });
 
-    describe('element trees and children', function () {
+    describe('element trees and children', function() {
       it('should error reconnecting missing children', () =>
         expectMarkupMismatch(<div><div /></div>, <div />));
 
@@ -2467,8 +2467,8 @@ describe('ReactDOMServerIntegration', () => {
     // Markup Mismatches: misc
     it('should error reconnecting a div with different dangerouslySetInnerHTML', () =>
       expectMarkupMismatch(
-        <div dangerouslySetInnerHTML={{ __html: "<span id='child1'/>" }} />,
-        <div dangerouslySetInnerHTML={{ __html: "<span id='child2'/>" }} />,
+        <div dangerouslySetInnerHTML={{__html: "<span id='child1'/>"}} />,
+        <div dangerouslySetInnerHTML={{__html: "<span id='child2'/>"}} />,
       ));
   });
 
@@ -2482,10 +2482,10 @@ describe('ReactDOMServerIntegration', () => {
       onAfterResetModules = () => {
         const DOMProperty = require('DOMProperty');
         DOMProperty.injection.injectDOMPropertyConfig({
-          isCustomAttribute: function (name) {
+          isCustomAttribute: function(name) {
             return name.indexOf('foo-') === 0;
           },
-          Properties: { foobar: null },
+          Properties: {foobar: null},
         });
       };
       resetModules();

From 60e7e3e2d3b8d8ce9155dbc59384101d86a7b410 Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Sat, 29 Jul 2017 19:44:42 +0800
Subject: [PATCH 14/19] Change integeration test description

---
 .../dom/shared/__tests__/ReactDOMServerIntegration-test.js    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 88444ea274bc..14eb538d575a 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -396,7 +396,7 @@ describe('ReactDOMServerIntegration', () => {
         expect(parent.childNodes[1].tagName).toBe('SPAN');
       });
 
-      it('throws if a plain object is used as a child on SSR', async () => {
+      it('throws if a plain object is used as a child when using SSR', async () => {
         var children = {
           x: <span />,
           y: <span />,
@@ -419,7 +419,7 @@ describe('ReactDOMServerIntegration', () => {
         );
       });
 
-      it('throws if a plain object even if it is in an owner on SSR', async () => {
+      it('throws if a plain object even if it is in an owner when using SSR', async () => {
         class Foo extends React.Component {
           render() {
             var children = {

From e4dd558fd1e6a538bf4a6382667d98d223d1ab5e Mon Sep 17 00:00:00 2001
From: guoyong <guoyong@kuniyuus-MacBook-Pro.local>
Date: Mon, 31 Jul 2017 10:57:14 +0800
Subject: [PATCH 15/19] 	modified:  
 src/renderers/dom/ReactDOMNodeStreamRenderer.js

---
 .../__tests__/ReactComponent-test.js          | 54 ++++++++++++++++
 .../dom/ReactDOMNodeStreamRenderer.js         | 12 ++--
 .../ReactDOMServerIntegration-test.js         | 61 ++-----------------
 3 files changed, 68 insertions(+), 59 deletions(-)

diff --git a/src/renderers/__tests__/ReactComponent-test.js b/src/renderers/__tests__/ReactComponent-test.js
index 30503913da05..dbea2bd3dcd5 100644
--- a/src/renderers/__tests__/ReactComponent-test.js
+++ b/src/renderers/__tests__/ReactComponent-test.js
@@ -13,6 +13,7 @@
 
 var React;
 var ReactDOM;
+var ReactDOMServer;
 var ReactDOMFeatureFlags;
 var ReactTestUtils;
 
@@ -24,6 +25,7 @@ describe('ReactComponent', () => {
   beforeEach(() => {
     React = require('react');
     ReactDOM = require('react-dom');
+    ReactDOMServer = require('react-dom/server');
     ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
     ReactTestUtils = require('react-dom/test-utils');
   });
@@ -439,4 +441,56 @@ describe('ReactComponent', () => {
         '    in Foo (at **)',
     );
   });
+
+  it('throws if a plain object is used as a child when using SSR', async () => {
+    var children = {
+      x: <span />,
+      y: <span />,
+      z: <span />,
+    };
+    var element = <div>{[children]}</div>;
+    var ex;
+    try {
+      ReactDOMServer.renderToString(element);
+    } catch (e) {
+      ex = e;
+    }
+    expect(ex).toBeDefined();
+    expect(normalizeCodeLocInfo(ex.message)).toBe(
+      'Objects are not valid as a React child (found: object with keys ' +
+        '{x, y, z}). If you meant to render a collection of children, use ' +
+        'an array instead.' +
+        // Fiber gives a slightly better stack with the nearest host components
+        (ReactDOMFeatureFlags.useFiber ? '\n    in div (at **)' : ''),
+    );
+  });
+
+  it('throws if a plain object even if it is in an owner when using SSR', async () => {
+    class Foo extends React.Component {
+      render() {
+        var children = {
+          a: <span />,
+          b: <span />,
+          c: <span />,
+        };
+        return <div>{[children]}</div>;
+      }
+    }
+    var container = document.createElement('div');
+    var ex;
+    try {
+      ReactDOMServer.renderToString(<Foo />, container);
+    } catch (e) {
+      ex = e;
+    }
+    expect(ex).toBeDefined();
+    expect(normalizeCodeLocInfo(ex.message)).toBe(
+      'Objects are not valid as a React child (found: object with keys ' +
+        '{a, b, c}). If you meant to render a collection of children, use ' +
+        'an array instead.\n' +
+        // Fiber gives a slightly better stack with the nearest host components
+        (ReactDOMFeatureFlags.useFiber ? '    in div (at **)\n' : '') +
+        '    in Foo (at **)',
+    );
+  });
 });
diff --git a/src/renderers/dom/ReactDOMNodeStreamRenderer.js b/src/renderers/dom/ReactDOMNodeStreamRenderer.js
index 689d339d81ef..866b16a24ad2 100644
--- a/src/renderers/dom/ReactDOMNodeStreamRenderer.js
+++ b/src/renderers/dom/ReactDOMNodeStreamRenderer.js
@@ -14,6 +14,7 @@
 var invariant = require('fbjs/lib/invariant');
 var React = require('react');
 var ReactPartialRenderer = require('ReactPartialRenderer');
+var ReactFeatureFlags = require('ReactFeatureFlags');
 
 var Readable = require('stream').Readable;
 
@@ -49,10 +50,13 @@ function renderToStream(element) {
  * See https://facebook.github.io/react/docs/react-dom-stream.html#rendertostaticstream
  */
 function renderToStaticStream(element) {
-  invariant(
-    React.isValidElement(element),
-    'renderToStaticStream(): You must pass a valid ReactElement.',
-  );
+  const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
+  if (disableNewFiberFeatures) {
+    invariant(
+      React.isValidElement(element),
+      'renderToString(): You must pass a valid ReactElement.',
+    );
+  }
   return new ReactMarkupReadableStream(element, true);
 }
 
diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 14eb538d575a..8edfeeeecddf 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -25,9 +25,6 @@ const stream = require('stream');
 
 // Helper functions for rendering tests
 // ====================================
-function normalizeCodeLocInfo(str) {
-  return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
-}
 
 // promisified version of ReactDOM.render()
 function asyncReactDOMRender(reactElement, domElement) {
@@ -369,15 +366,21 @@ describe('ReactDOMServerIntegration', () => {
         let Header = props => {
           return <p>header</p>;
         };
+        let Footer = props => {
+          return [<h2 key={1}>footer</h2>, <h3 key={2}>about</h3>];
+        };
         let e = await render([
           <div key={1}>text1</div>,
           <span key={2}>text2</span>,
           <Header key={3} />,
+          <Footer key={4} />,
         ]);
         let parent = e.parentNode;
         expect(parent.childNodes[0].tagName).toBe('DIV');
         expect(parent.childNodes[1].tagName).toBe('SPAN');
         expect(parent.childNodes[2].tagName).toBe('P');
+        expect(parent.childNodes[3].tagName).toBe('H2');
+        expect(parent.childNodes[4].tagName).toBe('H3');
       });
 
       itRenders('a single array element children as a child', async render => {
@@ -395,58 +398,6 @@ describe('ReactDOMServerIntegration', () => {
         expect(parent.childNodes[0].tagName).toBe('DIV');
         expect(parent.childNodes[1].tagName).toBe('SPAN');
       });
-
-      it('throws if a plain object is used as a child when using SSR', async () => {
-        var children = {
-          x: <span />,
-          y: <span />,
-          z: <span />,
-        };
-        var element = <div>{[children]}</div>;
-        var ex;
-        try {
-          ReactDOMServer.renderToString(element);
-        } catch (e) {
-          ex = e;
-        }
-        expect(ex).toBeDefined();
-        expect(normalizeCodeLocInfo(ex.message)).toBe(
-          'Objects are not valid as a React child (found: object with keys ' +
-            '{x, y, z}). If you meant to render a collection of children, use ' +
-            'an array instead.' +
-            // Fiber gives a slightly better stack with the nearest host components
-            (ReactDOMFeatureFlags.useFiber ? '\n    in div (at **)' : ''),
-        );
-      });
-
-      it('throws if a plain object even if it is in an owner when using SSR', async () => {
-        class Foo extends React.Component {
-          render() {
-            var children = {
-              a: <span />,
-              b: <span />,
-              c: <span />,
-            };
-            return <div>{[children]}</div>;
-          }
-        }
-        var container = document.createElement('div');
-        var ex;
-        try {
-          ReactDOMServer.renderToString(<Foo />, container);
-        } catch (e) {
-          ex = e;
-        }
-        expect(ex).toBeDefined();
-        expect(normalizeCodeLocInfo(ex.message)).toBe(
-          'Objects are not valid as a React child (found: object with keys ' +
-            '{a, b, c}). If you meant to render a collection of children, use ' +
-            'an array instead.\n' +
-            // Fiber gives a slightly better stack with the nearest host components
-            (ReactDOMFeatureFlags.useFiber ? '    in div (at **)\n' : '') +
-            '    in Foo (at **)',
-        );
-      });
     }
   });
 

From 59e08434ae5c14f91231815acdc2c7c2a74995dc Mon Sep 17 00:00:00 2001
From: Dan Abramov <gaearon@fb.com>
Date: Mon, 31 Jul 2017 15:15:18 +0100
Subject: [PATCH 16/19] Make invariants consistent

---
 src/renderers/dom/ReactDOMNodeStreamRenderer.js | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/renderers/dom/ReactDOMNodeStreamRenderer.js b/src/renderers/dom/ReactDOMNodeStreamRenderer.js
index 866b16a24ad2..c5379be7290b 100644
--- a/src/renderers/dom/ReactDOMNodeStreamRenderer.js
+++ b/src/renderers/dom/ReactDOMNodeStreamRenderer.js
@@ -41,6 +41,13 @@ class ReactMarkupReadableStream extends Readable {
  * See https://facebook.github.io/react/docs/react-dom-stream.html#rendertostream
  */
 function renderToStream(element) {
+  const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
+  if (disableNewFiberFeatures) {
+    invariant(
+      React.isValidElement(element),
+      'renderToStream(): You must pass a valid ReactElement.',
+    );
+  }
   return new ReactMarkupReadableStream(element, false);
 }
 
@@ -54,7 +61,7 @@ function renderToStaticStream(element) {
   if (disableNewFiberFeatures) {
     invariant(
       React.isValidElement(element),
-      'renderToString(): You must pass a valid ReactElement.',
+      'renderToStaticStream(): You must pass a valid ReactElement.',
     );
   }
   return new ReactMarkupReadableStream(element, true);

From 5539f099d0674823427bdc04ffdd3b596485bc17 Mon Sep 17 00:00:00 2001
From: Dan Abramov <gaearon@fb.com>
Date: Mon, 31 Jul 2017 15:26:53 +0100
Subject: [PATCH 17/19] Gate outdated test itself by feature flag

---
 .../__tests__/ReactServerRendering-test.js    | 21 ++++++++++---------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
index 9caf9196b23d..b75975f26455 100644
--- a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
@@ -319,11 +319,13 @@ describe('ReactDOMServer', () => {
       expect(numClicks).toEqual(2);
     });
 
-    it('should throw with silly args', () => {
-      expect(
-        ReactDOMServer.renderToString.bind(ReactDOMServer, 'not a component'),
-      ).toThrowError('renderToString(): You must pass a valid ReactElement.');
-    });
+    if (ReactFeatureFlags.disableNewFiberFeatures) {
+      it('should throw with silly args', () => {
+        expect(
+          ReactDOMServer.renderToString.bind(ReactDOMServer, 'not a component'),
+        ).toThrowError('renderToString(): You must pass a valid ReactElement.');
+      });
+    }
   });
 
   describe('renderToStaticMarkup', () => {
@@ -431,9 +433,8 @@ describe('ReactDOMServer', () => {
       runTest();
     });
 
-    it('should throw with silly args', () => {
-      const disableNewFiberFeatures = ReactFeatureFlags.disableNewFiberFeatures;
-      if (disableNewFiberFeatures) {
+    if (ReactFeatureFlags.disableNewFiberFeatures) {
+      it('should throw with silly args', () => {
         expect(
           ReactDOMServer.renderToStaticMarkup.bind(
             ReactDOMServer,
@@ -442,8 +443,8 @@ describe('ReactDOMServer', () => {
         ).toThrowError(
           'renderToStaticMarkup(): You must pass a valid ReactElement.',
         );
-      }
-    });
+      });
+    }
 
     it('allows setState in componentWillMount without using DOM', () => {
       class Component extends React.Component {

From 06d9971b7fbd9cce71d6c73416f3806eef42478d Mon Sep 17 00:00:00 2001
From: Dan Abramov <gaearon@fb.com>
Date: Mon, 31 Jul 2017 15:32:10 +0100
Subject: [PATCH 18/19] Change test to make sense both with old and new code

---
 .../__tests__/ReactServerRendering-test.js    | 39 ++++++++++---------
 1 file changed, 20 insertions(+), 19 deletions(-)

diff --git a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
index b75975f26455..0658255d6748 100644
--- a/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
@@ -35,7 +35,9 @@ describe('ReactDOMServer', () => {
     ReactMarkupChecksum = require('ReactMarkupChecksum');
     ReactReconcileTransaction = require('ReactReconcileTransaction');
     PropTypes = require('prop-types');
+
     ReactFeatureFlags = require('ReactFeatureFlags');
+    ReactFeatureFlags.disableNewFiberFeatures = false;
 
     ExecutionEnvironment = require('fbjs/lib/ExecutionEnvironment');
     ExecutionEnvironment.canUseDOM = false;
@@ -319,13 +321,15 @@ describe('ReactDOMServer', () => {
       expect(numClicks).toEqual(2);
     });
 
-    if (ReactFeatureFlags.disableNewFiberFeatures) {
-      it('should throw with silly args', () => {
-        expect(
-          ReactDOMServer.renderToString.bind(ReactDOMServer, 'not a component'),
-        ).toThrowError('renderToString(): You must pass a valid ReactElement.');
-      });
-    }
+    it('should throw with silly args', () => {
+      expect(
+        ReactDOMServer.renderToString.bind(ReactDOMServer, {x: 123}),
+      ).toThrowError(
+        ReactDOMFeatureFlags.useFiber
+          ? 'Objects are not valid as a React child (found: object with keys {x})'
+          : 'renderToString(): You must pass a valid ReactElement.',
+      );
+    });
   });
 
   describe('renderToStaticMarkup', () => {
@@ -433,18 +437,15 @@ describe('ReactDOMServer', () => {
       runTest();
     });
 
-    if (ReactFeatureFlags.disableNewFiberFeatures) {
-      it('should throw with silly args', () => {
-        expect(
-          ReactDOMServer.renderToStaticMarkup.bind(
-            ReactDOMServer,
-            'not a component',
-          ),
-        ).toThrowError(
-          'renderToStaticMarkup(): You must pass a valid ReactElement.',
-        );
-      });
-    }
+    it('should throw with silly args', () => {
+      expect(
+        ReactDOMServer.renderToStaticMarkup.bind(ReactDOMServer, {x: 123}),
+      ).toThrowError(
+        ReactDOMFeatureFlags.useFiber
+          ? 'Objects are not valid as a React child (found: object with keys {x})'
+          : 'renderToStaticMarkup(): You must pass a valid ReactElement.',
+      );
+    });
 
     it('allows setState in componentWillMount without using DOM', () => {
       class Component extends React.Component {

From b42dd082a051685b0970532354b05948969c34e8 Mon Sep 17 00:00:00 2001
From: Dan Abramov <gaearon@fb.com>
Date: Mon, 31 Jul 2017 15:38:24 +0100
Subject: [PATCH 19/19] Test more cases

---
 .../ReactDOMServerIntegration-test.js         | 27 ++++++++++++++++++-
 1 file changed, 26 insertions(+), 1 deletion(-)

diff --git a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
index 8edfeeeecddf..d7f8fe3ee203 100644
--- a/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+++ b/src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
@@ -392,11 +392,36 @@ describe('ReactDOMServerIntegration', () => {
       itRenders('a nested array children as a child', async render => {
         let e = await render([
           [<div key={1}>text1</div>],
-          <span key={2}>text2</span>,
+          <span key={1}>text2</span>,
+          [[[null, <p key={1} />], false]],
         ]);
         let parent = e.parentNode;
         expect(parent.childNodes[0].tagName).toBe('DIV');
         expect(parent.childNodes[1].tagName).toBe('SPAN');
+        expect(parent.childNodes[2].tagName).toBe('P');
+      });
+
+      itRenders('an iterable as a child', async render => {
+        const threeDivIterable = {
+          '@@iterator': function() {
+            var i = 0;
+            return {
+              next: function() {
+                if (i++ < 3) {
+                  return {value: <div key={i} />, done: false};
+                } else {
+                  return {value: undefined, done: true};
+                }
+              },
+            };
+          },
+        };
+        let e = await render(threeDivIterable);
+        let parent = e.parentNode;
+        expect(parent.childNodes.length).toBe(3);
+        expect(parent.childNodes[0].tagName).toBe('DIV');
+        expect(parent.childNodes[1].tagName).toBe('DIV');
+        expect(parent.childNodes[2].tagName).toBe('DIV');
       });
     }
   });
