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"</span>'}</div>);
953
+ expect(e.childNodes.length).toBe(1);
954
+ expectNode(e.firstChild, TEXT_NODE_TYPE, '<span>Text"</span>');
955
+ });
956
+
957
+ itRenders('>,<, and & as multiple children', async render => {
958
+ const e = await render(
959
+ <div>{'<span>Text1"</span>'}{'<span>Text2"</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"</span>');
965
+ expectTextNode(e.childNodes[1], '<span>Text2"</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"</span>');
971
+ expectTextNode(e.childNodes[3], '<span>Text2"</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
});