@samitouri / QOS-React-2 / commits / a947d3f92f

Remove usage of ReactDOMFeatureFlags.useFiber, assuming true (#10796)

Dan Abramov committed Sep 27, 2017 at 17:14 UTC a947d3f92fba61ba9107861dde9e4ea89ad87442
33 files changed +2699 -3643
src/isomorphic/children/__tests__/ReactChildren-test.js
+35 -39
@@ -9,8 +9,6 @@
9
10 'use strict';
11
12 -const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 -
12 describe('ReactChildren', () => {
13 var React;
14 var ReactTestUtils;
@@ -878,52 +876,50 @@ describe('ReactChildren', () => {
876 );
877 });
878
881 - if (ReactDOMFeatureFlags.useFiber) {
882 - describe('with fragments enabled', () => {
883 - it('warns for keys for arrays of elements in a fragment', () => {
884 - spyOn(console, 'error');
885 - class ComponentReturningArray extends React.Component {
886 - render() {
887 - return [<div />, <div />];
888 - }
879 + describe('with fragments enabled', () => {
880 + it('warns for keys for arrays of elements in a fragment', () => {
881 + spyOn(console, 'error');
882 + class ComponentReturningArray extends React.Component {
883 + render() {
884 + return [<div />, <div />];
885 }
886 + }
887
891 - ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
888 + ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
889
893 - expectDev(console.error.calls.count()).toBe(1);
894 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
895 - 'Warning: ' +
896 - 'Each child in an array or iterator should have a unique "key" prop.' +
897 - ' See https://fb.me/react-warning-keys for more information.' +
898 - '\n in ComponentReturningArray (at **)',
899 - );
900 - });
890 + expectDev(console.error.calls.count()).toBe(1);
891 + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
892 + 'Warning: ' +
893 + 'Each child in an array or iterator should have a unique "key" prop.' +
894 + ' See https://fb.me/react-warning-keys for more information.' +
895 + '\n in ComponentReturningArray (at **)',
896 + );
897 + });
898
902 - it('does not warn when there are keys on elements in a fragment', () => {
903 - spyOn(console, 'error');
904 - class ComponentReturningArray extends React.Component {
905 - render() {
906 - return [<div key="foo" />, <div key="bar" />];
907 - }
899 + it('does not warn when there are keys on elements in a fragment', () => {
900 + spyOn(console, 'error');
901 + class ComponentReturningArray extends React.Component {
902 + render() {
903 + return [<div key="foo" />, <div key="bar" />];
904 }
905 + }
906
910 - ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
907 + ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
908
912 - expectDev(console.error.calls.count()).toBe(0);
913 - });
909 + expectDev(console.error.calls.count()).toBe(0);
910 + });
911
915 - it('warns for keys for arrays at the top level', () => {
916 - spyOn(console, 'error');
912 + it('warns for keys for arrays at the top level', () => {
913 + spyOn(console, 'error');
914
918 - ReactTestUtils.renderIntoDocument([<div />, <div />]);
915 + ReactTestUtils.renderIntoDocument([<div />, <div />]);
916
920 - expectDev(console.error.calls.count()).toBe(1);
921 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
922 - 'Warning: ' +
923 - 'Each child in an array or iterator should have a unique "key" prop.' +
924 - ' See https://fb.me/react-warning-keys for more information.',
925 - );
926 - });
917 + expectDev(console.error.calls.count()).toBe(1);
918 + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
919 + 'Warning: ' +
920 + 'Each child in an array or iterator should have a unique "key" prop.' +
921 + ' See https://fb.me/react-warning-keys for more information.',
922 + );
923 });
928 - }
924 + });
925 });
src/isomorphic/classic/element/__tests__/ReactElement-test.js
+1 -8
@@ -12,7 +12,6 @@
12 var React;
13 var ReactDOM;
14 var ReactTestUtils;
15 -var ReactDOMFeatureFlags;
15
16 describe('ReactElement', () => {
17 var ComponentClass;
@@ -29,7 +28,6 @@ describe('ReactElement', () => {
28 React = require('react');
29 ReactDOM = require('react-dom');
30 ReactTestUtils = require('react-dom/test-utils');
32 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
31 // NOTE: We're explicitly not using JSX here. This is intended to test
32 // classic JS without JSX.
33 ComponentClass = class extends React.Component {
@@ -235,12 +233,7 @@ describe('ReactElement', () => {
233 var instance = ReactTestUtils.renderIntoDocument(
234 React.createElement(Wrapper),
235 );
238 -
239 - if (ReactDOMFeatureFlags.useFiber) {
240 - expect(element._owner.stateNode).toBe(instance);
241 - } else {
242 - expect(element._owner.getPublicInstance()).toBe(instance);
243 - }
236 + expect(element._owner.stateNode).toBe(instance);
237 });
238
239 it('merges an additional argument onto the children prop', () => {
src/renderers/__tests__/EventPluginHub-test.js
+4 -10
@@ -14,13 +14,11 @@ jest.mock('isEventSupported');
14 describe('EventPluginHub', () => {
15 var React;
16 var ReactTestUtils;
17 - var ReactDOMFeatureFlags;
17
18 beforeEach(() => {
19 jest.resetModules();
20 React = require('react');
21 ReactTestUtils = require('react-dom/test-utils');
23 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
22 });
23
24 it('should prevent non-function listeners, at dispatch', () => {
@@ -33,14 +31,10 @@ describe('EventPluginHub', () => {
31 }).toThrowError(
32 'Expected `onClick` listener to be a function, instead got a value of `string` type.',
33 );
36 - if (ReactDOMFeatureFlags.useFiber) {
37 - expectDev(console.error.calls.count()).toBe(1);
38 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
39 - 'Expected `onClick` listener to be a function, instead got a value of `string` type.',
40 - );
41 - } else {
42 - expectDev(console.error.calls.count()).toBe(0);
43 - }
34 + expectDev(console.error.calls.count()).toBe(1);
35 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
36 + 'Expected `onClick` listener to be a function, instead got a value of `string` type.',
37 + );
38 });
39
40 it('should not prevent null listeners, at dispatch', () => {
src/renderers/__tests__/ReactComponent-test.js
+71 -89
@@ -14,8 +14,6 @@ var ReactDOM;
14 var ReactDOMServer;
15 var ReactTestUtils;
16
17 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
18 -
17 describe('ReactComponent', () => {
18 function normalizeCodeLocInfo(str) {
19 return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -297,21 +295,11 @@ describe('ReactComponent', () => {
295 'outer componentDidMount',
296 'start update',
297 // Previous (equivalent) refs get cleared
300 - ...(ReactDOMFeatureFlags.useFiber
301 - ? [
302 - // Fiber renders first, resets refs later
303 - 'inner 1 render',
304 - 'inner 2 render',
305 - 'ref 1 got null',
306 - 'ref 2 got null',
307 - ]
308 - : [
309 - // Stack resets refs before rendering
310 - 'ref 1 got null',
311 - 'inner 1 render',
312 - 'ref 2 got null',
313 - 'inner 2 render',
314 - ]),
298 + // Fiber renders first, resets refs later
299 + 'inner 1 render',
300 + 'inner 2 render',
301 + 'ref 1 got null',
302 + 'ref 2 got null',
303 'inner 1 componentDidUpdate',
304 'ref 1 got instance 1',
305 'inner 2 componentDidUpdate',
@@ -406,8 +394,7 @@ describe('ReactComponent', () => {
394 'Objects are not valid as a React child (found: object with keys ' +
395 '{x, y, z}). If you meant to render a collection of children, use ' +
396 'an array instead.' +
409 - // Fiber gives a slightly better stack with the nearest host components
410 - (ReactDOMFeatureFlags.useFiber ? '\n in div (at **)' : ''),
397 + '\n in div (at **)',
398 );
399 });
400
@@ -434,8 +421,7 @@ describe('ReactComponent', () => {
421 'Objects are not valid as a React child (found: object with keys ' +
422 '{a, b, c}). If you meant to render a collection of children, use ' +
423 'an array instead.\n' +
437 - // Fiber gives a slightly better stack with the nearest host components
438 - (ReactDOMFeatureFlags.useFiber ? ' in div (at **)\n' : '') +
424 + ' in div (at **)\n' +
425 ' in Foo (at **)',
426 );
427 });
@@ -458,8 +444,7 @@ describe('ReactComponent', () => {
444 'Objects are not valid as a React child (found: object with keys ' +
445 '{x, y, z}). If you meant to render a collection of children, use ' +
446 'an array instead.' +
461 - // Fiber gives a slightly better stack with the nearest host components
462 - (ReactDOMFeatureFlags.useFiber ? '\n in div (at **)' : ''),
447 + '\n in div (at **)',
448 );
449 });
450
@@ -486,79 +471,76 @@ describe('ReactComponent', () => {
471 'Objects are not valid as a React child (found: object with keys ' +
472 '{a, b, c}). If you meant to render a collection of children, use ' +
473 'an array instead.\n' +
489 - // Fiber gives a slightly better stack with the nearest host components
490 - (ReactDOMFeatureFlags.useFiber ? ' in div (at **)\n' : '') +
474 + ' in div (at **)\n' +
475 ' in Foo (at **)',
476 );
477 });
478
495 - if (ReactDOMFeatureFlags.useFiber) {
496 - describe('with new features', () => {
497 - it('warns on function as a return value from a function', () => {
498 - function Foo() {
499 - return Foo;
500 - }
501 - spyOn(console, 'error');
502 - var container = document.createElement('div');
503 - ReactDOM.render(<Foo />, container);
504 - expectDev(console.error.calls.count()).toBe(1);
505 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
506 - 'Warning: Functions are not valid as a React child. This may happen if ' +
507 - 'you return a Component instead of <Component /> from render. ' +
508 - 'Or maybe you meant to call this function rather than return it.\n' +
509 - ' in Foo (at **)',
510 - );
511 - });
479 + describe('with new features', () => {
480 + it('warns on function as a return value from a function', () => {
481 + function Foo() {
482 + return Foo;
483 + }
484 + spyOn(console, 'error');
485 + var container = document.createElement('div');
486 + ReactDOM.render(<Foo />, container);
487 + expectDev(console.error.calls.count()).toBe(1);
488 + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
489 + 'Warning: Functions are not valid as a React child. This may happen if ' +
490 + 'you return a Component instead of <Component /> from render. ' +
491 + 'Or maybe you meant to call this function rather than return it.\n' +
492 + ' in Foo (at **)',
493 + );
494 + });
495
513 - it('warns on function as a return value from a class', () => {
514 - class Foo extends React.Component {
515 - render() {
516 - return Foo;
517 - }
496 + it('warns on function as a return value from a class', () => {
497 + class Foo extends React.Component {
498 + render() {
499 + return Foo;
500 }
519 - spyOn(console, 'error');
520 - var container = document.createElement('div');
521 - ReactDOM.render(<Foo />, container);
522 - expectDev(console.error.calls.count()).toBe(1);
523 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
524 - 'Warning: Functions are not valid as a React child. This may happen if ' +
525 - 'you return a Component instead of <Component /> from render. ' +
526 - 'Or maybe you meant to call this function rather than return it.\n' +
527 - ' in Foo (at **)',
528 - );
529 - });
501 + }
502 + spyOn(console, 'error');
503 + var container = document.createElement('div');
504 + ReactDOM.render(<Foo />, container);
505 + expectDev(console.error.calls.count()).toBe(1);
506 + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
507 + 'Warning: Functions are not valid as a React child. This may happen if ' +
508 + 'you return a Component instead of <Component /> from render. ' +
509 + 'Or maybe you meant to call this function rather than return it.\n' +
510 + ' in Foo (at **)',
511 + );
512 + });
513
531 - it('warns on function as a child to host component', () => {
532 - function Foo() {
533 - return <div><span>{Foo}</span></div>;
534 - }
535 - spyOn(console, 'error');
536 - var container = document.createElement('div');
537 - ReactDOM.render(<Foo />, container);
538 - expectDev(console.error.calls.count()).toBe(1);
539 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
540 - 'Warning: Functions are not valid as a React child. This may happen if ' +
541 - 'you return a Component instead of <Component /> from render. ' +
542 - 'Or maybe you meant to call this function rather than return it.\n' +
543 - ' in span (at **)\n' +
544 - ' in div (at **)\n' +
545 - ' in Foo (at **)',
546 - );
547 - });
514 + it('warns on function as a child to host component', () => {
515 + function Foo() {
516 + return <div><span>{Foo}</span></div>;
517 + }
518 + spyOn(console, 'error');
519 + var container = document.createElement('div');
520 + ReactDOM.render(<Foo />, container);
521 + expectDev(console.error.calls.count()).toBe(1);
522 + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
523 + 'Warning: Functions are not valid as a React child. This may happen if ' +
524 + 'you return a Component instead of <Component /> from render. ' +
525 + 'Or maybe you meant to call this function rather than return it.\n' +
526 + ' in span (at **)\n' +
527 + ' in div (at **)\n' +
528 + ' in Foo (at **)',
529 + );
530 + });
531
549 - it('does not warn for function-as-a-child that gets resolved', () => {
550 - function Bar(props) {
551 - return props.children();
552 - }
553 - function Foo() {
554 - return <Bar>{() => 'Hello'}</Bar>;
555 - }
556 - spyOn(console, 'error');
557 - var container = document.createElement('div');
558 - ReactDOM.render(<Foo />, container);
559 - expect(container.innerHTML).toBe('Hello');
560 - expectDev(console.error.calls.count()).toBe(0);
561 - });
532 + it('does not warn for function-as-a-child that gets resolved', () => {
533 + function Bar(props) {
534 + return props.children();
535 + }
536 + function Foo() {
537 + return <Bar>{() => 'Hello'}</Bar>;
538 + }
539 + spyOn(console, 'error');
540 + var container = document.createElement('div');
541 + ReactDOM.render(<Foo />, container);
542 + expect(container.innerHTML).toBe('Hello');
543 + expectDev(console.error.calls.count()).toBe(0);
544 });
563 - }
545 + });
546 });
src/renderers/__tests__/ReactComponentTreeHook-test.js
+7 -76
@@ -9,13 +9,9 @@
9
10 'use strict';
11
12 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 -var describeStack = ReactDOMFeatureFlags.useFiber ? describe.skip : describe;
14 -
15 -describe('ReactComponentTreeHook', () => {
12 +describe.skip('ReactComponentTreeHook', () => {
13 var React;
14 var ReactDOM;
18 - var ReactDOMServer;
15 var ReactInstanceMap;
16 var ReactComponentTreeHook;
17 var ReactDebugCurrentFiber;
@@ -26,7 +22,6 @@ describe('ReactComponentTreeHook', () => {
22
23 React = require('react');
24 ReactDOM = require('react-dom');
29 - ReactDOMServer = require('react-dom/server');
25 ReactInstanceMap = require('ReactInstanceMap');
26 ReactDebugCurrentFiber = require('ReactDebugCurrentFiber');
27 ReactComponentTreeHook = require('ReactComponentTreeHook');
@@ -37,9 +32,8 @@ describe('ReactComponentTreeHook', () => {
32 describe('stack addenda', () => {
33 it('gets created', () => {
34 function getAddendum(element) {
40 - var addendum = ReactDOMFeatureFlags.useFiber
41 - ? ReactDebugCurrentFiber.getCurrentFiberStackAddendum() || ''
42 - : ReactComponentTreeHook.getCurrentStackAddendum();
35 + var addendum =
36 + ReactDebugCurrentFiber.getCurrentFiberStackAddendum() || '';
37 return addendum.replace(/\(at .+?:\d+\)/g, '(at **)');
38 }
39
@@ -107,68 +101,6 @@ describe('ReactComponentTreeHook', () => {
101 // Make sure owner is fetched for the top element too.
102 // expectDev(getAddendum(rOwnedByQ)).toBe('\n in R (created by Q)');
103 });
110 -
111 - // These are features and regression tests that only affect
112 - // the Stack implementation of the stack addendum.
113 - if (!ReactDOMFeatureFlags.useFiber) {
114 - it('can be retrieved by ID', () => {
115 - function getAddendum(id) {
116 - var addendum = ReactComponentTreeHook.getStackAddendumByID(id);
117 - return addendum.replace(/\(at .+?:\d+\)/g, '(at **)');
118 - }
119 -
120 - class Q extends React.Component {
121 - render() {
122 - return null;
123 - }
124 - }
125 -
126 - var q = ReactDOM.render(<Q />, document.createElement('div'));
127 - expectDev(getAddendum(ReactInstanceMap.get(q)._debugID)).toBe(
128 - '\n in Q (at **)',
129 - );
130 -
131 - spyOn(console, 'error');
132 - getAddendum(-17);
133 - expectDev(console.error.calls.count()).toBe(1);
134 - expectDev(console.error.calls.argsFor(0)[0]).toBe(
135 - 'Warning: ReactComponentTreeHook: Missing React element for ' +
136 - 'debugID -17 when building stack',
137 - );
138 - });
139 -
140 - it('is created during mounting', () => {
141 - // https://github.com/facebook/react/issues/7187
142 - var el = document.createElement('div');
143 - var portalEl = document.createElement('div');
144 - class Foo extends React.Component {
145 - componentWillMount() {
146 - ReactDOM.render(<div />, portalEl);
147 - }
148 - render() {
149 - return <div><div /></div>;
150 - }
151 - }
152 - ReactDOM.render(<Foo />, el);
153 - });
154 -
155 - it('is created when calling renderToString during render', () => {
156 - // https://github.com/facebook/react/issues/7190
157 - var el = document.createElement('div');
158 - class Foo extends React.Component {
159 - render() {
160 - return (
161 - <div>
162 - <div>
163 - {ReactDOMServer.renderToString(<div />)}
164 - </div>
165 - </div>
166 - );
167 - }
168 - }
169 - ReactDOM.render(<Foo />, el);
170 - });
171 - }
104 });
105
106 // The rest of this file is not relevant for Fiber.
@@ -227,7 +159,7 @@ describe('ReactComponentTreeHook', () => {
159 expectWrapperTreeToEqual(null);
160 }
161
230 - describeStack('mount', () => {
162 + describe('mount', () => {
163 it('uses displayName or Unknown for classic components', () => {
164 class Foo extends React.Component {
165 render() {
@@ -699,7 +631,7 @@ describe('ReactComponentTreeHook', () => {
631 });
632 });
633
702 - describeStack('update', () => {
634 + describe('update', () => {
635 describe('host component', () => {
636 it('updates text of a single text child', () => {
637 var elementBefore = <div>Hi.</div>;
@@ -1919,7 +1851,7 @@ describe('ReactComponentTreeHook', () => {
1851 });
1852 });
1853
1922 - describeStack('misc', () => {
1854 + describe('misc', () => {
1855 it('tracks owner correctly', () => {
1856 class Foo extends React.Component {
1857 render() {
@@ -2089,7 +2021,7 @@ describe('ReactComponentTreeHook', () => {
2021 });
2022 });
2023
2092 - describeStack('in environment without Map, Set and Array.from', () => {
2024 + describe('in environment without Map, Set and Array.from', () => {
2025 var realMap;
2026 var realSet;
2027 var realArrayFrom;
@@ -2107,7 +2039,6 @@ describe('ReactComponentTreeHook', () => {
2039
2040 React = require('react');
2041 ReactDOM = require('react-dom');
2110 - ReactDOMServer = require('react-dom/server');
2042 ReactInstanceMap = require('ReactInstanceMap');
2043 ReactComponentTreeHook = require('ReactComponentTreeHook');
2044 ReactComponentTreeTestUtils = require('ReactComponentTreeTestUtils');
src/renderers/__tests__/ReactCompositeComponent-test.js
+10 -18
@@ -13,7 +13,6 @@ var ChildUpdates;
13 var MorphingComponent;
14 var React;
15 var ReactDOM;
16 -var ReactDOMFeatureFlags;
16 var ReactDOMServer;
17 var ReactCurrentOwner;
18 var ReactTestUtils;
@@ -26,7 +25,6 @@ describe('ReactCompositeComponent', () => {
25 jest.resetModules();
26 React = require('react');
27 ReactDOM = require('react-dom');
29 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
28 ReactDOMServer = require('react-dom/server');
29 ReactCurrentOwner = require('react')
30 .__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner;
@@ -123,25 +121,19 @@ describe('ReactCompositeComponent', () => {
121 var container = document.createElement('div');
122 container.innerHTML = markup;
123 ReactDOM.render(<Parent />, container);
126 - if (ReactDOMFeatureFlags.useFiber) {
127 - expectDev(console.warn.calls.count()).toBe(1);
128 - expectDev(console.warn.calls.argsFor(0)[0]).toContain(
129 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
130 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
131 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
132 - );
133 - } else {
134 - expectDev(console.warn.calls.count()).toBe(0);
135 - }
124 + expectDev(console.warn.calls.count()).toBe(1);
125 + expectDev(console.warn.calls.argsFor(0)[0]).toContain(
126 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
127 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
128 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
129 + );
130
131 // New explicit API
132 console.warn.calls.reset();
139 - if (ReactDOMFeatureFlags.useFiber) {
140 - container = document.createElement('div');
141 - container.innerHTML = markup;
142 - ReactDOM.hydrate(<Parent />, container);
143 - expectDev(console.warn.calls.count()).toBe(0);
144 - }
133 + container = document.createElement('div');
134 + container.innerHTML = markup;
135 + ReactDOM.hydrate(<Parent />, container);
136 + expectDev(console.warn.calls.count()).toBe(0);
137 });
138
139 it('should react to state changes from callbacks', () => {
src/renderers/__tests__/ReactCompositeComponentState-test.js
+39 -63
@@ -11,7 +11,6 @@
11
12 var React;
13 var ReactDOM;
14 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
14
15 var TestComponent;
16
@@ -179,17 +178,9 @@ describe('ReactCompositeComponent-state', () => {
178 // setState({color:'green'}) only enqueues a pending state.
179 ['componentWillReceiveProps-end', 'yellow'],
180 // pending state queue is processed
182 - ];
183 -
184 - if (ReactDOMFeatureFlags.useFiber) {
185 - // In Stack, this is never called because replaceState drops all updates
186 - // from the queue. In Fiber, we keep updates in the queue to support
181 + // We keep updates in the queue to support
182 // replaceState(prevState => newState).
188 - // TODO: Fix Stack to match Fiber.
189 - expected.push(['before-setState-receiveProps', 'yellow']);
190 - }
191 -
192 - expected.push(
183 + ['before-setState-receiveProps', 'yellow'],
184 ['before-setState-again-receiveProps', undefined],
185 ['after-setState-receiveProps', 'green'],
186 ['shouldComponentUpdate-currentState', 'yellow'],
@@ -220,7 +211,7 @@ describe('ReactCompositeComponent-state', () => {
211 // unmountComponent()
212 // state is available within `componentWillUnmount()`
213 ['componentWillUnmount', 'blue'],
223 - );
214 + ];
215
216 expect(stateListener.mock.calls.join('\n')).toEqual(expected.join('\n'));
217 });
@@ -289,20 +280,7 @@ describe('ReactCompositeComponent-state', () => {
280 child.setState({bar: false});
281 });
282 // We expect the same thing to happen if we bail out in the middle.
292 - expect(ops).toEqual(
293 - ReactDOMFeatureFlags.useFiber
294 - ? [
295 - // Fiber works as expected
296 - 'child did update',
297 - 'parent did update',
298 - ]
299 - : [
300 - // Stack treats these as two separate updates and therefore the order
301 - // is inverse.
302 - 'parent did update',
303 - 'child did update',
304 - ],
305 - );
283 + expect(ops).toEqual(['child did update', 'parent did update']);
284 });
285
286 it('should batch unmounts', () => {
@@ -445,45 +423,43 @@ describe('ReactCompositeComponent-state', () => {
423 );
424 });
425
448 - if (ReactDOMFeatureFlags.useFiber) {
449 - it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => {
450 - spyOn(console, 'error');
451 -
452 - let ops = [];
453 - class Test extends React.Component {
454 - state = {step: 1, extra: true};
455 - componentWillMount() {
456 - this.setState({step: 2}, () => {
457 - // Tests that earlier setState callbacks are not dropped
458 - ops.push(
459 - `callback -- step: ${this.state.step}, extra: ${!!this.state.extra}`,
460 - );
461 - });
462 - // Treat like replaceState
463 - this.state = {step: 3};
464 - }
465 - render() {
426 + it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => {
427 + spyOn(console, 'error');
428 +
429 + let ops = [];
430 + class Test extends React.Component {
431 + state = {step: 1, extra: true};
432 + componentWillMount() {
433 + this.setState({step: 2}, () => {
434 + // Tests that earlier setState callbacks are not dropped
435 ops.push(
467 - `render -- step: ${this.state.step}, extra: ${!!this.state.extra}`,
436 + `callback -- step: ${this.state.step}, extra: ${!!this.state.extra}`,
437 );
469 - return null;
470 - }
438 + });
439 + // Treat like replaceState
440 + this.state = {step: 3};
441 }
442 + render() {
443 + ops.push(
444 + `render -- step: ${this.state.step}, extra: ${!!this.state.extra}`,
445 + );
446 + return null;
447 + }
448 + }
449
473 - // Mount
474 - const container = document.createElement('div');
475 - ReactDOM.render(<Test />, container);
476 -
477 - expect(ops).toEqual([
478 - 'render -- step: 3, extra: false',
479 - 'callback -- step: 3, extra: false',
480 - ]);
481 - expect(console.error.calls.count()).toEqual(1);
482 - expect(console.error.calls.argsFor(0)[0]).toEqual(
483 - 'Warning: Test.componentWillMount(): Assigning directly to ' +
484 - "this.state is deprecated (except inside a component's constructor). " +
485 - 'Use setState instead.',
486 - );
487 - });
488 - }
450 + // Mount
451 + const container = document.createElement('div');
452 + ReactDOM.render(<Test />, container);
453 +
454 + expect(ops).toEqual([
455 + 'render -- step: 3, extra: false',
456 + 'callback -- step: 3, extra: false',
457 + ]);
458 + expect(console.error.calls.count()).toEqual(1);
459 + expect(console.error.calls.argsFor(0)[0]).toEqual(
460 + 'Warning: Test.componentWillMount(): Assigning directly to ' +
461 + "this.state is deprecated (except inside a component's constructor). " +
462 + 'Use setState instead.',
463 + );
464 + });
465 });
src/renderers/__tests__/ReactEmptyComponent-test.js
+15 -36
@@ -14,8 +14,6 @@ var ReactDOM;
14 var ReactTestUtils;
15 var TogglingComponent;
16
17 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
18 -
17 var log;
18
19 describe('ReactEmptyComponent', () => {
@@ -69,20 +67,18 @@ describe('ReactEmptyComponent', () => {
67 expect(container2.children.length).toBe(0);
68 });
69
72 - if (ReactDOMFeatureFlags.useFiber) {
73 - it('should still throw when rendering to undefined', () => {
74 - class Component extends React.Component {
75 - render() {}
76 - }
70 + it('should still throw when rendering to undefined', () => {
71 + class Component extends React.Component {
72 + render() {}
73 + }
74
78 - expect(function() {
79 - ReactTestUtils.renderIntoDocument(<Component />);
80 - }).toThrowError(
81 - 'Component(...): Nothing was returned from render. This usually means a return statement is missing. ' +
82 - 'Or, to render nothing, return null.',
83 - );
84 - });
85 - }
75 + expect(function() {
76 + ReactTestUtils.renderIntoDocument(<Component />);
77 + }).toThrowError(
78 + 'Component(...): Nothing was returned from render. This usually means a return statement is missing. ' +
79 + 'Or, to render nothing, return null.',
80 + );
81 + });
82
83 it('should be able to switch between rendering null and a normal tag', () => {
84 var instance1 = (
@@ -233,15 +229,8 @@ describe('ReactEmptyComponent', () => {
229
230 it('can render null at the top level', () => {
231 var div = document.createElement('div');
236 - if (ReactDOMFeatureFlags.useFiber) {
237 - ReactDOM.render(null, div);
238 - expect(div.innerHTML).toBe('');
239 - } else {
240 - // Stack does not implement this.
241 - expect(function() {
242 - ReactDOM.render(null, div);
243 - }).toThrowError('ReactDOM.render(): Invalid component element.');
244 - }
232 + ReactDOM.render(null, div);
233 + expect(div.innerHTML).toBe('');
234 });
235
236 it('does not break when updating during mount', () => {
@@ -293,21 +282,11 @@ describe('ReactEmptyComponent', () => {
282
283 ReactDOM.render(<Empty />, container);
284 var noscript1 = container.firstChild;
296 - if (ReactDOMFeatureFlags.useFiber) {
297 - expect(noscript1).toBe(null);
298 - } else {
299 - expect(noscript1.nodeName).toBe('#comment');
300 - }
285 + expect(noscript1).toBe(null);
286
287 // This update shouldn't create a DOM node
288 ReactDOM.render(<Empty />, container);
289 var noscript2 = container.firstChild;
305 - if (ReactDOMFeatureFlags.useFiber) {
306 - expect(noscript1).toBe(null);
307 - } else {
308 - expect(noscript2.nodeName).toBe('#comment');
309 - }
310 -
311 - expect(noscript1).toBe(noscript2);
290 + expect(noscript2).toBe(null);
291 });
292 });
src/renderers/__tests__/ReactErrorBoundaries-test.js
+536 -753
@@ -9,8 +9,6 @@
9
10 'use strict';
11
12 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 -
12 var PropTypes;
13 var React;
14 var ReactDOM;
@@ -399,12 +397,7 @@ describe('ReactErrorBoundaries', () => {
397 log.push('NoopErrorBoundary componentWillUnmount');
398 }
399 componentDidCatch() {
402 - if (ReactDOMFeatureFlags.useFiber) {
403 - log.push('NoopErrorBoundary componentDidCatch');
404 - } else {
405 - // In Stack, not calling setState() is treated as a rethrow.
406 - log.push('NoopErrorBoundary componentDidCatch [*]');
407 - }
400 + log.push('NoopErrorBoundary componentDidCatch');
401 }
402 };
403
@@ -507,14 +500,9 @@ describe('ReactErrorBoundaries', () => {
500 log.push('RetryErrorBoundary componentWillUnmount');
501 }
502 componentDidCatch(error) {
510 - if (ReactDOMFeatureFlags.useFiber) {
511 - log.push('RetryErrorBoundary componentDidCatch [!]');
512 - // In Fiber, calling setState() (and failing) is treated as a rethrow.
513 - this.setState({});
514 - } else {
515 - log.push('RetryErrorBoundary componentDidCatch [*]');
516 - // In Stack, not calling setState() is treated as a rethrow.
517 - }
503 + log.push('RetryErrorBoundary componentDidCatch [!]');
504 + // In Fiber, calling setState() (and failing) is treated as a rethrow.
505 + this.setState({});
506 }
507 };
508
@@ -639,22 +627,13 @@ describe('ReactErrorBoundaries', () => {
627 'BrokenRender constructor',
628 'BrokenRender componentWillMount',
629 'BrokenRender render [!]',
642 - ...(ReactDOMFeatureFlags.useFiber
643 - ? [
644 - // Fiber mounts with null children before capturing error
645 - 'ErrorBoundary componentDidMount',
646 - // Catch and render an error message
647 - 'ErrorBoundary componentDidCatch',
648 - 'ErrorBoundary componentWillUpdate',
649 - 'ErrorBoundary render error',
650 - 'ErrorBoundary componentDidUpdate',
651 - ]
652 - : [
653 - // Catch and render an error message
654 - 'ErrorBoundary componentDidCatch',
655 - 'ErrorBoundary render error',
656 - 'ErrorBoundary componentDidMount',
657 - ]),
630 + // Fiber mounts with null children before capturing error
631 + 'ErrorBoundary componentDidMount',
632 + // Catch and render an error message
633 + 'ErrorBoundary componentDidCatch',
634 + 'ErrorBoundary componentWillUpdate',
635 + 'ErrorBoundary render error',
636 + 'ErrorBoundary componentDidUpdate',
637 ]);
638
639 log.length = 0;
@@ -676,22 +655,13 @@ describe('ReactErrorBoundaries', () => {
655 'ErrorBoundary componentWillMount',
656 'ErrorBoundary render success',
657 'BrokenConstructor constructor [!]',
679 - ...(ReactDOMFeatureFlags.useFiber
680 - ? [
681 - // Fiber mounts with null children before capturing error
682 - 'ErrorBoundary componentDidMount',
683 - // Catch and render an error message
684 - 'ErrorBoundary componentDidCatch',
685 - 'ErrorBoundary componentWillUpdate',
686 - 'ErrorBoundary render error',
687 - 'ErrorBoundary componentDidUpdate',
688 - ]
689 - : [
690 - // Catch and render an error message
691 - 'ErrorBoundary componentDidCatch',
692 - 'ErrorBoundary render error',
693 - 'ErrorBoundary componentDidMount',
694 - ]),
658 + // Fiber mounts with null children before capturing error
659 + 'ErrorBoundary componentDidMount',
660 + // Catch and render an error message
661 + 'ErrorBoundary componentDidCatch',
662 + 'ErrorBoundary componentWillUpdate',
663 + 'ErrorBoundary render error',
664 + 'ErrorBoundary componentDidUpdate',
665 ]);
666
667 log.length = 0;
@@ -714,20 +684,11 @@ describe('ReactErrorBoundaries', () => {
684 'ErrorBoundary render success',
685 'BrokenComponentWillMount constructor',
686 'BrokenComponentWillMount componentWillMount [!]',
717 - ...(ReactDOMFeatureFlags.useFiber
718 - ? [
719 - 'ErrorBoundary componentDidMount',
720 - 'ErrorBoundary componentDidCatch',
721 - 'ErrorBoundary componentWillUpdate',
722 - 'ErrorBoundary render error',
723 - 'ErrorBoundary componentDidUpdate',
724 - ]
725 - : [
726 - // Catch and render an error message
727 - 'ErrorBoundary componentDidCatch',
728 - 'ErrorBoundary render error',
729 - 'ErrorBoundary componentDidMount',
730 - ]),
687 + 'ErrorBoundary componentDidMount',
688 + 'ErrorBoundary componentDidCatch',
689 + 'ErrorBoundary componentWillUpdate',
690 + 'ErrorBoundary render error',
691 + 'ErrorBoundary componentDidUpdate',
692 ]);
693
694 log.length = 0;
@@ -806,29 +767,15 @@ describe('ReactErrorBoundaries', () => {
767 'BrokenRender constructor',
768 'BrokenRender componentWillMount',
769 'BrokenRender render [!]',
809 - ...(ReactDOMFeatureFlags.useFiber
810 - ? [
811 - 'ErrorBoundary componentDidMount',
812 - 'ErrorBoundary componentDidCatch',
813 - 'ErrorBoundary componentWillUpdate',
814 - 'ErrorBoundary render error',
815 - 'ErrorMessage constructor',
816 - 'ErrorMessage componentWillMount',
817 - 'ErrorMessage render',
818 - 'ErrorMessage componentDidMount',
819 - 'ErrorBoundary componentDidUpdate',
820 - ]
821 - : [
822 - // Handle the error:
823 - 'ErrorBoundary componentDidCatch',
824 - 'ErrorBoundary render error',
825 - // Mount the error message:
826 - 'ErrorMessage constructor',
827 - 'ErrorMessage componentWillMount',
828 - 'ErrorMessage render',
829 - 'ErrorMessage componentDidMount',
830 - 'ErrorBoundary componentDidMount',
831 - ]),
770 + 'ErrorBoundary componentDidMount',
771 + 'ErrorBoundary componentDidCatch',
772 + 'ErrorBoundary componentWillUpdate',
773 + 'ErrorBoundary render error',
774 + 'ErrorMessage constructor',
775 + 'ErrorMessage componentWillMount',
776 + 'ErrorMessage render',
777 + 'ErrorMessage componentDidMount',
778 + 'ErrorBoundary componentDidUpdate',
779 ]);
780
781 log.length = 0;
@@ -860,39 +807,22 @@ describe('ReactErrorBoundaries', () => {
807 'BrokenRender constructor',
808 'BrokenRender componentWillMount',
809 'BrokenRender render [!]',
863 - ...(ReactDOMFeatureFlags.useFiber
864 - ? [
865 - // In Fiber, failed error boundaries render null before attempting to recover
866 - 'RetryErrorBoundary componentDidMount',
867 - 'RetryErrorBoundary componentDidCatch [!]',
868 - 'ErrorBoundary componentDidMount',
869 - // Retry
870 - 'RetryErrorBoundary render',
871 - 'BrokenRender constructor',
872 - 'BrokenRender componentWillMount',
873 - 'BrokenRender render [!]',
874 - // This time, the error propagates to the higher boundary
875 - 'RetryErrorBoundary componentWillUnmount',
876 - 'ErrorBoundary componentDidCatch',
877 - // Render the error
878 - 'ErrorBoundary componentWillUpdate',
879 - 'ErrorBoundary render error',
880 - 'ErrorBoundary componentDidUpdate',
881 - ]
882 - : [
883 - // The first error boundary catches the error.
884 - // However, it doesn't adjust its state so next render will also fail.
885 - 'RetryErrorBoundary componentDidCatch [*]',
886 - 'RetryErrorBoundary render',
887 - 'BrokenRender constructor',
888 - 'BrokenRender componentWillMount',
889 - 'BrokenRender render [!]',
890 - // This time, the error propagates to the higher boundary
891 - 'ErrorBoundary componentDidCatch',
892 - // Render the error
893 - 'ErrorBoundary render error',
894 - 'ErrorBoundary componentDidMount',
895 - ]),
810 + // In Fiber, failed error boundaries render null before attempting to recover
811 + 'RetryErrorBoundary componentDidMount',
812 + 'RetryErrorBoundary componentDidCatch [!]',
813 + 'ErrorBoundary componentDidMount',
814 + // Retry
815 + 'RetryErrorBoundary render',
816 + 'BrokenRender constructor',
817 + 'BrokenRender componentWillMount',
818 + 'BrokenRender render [!]',
819 + // This time, the error propagates to the higher boundary
820 + 'RetryErrorBoundary componentWillUnmount',
821 + 'ErrorBoundary componentDidCatch',
822 + // Render the error
823 + 'ErrorBoundary componentWillUpdate',
824 + 'ErrorBoundary render error',
825 + 'ErrorBoundary componentDidUpdate',
826 ]);
827
828 log.length = 0;
@@ -916,20 +846,11 @@ describe('ReactErrorBoundaries', () => {
846 'BrokenComponentWillMountErrorBoundary constructor',
847 'BrokenComponentWillMountErrorBoundary componentWillMount [!]',
848 // The error propagates to the higher boundary
919 - ...(ReactDOMFeatureFlags.useFiber
920 - ? [
921 - 'ErrorBoundary componentDidMount',
922 - 'ErrorBoundary componentDidCatch',
923 - 'ErrorBoundary componentWillUpdate',
924 - 'ErrorBoundary render error',
925 - 'ErrorBoundary componentDidUpdate',
926 - ]
927 - : [
928 - 'ErrorBoundary componentDidCatch',
929 - // Render the error
930 - 'ErrorBoundary render error',
931 - 'ErrorBoundary componentDidMount',
932 - ]),
849 + 'ErrorBoundary componentDidMount',
850 + 'ErrorBoundary componentDidCatch',
851 + 'ErrorBoundary componentWillUpdate',
852 + 'ErrorBoundary render error',
853 + 'ErrorBoundary componentDidUpdate',
854 ]);
855
856 log.length = 0;
@@ -960,31 +881,19 @@ describe('ReactErrorBoundaries', () => {
881 'BrokenRender render [!]',
882 // The first error boundary catches the error
883 // It adjusts state but throws displaying the message
963 - ...(ReactDOMFeatureFlags.useFiber
964 - ? [
965 - // Finish mounting with null children
966 - 'BrokenRenderErrorBoundary componentDidMount',
967 - // Attempt to handle the error
968 - 'BrokenRenderErrorBoundary componentDidCatch',
969 - 'ErrorBoundary componentDidMount',
970 - 'BrokenRenderErrorBoundary render error [!]',
971 - // Boundary fails with new error, propagate to next boundary
972 - 'BrokenRenderErrorBoundary componentWillUnmount',
973 - // Attempt to handle the error again
974 - 'ErrorBoundary componentDidCatch',
975 - 'ErrorBoundary componentWillUpdate',
976 - 'ErrorBoundary render error',
977 - 'ErrorBoundary componentDidUpdate',
978 - ]
979 - : [
980 - 'BrokenRenderErrorBoundary componentDidCatch',
981 - 'BrokenRenderErrorBoundary render error [!]',
982 - // The error propagates to the higher boundary
983 - 'ErrorBoundary componentDidCatch',
984 - // Render the error
985 - 'ErrorBoundary render error',
986 - 'ErrorBoundary componentDidMount',
987 - ]),
884 + // Finish mounting with null children
885 + 'BrokenRenderErrorBoundary componentDidMount',
886 + // Attempt to handle the error
887 + 'BrokenRenderErrorBoundary componentDidCatch',
888 + 'ErrorBoundary componentDidMount',
889 + 'BrokenRenderErrorBoundary render error [!]',
890 + // Boundary fails with new error, propagate to next boundary
891 + 'BrokenRenderErrorBoundary componentWillUnmount',
892 + // Attempt to handle the error again
893 + 'ErrorBoundary componentDidCatch',
894 + 'ErrorBoundary componentWillUpdate',
895 + 'ErrorBoundary render error',
896 + 'ErrorBoundary componentDidUpdate',
897 ]);
898
899 log.length = 0;
@@ -1015,23 +924,14 @@ describe('ReactErrorBoundaries', () => {
924 'BrokenRender constructor',
925 'BrokenRender componentWillMount',
926 'BrokenRender render [!]',
1018 - ...(ReactDOMFeatureFlags.useFiber
1019 - ? [
1020 - // Finish mounting with null children
1021 - 'ErrorBoundary componentDidMount',
1022 - // Handle the error
1023 - 'ErrorBoundary componentDidCatch',
1024 - // Render the error message
1025 - 'ErrorBoundary componentWillUpdate',
1026 - 'ErrorBoundary render error',
1027 - 'ErrorBoundary componentDidUpdate',
1028 - ]
1029 - : [
1030 - 'ErrorBoundary componentDidCatch',
1031 - // Render the error message
1032 - 'ErrorBoundary render error',
1033 - 'ErrorBoundary componentDidMount',
1034 - ]),
927 + // Finish mounting with null children
928 + 'ErrorBoundary componentDidMount',
929 + // Handle the error
930 + 'ErrorBoundary componentDidCatch',
931 + // Render the error message
932 + 'ErrorBoundary componentWillUpdate',
933 + 'ErrorBoundary render error',
934 + 'ErrorBoundary componentDidUpdate',
935 ]);
936
937 log.length = 0;
@@ -1064,28 +964,15 @@ describe('ReactErrorBoundaries', () => {
964 'BrokenRender componentWillMount',
965 'BrokenRender render [!]',
966 // Handle error:
1067 - ...(ReactDOMFeatureFlags.useFiber
1068 - ? [
1069 - // Finish mounting with null children
1070 - 'ErrorBoundary componentDidMount',
1071 - // Handle the error
1072 - 'ErrorBoundary componentDidCatch',
1073 - // Render the error message
1074 - 'ErrorBoundary componentWillUpdate',
1075 - 'ErrorBoundary render error',
1076 - 'Error message ref is set to [object HTMLDivElement]',
1077 - 'ErrorBoundary componentDidUpdate',
1078 - ]
1079 - : [
1080 - 'ErrorBoundary componentDidCatch',
1081 - // Stack reconciler resets ref on update, as it doesn't know ref was never set.
1082 - // This is unnecessary, and Fiber doesn't do it:
1083 - 'Child ref is set to null',
1084 - 'ErrorBoundary render error',
1085 - // Ref to error message should get set:
1086 - 'Error message ref is set to [object HTMLDivElement]',
1087 - 'ErrorBoundary componentDidMount',
1088 - ]),
967 + // Finish mounting with null children
968 + 'ErrorBoundary componentDidMount',
969 + // Handle the error
970 + 'ErrorBoundary componentDidCatch',
971 + // Render the error message
972 + 'ErrorBoundary componentWillUpdate',
973 + 'ErrorBoundary render error',
974 + 'Error message ref is set to [object HTMLDivElement]',
975 + 'ErrorBoundary componentDidUpdate',
976 ]);
977
978 log.length = 0;
@@ -1149,26 +1036,15 @@ describe('ReactErrorBoundaries', () => {
1036 'Normal2 render',
1037 // BrokenConstructor will abort rendering:
1038 'BrokenConstructor constructor [!]',
1152 - ...(ReactDOMFeatureFlags.useFiber
1153 - ? [
1154 - // Finish updating with null children
1155 - 'Normal componentWillUnmount',
1156 - 'ErrorBoundary componentDidUpdate',
1157 - // Handle the error
1158 - 'ErrorBoundary componentDidCatch',
1159 - // Render the error message
1160 - 'ErrorBoundary componentWillUpdate',
1161 - 'ErrorBoundary render error',
1162 - 'ErrorBoundary componentDidUpdate',
1163 - ]
1164 - : [
1165 - 'ErrorBoundary componentDidCatch',
1166 - // Stack unmounts first, then renders:
1167 - 'Normal componentWillUnmount',
1168 - 'ErrorBoundary render error',
1169 - // Normal2 does not get lifefycle because it was never mounted
1170 - 'ErrorBoundary componentDidUpdate',
1171 - ]),
1039 + // Finish updating with null children
1040 + 'Normal componentWillUnmount',
1041 + 'ErrorBoundary componentDidUpdate',
1042 + // Handle the error
1043 + 'ErrorBoundary componentDidCatch',
1044 + // Render the error message
1045 + 'ErrorBoundary componentWillUpdate',
1046 + 'ErrorBoundary render error',
1047 + 'ErrorBoundary componentDidUpdate',
1048 ]);
1049
1050 log.length = 0;
@@ -1209,26 +1085,15 @@ describe('ReactErrorBoundaries', () => {
1085 // BrokenComponentWillMount will abort rendering:
1086 'BrokenComponentWillMount constructor',
1087 'BrokenComponentWillMount componentWillMount [!]',
1212 - ...(ReactDOMFeatureFlags.useFiber
1213 - ? [
1214 - // Finish updating with null children
1215 - 'Normal componentWillUnmount',
1216 - 'ErrorBoundary componentDidUpdate',
1217 - // Handle the error
1218 - 'ErrorBoundary componentDidCatch',
1219 - // Render the error message
1220 - 'ErrorBoundary componentWillUpdate',
1221 - 'ErrorBoundary render error',
1222 - 'ErrorBoundary componentDidUpdate',
1223 - ]
1224 - : [
1225 - 'ErrorBoundary componentDidCatch',
1226 - // Stack unmounts first, then renders:
1227 - 'Normal componentWillUnmount',
1228 - 'ErrorBoundary render error',
1229 - // Normal2 does not get lifefycle because it was never mounted
1230 - 'ErrorBoundary componentDidUpdate',
1231 - ]),
1088 + // Finish updating with null children
1089 + 'Normal componentWillUnmount',
1090 + 'ErrorBoundary componentDidUpdate',
1091 + // Handle the error
1092 + 'ErrorBoundary componentDidCatch',
1093 + // Render the error message
1094 + 'ErrorBoundary componentWillUpdate',
1095 + 'ErrorBoundary render error',
1096 + 'ErrorBoundary componentDidUpdate',
1097 ]);
1098
1099 log.length = 0;
@@ -1264,26 +1129,15 @@ describe('ReactErrorBoundaries', () => {
1129 'Normal render',
1130 // BrokenComponentWillReceiveProps will abort rendering:
1131 'BrokenComponentWillReceiveProps componentWillReceiveProps [!]',
1267 - ...(ReactDOMFeatureFlags.useFiber
1268 - ? [
1269 - // Finish updating with null children
1270 - 'Normal componentWillUnmount',
1271 - 'BrokenComponentWillReceiveProps componentWillUnmount',
1272 - 'ErrorBoundary componentDidUpdate',
1273 - // Handle the error
1274 - 'ErrorBoundary componentDidCatch',
1275 - 'ErrorBoundary componentWillUpdate',
1276 - 'ErrorBoundary render error',
1277 - 'ErrorBoundary componentDidUpdate',
1278 - ]
1279 - : [
1280 - 'ErrorBoundary componentDidCatch',
1281 - // Stack unmounts first, then renders:
1282 - 'Normal componentWillUnmount',
1283 - 'BrokenComponentWillReceiveProps componentWillUnmount',
1284 - 'ErrorBoundary render error',
1285 - 'ErrorBoundary componentDidUpdate',
1286 - ]),
1132 + // Finish updating with null children
1133 + 'Normal componentWillUnmount',
1134 + 'BrokenComponentWillReceiveProps componentWillUnmount',
1135 + 'ErrorBoundary componentDidUpdate',
1136 + // Handle the error
1137 + 'ErrorBoundary componentDidCatch',
1138 + 'ErrorBoundary componentWillUpdate',
1139 + 'ErrorBoundary render error',
1140 + 'ErrorBoundary componentDidUpdate',
1141 ]);
1142
1143 log.length = 0;
@@ -1320,26 +1174,15 @@ describe('ReactErrorBoundaries', () => {
1174 // BrokenComponentWillUpdate will abort rendering:
1175 'BrokenComponentWillUpdate componentWillReceiveProps',
1176 'BrokenComponentWillUpdate componentWillUpdate [!]',
1323 - ...(ReactDOMFeatureFlags.useFiber
1324 - ? [
1325 - // Finish updating with null children
1326 - 'Normal componentWillUnmount',
1327 - 'BrokenComponentWillUpdate componentWillUnmount',
1328 - 'ErrorBoundary componentDidUpdate',
1329 - // Handle the error
1330 - 'ErrorBoundary componentDidCatch',
1331 - 'ErrorBoundary componentWillUpdate',
1332 - 'ErrorBoundary render error',
1333 - 'ErrorBoundary componentDidUpdate',
1334 - ]
1335 - : [
1336 - 'ErrorBoundary componentDidCatch',
1337 - // Stack unmounts first, then renders:
1338 - 'Normal componentWillUnmount',
1339 - 'BrokenComponentWillUpdate componentWillUnmount',
1340 - 'ErrorBoundary render error',
1341 - 'ErrorBoundary componentDidUpdate',
1342 - ]),
1177 + // Finish updating with null children
1178 + 'Normal componentWillUnmount',
1179 + 'BrokenComponentWillUpdate componentWillUnmount',
1180 + 'ErrorBoundary componentDidUpdate',
1181 + // Handle the error
1182 + 'ErrorBoundary componentDidCatch',
1183 + 'ErrorBoundary componentWillUpdate',
1184 + 'ErrorBoundary render error',
1185 + 'ErrorBoundary componentDidUpdate',
1186 ]);
1187
1188 log.length = 0;
@@ -1381,25 +1224,14 @@ describe('ReactErrorBoundaries', () => {
1224 'BrokenRender constructor',
1225 'BrokenRender componentWillMount',
1226 'BrokenRender render [!]',
1384 - ...(ReactDOMFeatureFlags.useFiber
1385 - ? [
1386 - // Finish updating with null children
1387 - 'Normal componentWillUnmount',
1388 - 'ErrorBoundary componentDidUpdate',
1389 - // Handle the error
1390 - 'ErrorBoundary componentDidCatch',
1391 - 'ErrorBoundary componentWillUpdate',
1392 - 'ErrorBoundary render error',
1393 - 'ErrorBoundary componentDidUpdate',
1394 - ]
1395 - : [
1396 - 'ErrorBoundary componentDidCatch',
1397 - // Stack unmounts first, then renders:
1398 - 'Normal componentWillUnmount',
1399 - 'ErrorBoundary render error',
1400 - // Normal2 does not get lifefycle because it was never mounted
1401 - 'ErrorBoundary componentDidUpdate',
1402 - ]),
1227 + // Finish updating with null children
1228 + 'Normal componentWillUnmount',
1229 + 'ErrorBoundary componentDidUpdate',
1230 + // Handle the error
1231 + 'ErrorBoundary componentDidCatch',
1232 + 'ErrorBoundary componentWillUpdate',
1233 + 'ErrorBoundary render error',
1234 + 'ErrorBoundary componentDidUpdate',
1235 ]);
1236
1237 log.length = 0;
@@ -1451,22 +1283,13 @@ describe('ReactErrorBoundaries', () => {
1283 'BrokenRender constructor',
1284 'BrokenRender componentWillMount',
1285 'BrokenRender render [!]',
1454 - ...(ReactDOMFeatureFlags.useFiber
1455 - ? [
1456 - // Finish updating with null children
1457 - 'Child1 ref is set to null',
1458 - 'ErrorBoundary componentDidUpdate',
1459 - // Handle the error
1460 - 'ErrorBoundary componentDidCatch',
1461 - 'ErrorBoundary componentWillUpdate',
1462 - 'ErrorBoundary render error',
1463 - ]
1464 - : [
1465 - 'ErrorBoundary componentDidCatch',
1466 - // Stack resets ref first, renders later
1467 - 'Child1 ref is set to null',
1468 - 'ErrorBoundary render error',
1469 - ]),
1286 + // Finish updating with null children
1287 + 'Child1 ref is set to null',
1288 + 'ErrorBoundary componentDidUpdate',
1289 + // Handle the error
1290 + 'ErrorBoundary componentDidCatch',
1291 + 'ErrorBoundary componentWillUpdate',
1292 + 'ErrorBoundary render error',
1293 'Error message ref is set to [object HTMLDivElement]',
1294 // Child2 ref is never set because its mounting aborted
1295 'ErrorBoundary componentDidUpdate',
@@ -1509,35 +1332,22 @@ describe('ReactErrorBoundaries', () => {
1332 'BrokenComponentWillUnmount render',
1333 // Unmounting throws:
1334 'BrokenComponentWillUnmount componentWillUnmount [!]',
1512 - ...(ReactDOMFeatureFlags.useFiber
1513 - ? [
1514 - // Fiber proceeds with lifecycles despite errors
1515 - 'Normal componentWillUnmount',
1516 - // The components have updated in this phase
1517 - 'BrokenComponentWillUnmount componentDidUpdate',
1518 - 'ErrorBoundary componentDidUpdate',
1519 - // Now that commit phase is done, Fiber unmounts the boundary's children
1520 - 'BrokenComponentWillUnmount componentWillUnmount [!]',
1521 - 'ErrorBoundary componentDidCatch',
1522 - // The initial render was aborted, so
1523 - // Fiber retries from the root.
1524 - 'ErrorBoundary componentWillUpdate',
1525 - // Render an error now (stack will do it later)
1526 - 'ErrorBoundary render error',
1527 - // Attempt to unmount previous child:
1528 - // Done
1529 - 'ErrorBoundary componentDidUpdate',
1530 - ]
1531 - : [
1532 - // Stack will handle error immediately
1533 - 'ErrorBoundary componentDidCatch',
1534 - // Attempt to unmount previous children:
1535 - 'BrokenComponentWillUnmount componentWillUnmount [!]',
1536 - 'Normal componentWillUnmount',
1537 - // Render an error now (Fiber will do it earlier)
1538 - 'ErrorBoundary render error',
1539 - 'ErrorBoundary componentDidUpdate',
1540 - ]),
1335 + // Fiber proceeds with lifecycles despite errors
1336 + 'Normal componentWillUnmount',
1337 + // The components have updated in this phase
1338 + 'BrokenComponentWillUnmount componentDidUpdate',
1339 + 'ErrorBoundary componentDidUpdate',
1340 + // Now that commit phase is done, Fiber unmounts the boundary's children
1341 + 'BrokenComponentWillUnmount componentWillUnmount [!]',
1342 + 'ErrorBoundary componentDidCatch',
1343 + // The initial render was aborted, so
1344 + // Fiber retries from the root.
1345 + 'ErrorBoundary componentWillUpdate',
1346 + // Render an error now (stack will do it later)
1347 + 'ErrorBoundary render error',
1348 + // Attempt to unmount previous child:
1349 + // Done
1350 + 'ErrorBoundary componentDidUpdate',
1351 ]);
1352
1353 log.length = 0;
@@ -1580,33 +1390,21 @@ describe('ReactErrorBoundaries', () => {
1390 'BrokenComponentWillUnmount render',
1391 // Unmounting throws:
1392 'BrokenComponentWillUnmount componentWillUnmount [!]',
1583 - ...(ReactDOMFeatureFlags.useFiber
1584 - ? [
1585 - // Fiber proceeds with lifecycles despite errors
1586 - 'BrokenComponentWillUnmount componentDidUpdate',
1587 - 'Normal componentDidUpdate',
1588 - 'ErrorBoundary componentDidUpdate',
1589 - 'Normal componentWillUnmount',
1590 - 'BrokenComponentWillUnmount componentWillUnmount [!]',
1591 - // Now that commit phase is done, Fiber handles errors
1592 - 'ErrorBoundary componentDidCatch',
1593 - // The initial render was aborted, so
1594 - // Fiber retries from the root.
1595 - 'ErrorBoundary componentWillUpdate',
1596 - // Render an error now (stack will do it later)
1597 - 'ErrorBoundary render error',
1598 - // Done
1599 - 'ErrorBoundary componentDidUpdate',
1600 - ]
1601 - : [
1602 - 'ErrorBoundary componentDidCatch',
1603 - // Attempt to unmount previous children:
1604 - 'Normal componentWillUnmount',
1605 - 'BrokenComponentWillUnmount componentWillUnmount [!]',
1606 - // Stack calls lifecycles first, then renders.
1607 - 'ErrorBoundary render error',
1608 - 'ErrorBoundary componentDidUpdate',
1609 - ]),
1393 + // Fiber proceeds with lifecycles despite errors
1394 + 'BrokenComponentWillUnmount componentDidUpdate',
1395 + 'Normal componentDidUpdate',
1396 + 'ErrorBoundary componentDidUpdate',
1397 + 'Normal componentWillUnmount',
1398 + 'BrokenComponentWillUnmount componentWillUnmount [!]',
1399 + // Now that commit phase is done, Fiber handles errors
1400 + 'ErrorBoundary componentDidCatch',
1401 + // The initial render was aborted, so
1402 + // Fiber retries from the root.
1403 + 'ErrorBoundary componentWillUpdate',
1404 + // Render an error now (stack will do it later)
1405 + 'ErrorBoundary render error',
1406 + // Done
1407 + 'ErrorBoundary componentDidUpdate',
1408 ]);
1409
1410 log.length = 0;
@@ -1660,31 +1458,19 @@ describe('ReactErrorBoundaries', () => {
1458 'InnerErrorBoundary render success',
1459 // Try unmounting child
1460 'BrokenComponentWillUnmount componentWillUnmount [!]',
1663 - ...(ReactDOMFeatureFlags.useFiber
1664 - ? [
1665 - // Fiber proceeds with lifecycles despite errors
1666 - // Inner and outer boundaries have updated in this phase
1667 - 'InnerErrorBoundary componentDidUpdate',
1668 - 'OuterErrorBoundary componentDidUpdate',
1669 - // Now that commit phase is done, Fiber handles errors
1670 - // Only inner boundary receives the error:
1671 - 'InnerErrorBoundary componentDidCatch',
1672 - 'InnerErrorBoundary componentWillUpdate',
1673 - // Render an error now
1674 - 'InnerErrorBoundary render error',
1675 - // In Fiber, this was a local update to the
1676 - // inner boundary so only its hook fires
1677 - 'InnerErrorBoundary componentDidUpdate',
1678 - ]
1679 - : [
1680 - // Stack will handle error immediately
1681 - 'InnerErrorBoundary componentDidCatch',
1682 - 'InnerErrorBoundary render error',
1683 - // In stack, this was a part of the update to the
1684 - // outer boundary so both lifecycles fire
1685 - 'InnerErrorBoundary componentDidUpdate',
1686 - 'OuterErrorBoundary componentDidUpdate',
1687 - ]),
1461 + // Fiber proceeds with lifecycles despite errors
1462 + // Inner and outer boundaries have updated in this phase
1463 + 'InnerErrorBoundary componentDidUpdate',
1464 + 'OuterErrorBoundary componentDidUpdate',
1465 + // Now that commit phase is done, Fiber handles errors
1466 + // Only inner boundary receives the error:
1467 + 'InnerErrorBoundary componentDidCatch',
1468 + 'InnerErrorBoundary componentWillUpdate',
1469 + // Render an error now
1470 + 'InnerErrorBoundary render error',
1471 + // In Fiber, this was a local update to the
1472 + // inner boundary so only its hook fires
1473 + 'InnerErrorBoundary componentDidUpdate',
1474 ]);
1475
1476 log.length = 0;
@@ -1856,374 +1642,371 @@ describe('ReactErrorBoundaries', () => {
1642 expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
1643 });
1644
1859 - // The tests below implement new features in Fiber.
1860 - if (ReactDOMFeatureFlags.useFiber) {
1861 - it('catches errors originating downstream', () => {
1862 - var fail = false;
1863 - class Stateful extends React.Component {
1864 - state = {shouldThrow: false};
1645 + it('catches errors originating downstream', () => {
1646 + var fail = false;
1647 + class Stateful extends React.Component {
1648 + state = {shouldThrow: false};
1649
1866 - render() {
1867 - if (fail) {
1868 - log.push('Stateful render [!]');
1869 - throw new Error('Hello');
1870 - }
1871 - return <div>{this.props.children}</div>;
1650 + render() {
1651 + if (fail) {
1652 + log.push('Stateful render [!]');
1653 + throw new Error('Hello');
1654 }
1655 + return <div>{this.props.children}</div>;
1656 }
1657 + }
1658
1875 - var statefulInst;
1876 - var container = document.createElement('div');
1877 - ReactDOM.render(
1878 - <ErrorBoundary>
1879 - <Stateful ref={inst => (statefulInst = inst)} />
1880 - </ErrorBoundary>,
1881 - container,
1882 - );
1883 -
1884 - log.length = 0;
1885 - expect(() => {
1886 - fail = true;
1887 - statefulInst.forceUpdate();
1888 - }).not.toThrow();
1889 -
1890 - expect(log).toEqual([
1891 - 'Stateful render [!]',
1892 - 'ErrorBoundary componentDidCatch',
1893 - 'ErrorBoundary componentWillUpdate',
1894 - 'ErrorBoundary render error',
1895 - 'ErrorBoundary componentDidUpdate',
1896 - ]);
1897 -
1898 - log.length = 0;
1899 - ReactDOM.unmountComponentAtNode(container);
1900 - expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
1901 - });
1902 -
1903 - it('catches errors in componentDidMount', () => {
1904 - var container = document.createElement('div');
1905 - ReactDOM.render(
1906 - <ErrorBoundary>
1907 - <BrokenComponentWillUnmount>
1908 - <Normal />
1909 - </BrokenComponentWillUnmount>
1910 - <BrokenComponentDidMount />
1911 - <Normal logName="LastChild" />
1912 - </ErrorBoundary>,
1913 - container,
1914 - );
1915 - expect(log).toEqual([
1916 - 'ErrorBoundary constructor',
1917 - 'ErrorBoundary componentWillMount',
1918 - 'ErrorBoundary render success',
1919 - 'BrokenComponentWillUnmount constructor',
1920 - 'BrokenComponentWillUnmount componentWillMount',
1921 - 'BrokenComponentWillUnmount render',
1922 - 'Normal constructor',
1923 - 'Normal componentWillMount',
1924 - 'Normal render',
1925 - 'BrokenComponentDidMount constructor',
1926 - 'BrokenComponentDidMount componentWillMount',
1927 - 'BrokenComponentDidMount render',
1928 - 'LastChild constructor',
1929 - 'LastChild componentWillMount',
1930 - 'LastChild render',
1931 - // Start flushing didMount queue
1932 - 'Normal componentDidMount',
1933 - 'BrokenComponentWillUnmount componentDidMount',
1934 - 'BrokenComponentDidMount componentDidMount [!]',
1935 - // Continue despite the error
1936 - 'LastChild componentDidMount',
1937 - 'ErrorBoundary componentDidMount',
1938 - // Now we are ready to handle the error
1939 - // Safely unmount every child
1940 - 'BrokenComponentWillUnmount componentWillUnmount [!]',
1941 - // Continue unmounting safely despite any errors
1942 - 'Normal componentWillUnmount',
1943 - 'BrokenComponentDidMount componentWillUnmount',
1944 - 'LastChild componentWillUnmount',
1945 - // Handle the error
1946 - 'ErrorBoundary componentDidCatch',
1947 - 'ErrorBoundary componentWillUpdate',
1948 - 'ErrorBoundary render error',
1949 - // The update has finished
1950 - 'ErrorBoundary componentDidUpdate',
1951 - ]);
1952 -
1953 - log.length = 0;
1954 - ReactDOM.unmountComponentAtNode(container);
1955 - expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
1956 - });
1957 -
1958 - it('catches errors in componentDidUpdate', () => {
1959 - var container = document.createElement('div');
1960 - ReactDOM.render(
1961 - <ErrorBoundary>
1962 - <BrokenComponentDidUpdate />
1963 - </ErrorBoundary>,
1964 - container,
1965 - );
1966 -
1967 - log.length = 0;
1968 - ReactDOM.render(
1969 - <ErrorBoundary>
1970 - <BrokenComponentDidUpdate />
1971 - </ErrorBoundary>,
1972 - container,
1973 - );
1974 - expect(log).toEqual([
1975 - 'ErrorBoundary componentWillReceiveProps',
1976 - 'ErrorBoundary componentWillUpdate',
1977 - 'ErrorBoundary render success',
1978 - 'BrokenComponentDidUpdate componentWillReceiveProps',
1979 - 'BrokenComponentDidUpdate componentWillUpdate',
1980 - 'BrokenComponentDidUpdate render',
1981 - // All lifecycles run
1982 - 'BrokenComponentDidUpdate componentDidUpdate [!]',
1983 - 'ErrorBoundary componentDidUpdate',
1984 - 'BrokenComponentDidUpdate componentWillUnmount',
1985 - // Then, error is handled
1986 - 'ErrorBoundary componentDidCatch',
1987 - 'ErrorBoundary componentWillUpdate',
1988 - 'ErrorBoundary render error',
1989 - 'ErrorBoundary componentDidUpdate',
1990 - ]);
1991 -
1992 - log.length = 0;
1993 - ReactDOM.unmountComponentAtNode(container);
1994 - expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
1995 - });
1996 -
1997 - it('propagates errors inside boundary during componentDidMount', () => {
1998 - var container = document.createElement('div');
1999 - ReactDOM.render(
2000 - <ErrorBoundary>
2001 - <BrokenComponentDidMountErrorBoundary
2002 - renderError={error => (
2003 - <div>
2004 - We should never catch our own error: {error.message}.
2005 - </div>
2006 - )}
2007 - />
2008 - </ErrorBoundary>,
2009 - container,
2010 - );
2011 - expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
2012 - expect(log).toEqual([
2013 - 'ErrorBoundary constructor',
2014 - 'ErrorBoundary componentWillMount',
2015 - 'ErrorBoundary render success',
2016 - 'BrokenComponentDidMountErrorBoundary constructor',
2017 - 'BrokenComponentDidMountErrorBoundary componentWillMount',
2018 - 'BrokenComponentDidMountErrorBoundary render success',
2019 - 'BrokenComponentDidMountErrorBoundary componentDidMount [!]',
2020 - // Fiber proceeds with the hooks
2021 - 'ErrorBoundary componentDidMount',
2022 - 'BrokenComponentDidMountErrorBoundary componentWillUnmount',
2023 - // The error propagates to the higher boundary
2024 - 'ErrorBoundary componentDidCatch',
2025 - // Fiber retries from the root
2026 - 'ErrorBoundary componentWillUpdate',
2027 - 'ErrorBoundary render error',
2028 - 'ErrorBoundary componentDidUpdate',
2029 - ]);
2030 -
2031 - log.length = 0;
2032 - ReactDOM.unmountComponentAtNode(container);
2033 - expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
2034 - });
2035 -
2036 - it('lets different boundaries catch their own first errors', () => {
2037 - function renderUnmountError(error) {
2038 - return <div>Caught an unmounting error: {error.message}.</div>;
2039 - }
2040 - function renderUpdateError(error) {
2041 - return <div>Caught an updating error: {error.message}.</div>;
2042 - }
2043 -
2044 - var container = document.createElement('div');
2045 - ReactDOM.render(
2046 - <ErrorBoundary logName="OuterErrorBoundary">
2047 - <ErrorBoundary
2048 - logName="InnerUnmountBoundary"
2049 - renderError={renderUnmountError}>
2050 - <BrokenComponentWillUnmount errorText="E1" />
2051 - <BrokenComponentWillUnmount errorText="E2" />
2052 - </ErrorBoundary>
2053 - <ErrorBoundary
2054 - logName="InnerUpdateBoundary"
2055 - renderError={renderUpdateError}>
2056 - <BrokenComponentDidUpdate errorText="E3" />
2057 - <BrokenComponentDidUpdate errorText="E4" />
2058 - </ErrorBoundary>
2059 - </ErrorBoundary>,
2060 - container,
2061 - );
2062 -
2063 - log.length = 0;
2064 - ReactDOM.render(
2065 - <ErrorBoundary logName="OuterErrorBoundary">
2066 - <ErrorBoundary
2067 - logName="InnerUnmountBoundary"
2068 - renderError={renderUnmountError}
2069 - />
2070 - <ErrorBoundary
2071 - logName="InnerUpdateBoundary"
2072 - renderError={renderUpdateError}>
2073 - <BrokenComponentDidUpdate errorText="E3" />
2074 - <BrokenComponentDidUpdate errorText="E4" />
2075 - </ErrorBoundary>
2076 - </ErrorBoundary>,
2077 - container,
2078 - );
2079 -
2080 - expect(container.firstChild.textContent).toBe(
2081 - 'Caught an unmounting error: E1.' + 'Caught an updating error: E3.',
2082 - );
2083 - expect(log).toEqual([
2084 - // Begin update phase
2085 - 'OuterErrorBoundary componentWillReceiveProps',
2086 - 'OuterErrorBoundary componentWillUpdate',
2087 - 'OuterErrorBoundary render success',
2088 - 'InnerUnmountBoundary componentWillReceiveProps',
2089 - 'InnerUnmountBoundary componentWillUpdate',
2090 - 'InnerUnmountBoundary render success',
2091 - 'InnerUpdateBoundary componentWillReceiveProps',
2092 - 'InnerUpdateBoundary componentWillUpdate',
2093 - 'InnerUpdateBoundary render success',
2094 - // First come the updates
2095 - 'BrokenComponentDidUpdate componentWillReceiveProps',
2096 - 'BrokenComponentDidUpdate componentWillUpdate',
2097 - 'BrokenComponentDidUpdate render',
2098 - 'BrokenComponentDidUpdate componentWillReceiveProps',
2099 - 'BrokenComponentDidUpdate componentWillUpdate',
2100 - 'BrokenComponentDidUpdate render',
2101 - // We're in commit phase now, deleting
2102 - 'BrokenComponentWillUnmount componentWillUnmount [!]',
2103 - 'BrokenComponentWillUnmount componentWillUnmount [!]',
2104 - // Continue despite errors, handle them after commit is done
2105 - 'InnerUnmountBoundary componentDidUpdate',
2106 - // We're still in commit phase, now calling update lifecycles
2107 - 'BrokenComponentDidUpdate componentDidUpdate [!]',
2108 - // Again, continue despite errors, we'll handle them later
2109 - 'BrokenComponentDidUpdate componentDidUpdate [!]',
2110 - 'InnerUpdateBoundary componentDidUpdate',
2111 - 'OuterErrorBoundary componentDidUpdate',
2112 - // After the commit phase, attempt to recover from any errors that
2113 - // were captured
2114 - 'BrokenComponentDidUpdate componentWillUnmount',
2115 - 'BrokenComponentDidUpdate componentWillUnmount',
2116 - 'InnerUnmountBoundary componentDidCatch',
2117 - 'InnerUpdateBoundary componentDidCatch',
2118 - 'InnerUnmountBoundary componentWillUpdate',
2119 - 'InnerUnmountBoundary render error',
2120 - 'InnerUpdateBoundary componentWillUpdate',
2121 - 'InnerUpdateBoundary render error',
2122 - 'InnerUnmountBoundary componentDidUpdate',
2123 - 'InnerUpdateBoundary componentDidUpdate',
2124 - ]);
2125 -
2126 - log.length = 0;
2127 - ReactDOM.unmountComponentAtNode(container);
2128 - expect(log).toEqual([
2129 - 'OuterErrorBoundary componentWillUnmount',
2130 - 'InnerUnmountBoundary componentWillUnmount',
2131 - 'InnerUpdateBoundary componentWillUnmount',
2132 - ]);
2133 - });
2134 -
2135 - it('discards a bad root if the root component fails', () => {
2136 - spyOn(console, 'error');
2137 -
2138 - const X = null;
2139 - const Y = undefined;
2140 - let err1;
2141 - let err2;
2142 -
2143 - try {
2144 - let container = document.createElement('div');
2145 - ReactDOM.render(<X />, container);
2146 - } catch (err) {
2147 - err1 = err;
2148 - }
2149 - try {
2150 - let container = document.createElement('div');
2151 - ReactDOM.render(<Y />, container);
2152 - } catch (err) {
2153 - err2 = err;
2154 - }
2155 -
2156 - expect(err1.message).toMatch(/got: null/);
2157 - expect(err2.message).toMatch(/got: undefined/);
2158 - });
2159 -
2160 - it('renders empty output if error boundary does not handle the error', () => {
2161 - var container = document.createElement('div');
2162 - ReactDOM.render(
2163 - <div>
2164 - Sibling
2165 - <NoopErrorBoundary>
2166 - <BrokenRender />
2167 - </NoopErrorBoundary>
2168 - </div>,
2169 - container,
2170 - );
2171 - expect(container.firstChild.textContent).toBe('Sibling');
2172 - expect(log).toEqual([
2173 - 'NoopErrorBoundary constructor',
2174 - 'NoopErrorBoundary componentWillMount',
2175 - 'NoopErrorBoundary render',
2176 - 'BrokenRender constructor',
2177 - 'BrokenRender componentWillMount',
2178 - 'BrokenRender render [!]',
2179 - // In Fiber, noop error boundaries render null
2180 - 'NoopErrorBoundary componentDidMount',
2181 - 'NoopErrorBoundary componentDidCatch',
2182 - // Nothing happens.
2183 - ]);
2184 -
2185 - log.length = 0;
2186 - ReactDOM.unmountComponentAtNode(container);
2187 - expect(log).toEqual(['NoopErrorBoundary componentWillUnmount']);
2188 - });
1659 + var statefulInst;
1660 + var container = document.createElement('div');
1661 + ReactDOM.render(
1662 + <ErrorBoundary>
1663 + <Stateful ref={inst => (statefulInst = inst)} />
1664 + </ErrorBoundary>,
1665 + container,
1666 + );
1667
2190 - it('passes first error when two errors happen in commit', () => {
2191 - const errors = [];
2192 - let caughtError;
2193 - class Parent extends React.Component {
2194 - render() {
2195 - return <Child />;
2196 - }
2197 - componentDidMount() {
2198 - errors.push('parent sad');
2199 - throw new Error('parent sad');
2200 - }
1668 + log.length = 0;
1669 + expect(() => {
1670 + fail = true;
1671 + statefulInst.forceUpdate();
1672 + }).not.toThrow();
1673 +
1674 + expect(log).toEqual([
1675 + 'Stateful render [!]',
1676 + 'ErrorBoundary componentDidCatch',
1677 + 'ErrorBoundary componentWillUpdate',
1678 + 'ErrorBoundary render error',
1679 + 'ErrorBoundary componentDidUpdate',
1680 + ]);
1681 +
1682 + log.length = 0;
1683 + ReactDOM.unmountComponentAtNode(container);
1684 + expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
1685 + });
1686 +
1687 + it('catches errors in componentDidMount', () => {
1688 + var container = document.createElement('div');
1689 + ReactDOM.render(
1690 + <ErrorBoundary>
1691 + <BrokenComponentWillUnmount>
1692 + <Normal />
1693 + </BrokenComponentWillUnmount>
1694 + <BrokenComponentDidMount />
1695 + <Normal logName="LastChild" />
1696 + </ErrorBoundary>,
1697 + container,
1698 + );
1699 + expect(log).toEqual([
1700 + 'ErrorBoundary constructor',
1701 + 'ErrorBoundary componentWillMount',
1702 + 'ErrorBoundary render success',
1703 + 'BrokenComponentWillUnmount constructor',
1704 + 'BrokenComponentWillUnmount componentWillMount',
1705 + 'BrokenComponentWillUnmount render',
1706 + 'Normal constructor',
1707 + 'Normal componentWillMount',
1708 + 'Normal render',
1709 + 'BrokenComponentDidMount constructor',
1710 + 'BrokenComponentDidMount componentWillMount',
1711 + 'BrokenComponentDidMount render',
1712 + 'LastChild constructor',
1713 + 'LastChild componentWillMount',
1714 + 'LastChild render',
1715 + // Start flushing didMount queue
1716 + 'Normal componentDidMount',
1717 + 'BrokenComponentWillUnmount componentDidMount',
1718 + 'BrokenComponentDidMount componentDidMount [!]',
1719 + // Continue despite the error
1720 + 'LastChild componentDidMount',
1721 + 'ErrorBoundary componentDidMount',
1722 + // Now we are ready to handle the error
1723 + // Safely unmount every child
1724 + 'BrokenComponentWillUnmount componentWillUnmount [!]',
1725 + // Continue unmounting safely despite any errors
1726 + 'Normal componentWillUnmount',
1727 + 'BrokenComponentDidMount componentWillUnmount',
1728 + 'LastChild componentWillUnmount',
1729 + // Handle the error
1730 + 'ErrorBoundary componentDidCatch',
1731 + 'ErrorBoundary componentWillUpdate',
1732 + 'ErrorBoundary render error',
1733 + // The update has finished
1734 + 'ErrorBoundary componentDidUpdate',
1735 + ]);
1736 +
1737 + log.length = 0;
1738 + ReactDOM.unmountComponentAtNode(container);
1739 + expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
1740 + });
1741 +
1742 + it('catches errors in componentDidUpdate', () => {
1743 + var container = document.createElement('div');
1744 + ReactDOM.render(
1745 + <ErrorBoundary>
1746 + <BrokenComponentDidUpdate />
1747 + </ErrorBoundary>,
1748 + container,
1749 + );
1750 +
1751 + log.length = 0;
1752 + ReactDOM.render(
1753 + <ErrorBoundary>
1754 + <BrokenComponentDidUpdate />
1755 + </ErrorBoundary>,
1756 + container,
1757 + );
1758 + expect(log).toEqual([
1759 + 'ErrorBoundary componentWillReceiveProps',
1760 + 'ErrorBoundary componentWillUpdate',
1761 + 'ErrorBoundary render success',
1762 + 'BrokenComponentDidUpdate componentWillReceiveProps',
1763 + 'BrokenComponentDidUpdate componentWillUpdate',
1764 + 'BrokenComponentDidUpdate render',
1765 + // All lifecycles run
1766 + 'BrokenComponentDidUpdate componentDidUpdate [!]',
1767 + 'ErrorBoundary componentDidUpdate',
1768 + 'BrokenComponentDidUpdate componentWillUnmount',
1769 + // Then, error is handled
1770 + 'ErrorBoundary componentDidCatch',
1771 + 'ErrorBoundary componentWillUpdate',
1772 + 'ErrorBoundary render error',
1773 + 'ErrorBoundary componentDidUpdate',
1774 + ]);
1775 +
1776 + log.length = 0;
1777 + ReactDOM.unmountComponentAtNode(container);
1778 + expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
1779 + });
1780 +
1781 + it('propagates errors inside boundary during componentDidMount', () => {
1782 + var container = document.createElement('div');
1783 + ReactDOM.render(
1784 + <ErrorBoundary>
1785 + <BrokenComponentDidMountErrorBoundary
1786 + renderError={error => (
1787 + <div>
1788 + We should never catch our own error: {error.message}.
1789 + </div>
1790 + )}
1791 + />
1792 + </ErrorBoundary>,
1793 + container,
1794 + );
1795 + expect(container.firstChild.textContent).toBe('Caught an error: Hello.');
1796 + expect(log).toEqual([
1797 + 'ErrorBoundary constructor',
1798 + 'ErrorBoundary componentWillMount',
1799 + 'ErrorBoundary render success',
1800 + 'BrokenComponentDidMountErrorBoundary constructor',
1801 + 'BrokenComponentDidMountErrorBoundary componentWillMount',
1802 + 'BrokenComponentDidMountErrorBoundary render success',
1803 + 'BrokenComponentDidMountErrorBoundary componentDidMount [!]',
1804 + // Fiber proceeds with the hooks
1805 + 'ErrorBoundary componentDidMount',
1806 + 'BrokenComponentDidMountErrorBoundary componentWillUnmount',
1807 + // The error propagates to the higher boundary
1808 + 'ErrorBoundary componentDidCatch',
1809 + // Fiber retries from the root
1810 + 'ErrorBoundary componentWillUpdate',
1811 + 'ErrorBoundary render error',
1812 + 'ErrorBoundary componentDidUpdate',
1813 + ]);
1814 +
1815 + log.length = 0;
1816 + ReactDOM.unmountComponentAtNode(container);
1817 + expect(log).toEqual(['ErrorBoundary componentWillUnmount']);
1818 + });
1819 +
1820 + it('lets different boundaries catch their own first errors', () => {
1821 + function renderUnmountError(error) {
1822 + return <div>Caught an unmounting error: {error.message}.</div>;
1823 + }
1824 + function renderUpdateError(error) {
1825 + return <div>Caught an updating error: {error.message}.</div>;
1826 + }
1827 +
1828 + var container = document.createElement('div');
1829 + ReactDOM.render(
1830 + <ErrorBoundary logName="OuterErrorBoundary">
1831 + <ErrorBoundary
1832 + logName="InnerUnmountBoundary"
1833 + renderError={renderUnmountError}>
1834 + <BrokenComponentWillUnmount errorText="E1" />
1835 + <BrokenComponentWillUnmount errorText="E2" />
1836 + </ErrorBoundary>
1837 + <ErrorBoundary
1838 + logName="InnerUpdateBoundary"
1839 + renderError={renderUpdateError}>
1840 + <BrokenComponentDidUpdate errorText="E3" />
1841 + <BrokenComponentDidUpdate errorText="E4" />
1842 + </ErrorBoundary>
1843 + </ErrorBoundary>,
1844 + container,
1845 + );
1846 +
1847 + log.length = 0;
1848 + ReactDOM.render(
1849 + <ErrorBoundary logName="OuterErrorBoundary">
1850 + <ErrorBoundary
1851 + logName="InnerUnmountBoundary"
1852 + renderError={renderUnmountError}
1853 + />
1854 + <ErrorBoundary
1855 + logName="InnerUpdateBoundary"
1856 + renderError={renderUpdateError}>
1857 + <BrokenComponentDidUpdate errorText="E3" />
1858 + <BrokenComponentDidUpdate errorText="E4" />
1859 + </ErrorBoundary>
1860 + </ErrorBoundary>,
1861 + container,
1862 + );
1863 +
1864 + expect(container.firstChild.textContent).toBe(
1865 + 'Caught an unmounting error: E1.' + 'Caught an updating error: E3.',
1866 + );
1867 + expect(log).toEqual([
1868 + // Begin update phase
1869 + 'OuterErrorBoundary componentWillReceiveProps',
1870 + 'OuterErrorBoundary componentWillUpdate',
1871 + 'OuterErrorBoundary render success',
1872 + 'InnerUnmountBoundary componentWillReceiveProps',
1873 + 'InnerUnmountBoundary componentWillUpdate',
1874 + 'InnerUnmountBoundary render success',
1875 + 'InnerUpdateBoundary componentWillReceiveProps',
1876 + 'InnerUpdateBoundary componentWillUpdate',
1877 + 'InnerUpdateBoundary render success',
1878 + // First come the updates
1879 + 'BrokenComponentDidUpdate componentWillReceiveProps',
1880 + 'BrokenComponentDidUpdate componentWillUpdate',
1881 + 'BrokenComponentDidUpdate render',
1882 + 'BrokenComponentDidUpdate componentWillReceiveProps',
1883 + 'BrokenComponentDidUpdate componentWillUpdate',
1884 + 'BrokenComponentDidUpdate render',
1885 + // We're in commit phase now, deleting
1886 + 'BrokenComponentWillUnmount componentWillUnmount [!]',
1887 + 'BrokenComponentWillUnmount componentWillUnmount [!]',
1888 + // Continue despite errors, handle them after commit is done
1889 + 'InnerUnmountBoundary componentDidUpdate',
1890 + // We're still in commit phase, now calling update lifecycles
1891 + 'BrokenComponentDidUpdate componentDidUpdate [!]',
1892 + // Again, continue despite errors, we'll handle them later
1893 + 'BrokenComponentDidUpdate componentDidUpdate [!]',
1894 + 'InnerUpdateBoundary componentDidUpdate',
1895 + 'OuterErrorBoundary componentDidUpdate',
1896 + // After the commit phase, attempt to recover from any errors that
1897 + // were captured
1898 + 'BrokenComponentDidUpdate componentWillUnmount',
1899 + 'BrokenComponentDidUpdate componentWillUnmount',
1900 + 'InnerUnmountBoundary componentDidCatch',
1901 + 'InnerUpdateBoundary componentDidCatch',
1902 + 'InnerUnmountBoundary componentWillUpdate',
1903 + 'InnerUnmountBoundary render error',
1904 + 'InnerUpdateBoundary componentWillUpdate',
1905 + 'InnerUpdateBoundary render error',
1906 + 'InnerUnmountBoundary componentDidUpdate',
1907 + 'InnerUpdateBoundary componentDidUpdate',
1908 + ]);
1909 +
1910 + log.length = 0;
1911 + ReactDOM.unmountComponentAtNode(container);
1912 + expect(log).toEqual([
1913 + 'OuterErrorBoundary componentWillUnmount',
1914 + 'InnerUnmountBoundary componentWillUnmount',
1915 + 'InnerUpdateBoundary componentWillUnmount',
1916 + ]);
1917 + });
1918 +
1919 + it('discards a bad root if the root component fails', () => {
1920 + spyOn(console, 'error');
1921 +
1922 + const X = null;
1923 + const Y = undefined;
1924 + let err1;
1925 + let err2;
1926 +
1927 + try {
1928 + let container = document.createElement('div');
1929 + ReactDOM.render(<X />, container);
1930 + } catch (err) {
1931 + err1 = err;
1932 + }
1933 + try {
1934 + let container = document.createElement('div');
1935 + ReactDOM.render(<Y />, container);
1936 + } catch (err) {
1937 + err2 = err;
1938 + }
1939 +
1940 + expect(err1.message).toMatch(/got: null/);
1941 + expect(err2.message).toMatch(/got: undefined/);
1942 + });
1943 +
1944 + it('renders empty output if error boundary does not handle the error', () => {
1945 + var container = document.createElement('div');
1946 + ReactDOM.render(
1947 + <div>
1948 + Sibling
1949 + <NoopErrorBoundary>
1950 + <BrokenRender />
1951 + </NoopErrorBoundary>
1952 + </div>,
1953 + container,
1954 + );
1955 + expect(container.firstChild.textContent).toBe('Sibling');
1956 + expect(log).toEqual([
1957 + 'NoopErrorBoundary constructor',
1958 + 'NoopErrorBoundary componentWillMount',
1959 + 'NoopErrorBoundary render',
1960 + 'BrokenRender constructor',
1961 + 'BrokenRender componentWillMount',
1962 + 'BrokenRender render [!]',
1963 + // In Fiber, noop error boundaries render null
1964 + 'NoopErrorBoundary componentDidMount',
1965 + 'NoopErrorBoundary componentDidCatch',
1966 + // Nothing happens.
1967 + ]);
1968 +
1969 + log.length = 0;
1970 + ReactDOM.unmountComponentAtNode(container);
1971 + expect(log).toEqual(['NoopErrorBoundary componentWillUnmount']);
1972 + });
1973 +
1974 + it('passes first error when two errors happen in commit', () => {
1975 + const errors = [];
1976 + let caughtError;
1977 + class Parent extends React.Component {
1978 + render() {
1979 + return <Child />;
1980 }
2202 - class Child extends React.Component {
2203 - render() {
2204 - return <div />;
2205 - }
2206 - componentDidMount() {
2207 - errors.push('child sad');
2208 - throw new Error('child sad');
2209 - }
1981 + componentDidMount() {
1982 + errors.push('parent sad');
1983 + throw new Error('parent sad');
1984 }
2211 -
2212 - var container = document.createElement('div');
2213 - try {
2214 - // Here, we test the behavior where there is no error boundary and we
2215 - // delegate to the host root.
2216 - ReactDOM.render(<Parent />, container);
2217 - } catch (e) {
2218 - if (e.message !== 'parent sad' && e.message !== 'child sad') {
2219 - throw e;
2220 - }
2221 - caughtError = e;
1985 + }
1986 + class Child extends React.Component {
1987 + render() {
1988 + return <div />;
1989 + }
1990 + componentDidMount() {
1991 + errors.push('child sad');
1992 + throw new Error('child sad');
1993 }
1994 + }
1995
2224 - expect(errors).toEqual(['child sad', 'parent sad']);
2225 - // Error should be the first thrown
2226 - expect(caughtError.message).toBe('child sad');
2227 - });
2228 - }
1996 + var container = document.createElement('div');
1997 + try {
1998 + // Here, we test the behavior where there is no error boundary and we
1999 + // delegate to the host root.
2000 + ReactDOM.render(<Parent />, container);
2001 + } catch (e) {
2002 + if (e.message !== 'parent sad' && e.message !== 'child sad') {
2003 + throw e;
2004 + }
2005 + caughtError = e;
2006 + }
2007 +
2008 + expect(errors).toEqual(['child sad', 'parent sad']);
2009 + // Error should be the first thrown
2010 + expect(caughtError.message).toBe('child sad');
2011 + });
2012 });
src/renderers/__tests__/ReactHostOperationHistoryHook-test.js
+1 -4
@@ -9,12 +9,9 @@
9
10 'use strict';
11
12 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 -var describeStack = ReactDOMFeatureFlags.useFiber ? describe.skip : describe;
14 -
12 // This is only used by ReactPerf which is currently not supported on Fiber.
13 // Use browser timeline integration instead.
17 -describeStack('ReactHostOperationHistoryHook', () => {
14 +describe.skip('ReactHostOperationHistoryHook', () => {
15 var React;
16 var ReactPerf;
17 var ReactDOM;
src/renderers/__tests__/ReactMultiChild-test.js
+7 -21
@@ -16,13 +16,11 @@ describe('ReactMultiChild', () => {
16
17 var React;
18 var ReactDOM;
19 - var ReactDOMFeatureFlags;
19
20 beforeEach(() => {
21 jest.resetModules();
22 React = require('react');
23 ReactDOM = require('react-dom');
25 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
24 });
25
26 describe('reconciliation', () => {
@@ -280,8 +278,7 @@ describe('ReactMultiChild', () => {
278 'Warning: Using Maps as children is unsupported and will likely yield ' +
279 'unexpected results. Convert it to a sequence/iterable of keyed ' +
280 'ReactElements instead.\n' +
283 - // Fiber gives a slightly better stack with the nearest host components
284 - (ReactDOMFeatureFlags.useFiber ? ' in div (at **)\n' : '') +
281 + ' in div (at **)\n' +
282 ' in Parent (at **)',
283 );
284 });
@@ -368,23 +365,12 @@ describe('ReactMultiChild', () => {
365 'oneA componentDidMount',
366 'twoA componentDidMount',
367
371 - ...(ReactDOMFeatureFlags.useFiber
372 - ? [
373 - 'oneB componentWillMount',
374 - 'oneB render',
375 - 'twoB componentWillMount',
376 - 'twoB render',
377 - 'oneA componentWillUnmount',
378 - 'twoA componentWillUnmount',
379 - ]
380 - : [
381 - 'oneB componentWillMount',
382 - 'oneB render',
383 - 'oneA componentWillUnmount',
384 - 'twoB componentWillMount',
385 - 'twoB render',
386 - 'twoA componentWillUnmount',
387 - ]),
368 + 'oneB componentWillMount',
369 + 'oneB render',
370 + 'twoB componentWillMount',
371 + 'twoB render',
372 + 'oneA componentWillUnmount',
373 + 'twoA componentWillUnmount',
374
375 'oneB componentDidMount',
376 'twoB componentDidMount',
src/renderers/__tests__/ReactMultiChildText-test.js
+12 -46
@@ -11,7 +11,6 @@
11
12 var React = require('react');
13 var ReactDOM = require('react-dom');
14 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
14 var ReactTestUtils = require('react-dom/test-utils');
15
16 // Helpers
@@ -48,43 +47,16 @@ var expectChildren = function(container, children) {
47 expect(textNode.data).toBe('' + children);
48 }
49 } else {
51 - var openingCommentNode;
52 - var closingCommentNode;
50 var mountIndex = 0;
51
52 for (var i = 0; i < children.length; i++) {
53 var child = children[i];
54
55 if (typeof child === 'string') {
59 - if (ReactDOMFeatureFlags.useFiber) {
60 - textNode = outerNode.childNodes[mountIndex];
61 - expect(textNode.nodeType).toBe(3);
62 - expect(textNode.data).toBe('' + child);
63 - mountIndex++;
64 - } else {
65 - openingCommentNode = outerNode.childNodes[mountIndex];
66 -
67 - expect(openingCommentNode.nodeType).toBe(8);
68 - expect(openingCommentNode.nodeValue).toMatch(/ react-text: [0-9]+ /);
69 -
70 - if (child === '') {
71 - textNode = null;
72 - closingCommentNode = openingCommentNode.nextSibling;
73 - mountIndex += 2;
74 - } else {
75 - textNode = openingCommentNode.nextSibling;
76 - closingCommentNode = textNode.nextSibling;
77 - mountIndex += 3;
78 - }
79 -
80 - if (textNode) {
81 - expect(textNode.nodeType).toBe(3);
82 - expect(textNode.data).toBe('' + child);
83 - }
84 -
85 - expect(closingCommentNode.nodeType).toBe(8);
86 - expect(closingCommentNode.nodeValue).toBe(' /react-text ');
87 - }
56 + textNode = outerNode.childNodes[mountIndex];
57 + expect(textNode.nodeType).toBe(3);
58 + expect(textNode.data).toBe('' + child);
59 + mountIndex++;
60 } else {
61 var elementDOMNode = outerNode.childNodes[mountIndex];
62 expect(elementDOMNode.tagName).toBe('DIV');
@@ -189,20 +161,14 @@ describe('ReactMultiChildText', () => {
161 [true, <div>{1.2}{''}{<div />}{'foo'}</div>, true, 1.2], [<div />, '1.2'],
162 ['', 'foo', <div>{true}{<div />}{1.2}{''}</div>, 'foo'], ['', 'foo', <div />, 'foo'],
163 ]);
192 - if (ReactDOMFeatureFlags.useFiber) {
193 - expectDev(console.error.calls.count()).toBe(2);
194 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
195 - 'Warning: Each child in an array or iterator should have a unique "key" prop.',
196 - );
197 - expectDev(console.error.calls.argsFor(1)[0]).toContain(
198 - 'Warning: Each child in an array or iterator should have a unique "key" prop.',
199 - );
200 - } else {
201 - expectDev(console.error.calls.count()).toBe(1);
202 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
203 - 'Warning: Each child in an array or iterator should have a unique "key" prop.',
204 - );
205 - }
164 +
165 + expectDev(console.error.calls.count()).toBe(2);
166 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
167 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
168 + );
169 + expectDev(console.error.calls.argsFor(1)[0]).toContain(
170 + 'Warning: Each child in an array or iterator should have a unique "key" prop.',
171 + );
172 });
173
174 it('should throw if rendering both HTML and children', () => {
src/renderers/__tests__/ReactPerf-test.js
+1 -4
@@ -9,12 +9,9 @@
9
10 'use strict';
11
12 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 -var describeStack = ReactDOMFeatureFlags.useFiber ? describe.skip : describe;
14 -
12 // ReactPerf is currently not supported on Fiber.
13 // Use browser timeline integration instead.
17 -describeStack('ReactPerf', () => {
14 +describe.skip('ReactPerf', () => {
15 var React;
16 var ReactDOM;
17 var ReactPerf;
src/renderers/__tests__/ReactStatelessComponent-test.js
+14 -48
@@ -14,8 +14,6 @@ var React;
14 var ReactDOM;
15 var ReactTestUtils;
16
17 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
18 -
17 function StatelessComponent(props) {
18 return <div>{props.name}</div>;
19 }
@@ -118,54 +116,22 @@ describe('ReactStatelessComponent', () => {
116
117 ReactDOM.render(<StatelessComponentWithChildContext name="A" />, container);
118
121 - // Stack and Fiber differ in terms of they show warnings
122 - if (ReactDOMFeatureFlags.useFiber) {
123 - expectDev(console.error.calls.count()).toBe(1);
124 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
125 - 'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
126 - 'be defined on a functional component.',
127 - );
128 - } else {
129 - expectDev(console.error.calls.count()).toBe(2);
130 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
131 - 'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
132 - 'be defined on a functional component.',
133 - );
134 - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
135 - 'Warning: StatelessComponentWithChildContext.childContextTypes is specified ' +
136 - 'but there is no getChildContext() method on the instance. You can either ' +
137 - 'define getChildContext() on StatelessComponentWithChildContext or remove ' +
138 - 'childContextTypes from it.',
139 - );
140 - }
119 + expectDev(console.error.calls.count()).toBe(1);
120 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
121 + 'StatelessComponentWithChildContext(...): childContextTypes cannot ' +
122 + 'be defined on a functional component.',
123 + );
124 });
125
143 - if (!ReactDOMFeatureFlags.useFiber) {
144 - // Stack doesn't support fragments
145 - it('should throw when stateless component returns array', () => {
146 - function NotAComponent() {
147 - return [<div />, <div />];
148 - }
149 - expect(function() {
150 - ReactTestUtils.renderIntoDocument(<div><NotAComponent /></div>);
151 - }).toThrowError(
152 - 'NotAComponent(...): A valid React element (or null) must be returned. ' +
153 - 'You may have returned undefined, an array or some other invalid object.',
154 - );
155 - });
156 - }
157 -
158 - if (ReactDOMFeatureFlags.useFiber) {
159 - it('should throw when stateless component returns undefined', () => {
160 - function NotAComponent() {}
161 - expect(function() {
162 - ReactTestUtils.renderIntoDocument(<div><NotAComponent /></div>);
163 - }).toThrowError(
164 - 'NotAComponent(...): Nothing was returned from render. ' +
165 - 'This usually means a return statement is missing. Or, to render nothing, return null.',
166 - );
167 - });
168 - }
126 + it('should throw when stateless component returns undefined', () => {
127 + function NotAComponent() {}
128 + expect(function() {
129 + ReactTestUtils.renderIntoDocument(<div><NotAComponent /></div>);
130 + }).toThrowError(
131 + 'NotAComponent(...): Nothing was returned from render. ' +
132 + 'This usually means a return statement is missing. Or, to render nothing, return null.',
133 + );
134 + });
135
136 it('should throw on string refs in pure functions', () => {
137 function Child() {
src/renderers/__tests__/ReactUpdates-test.js
+25 -35
@@ -12,13 +12,11 @@
12 var React;
13 var ReactDOM;
14 var ReactTestUtils;
15 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
15
16 describe('ReactUpdates', () => {
17 beforeEach(() => {
18 React = require('react');
19 ReactDOM = require('react-dom');
21 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
20 ReactTestUtils = require('react-dom/test-utils');
21 });
22
@@ -519,9 +517,7 @@ describe('ReactUpdates', () => {
517 render() {
518 var portal = null;
519 // If we're using Fiber, we use Portals instead to achieve this.
522 - if (ReactDOMFeatureFlags.useFiber) {
523 - portal = ReactDOM.createPortal(<B ref={n => (b = n)} />, bContainer);
524 - }
520 + portal = ReactDOM.createPortal(<B ref={n => (b = n)} />, bContainer);
521 return <div>A{this.state.x}{portal}</div>;
522 }
523 }
@@ -535,10 +531,6 @@ describe('ReactUpdates', () => {
531 }
532
533 a = ReactTestUtils.renderIntoDocument(<A />);
538 - if (!ReactDOMFeatureFlags.useFiber) {
539 - ReactDOM.render(<B ref={n => (b = n)} />, bContainer);
540 - }
541 -
534 ReactDOM.unstable_batchedUpdates(function() {
535 a.setState({x: 1});
536 b.setState({x: 1});
@@ -1178,35 +1170,33 @@ describe('ReactUpdates', () => {
1170 }).toThrow('Maximum');
1171 });
1172
1181 - if (ReactDOMFeatureFlags.useFiber) {
1182 - it('does not fall into an infinite error loop', () => {
1183 - function BadRender() {
1184 - throw new Error('error');
1185 - }
1173 + it('does not fall into an infinite error loop', () => {
1174 + function BadRender() {
1175 + throw new Error('error');
1176 + }
1177
1187 - class ErrorBoundary extends React.Component {
1188 - componentDidCatch() {
1189 - this.props.parent.remount();
1190 - }
1191 - render() {
1192 - return <BadRender />;
1193 - }
1178 + class ErrorBoundary extends React.Component {
1179 + componentDidCatch() {
1180 + this.props.parent.remount();
1181 + }
1182 + render() {
1183 + return <BadRender />;
1184 }
1185 + }
1186
1196 - class NonTerminating extends React.Component {
1197 - state = {step: 0};
1198 - remount() {
1199 - this.setState(state => ({step: state.step + 1}));
1200 - }
1201 - render() {
1202 - return <ErrorBoundary key={this.state.step} parent={this} />;
1203 - }
1187 + class NonTerminating extends React.Component {
1188 + state = {step: 0};
1189 + remount() {
1190 + this.setState(state => ({step: state.step + 1}));
1191 + }
1192 + render() {
1193 + return <ErrorBoundary key={this.state.step} parent={this} />;
1194 }
1195 + }
1196
1206 - const container = document.createElement('div');
1207 - expect(() => {
1208 - ReactDOM.render(<NonTerminating />, container);
1209 - }).toThrow('Maximum');
1210 - });
1211 - }
1197 + const container = document.createElement('div');
1198 + expect(() => {
1199 + ReactDOM.render(<NonTerminating />, container);
1200 + }).toThrow('Maximum');
1201 + });
1202 });
src/renderers/__tests__/multiple-copies-of-react-test.js
+7 -19
@@ -10,7 +10,6 @@
10 'use strict';
11
12 let React = require('react');
13 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 var ReactTestUtils = require('react-dom/test-utils');
14
15 class TextWithStringRef extends React.Component {
@@ -27,23 +26,12 @@ class TextWithStringRef extends React.Component {
26
27 describe('when different React version is used with string ref', () => {
28 it('throws the "Refs must have owner" warning', () => {
30 - if (ReactDOMFeatureFlags.useFiber) {
31 - expect(() => {
32 - ReactTestUtils.renderIntoDocument(<TextWithStringRef />);
33 - }).toThrow(
34 - 'Element ref was specified as a string (foo) but no owner was set.' +
35 - ' You may have multiple copies of React loaded. (details: ' +
36 - 'https://fb.me/react-refs-must-have-owner).',
37 - );
38 - } else {
39 - expect(() => {
40 - ReactTestUtils.renderIntoDocument(<TextWithStringRef />);
41 - }).toThrow(
42 - 'Only a ReactOwner can have refs. You might be adding a ref to a ' +
43 - "component that was not created inside a component's `render` " +
44 - 'method, or you have multiple copies of React loaded ' +
45 - '(details: https://fb.me/react-refs-must-have-owner)',
46 - );
47 - }
29 + expect(() => {
30 + ReactTestUtils.renderIntoDocument(<TextWithStringRef />);
31 + }).toThrow(
32 + 'Element ref was specified as a string (foo) but no owner was set.' +
33 + ' You may have multiple copies of React loaded. (details: ' +
34 + 'https://fb.me/react-refs-must-have-owner).',
35 + );
36 });
37 });
src/renderers/__tests__/refs-test.js
+34 -56
@@ -10,7 +10,6 @@
10 'use strict';
11
12 var React = require('react');
13 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 var ReactTestUtils = require('react-dom/test-utils');
14
15 /**
@@ -348,40 +347,38 @@ describe('root level refs', () => {
347 expect(ref).toHaveBeenCalledTimes(2);
348 expect(ref.mock.calls[1][0]).toBe(null);
349
351 - if (ReactDOMFeatureFlags.useFiber) {
352 - // fragment
353 - inst = null;
354 - ref = jest.fn(value => (inst = value));
355 - var divInst = null;
356 - var ref2 = jest.fn(value => (divInst = value));
357 - result = ReactDOM.render(
358 - [<Comp ref={ref} key="a" />, 5, <div ref={ref2} key="b">Hello</div>],
359 - container,
360 - );
350 + // fragment
351 + inst = null;
352 + ref = jest.fn(value => (inst = value));
353 + var divInst = null;
354 + var ref2 = jest.fn(value => (divInst = value));
355 + result = ReactDOM.render(
356 + [<Comp ref={ref} key="a" />, 5, <div ref={ref2} key="b">Hello</div>],
357 + container,
358 + );
359
362 - // first call should be `Comp`
363 - expect(ref).toHaveBeenCalledTimes(1);
364 - expect(ref.mock.calls[0][0]).toBeInstanceOf(Comp);
365 - expect(result).toBe(ref.mock.calls[0][0]);
360 + // first call should be `Comp`
361 + expect(ref).toHaveBeenCalledTimes(1);
362 + expect(ref.mock.calls[0][0]).toBeInstanceOf(Comp);
363 + expect(result).toBe(ref.mock.calls[0][0]);
364
367 - expect(ref2).toHaveBeenCalledTimes(1);
368 - expect(divInst).toBeInstanceOf(HTMLDivElement);
369 - expect(result).not.toBe(divInst);
365 + expect(ref2).toHaveBeenCalledTimes(1);
366 + expect(divInst).toBeInstanceOf(HTMLDivElement);
367 + expect(result).not.toBe(divInst);
368
371 - ReactDOM.unmountComponentAtNode(container);
372 - expect(ref).toHaveBeenCalledTimes(2);
373 - expect(ref.mock.calls[1][0]).toBe(null);
374 - expect(ref2).toHaveBeenCalledTimes(2);
375 - expect(ref2.mock.calls[1][0]).toBe(null);
369 + ReactDOM.unmountComponentAtNode(container);
370 + expect(ref).toHaveBeenCalledTimes(2);
371 + expect(ref.mock.calls[1][0]).toBe(null);
372 + expect(ref2).toHaveBeenCalledTimes(2);
373 + expect(ref2.mock.calls[1][0]).toBe(null);
374
377 - // null
378 - result = ReactDOM.render(null, container);
379 - expect(result).toBe(null);
375 + // null
376 + result = ReactDOM.render(null, container);
377 + expect(result).toBe(null);
378
381 - // primitives
382 - result = ReactDOM.render(5, container);
383 - expect(result).toBeInstanceOf(Text);
384 - }
379 + // primitives
380 + result = ReactDOM.render(5, container);
381 + expect(result).toBeInstanceOf(Text);
382 });
383 });
384
@@ -397,28 +394,6 @@ describe('creating element with ref in constructor', () => {
394 }
395 }
396
400 - var devErrorMessage =
401 - 'addComponentAsRefTo(...): Only a ReactOwner can have refs. You might ' +
402 - "be adding a ref to a component that was not created inside a component's " +
403 - '`render` method, or you have multiple copies of React loaded ' +
404 - '(details: https://fb.me/react-refs-must-have-owner).';
405 -
406 - var prodErrorMessage =
407 - 'Minified React error #119; visit ' +
408 - 'http://facebook.github.io/react/docs/error-decoder.html?invariant=119 for the full message ' +
409 - 'or use the non-minified dev environment for full errors and additional helpful warnings.';
410 -
411 - var fiberDevErrorMessage =
412 - 'Element ref was specified as a string (p) but no owner was ' +
413 - 'set. You may have multiple copies of React loaded. ' +
414 - '(details: https://fb.me/react-refs-must-have-owner).';
415 -
416 - var fiberProdErrorMessage =
417 - 'Minified React error #149; visit ' +
418 - 'http://facebook.github.io/react/docs/error-decoder.html?invariant=149&args[]=p ' +
419 - 'for the full message or use the non-minified dev environment for full errors and additional ' +
420 - 'helpful warnings.';
421 -
397 it('throws an error when __DEV__ = true', () => {
398 ReactTestUtils = require('react-dom/test-utils');
399
@@ -429,7 +404,9 @@ describe('creating element with ref in constructor', () => {
404 expect(function() {
405 ReactTestUtils.renderIntoDocument(<RefTest />);
406 }).toThrowError(
432 - ReactDOMFeatureFlags.useFiber ? fiberDevErrorMessage : devErrorMessage,
407 + 'Element ref was specified as a string (p) but no owner was ' +
408 + 'set. You may have multiple copies of React loaded. ' +
409 + '(details: https://fb.me/react-refs-must-have-owner).',
410 );
411 } finally {
412 __DEV__ = originalDev;
@@ -446,9 +423,10 @@ describe('creating element with ref in constructor', () => {
423 expect(function() {
424 ReactTestUtils.renderIntoDocument(<RefTest />);
425 }).toThrowError(
449 - ReactDOMFeatureFlags.useFiber
450 - ? fiberProdErrorMessage
451 - : prodErrorMessage,
426 + 'Minified React error #149; visit ' +
427 + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=149&args[]=p ' +
428 + 'for the full message or use the non-minified dev environment for full errors and additional ' +
429 + 'helpful warnings.',
430 );
431 } finally {
432 __DEV__ = originalDev;
src/renderers/dom/__tests__/ReactDOMProduction-test.js
+49 -53
@@ -9,8 +9,6 @@
9 'use strict';
10
11 describe('ReactDOMProduction', () => {
12 - var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 -
12 var React;
13 var ReactDOM;
14 var ReactDOMServer;
@@ -195,7 +193,7 @@ describe('ReactDOMProduction', () => {
193 });
194
195 it('should throw with an error code in production', () => {
198 - const errorCode = ReactDOMFeatureFlags.useFiber ? 152 : 109;
196 + const errorCode = 152;
197 expect(function() {
198 class Component extends React.Component {
199 render() {
@@ -235,57 +233,55 @@ describe('ReactDOMProduction', () => {
233 }
234 });
235
238 - if (ReactDOMFeatureFlags.useFiber) {
239 - // This test is originally from ReactDOMFiber-test but we replicate it here
240 - // to avoid production-only regressions because of host context differences
241 - // in dev and prod.
242 - it('should keep track of namespace across portals in production', () => {
243 - var svgEls, htmlEls;
244 - var expectSVG = {ref: el => svgEls.push(el)};
245 - var expectHTML = {ref: el => htmlEls.push(el)};
246 - var usePortal = function(tree) {
247 - return ReactDOM.createPortal(tree, document.createElement('div'));
248 - };
249 - var assertNamespacesMatch = function(tree) {
250 - var container = document.createElement('div');
251 - svgEls = [];
252 - htmlEls = [];
253 - ReactDOM.render(tree, container);
254 - svgEls.forEach(el => {
255 - expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg');
256 - });
257 - htmlEls.forEach(el => {
258 - expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml');
259 - });
260 - ReactDOM.unmountComponentAtNode(container);
261 - expect(container.innerHTML).toBe('');
262 - };
236 + // This test is originally from ReactDOMFiber-test but we replicate it here
237 + // to avoid production-only regressions because of host context differences
238 + // in dev and prod.
239 + it('should keep track of namespace across portals in production', () => {
240 + var svgEls, htmlEls;
241 + var expectSVG = {ref: el => svgEls.push(el)};
242 + var expectHTML = {ref: el => htmlEls.push(el)};
243 + var usePortal = function(tree) {
244 + return ReactDOM.createPortal(tree, document.createElement('div'));
245 + };
246 + var assertNamespacesMatch = function(tree) {
247 + var container = document.createElement('div');
248 + svgEls = [];
249 + htmlEls = [];
250 + ReactDOM.render(tree, container);
251 + svgEls.forEach(el => {
252 + expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg');
253 + });
254 + htmlEls.forEach(el => {
255 + expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml');
256 + });
257 + ReactDOM.unmountComponentAtNode(container);
258 + expect(container.innerHTML).toBe('');
259 + };
260
264 - assertNamespacesMatch(
265 - <div {...expectHTML}>
266 - <svg {...expectSVG}>
267 - <foreignObject {...expectSVG}>
268 - <p {...expectHTML} />
269 - {usePortal(
261 + assertNamespacesMatch(
262 + <div {...expectHTML}>
263 + <svg {...expectSVG}>
264 + <foreignObject {...expectSVG}>
265 + <p {...expectHTML} />
266 + {usePortal(
267 + <svg {...expectSVG}>
268 + <image {...expectSVG} />
269 <svg {...expectSVG}>
270 <image {...expectSVG} />
272 - <svg {...expectSVG}>
273 - <image {...expectSVG} />
274 - <foreignObject {...expectSVG}>
275 - <p {...expectHTML} />
276 - </foreignObject>
277 - {usePortal(<p {...expectHTML} />)}
278 - </svg>
279 - <image {...expectSVG} />
280 - </svg>,
281 - )}
282 - <p {...expectHTML} />
283 - </foreignObject>
284 - <image {...expectSVG} />
285 - </svg>
286 - <p {...expectHTML} />
287 - </div>,
288 - );
289 - });
290 - }
271 + <foreignObject {...expectSVG}>
272 + <p {...expectHTML} />
273 + </foreignObject>
274 + {usePortal(<p {...expectHTML} />)}
275 + </svg>
276 + <image {...expectSVG} />
277 + </svg>,
278 + )}
279 + <p {...expectHTML} />
280 + </foreignObject>
281 + <image {...expectSVG} />
282 + </svg>
283 + <p {...expectHTML} />
284 + </div>,
285 + );
286 + });
287 });
src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js
+908 -913
@@ -11,7 +11,6 @@
11
12 var React = require('react');
13 var ReactDOM = require('react-dom');
14 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
14 var ReactTestUtils = require('react-dom/test-utils');
15 var PropTypes = require('prop-types');
16
@@ -74,1076 +73,1072 @@ describe('ReactDOMFiber', () => {
73 expect(called).toEqual(true);
74 });
75
77 - if (ReactDOMFeatureFlags.useFiber) {
78 - it('should render a component returning strings directly from render', () => {
79 - const Text = ({value}) => value;
76 + it('should render a component returning strings directly from render', () => {
77 + const Text = ({value}) => value;
78
81 - ReactDOM.render(<Text value="foo" />, container);
82 - expect(container.textContent).toEqual('foo');
83 - });
79 + ReactDOM.render(<Text value="foo" />, container);
80 + expect(container.textContent).toEqual('foo');
81 + });
82
85 - it('should render a component returning numbers directly from render', () => {
86 - const Text = ({value}) => value;
83 + it('should render a component returning numbers directly from render', () => {
84 + const Text = ({value}) => value;
85
88 - ReactDOM.render(<Text value={10} />, container);
86 + ReactDOM.render(<Text value={10} />, container);
87
90 - expect(container.textContent).toEqual('10');
91 - });
88 + expect(container.textContent).toEqual('10');
89 + });
90
93 - it('finds the DOM Text node of a string child', () => {
94 - class Text extends React.Component {
95 - render() {
96 - return this.props.value;
97 - }
91 + it('finds the DOM Text node of a string child', () => {
92 + class Text extends React.Component {
93 + render() {
94 + return this.props.value;
95 }
96 + }
97
100 - let instance = null;
101 - ReactDOM.render(
102 - <Text value="foo" ref={ref => (instance = ref)} />,
103 - container,
104 - );
98 + let instance = null;
99 + ReactDOM.render(
100 + <Text value="foo" ref={ref => (instance = ref)} />,
101 + container,
102 + );
103
106 - const textNode = ReactDOM.findDOMNode(instance);
107 - expect(textNode).toBe(container.firstChild);
108 - expect(textNode.nodeType).toBe(3);
109 - expect(textNode.nodeValue).toBe('foo');
110 - });
104 + const textNode = ReactDOM.findDOMNode(instance);
105 + expect(textNode).toBe(container.firstChild);
106 + expect(textNode.nodeType).toBe(3);
107 + expect(textNode.nodeValue).toBe('foo');
108 + });
109
112 - it('finds the first child when a component returns a fragment', () => {
113 - class Fragment extends React.Component {
114 - render() {
115 - return [<div key="a" />, <span key="b" />];
116 - }
110 + it('finds the first child when a component returns a fragment', () => {
111 + class Fragment extends React.Component {
112 + render() {
113 + return [<div key="a" />, <span key="b" />];
114 }
115 + }
116
119 - let instance = null;
120 - ReactDOM.render(<Fragment ref={ref => (instance = ref)} />, container);
117 + let instance = null;
118 + ReactDOM.render(<Fragment ref={ref => (instance = ref)} />, container);
119
122 - expect(container.childNodes.length).toBe(2);
120 + expect(container.childNodes.length).toBe(2);
121
124 - const firstNode = ReactDOM.findDOMNode(instance);
125 - expect(firstNode).toBe(container.firstChild);
126 - expect(firstNode.tagName).toBe('DIV');
127 - });
122 + const firstNode = ReactDOM.findDOMNode(instance);
123 + expect(firstNode).toBe(container.firstChild);
124 + expect(firstNode.tagName).toBe('DIV');
125 + });
126
129 - it('finds the first child even when fragment is nested', () => {
130 - class Wrapper extends React.Component {
131 - render() {
132 - return this.props.children;
133 - }
127 + it('finds the first child even when fragment is nested', () => {
128 + class Wrapper extends React.Component {
129 + render() {
130 + return this.props.children;
131 }
132 + }
133
136 - class Fragment extends React.Component {
137 - render() {
138 - return [<Wrapper key="a"><div /></Wrapper>, <span key="b" />];
139 - }
134 + class Fragment extends React.Component {
135 + render() {
136 + return [<Wrapper key="a"><div /></Wrapper>, <span key="b" />];
137 }
138 + }
139
142 - let instance = null;
143 - ReactDOM.render(<Fragment ref={ref => (instance = ref)} />, container);
140 + let instance = null;
141 + ReactDOM.render(<Fragment ref={ref => (instance = ref)} />, container);
142
145 - expect(container.childNodes.length).toBe(2);
143 + expect(container.childNodes.length).toBe(2);
144
147 - const firstNode = ReactDOM.findDOMNode(instance);
148 - expect(firstNode).toBe(container.firstChild);
149 - expect(firstNode.tagName).toBe('DIV');
150 - });
145 + const firstNode = ReactDOM.findDOMNode(instance);
146 + expect(firstNode).toBe(container.firstChild);
147 + expect(firstNode.tagName).toBe('DIV');
148 + });
149
152 - it('finds the first child even when first child renders null', () => {
153 - class NullComponent extends React.Component {
154 - render() {
155 - return null;
156 - }
150 + it('finds the first child even when first child renders null', () => {
151 + class NullComponent extends React.Component {
152 + render() {
153 + return null;
154 }
155 + }
156
159 - class Fragment extends React.Component {
160 - render() {
161 - return [<NullComponent key="a" />, <div key="b" />, <span key="c" />];
162 - }
157 + class Fragment extends React.Component {
158 + render() {
159 + return [<NullComponent key="a" />, <div key="b" />, <span key="c" />];
160 }
161 + }
162
165 - let instance = null;
166 - ReactDOM.render(<Fragment ref={ref => (instance = ref)} />, container);
163 + let instance = null;
164 + ReactDOM.render(<Fragment ref={ref => (instance = ref)} />, container);
165
168 - expect(container.childNodes.length).toBe(2);
166 + expect(container.childNodes.length).toBe(2);
167
170 - const firstNode = ReactDOM.findDOMNode(instance);
171 - expect(firstNode).toBe(container.firstChild);
172 - expect(firstNode.tagName).toBe('DIV');
173 - });
174 - }
175 -
176 - if (ReactDOMFeatureFlags.useFiber) {
177 - var svgEls, htmlEls, mathEls;
178 - var expectSVG = {ref: el => svgEls.push(el)};
179 - var expectHTML = {ref: el => htmlEls.push(el)};
180 - var expectMath = {ref: el => mathEls.push(el)};
181 -
182 - var usePortal = function(tree) {
183 - return ReactDOM.createPortal(tree, document.createElement('div'));
184 - };
185 -
186 - var assertNamespacesMatch = function(tree) {
187 - container = document.createElement('div');
188 - svgEls = [];
189 - htmlEls = [];
190 - mathEls = [];
191 -
192 - ReactDOM.render(tree, container);
193 - svgEls.forEach(el => {
194 - expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg');
195 - });
196 - htmlEls.forEach(el => {
197 - expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml');
198 - });
199 - mathEls.forEach(el => {
200 - expect(el.namespaceURI).toBe('http://www.w3.org/1998/Math/MathML');
201 - });
202 -
203 - ReactDOM.unmountComponentAtNode(container);
204 - expect(container.innerHTML).toBe('');
205 - };
206 -
207 - it('should render one portal', () => {
208 - var portalContainer = document.createElement('div');
209 -
210 - ReactDOM.render(
211 - <div>
212 - {ReactDOM.createPortal(<div>portal</div>, portalContainer)}
213 - </div>,
214 - container,
215 - );
216 - expect(portalContainer.innerHTML).toBe('<div>portal</div>');
217 - expect(container.innerHTML).toBe('<div></div>');
168 + const firstNode = ReactDOM.findDOMNode(instance);
169 + expect(firstNode).toBe(container.firstChild);
170 + expect(firstNode.tagName).toBe('DIV');
171 + });
172
219 - ReactDOM.unmountComponentAtNode(container);
220 - expect(portalContainer.innerHTML).toBe('');
221 - expect(container.innerHTML).toBe('');
222 - });
173 + var svgEls, htmlEls, mathEls;
174 + var expectSVG = {ref: el => svgEls.push(el)};
175 + var expectHTML = {ref: el => htmlEls.push(el)};
176 + var expectMath = {ref: el => mathEls.push(el)};
177
224 - // TODO: remove in React 17
225 - it('should support unstable_createPortal alias', () => {
226 - var portalContainer = document.createElement('div');
178 + var usePortal = function(tree) {
179 + return ReactDOM.createPortal(tree, document.createElement('div'));
180 + };
181
228 - ReactDOM.render(
229 - <div>
230 - {ReactDOM.unstable_createPortal(<div>portal</div>, portalContainer)}
231 - </div>,
232 - container,
233 - );
234 - expect(portalContainer.innerHTML).toBe('<div>portal</div>');
235 - expect(container.innerHTML).toBe('<div></div>');
182 + var assertNamespacesMatch = function(tree) {
183 + container = document.createElement('div');
184 + svgEls = [];
185 + htmlEls = [];
186 + mathEls = [];
187
237 - ReactDOM.unmountComponentAtNode(container);
238 - expect(portalContainer.innerHTML).toBe('');
239 - expect(container.innerHTML).toBe('');
188 + ReactDOM.render(tree, container);
189 + svgEls.forEach(el => {
190 + expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg');
191 + });
192 + htmlEls.forEach(el => {
193 + expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml');
194 + });
195 + mathEls.forEach(el => {
196 + expect(el.namespaceURI).toBe('http://www.w3.org/1998/Math/MathML');
197 });
198
242 - it('should render many portals', () => {
243 - var portalContainer1 = document.createElement('div');
244 - var portalContainer2 = document.createElement('div');
245 -
246 - var ops = [];
247 - class Child extends React.Component {
248 - componentDidMount() {
249 - ops.push(`${this.props.name} componentDidMount`);
250 - }
251 - componentDidUpdate() {
252 - ops.push(`${this.props.name} componentDidUpdate`);
253 - }
254 - componentWillUnmount() {
255 - ops.push(`${this.props.name} componentWillUnmount`);
256 - }
257 - render() {
258 - return <div>{this.props.name}</div>;
259 - }
260 - }
199 + ReactDOM.unmountComponentAtNode(container);
200 + expect(container.innerHTML).toBe('');
201 + };
202 +
203 + it('should render one portal', () => {
204 + var portalContainer = document.createElement('div');
205 +
206 + ReactDOM.render(
207 + <div>
208 + {ReactDOM.createPortal(<div>portal</div>, portalContainer)}
209 + </div>,
210 + container,
211 + );
212 + expect(portalContainer.innerHTML).toBe('<div>portal</div>');
213 + expect(container.innerHTML).toBe('<div></div>');
214 +
215 + ReactDOM.unmountComponentAtNode(container);
216 + expect(portalContainer.innerHTML).toBe('');
217 + expect(container.innerHTML).toBe('');
218 + });
219
262 - class Parent extends React.Component {
263 - componentDidMount() {
264 - ops.push(`Parent:${this.props.step} componentDidMount`);
265 - }
266 - componentDidUpdate() {
267 - ops.push(`Parent:${this.props.step} componentDidUpdate`);
268 - }
269 - componentWillUnmount() {
270 - ops.push(`Parent:${this.props.step} componentWillUnmount`);
271 - }
272 - render() {
273 - const {step} = this.props;
274 - return [
275 - <Child key="a" name={`normal[0]:${step}`} />,
276 - ReactDOM.createPortal(
277 - <Child key="b" name={`portal1[0]:${step}`} />,
278 - portalContainer1,
279 - ),
280 - <Child key="c" name={`normal[1]:${step}`} />,
281 - ReactDOM.createPortal(
282 - [
283 - <Child key="d" name={`portal2[0]:${step}`} />,
284 - <Child key="e" name={`portal2[1]:${step}`} />,
285 - ],
286 - portalContainer2,
287 - ),
288 - ];
289 - }
290 - }
220 + // TODO: remove in React 17
221 + it('should support unstable_createPortal alias', () => {
222 + var portalContainer = document.createElement('div');
223 +
224 + ReactDOM.render(
225 + <div>
226 + {ReactDOM.unstable_createPortal(<div>portal</div>, portalContainer)}
227 + </div>,
228 + container,
229 + );
230 + expect(portalContainer.innerHTML).toBe('<div>portal</div>');
231 + expect(container.innerHTML).toBe('<div></div>');
232 +
233 + ReactDOM.unmountComponentAtNode(container);
234 + expect(portalContainer.innerHTML).toBe('');
235 + expect(container.innerHTML).toBe('');
236 + });
237
292 - ReactDOM.render(<Parent step="a" />, container);
293 - expect(portalContainer1.innerHTML).toBe('<div>portal1[0]:a</div>');
294 - expect(portalContainer2.innerHTML).toBe(
295 - '<div>portal2[0]:a</div><div>portal2[1]:a</div>',
296 - );
297 - expect(container.innerHTML).toBe(
298 - '<div>normal[0]:a</div><div>normal[1]:a</div>',
299 - );
300 - expect(ops).toEqual([
301 - 'normal[0]:a componentDidMount',
302 - 'portal1[0]:a componentDidMount',
303 - 'normal[1]:a componentDidMount',
304 - 'portal2[0]:a componentDidMount',
305 - 'portal2[1]:a componentDidMount',
306 - 'Parent:a componentDidMount',
307 - ]);
308 -
309 - ops.length = 0;
310 - ReactDOM.render(<Parent step="b" />, container);
311 - expect(portalContainer1.innerHTML).toBe('<div>portal1[0]:b</div>');
312 - expect(portalContainer2.innerHTML).toBe(
313 - '<div>portal2[0]:b</div><div>portal2[1]:b</div>',
314 - );
315 - expect(container.innerHTML).toBe(
316 - '<div>normal[0]:b</div><div>normal[1]:b</div>',
317 - );
318 - expect(ops).toEqual([
319 - 'normal[0]:b componentDidUpdate',
320 - 'portal1[0]:b componentDidUpdate',
321 - 'normal[1]:b componentDidUpdate',
322 - 'portal2[0]:b componentDidUpdate',
323 - 'portal2[1]:b componentDidUpdate',
324 - 'Parent:b componentDidUpdate',
325 - ]);
326 -
327 - ops.length = 0;
328 - ReactDOM.unmountComponentAtNode(container);
329 - expect(portalContainer1.innerHTML).toBe('');
330 - expect(portalContainer2.innerHTML).toBe('');
331 - expect(container.innerHTML).toBe('');
332 - expect(ops).toEqual([
333 - 'Parent:b componentWillUnmount',
334 - 'normal[0]:b componentWillUnmount',
335 - 'portal1[0]:b componentWillUnmount',
336 - 'normal[1]:b componentWillUnmount',
337 - 'portal2[0]:b componentWillUnmount',
338 - 'portal2[1]:b componentWillUnmount',
339 - ]);
340 - });
238 + it('should render many portals', () => {
239 + var portalContainer1 = document.createElement('div');
240 + var portalContainer2 = document.createElement('div');
241
342 - it('should render nested portals', () => {
343 - var portalContainer1 = document.createElement('div');
344 - var portalContainer2 = document.createElement('div');
345 - var portalContainer3 = document.createElement('div');
242 + var ops = [];
243 + class Child extends React.Component {
244 + componentDidMount() {
245 + ops.push(`${this.props.name} componentDidMount`);
246 + }
247 + componentDidUpdate() {
248 + ops.push(`${this.props.name} componentDidUpdate`);
249 + }
250 + componentWillUnmount() {
251 + ops.push(`${this.props.name} componentWillUnmount`);
252 + }
253 + render() {
254 + return <div>{this.props.name}</div>;
255 + }
256 + }
257
347 - ReactDOM.render(
348 - [
349 - <div key="a">normal[0]</div>,
258 + class Parent extends React.Component {
259 + componentDidMount() {
260 + ops.push(`Parent:${this.props.step} componentDidMount`);
261 + }
262 + componentDidUpdate() {
263 + ops.push(`Parent:${this.props.step} componentDidUpdate`);
264 + }
265 + componentWillUnmount() {
266 + ops.push(`Parent:${this.props.step} componentWillUnmount`);
267 + }
268 + render() {
269 + const {step} = this.props;
270 + return [
271 + <Child key="a" name={`normal[0]:${step}`} />,
272 + ReactDOM.createPortal(
273 + <Child key="b" name={`portal1[0]:${step}`} />,
274 + portalContainer1,
275 + ),
276 + <Child key="c" name={`normal[1]:${step}`} />,
277 ReactDOM.createPortal(
278 [
352 - <div key="b">portal1[0]</div>,
353 - ReactDOM.createPortal(
354 - <div key="c">portal2[0]</div>,
355 - portalContainer2,
356 - ),
357 - ReactDOM.createPortal(
358 - <div key="d">portal3[0]</div>,
359 - portalContainer3,
360 - ),
361 - <div key="e">portal1[1]</div>,
279 + <Child key="d" name={`portal2[0]:${step}`} />,
280 + <Child key="e" name={`portal2[1]:${step}`} />,
281 ],
363 - portalContainer1,
282 + portalContainer2,
283 ),
365 - <div key="f">normal[1]</div>,
366 - ],
367 - container,
368 - );
369 - expect(portalContainer1.innerHTML).toBe(
370 - '<div>portal1[0]</div><div>portal1[1]</div>',
371 - );
372 - expect(portalContainer2.innerHTML).toBe('<div>portal2[0]</div>');
373 - expect(portalContainer3.innerHTML).toBe('<div>portal3[0]</div>');
374 - expect(container.innerHTML).toBe(
375 - '<div>normal[0]</div><div>normal[1]</div>',
376 - );
284 + ];
285 + }
286 + }
287
378 - ReactDOM.unmountComponentAtNode(container);
379 - expect(portalContainer1.innerHTML).toBe('');
380 - expect(portalContainer2.innerHTML).toBe('');
381 - expect(portalContainer3.innerHTML).toBe('');
382 - expect(container.innerHTML).toBe('');
383 - });
288 + ReactDOM.render(<Parent step="a" />, container);
289 + expect(portalContainer1.innerHTML).toBe('<div>portal1[0]:a</div>');
290 + expect(portalContainer2.innerHTML).toBe(
291 + '<div>portal2[0]:a</div><div>portal2[1]:a</div>',
292 + );
293 + expect(container.innerHTML).toBe(
294 + '<div>normal[0]:a</div><div>normal[1]:a</div>',
295 + );
296 + expect(ops).toEqual([
297 + 'normal[0]:a componentDidMount',
298 + 'portal1[0]:a componentDidMount',
299 + 'normal[1]:a componentDidMount',
300 + 'portal2[0]:a componentDidMount',
301 + 'portal2[1]:a componentDidMount',
302 + 'Parent:a componentDidMount',
303 + ]);
304 +
305 + ops.length = 0;
306 + ReactDOM.render(<Parent step="b" />, container);
307 + expect(portalContainer1.innerHTML).toBe('<div>portal1[0]:b</div>');
308 + expect(portalContainer2.innerHTML).toBe(
309 + '<div>portal2[0]:b</div><div>portal2[1]:b</div>',
310 + );
311 + expect(container.innerHTML).toBe(
312 + '<div>normal[0]:b</div><div>normal[1]:b</div>',
313 + );
314 + expect(ops).toEqual([
315 + 'normal[0]:b componentDidUpdate',
316 + 'portal1[0]:b componentDidUpdate',
317 + 'normal[1]:b componentDidUpdate',
318 + 'portal2[0]:b componentDidUpdate',
319 + 'portal2[1]:b componentDidUpdate',
320 + 'Parent:b componentDidUpdate',
321 + ]);
322 +
323 + ops.length = 0;
324 + ReactDOM.unmountComponentAtNode(container);
325 + expect(portalContainer1.innerHTML).toBe('');
326 + expect(portalContainer2.innerHTML).toBe('');
327 + expect(container.innerHTML).toBe('');
328 + expect(ops).toEqual([
329 + 'Parent:b componentWillUnmount',
330 + 'normal[0]:b componentWillUnmount',
331 + 'portal1[0]:b componentWillUnmount',
332 + 'normal[1]:b componentWillUnmount',
333 + 'portal2[0]:b componentWillUnmount',
334 + 'portal2[1]:b componentWillUnmount',
335 + ]);
336 + });
337
385 - it('should reconcile portal children', () => {
386 - var portalContainer = document.createElement('div');
338 + it('should render nested portals', () => {
339 + var portalContainer1 = document.createElement('div');
340 + var portalContainer2 = document.createElement('div');
341 + var portalContainer3 = document.createElement('div');
342 +
343 + ReactDOM.render(
344 + [
345 + <div key="a">normal[0]</div>,
346 + ReactDOM.createPortal(
347 + [
348 + <div key="b">portal1[0]</div>,
349 + ReactDOM.createPortal(
350 + <div key="c">portal2[0]</div>,
351 + portalContainer2,
352 + ),
353 + ReactDOM.createPortal(
354 + <div key="d">portal3[0]</div>,
355 + portalContainer3,
356 + ),
357 + <div key="e">portal1[1]</div>,
358 + ],
359 + portalContainer1,
360 + ),
361 + <div key="f">normal[1]</div>,
362 + ],
363 + container,
364 + );
365 + expect(portalContainer1.innerHTML).toBe(
366 + '<div>portal1[0]</div><div>portal1[1]</div>',
367 + );
368 + expect(portalContainer2.innerHTML).toBe('<div>portal2[0]</div>');
369 + expect(portalContainer3.innerHTML).toBe('<div>portal3[0]</div>');
370 + expect(container.innerHTML).toBe(
371 + '<div>normal[0]</div><div>normal[1]</div>',
372 + );
373 +
374 + ReactDOM.unmountComponentAtNode(container);
375 + expect(portalContainer1.innerHTML).toBe('');
376 + expect(portalContainer2.innerHTML).toBe('');
377 + expect(portalContainer3.innerHTML).toBe('');
378 + expect(container.innerHTML).toBe('');
379 + });
380
388 - ReactDOM.render(
389 - <div>
390 - {ReactDOM.createPortal(<div>portal:1</div>, portalContainer)}
391 - </div>,
392 - container,
393 - );
394 - expect(portalContainer.innerHTML).toBe('<div>portal:1</div>');
395 - expect(container.innerHTML).toBe('<div></div>');
396 -
397 - ReactDOM.render(
398 - <div>
399 - {ReactDOM.createPortal(<div>portal:2</div>, portalContainer)}
400 - </div>,
401 - container,
402 - );
403 - expect(portalContainer.innerHTML).toBe('<div>portal:2</div>');
404 - expect(container.innerHTML).toBe('<div></div>');
405 -
406 - ReactDOM.render(
407 - <div>
408 - {ReactDOM.createPortal(<p>portal:3</p>, portalContainer)}
409 - </div>,
410 - container,
411 - );
412 - expect(portalContainer.innerHTML).toBe('<p>portal:3</p>');
413 - expect(container.innerHTML).toBe('<div></div>');
414 -
415 - ReactDOM.render(
416 - <div>
417 - {ReactDOM.createPortal(['Hi', 'Bye'], portalContainer)}
418 - </div>,
419 - container,
420 - );
421 - expect(portalContainer.innerHTML).toBe('HiBye');
422 - expect(container.innerHTML).toBe('<div></div>');
423 -
424 - ReactDOM.render(
425 - <div>
426 - {ReactDOM.createPortal(['Bye', 'Hi'], portalContainer)}
427 - </div>,
428 - container,
429 - );
430 - expect(portalContainer.innerHTML).toBe('ByeHi');
431 - expect(container.innerHTML).toBe('<div></div>');
432 -
433 - ReactDOM.render(
434 - <div>
435 - {ReactDOM.createPortal(null, portalContainer)}
436 - </div>,
437 - container,
438 - );
439 - expect(portalContainer.innerHTML).toBe('');
440 - expect(container.innerHTML).toBe('<div></div>');
441 - });
381 + it('should reconcile portal children', () => {
382 + var portalContainer = document.createElement('div');
383 +
384 + ReactDOM.render(
385 + <div>
386 + {ReactDOM.createPortal(<div>portal:1</div>, portalContainer)}
387 + </div>,
388 + container,
389 + );
390 + expect(portalContainer.innerHTML).toBe('<div>portal:1</div>');
391 + expect(container.innerHTML).toBe('<div></div>');
392 +
393 + ReactDOM.render(
394 + <div>
395 + {ReactDOM.createPortal(<div>portal:2</div>, portalContainer)}
396 + </div>,
397 + container,
398 + );
399 + expect(portalContainer.innerHTML).toBe('<div>portal:2</div>');
400 + expect(container.innerHTML).toBe('<div></div>');
401 +
402 + ReactDOM.render(
403 + <div>
404 + {ReactDOM.createPortal(<p>portal:3</p>, portalContainer)}
405 + </div>,
406 + container,
407 + );
408 + expect(portalContainer.innerHTML).toBe('<p>portal:3</p>');
409 + expect(container.innerHTML).toBe('<div></div>');
410 +
411 + ReactDOM.render(
412 + <div>
413 + {ReactDOM.createPortal(['Hi', 'Bye'], portalContainer)}
414 + </div>,
415 + container,
416 + );
417 + expect(portalContainer.innerHTML).toBe('HiBye');
418 + expect(container.innerHTML).toBe('<div></div>');
419 +
420 + ReactDOM.render(
421 + <div>
422 + {ReactDOM.createPortal(['Bye', 'Hi'], portalContainer)}
423 + </div>,
424 + container,
425 + );
426 + expect(portalContainer.innerHTML).toBe('ByeHi');
427 + expect(container.innerHTML).toBe('<div></div>');
428 +
429 + ReactDOM.render(
430 + <div>
431 + {ReactDOM.createPortal(null, portalContainer)}
432 + </div>,
433 + container,
434 + );
435 + expect(portalContainer.innerHTML).toBe('');
436 + expect(container.innerHTML).toBe('<div></div>');
437 + });
438
443 - it('should keep track of namespace across portals (simple)', () => {
444 - assertNamespacesMatch(
445 - <svg {...expectSVG}>
446 - <image {...expectSVG} />
447 - {usePortal(<div {...expectHTML} />)}
448 - <image {...expectSVG} />
449 - </svg>,
450 - );
451 - assertNamespacesMatch(
452 - <math {...expectMath}>
453 - <mi {...expectMath} />
454 - {usePortal(<div {...expectHTML} />)}
455 - <mi {...expectMath} />
456 - </math>,
457 - );
458 - assertNamespacesMatch(
459 - <div {...expectHTML}>
460 - <p {...expectHTML} />
461 - {usePortal(
462 - <svg {...expectSVG}>
463 - <image {...expectSVG} />
464 - </svg>,
465 - )}
466 - <p {...expectHTML} />
467 - </div>,
468 - );
469 - });
439 + it('should keep track of namespace across portals (simple)', () => {
440 + assertNamespacesMatch(
441 + <svg {...expectSVG}>
442 + <image {...expectSVG} />
443 + {usePortal(<div {...expectHTML} />)}
444 + <image {...expectSVG} />
445 + </svg>,
446 + );
447 + assertNamespacesMatch(
448 + <math {...expectMath}>
449 + <mi {...expectMath} />
450 + {usePortal(<div {...expectHTML} />)}
451 + <mi {...expectMath} />
452 + </math>,
453 + );
454 + assertNamespacesMatch(
455 + <div {...expectHTML}>
456 + <p {...expectHTML} />
457 + {usePortal(
458 + <svg {...expectSVG}>
459 + <image {...expectSVG} />
460 + </svg>,
461 + )}
462 + <p {...expectHTML} />
463 + </div>,
464 + );
465 + });
466
471 - it('should keep track of namespace across portals (medium)', () => {
472 - assertNamespacesMatch(
473 - <svg {...expectSVG}>
474 - <image {...expectSVG} />
475 - {usePortal(<div {...expectHTML} />)}
476 - <image {...expectSVG} />
477 - {usePortal(<div {...expectHTML} />)}
478 - <image {...expectSVG} />
479 - </svg>,
480 - );
481 - assertNamespacesMatch(
482 - <div {...expectHTML}>
483 - <math {...expectMath}>
484 - <mi {...expectMath} />
485 - {usePortal(
486 - <svg {...expectSVG}>
487 - <image {...expectSVG} />
488 - </svg>,
489 - )}
490 - </math>
491 - <p {...expectHTML} />
492 - </div>,
493 - );
494 - assertNamespacesMatch(
467 + it('should keep track of namespace across portals (medium)', () => {
468 + assertNamespacesMatch(
469 + <svg {...expectSVG}>
470 + <image {...expectSVG} />
471 + {usePortal(<div {...expectHTML} />)}
472 + <image {...expectSVG} />
473 + {usePortal(<div {...expectHTML} />)}
474 + <image {...expectSVG} />
475 + </svg>,
476 + );
477 + assertNamespacesMatch(
478 + <div {...expectHTML}>
479 <math {...expectMath}>
480 <mi {...expectMath} />
481 {usePortal(
482 <svg {...expectSVG}>
483 <image {...expectSVG} />
500 - <foreignObject {...expectSVG}>
501 - <p {...expectHTML} />
502 - <math {...expectMath}>
503 - <mi {...expectMath} />
504 - </math>
505 - <p {...expectHTML} />
506 - </foreignObject>
507 - <image {...expectSVG} />
508 - </svg>,
509 - )}
510 - <mi {...expectMath} />
511 - </math>,
512 - );
513 - assertNamespacesMatch(
514 - <div {...expectHTML}>
515 - {usePortal(
516 - <svg {...expectSVG}>
517 - {usePortal(<div {...expectHTML} />)}
518 - <image {...expectSVG} />
484 </svg>,
485 )}
521 - <p {...expectHTML} />
522 - </div>,
523 - );
524 - assertNamespacesMatch(
525 - <svg {...expectSVG}>
486 + </math>
487 + <p {...expectHTML} />
488 + </div>,
489 + );
490 + assertNamespacesMatch(
491 + <math {...expectMath}>
492 + <mi {...expectMath} />
493 + {usePortal(
494 + <svg {...expectSVG}>
495 + <image {...expectSVG} />
496 + <foreignObject {...expectSVG}>
497 + <p {...expectHTML} />
498 + <math {...expectMath}>
499 + <mi {...expectMath} />
500 + </math>
501 + <p {...expectHTML} />
502 + </foreignObject>
503 + <image {...expectSVG} />
504 + </svg>,
505 + )}
506 + <mi {...expectMath} />
507 + </math>,
508 + );
509 + assertNamespacesMatch(
510 + <div {...expectHTML}>
511 + {usePortal(
512 <svg {...expectSVG}>
513 {usePortal(<div {...expectHTML} />)}
514 <image {...expectSVG} />
529 - </svg>
515 + </svg>,
516 + )}
517 + <p {...expectHTML} />
518 + </div>,
519 + );
520 + assertNamespacesMatch(
521 + <svg {...expectSVG}>
522 + <svg {...expectSVG}>
523 + {usePortal(<div {...expectHTML} />)}
524 <image {...expectSVG} />
531 - </svg>,
532 - );
533 - });
525 + </svg>
526 + <image {...expectSVG} />
527 + </svg>,
528 + );
529 + });
530
535 - it('should keep track of namespace across portals (complex)', () => {
536 - assertNamespacesMatch(
537 - <div {...expectHTML}>
538 - {usePortal(
539 - <svg {...expectSVG}>
540 - <image {...expectSVG} />
541 - </svg>,
542 - )}
543 - <p {...expectHTML} />
531 + it('should keep track of namespace across portals (complex)', () => {
532 + assertNamespacesMatch(
533 + <div {...expectHTML}>
534 + {usePortal(
535 <svg {...expectSVG}>
536 <image {...expectSVG} />
546 - </svg>
537 + </svg>,
538 + )}
539 + <p {...expectHTML} />
540 + <svg {...expectSVG}>
541 + <image {...expectSVG} />
542 + </svg>
543 + <svg {...expectSVG}>
544 <svg {...expectSVG}>
548 - <svg {...expectSVG}>
549 - <image {...expectSVG} />
550 - </svg>
545 <image {...expectSVG} />
546 </svg>
553 - <p {...expectHTML} />
554 - </div>,
555 - );
556 - assertNamespacesMatch(
557 - <div {...expectHTML}>
547 + <image {...expectSVG} />
548 + </svg>
549 + <p {...expectHTML} />
550 + </div>,
551 + );
552 + assertNamespacesMatch(
553 + <div {...expectHTML}>
554 + <svg {...expectSVG}>
555 <svg {...expectSVG}>
559 - <svg {...expectSVG}>
560 - <image {...expectSVG} />
561 - {usePortal(
556 + <image {...expectSVG} />
557 + {usePortal(
558 + <svg {...expectSVG}>
559 + <image {...expectSVG} />
560 <svg {...expectSVG}>
561 <image {...expectSVG} />
564 - <svg {...expectSVG}>
565 - <image {...expectSVG} />
566 - </svg>
567 - <image {...expectSVG} />
568 - </svg>,
569 - )}
570 - <image {...expectSVG} />
571 - <foreignObject {...expectSVG}>
572 - <p {...expectHTML} />
573 - {usePortal(<p {...expectHTML} />)}
574 - <p {...expectHTML} />
575 - </foreignObject>
576 - </svg>
562 + </svg>
563 + <image {...expectSVG} />
564 + </svg>,
565 + )}
566 <image {...expectSVG} />
578 - </svg>
579 - <p {...expectHTML} />
580 - </div>,
581 - );
582 - assertNamespacesMatch(
583 - <div {...expectHTML}>
584 - <svg {...expectSVG}>
567 <foreignObject {...expectSVG}>
568 <p {...expectHTML} />
587 - {usePortal(
588 - <svg {...expectSVG}>
589 - <image {...expectSVG} />
590 - <svg {...expectSVG}>
591 - <image {...expectSVG} />
592 - <foreignObject {...expectSVG}>
593 - <p {...expectHTML} />
594 - </foreignObject>
595 - {usePortal(<p {...expectHTML} />)}
596 - </svg>
597 - <image {...expectSVG} />
598 - </svg>,
599 - )}
569 + {usePortal(<p {...expectHTML} />)}
570 <p {...expectHTML} />
571 </foreignObject>
602 - <image {...expectSVG} />
572 </svg>
604 - <p {...expectHTML} />
605 - </div>,
606 - );
607 - });
608 -
609 - it('should unwind namespaces on uncaught errors', () => {
610 - function BrokenRender() {
611 - throw new Error('Hello');
612 - }
613 -
614 - expect(() => {
615 - assertNamespacesMatch(
616 - <svg {...expectSVG}>
617 - <BrokenRender />
618 - </svg>,
619 - );
620 - }).toThrow('Hello');
621 - assertNamespacesMatch(<div {...expectHTML} />);
622 - });
623 -
624 - it('should unwind namespaces on caught errors', () => {
625 - function BrokenRender() {
626 - throw new Error('Hello');
627 - }
628 -
629 - class ErrorBoundary extends React.Component {
630 - state = {error: null};
631 - componentDidCatch(error) {
632 - this.setState({error});
633 - }
634 - render() {
635 - if (this.state.error) {
636 - return <p {...expectHTML} />;
637 - }
638 - return this.props.children;
639 - }
640 - }
641 -
642 - assertNamespacesMatch(
573 + <image {...expectSVG} />
574 + </svg>
575 + <p {...expectHTML} />
576 + </div>,
577 + );
578 + assertNamespacesMatch(
579 + <div {...expectHTML}>
580 <svg {...expectSVG}>
581 <foreignObject {...expectSVG}>
645 - <ErrorBoundary>
646 - <math {...expectMath}>
647 - <BrokenRender />
648 - </math>
649 - </ErrorBoundary>
582 + <p {...expectHTML} />
583 + {usePortal(
584 + <svg {...expectSVG}>
585 + <image {...expectSVG} />
586 + <svg {...expectSVG}>
587 + <image {...expectSVG} />
588 + <foreignObject {...expectSVG}>
589 + <p {...expectHTML} />
590 + </foreignObject>
591 + {usePortal(<p {...expectHTML} />)}
592 + </svg>
593 + <image {...expectSVG} />
594 + </svg>,
595 + )}
596 + <p {...expectHTML} />
597 </foreignObject>
598 <image {...expectSVG} />
652 - </svg>,
653 - );
654 - assertNamespacesMatch(<div {...expectHTML} />);
655 - });
656 -
657 - it('should unwind namespaces on caught errors in a portal', () => {
658 - function BrokenRender() {
659 - throw new Error('Hello');
660 - }
599 + </svg>
600 + <p {...expectHTML} />
601 + </div>,
602 + );
603 + });
604
662 - class ErrorBoundary extends React.Component {
663 - state = {error: null};
664 - componentDidCatch(error) {
665 - this.setState({error});
666 - }
667 - render() {
668 - if (this.state.error) {
669 - return <image {...expectSVG} />;
670 - }
671 - return this.props.children;
672 - }
673 - }
605 + it('should unwind namespaces on uncaught errors', () => {
606 + function BrokenRender() {
607 + throw new Error('Hello');
608 + }
609
610 + expect(() => {
611 assertNamespacesMatch(
612 <svg {...expectSVG}>
677 - <ErrorBoundary>
678 - {usePortal(
679 - <div {...expectHTML}>
680 - <math {...expectMath}>
681 - <BrokenRender />)
682 - </math>
683 - </div>,
684 - )}
685 - </ErrorBoundary>
686 - {usePortal(<div {...expectHTML} />)}
613 + <BrokenRender />
614 </svg>,
615 );
689 - });
690 -
691 - it('should pass portal context when rendering subtree elsewhere', () => {
692 - var portalContainer = document.createElement('div');
616 + }).toThrow('Hello');
617 + assertNamespacesMatch(<div {...expectHTML} />);
618 + });
619
694 - class Component extends React.Component {
695 - static contextTypes = {
696 - foo: PropTypes.string.isRequired,
697 - };
620 + it('should unwind namespaces on caught errors', () => {
621 + function BrokenRender() {
622 + throw new Error('Hello');
623 + }
624
699 - render() {
700 - return <div>{this.context.foo}</div>;
625 + class ErrorBoundary extends React.Component {
626 + state = {error: null};
627 + componentDidCatch(error) {
628 + this.setState({error});
629 + }
630 + render() {
631 + if (this.state.error) {
632 + return <p {...expectHTML} />;
633 }
634 + return this.props.children;
635 }
636 + }
637
704 - class Parent extends React.Component {
705 - static childContextTypes = {
706 - foo: PropTypes.string.isRequired,
707 - };
638 + assertNamespacesMatch(
639 + <svg {...expectSVG}>
640 + <foreignObject {...expectSVG}>
641 + <ErrorBoundary>
642 + <math {...expectMath}>
643 + <BrokenRender />
644 + </math>
645 + </ErrorBoundary>
646 + </foreignObject>
647 + <image {...expectSVG} />
648 + </svg>,
649 + );
650 + assertNamespacesMatch(<div {...expectHTML} />);
651 + });
652
709 - getChildContext() {
710 - return {
711 - foo: 'bar',
712 - };
713 - }
653 + it('should unwind namespaces on caught errors in a portal', () => {
654 + function BrokenRender() {
655 + throw new Error('Hello');
656 + }
657
715 - render() {
716 - return ReactDOM.createPortal(<Component />, portalContainer);
658 + class ErrorBoundary extends React.Component {
659 + state = {error: null};
660 + componentDidCatch(error) {
661 + this.setState({error});
662 + }
663 + render() {
664 + if (this.state.error) {
665 + return <image {...expectSVG} />;
666 }
667 + return this.props.children;
668 }
669 + }
670
720 - ReactDOM.render(<Parent />, container);
721 - expect(container.innerHTML).toBe('');
722 - expect(portalContainer.innerHTML).toBe('<div>bar</div>');
723 - });
671 + assertNamespacesMatch(
672 + <svg {...expectSVG}>
673 + <ErrorBoundary>
674 + {usePortal(
675 + <div {...expectHTML}>
676 + <math {...expectMath}>
677 + <BrokenRender />)
678 + </math>
679 + </div>,
680 + )}
681 + </ErrorBoundary>
682 + {usePortal(<div {...expectHTML} />)}
683 + </svg>,
684 + );
685 + });
686
725 - it('should update portal context if it changes due to setState', () => {
726 - var portalContainer = document.createElement('div');
687 + it('should pass portal context when rendering subtree elsewhere', () => {
688 + var portalContainer = document.createElement('div');
689
728 - class Component extends React.Component {
729 - static contextTypes = {
730 - foo: PropTypes.string.isRequired,
731 - getFoo: PropTypes.func.isRequired,
732 - };
690 + class Component extends React.Component {
691 + static contextTypes = {
692 + foo: PropTypes.string.isRequired,
693 + };
694
734 - render() {
735 - return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
736 - }
695 + render() {
696 + return <div>{this.context.foo}</div>;
697 }
698 + }
699
739 - class Parent extends React.Component {
740 - static childContextTypes = {
741 - foo: PropTypes.string.isRequired,
742 - getFoo: PropTypes.func.isRequired,
743 - };
700 + class Parent extends React.Component {
701 + static childContextTypes = {
702 + foo: PropTypes.string.isRequired,
703 + };
704
745 - state = {
746 - bar: 'initial',
705 + getChildContext() {
706 + return {
707 + foo: 'bar',
708 };
709 + }
710
749 - getChildContext() {
750 - return {
751 - foo: this.state.bar,
752 - getFoo: () => this.state.bar,
753 - };
754 - }
755 -
756 - render() {
757 - return ReactDOM.createPortal(<Component />, portalContainer);
758 - }
711 + render() {
712 + return ReactDOM.createPortal(<Component />, portalContainer);
713 }
714 + }
715
761 - var instance = ReactDOM.render(<Parent />, container);
762 - expect(portalContainer.innerHTML).toBe('<div>initial-initial</div>');
763 - expect(container.innerHTML).toBe('');
764 - instance.setState({bar: 'changed'});
765 - expect(portalContainer.innerHTML).toBe('<div>changed-changed</div>');
766 - expect(container.innerHTML).toBe('');
767 - });
716 + ReactDOM.render(<Parent />, container);
717 + expect(container.innerHTML).toBe('');
718 + expect(portalContainer.innerHTML).toBe('<div>bar</div>');
719 + });
720
769 - it('should update portal context if it changes due to re-render', () => {
770 - var portalContainer = document.createElement('div');
721 + it('should update portal context if it changes due to setState', () => {
722 + var portalContainer = document.createElement('div');
723
772 - class Component extends React.Component {
773 - static contextTypes = {
774 - foo: PropTypes.string.isRequired,
775 - getFoo: PropTypes.func.isRequired,
776 - };
724 + class Component extends React.Component {
725 + static contextTypes = {
726 + foo: PropTypes.string.isRequired,
727 + getFoo: PropTypes.func.isRequired,
728 + };
729
778 - render() {
779 - return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
780 - }
730 + render() {
731 + return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
732 }
733 + }
734
783 - class Parent extends React.Component {
784 - static childContextTypes = {
785 - foo: PropTypes.string.isRequired,
786 - getFoo: PropTypes.func.isRequired,
787 - };
735 + class Parent extends React.Component {
736 + static childContextTypes = {
737 + foo: PropTypes.string.isRequired,
738 + getFoo: PropTypes.func.isRequired,
739 + };
740
789 - getChildContext() {
790 - return {
791 - foo: this.props.bar,
792 - getFoo: () => this.props.bar,
793 - };
794 - }
741 + state = {
742 + bar: 'initial',
743 + };
744
796 - render() {
797 - return ReactDOM.createPortal(<Component />, portalContainer);
798 - }
745 + getChildContext() {
746 + return {
747 + foo: this.state.bar,
748 + getFoo: () => this.state.bar,
749 + };
750 }
751
801 - ReactDOM.render(<Parent bar="initial" />, container);
802 - expect(portalContainer.innerHTML).toBe('<div>initial-initial</div>');
803 - expect(container.innerHTML).toBe('');
804 - ReactDOM.render(<Parent bar="changed" />, container);
805 - expect(portalContainer.innerHTML).toBe('<div>changed-changed</div>');
806 - expect(container.innerHTML).toBe('');
807 - });
808 -
809 - it('findDOMNode should find dom element after expanding a fragment', () => {
810 - class MyNode extends React.Component {
811 - render() {
812 - return !this.props.flag
813 - ? [<div key="a" />]
814 - : [<span key="b" />, <div key="a" />];
815 - }
752 + render() {
753 + return ReactDOM.createPortal(<Component />, portalContainer);
754 }
755 + }
756
818 - var myNodeA = ReactDOM.render(<MyNode />, container);
819 - var a = ReactDOM.findDOMNode(myNodeA);
820 - expect(a.tagName).toBe('DIV');
757 + var instance = ReactDOM.render(<Parent />, container);
758 + expect(portalContainer.innerHTML).toBe('<div>initial-initial</div>');
759 + expect(container.innerHTML).toBe('');
760 + instance.setState({bar: 'changed'});
761 + expect(portalContainer.innerHTML).toBe('<div>changed-changed</div>');
762 + expect(container.innerHTML).toBe('');
763 + });
764
822 - var myNodeB = ReactDOM.render(<MyNode flag={true} />, container);
823 - expect(myNodeA === myNodeB).toBe(true);
765 + it('should update portal context if it changes due to re-render', () => {
766 + var portalContainer = document.createElement('div');
767
825 - var b = ReactDOM.findDOMNode(myNodeB);
826 - expect(b.tagName).toBe('SPAN');
827 - });
768 + class Component extends React.Component {
769 + static contextTypes = {
770 + foo: PropTypes.string.isRequired,
771 + getFoo: PropTypes.func.isRequired,
772 + };
773
829 - it('should bubble events from the portal to the parent', () => {
830 - var portalContainer = document.createElement('div');
774 + render() {
775 + return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
776 + }
777 + }
778
832 - var ops = [];
833 - var portal = null;
779 + class Parent extends React.Component {
780 + static childContextTypes = {
781 + foo: PropTypes.string.isRequired,
782 + getFoo: PropTypes.func.isRequired,
783 + };
784
835 - ReactDOM.render(
836 - <div onClick={() => ops.push('parent clicked')}>
837 - {ReactDOM.createPortal(
838 - <div
839 - onClick={() => ops.push('portal clicked')}
840 - ref={n => (portal = n)}>
841 - portal
842 - </div>,
843 - portalContainer,
844 - )}
845 - </div>,
846 - container,
847 - );
785 + getChildContext() {
786 + return {
787 + foo: this.props.bar,
788 + getFoo: () => this.props.bar,
789 + };
790 + }
791
849 - expect(portal.tagName).toBe('DIV');
792 + render() {
793 + return ReactDOM.createPortal(<Component />, portalContainer);
794 + }
795 + }
796
851 - var fakeNativeEvent = {};
852 - ReactTestUtils.simulateNativeEventOnNode(
853 - 'topClick',
854 - portal,
855 - fakeNativeEvent,
856 - );
797 + ReactDOM.render(<Parent bar="initial" />, container);
798 + expect(portalContainer.innerHTML).toBe('<div>initial-initial</div>');
799 + expect(container.innerHTML).toBe('');
800 + ReactDOM.render(<Parent bar="changed" />, container);
801 + expect(portalContainer.innerHTML).toBe('<div>changed-changed</div>');
802 + expect(container.innerHTML).toBe('');
803 + });
804
858 - expect(ops).toEqual(['portal clicked', 'parent clicked']);
859 - });
805 + it('findDOMNode should find dom element after expanding a fragment', () => {
806 + class MyNode extends React.Component {
807 + render() {
808 + return !this.props.flag
809 + ? [<div key="a" />]
810 + : [<span key="b" />, <div key="a" />];
811 + }
812 + }
813
861 - it('should not onMouseLeave when staying in the portal', () => {
862 - var portalContainer = document.createElement('div');
814 + var myNodeA = ReactDOM.render(<MyNode />, container);
815 + var a = ReactDOM.findDOMNode(myNodeA);
816 + expect(a.tagName).toBe('DIV');
817
864 - var ops = [];
865 - var firstTarget = null;
866 - var secondTarget = null;
867 - var thirdTarget = null;
818 + var myNodeB = ReactDOM.render(<MyNode flag={true} />, container);
819 + expect(myNodeA === myNodeB).toBe(true);
820
869 - function simulateMouseMove(from, to) {
870 - if (from) {
871 - ReactTestUtils.simulateNativeEventOnNode('topMouseOut', from, {
872 - target: from,
873 - relatedTarget: to,
874 - });
875 - }
876 - if (to) {
877 - ReactTestUtils.simulateNativeEventOnNode('topMouseOver', to, {
878 - target: to,
879 - relatedTarget: from,
880 - });
881 - }
882 - }
821 + var b = ReactDOM.findDOMNode(myNodeB);
822 + expect(b.tagName).toBe('SPAN');
823 + });
824
884 - ReactDOM.render(
885 - <div>
886 - <div
887 - onMouseEnter={() => ops.push('enter parent')}
888 - onMouseLeave={() => ops.push('leave parent')}>
889 - <div ref={n => (firstTarget = n)} />
890 - {ReactDOM.createPortal(
891 - <div
892 - onMouseEnter={() => ops.push('enter portal')}
893 - onMouseLeave={() => ops.push('leave portal')}
894 - ref={n => (secondTarget = n)}>
895 - portal
896 - </div>,
897 - portalContainer,
898 - )}
899 - </div>
900 - <div ref={n => (thirdTarget = n)} />
901 - </div>,
902 - container,
903 - );
825 + it('should bubble events from the portal to the parent', () => {
826 + var portalContainer = document.createElement('div');
827
905 - simulateMouseMove(null, firstTarget);
906 - expect(ops).toEqual(['enter parent']);
828 + var ops = [];
829 + var portal = null;
830
908 - ops = [];
831 + ReactDOM.render(
832 + <div onClick={() => ops.push('parent clicked')}>
833 + {ReactDOM.createPortal(
834 + <div
835 + onClick={() => ops.push('portal clicked')}
836 + ref={n => (portal = n)}>
837 + portal
838 + </div>,
839 + portalContainer,
840 + )}
841 + </div>,
842 + container,
843 + );
844 +
845 + expect(portal.tagName).toBe('DIV');
846 +
847 + var fakeNativeEvent = {};
848 + ReactTestUtils.simulateNativeEventOnNode(
849 + 'topClick',
850 + portal,
851 + fakeNativeEvent,
852 + );
853 +
854 + expect(ops).toEqual(['portal clicked', 'parent clicked']);
855 + });
856
910 - simulateMouseMove(firstTarget, secondTarget);
911 - expect(ops).toEqual([
912 - // Parent did not invoke leave because we're still inside the portal.
913 - 'enter portal',
914 - ]);
857 + it('should not onMouseLeave when staying in the portal', () => {
858 + var portalContainer = document.createElement('div');
859
916 - ops = [];
860 + var ops = [];
861 + var firstTarget = null;
862 + var secondTarget = null;
863 + var thirdTarget = null;
864
918 - simulateMouseMove(secondTarget, thirdTarget);
919 - expect(ops).toEqual([
920 - 'leave portal',
921 - 'leave parent', // Only when we leave the portal does onMouseLeave fire.
922 - ]);
923 - });
865 + function simulateMouseMove(from, to) {
866 + if (from) {
867 + ReactTestUtils.simulateNativeEventOnNode('topMouseOut', from, {
868 + target: from,
869 + relatedTarget: to,
870 + });
871 + }
872 + if (to) {
873 + ReactTestUtils.simulateNativeEventOnNode('topMouseOver', to, {
874 + target: to,
875 + relatedTarget: from,
876 + });
877 + }
878 + }
879
925 - it('should throw on bad createPortal argument', () => {
926 - expect(() => {
927 - ReactDOM.createPortal(<div>portal</div>, null);
928 - }).toThrow('Target container is not a DOM element.');
929 - expect(() => {
930 - ReactDOM.createPortal(<div>portal</div>, document.createTextNode('hi'));
931 - }).toThrow('Target container is not a DOM element.');
932 - });
880 + ReactDOM.render(
881 + <div>
882 + <div
883 + onMouseEnter={() => ops.push('enter parent')}
884 + onMouseLeave={() => ops.push('leave parent')}>
885 + <div ref={n => (firstTarget = n)} />
886 + {ReactDOM.createPortal(
887 + <div
888 + onMouseEnter={() => ops.push('enter portal')}
889 + onMouseLeave={() => ops.push('leave portal')}
890 + ref={n => (secondTarget = n)}>
891 + portal
892 + </div>,
893 + portalContainer,
894 + )}
895 + </div>
896 + <div ref={n => (thirdTarget = n)} />
897 + </div>,
898 + container,
899 + );
900 +
901 + simulateMouseMove(null, firstTarget);
902 + expect(ops).toEqual(['enter parent']);
903 +
904 + ops = [];
905 +
906 + simulateMouseMove(firstTarget, secondTarget);
907 + expect(ops).toEqual([
908 + // Parent did not invoke leave because we're still inside the portal.
909 + 'enter portal',
910 + ]);
911 +
912 + ops = [];
913 +
914 + simulateMouseMove(secondTarget, thirdTarget);
915 + expect(ops).toEqual([
916 + 'leave portal',
917 + 'leave parent', // Only when we leave the portal does onMouseLeave fire.
918 + ]);
919 + });
920
934 - it('should warn for non-functional event listeners', () => {
935 - spyOn(console, 'error');
936 - class Example extends React.Component {
937 - render() {
938 - return <div onClick="woops" />;
939 - }
921 + it('should throw on bad createPortal argument', () => {
922 + expect(() => {
923 + ReactDOM.createPortal(<div>portal</div>, null);
924 + }).toThrow('Target container is not a DOM element.');
925 + expect(() => {
926 + ReactDOM.createPortal(<div>portal</div>, document.createTextNode('hi'));
927 + }).toThrow('Target container is not a DOM element.');
928 + });
929 +
930 + it('should warn for non-functional event listeners', () => {
931 + spyOn(console, 'error');
932 + class Example extends React.Component {
933 + render() {
934 + return <div onClick="woops" />;
935 }
941 - ReactDOM.render(<Example />, container);
942 - expectDev(console.error.calls.count()).toBe(1);
943 - expectDev(
944 - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
945 - ).toContain(
946 - 'Expected `onClick` listener to be a function, instead got a value of `string` type.\n' +
947 - ' in div (at **)\n' +
948 - ' in Example (at **)',
949 - );
950 - });
936 + }
937 + ReactDOM.render(<Example />, container);
938 + expectDev(console.error.calls.count()).toBe(1);
939 + expectDev(
940 + normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
941 + ).toContain(
942 + 'Expected `onClick` listener to be a function, instead got a value of `string` type.\n' +
943 + ' in div (at **)\n' +
944 + ' in Example (at **)',
945 + );
946 + });
947
952 - it('should not update event handlers until commit', () => {
953 - let ops = [];
954 - const handlerA = () => ops.push('A');
955 - const handlerB = () => ops.push('B');
948 + it('should not update event handlers until commit', () => {
949 + let ops = [];
950 + const handlerA = () => ops.push('A');
951 + const handlerB = () => ops.push('B');
952
957 - class Example extends React.Component {
958 - state = {flip: false, count: 0};
959 - flip() {
960 - this.setState({flip: true, count: this.state.count + 1});
961 - }
962 - tick() {
963 - this.setState({count: this.state.count + 1});
964 - }
965 - render() {
966 - const useB = !this.props.forceA && this.state.flip;
967 - return <div onClick={useB ? handlerB : handlerA} />;
968 - }
953 + class Example extends React.Component {
954 + state = {flip: false, count: 0};
955 + flip() {
956 + this.setState({flip: true, count: this.state.count + 1});
957 }
970 -
971 - class Click extends React.Component {
972 - constructor() {
973 - super();
974 - click(node);
975 - }
976 - render() {
977 - return null;
978 - }
958 + tick() {
959 + this.setState({count: this.state.count + 1});
960 }
961 + render() {
962 + const useB = !this.props.forceA && this.state.flip;
963 + return <div onClick={useB ? handlerB : handlerA} />;
964 + }
965 + }
966
981 - let inst;
982 - ReactDOM.render([<Example key="a" ref={n => (inst = n)} />], container);
983 - const node = container.firstChild;
984 - expect(node.tagName).toEqual('DIV');
985 -
986 - function click(target) {
987 - var fakeNativeEvent = {};
988 - ReactTestUtils.simulateNativeEventOnNode(
989 - 'topClick',
990 - target,
991 - fakeNativeEvent,
992 - );
967 + class Click extends React.Component {
968 + constructor() {
969 + super();
970 + click(node);
971 }
972 + render() {
973 + return null;
974 + }
975 + }
976
995 - click(node);
977 + let inst;
978 + ReactDOM.render([<Example key="a" ref={n => (inst = n)} />], container);
979 + const node = container.firstChild;
980 + expect(node.tagName).toEqual('DIV');
981
997 - expect(ops).toEqual(['A']);
998 - ops = [];
982 + function click(target) {
983 + var fakeNativeEvent = {};
984 + ReactTestUtils.simulateNativeEventOnNode(
985 + 'topClick',
986 + target,
987 + fakeNativeEvent,
988 + );
989 + }
990
1000 - // Render with the other event handler.
1001 - inst.flip();
991 + click(node);
992
1003 - click(node);
993 + expect(ops).toEqual(['A']);
994 + ops = [];
995
1005 - expect(ops).toEqual(['B']);
1006 - ops = [];
996 + // Render with the other event handler.
997 + inst.flip();
998
1008 - // Rerender without changing any props.
1009 - inst.tick();
999 + click(node);
1000
1011 - click(node);
1001 + expect(ops).toEqual(['B']);
1002 + ops = [];
1003
1013 - expect(ops).toEqual(['B']);
1014 - ops = [];
1004 + // Rerender without changing any props.
1005 + inst.tick();
1006
1016 - // Render a flip back to the A handler. The second component invokes the
1017 - // click handler during render to simulate a click during an aborted
1018 - // render. I use this hack because at current time we don't have a way to
1019 - // test aborted ReactDOM renders.
1020 - ReactDOM.render(
1021 - [<Example key="a" forceA={true} />, <Click key="b" />],
1022 - container,
1023 - );
1007 + click(node);
1008
1025 - // Because the new click handler has not yet committed, we should still
1026 - // invoke B.
1027 - expect(ops).toEqual(['B']);
1028 - ops = [];
1009 + expect(ops).toEqual(['B']);
1010 + ops = [];
1011
1030 - // Any click that happens after commit, should invoke A.
1031 - click(node);
1032 - expect(ops).toEqual(['A']);
1033 - });
1012 + // Render a flip back to the A handler. The second component invokes the
1013 + // click handler during render to simulate a click during an aborted
1014 + // render. I use this hack because at current time we don't have a way to
1015 + // test aborted ReactDOM renders.
1016 + ReactDOM.render(
1017 + [<Example key="a" forceA={true} />, <Click key="b" />],
1018 + container,
1019 + );
1020
1035 - it('should not crash encountering low-priority tree', () => {
1036 - ReactDOM.render(
1037 - <div hidden={true}>
1038 - <div />
1039 - </div>,
1040 - container,
1041 - );
1042 - });
1021 + // Because the new click handler has not yet committed, we should still
1022 + // invoke B.
1023 + expect(ops).toEqual(['B']);
1024 + ops = [];
1025
1044 - it('should not warn when rendering into an empty container', () => {
1045 - spyOn(console, 'error');
1046 - ReactDOM.render(<div>foo</div>, container);
1047 - expect(container.innerHTML).toBe('<div>foo</div>');
1048 - ReactDOM.render(null, container);
1049 - expect(container.innerHTML).toBe('');
1050 - expectDev(console.error.calls.count()).toBe(0);
1051 - ReactDOM.render(<div>bar</div>, container);
1052 - expect(container.innerHTML).toBe('<div>bar</div>');
1053 - expectDev(console.error.calls.count()).toBe(0);
1054 - });
1026 + // Any click that happens after commit, should invoke A.
1027 + click(node);
1028 + expect(ops).toEqual(['A']);
1029 + });
1030
1056 - it('should warn when replacing a container which was manually updated outside of React', () => {
1057 - spyOn(console, 'error');
1058 - // when not messing with the DOM outside of React
1059 - ReactDOM.render(<div key="1">foo</div>, container);
1060 - ReactDOM.render(<div key="1">bar</div>, container);
1061 - expect(container.innerHTML).toBe('<div>bar</div>');
1062 - // then we mess with the DOM before an update
1063 - // we know this will error - that is expected right now
1064 - // It's an error of type 'NotFoundError' with no message
1065 - expect(() => {
1066 - container.innerHTML = '<div>MEOW.</div>';
1067 - ReactDOM.render(<div key="2">baz</div>, container);
1068 - }).toThrowError();
1069 - expectDev(console.error.calls.count()).toBe(1);
1070 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1071 - 'render(...): ' +
1072 - 'It looks like the React-rendered content of this container was ' +
1073 - 'removed without using React. This is not supported and will ' +
1074 - 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1075 - 'to empty a container.',
1076 - );
1077 - });
1031 + it('should not crash encountering low-priority tree', () => {
1032 + ReactDOM.render(
1033 + <div hidden={true}>
1034 + <div />
1035 + </div>,
1036 + container,
1037 + );
1038 + });
1039
1079 - it('should warn when doing an update to a container manually updated outside of React', () => {
1080 - spyOn(console, 'error');
1081 - // when not messing with the DOM outside of React
1082 - ReactDOM.render(<div>foo</div>, container);
1083 - ReactDOM.render(<div>bar</div>, container);
1084 - expect(container.innerHTML).toBe('<div>bar</div>');
1085 - // then we mess with the DOM before an update
1086 - container.innerHTML = '<div>MEOW.</div>';
1087 - ReactDOM.render(<div>baz</div>, container);
1088 - // silently fails to update
1089 - expectDev(console.error.calls.count()).toBe(1);
1090 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1091 - 'render(...): ' +
1092 - 'It looks like the React-rendered content of this container was ' +
1093 - 'removed without using React. This is not supported and will ' +
1094 - 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1095 - 'to empty a container.',
1096 - );
1097 - });
1040 + it('should not warn when rendering into an empty container', () => {
1041 + spyOn(console, 'error');
1042 + ReactDOM.render(<div>foo</div>, container);
1043 + expect(container.innerHTML).toBe('<div>foo</div>');
1044 + ReactDOM.render(null, container);
1045 + expect(container.innerHTML).toBe('');
1046 + expectDev(console.error.calls.count()).toBe(0);
1047 + ReactDOM.render(<div>bar</div>, container);
1048 + expect(container.innerHTML).toBe('<div>bar</div>');
1049 + expectDev(console.error.calls.count()).toBe(0);
1050 + });
1051
1099 - it('should warn when doing an update to a container manually cleared outside of React', () => {
1100 - spyOn(console, 'error');
1101 - // when not messing with the DOM outside of React
1102 - ReactDOM.render(<div>foo</div>, container);
1103 - ReactDOM.render(<div>bar</div>, container);
1104 - expect(container.innerHTML).toBe('<div>bar</div>');
1105 - // then we mess with the DOM before an update
1106 - container.innerHTML = '';
1107 - ReactDOM.render(<div>baz</div>, container);
1108 - // silently fails to update
1109 - expectDev(console.error.calls.count()).toBe(1);
1110 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
1111 - 'render(...): ' +
1112 - 'It looks like the React-rendered content of this container was ' +
1113 - 'removed without using React. This is not supported and will ' +
1114 - 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1115 - 'to empty a container.',
1116 - );
1117 - });
1052 + it('should warn when replacing a container which was manually updated outside of React', () => {
1053 + spyOn(console, 'error');
1054 + // when not messing with the DOM outside of React
1055 + ReactDOM.render(<div key="1">foo</div>, container);
1056 + ReactDOM.render(<div key="1">bar</div>, container);
1057 + expect(container.innerHTML).toBe('<div>bar</div>');
1058 + // then we mess with the DOM before an update
1059 + // we know this will error - that is expected right now
1060 + // It's an error of type 'NotFoundError' with no message
1061 + expect(() => {
1062 + container.innerHTML = '<div>MEOW.</div>';
1063 + ReactDOM.render(<div key="2">baz</div>, container);
1064 + }).toThrowError();
1065 + expectDev(console.error.calls.count()).toBe(1);
1066 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
1067 + 'render(...): ' +
1068 + 'It looks like the React-rendered content of this container was ' +
1069 + 'removed without using React. This is not supported and will ' +
1070 + 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1071 + 'to empty a container.',
1072 + );
1073 + });
1074
1119 - it('should render a text component with a text DOM node on the same document as the container', () => {
1120 - // 1. Create a new document through the use of iframe
1121 - // 2. Set up the spy to make asserts when a text component
1122 - // is rendered inside the iframe container
1123 - var textContent = 'Hello world';
1124 - var iframe = document.createElement('iframe');
1125 - document.body.appendChild(iframe);
1126 - var iframeDocument = iframe.contentDocument;
1127 - iframeDocument.write(
1128 - '<!DOCTYPE html><html><head></head><body><div></div></body></html>',
1129 - );
1130 - iframeDocument.close();
1131 - var iframeContainer = iframeDocument.body.firstChild;
1075 + it('should warn when doing an update to a container manually updated outside of React', () => {
1076 + spyOn(console, 'error');
1077 + // when not messing with the DOM outside of React
1078 + ReactDOM.render(<div>foo</div>, container);
1079 + ReactDOM.render(<div>bar</div>, container);
1080 + expect(container.innerHTML).toBe('<div>bar</div>');
1081 + // then we mess with the DOM before an update
1082 + container.innerHTML = '<div>MEOW.</div>';
1083 + ReactDOM.render(<div>baz</div>, container);
1084 + // silently fails to update
1085 + expectDev(console.error.calls.count()).toBe(1);
1086 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
1087 + 'render(...): ' +
1088 + 'It looks like the React-rendered content of this container was ' +
1089 + 'removed without using React. This is not supported and will ' +
1090 + 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1091 + 'to empty a container.',
1092 + );
1093 + });
1094
1133 - var actualDocument;
1134 - var textNode;
1095 + it('should warn when doing an update to a container manually cleared outside of React', () => {
1096 + spyOn(console, 'error');
1097 + // when not messing with the DOM outside of React
1098 + ReactDOM.render(<div>foo</div>, container);
1099 + ReactDOM.render(<div>bar</div>, container);
1100 + expect(container.innerHTML).toBe('<div>bar</div>');
1101 + // then we mess with the DOM before an update
1102 + container.innerHTML = '';
1103 + ReactDOM.render(<div>baz</div>, container);
1104 + // silently fails to update
1105 + expectDev(console.error.calls.count()).toBe(1);
1106 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
1107 + 'render(...): ' +
1108 + 'It looks like the React-rendered content of this container was ' +
1109 + 'removed without using React. This is not supported and will ' +
1110 + 'cause errors. Instead, call ReactDOM.unmountComponentAtNode ' +
1111 + 'to empty a container.',
1112 + );
1113 + });
1114
1136 - spyOn(iframeContainer, 'appendChild').and.callFake(node => {
1137 - actualDocument = node.ownerDocument;
1138 - textNode = node;
1139 - });
1115 + it('should render a text component with a text DOM node on the same document as the container', () => {
1116 + // 1. Create a new document through the use of iframe
1117 + // 2. Set up the spy to make asserts when a text component
1118 + // is rendered inside the iframe container
1119 + var textContent = 'Hello world';
1120 + var iframe = document.createElement('iframe');
1121 + document.body.appendChild(iframe);
1122 + var iframeDocument = iframe.contentDocument;
1123 + iframeDocument.write(
1124 + '<!DOCTYPE html><html><head></head><body><div></div></body></html>',
1125 + );
1126 + iframeDocument.close();
1127 + var iframeContainer = iframeDocument.body.firstChild;
1128 +
1129 + var actualDocument;
1130 + var textNode;
1131 +
1132 + spyOn(iframeContainer, 'appendChild').and.callFake(node => {
1133 + actualDocument = node.ownerDocument;
1134 + textNode = node;
1135 + });
1136
1141 - ReactDOM.render(textContent, iframeContainer);
1137 + ReactDOM.render(textContent, iframeContainer);
1138
1143 - expect(textNode.textContent).toBe(textContent);
1144 - expect(actualDocument).not.toBe(document);
1145 - expect(actualDocument).toBe(iframeDocument);
1146 - expect(iframeContainer.appendChild).toHaveBeenCalledTimes(1);
1147 - });
1148 - }
1139 + expect(textNode.textContent).toBe(textContent);
1140 + expect(actualDocument).not.toBe(document);
1141 + expect(actualDocument).toBe(iframeDocument);
1142 + expect(iframeContainer.appendChild).toHaveBeenCalledTimes(1);
1143 + });
1144 });
src/renderers/dom/fiber/__tests__/ReactDOMFiberAsync-test.js
+222 -225
@@ -1,5 +1,4 @@
1 var React = require('react');
2 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
2 var ReactFeatureFlags = require('ReactFeatureFlags');
3
4 var ReactDOM;
@@ -25,270 +24,268 @@ describe('ReactDOMFiberAsync', () => {
24 expect(ops).toEqual(['Hi', 'Bye']);
25 });
26
28 - if (ReactDOMFeatureFlags.useFiber) {
29 - describe('with feature flag disabled', () => {
30 - beforeEach(() => {
31 - jest.resetModules();
32 - ReactFeatureFlags = require('ReactFeatureFlags');
33 - container = document.createElement('div');
34 - ReactFeatureFlags.enableAsyncSubtreeAPI = false;
35 - ReactDOM = require('react-dom');
36 - });
27 + describe('with feature flag disabled', () => {
28 + beforeEach(() => {
29 + jest.resetModules();
30 + ReactFeatureFlags = require('ReactFeatureFlags');
31 + container = document.createElement('div');
32 + ReactFeatureFlags.enableAsyncSubtreeAPI = false;
33 + ReactDOM = require('react-dom');
34 + });
35
38 - it('renders synchronously', () => {
39 - ReactDOM.render(
40 - <AsyncComponent><div>Hi</div></AsyncComponent>,
41 - container,
42 - );
43 - expect(container.textContent).toEqual('Hi');
44 -
45 - ReactDOM.render(
46 - <AsyncComponent><div>Bye</div></AsyncComponent>,
47 - container,
48 - );
49 - expect(container.textContent).toEqual('Bye');
50 - });
36 + it('renders synchronously', () => {
37 + ReactDOM.render(
38 + <AsyncComponent><div>Hi</div></AsyncComponent>,
39 + container,
40 + );
41 + expect(container.textContent).toEqual('Hi');
42 +
43 + ReactDOM.render(
44 + <AsyncComponent><div>Bye</div></AsyncComponent>,
45 + container,
46 + );
47 + expect(container.textContent).toEqual('Bye');
48 });
49 + });
50
53 - describe('with feature flag enabled', () => {
54 - beforeEach(() => {
55 - jest.resetModules();
56 - ReactFeatureFlags = require('ReactFeatureFlags');
57 - container = document.createElement('div');
58 - ReactFeatureFlags.enableAsyncSubtreeAPI = true;
59 - ReactDOM = require('react-dom');
60 - });
51 + describe('with feature flag enabled', () => {
52 + beforeEach(() => {
53 + jest.resetModules();
54 + ReactFeatureFlags = require('ReactFeatureFlags');
55 + container = document.createElement('div');
56 + ReactFeatureFlags.enableAsyncSubtreeAPI = true;
57 + ReactDOM = require('react-dom');
58 + });
59
62 - it('AsyncComponent at the root makes the entire tree async', () => {
63 - ReactDOM.render(
64 - <AsyncComponent><div>Hi</div></AsyncComponent>,
65 - container,
66 - );
67 - expect(container.textContent).toEqual('');
68 - jest.runAllTimers();
69 - expect(container.textContent).toEqual('Hi');
70 -
71 - ReactDOM.render(
72 - <AsyncComponent><div>Bye</div></AsyncComponent>,
73 - container,
74 - );
75 - expect(container.textContent).toEqual('Hi');
76 - jest.runAllTimers();
77 - expect(container.textContent).toEqual('Bye');
78 - });
60 + it('AsyncComponent at the root makes the entire tree async', () => {
61 + ReactDOM.render(
62 + <AsyncComponent><div>Hi</div></AsyncComponent>,
63 + container,
64 + );
65 + expect(container.textContent).toEqual('');
66 + jest.runAllTimers();
67 + expect(container.textContent).toEqual('Hi');
68 +
69 + ReactDOM.render(
70 + <AsyncComponent><div>Bye</div></AsyncComponent>,
71 + container,
72 + );
73 + expect(container.textContent).toEqual('Hi');
74 + jest.runAllTimers();
75 + expect(container.textContent).toEqual('Bye');
76 + });
77
80 - it('updates inside an async tree are async by default', () => {
81 - let instance;
82 - class Component extends React.Component {
83 - state = {step: 0};
84 - render() {
85 - instance = this;
86 - return <div>{this.state.step}</div>;
87 - }
78 + it('updates inside an async tree are async by default', () => {
79 + let instance;
80 + class Component extends React.Component {
81 + state = {step: 0};
82 + render() {
83 + instance = this;
84 + return <div>{this.state.step}</div>;
85 }
86 + }
87 +
88 + ReactDOM.render(
89 + <AsyncComponent><Component /></AsyncComponent>,
90 + container,
91 + );
92 + expect(container.textContent).toEqual('');
93 + jest.runAllTimers();
94 + expect(container.textContent).toEqual('0');
95 +
96 + instance.setState({step: 1});
97 + expect(container.textContent).toEqual('0');
98 + jest.runAllTimers();
99 + expect(container.textContent).toEqual('1');
100 + });
101
90 - ReactDOM.render(
91 - <AsyncComponent><Component /></AsyncComponent>,
92 - container,
93 - );
94 - expect(container.textContent).toEqual('');
95 - jest.runAllTimers();
96 - expect(container.textContent).toEqual('0');
97 -
98 - instance.setState({step: 1});
99 - expect(container.textContent).toEqual('0');
100 - jest.runAllTimers();
101 - expect(container.textContent).toEqual('1');
102 - });
103 -
104 - it('AsyncComponent creates an async subtree', () => {
105 - let instance;
106 - class Component extends React.unstable_AsyncComponent {
107 - state = {step: 0};
108 - render() {
109 - instance = this;
110 - return <div>{this.state.step}</div>;
111 - }
102 + it('AsyncComponent creates an async subtree', () => {
103 + let instance;
104 + class Component extends React.unstable_AsyncComponent {
105 + state = {step: 0};
106 + render() {
107 + instance = this;
108 + return <div>{this.state.step}</div>;
109 }
110 + }
111
114 - ReactDOM.render(<div><Component /></div>, container);
115 - jest.runAllTimers();
112 + ReactDOM.render(<div><Component /></div>, container);
113 + jest.runAllTimers();
114
117 - instance.setState({step: 1});
118 - expect(container.textContent).toEqual('0');
119 - jest.runAllTimers();
120 - expect(container.textContent).toEqual('1');
121 - });
115 + instance.setState({step: 1});
116 + expect(container.textContent).toEqual('0');
117 + jest.runAllTimers();
118 + expect(container.textContent).toEqual('1');
119 + });
120
123 - it('updates inside an async subtree are async by default', () => {
124 - class Component extends React.unstable_AsyncComponent {
125 - render() {
126 - return <Child />;
127 - }
121 + it('updates inside an async subtree are async by default', () => {
122 + class Component extends React.unstable_AsyncComponent {
123 + render() {
124 + return <Child />;
125 }
129 -
130 - let instance;
131 - class Child extends React.Component {
132 - state = {step: 0};
133 - render() {
134 - instance = this;
135 - return <div>{this.state.step}</div>;
136 - }
126 + }
127 +
128 + let instance;
129 + class Child extends React.Component {
130 + state = {step: 0};
131 + render() {
132 + instance = this;
133 + return <div>{this.state.step}</div>;
134 }
135 + }
136
139 - ReactDOM.render(<div><Component /></div>, container);
140 - jest.runAllTimers();
137 + ReactDOM.render(<div><Component /></div>, container);
138 + jest.runAllTimers();
139
142 - instance.setState({step: 1});
143 - expect(container.textContent).toEqual('0');
144 - jest.runAllTimers();
145 - expect(container.textContent).toEqual('1');
146 - });
140 + instance.setState({step: 1});
141 + expect(container.textContent).toEqual('0');
142 + jest.runAllTimers();
143 + expect(container.textContent).toEqual('1');
144 + });
145
148 - it('flushSync batches sync updates and flushes them at the end of the batch', () => {
149 - let ops = [];
150 - let instance;
151 -
152 - class Component extends React.Component {
153 - state = {text: ''};
154 - push(val) {
155 - this.setState(state => ({text: state.text + val}));
156 - }
157 - componentDidUpdate() {
158 - ops.push(this.state.text);
159 - }
160 - render() {
161 - instance = this;
162 - return <span>{this.state.text}</span>;
163 - }
146 + it('flushSync batches sync updates and flushes them at the end of the batch', () => {
147 + let ops = [];
148 + let instance;
149 +
150 + class Component extends React.Component {
151 + state = {text: ''};
152 + push(val) {
153 + this.setState(state => ({text: state.text + val}));
154 + }
155 + componentDidUpdate() {
156 + ops.push(this.state.text);
157 + }
158 + render() {
159 + instance = this;
160 + return <span>{this.state.text}</span>;
161 }
162 + }
163
166 - ReactDOM.render(<Component />, container);
164 + ReactDOM.render(<Component />, container);
165
168 - instance.push('A');
169 - expect(ops).toEqual(['A']);
170 - expect(container.textContent).toEqual('A');
166 + instance.push('A');
167 + expect(ops).toEqual(['A']);
168 + expect(container.textContent).toEqual('A');
169
172 - ReactDOM.flushSync(() => {
173 - instance.push('B');
174 - instance.push('C');
175 - // Not flushed yet
176 - expect(container.textContent).toEqual('A');
177 - expect(ops).toEqual(['A']);
178 - });
179 - expect(container.textContent).toEqual('ABC');
180 - expect(ops).toEqual(['A', 'ABC']);
181 - instance.push('D');
182 - expect(container.textContent).toEqual('ABCD');
183 - expect(ops).toEqual(['A', 'ABC', 'ABCD']);
170 + ReactDOM.flushSync(() => {
171 + instance.push('B');
172 + instance.push('C');
173 + // Not flushed yet
174 + expect(container.textContent).toEqual('A');
175 + expect(ops).toEqual(['A']);
176 });
177 + expect(container.textContent).toEqual('ABC');
178 + expect(ops).toEqual(['A', 'ABC']);
179 + instance.push('D');
180 + expect(container.textContent).toEqual('ABCD');
181 + expect(ops).toEqual(['A', 'ABC', 'ABCD']);
182 + });
183 +
184 + it('flushSync flushes updates even if nested inside another flushSync', () => {
185 + let ops = [];
186 + let instance;
187
186 - it('flushSync flushes updates even if nested inside another flushSync', () => {
187 - let ops = [];
188 - let instance;
189 -
190 - class Component extends React.Component {
191 - state = {text: ''};
192 - push(val) {
193 - this.setState(state => ({text: state.text + val}));
194 - }
195 - componentDidUpdate() {
196 - ops.push(this.state.text);
197 - }
198 - render() {
199 - instance = this;
200 - return <span>{this.state.text}</span>;
201 - }
188 + class Component extends React.Component {
189 + state = {text: ''};
190 + push(val) {
191 + this.setState(state => ({text: state.text + val}));
192 }
193 + componentDidUpdate() {
194 + ops.push(this.state.text);
195 + }
196 + render() {
197 + instance = this;
198 + return <span>{this.state.text}</span>;
199 + }
200 + }
201
204 - ReactDOM.render(<Component />, container);
202 + ReactDOM.render(<Component />, container);
203
206 - instance.push('A');
207 - expect(ops).toEqual(['A']);
204 + instance.push('A');
205 + expect(ops).toEqual(['A']);
206 + expect(container.textContent).toEqual('A');
207 +
208 + ReactDOM.flushSync(() => {
209 + instance.push('B');
210 + instance.push('C');
211 + // Not flushed yet
212 expect(container.textContent).toEqual('A');
213 + expect(ops).toEqual(['A']);
214
215 ReactDOM.flushSync(() => {
211 - instance.push('B');
212 - instance.push('C');
213 - // Not flushed yet
214 - expect(container.textContent).toEqual('A');
215 - expect(ops).toEqual(['A']);
216 -
217 - ReactDOM.flushSync(() => {
218 - instance.push('D');
219 - });
220 - // The nested flushSync caused everything to flush.
221 - expect(container.textContent).toEqual('ABCD');
222 - expect(ops).toEqual(['A', 'ABCD']);
216 + instance.push('D');
217 });
218 + // The nested flushSync caused everything to flush.
219 expect(container.textContent).toEqual('ABCD');
220 expect(ops).toEqual(['A', 'ABCD']);
221 });
222 + expect(container.textContent).toEqual('ABCD');
223 + expect(ops).toEqual(['A', 'ABCD']);
224 + });
225
228 - it('flushSync throws if already performing work', () => {
229 - class Component extends React.Component {
230 - componentDidUpdate() {
231 - ReactDOM.flushSync(() => {});
232 - }
233 - render() {
234 - return null;
235 - }
226 + it('flushSync throws if already performing work', () => {
227 + class Component extends React.Component {
228 + componentDidUpdate() {
229 + ReactDOM.flushSync(() => {});
230 }
231 + render() {
232 + return null;
233 + }
234 + }
235 +
236 + // Initial mount
237 + ReactDOM.render(<Component />, container);
238 + // Update
239 + expect(() => ReactDOM.render(<Component />, container)).toThrow(
240 + 'flushSync was called from inside a lifecycle method',
241 + );
242 + });
243
238 - // Initial mount
239 - ReactDOM.render(<Component />, container);
240 - // Update
241 - expect(() => ReactDOM.render(<Component />, container)).toThrow(
242 - 'flushSync was called from inside a lifecycle method',
243 - );
244 - });
244 + it('flushSync flushes updates before end of the tick', () => {
245 + let ops = [];
246 + let instance;
247
246 - it('flushSync flushes updates before end of the tick', () => {
247 - let ops = [];
248 - let instance;
249 -
250 - class Component extends React.unstable_AsyncComponent {
251 - state = {text: ''};
252 - push(val) {
253 - this.setState(state => ({text: state.text + val}));
254 - }
255 - componentDidUpdate() {
256 - ops.push(this.state.text);
257 - }
258 - render() {
259 - instance = this;
260 - return <span>{this.state.text}</span>;
261 - }
248 + class Component extends React.unstable_AsyncComponent {
249 + state = {text: ''};
250 + push(val) {
251 + this.setState(state => ({text: state.text + val}));
252 }
253 + componentDidUpdate() {
254 + ops.push(this.state.text);
255 + }
256 + render() {
257 + instance = this;
258 + return <span>{this.state.text}</span>;
259 + }
260 + }
261
264 - ReactDOM.render(<Component />, container);
265 - jest.runAllTimers();
262 + ReactDOM.render(<Component />, container);
263 + jest.runAllTimers();
264
267 - // Updates are async by default
268 - instance.push('A');
269 - expect(ops).toEqual([]);
270 - expect(container.textContent).toEqual('');
265 + // Updates are async by default
266 + instance.push('A');
267 + expect(ops).toEqual([]);
268 + expect(container.textContent).toEqual('');
269
272 - ReactDOM.flushSync(() => {
273 - instance.push('B');
274 - instance.push('C');
275 - // Not flushed yet
276 - expect(container.textContent).toEqual('');
277 - expect(ops).toEqual([]);
278 - });
279 - // Only the active updates have flushed
280 - expect(container.textContent).toEqual('BC');
281 - expect(ops).toEqual(['BC']);
282 -
283 - instance.push('D');
284 - expect(container.textContent).toEqual('BC');
285 - expect(ops).toEqual(['BC']);
286 -
287 - // Flush the async updates
288 - jest.runAllTimers();
289 - expect(container.textContent).toEqual('BCAD');
290 - expect(ops).toEqual(['BC', 'BCAD']);
270 + ReactDOM.flushSync(() => {
271 + instance.push('B');
272 + instance.push('C');
273 + // Not flushed yet
274 + expect(container.textContent).toEqual('');
275 + expect(ops).toEqual([]);
276 });
277 + // Only the active updates have flushed
278 + expect(container.textContent).toEqual('BC');
279 + expect(ops).toEqual(['BC']);
280 +
281 + instance.push('D');
282 + expect(container.textContent).toEqual('BC');
283 + expect(ops).toEqual(['BC']);
284 +
285 + // Flush the async updates
286 + jest.runAllTimers();
287 + expect(container.textContent).toEqual('BCAD');
288 + expect(ops).toEqual(['BC', 'BCAD']);
289 });
293 - }
290 + });
291 });
src/renderers/dom/shared/__tests__/ReactDOMComponent-test.js
+74 -139
@@ -15,7 +15,6 @@ describe('ReactDOMComponent', () => {
15 var ReactDOM;
16 var ReactDOMServer;
17 var inputValueTracking;
18 - var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
18
19 function normalizeCodeLocInfo(str) {
20 return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -25,7 +24,6 @@ describe('ReactDOMComponent', () => {
24 jest.resetModules();
25 React = require('react');
26 ReactDOM = require('react-dom');
28 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
27 ReactDOMServer = require('react-dom/server');
28 ReactTestUtils = require('react-dom/test-utils');
29 // TODO: can we express this test with only public API?
@@ -940,9 +938,7 @@ describe('ReactDOMComponent', () => {
938 '<hasOwnProperty /> is using uppercase HTML',
939 );
940 expectDev(console.error.calls.argsFor(3)[0]).toContain(
943 - ReactDOMFeatureFlags.useFiber
944 - ? 'The tag <hasOwnProperty> is unrecognized in this browser'
945 - : 'The tag <hasownproperty> is unrecognized in this browser',
941 + 'The tag <hasOwnProperty> is unrecognized in this browser',
942 );
943 });
944
@@ -1323,36 +1319,6 @@ describe('ReactDOMComponent', () => {
1319 });
1320
1321 describe('unmountComponent', () => {
1326 - // Fiber does not have a clean-up phase for host components; relies on GC
1327 - if (!ReactDOMFeatureFlags.useFiber) {
1328 - it('should clean up input value tracking', () => {
1329 - var container = document.createElement('div');
1330 - var node = ReactDOM.render(
1331 - <input type="text" defaultValue="foo" />,
1332 - container,
1333 - );
1334 - var tracker = inputValueTracking._getTrackerFromNode(node);
1335 -
1336 - spyOn(tracker, 'stopTracking');
1337 -
1338 - ReactDOM.unmountComponentAtNode(container);
1339 -
1340 - expect(tracker.stopTracking.calls.count()).toBe(1);
1341 - });
1342 -
1343 - it('should clean up input textarea tracking', () => {
1344 - var container = document.createElement('div');
1345 - var node = ReactDOM.render(<textarea defaultValue="foo" />, container);
1346 - var tracker = inputValueTracking._getTrackerFromNode(node);
1347 -
1348 - spyOn(tracker, 'stopTracking');
1349 -
1350 - ReactDOM.unmountComponentAtNode(container);
1351 -
1352 - expect(tracker.stopTracking.calls.count()).toBe(1);
1353 - });
1354 - }
1355 -
1322 it('unmounts children before unsetting DOM node info', () => {
1323 class Inner extends React.Component {
1324 render() {
@@ -1402,15 +1368,12 @@ describe('ReactDOMComponent', () => {
1368 spyOn(console, 'error');
1369 ReactTestUtils.renderIntoDocument(<div><tr /><tr /></div>);
1370
1405 - var addendum = ReactDOMFeatureFlags.useFiber
1406 - ? '\n in tr (at **)' + '\n in div (at **)'
1407 - : ' See div > tr.';
1408 -
1371 expectDev(console.error.calls.count()).toBe(1);
1372 expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1373 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1374 '<div>.' +
1413 - addendum,
1375 + '\n in tr (at **)' +
1376 + '\n in div (at **)',
1377 );
1378 });
1379
@@ -1419,16 +1382,13 @@ describe('ReactDOMComponent', () => {
1382 var p = document.createElement('p');
1383 ReactDOM.render(<span><p /></span>, p);
1384
1422 - var addendum = ReactDOMFeatureFlags.useFiber
1423 - ? // There is no outer `p` here because root container is not part of the stack.
1424 - '\n in p (at **)' + '\n in span (at **)'
1425 - : ' See p > ... > p.';
1426 -
1385 expectDev(console.error.calls.count()).toBe(1);
1386 expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1387 'Warning: validateDOMNesting(...): <p> cannot appear as a descendant ' +
1388 'of <p>.' +
1431 - addendum,
1389 + // There is no outer `p` here because root container is not part of the stack.
1390 + '\n in p (at **)' +
1391 + '\n in span (at **)',
1392 );
1393 });
1394
@@ -1450,39 +1410,31 @@ describe('ReactDOMComponent', () => {
1410 ReactTestUtils.renderIntoDocument(<Foo />);
1411 expectDev(console.error.calls.count()).toBe(3);
1412
1453 - var addendum1 = ReactDOMFeatureFlags.useFiber
1454 - ? '\n in tr (at **)' +
1455 - '\n in Row (at **)' +
1456 - '\n in table (at **)' +
1457 - '\n in Foo (at **)'
1458 - : ' See Foo > table > Row > tr.';
1413 expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
1414 'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
1415 '<table>. Add a <tbody> to your code to match the DOM tree generated ' +
1416 'by the browser.' +
1463 - addendum1,
1417 + '\n in tr (at **)' +
1418 + '\n in Row (at **)' +
1419 + '\n in table (at **)' +
1420 + '\n in Foo (at **)',
1421 );
1422
1466 - var addendum2 = ReactDOMFeatureFlags.useFiber
1467 - ? '\n in tr (at **)' +
1468 - '\n in Row (at **)' +
1469 - '\n in table (at **)' +
1470 - '\n in Foo (at **)'
1471 - : ' See Row > tr > #text.';
1423 expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
1424 'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
1425 'child of <tr>.' +
1475 - addendum2,
1426 + '\n in tr (at **)' +
1427 + '\n in Row (at **)' +
1428 + '\n in table (at **)' +
1429 + '\n in Foo (at **)',
1430 );
1431
1478 - var addendum3 = ReactDOMFeatureFlags.useFiber
1479 - ? '\n in table (at **)' + '\n in Foo (at **)'
1480 - : ' See Foo > table > #text.';
1432 expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
1433 'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
1434 "appear as a child of <table>. Make sure you don't have any extra " +
1435 'whitespace between tags on each line of your source code.' +
1485 - addendum3,
1436 + '\n in table (at **)' +
1437 + '\n in Foo (at **)',
1438 );
1439 });
1440
@@ -1518,13 +1470,11 @@ describe('ReactDOMComponent', () => {
1470 expectDev(
1471 normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
1472 ).toContain(
1521 - ReactDOMFeatureFlags.useFiber
1522 - ? '\n in tr (at **)' +
1523 - '\n in Row (at **)' +
1524 - '\n in FancyRow (at **)' +
1525 - '\n in table (at **)' +
1526 - '\n in Viz1 (at **)'
1527 - : 'See Viz1 > table > FancyRow > Row > tr.',
1473 + '\n in tr (at **)' +
1474 + '\n in Row (at **)' +
1475 + '\n in FancyRow (at **)' +
1476 + '\n in table (at **)' +
1477 + '\n in Viz1 (at **)',
1478 );
1479
1480 function Viz2() {
@@ -1538,15 +1488,13 @@ describe('ReactDOMComponent', () => {
1488 expectDev(
1489 normalizeCodeLocInfo(console.error.calls.argsFor(1)[0]),
1490 ).toContain(
1541 - ReactDOMFeatureFlags.useFiber
1542 - ? '\n in tr (at **)' +
1543 - '\n in Row (at **)' +
1544 - '\n in FancyRow (at **)' +
1545 - '\n in table (at **)' +
1546 - '\n in Table (at **)' +
1547 - '\n in FancyTable (at **)' +
1548 - '\n in Viz2 (at **)'
1549 - : 'See Viz2 > FancyTable > Table > table > FancyRow > Row > tr.',
1491 + '\n in tr (at **)' +
1492 + '\n in Row (at **)' +
1493 + '\n in FancyRow (at **)' +
1494 + '\n in table (at **)' +
1495 + '\n in Table (at **)' +
1496 + '\n in FancyTable (at **)' +
1497 + '\n in Viz2 (at **)',
1498 );
1499
1500 ReactTestUtils.renderIntoDocument(<FancyTable><FancyRow /></FancyTable>);
@@ -1554,14 +1502,12 @@ describe('ReactDOMComponent', () => {
1502 expectDev(
1503 normalizeCodeLocInfo(console.error.calls.argsFor(2)[0]),
1504 ).toContain(
1557 - ReactDOMFeatureFlags.useFiber
1558 - ? '\n in tr (at **)' +
1559 - '\n in Row (at **)' +
1560 - '\n in FancyRow (at **)' +
1561 - '\n in table (at **)' +
1562 - '\n in Table (at **)' +
1563 - '\n in FancyTable (at **)'
1564 - : 'See FancyTable > Table > table > FancyRow > Row > tr.',
1505 + '\n in tr (at **)' +
1506 + '\n in Row (at **)' +
1507 + '\n in FancyRow (at **)' +
1508 + '\n in table (at **)' +
1509 + '\n in Table (at **)' +
1510 + '\n in FancyTable (at **)',
1511 );
1512
1513 ReactTestUtils.renderIntoDocument(<table><FancyRow /></table>);
@@ -1569,12 +1515,10 @@ describe('ReactDOMComponent', () => {
1515 expectDev(
1516 normalizeCodeLocInfo(console.error.calls.argsFor(3)[0]),
1517 ).toContain(
1572 - ReactDOMFeatureFlags.useFiber
1573 - ? '\n in tr (at **)' +
1574 - '\n in Row (at **)' +
1575 - '\n in FancyRow (at **)' +
1576 - '\n in table (at **)'
1577 - : 'See table > FancyRow > Row > tr.',
1518 + '\n in tr (at **)' +
1519 + '\n in Row (at **)' +
1520 + '\n in FancyRow (at **)' +
1521 + '\n in table (at **)',
1522 );
1523
1524 ReactTestUtils.renderIntoDocument(<FancyTable><tr /></FancyTable>);
@@ -1582,12 +1526,10 @@ describe('ReactDOMComponent', () => {
1526 expectDev(
1527 normalizeCodeLocInfo(console.error.calls.argsFor(4)[0]),
1528 ).toContain(
1585 - ReactDOMFeatureFlags.useFiber
1586 - ? '\n in tr (at **)' +
1587 - '\n in table (at **)' +
1588 - '\n in Table (at **)' +
1589 - '\n in FancyTable (at **)'
1590 - : 'See FancyTable > Table > table > tr.',
1529 + '\n in tr (at **)' +
1530 + '\n in table (at **)' +
1531 + '\n in Table (at **)' +
1532 + '\n in FancyTable (at **)',
1533 );
1534
1535 class Link extends React.Component {
@@ -1601,13 +1543,11 @@ describe('ReactDOMComponent', () => {
1543 expectDev(
1544 normalizeCodeLocInfo(console.error.calls.argsFor(5)[0]),
1545 ).toContain(
1604 - ReactDOMFeatureFlags.useFiber
1605 - ? '\n in a (at **)' +
1606 - '\n in Link (at **)' +
1607 - '\n in div (at **)' +
1608 - '\n in a (at **)' +
1609 - '\n in Link (at **)'
1610 - : 'See Link > a > ... > Link > a.',
1546 + '\n in a (at **)' +
1547 + '\n in Link (at **)' +
1548 + '\n in div (at **)' +
1549 + '\n in a (at **)' +
1550 + '\n in Link (at **)',
1551 );
1552 });
1553
@@ -2294,42 +2234,37 @@ describe('ReactDOMComponent', () => {
2234 });
2235 });
2236
2297 - if (ReactDOMFeatureFlags.useFiber) {
2298 - // This is currently broken (and has been broken for a while).
2299 - // We had a fix based on reading namespace, but it was too convoluted.
2300 - // TODO: a proper fix that would happen at the diffing stage.
2301 - describe('Hyphenated SVG elements', function() {
2302 - it('the font-face element is not a custom element', function() {
2303 - spyOn(console, 'error');
2304 - var el = ReactTestUtils.renderIntoDocument(
2305 - <svg><font-face x-height={false} /></svg>,
2306 - );
2237 + describe('Hyphenated SVG elements', function() {
2238 + it('the font-face element is not a custom element', function() {
2239 + spyOn(console, 'error');
2240 + var el = ReactTestUtils.renderIntoDocument(
2241 + <svg><font-face x-height={false} /></svg>,
2242 + );
2243
2308 - expect(el.querySelector('font-face').hasAttribute('x-height')).toBe(
2309 - false,
2310 - );
2244 + expect(el.querySelector('font-face').hasAttribute('x-height')).toBe(
2245 + false,
2246 + );
2247
2312 - expectDev(console.error.calls.count()).toBe(1);
2313 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2314 - 'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
2315 - );
2316 - });
2248 + expectDev(console.error.calls.count()).toBe(1);
2249 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
2250 + 'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
2251 + );
2252 + });
2253
2318 - it('the font-face element does not allow unknown boolean values', function() {
2319 - spyOn(console, 'error');
2320 - var el = ReactTestUtils.renderIntoDocument(
2321 - <svg><font-face whatever={false} /></svg>,
2322 - );
2254 + it('the font-face element does not allow unknown boolean values', function() {
2255 + spyOn(console, 'error');
2256 + var el = ReactTestUtils.renderIntoDocument(
2257 + <svg><font-face whatever={false} /></svg>,
2258 + );
2259
2324 - expect(el.querySelector('font-face').hasAttribute('whatever')).toBe(
2325 - false,
2326 - );
2260 + expect(el.querySelector('font-face').hasAttribute('whatever')).toBe(
2261 + false,
2262 + );
2263
2328 - expectDev(console.error.calls.count()).toBe(1);
2329 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
2330 - 'Warning: Received `false` for non-boolean attribute `whatever`.',
2331 - );
2332 - });
2264 + expectDev(console.error.calls.count()).toBe(1);
2265 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
2266 + 'Warning: Received `false` for non-boolean attribute `whatever`.',
2267 + );
2268 });
2334 - }
2269 + });
2270 });
src/renderers/dom/shared/__tests__/ReactDOMComponentTree-test.js
+1 -7
@@ -9,8 +9,6 @@
9
10 'use strict';
11
12 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 -
12 describe('ReactDOMComponentTree', () => {
13 var React;
14 var ReactDOM;
@@ -21,11 +19,7 @@ describe('ReactDOMComponentTree', () => {
19 var container = document.createElement('div');
20 // Force server-rendering path:
21 container.innerHTML = ReactDOMServer.renderToString(elt);
24 - if (ReactDOMFeatureFlags.useFiber) {
25 - return ReactDOM.hydrate(elt, container);
26 - } else {
27 - return ReactDOM.render(elt, container);
28 - }
22 + return ReactDOM.hydrate(elt, container);
23 }
24
25 function getTypeOf(instance) {
src/renderers/dom/shared/__tests__/ReactDOMServerIntegration-test.js
+208 -379
@@ -9,8 +9,6 @@
9
10 'use strict';
11
12 -let ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 -
12 let ExecutionEnvironment;
13 let PropTypes;
14 let React;
@@ -21,7 +19,6 @@ let ReactTestUtils;
19 const stream = require('stream');
20
21 const TEXT_NODE_TYPE = 3;
24 -const COMMENT_NODE_TYPE = 8;
22
23 // Helper functions for rendering tests
24 // ====================================
@@ -29,7 +26,7 @@ const COMMENT_NODE_TYPE = 8;
26 // promisified version of ReactDOM.render()
27 function asyncReactDOMRender(reactElement, domElement, forceHydrate) {
28 return new Promise(resolve => {
32 - if (forceHydrate && ReactDOMFeatureFlags.useFiber) {
29 + if (forceHydrate) {
30 ReactDOM.hydrate(reactElement, domElement);
31 } else {
32 ReactDOM.render(reactElement, domElement);
@@ -213,9 +210,7 @@ const clientRenderOnBadMarkup = async (element, errorCount = 0) => {
210 // as that will not work in the server string scenario.
211 function itRenders(desc, testFn) {
212 it(`renders ${desc} with server string render`, () => testFn(serverRender));
216 - if (ReactDOMFeatureFlags.useFiber) {
217 - it(`renders ${desc} with server stream render`, () => testFn(streamRender));
218 - }
213 + it(`renders ${desc} with server stream render`, () => testFn(streamRender));
214 itClientRenders(desc, testFn);
215 }
216
@@ -324,7 +319,6 @@ function resetModules() {
319 PropTypes = require('prop-types');
320 React = require('react');
321 ReactDOM = require('react-dom');
327 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
322 ReactTestUtils = require('react-dom/test-utils');
323
324 // Now we reset the modules again to load the server renderer.
@@ -358,97 +352,95 @@ describe('ReactDOMServerIntegration', () => {
352 expect(e.firstChild.tagName).toBe('BR');
353 });
354
361 - if (ReactDOMFeatureFlags.useFiber) {
362 - itRenders('a string', async render => {
363 - let e = await render('Hello');
364 - expect(e.nodeType).toBe(3);
365 - expect(e.nodeValue).toMatch('Hello');
366 - });
355 + itRenders('a string', async render => {
356 + let e = await render('Hello');
357 + expect(e.nodeType).toBe(3);
358 + expect(e.nodeValue).toMatch('Hello');
359 + });
360
368 - itRenders('a number', async render => {
369 - let e = await render(42);
370 - expect(e.nodeType).toBe(3);
371 - expect(e.nodeValue).toMatch('42');
372 - });
361 + itRenders('a number', async render => {
362 + let e = await render(42);
363 + expect(e.nodeType).toBe(3);
364 + expect(e.nodeValue).toMatch('42');
365 + });
366
374 - itRenders('an array with one child', async render => {
375 - let e = await render([<div key={1}>text1</div>]);
376 - let parent = e.parentNode;
377 - expect(parent.childNodes[0].tagName).toBe('DIV');
378 - });
367 + itRenders('an array with one child', async render => {
368 + let e = await render([<div key={1}>text1</div>]);
369 + let parent = e.parentNode;
370 + expect(parent.childNodes[0].tagName).toBe('DIV');
371 + });
372
380 - itRenders('an array with several children', async render => {
381 - let Header = props => {
382 - return <p>header</p>;
383 - };
384 - let Footer = props => {
385 - return [<h2 key={1}>footer</h2>, <h3 key={2}>about</h3>];
386 - };
387 - let e = await render([
388 - <div key={1}>text1</div>,
389 - <span key={2}>text2</span>,
390 - <Header key={3} />,
391 - <Footer key={4} />,
392 - ]);
393 - let parent = e.parentNode;
394 - expect(parent.childNodes[0].tagName).toBe('DIV');
395 - expect(parent.childNodes[1].tagName).toBe('SPAN');
396 - expect(parent.childNodes[2].tagName).toBe('P');
397 - expect(parent.childNodes[3].tagName).toBe('H2');
398 - expect(parent.childNodes[4].tagName).toBe('H3');
399 - });
400 -
401 - itRenders('a nested array', async render => {
402 - let e = await render([
403 - [<div key={1}>text1</div>],
404 - <span key={1}>text2</span>,
405 - [[[null, <p key={1} />], false]],
406 - ]);
407 - let parent = e.parentNode;
408 - expect(parent.childNodes[0].tagName).toBe('DIV');
409 - expect(parent.childNodes[1].tagName).toBe('SPAN');
410 - expect(parent.childNodes[2].tagName).toBe('P');
411 - });
412 -
413 - itRenders('an iterable', async render => {
414 - const threeDivIterable = {
415 - '@@iterator': function() {
416 - var i = 0;
417 - return {
418 - next: function() {
419 - if (i++ < 3) {
420 - return {value: <div key={i} />, done: false};
421 - } else {
422 - return {value: undefined, done: true};
423 - }
424 - },
425 - };
426 - },
427 - };
428 - let e = await render(threeDivIterable);
429 - let parent = e.parentNode;
430 - expect(parent.childNodes.length).toBe(3);
431 - expect(parent.childNodes[0].tagName).toBe('DIV');
432 - expect(parent.childNodes[1].tagName).toBe('DIV');
433 - expect(parent.childNodes[2].tagName).toBe('DIV');
434 - });
373 + itRenders('an array with several children', async render => {
374 + let Header = props => {
375 + return <p>header</p>;
376 + };
377 + let Footer = props => {
378 + return [<h2 key={1}>footer</h2>, <h3 key={2}>about</h3>];
379 + };
380 + let e = await render([
381 + <div key={1}>text1</div>,
382 + <span key={2}>text2</span>,
383 + <Header key={3} />,
384 + <Footer key={4} />,
385 + ]);
386 + let parent = e.parentNode;
387 + expect(parent.childNodes[0].tagName).toBe('DIV');
388 + expect(parent.childNodes[1].tagName).toBe('SPAN');
389 + expect(parent.childNodes[2].tagName).toBe('P');
390 + expect(parent.childNodes[3].tagName).toBe('H2');
391 + expect(parent.childNodes[4].tagName).toBe('H3');
392 + });
393
436 - itRenders('emptyish values', async render => {
437 - let e = await render(0);
438 - expect(e.nodeType).toBe(TEXT_NODE_TYPE);
439 - expect(e.nodeValue).toMatch('0');
394 + itRenders('a nested array', async render => {
395 + let e = await render([
396 + [<div key={1}>text1</div>],
397 + <span key={1}>text2</span>,
398 + [[[null, <p key={1} />], false]],
399 + ]);
400 + let parent = e.parentNode;
401 + expect(parent.childNodes[0].tagName).toBe('DIV');
402 + expect(parent.childNodes[1].tagName).toBe('SPAN');
403 + expect(parent.childNodes[2].tagName).toBe('P');
404 + });
405
441 - // Empty string is special because client renders a node
442 - // but server returns empty HTML. So we compare parent text.
443 - expect((await render(<div>{''}</div>)).textContent).toBe('');
406 + itRenders('an iterable', async render => {
407 + const threeDivIterable = {
408 + '@@iterator': function() {
409 + var i = 0;
410 + return {
411 + next: function() {
412 + if (i++ < 3) {
413 + return {value: <div key={i} />, done: false};
414 + } else {
415 + return {value: undefined, done: true};
416 + }
417 + },
418 + };
419 + },
420 + };
421 + let e = await render(threeDivIterable);
422 + let parent = e.parentNode;
423 + expect(parent.childNodes.length).toBe(3);
424 + expect(parent.childNodes[0].tagName).toBe('DIV');
425 + expect(parent.childNodes[1].tagName).toBe('DIV');
426 + expect(parent.childNodes[2].tagName).toBe('DIV');
427 + });
428
445 - expect(await render([])).toBe(null);
446 - expect(await render(false)).toBe(null);
447 - expect(await render(true)).toBe(null);
448 - expect(await render(undefined)).toBe(null);
449 - expect(await render([[[false]], undefined])).toBe(null);
450 - });
451 - }
429 + itRenders('emptyish values', async render => {
430 + let e = await render(0);
431 + expect(e.nodeType).toBe(TEXT_NODE_TYPE);
432 + expect(e.nodeValue).toMatch('0');
433 +
434 + // Empty string is special because client renders a node
435 + // but server returns empty HTML. So we compare parent text.
436 + expect((await render(<div>{''}</div>)).textContent).toBe('');
437 +
438 + expect(await render([])).toBe(null);
439 + expect(await render(false)).toBe(null);
440 + expect(await render(true)).toBe(null);
441 + expect(await render(undefined)).toBe(null);
442 + expect(await render([[[false]], undefined])).toBe(null);
443 + });
444 });
445
446 describe('property to attribute mapping', function() {
@@ -999,23 +991,7 @@ describe('ReactDOMServerIntegration', () => {
991 }
992
993 function expectTextNode(node, text) {
1002 - if (ReactDOMFeatureFlags.useFiber) {
1003 - // with Fiber, a React text node is just a DOM text node.
1004 - expectNode(node, TEXT_NODE_TYPE, text);
1005 - } else {
1006 - // with Stack, a React text node is a DOM text node surrounded by
1007 - // react-text comment nodes.
1008 - expectNode(node, COMMENT_NODE_TYPE, / react-text: [0-9]+ /);
1009 - if (text.length > 0) {
1010 - node = node.nextSibling;
1011 - expectNode(node, TEXT_NODE_TYPE, text);
1012 - }
1013 - expectNode(node.nextSibling, COMMENT_NODE_TYPE, / \/react-text /);
1014 - }
1015 - }
1016 -
1017 - function expectEmptyNode(node) {
1018 - expectNode(node, COMMENT_NODE_TYPE, / react-empty: [0-9]+ /);
994 + expectNode(node, TEXT_NODE_TYPE, text);
995 }
996
997 describe('text children', function() {
@@ -1040,74 +1016,45 @@ describe('ReactDOMServerIntegration', () => {
1016
1017 itRenders('a div with multiple empty text children', async render => {
1018 const e = await render(<div>{''}{''}{''}</div>);
1043 - if (ReactDOMFeatureFlags.useFiber) {
1044 - // with Fiber, there are just three separate text node children,
1045 - // each of which is blank.
1046 - if (render === serverRender || render === streamRender) {
1047 - // For plain server markup result we should have no text nodes if
1048 - // they're all empty.
1049 - expect(e.childNodes.length).toBe(0);
1050 - expect(e.textContent).toBe('');
1051 - } else {
1052 - expect(e.childNodes.length).toBe(3);
1053 - expectTextNode(e.childNodes[0], '');
1054 - expectTextNode(e.childNodes[1], '');
1055 - expectTextNode(e.childNodes[2], '');
1056 - }
1019 + if (render === serverRender || render === streamRender) {
1020 + // For plain server markup result we should have no text nodes if
1021 + // they're all empty.
1022 + expect(e.childNodes.length).toBe(0);
1023 + expect(e.textContent).toBe('');
1024 } else {
1058 - // with Stack, there are six react-text comment nodes.
1059 - expect(e.childNodes.length).toBe(6);
1025 + expect(e.childNodes.length).toBe(3);
1026 expectTextNode(e.childNodes[0], '');
1027 + expectTextNode(e.childNodes[1], '');
1028 expectTextNode(e.childNodes[2], '');
1062 - expectTextNode(e.childNodes[4], '');
1029 }
1030 });
1031
1032 itRenders('a div with multiple whitespace children', async render => {
1033 const e = await render(<div>{' '}{' '}{' '}</div>);
1068 - if (ReactDOMFeatureFlags.useFiber) {
1069 - // with Fiber, there are normally just three text nodes
1070 - if (
1071 - render === serverRender ||
1072 - render === clientRenderOnServerString ||
1073 - render === streamRender
1074 - ) {
1075 - // For plain server markup result we have comments between.
1076 - // If we're able to hydrate, they remain.
1077 - expect(e.childNodes.length).toBe(5);
1078 - expectTextNode(e.childNodes[0], ' ');
1079 - expectTextNode(e.childNodes[2], ' ');
1080 - expectTextNode(e.childNodes[4], ' ');
1081 - } else {
1082 - expect(e.childNodes.length).toBe(3);
1083 - expectTextNode(e.childNodes[0], ' ');
1084 - expectTextNode(e.childNodes[1], ' ');
1085 - expectTextNode(e.childNodes[2], ' ');
1086 - }
1034 + if (
1035 + render === serverRender ||
1036 + render === clientRenderOnServerString ||
1037 + render === streamRender
1038 + ) {
1039 + // For plain server markup result we have comments between.
1040 + // If we're able to hydrate, they remain.
1041 + expect(e.childNodes.length).toBe(5);
1042 + expectTextNode(e.childNodes[0], ' ');
1043 + expectTextNode(e.childNodes[2], ' ');
1044 + expectTextNode(e.childNodes[4], ' ');
1045 } else {
1088 - // with Stack, each of the text nodes is surrounded by react-text
1089 - // comment nodes, making 9 nodes in total.
1090 - expect(e.childNodes.length).toBe(9);
1046 + expect(e.childNodes.length).toBe(3);
1047 expectTextNode(e.childNodes[0], ' ');
1092 - expectTextNode(e.childNodes[3], ' ');
1093 - expectTextNode(e.childNodes[6], ' ');
1048 + expectTextNode(e.childNodes[1], ' ');
1049 + expectTextNode(e.childNodes[2], ' ');
1050 }
1051 });
1052
1053 itRenders('a div with text sibling to a node', async render => {
1054 const e = await render(<div>Text<span>More Text</span></div>);
1055 let spanNode;
1100 - if (ReactDOMFeatureFlags.useFiber) {
1101 - // with Fiber, there are only two children, the "Text" text node and
1102 - // the span element.
1103 - expect(e.childNodes.length).toBe(2);
1104 - spanNode = e.childNodes[1];
1105 - } else {
1106 - // with Stack, there are four children, a "Text" text node surrounded
1107 - // by react-text comment nodes, and the span element.
1108 - expect(e.childNodes.length).toBe(4);
1109 - spanNode = e.childNodes[3];
1110 - }
1056 + expect(e.childNodes.length).toBe(2);
1057 + spanNode = e.childNodes[1];
1058 expectTextNode(e.childNodes[0], 'Text');
1059 expect(spanNode.tagName).toBe('SPAN');
1060 expect(spanNode.childNodes.length).toBe(1);
@@ -1130,72 +1077,44 @@ describe('ReactDOMServerIntegration', () => {
1077
1078 itRenders('a leading blank child with a text sibling', async render => {
1079 const e = await render(<div>{''}foo</div>);
1133 - if (ReactDOMFeatureFlags.useFiber) {
1134 - // with Fiber, there are just two text nodes.
1135 - if (render === serverRender || render === streamRender) {
1136 - expect(e.childNodes.length).toBe(1);
1137 - expectTextNode(e.childNodes[0], 'foo');
1138 - } else {
1139 - expect(e.childNodes.length).toBe(2);
1140 - expectTextNode(e.childNodes[0], '');
1141 - expectTextNode(e.childNodes[1], 'foo');
1142 - }
1080 + if (render === serverRender || render === streamRender) {
1081 + expect(e.childNodes.length).toBe(1);
1082 + expectTextNode(e.childNodes[0], 'foo');
1083 } else {
1144 - // with Stack, there are five nodes: two react-text comment nodes
1145 - // without any text between them, and the text node foo surrounded
1146 - // by react-text comment nodes.
1147 - expect(e.childNodes.length).toBe(5);
1084 + expect(e.childNodes.length).toBe(2);
1085 expectTextNode(e.childNodes[0], '');
1149 - expectTextNode(e.childNodes[2], 'foo');
1086 + expectTextNode(e.childNodes[1], 'foo');
1087 }
1088 });
1089
1090 itRenders('a trailing blank child with a text sibling', async render => {
1091 const e = await render(<div>foo{''}</div>);
1155 - if (ReactDOMFeatureFlags.useFiber) {
1156 - // with Fiber, there are just two text nodes.
1157 - if (render === serverRender || render === streamRender) {
1158 - expect(e.childNodes.length).toBe(1);
1159 - expectTextNode(e.childNodes[0], 'foo');
1160 - } else {
1161 - expect(e.childNodes.length).toBe(2);
1162 - expectTextNode(e.childNodes[0], 'foo');
1163 - expectTextNode(e.childNodes[1], '');
1164 - }
1092 + // with Fiber, there are just two text nodes.
1093 + if (render === serverRender || render === streamRender) {
1094 + expect(e.childNodes.length).toBe(1);
1095 + expectTextNode(e.childNodes[0], 'foo');
1096 } else {
1166 - // with Stack, there are five nodes: the text node foo surrounded
1167 - // by react-text comment nodes, and two react-text comment nodes
1168 - // without any text between them.
1169 - expect(e.childNodes.length).toBe(5);
1097 + expect(e.childNodes.length).toBe(2);
1098 expectTextNode(e.childNodes[0], 'foo');
1171 - expectTextNode(e.childNodes[3], '');
1099 + expectTextNode(e.childNodes[1], '');
1100 }
1101 });
1102
1103 itRenders('an element with two text children', async render => {
1104 const e = await render(<div>{'foo'}{'bar'}</div>);
1177 - if (ReactDOMFeatureFlags.useFiber) {
1178 - // with Fiber, there are just two text nodes.
1179 - if (
1180 - render === serverRender ||
1181 - render === clientRenderOnServerString ||
1182 - render === streamRender
1183 - ) {
1184 - // In the server render output there's a comment between them.
1185 - expect(e.childNodes.length).toBe(3);
1186 - expectTextNode(e.childNodes[0], 'foo');
1187 - expectTextNode(e.childNodes[2], 'bar');
1188 - } else {
1189 - expect(e.childNodes.length).toBe(2);
1190 - expectTextNode(e.childNodes[0], 'foo');
1191 - expectTextNode(e.childNodes[1], 'bar');
1192 - }
1105 + if (
1106 + render === serverRender ||
1107 + render === clientRenderOnServerString ||
1108 + render === streamRender
1109 + ) {
1110 + // In the server render output there's a comment between them.
1111 + expect(e.childNodes.length).toBe(3);
1112 + expectTextNode(e.childNodes[0], 'foo');
1113 + expectTextNode(e.childNodes[2], 'bar');
1114 } else {
1194 - // with Stack, there are six nodes: two text nodes, each surrounded
1195 - // by react-text comment nodes.
1196 - expect(e.childNodes.length).toBe(6);
1115 + expect(e.childNodes.length).toBe(2);
1116 expectTextNode(e.childNodes[0], 'foo');
1198 - expectTextNode(e.childNodes[3], 'bar');
1117 + expectTextNode(e.childNodes[1], 'bar');
1118 }
1119 });
1120 });
@@ -1214,28 +1133,20 @@ describe('ReactDOMServerIntegration', () => {
1133
1134 itRenders('an element with number and text children', async render => {
1135 const e = await render(<div>{'foo'}{40}</div>);
1217 - if (ReactDOMFeatureFlags.useFiber) {
1218 - // with Fiber, there are just two text nodes.
1219 - if (
1220 - render === serverRender ||
1221 - render === clientRenderOnServerString ||
1222 - render === streamRender
1223 - ) {
1224 - // In the server markup there's a comment between.
1225 - expect(e.childNodes.length).toBe(3);
1226 - expectTextNode(e.childNodes[0], 'foo');
1227 - expectTextNode(e.childNodes[2], '40');
1228 - } else {
1229 - expect(e.childNodes.length).toBe(2);
1230 - expectTextNode(e.childNodes[0], 'foo');
1231 - expectTextNode(e.childNodes[1], '40');
1232 - }
1136 + // with Fiber, there are just two text nodes.
1137 + if (
1138 + render === serverRender ||
1139 + render === clientRenderOnServerString ||
1140 + render === streamRender
1141 + ) {
1142 + // In the server markup there's a comment between.
1143 + expect(e.childNodes.length).toBe(3);
1144 + expectTextNode(e.childNodes[0], 'foo');
1145 + expectTextNode(e.childNodes[2], '40');
1146 } else {
1234 - // with Stack, there are six nodes: two text nodes, each surrounded
1235 - // by react-text comment nodes.
1236 - expect(e.childNodes.length).toBe(6);
1147 + expect(e.childNodes.length).toBe(2);
1148 expectTextNode(e.childNodes[0], 'foo');
1238 - expectTextNode(e.childNodes[3], '40');
1149 + expectTextNode(e.childNodes[1], '40');
1150 }
1151 });
1152 });
@@ -1259,54 +1170,25 @@ describe('ReactDOMServerIntegration', () => {
1170 itRenders('a null component children as empty', async render => {
1171 const NullComponent = () => null;
1172 const e = await render(<div><NullComponent /></div>);
1262 - if (ReactDOMFeatureFlags.useFiber) {
1263 - // with Fiber, an empty component results in no markup.
1264 - expect(e.childNodes.length).toBe(0);
1265 - } else {
1266 - // with Stack, an empty component results in one react-empty comment
1267 - // node.
1268 - expect(e.childNodes.length).toBe(1);
1269 - expectEmptyNode(e.firstChild);
1270 - }
1173 + expect(e.childNodes.length).toBe(0);
1174 });
1175
1176 itRenders('null children as blank', async render => {
1177 const e = await render(<div>{null}foo</div>);
1275 - if (ReactDOMFeatureFlags.useFiber) {
1276 - // with Fiber, there is just one text node.
1277 - expect(e.childNodes.length).toBe(1);
1278 - expectTextNode(e.childNodes[0], 'foo');
1279 - } else {
1280 - // with Stack, there's a text node surronded by react-text comment nodes.
1281 - expect(e.childNodes.length).toBe(3);
1282 - expectTextNode(e.childNodes[0], 'foo');
1283 - }
1178 + expect(e.childNodes.length).toBe(1);
1179 + expectTextNode(e.childNodes[0], 'foo');
1180 });
1181
1182 itRenders('false children as blank', async render => {
1183 const e = await render(<div>{false}foo</div>);
1288 - if (ReactDOMFeatureFlags.useFiber) {
1289 - // with Fiber, there is just one text node.
1290 - expect(e.childNodes.length).toBe(1);
1291 - expectTextNode(e.childNodes[0], 'foo');
1292 - } else {
1293 - // with Stack, there's a text node surronded by react-text comment nodes.
1294 - expect(e.childNodes.length).toBe(3);
1295 - expectTextNode(e.childNodes[0], 'foo');
1296 - }
1184 + expect(e.childNodes.length).toBe(1);
1185 + expectTextNode(e.childNodes[0], 'foo');
1186 });
1187
1188 itRenders('null and false children together as blank', async render => {
1189 const e = await render(<div>{false}{null}foo{null}{false}</div>);
1301 - if (ReactDOMFeatureFlags.useFiber) {
1302 - // with Fiber, there is just one text node.
1303 - expect(e.childNodes.length).toBe(1);
1304 - expectTextNode(e.childNodes[0], 'foo');
1305 - } else {
1306 - // with Stack, there's a text node surronded by react-text comment nodes.
1307 - expect(e.childNodes.length).toBe(3);
1308 - expectTextNode(e.childNodes[0], 'foo');
1309 - }
1190 + expect(e.childNodes.length).toBe(1);
1191 + expectTextNode(e.childNodes[0], 'foo');
1192 });
1193
1194 itRenders('only null and false children as blank', async render => {
@@ -1522,21 +1404,10 @@ describe('ReactDOMServerIntegration', () => {
1404 );
1405 expect(e.id).toBe('parent');
1406 let child1, child2, textNode;
1525 - if (ReactDOMFeatureFlags.useFiber) {
1526 - // with Fiber, there are three children: the child1 element, a
1527 - // single space text node, and the child2 element.
1528 - expect(e.childNodes.length).toBe(3);
1529 - child1 = e.childNodes[0];
1530 - textNode = e.childNodes[1];
1531 - child2 = e.childNodes[2];
1532 - } else {
1533 - // with Stack, there are five children: the child1 element, a single
1534 - // space surrounded by react-text comments, and the child2 element.
1535 - expect(e.childNodes.length).toBe(5);
1536 - child1 = e.childNodes[0];
1537 - textNode = e.childNodes[1];
1538 - child2 = e.childNodes[4];
1539 - }
1407 + expect(e.childNodes.length).toBe(3);
1408 + child1 = e.childNodes[0];
1409 + textNode = e.childNodes[1];
1410 + child2 = e.childNodes[2];
1411 expect(child1.id).toBe('child1');
1412 expect(child1.childNodes.length).toBe(0);
1413 expectTextNode(textNode, ' ');
@@ -1551,22 +1422,10 @@ describe('ReactDOMServerIntegration', () => {
1422 // prettier-ignore
1423 const e = await render(<div id="parent"> <div id="child" /> </div>); // eslint-disable-line no-multi-spaces
1424 let textNode1, child, textNode2;
1554 - if (ReactDOMFeatureFlags.useFiber) {
1555 - // with Fiber, there are three children: a one-space text node, the
1556 - // child element, and a two-space text node.
1557 - expect(e.childNodes.length).toBe(3);
1558 - textNode1 = e.childNodes[0];
1559 - child = e.childNodes[1];
1560 - textNode2 = e.childNodes[2];
1561 - } else {
1562 - // with Stack, there are 7 children: a one-space text node surrounded
1563 - // by react-text comments, the child element, and a two-space text node
1564 - // surrounded by react-text comments.
1565 - expect(e.childNodes.length).toBe(7);
1566 - textNode1 = e.childNodes[0];
1567 - child = e.childNodes[3];
1568 - textNode2 = e.childNodes[4];
1569 - }
1425 + expect(e.childNodes.length).toBe(3);
1426 + textNode1 = e.childNodes[0];
1427 + child = e.childNodes[1];
1428 + textNode2 = e.childNodes[2];
1429 expect(e.id).toBe('parent');
1430 expectTextNode(textNode1, ' ');
1431 expect(child.id).toBe('child');
@@ -1575,33 +1434,31 @@ describe('ReactDOMServerIntegration', () => {
1434 },
1435 );
1436
1578 - if (ReactDOMFeatureFlags.useFiber) {
1579 - itRenders('a composite with multiple children', async render => {
1580 - const Component = props => props.children;
1581 - const e = await render(
1582 - <Component>{['a', 'b', [undefined], [[false, 'c']]]}</Component>,
1583 - );
1437 + itRenders('a composite with multiple children', async render => {
1438 + const Component = props => props.children;
1439 + const e = await render(
1440 + <Component>{['a', 'b', [undefined], [[false, 'c']]]}</Component>,
1441 + );
1442
1585 - let parent = e.parentNode;
1586 - if (
1587 - render === serverRender ||
1588 - render === clientRenderOnServerString ||
1589 - render === streamRender
1590 - ) {
1591 - // For plain server markup result we have comments between.
1592 - // If we're able to hydrate, they remain.
1593 - expect(parent.childNodes.length).toBe(5);
1594 - expectTextNode(parent.childNodes[0], 'a');
1595 - expectTextNode(parent.childNodes[2], 'b');
1596 - expectTextNode(parent.childNodes[4], 'c');
1597 - } else {
1598 - expect(parent.childNodes.length).toBe(3);
1599 - expectTextNode(parent.childNodes[0], 'a');
1600 - expectTextNode(parent.childNodes[1], 'b');
1601 - expectTextNode(parent.childNodes[2], 'c');
1602 - }
1603 - });
1604 - }
1443 + let parent = e.parentNode;
1444 + if (
1445 + render === serverRender ||
1446 + render === clientRenderOnServerString ||
1447 + render === streamRender
1448 + ) {
1449 + // For plain server markup result we have comments between.
1450 + // If we're able to hydrate, they remain.
1451 + expect(parent.childNodes.length).toBe(5);
1452 + expectTextNode(parent.childNodes[0], 'a');
1453 + expectTextNode(parent.childNodes[2], 'b');
1454 + expectTextNode(parent.childNodes[4], 'c');
1455 + } else {
1456 + expect(parent.childNodes.length).toBe(3);
1457 + expectTextNode(parent.childNodes[0], 'a');
1458 + expectTextNode(parent.childNodes[1], 'b');
1459 + expectTextNode(parent.childNodes[2], 'c');
1460 + }
1461 + });
1462 });
1463
1464 describe('escaping >, <, and &', function() {
@@ -1615,27 +1472,18 @@ describe('ReactDOMServerIntegration', () => {
1472 const e = await render(
1473 <div>{'<span>Text1&quot;</span>'}{'<span>Text2&quot;</span>'}</div>,
1474 );
1618 - if (ReactDOMFeatureFlags.useFiber) {
1619 - // with Fiber, there are just two text nodes.
1620 - if (
1621 - render === serverRender ||
1622 - render === clientRenderOnServerString ||
1623 - render === streamRender
1624 - ) {
1625 - expect(e.childNodes.length).toBe(3);
1626 - expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
1627 - expectTextNode(e.childNodes[2], '<span>Text2&quot;</span>');
1628 - } else {
1629 - expect(e.childNodes.length).toBe(2);
1630 - expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
1631 - expectTextNode(e.childNodes[1], '<span>Text2&quot;</span>');
1632 - }
1475 + if (
1476 + render === serverRender ||
1477 + render === clientRenderOnServerString ||
1478 + render === streamRender
1479 + ) {
1480 + expect(e.childNodes.length).toBe(3);
1481 + expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
1482 + expectTextNode(e.childNodes[2], '<span>Text2&quot;</span>');
1483 } else {
1634 - // with Stack there are six nodes: two text nodes each surrounded by
1635 - // two react-text comment nodes.
1636 - expect(e.childNodes.length).toBe(6);
1484 + expect(e.childNodes.length).toBe(2);
1485 expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
1638 - expectTextNode(e.childNodes[3], '<span>Text2&quot;</span>');
1486 + expectTextNode(e.childNodes[1], '<span>Text2&quot;</span>');
1487 }
1488 });
1489 });
@@ -1647,12 +1495,9 @@ describe('ReactDOMServerIntegration', () => {
1495 const UndefinedComponent = () => undefined;
1496 await render(<UndefinedComponent />, 1);
1497 },
1650 - ReactDOMFeatureFlags.useFiber
1651 - ? 'UndefinedComponent(...): Nothing was returned from render. ' +
1652 - 'This usually means a return statement is missing. Or, to ' +
1653 - 'render nothing, return null.'
1654 - : 'A valid React element (or null) must be returned. ' +
1655 - 'You may have returned undefined, an array or some other invalid object.',
1498 + 'UndefinedComponent(...): Nothing was returned from render. ' +
1499 + 'This usually means a return statement is missing. Or, to ' +
1500 + 'render nothing, return null.',
1501 );
1502
1503 itThrowsWhenRendering(
@@ -1665,12 +1510,9 @@ describe('ReactDOMServerIntegration', () => {
1510 }
1511 await render(<UndefinedComponent />, 1);
1512 },
1668 - ReactDOMFeatureFlags.useFiber
1669 - ? 'UndefinedComponent(...): Nothing was returned from render. ' +
1670 - 'This usually means a return statement is missing. Or, to ' +
1671 - 'render nothing, return null.'
1672 - : 'A valid React element (or null) must be returned. ' +
1673 - 'You may have returned undefined, an array or some other invalid object.',
1513 + 'UndefinedComponent(...): Nothing was returned from render. ' +
1514 + 'This usually means a return statement is missing. Or, to ' +
1515 + 'render nothing, return null.',
1516 );
1517
1518 itThrowsWhenRendering(
@@ -1679,12 +1521,9 @@ describe('ReactDOMServerIntegration', () => {
1521 const ObjectComponent = () => ({x: 123});
1522 await render(<ObjectComponent />, 1);
1523 },
1682 - ReactDOMFeatureFlags.useFiber
1683 - ? 'Objects are not valid as a React child (found: object with keys ' +
1684 - '{x}). If you meant to render a collection of children, use ' +
1685 - 'an array instead.'
1686 - : 'A valid React element (or null) must be returned. ' +
1687 - 'You may have returned undefined, an array or some other invalid object.',
1524 + 'Objects are not valid as a React child (found: object with keys ' +
1525 + '{x}). If you meant to render a collection of children, use ' +
1526 + 'an array instead.',
1527 );
1528
1529 itThrowsWhenRendering(
@@ -1697,12 +1536,9 @@ describe('ReactDOMServerIntegration', () => {
1536 }
1537 await render(<ObjectComponent />, 1);
1538 },
1700 - ReactDOMFeatureFlags.useFiber
1701 - ? 'Objects are not valid as a React child (found: object with keys ' +
1702 - '{x}). If you meant to render a collection of children, use ' +
1703 - 'an array instead.'
1704 - : 'A valid React element (or null) must be returned. ' +
1705 - 'You may have returned undefined, an array or some other invalid object.',
1539 + 'Objects are not valid as a React child (found: object with keys ' +
1540 + '{x}). If you meant to render a collection of children, use ' +
1541 + 'an array instead.',
1542 );
1543
1544 itThrowsWhenRendering(
@@ -1710,11 +1546,9 @@ describe('ReactDOMServerIntegration', () => {
1546 async render => {
1547 await render({x: 123});
1548 },
1713 - ReactDOMFeatureFlags.useFiber
1714 - ? 'Objects are not valid as a React child (found: object with keys ' +
1715 - '{x}). If you meant to render a collection of children, use ' +
1716 - 'an array instead.'
1717 - : 'Invalid component element.',
1549 + 'Objects are not valid as a React child (found: object with keys ' +
1550 + '{x}). If you meant to render a collection of children, use ' +
1551 + 'an array instead.',
1552 );
1553 });
1554 });
@@ -2734,12 +2568,7 @@ describe('ReactDOMServerIntegration', () => {
2568 ));
2569
2570 it('can distinguish an empty component from an empty text component', () =>
2737 - (ReactDOMFeatureFlags.useFiber
2738 - ? expectMarkupMatch
2739 - : expectMarkupMismatch)(
2740 - <div><EmptyComponent /></div>,
2741 - <div>{''}</div>,
2742 - ));
2571 + expectMarkupMatch(<div><EmptyComponent /></div>, <div>{''}</div>));
2572 });
2573
2574 // Markup Mismatches: misc
src/renderers/dom/shared/__tests__/ReactDOMTextComponent-test.js
+2 -12
@@ -12,7 +12,6 @@
12 var React;
13 var ReactDOM;
14 var ReactDOMServer;
15 -var ReactDOMFeatureFlags;
15
16 // In standard React, TextComponent keeps track of different Text templates
17 // using comments. However, in React Fiber, those comments are not outputted due
@@ -28,7 +27,6 @@ describe('ReactDOMTextComponent', () => {
27 React = require('react');
28 ReactDOM = require('react-dom');
29 ReactDOMServer = require('react-dom/server');
31 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
30 });
31
32 it('updates a mounted text component in place', () => {
@@ -117,11 +115,7 @@ describe('ReactDOMTextComponent', () => {
115 var reactEl = <div>{'foo'}{'bar'}{'baz'}</div>;
116 el.innerHTML = ReactDOMServer.renderToString(reactEl);
117
120 - if (ReactDOMFeatureFlags.useFiber) {
121 - ReactDOM.hydrate(reactEl, el);
122 - } else {
123 - ReactDOM.render(reactEl, el);
124 - }
118 + ReactDOM.hydrate(reactEl, el);
119 expect(el.textContent).toBe('foobarbaz');
120
121 ReactDOM.unmountComponentAtNode(el);
@@ -129,11 +123,7 @@ describe('ReactDOMTextComponent', () => {
123 reactEl = <div>{''}{''}{''}</div>;
124 el.innerHTML = ReactDOMServer.renderToString(reactEl);
125
132 - if (ReactDOMFeatureFlags.useFiber) {
133 - ReactDOM.hydrate(reactEl, el);
134 - } else {
135 - ReactDOM.render(reactEl, el);
136 - }
126 + ReactDOM.hydrate(reactEl, el);
127 expect(el.textContent).toBe('');
128 });
129
src/renderers/dom/shared/__tests__/ReactMount-test.js
+60 -99
@@ -9,7 +9,6 @@
9
10 'use strict';
11
12 -const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
12 const {COMMENT_NODE} = require('HTMLNodeType');
13
14 const invariant = require('invariant');
@@ -66,24 +65,22 @@ describe('ReactMount', () => {
65 });
66 });
67
69 - if (ReactDOMFeatureFlags.useFiber) {
70 - it('warns when given a factory', () => {
71 - spyOn(console, 'error');
72 - class Component extends React.Component {
73 - render() {
74 - return <div />;
75 - }
68 + it('warns when given a factory', () => {
69 + spyOn(console, 'error');
70 + class Component extends React.Component {
71 + render() {
72 + return <div />;
73 }
74 + }
75
78 - ReactTestUtils.renderIntoDocument(Component);
79 - expectDev(console.error.calls.count()).toBe(1);
80 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
81 - 'Functions are not valid as a React child. ' +
82 - 'This may happen if you return a Component instead of <Component /> from render. ' +
83 - 'Or maybe you meant to call this function rather than return it.',
84 - );
85 - });
86 - }
76 + ReactTestUtils.renderIntoDocument(Component);
77 + expectDev(console.error.calls.count()).toBe(1);
78 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
79 + 'Functions are not valid as a React child. ' +
80 + 'This may happen if you return a Component instead of <Component /> from render. ' +
81 + 'Or maybe you meant to call this function rather than return it.',
82 + );
83 + });
84
85 it('should render different components in same root', () => {
86 var container = document.createElement('container');
@@ -144,21 +141,11 @@ describe('ReactMount', () => {
141 container.innerHTML = ReactDOMServer.renderToString(<div />) + ' ';
142
143 spyOn(console, 'error');
147 - if (ReactDOMFeatureFlags.useFiber) {
148 - ReactDOM.hydrate(<div />, container);
149 - } else {
150 - ReactDOM.render(<div />, container);
151 - }
144 + ReactDOM.hydrate(<div />, container);
145 expectDev(console.error.calls.count()).toBe(1);
153 - if (ReactDOMFeatureFlags.useFiber) {
154 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
155 - 'Did not expect server HTML to contain the text node " " in <container>.',
156 - );
157 - } else {
158 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
159 - 'Target node has markup rendered by React, but there are unrelated nodes as well.',
160 - );
161 - }
146 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
147 + 'Did not expect server HTML to contain the text node " " in <container>.',
148 + );
149 });
150
151 it('should warn if mounting into right padded rendered markup', () => {
@@ -166,21 +153,11 @@ describe('ReactMount', () => {
153 container.innerHTML = ' ' + ReactDOMServer.renderToString(<div />);
154
155 spyOn(console, 'error');
169 - if (ReactDOMFeatureFlags.useFiber) {
170 - ReactDOM.hydrate(<div />, container);
171 - } else {
172 - ReactDOM.render(<div />, container);
173 - }
156 + ReactDOM.hydrate(<div />, container);
157 expectDev(console.error.calls.count()).toBe(1);
175 - if (ReactDOMFeatureFlags.useFiber) {
176 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
177 - 'Did not expect server HTML to contain the text node " " in <container>.',
178 - );
179 - } else {
180 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
181 - 'Target node has markup rendered by React, but there are unrelated nodes as well.',
182 - );
183 - }
158 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
159 + 'Did not expect server HTML to contain the text node " " in <container>.',
160 + );
161 });
162
163 it('should not warn if mounting into non-empty node', () => {
@@ -213,29 +190,15 @@ describe('ReactMount', () => {
190 div.innerHTML = markup;
191
192 spyOn(console, 'error');
216 - if (ReactDOMFeatureFlags.useFiber) {
217 - ReactDOM.hydrate(
218 - <div>This markup contains an nbsp entity: &nbsp; client text</div>,
219 - div,
220 - );
221 - } else {
222 - ReactDOM.render(
223 - <div>This markup contains an nbsp entity: &nbsp; client text</div>,
224 - div,
225 - );
226 - }
193 + ReactDOM.hydrate(
194 + <div>This markup contains an nbsp entity: &nbsp; client text</div>,
195 + div,
196 + );
197 expectDev(console.error.calls.count()).toBe(1);
228 - if (ReactDOMFeatureFlags.useFiber) {
229 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
230 - 'Server: "This markup contains an nbsp entity:   server text" ' +
231 - 'Client: "This markup contains an nbsp entity:   client text"',
232 - );
233 - } else {
234 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
235 - ' (client) nbsp entity: &nbsp; client text</div>\n' +
236 - ' (server) nbsp entity: &nbsp; server text</div>',
237 - );
238 - }
198 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
199 + 'Server: "This markup contains an nbsp entity:   server text" ' +
200 + 'Client: "This markup contains an nbsp entity:   client text"',
201 + );
202 });
203
204 if (WebComponents !== undefined) {
@@ -388,41 +351,39 @@ describe('ReactMount', () => {
351 expect(container2.textContent).toEqual('a!');
352 });
353
391 - if (ReactDOMFeatureFlags.useFiber) {
392 - describe('mount point is a comment node', () => {
393 - let containerDiv;
394 - let mountPoint;
354 + describe('mount point is a comment node', () => {
355 + let containerDiv;
356 + let mountPoint;
357
396 - beforeEach(() => {
397 - containerDiv = document.createElement('div');
398 - containerDiv.innerHTML = 'A<!-- react-mount-point-unstable -->B';
399 - mountPoint = containerDiv.childNodes[1];
400 - invariant(mountPoint.nodeType === COMMENT_NODE, 'Expected comment');
401 - });
358 + beforeEach(() => {
359 + containerDiv = document.createElement('div');
360 + containerDiv.innerHTML = 'A<!-- react-mount-point-unstable -->B';
361 + mountPoint = containerDiv.childNodes[1];
362 + invariant(mountPoint.nodeType === COMMENT_NODE, 'Expected comment');
363 + });
364
403 - it('renders at a comment node', () => {
404 - function Char(props) {
405 - return props.children;
406 - }
407 - function list(chars) {
408 - return chars.split('').map(c => <Char key={c}>{c}</Char>);
409 - }
410 -
411 - ReactDOM.render(list('aeiou'), mountPoint);
412 - expect(containerDiv.innerHTML).toBe(
413 - 'Aaeiou<!-- react-mount-point-unstable -->B',
414 - );
365 + it('renders at a comment node', () => {
366 + function Char(props) {
367 + return props.children;
368 + }
369 + function list(chars) {
370 + return chars.split('').map(c => <Char key={c}>{c}</Char>);
371 + }
372
416 - ReactDOM.render(list('yea'), mountPoint);
417 - expect(containerDiv.innerHTML).toBe(
418 - 'Ayea<!-- react-mount-point-unstable -->B',
419 - );
373 + ReactDOM.render(list('aeiou'), mountPoint);
374 + expect(containerDiv.innerHTML).toBe(
375 + 'Aaeiou<!-- react-mount-point-unstable -->B',
376 + );
377
421 - ReactDOM.render(list(''), mountPoint);
422 - expect(containerDiv.innerHTML).toBe(
423 - 'A<!-- react-mount-point-unstable -->B',
424 - );
425 - });
378 + ReactDOM.render(list('yea'), mountPoint);
379 + expect(containerDiv.innerHTML).toBe(
380 + 'Ayea<!-- react-mount-point-unstable -->B',
381 + );
382 +
383 + ReactDOM.render(list(''), mountPoint);
384 + expect(containerDiv.innerHTML).toBe(
385 + 'A<!-- react-mount-point-unstable -->B',
386 + );
387 });
427 - }
388 + });
389 });
src/renderers/dom/shared/__tests__/ReactRenderDocument-test.js
+228 -291
@@ -15,15 +15,6 @@ var ReactDOMServer;
15
16 var getTestDocument;
17
18 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
19 -
20 -var UNMOUNT_INVARIANT_MESSAGE =
21 - '<html> tried to unmount. ' +
22 - 'Because of cross-browser quirks it is impossible to unmount some ' +
23 - 'top-level components (eg <html>, <head>, and <body>) reliably and ' +
24 - 'efficiently. To fix this, have a single top-level component that ' +
25 - 'never unmounts render these elements.';
26 -
18 describe('rendering React components at document', () => {
19 beforeEach(() => {
20 jest.resetModules();
@@ -36,16 +27,12 @@ describe('rendering React components at document', () => {
27
28 describe('with old implicit hydration API', () => {
29 function expectDeprecationWarningWithFiber() {
39 - if (ReactDOMFeatureFlags.useFiber) {
40 - expectDev(console.warn.calls.count()).toBe(1);
41 - expectDev(console.warn.calls.argsFor(0)[0]).toContain(
42 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
43 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
44 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
45 - );
46 - } else {
47 - expectDev(console.warn.calls.count()).toBe(0);
48 - }
30 + expectDev(console.warn.calls.count()).toBe(1);
31 + expectDev(console.warn.calls.argsFor(0)[0]).toContain(
32 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
33 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
34 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
35 + );
36 }
37
38 it('should be able to adopt server markup', () => {
@@ -101,17 +88,9 @@ describe('rendering React components at document', () => {
88 ReactDOM.render(<Root />, testDocument);
89 expect(testDocument.body.innerHTML).toBe('Hello world');
90
104 - if (ReactDOMFeatureFlags.useFiber) {
105 - // In Fiber this actually works. It might not be a good idea though.
106 - ReactDOM.unmountComponentAtNode(testDocument);
107 - expect(testDocument.firstChild).toBe(null);
108 - } else {
109 - expect(function() {
110 - ReactDOM.unmountComponentAtNode(testDocument);
111 - }).toThrowError(UNMOUNT_INVARIANT_MESSAGE);
112 -
113 - expect(testDocument.body.innerHTML).toBe('Hello world');
114 - }
91 + // In Fiber this actually works. It might not be a good idea though.
92 + ReactDOM.unmountComponentAtNode(testDocument);
93 + expect(testDocument.firstChild).toBe(null);
94
95 expectDeprecationWarningWithFiber();
96 });
@@ -152,23 +131,12 @@ describe('rendering React components at document', () => {
131 var testDocument = getTestDocument(markup);
132
133 ReactDOM.render(<Component />, testDocument);
155 -
134 expect(testDocument.body.innerHTML).toBe('Hello world');
135
158 - // Reactive update
159 - if (ReactDOMFeatureFlags.useFiber) {
160 - // This works but is probably a bad idea.
161 - ReactDOM.render(<Component2 />, testDocument);
162 -
163 - expect(testDocument.body.innerHTML).toBe('Goodbye world');
164 - } else {
165 - expect(function() {
166 - ReactDOM.render(<Component2 />, testDocument);
167 - }).toThrowError(UNMOUNT_INVARIANT_MESSAGE);
168 -
169 - expect(testDocument.body.innerHTML).toBe('Hello world');
170 - }
136 + // This works but is probably a bad idea.
137 + ReactDOM.render(<Component2 />, testDocument);
138
139 + expect(testDocument.body.innerHTML).toBe('Goodbye world');
140 expectDeprecationWarningWithFiber();
141 });
142
@@ -231,38 +199,20 @@ describe('rendering React components at document', () => {
199 );
200 var testDocument = getTestDocument(markup);
201
234 - if (ReactDOMFeatureFlags.useFiber) {
235 - spyOn(console, 'warn');
236 - spyOn(console, 'error');
237 - ReactDOM.render(<Component text="Hello world" />, testDocument);
238 - expect(testDocument.body.innerHTML).toBe('Hello world');
239 - expectDev(console.warn.calls.count()).toBe(1);
240 - expectDev(console.warn.calls.argsFor(0)[0]).toContain(
241 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
242 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
243 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
244 - );
245 - expectDev(console.error.calls.count()).toBe(1);
246 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
247 - 'Warning: Text content did not match.',
248 - );
249 - } else {
250 - expect(function() {
251 - // Notice the text is different!
252 - ReactDOM.render(<Component text="Hello world" />, testDocument);
253 - }).toThrowError(
254 - "You're trying to render a component to the document using " +
255 - 'server rendering but the checksum was invalid. This usually ' +
256 - 'means you rendered a different component type or props on ' +
257 - 'the client from the one on the server, or your render() methods ' +
258 - 'are impure. React cannot handle this case due to cross-browser ' +
259 - 'quirks by rendering at the document root. You should look for ' +
260 - 'environment dependent code in your components and ensure ' +
261 - 'the props are the same client and server side:\n' +
262 - ' (client) dy data-reactid="4">Hello world</body></\n' +
263 - ' (server) dy data-reactid="4">Goodbye world</body>',
264 - );
265 - }
202 + spyOn(console, 'warn');
203 + spyOn(console, 'error');
204 + ReactDOM.render(<Component text="Hello world" />, testDocument);
205 + expect(testDocument.body.innerHTML).toBe('Hello world');
206 + expectDev(console.warn.calls.count()).toBe(1);
207 + expectDev(console.warn.calls.argsFor(0)[0]).toContain(
208 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
209 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
210 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
211 + );
212 + expectDev(console.error.calls.count()).toBe(1);
213 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
214 + 'Warning: Text content did not match.',
215 + );
216 });
217
218 it('should throw on full document render w/ no markup', () => {
@@ -283,19 +233,8 @@ describe('rendering React components at document', () => {
233 }
234 }
235
286 - if (ReactDOMFeatureFlags.useFiber) {
287 - ReactDOM.render(<Component text="Hello world" />, testDocument);
288 - expect(testDocument.body.innerHTML).toBe('Hello world');
289 - } else {
290 - expect(function() {
291 - ReactDOM.render(<Component />, testDocument);
292 - }).toThrowError(
293 - "You're trying to render a component to the document but you didn't " +
294 - "use server rendering. We can't do this without using server " +
295 - 'rendering due to cross-browser quirks. See ' +
296 - 'ReactDOMServer.renderToString() for server rendering.',
297 - );
298 - }
236 + ReactDOM.render(<Component text="Hello world" />, testDocument);
237 + expect(testDocument.body.innerHTML).toBe('Hello world');
238 // We don't expect a warning about new hydration API here because
239 // we aren't sure if the user meant to hydrate or replace the document.
240 // We would see a warning if the document had React-rendered HTML in it.
@@ -323,221 +262,219 @@ describe('rendering React components at document', () => {
262 });
263 });
264
326 - if (ReactDOMFeatureFlags.useFiber) {
327 - describe('with new explicit hydration API', () => {
328 - it('should be able to adopt server markup', () => {
329 - class Root extends React.Component {
330 - render() {
331 - return (
332 - <html>
333 - <head>
334 - <title>Hello World</title>
335 - </head>
336 - <body>
337 - {'Hello ' + this.props.hello}
338 - </body>
339 - </html>
340 - );
341 - }
265 + describe('with new explicit hydration API', () => {
266 + it('should be able to adopt server markup', () => {
267 + class Root extends React.Component {
268 + render() {
269 + return (
270 + <html>
271 + <head>
272 + <title>Hello World</title>
273 + </head>
274 + <body>
275 + {'Hello ' + this.props.hello}
276 + </body>
277 + </html>
278 + );
279 }
280 + }
281 +
282 + var markup = ReactDOMServer.renderToString(<Root hello="world" />);
283 + var testDocument = getTestDocument(markup);
284 + var body = testDocument.body;
285 +
286 + ReactDOM.hydrate(<Root hello="world" />, testDocument);
287 + expect(testDocument.body.innerHTML).toBe('Hello world');
288 +
289 + ReactDOM.hydrate(<Root hello="moon" />, testDocument);
290 + expect(testDocument.body.innerHTML).toBe('Hello moon');
291 +
292 + expect(body === testDocument.body).toBe(true);
293 + });
294
344 - var markup = ReactDOMServer.renderToString(<Root hello="world" />);
345 - var testDocument = getTestDocument(markup);
346 - var body = testDocument.body;
347 -
348 - ReactDOM.hydrate(<Root hello="world" />, testDocument);
349 - expect(testDocument.body.innerHTML).toBe('Hello world');
350 -
351 - ReactDOM.hydrate(<Root hello="moon" />, testDocument);
352 - expect(testDocument.body.innerHTML).toBe('Hello moon');
353 -
354 - expect(body === testDocument.body).toBe(true);
355 - });
356 -
357 - it('should not be able to unmount component from document node', () => {
358 - class Root extends React.Component {
359 - render() {
360 - return (
361 - <html>
362 - <head>
363 - <title>Hello World</title>
364 - </head>
365 - <body>
366 - Hello world
367 - </body>
368 - </html>
369 - );
370 - }
295 + it('should not be able to unmount component from document node', () => {
296 + class Root extends React.Component {
297 + render() {
298 + return (
299 + <html>
300 + <head>
301 + <title>Hello World</title>
302 + </head>
303 + <body>
304 + Hello world
305 + </body>
306 + </html>
307 + );
308 }
309 + }
310 +
311 + var markup = ReactDOMServer.renderToString(<Root />);
312 + var testDocument = getTestDocument(markup);
313 + ReactDOM.hydrate(<Root />, testDocument);
314 + expect(testDocument.body.innerHTML).toBe('Hello world');
315 +
316 + // In Fiber this actually works. It might not be a good idea though.
317 + ReactDOM.unmountComponentAtNode(testDocument);
318 + expect(testDocument.firstChild).toBe(null);
319 + });
320
373 - var markup = ReactDOMServer.renderToString(<Root />);
374 - var testDocument = getTestDocument(markup);
375 - ReactDOM.hydrate(<Root />, testDocument);
376 - expect(testDocument.body.innerHTML).toBe('Hello world');
377 -
378 - // In Fiber this actually works. It might not be a good idea though.
379 - ReactDOM.unmountComponentAtNode(testDocument);
380 - expect(testDocument.firstChild).toBe(null);
381 - });
382 -
383 - it('should not be able to switch root constructors', () => {
384 - class Component extends React.Component {
385 - render() {
386 - return (
387 - <html>
388 - <head>
389 - <title>Hello World</title>
390 - </head>
391 - <body>
392 - Hello world
393 - </body>
394 - </html>
395 - );
396 - }
321 + it('should not be able to switch root constructors', () => {
322 + class Component extends React.Component {
323 + render() {
324 + return (
325 + <html>
326 + <head>
327 + <title>Hello World</title>
328 + </head>
329 + <body>
330 + Hello world
331 + </body>
332 + </html>
333 + );
334 }
335 + }
336
399 - class Component2 extends React.Component {
400 - render() {
401 - return (
402 - <html>
403 - <head>
404 - <title>Hello World</title>
405 - </head>
406 - <body>
407 - Goodbye world
408 - </body>
409 - </html>
410 - );
411 - }
337 + class Component2 extends React.Component {
338 + render() {
339 + return (
340 + <html>
341 + <head>
342 + <title>Hello World</title>
343 + </head>
344 + <body>
345 + Goodbye world
346 + </body>
347 + </html>
348 + );
349 }
350 + }
351 +
352 + var markup = ReactDOMServer.renderToString(<Component />);
353 + var testDocument = getTestDocument(markup);
354 +
355 + ReactDOM.hydrate(<Component />, testDocument);
356
414 - var markup = ReactDOMServer.renderToString(<Component />);
415 - var testDocument = getTestDocument(markup);
416 -
417 - ReactDOM.hydrate(<Component />, testDocument);
418 -
419 - expect(testDocument.body.innerHTML).toBe('Hello world');
420 -
421 - // This works but is probably a bad idea.
422 - ReactDOM.hydrate(<Component2 />, testDocument);
423 -
424 - expect(testDocument.body.innerHTML).toBe('Goodbye world');
425 - });
426 -
427 - it('should be able to mount into document', () => {
428 - class Component extends React.Component {
429 - render() {
430 - return (
431 - <html>
432 - <head>
433 - <title>Hello World</title>
434 - </head>
435 - <body>
436 - {this.props.text}
437 - </body>
438 - </html>
439 - );
440 - }
357 + expect(testDocument.body.innerHTML).toBe('Hello world');
358 +
359 + // This works but is probably a bad idea.
360 + ReactDOM.hydrate(<Component2 />, testDocument);
361 +
362 + expect(testDocument.body.innerHTML).toBe('Goodbye world');
363 + });
364 +
365 + it('should be able to mount into document', () => {
366 + class Component extends React.Component {
367 + render() {
368 + return (
369 + <html>
370 + <head>
371 + <title>Hello World</title>
372 + </head>
373 + <body>
374 + {this.props.text}
375 + </body>
376 + </html>
377 + );
378 }
379 + }
380 +
381 + var markup = ReactDOMServer.renderToString(
382 + <Component text="Hello world" />,
383 + );
384 + var testDocument = getTestDocument(markup);
385
443 - var markup = ReactDOMServer.renderToString(
444 - <Component text="Hello world" />,
445 - );
446 - var testDocument = getTestDocument(markup);
447 -
448 - ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
449 -
450 - expect(testDocument.body.innerHTML).toBe('Hello world');
451 - });
452 -
453 - it('renders over an existing text child without throwing', () => {
454 - spyOn(console, 'error');
455 - const container = document.createElement('div');
456 - container.textContent = 'potato';
457 - ReactDOM.hydrate(<div>parsnip</div>, container);
458 - expect(container.textContent).toBe('parsnip');
459 - expectDev(console.error.calls.count()).toBe(1);
460 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
461 - 'Did not expect server HTML to contain the text node "potato" in <div>.',
462 - );
463 - });
464 -
465 - it('should give helpful errors on state desync', () => {
466 - class Component extends React.Component {
467 - render() {
468 - return (
469 - <html>
470 - <head>
471 - <title>Hello World</title>
472 - </head>
473 - <body>
474 - {this.props.text}
475 - </body>
476 - </html>
477 - );
478 - }
386 + ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
387 +
388 + expect(testDocument.body.innerHTML).toBe('Hello world');
389 + });
390 +
391 + it('renders over an existing text child without throwing', () => {
392 + spyOn(console, 'error');
393 + const container = document.createElement('div');
394 + container.textContent = 'potato';
395 + ReactDOM.hydrate(<div>parsnip</div>, container);
396 + expect(container.textContent).toBe('parsnip');
397 + expectDev(console.error.calls.count()).toBe(1);
398 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
399 + 'Did not expect server HTML to contain the text node "potato" in <div>.',
400 + );
401 + });
402 +
403 + it('should give helpful errors on state desync', () => {
404 + class Component extends React.Component {
405 + render() {
406 + return (
407 + <html>
408 + <head>
409 + <title>Hello World</title>
410 + </head>
411 + <body>
412 + {this.props.text}
413 + </body>
414 + </html>
415 + );
416 }
417 + }
418 +
419 + var markup = ReactDOMServer.renderToString(
420 + <Component text="Goodbye world" />,
421 + );
422 + var testDocument = getTestDocument(markup);
423 +
424 + spyOn(console, 'error');
425 + ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
426 + expect(testDocument.body.innerHTML).toBe('Hello world');
427 + expectDev(console.error.calls.count()).toBe(1);
428 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
429 + 'Warning: Text content did not match.',
430 + );
431 + });
432 +
433 + it('should render w/ no markup to full document', () => {
434 + spyOn(console, 'error');
435 + var testDocument = getTestDocument();
436
481 - var markup = ReactDOMServer.renderToString(
482 - <Component text="Goodbye world" />,
483 - );
484 - var testDocument = getTestDocument(markup);
485 -
486 - spyOn(console, 'error');
487 - ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
488 - expect(testDocument.body.innerHTML).toBe('Hello world');
489 - expectDev(console.error.calls.count()).toBe(1);
490 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
491 - 'Warning: Text content did not match.',
492 - );
493 - });
494 -
495 - it('should render w/ no markup to full document', () => {
496 - spyOn(console, 'error');
497 - var testDocument = getTestDocument();
498 -
499 - class Component extends React.Component {
500 - render() {
501 - return (
502 - <html>
503 - <head>
504 - <title>Hello World</title>
505 - </head>
506 - <body>
507 - {this.props.text}
508 - </body>
509 - </html>
510 - );
511 - }
437 + class Component extends React.Component {
438 + render() {
439 + return (
440 + <html>
441 + <head>
442 + <title>Hello World</title>
443 + </head>
444 + <body>
445 + {this.props.text}
446 + </body>
447 + </html>
448 + );
449 }
450 + }
451
514 - ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
515 - expect(testDocument.body.innerHTML).toBe('Hello world');
516 - expectDev(console.error.calls.count()).toBe(1);
517 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
518 - // getTestDocument() has an extra <meta> that we didn't render.
519 - 'Did not expect server HTML to contain a <meta> in <head>.',
520 - );
521 - });
522 -
523 - it('supports findDOMNode on full-page components', () => {
524 - var tree = (
525 - <html>
526 - <head>
527 - <title>Hello World</title>
528 - </head>
529 - <body>
530 - Hello world
531 - </body>
532 - </html>
533 - );
534 -
535 - var markup = ReactDOMServer.renderToString(tree);
536 - var testDocument = getTestDocument(markup);
537 - var component = ReactDOM.hydrate(tree, testDocument);
538 - expect(testDocument.body.innerHTML).toBe('Hello world');
539 - expect(ReactDOM.findDOMNode(component).tagName).toBe('HTML');
540 - });
452 + ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
453 + expect(testDocument.body.innerHTML).toBe('Hello world');
454 + expectDev(console.error.calls.count()).toBe(1);
455 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
456 + // getTestDocument() has an extra <meta> that we didn't render.
457 + 'Did not expect server HTML to contain a <meta> in <head>.',
458 + );
459 });
542 - }
460 +
461 + it('supports findDOMNode on full-page components', () => {
462 + var tree = (
463 + <html>
464 + <head>
465 + <title>Hello World</title>
466 + </head>
467 + <body>
468 + Hello world
469 + </body>
470 + </html>
471 + );
472 +
473 + var markup = ReactDOMServer.renderToString(tree);
474 + var testDocument = getTestDocument(markup);
475 + var component = ReactDOM.hydrate(tree, testDocument);
476 + expect(testDocument.body.innerHTML).toBe('Hello world');
477 + expect(ReactDOM.findDOMNode(component).tagName).toBe('HTML');
478 + });
479 + });
480 });
src/renderers/dom/shared/__tests__/ReactServerRendering-test.js
+91 -139
@@ -16,9 +16,6 @@ var ReactDOMServer;
16 var ReactTestUtils;
17 var PropTypes;
18
19 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
20 -
21 -var ID_ATTRIBUTE_NAME;
19 var ROOT_ATTRIBUTE_NAME;
20
21 describe('ReactDOMServer', () => {
@@ -34,7 +31,6 @@ describe('ReactDOMServer', () => {
31 ReactDOMServer = require('react-dom/server');
32
33 var DOMProperty = require('DOMProperty');
37 - ID_ATTRIBUTE_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
34 ROOT_ATTRIBUTE_NAME = DOMProperty.ROOT_ATTRIBUTE_NAME;
35 });
36
@@ -72,11 +68,7 @@ describe('ReactDOMServer', () => {
68 }
69
70 var response = ReactDOMServer.renderToString(<NullComponent />);
75 - if (ReactDOMFeatureFlags.useFiber) {
76 - expect(response).toBe('');
77 - } else {
78 - expect(response).toBe('<!-- react-empty: 1 -->');
79 - }
71 + expect(response).toBe('');
72 });
73
74 // TODO: Test that listeners are not registered onto any document/container.
@@ -103,10 +95,7 @@ describe('ReactDOMServer', () => {
95 '>' +
96 '<span' +
97 '>' +
106 - (ReactDOMFeatureFlags.useFiber
107 - ? 'My name is <!-- -->child'
108 - : '<!-- react-text: [0-9]+ -->My name is <!-- /react-text -->' +
109 - '<!-- react-text: [0-9]+ -->child<!-- /react-text -->') +
98 + 'My name is <!-- -->child' +
99 '</span>' +
100 '</div>',
101 ),
@@ -165,14 +154,8 @@ describe('ReactDOMServer', () => {
154 '<span ' +
155 ROOT_ATTRIBUTE_NAME +
156 '=""' +
168 - (ReactDOMFeatureFlags.useFiber
169 - ? ''
170 - : ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') +
157 '>' +
172 - (ReactDOMFeatureFlags.useFiber
173 - ? 'Component name: <!-- -->TestComponent'
174 - : '<!-- react-text: [0-9]+ -->Component name: <!-- /react-text -->' +
175 - '<!-- react-text: [0-9]+ -->TestComponent<!-- /react-text -->') +
158 + 'Component name: <!-- -->TestComponent' +
159 '</span>',
160 ),
161 );
@@ -247,24 +230,14 @@ describe('ReactDOMServer', () => {
230
231 var instance = ReactDOM.render(<TestComponent name="x" />, element);
232 expect(mountCount).toEqual(3);
250 - if (ReactDOMFeatureFlags.useFiber) {
251 - expectDev(console.warn.calls.count()).toBe(1);
252 - expectDev(console.warn.calls.argsFor(0)[0]).toContain(
253 - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
254 - 'will stop working in React v17. Replace the ReactDOM.render() call ' +
255 - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
256 - );
257 - } else {
258 - expectDev(console.warn.calls.count()).toBe(0);
259 - }
233 + expectDev(console.warn.calls.count()).toBe(1);
234 + expectDev(console.warn.calls.argsFor(0)[0]).toContain(
235 + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' +
236 + 'will stop working in React v17. Replace the ReactDOM.render() call ' +
237 + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.',
238 + );
239 console.warn.calls.reset();
261 -
262 - var expectedMarkup = lastMarkup;
263 - if (ReactDOMFeatureFlags.useFiber) {
264 - var reactComments = /<!-- \/?react-text(: \d+)? -->/g;
265 - expectedMarkup = expectedMarkup.replace(reactComments, '');
266 - }
267 - expect(element.innerHTML).toBe(expectedMarkup);
240 + expect(element.innerHTML).toBe(lastMarkup);
241
242 // Ensure the events system works after mount into server markup
243 expect(numClicks).toEqual(0);
@@ -280,18 +253,9 @@ describe('ReactDOMServer', () => {
253 instance = ReactDOM.render(<TestComponent name="y" />, element);
254 expect(mountCount).toEqual(4);
255 expectDev(console.error.calls.count()).toBe(1);
283 - if (ReactDOMFeatureFlags.useFiber) {
284 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
285 - 'Text content did not match. Server: "x" Client: "y"',
286 - );
287 - } else {
288 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
289 - '(client) -- react-text: 3 -->y<!-- /react-text --',
290 - );
291 - expectDev(console.error.calls.argsFor(0)[0]).toContain(
292 - '(server) -- react-text: 3 -->x<!-- /react-text --',
293 - );
294 - }
256 + expectDev(console.error.calls.argsFor(0)[0]).toContain(
257 + 'Text content did not match. Server: "x" Client: "y"',
258 + );
259 console.error.calls.reset();
260 expect(element.innerHTML.length > 0).toBe(true);
261 expect(element.innerHTML).not.toEqual(lastMarkup);
@@ -304,104 +268,94 @@ describe('ReactDOMServer', () => {
268 expectDev(console.error.calls.count()).toBe(0);
269 });
270
307 - if (ReactDOMFeatureFlags.useFiber) {
308 - it('should have the correct mounting behavior (new hydrate API)', () => {
309 - spyOn(console, 'error');
310 - // This test is testing client-side behavior.
311 - ExecutionEnvironment.canUseDOM = true;
271 + it('should have the correct mounting behavior (new hydrate API)', () => {
272 + spyOn(console, 'error');
273 + // This test is testing client-side behavior.
274 + ExecutionEnvironment.canUseDOM = true;
275
313 - var mountCount = 0;
314 - var numClicks = 0;
276 + var mountCount = 0;
277 + var numClicks = 0;
278
316 - class TestComponent extends React.Component {
317 - componentDidMount() {
318 - mountCount++;
319 - }
279 + class TestComponent extends React.Component {
280 + componentDidMount() {
281 + mountCount++;
282 + }
283
321 - click = () => {
322 - numClicks++;
323 - };
284 + click = () => {
285 + numClicks++;
286 + };
287
325 - render() {
326 - return (
327 - <span ref="span" onClick={this.click}>
328 - Name: {this.props.name}
329 - </span>
330 - );
331 - }
288 + render() {
289 + return (
290 + <span ref="span" onClick={this.click}>
291 + Name: {this.props.name}
292 + </span>
293 + );
294 }
295 + }
296
334 - var element = document.createElement('div');
335 - ReactDOM.render(<TestComponent />, element);
336 -
337 - var lastMarkup = element.innerHTML;
338 -
339 - // Exercise the update path. Markup should not change,
340 - // but some lifecycle methods should be run again.
341 - ReactDOM.render(<TestComponent name="x" />, element);
342 - expect(mountCount).toEqual(1);
343 -
344 - // Unmount and remount. We should get another mount event and
345 - // we should get different markup, as the IDs are unique each time.
346 - ReactDOM.unmountComponentAtNode(element);
347 - expect(element.innerHTML).toEqual('');
348 - ReactDOM.render(<TestComponent name="x" />, element);
349 - expect(mountCount).toEqual(2);
350 - expect(element.innerHTML).not.toEqual(lastMarkup);
351 -
352 - // Now kill the node and render it on top of server-rendered markup, as if
353 - // we used server rendering. We should mount again, but the markup should
354 - // be unchanged. We will append a sentinel at the end of innerHTML to be
355 - // sure that innerHTML was not changed.
356 - ReactDOM.unmountComponentAtNode(element);
357 - expect(element.innerHTML).toEqual('');
358 -
359 - ExecutionEnvironment.canUseDOM = false;
360 - lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
361 - ExecutionEnvironment.canUseDOM = true;
362 - element.innerHTML = lastMarkup;
363 -
364 - var instance = ReactDOM.hydrate(<TestComponent name="x" />, element);
365 - expect(mountCount).toEqual(3);
366 -
367 - var expectedMarkup = lastMarkup;
368 - if (ReactDOMFeatureFlags.useFiber) {
369 - var reactComments = /<!-- \/?react-text(: \d+)? -->/g;
370 - expectedMarkup = expectedMarkup.replace(reactComments, '');
371 - }
372 - expect(element.innerHTML).toBe(expectedMarkup);
373 -
374 - // Ensure the events system works after mount into server markup
375 - expect(numClicks).toEqual(0);
376 - ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
377 - expect(numClicks).toEqual(1);
378 -
379 - ReactDOM.unmountComponentAtNode(element);
380 - expect(element.innerHTML).toEqual('');
381 -
382 - // Now simulate a situation where the app is not idempotent. React should
383 - // warn but do the right thing.
384 - element.innerHTML = lastMarkup;
385 - instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
386 - expect(mountCount).toEqual(4);
387 - expectDev(console.error.calls.count()).toBe(1);
388 - expect(element.innerHTML.length > 0).toBe(true);
389 - expect(element.innerHTML).not.toEqual(lastMarkup);
390 -
391 - // Ensure the events system works after markup mismatch.
392 - expect(numClicks).toEqual(1);
393 - ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
394 - expect(numClicks).toEqual(2);
395 - });
396 - }
297 + var element = document.createElement('div');
298 + ReactDOM.render(<TestComponent />, element);
299 +
300 + var lastMarkup = element.innerHTML;
301 +
302 + // Exercise the update path. Markup should not change,
303 + // but some lifecycle methods should be run again.
304 + ReactDOM.render(<TestComponent name="x" />, element);
305 + expect(mountCount).toEqual(1);
306 +
307 + // Unmount and remount. We should get another mount event and
308 + // we should get different markup, as the IDs are unique each time.
309 + ReactDOM.unmountComponentAtNode(element);
310 + expect(element.innerHTML).toEqual('');
311 + ReactDOM.render(<TestComponent name="x" />, element);
312 + expect(mountCount).toEqual(2);
313 + expect(element.innerHTML).not.toEqual(lastMarkup);
314 +
315 + // Now kill the node and render it on top of server-rendered markup, as if
316 + // we used server rendering. We should mount again, but the markup should
317 + // be unchanged. We will append a sentinel at the end of innerHTML to be
318 + // sure that innerHTML was not changed.
319 + ReactDOM.unmountComponentAtNode(element);
320 + expect(element.innerHTML).toEqual('');
321 +
322 + ExecutionEnvironment.canUseDOM = false;
323 + lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
324 + ExecutionEnvironment.canUseDOM = true;
325 + element.innerHTML = lastMarkup;
326 +
327 + var instance = ReactDOM.hydrate(<TestComponent name="x" />, element);
328 + expect(mountCount).toEqual(3);
329 + expect(element.innerHTML).toBe(lastMarkup);
330 +
331 + // Ensure the events system works after mount into server markup
332 + expect(numClicks).toEqual(0);
333 + ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
334 + expect(numClicks).toEqual(1);
335 +
336 + ReactDOM.unmountComponentAtNode(element);
337 + expect(element.innerHTML).toEqual('');
338 +
339 + // Now simulate a situation where the app is not idempotent. React should
340 + // warn but do the right thing.
341 + element.innerHTML = lastMarkup;
342 + instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
343 + expect(mountCount).toEqual(4);
344 + expectDev(console.error.calls.count()).toBe(1);
345 + expect(element.innerHTML.length > 0).toBe(true);
346 + expect(element.innerHTML).not.toEqual(lastMarkup);
347 +
348 + // Ensure the events system works after markup mismatch.
349 + expect(numClicks).toEqual(1);
350 + ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
351 + expect(numClicks).toEqual(2);
352 + });
353
354 it('should throw with silly args', () => {
355 expect(
356 ReactDOMServer.renderToString.bind(ReactDOMServer, {x: 123}),
357 ).toThrowError(
402 - ReactDOMFeatureFlags.useFiber
403 - ? 'Objects are not valid as a React child (found: object with keys {x})'
404 - : 'renderToString(): Invalid component element.',
358 + 'Objects are not valid as a React child (found: object with keys {x})',
359 );
360 });
361 });
@@ -515,9 +469,7 @@ describe('ReactDOMServer', () => {
469 expect(
470 ReactDOMServer.renderToStaticMarkup.bind(ReactDOMServer, {x: 123}),
471 ).toThrowError(
518 - ReactDOMFeatureFlags.useFiber
519 - ? 'Objects are not valid as a React child (found: object with keys {x})'
520 - : 'renderToStaticMarkup(): Invalid component element.',
472 + 'Objects are not valid as a React child (found: object with keys {x})',
473 );
474 });
475
src/renderers/dom/shared/__tests__/ReactServerRenderingBrowser-test.js
+12 -16
@@ -13,8 +13,6 @@ var React;
13 var ReactDOMServer;
14 var ReactDOMServerBrowser;
15
16 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
17 -
16 describe('ReactServerRenderingBrowser', () => {
17 beforeEach(() => {
18 jest.resetModules();
@@ -53,18 +51,16 @@ describe('ReactServerRenderingBrowser', () => {
51 );
52 });
53
56 - if (ReactDOMFeatureFlags.useFiber) {
57 - it('throws meaningfully for server-only APIs', () => {
58 - expect(() => ReactDOMServerBrowser.renderToNodeStream(<div />)).toThrow(
59 - 'ReactDOMServer.renderToNodeStream(): The streaming API is not available ' +
60 - 'in the browser. Use ReactDOMServer.renderToString() instead.',
61 - );
62 - expect(() =>
63 - ReactDOMServerBrowser.renderToStaticNodeStream(<div />),
64 - ).toThrow(
65 - 'ReactDOMServer.renderToStaticNodeStream(): The streaming API is not available ' +
66 - 'in the browser. Use ReactDOMServer.renderToStaticMarkup() instead.',
67 - );
68 - });
69 - }
54 + it('throws meaningfully for server-only APIs', () => {
55 + expect(() => ReactDOMServerBrowser.renderToNodeStream(<div />)).toThrow(
56 + 'ReactDOMServer.renderToNodeStream(): The streaming API is not available ' +
57 + 'in the browser. Use ReactDOMServer.renderToString() instead.',
58 + );
59 + expect(() =>
60 + ReactDOMServerBrowser.renderToStaticNodeStream(<div />),
61 + ).toThrow(
62 + 'ReactDOMServer.renderToStaticNodeStream(): The streaming API is not available ' +
63 + 'in the browser. Use ReactDOMServer.renderToStaticMarkup() instead.',
64 + );
65 + });
66 });
src/renderers/dom/shared/__tests__/renderSubtreeIntoContainer-test.js
+20 -23
@@ -12,7 +12,6 @@
12 var React = require('react');
13 var PropTypes = require('prop-types');
14 var ReactDOM = require('react-dom');
15 -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
15 var ReactTestUtils = require('react-dom/test-utils');
16 var renderSubtreeIntoContainer = require('react-dom')
17 .unstable_renderSubtreeIntoContainer;
@@ -299,26 +298,24 @@ describe('renderSubtreeIntoContainer', () => {
298 expect(portal2.textContent).toBe('foo');
299 });
300
302 - if (ReactDOMFeatureFlags.useFiber) {
303 - it('fails gracefully when mixing React 15 and 16', () => {
304 - class C extends React.Component {
305 - render() {
306 - return <div />;
307 - }
308 - }
309 - const c = ReactDOM.render(<C />, document.createElement('div'));
310 - // React 15 calls this:
311 - // https://github.com/facebook/react/blob/77b71fc3c4/src/renderers/dom/client/ReactMount.js#L478-L479
312 - expect(() => {
313 - c._reactInternalInstance._processChildContext({});
314 - }).toThrow(
315 - '_processChildContext is not available in React 16+. This likely ' +
316 - 'means you have multiple copies of React and are attempting to nest ' +
317 - 'a React 15 tree inside a React 16 tree using ' +
318 - "unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
319 - 'make sure you have only one copy of React (and ideally, switch to ' +
320 - 'ReactDOM.createPortal).',
321 - );
322 - });
323 - }
301 + it('fails gracefully when mixing React 15 and 16', () => {
302 + class C extends React.Component {
303 + render() {
304 + return <div />;
305 + }
306 + }
307 + const c = ReactDOM.render(<C />, document.createElement('div'));
308 + // React 15 calls this:
309 + // https://github.com/facebook/react/blob/77b71fc3c4/src/renderers/dom/client/ReactMount.js#L478-L479
310 + expect(() => {
311 + c._reactInternalInstance._processChildContext({});
312 + }).toThrow(
313 + '_processChildContext is not available in React 16+. This likely ' +
314 + 'means you have multiple copies of React and are attempting to nest ' +
315 + 'a React 15 tree inside a React 16 tree using ' +
316 + "unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
317 + 'make sure you have only one copy of React (and ideally, switch to ' +
318 + 'ReactDOM.createPortal).',
319 + );
320 + });
321 });
src/renderers/dom/shared/wrappers/__tests__/ReactDOMInput-test.js
-6
@@ -15,7 +15,6 @@ describe('ReactDOMInput', () => {
15 var React;
16 var ReactDOM;
17 var ReactDOMServer;
18 - var ReactDOMFeatureFlags;
18 var ReactTestUtils;
19 var inputValueTracking;
20
@@ -35,7 +34,6 @@ describe('ReactDOMInput', () => {
34 React = require('react');
35 ReactDOM = require('react-dom');
36 ReactDOMServer = require('react-dom/server');
38 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
37 ReactTestUtils = require('react-dom/test-utils');
38 // TODO: can we express this test with only public API?
39 inputValueTracking = require('inputValueTracking');
@@ -1187,7 +1185,6 @@ describe('ReactDOMInput', () => {
1185 <input value="0" type="range" min="0" max="100" step="1" />,
1186 );
1187 expect(log).toEqual([
1190 - ...(ReactDOMFeatureFlags.useFiber ? [] : ['set data-reactroot']),
1188 'set type',
1189 'set step',
1190 'set min',
@@ -1251,9 +1248,6 @@ describe('ReactDOMInput', () => {
1248 <input type="date" defaultValue="1980-01-01" />,
1249 );
1250 expect(log).toEqual([
1254 - ...(ReactDOMFeatureFlags.useFiber
1255 - ? []
1256 - : ['node.setAttribute("data-reactroot", "")']),
1251 'node.setAttribute("type", "date")',
1252 'node.setAttribute("value", "1980-01-01")',
1253 'node.value = ""',
src/renderers/dom/shared/wrappers/__tests__/ReactDOMOption-test.js
+3 -7
@@ -16,13 +16,11 @@ describe('ReactDOMOption', () => {
16
17 var React;
18 var ReactDOM;
19 - var ReactDOMFeatureFlags;
19 var ReactTestUtils;
20
21 beforeEach(() => {
22 React = require('react');
23 ReactDOM = require('react-dom');
25 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
24 ReactTestUtils = require('react-dom/test-utils');
25 });
26
@@ -45,11 +43,9 @@ describe('ReactDOMOption', () => {
43 expectDev(
44 normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
45 ).toContain(
48 - ReactDOMFeatureFlags.useFiber
49 - ? '<div> cannot appear as a child of <option>.\n' +
50 - ' in div (at **)\n' +
51 - ' in option (at **)'
52 - : 'Only strings and numbers are supported as <option> children.',
46 + '<div> cannot appear as a child of <option>.\n' +
47 + ' in div (at **)\n' +
48 + ' in option (at **)',
49 );
50 });
51
src/renderers/dom/test/__tests__/ReactTestUtils-test.js
+1 -5
@@ -12,7 +12,6 @@
12 let createRenderer;
13 let React;
14 let ReactDOM;
15 -let ReactDOMFeatureFlags;
15 let ReactDOMServer;
16 let ReactTestUtils;
17
@@ -23,7 +22,6 @@ describe('ReactTestUtils', () => {
22 ReactDOM = require('react-dom');
23 ReactDOMServer = require('react-dom/server');
24 ReactTestUtils = require('react-dom/test-utils');
26 - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
25 });
26
27 it('can scryRenderedDOMComponentsWithClass with TextComponent', () => {
@@ -173,9 +171,7 @@ describe('ReactTestUtils', () => {
171
172 const markup = ReactDOMServer.renderToString(<Root />);
173 const testDocument = getTestDocument(markup);
176 - const component = ReactDOMFeatureFlags.useFiber
177 - ? ReactDOM.hydrate(<Root />, testDocument)
178 - : ReactDOM.render(<Root />, testDocument);
174 + const component = ReactDOM.hydrate(<Root />, testDocument);
175
176 expect(component.refs.html.tagName).toBe('HTML');
177 expect(component.refs.head.tagName).toBe('HEAD');
src/renderers/shared/__tests__/ReactDOMFrameScheduling-test.js
+1 -4
@@ -9,10 +9,7 @@
9
10 'use strict';
11
12 -const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
13 -const describeFiber = ReactDOMFeatureFlags.useFiber ? describe : xdescribe;
14 -
15 -describeFiber('ReactDOMFrameScheduling', () => {
12 +describe('ReactDOMFrameScheduling', () => {
13 it('warns when requestAnimationFrame is not polyfilled in the browser', () => {
14 const previousRAF = global.requestAnimationFrame;
15 try {