@samitouri / QOS-React-2 / commits / 25deff6203

Add more SSR unit tests for elements and children. (#9221)

* Adding more SSR unit tests for elements and children. * Some of my SSR tests were testing for react-text and react-empty elements that no longer exist in Fiber. Fixed the tests so that they expect correct markup in Fiber. * Tweaked some test names after @gaearon review comment https://github.com/facebook/react/pull/9221#discussion_r107045673 . Also realized that one of the tests was essentially a direct copy of another, so deleted it. * Responding to code review https://github.com/facebook/react/pull/9221#pullrequestreview-28996315 . Thanks @spicyj!

Sasha Aickin committed Mar 24, 2017 at 13:31 UTC 25deff6203d4cce3e8a9e557c84590052455e22d
4 files changed +778
scripts/fiber/tests-failing.txt
+56
@@ -46,6 +46,62 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
46 * renders unknown attributes for custom elements with client render on top of good server markup
47 * renders unknown attributes for custom elements using is with client render on top of good server markup
48 * renders no HTML events with client render on top of good server markup
49 +* renders a div with text with client render on top of good server markup
50 +* renders a div with text with flanking whitespace with client render on top of good server markup
51 +* renders a div with an empty text child with client render on top of good server markup
52 +* renders a div with multiple empty text children with server string render
53 +* renders a div with multiple empty text children with client render on top of good server markup
54 +* renders a div with multiple whitespace children with server string render
55 +* renders a div with multiple whitespace children with client render on top of good server markup
56 +* renders a div with text sibling to a node with server string render
57 +* renders a div with text sibling to a node with client render on top of good server markup
58 +* renders a non-standard element with text with client render on top of good server markup
59 +* renders a custom element with text with client render on top of good server markup
60 +* renders a leading blank child with a text sibling with server string render
61 +* renders a leading blank child with a text sibling with client render on top of good server markup
62 +* renders a trailing blank child with a text sibling with server string render
63 +* renders a trailing blank child with a text sibling with client render on top of good server markup
64 +* renders an element with two text children with server string render
65 +* renders an element with two text children with client render on top of good server markup
66 +* renders a number as single child with client render on top of good server markup
67 +* renders zero as single child with client render on top of good server markup
68 +* renders an element with number and text children with server string render
69 +* renders an element with number and text children with client render on top of good server markup
70 +* renders null single child as blank with client render on top of good server markup
71 +* renders false single child as blank with client render on top of good server markup
72 +* renders undefined single child as blank with client render on top of good server markup
73 +* renders a null component children as empty with server string render
74 +* renders a null component children as empty with client render on top of good server markup
75 +* renders null children as blank with server string render
76 +* renders null children as blank with client render on top of good server markup
77 +* renders false children as blank with server string render
78 +* renders false children as blank with client render on top of good server markup
79 +* renders null and false children together as blank with server string render
80 +* renders null and false children together as blank with client render on top of good server markup
81 +* renders only null and false children as blank with client render on top of good server markup
82 +* renders an svg element with client render on top of good server markup
83 +* renders svg element with an xlink with client render on top of good server markup
84 +* renders a math element with client render on top of good server markup
85 +* renders an img with client render on top of good server markup
86 +* renders a button with client render on top of good server markup
87 +* renders a div with dangerouslySetInnerHTML with client render on top of good server markup
88 +* renders a newline-eating tag with content not starting with \n with client render on top of good server markup
89 +* renders a newline-eating tag with content starting with \n with client render on top of good server markup
90 +* renders a normal tag with content starting with \n with client render on top of good server markup
91 +* renders stateless components with client render on top of good server markup
92 +* renders ES6 class components with client render on top of good server markup
93 +* renders factory components with client render on top of good server markup
94 +* renders single child hierarchies of components with client render on top of good server markup
95 +* renders multi-child hierarchies of components with client render on top of good server markup
96 +* renders a div with a child with client render on top of good server markup
97 +* renders a div with multiple children with client render on top of good server markup
98 +* renders a div with multiple children separated by whitespace with server string render
99 +* renders a div with multiple children separated by whitespace with client render on top of good server markup
100 +* renders a div with a single child surrounded by whitespace with server string render
101 +* renders a div with a single child surrounded by whitespace with client render on top of good server markup
102 +* renders >,<, and & as single child with client render on top of good server markup
103 +* renders >,<, and & as multiple children with server string render
104 +* renders >,<, and & as multiple children with client render on top of good server markup
105
106 src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
107 * can reconcile text merged by Node.normalize() alongside other elements
scripts/fiber/tests-passing-except-dev.txt
+48
@@ -47,6 +47,54 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
47 * renders unknown attributes for custom elements with client render on top of bad server markup
48 * renders unknown attributes for custom elements using is with client render on top of bad server markup
49 * renders no HTML events with client render on top of bad server markup
50 +* renders a div with text with client render on top of bad server markup
51 +* renders a div with text with flanking whitespace with client render on top of bad server markup
52 +* renders a div with an empty text child with client render on top of bad server markup
53 +* renders a div with multiple empty text children with client render on top of bad server markup
54 +* renders a div with multiple whitespace children with client render on top of bad server markup
55 +* renders a div with text sibling to a node with client render on top of bad server markup
56 +* renders a non-standard element with text with client render on top of bad server markup
57 +* renders a custom element with text with client render on top of bad server markup
58 +* renders a leading blank child with a text sibling with client render on top of bad server markup
59 +* renders a trailing blank child with a text sibling with client render on top of bad server markup
60 +* renders an element with two text children with client render on top of bad server markup
61 +* renders a number as single child with client render on top of bad server markup
62 +* renders zero as single child with client render on top of bad server markup
63 +* renders an element with number and text children with client render on top of bad server markup
64 +* renders null single child as blank with client render on top of bad server markup
65 +* renders false single child as blank with client render on top of bad server markup
66 +* renders undefined single child as blank with client render on top of bad server markup
67 +* renders a null component children as empty with client render on top of bad server markup
68 +* renders null children as blank with client render on top of bad server markup
69 +* renders false children as blank with client render on top of bad server markup
70 +* renders null and false children together as blank with client render on top of bad server markup
71 +* renders only null and false children as blank with client render on top of bad server markup
72 +* renders an svg element with client render on top of bad server markup
73 +* renders svg element with an xlink with client render on top of bad server markup
74 +* renders a math element with client render on top of bad server markup
75 +* renders an img with client render on top of bad server markup
76 +* renders a button with client render on top of bad server markup
77 +* renders a div with dangerouslySetInnerHTML with client render on top of bad server markup
78 +* renders a newline-eating tag with content not starting with \n with client render on top of bad server markup
79 +* renders a newline-eating tag with content starting with \n with client render on top of bad server markup
80 +* renders a normal tag with content starting with \n with client render on top of bad server markup
81 +* renders stateless components with client render on top of bad server markup
82 +* renders ES6 class components with client render on top of bad server markup
83 +* renders factory components with client render on top of bad server markup
84 +* renders single child hierarchies of components with client render on top of bad server markup
85 +* renders multi-child hierarchies of components with client render on top of bad server markup
86 +* renders a div with a child with client render on top of bad server markup
87 +* renders a div with multiple children with client render on top of bad server markup
88 +* renders a div with multiple children separated by whitespace with client render on top of bad server markup
89 +* renders a div with a single child surrounded by whitespace with client render on top of bad server markup
90 +* renders >,<, and & as single child with client render on top of bad server markup
91 +* renders >,<, and & as multiple children with client render on top of bad server markup
92 +* throws when rendering a string component with clean client render
93 +* throws when rendering a string component with client render on top of bad server markup
94 +* throws when rendering an undefined component with clean client render
95 +* throws when rendering an undefined component with client render on top of bad server markup
96 +* throws when rendering a number component with clean client render
97 +* throws when rendering a number component with client render on top of bad server markup
98
99 src/renderers/dom/shared/__tests__/ReactMount-test.js
100 * should warn if mounting into dirty rendered markup
scripts/fiber/tests-passing.txt
+88
@@ -1066,6 +1066,94 @@ src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
1066 * renders unknown attributes for custom elements using is with clean client render
1067 * renders no HTML events with server string render
1068 * renders no HTML events with clean client render
1069 +* renders a div with text with server string render
1070 +* renders a div with text with clean client render
1071 +* renders a div with text with flanking whitespace with server string render
1072 +* renders a div with text with flanking whitespace with clean client render
1073 +* renders a div with an empty text child with server string render
1074 +* renders a div with an empty text child with clean client render
1075 +* renders a div with multiple empty text children with clean client render
1076 +* renders a div with multiple whitespace children with clean client render
1077 +* renders a div with text sibling to a node with clean client render
1078 +* renders a non-standard element with text with server string render
1079 +* renders a non-standard element with text with clean client render
1080 +* renders a custom element with text with server string render
1081 +* renders a custom element with text with clean client render
1082 +* renders a leading blank child with a text sibling with clean client render
1083 +* renders a trailing blank child with a text sibling with clean client render
1084 +* renders an element with two text children with clean client render
1085 +* renders a number as single child with server string render
1086 +* renders a number as single child with clean client render
1087 +* renders zero as single child with server string render
1088 +* renders zero as single child with clean client render
1089 +* renders an element with number and text children with clean client render
1090 +* renders null single child as blank with server string render
1091 +* renders null single child as blank with clean client render
1092 +* renders false single child as blank with server string render
1093 +* renders false single child as blank with clean client render
1094 +* renders undefined single child as blank with server string render
1095 +* renders undefined single child as blank with clean client render
1096 +* renders a null component children as empty with clean client render
1097 +* renders null children as blank with clean client render
1098 +* renders false children as blank with clean client render
1099 +* renders null and false children together as blank with clean client render
1100 +* renders only null and false children as blank with server string render
1101 +* renders only null and false children as blank with clean client render
1102 +* renders an svg element with server string render
1103 +* renders an svg element with clean client render
1104 +* renders svg element with an xlink with server string render
1105 +* renders svg element with an xlink with clean client render
1106 +* renders a math element with server string render
1107 +* renders a math element with clean client render
1108 +* renders an img with server string render
1109 +* renders an img with clean client render
1110 +* renders a button with server string render
1111 +* renders a button with clean client render
1112 +* renders a div with dangerouslySetInnerHTML with server string render
1113 +* renders a div with dangerouslySetInnerHTML with clean client render
1114 +* renders a newline-eating tag with content not starting with \n with server string render
1115 +* renders a newline-eating tag with content not starting with \n with clean client render
1116 +* renders a newline-eating tag with content starting with \n with server string render
1117 +* renders a newline-eating tag with content starting with \n with clean client render
1118 +* renders a normal tag with content starting with \n with server string render
1119 +* renders a normal tag with content starting with \n with clean client render
1120 +* renders stateless components with server string render
1121 +* renders stateless components with clean client render
1122 +* renders ES6 class components with server string render
1123 +* renders ES6 class components with clean client render
1124 +* renders factory components with server string render
1125 +* renders factory components with clean client render
1126 +* renders single child hierarchies of components with server string render
1127 +* renders single child hierarchies of components with clean client render
1128 +* renders multi-child hierarchies of components with server string render
1129 +* renders multi-child hierarchies of components with clean client render
1130 +* renders a div with a child with server string render
1131 +* renders a div with a child with clean client render
1132 +* renders a div with multiple children with server string render
1133 +* renders a div with multiple children with clean client render
1134 +* renders a div with multiple children separated by whitespace with clean client render
1135 +* renders a div with a single child surrounded by whitespace with clean client render
1136 +* renders >,<, and & as single child with server string render
1137 +* renders >,<, and & as single child with clean client render
1138 +* renders >,<, and & as multiple children with clean client render
1139 +* throws when rendering a string component with server string render
1140 +* throws when rendering an undefined component with server string render
1141 +* throws when rendering a number component with server string render
1142 +* throws when rendering null with server string render
1143 +* throws when rendering null with clean client render
1144 +* throws when rendering null with client render on top of bad server markup
1145 +* throws when rendering false with server string render
1146 +* throws when rendering false with clean client render
1147 +* throws when rendering false with client render on top of bad server markup
1148 +* throws when rendering undefined with server string render
1149 +* throws when rendering undefined with clean client render
1150 +* throws when rendering undefined with client render on top of bad server markup
1151 +* throws when rendering number with server string render
1152 +* throws when rendering number with clean client render
1153 +* throws when rendering number with client render on top of bad server markup
1154 +* throws when rendering string with server string render
1155 +* throws when rendering string with clean client render
1156 +* throws when rendering string with client render on top of bad server markup
1157
1158 src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
1159 * updates a mounted text component in place
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+586
@@ -15,6 +15,7 @@ let ExecutionEnvironment;
15 let React;
16 let ReactDOM;
17 let ReactDOMServer;
18 +let ReactDOMFeatureFlags;
19
20 // Helper functions for rendering tests
21 // ====================================
@@ -148,6 +149,31 @@ function itClientRenders(desc, testFn) {
149 testFn(clientRenderOnBadMarkup));
150 }
151
152 +function itThrows(desc, testFn) {
153 + it(`throws ${desc}`, () => {
154 + return testFn()
155 + .then(() =>
156 + expect(false).toBe('The promise resolved and should not have.'))
157 + .catch(() => {});
158 + });
159 +}
160 +
161 +function itThrowsWhenRendering(desc, testFn) {
162 + itThrows(`when rendering ${desc} with server string render`, () =>
163 + testFn(serverRender));
164 + itThrows(`when rendering ${desc} with clean client render`, () =>
165 + testFn(clientCleanRender));
166 +
167 + // we subtract one from the warning count here because the throw means that it won't
168 + // get the usual markup mismatch warning.
169 + itThrows(
170 + `when rendering ${desc} with client render on top of bad server markup`,
171 + () =>
172 + testFn((element, warningCount = 0) =>
173 + clientRenderOnBadMarkup(element, warningCount - 1)),
174 + );
175 +}
176 +
177 // When there is a test that renders on server and then on client and expects a logged
178 // error, you want to see the error show up both on server and client. Unfortunately,
179 // React refuses to issue the same error twice to avoid clogging up the console.
@@ -157,6 +183,7 @@ function resetModules() {
183 React = require('React');
184 ReactDOM = require('ReactDOM');
185 ReactDOMServer = require('ReactDOMServer');
186 + ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
187 ExecutionEnvironment = require('ExecutionEnvironment');
188 }
189
@@ -410,4 +437,563 @@ describe('ReactDOMServerIntegration', () => {
437 expect(e.getAttribute('click')).toBe(null);
438 });
439 });
440 +
441 + describe('elements and children', function() {
442 + // helper functions.
443 + const TEXT_NODE_TYPE = 3;
444 + const COMMENT_NODE_TYPE = 8;
445 +
446 + function expectNode(node, type, value) {
447 + expect(node).not.toBe(null);
448 + expect(node.nodeType).toBe(type);
449 + expect(node.nodeValue).toMatch(value);
450 + }
451 +
452 + function expectTextNode(node, text) {
453 + if (ReactDOMFeatureFlags.useFiber) {
454 + // with Fiber, a React text node is just a DOM text node.
455 + expectNode(node, TEXT_NODE_TYPE, text);
456 + } else {
457 + // with Stack, a React text node is a DOM text node surrounded by
458 + // react-text comment nodes.
459 + expectNode(node, COMMENT_NODE_TYPE, / react-text: [0-9]+ /);
460 + if (text.length > 0) {
461 + node = node.nextSibling;
462 + expectNode(node, TEXT_NODE_TYPE, text);
463 + }
464 + expectNode(node.nextSibling, COMMENT_NODE_TYPE, / \/react-text /);
465 + }
466 + }
467 +
468 + function expectEmptyNode(node) {
469 + expectNode(node, COMMENT_NODE_TYPE, / react-empty: [0-9]+ /);
470 + }
471 +
472 + describe('text children', function() {
473 + itRenders('a div with text', async render => {
474 + const e = await render(<div>Text</div>);
475 + expect(e.tagName).toBe('DIV');
476 + expect(e.childNodes.length).toBe(1);
477 + expectNode(e.firstChild, TEXT_NODE_TYPE, 'Text');
478 + });
479 +
480 + itRenders('a div with text with flanking whitespace', async render => {
481 + // prettier-ignore
482 + const e = await render(<div> Text </div>);
483 + expect(e.childNodes.length).toBe(1);
484 + expectNode(e.childNodes[0], TEXT_NODE_TYPE, ' Text ');
485 + });
486 +
487 + itRenders('a div with an empty text child', async render => {
488 + const e = await render(<div>{''}</div>);
489 + expect(e.childNodes.length).toBe(0);
490 + });
491 +
492 + itRenders('a div with multiple empty text children', async render => {
493 + const e = await render(<div>{''}{''}{''}</div>);
494 + if (ReactDOMFeatureFlags.useFiber) {
495 + // with Fiber, there are just three separate text node children,
496 + // each of which is blank.
497 + expect(e.childNodes.length).toBe(3);
498 + expectTextNode(e.childNodes[0], '');
499 + expectTextNode(e.childNodes[1], '');
500 + expectTextNode(e.childNodes[2], '');
501 + } else {
502 + // with Stack, there are six react-text comment nodes.
503 + expect(e.childNodes.length).toBe(6);
504 + expectTextNode(e.childNodes[0], '');
505 + expectTextNode(e.childNodes[2], '');
506 + expectTextNode(e.childNodes[4], '');
507 + }
508 + });
509 +
510 + itRenders('a div with multiple whitespace children', async render => {
511 + const e = await render(<div>{' '}{' '}{' '}</div>);
512 + if (ReactDOMFeatureFlags.useFiber) {
513 + // with Fiber, there are just three text nodes.
514 + expect(e.childNodes.length).toBe(3);
515 + expectTextNode(e.childNodes[0], ' ');
516 + expectTextNode(e.childNodes[1], ' ');
517 + expectTextNode(e.childNodes[2], ' ');
518 + } else {
519 + // with Stack, each of the text nodes is surrounded by react-text
520 + // comment nodes, making 9 nodes in total.
521 + expect(e.childNodes.length).toBe(9);
522 + expectTextNode(e.childNodes[0], ' ');
523 + expectTextNode(e.childNodes[3], ' ');
524 + expectTextNode(e.childNodes[6], ' ');
525 + }
526 + });
527 +
528 + itRenders('a div with text sibling to a node', async render => {
529 + const e = await render(<div>Text<span>More Text</span></div>);
530 + let spanNode;
531 + if (ReactDOMFeatureFlags.useFiber) {
532 + // with Fiber, there are only two children, the "Text" text node and
533 + // the span element.
534 + expect(e.childNodes.length).toBe(2);
535 + spanNode = e.childNodes[1];
536 + } else {
537 + // with Stack, there are four children, a "Text" text node surrounded
538 + // by react-text comment nodes, and the span element.
539 + expect(e.childNodes.length).toBe(4);
540 + spanNode = e.childNodes[3];
541 + }
542 + expectTextNode(e.childNodes[0], 'Text');
543 + expect(spanNode.tagName).toBe('SPAN');
544 + expect(spanNode.childNodes.length).toBe(1);
545 + expectNode(spanNode.firstChild, TEXT_NODE_TYPE, 'More Text');
546 + });
547 +
548 + itRenders('a non-standard element with text', async render => {
549 + const e = await render(<nonstandard>Text</nonstandard>);
550 + expect(e.tagName).toBe('NONSTANDARD');
551 + expect(e.childNodes.length).toBe(1);
552 + expectNode(e.firstChild, TEXT_NODE_TYPE, 'Text');
553 + });
554 +
555 + itRenders('a custom element with text', async render => {
556 + const e = await render(<custom-element>Text</custom-element>);
557 + expect(e.tagName).toBe('CUSTOM-ELEMENT');
558 + expect(e.childNodes.length).toBe(1);
559 + expectNode(e.firstChild, TEXT_NODE_TYPE, 'Text');
560 + });
561 +
562 + itRenders('a leading blank child with a text sibling', async render => {
563 + const e = await render(<div>{''}foo</div>);
564 + if (ReactDOMFeatureFlags.useFiber) {
565 + // with Fiber, there are just two text nodes.
566 + expect(e.childNodes.length).toBe(2);
567 + expectTextNode(e.childNodes[0], '');
568 + expectTextNode(e.childNodes[1], 'foo');
569 + } else {
570 + // with Stack, there are five nodes: two react-text comment nodes
571 + // without any text between them, and the text node foo surrounded
572 + // by react-text comment nodes.
573 + expect(e.childNodes.length).toBe(5);
574 + expectTextNode(e.childNodes[0], '');
575 + expectTextNode(e.childNodes[2], 'foo');
576 + }
577 + });
578 +
579 + itRenders('a trailing blank child with a text sibling', async render => {
580 + const e = await render(<div>foo{''}</div>);
581 + if (ReactDOMFeatureFlags.useFiber) {
582 + // with Fiber, there are just two text nodes.
583 + expect(e.childNodes.length).toBe(2);
584 + expectTextNode(e.childNodes[0], 'foo');
585 + expectTextNode(e.childNodes[1], '');
586 + } else {
587 + // with Stack, there are five nodes: the text node foo surrounded
588 + // by react-text comment nodes, and two react-text comment nodes
589 + // without any text between them.
590 + expect(e.childNodes.length).toBe(5);
591 + expectTextNode(e.childNodes[0], 'foo');
592 + expectTextNode(e.childNodes[3], '');
593 + }
594 + });
595 +
596 + itRenders('an element with two text children', async render => {
597 + const e = await render(<div>{'foo'}{'bar'}</div>);
598 + if (ReactDOMFeatureFlags.useFiber) {
599 + // with Fiber, there are just two text nodes.
600 + expect(e.childNodes.length).toBe(2);
601 + expectTextNode(e.childNodes[0], 'foo');
602 + expectTextNode(e.childNodes[1], 'bar');
603 + } else {
604 + // with Stack, there are six nodes: two text nodes, each surrounded
605 + // by react-text comment nodes.
606 + expect(e.childNodes.length).toBe(6);
607 + expectTextNode(e.childNodes[0], 'foo');
608 + expectTextNode(e.childNodes[3], 'bar');
609 + }
610 + });
611 + });
612 +
613 + describe('number children', function() {
614 + itRenders('a number as single child', async render => {
615 + const e = await render(<div>{3}</div>);
616 + expect(e.textContent).toBe('3');
617 + });
618 +
619 + // zero is falsey, so it could look like no children if the code isn't careful.
620 + itRenders('zero as single child', async render => {
621 + const e = await render(<div>{0}</div>);
622 + expect(e.textContent).toBe('0');
623 + });
624 +
625 + itRenders('an element with number and text children', async render => {
626 + const e = await render(<div>{'foo'}{40}</div>);
627 + if (ReactDOMFeatureFlags.useFiber) {
628 + // with Fiber, there are just two text nodes.
629 + expect(e.childNodes.length).toBe(2);
630 + expectTextNode(e.childNodes[0], 'foo');
631 + expectTextNode(e.childNodes[1], '40');
632 + } else {
633 + // with Stack, there are six nodes: two text nodes, each surrounded
634 + // by react-text comment nodes.
635 + expect(e.childNodes.length).toBe(6);
636 + expectTextNode(e.childNodes[0], 'foo');
637 + expectTextNode(e.childNodes[3], '40');
638 + }
639 + });
640 + });
641 +
642 + describe('null, false, and undefined children', function() {
643 + itRenders('null single child as blank', async render => {
644 + const e = await render(<div>{null}</div>);
645 + expect(e.childNodes.length).toBe(0);
646 + });
647 +
648 + itRenders('false single child as blank', async render => {
649 + const e = await render(<div>{false}</div>);
650 + expect(e.childNodes.length).toBe(0);
651 + });
652 +
653 + itRenders('undefined single child as blank', async render => {
654 + const e = await render(<div>{undefined}</div>);
655 + expect(e.childNodes.length).toBe(0);
656 + });
657 +
658 + itRenders('a null component children as empty', async render => {
659 + const NullComponent = () => null;
660 + const e = await render(<div><NullComponent /></div>);
661 + if (ReactDOMFeatureFlags.useFiber) {
662 + // with Fiber, an empty component results in no markup.
663 + expect(e.childNodes.length).toBe(0);
664 + } else {
665 + // with Stack, an empty component results in one react-empty comment
666 + // node.
667 + expect(e.childNodes.length).toBe(1);
668 + expectEmptyNode(e.firstChild);
669 + }
670 + });
671 +
672 + itRenders('null children as blank', async render => {
673 + const e = await render(<div>{null}foo</div>);
674 + if (ReactDOMFeatureFlags.useFiber) {
675 + // with Fiber, there is just one text node.
676 + expect(e.childNodes.length).toBe(1);
677 + } else {
678 + // with Stack, there's a text node surronded by react-text comment nodes.
679 + expect(e.childNodes.length).toBe(3);
680 + }
681 + expectTextNode(e.childNodes[0], 'foo');
682 + });
683 +
684 + itRenders('false children as blank', async render => {
685 + const e = await render(<div>{false}foo</div>);
686 + if (ReactDOMFeatureFlags.useFiber) {
687 + // with Fiber, there is just one text node.
688 + expect(e.childNodes.length).toBe(1);
689 + } else {
690 + // with Stack, there's a text node surronded by react-text comment nodes.
691 + expect(e.childNodes.length).toBe(3);
692 + }
693 + expectTextNode(e.childNodes[0], 'foo');
694 + });
695 +
696 + itRenders('null and false children together as blank', async render => {
697 + const e = await render(<div>{false}{null}foo{null}{false}</div>);
698 + if (ReactDOMFeatureFlags.useFiber) {
699 + // with Fiber, there is just one text node.
700 + expect(e.childNodes.length).toBe(1);
701 + } else {
702 + // with Stack, there's a text node surronded by react-text comment nodes.
703 + expect(e.childNodes.length).toBe(3);
704 + }
705 + expectTextNode(e.childNodes[0], 'foo');
706 + });
707 +
708 + itRenders('only null and false children as blank', async render => {
709 + const e = await render(<div>{false}{null}{null}{false}</div>);
710 + expect(e.childNodes.length).toBe(0);
711 + });
712 + });
713 +
714 + describe('elements with implicit namespaces', function() {
715 + itRenders('an svg element', async render => {
716 + const e = await render(<svg />);
717 + expect(e.childNodes.length).toBe(0);
718 + expect(e.tagName).toBe('svg');
719 + expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
720 + });
721 +
722 + itRenders('svg element with an xlink', async render => {
723 + let e = await render(
724 + <svg><image xlinkHref="http://i.imgur.com/w7GCRPb.png" /></svg>,
725 + );
726 + e = e.firstChild;
727 + expect(e.childNodes.length).toBe(0);
728 + expect(e.tagName).toBe('image');
729 + expect(e.namespaceURI).toBe('http://www.w3.org/2000/svg');
730 + expect(e.getAttributeNS('http://www.w3.org/1999/xlink', 'href')).toBe(
731 + 'http://i.imgur.com/w7GCRPb.png',
732 + );
733 + });
734 +
735 + itRenders('a math element', async render => {
736 + const e = await render(<math />);
737 + expect(e.childNodes.length).toBe(0);
738 + expect(e.tagName).toBe('math');
739 + expect(e.namespaceURI).toBe('http://www.w3.org/1998/Math/MathML');
740 + });
741 + });
742 + // specially wrapped components
743 + // (see the big switch near the beginning ofReactDOMComponent.mountComponent)
744 + itRenders('an img', async render => {
745 + const e = await render(<img />);
746 + expect(e.childNodes.length).toBe(0);
747 + expect(e.nextSibling).toBe(null);
748 + expect(e.tagName).toBe('IMG');
749 + });
750 +
751 + itRenders('a button', async render => {
752 + const e = await render(<button />);
753 + expect(e.childNodes.length).toBe(0);
754 + expect(e.nextSibling).toBe(null);
755 + expect(e.tagName).toBe('BUTTON');
756 + });
757 +
758 + itRenders('a div with dangerouslySetInnerHTML', async render => {
759 + const e = await render(
760 + <div dangerouslySetInnerHTML={{__html: "<span id='child'/>"}} />,
761 + );
762 + expect(e.childNodes.length).toBe(1);
763 + expect(e.firstChild.tagName).toBe('SPAN');
764 + expect(e.firstChild.getAttribute('id')).toBe('child');
765 + expect(e.firstChild.childNodes.length).toBe(0);
766 + });
767 +
768 + describe('newline-eating elements', function() {
769 + itRenders(
770 + 'a newline-eating tag with content not starting with \\n',
771 + async render => {
772 + const e = await render(<pre>Hello</pre>);
773 + expect(e.textContent).toBe('Hello');
774 + },
775 + );
776 + itRenders(
777 + 'a newline-eating tag with content starting with \\n',
778 + async render => {
779 + const e = await render(<pre>{'\nHello'}</pre>);
780 + expect(e.textContent).toBe('\nHello');
781 + },
782 + );
783 + itRenders('a normal tag with content starting with \\n', async render => {
784 + const e = await render(<div>{'\nHello'}</div>);
785 + expect(e.textContent).toBe('\nHello');
786 + });
787 + });
788 +
789 + describe('different component implementations', function() {
790 + function checkFooDiv(e) {
791 + expect(e.childNodes.length).toBe(1);
792 + expectNode(e.firstChild, TEXT_NODE_TYPE, 'foo');
793 + }
794 +
795 + itRenders('stateless components', async render => {
796 + const StatelessComponent = () => <div>foo</div>;
797 + checkFooDiv(await render(<StatelessComponent />));
798 + });
799 +
800 + itRenders('ES6 class components', async render => {
801 + class ClassComponent extends React.Component {
802 + render() {
803 + return <div>foo</div>;
804 + }
805 + }
806 + checkFooDiv(await render(<ClassComponent />));
807 + });
808 +
809 + itRenders('factory components', async render => {
810 + const FactoryComponent = () => {
811 + return {
812 + render: function() {
813 + return <div>foo</div>;
814 + },
815 + };
816 + };
817 + checkFooDiv(await render(<FactoryComponent />));
818 + });
819 + });
820 +
821 + describe('component hierarchies', async function() {
822 + itRenders('single child hierarchies of components', async render => {
823 + const Component = props => <div>{props.children}</div>;
824 + let e = await render(
825 + <Component>
826 + <Component>
827 + <Component>
828 + <Component />
829 + </Component>
830 + </Component>
831 + </Component>,
832 + );
833 + for (var i = 0; i < 3; i++) {
834 + expect(e.tagName).toBe('DIV');
835 + expect(e.childNodes.length).toBe(1);
836 + e = e.firstChild;
837 + }
838 + expect(e.tagName).toBe('DIV');
839 + expect(e.childNodes.length).toBe(0);
840 + });
841 +
842 + itRenders('multi-child hierarchies of components', async render => {
843 + const Component = props => <div>{props.children}</div>;
844 + const e = await render(
845 + <Component>
846 + <Component>
847 + <Component /><Component />
848 + </Component>
849 + <Component>
850 + <Component /><Component />
851 + </Component>
852 + </Component>,
853 + );
854 + expect(e.tagName).toBe('DIV');
855 + expect(e.childNodes.length).toBe(2);
856 + for (var i = 0; i < 2; i++) {
857 + var child = e.childNodes[i];
858 + expect(child.tagName).toBe('DIV');
859 + expect(child.childNodes.length).toBe(2);
860 + for (var j = 0; j < 2; j++) {
861 + var grandchild = child.childNodes[j];
862 + expect(grandchild.tagName).toBe('DIV');
863 + expect(grandchild.childNodes.length).toBe(0);
864 + }
865 + }
866 + });
867 +
868 + itRenders('a div with a child', async render => {
869 + const e = await render(<div id="parent"><div id="child" /></div>);
870 + expect(e.id).toBe('parent');
871 + expect(e.childNodes.length).toBe(1);
872 + expect(e.childNodes[0].id).toBe('child');
873 + expect(e.childNodes[0].childNodes.length).toBe(0);
874 + });
875 +
876 + itRenders('a div with multiple children', async render => {
877 + const e = await render(
878 + <div id="parent"><div id="child1" /><div id="child2" /></div>,
879 + );
880 + expect(e.id).toBe('parent');
881 + expect(e.childNodes.length).toBe(2);
882 + expect(e.childNodes[0].id).toBe('child1');
883 + expect(e.childNodes[0].childNodes.length).toBe(0);
884 + expect(e.childNodes[1].id).toBe('child2');
885 + expect(e.childNodes[1].childNodes.length).toBe(0);
886 + });
887 +
888 + itRenders(
889 + 'a div with multiple children separated by whitespace',
890 + async render => {
891 + const e = await render(
892 + <div id="parent"><div id="child1" /> <div id="child2" /></div>,
893 + );
894 + expect(e.id).toBe('parent');
895 + let child1, child2, textNode;
896 + if (ReactDOMFeatureFlags.useFiber) {
897 + // with Fiber, there are three children: the child1 element, a
898 + // single space text node, and the child2 element.
899 + expect(e.childNodes.length).toBe(3);
900 + child1 = e.childNodes[0];
901 + textNode = e.childNodes[1];
902 + child2 = e.childNodes[2];
903 + } else {
904 + // with Stack, there are five children: the child1 element, a single
905 + // space surrounded by react-text comments, and the child2 element.
906 + expect(e.childNodes.length).toBe(5);
907 + child1 = e.childNodes[0];
908 + textNode = e.childNodes[1];
909 + child2 = e.childNodes[4];
910 + }
911 + expect(child1.id).toBe('child1');
912 + expect(child1.childNodes.length).toBe(0);
913 + expectTextNode(textNode, ' ');
914 + expect(child2.id).toBe('child2');
915 + expect(child2.childNodes.length).toBe(0);
916 + },
917 + );
918 +
919 + itRenders(
920 + 'a div with a single child surrounded by whitespace',
921 + async render => {
922 + // prettier-ignore
923 + const e = await render(<div id="parent"> <div id="child" /> </div>); // eslint-disable-line no-multi-spaces
924 + let textNode1, child, textNode2;
925 + if (ReactDOMFeatureFlags.useFiber) {
926 + // with Fiber, there are three children: a one-space text node, the
927 + // child element, and a two-space text node.
928 + expect(e.childNodes.length).toBe(3);
929 + textNode1 = e.childNodes[0];
930 + child = e.childNodes[1];
931 + textNode2 = e.childNodes[2];
932 + } else {
933 + // with Stack, there are 7 children: a one-space text node surrounded
934 + // by react-text comments, the child element, and a two-space text node
935 + // surrounded by react-text comments.
936 + expect(e.childNodes.length).toBe(7);
937 + textNode1 = e.childNodes[0];
938 + child = e.childNodes[3];
939 + textNode2 = e.childNodes[4];
940 + }
941 + expect(e.id).toBe('parent');
942 + expectTextNode(textNode1, ' ');
943 + expect(child.id).toBe('child');
944 + expect(child.childNodes.length).toBe(0);
945 + expectTextNode(textNode2, ' ');
946 + },
947 + );
948 + });
949 +
950 + describe('escaping >, <, and &', function() {
951 + itRenders('>,<, and & as single child', async render => {
952 + const e = await render(<div>{'<span>Text&quot;</span>'}</div>);
953 + expect(e.childNodes.length).toBe(1);
954 + expectNode(e.firstChild, TEXT_NODE_TYPE, '<span>Text&quot;</span>');
955 + });
956 +
957 + itRenders('>,<, and & as multiple children', async render => {
958 + const e = await render(
959 + <div>{'<span>Text1&quot;</span>'}{'<span>Text2&quot;</span>'}</div>,
960 + );
961 + if (ReactDOMFeatureFlags.useFiber) {
962 + // with Fiber, there are just two text nodes.
963 + expect(e.childNodes.length).toBe(2);
964 + expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
965 + expectTextNode(e.childNodes[1], '<span>Text2&quot;</span>');
966 + } else {
967 + // with Stack there are six nodes: two text nodes each surrounded by
968 + // two react-text comment nodes.
969 + expect(e.childNodes.length).toBe(6);
970 + expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
971 + expectTextNode(e.childNodes[3], '<span>Text2&quot;</span>');
972 + }
973 + });
974 + });
975 +
976 + describe('components that throw errors', function() {
977 + itThrowsWhenRendering('a string component', async render => {
978 + const StringComponent = () => 'foo';
979 + await render(<StringComponent />, 1);
980 + });
981 +
982 + itThrowsWhenRendering('an undefined component', async render => {
983 + const UndefinedComponent = () => undefined;
984 + await render(<UndefinedComponent />, 1);
985 + });
986 +
987 + itThrowsWhenRendering('a number component', async render => {
988 + const NumberComponent = () => 54;
989 + await render(<NumberComponent />, 1);
990 + });
991 +
992 + itThrowsWhenRendering('null', render => render(null));
993 + itThrowsWhenRendering('false', render => render(false));
994 + itThrowsWhenRendering('undefined', render => render(undefined));
995 + itThrowsWhenRendering('number', render => render(30));
996 + itThrowsWhenRendering('string', render => render('foo'));
997 + });
998 + });
999 });