diff --git a/src/isomorphic/children/__tests__/ReactChildren-test.js b/src/isomorphic/children/__tests__/ReactChildren-test.js index ad92430e622..88c68fe0aac 100644 --- a/src/isomorphic/children/__tests__/ReactChildren-test.js +++ b/src/isomorphic/children/__tests__/ReactChildren-test.js @@ -9,8 +9,6 @@ 'use strict'; -const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); - describe('ReactChildren', () => { var React; var ReactTestUtils; @@ -878,52 +876,50 @@ describe('ReactChildren', () => { ); }); - if (ReactDOMFeatureFlags.useFiber) { - describe('with fragments enabled', () => { - it('warns for keys for arrays of elements in a fragment', () => { - spyOn(console, 'error'); - class ComponentReturningArray extends React.Component { - render() { - return [
,
]; - } + describe('with fragments enabled', () => { + it('warns for keys for arrays of elements in a fragment', () => { + spyOn(console, 'error'); + class ComponentReturningArray extends React.Component { + render() { + return [
,
]; } + } - ReactTestUtils.renderIntoDocument(); + ReactTestUtils.renderIntoDocument(); - expectDev(console.error.calls.count()).toBe(1); - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( - 'Warning: ' + - 'Each child in an array or iterator should have a unique "key" prop.' + - ' See https://fb.me/react-warning-keys for more information.' + - '\n in ComponentReturningArray (at **)', - ); - }); + expectDev(console.error.calls.count()).toBe(1); + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( + 'Warning: ' + + 'Each child in an array or iterator should have a unique "key" prop.' + + ' See https://fb.me/react-warning-keys for more information.' + + '\n in ComponentReturningArray (at **)', + ); + }); - it('does not warn when there are keys on elements in a fragment', () => { - spyOn(console, 'error'); - class ComponentReturningArray extends React.Component { - render() { - return [
,
]; - } + it('does not warn when there are keys on elements in a fragment', () => { + spyOn(console, 'error'); + class ComponentReturningArray extends React.Component { + render() { + return [
,
]; } + } - ReactTestUtils.renderIntoDocument(); + ReactTestUtils.renderIntoDocument(); - expectDev(console.error.calls.count()).toBe(0); - }); + expectDev(console.error.calls.count()).toBe(0); + }); - it('warns for keys for arrays at the top level', () => { - spyOn(console, 'error'); + it('warns for keys for arrays at the top level', () => { + spyOn(console, 'error'); - ReactTestUtils.renderIntoDocument([
,
]); + ReactTestUtils.renderIntoDocument([
,
]); - expectDev(console.error.calls.count()).toBe(1); - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( - 'Warning: ' + - 'Each child in an array or iterator should have a unique "key" prop.' + - ' See https://fb.me/react-warning-keys for more information.', - ); - }); + expectDev(console.error.calls.count()).toBe(1); + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( + 'Warning: ' + + 'Each child in an array or iterator should have a unique "key" prop.' + + ' See https://fb.me/react-warning-keys for more information.', + ); }); - } + }); }); diff --git a/src/isomorphic/classic/element/__tests__/ReactElement-test.js b/src/isomorphic/classic/element/__tests__/ReactElement-test.js index 935937836ac..8a296917c03 100644 --- a/src/isomorphic/classic/element/__tests__/ReactElement-test.js +++ b/src/isomorphic/classic/element/__tests__/ReactElement-test.js @@ -12,7 +12,6 @@ var React; var ReactDOM; var ReactTestUtils; -var ReactDOMFeatureFlags; describe('ReactElement', () => { var ComponentClass; @@ -29,7 +28,6 @@ describe('ReactElement', () => { React = require('react'); ReactDOM = require('react-dom'); ReactTestUtils = require('react-dom/test-utils'); - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); // NOTE: We're explicitly not using JSX here. This is intended to test // classic JS without JSX. ComponentClass = class extends React.Component { @@ -235,12 +233,7 @@ describe('ReactElement', () => { var instance = ReactTestUtils.renderIntoDocument( React.createElement(Wrapper), ); - - if (ReactDOMFeatureFlags.useFiber) { - expect(element._owner.stateNode).toBe(instance); - } else { - expect(element._owner.getPublicInstance()).toBe(instance); - } + expect(element._owner.stateNode).toBe(instance); }); it('merges an additional argument onto the children prop', () => { diff --git a/src/renderers/__tests__/EventPluginHub-test.js b/src/renderers/__tests__/EventPluginHub-test.js index ea4accef400..74421573dc3 100644 --- a/src/renderers/__tests__/EventPluginHub-test.js +++ b/src/renderers/__tests__/EventPluginHub-test.js @@ -14,13 +14,11 @@ jest.mock('isEventSupported'); describe('EventPluginHub', () => { var React; var ReactTestUtils; - var ReactDOMFeatureFlags; beforeEach(() => { jest.resetModules(); React = require('react'); ReactTestUtils = require('react-dom/test-utils'); - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); }); it('should prevent non-function listeners, at dispatch', () => { @@ -33,14 +31,10 @@ describe('EventPluginHub', () => { }).toThrowError( 'Expected `onClick` listener to be a function, instead got a value of `string` type.', ); - if (ReactDOMFeatureFlags.useFiber) { - expectDev(console.error.calls.count()).toBe(1); - expectDev(console.error.calls.argsFor(0)[0]).toContain( - 'Expected `onClick` listener to be a function, instead got a value of `string` type.', - ); - } else { - expectDev(console.error.calls.count()).toBe(0); - } + expectDev(console.error.calls.count()).toBe(1); + expectDev(console.error.calls.argsFor(0)[0]).toContain( + 'Expected `onClick` listener to be a function, instead got a value of `string` type.', + ); }); it('should not prevent null listeners, at dispatch', () => { diff --git a/src/renderers/__tests__/ReactComponent-test.js b/src/renderers/__tests__/ReactComponent-test.js index f0db215a0ad..86a22155f36 100644 --- a/src/renderers/__tests__/ReactComponent-test.js +++ b/src/renderers/__tests__/ReactComponent-test.js @@ -14,8 +14,6 @@ var ReactDOM; var ReactDOMServer; var ReactTestUtils; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); - describe('ReactComponent', () => { function normalizeCodeLocInfo(str) { return str && str.replace(/\(at .+?:\d+\)/g, '(at **)'); @@ -297,21 +295,11 @@ describe('ReactComponent', () => { 'outer componentDidMount', 'start update', // Previous (equivalent) refs get cleared - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Fiber renders first, resets refs later - 'inner 1 render', - 'inner 2 render', - 'ref 1 got null', - 'ref 2 got null', - ] - : [ - // Stack resets refs before rendering - 'ref 1 got null', - 'inner 1 render', - 'ref 2 got null', - 'inner 2 render', - ]), + // Fiber renders first, resets refs later + 'inner 1 render', + 'inner 2 render', + 'ref 1 got null', + 'ref 2 got null', 'inner 1 componentDidUpdate', 'ref 1 got instance 1', 'inner 2 componentDidUpdate', @@ -406,8 +394,7 @@ describe('ReactComponent', () => { 'Objects are not valid as a React child (found: object with keys ' + '{x, y, z}). If you meant to render a collection of children, use ' + 'an array instead.' + - // Fiber gives a slightly better stack with the nearest host components - (ReactDOMFeatureFlags.useFiber ? '\n in div (at **)' : ''), + '\n in div (at **)', ); }); @@ -434,8 +421,7 @@ describe('ReactComponent', () => { 'Objects are not valid as a React child (found: object with keys ' + '{a, b, c}). If you meant to render a collection of children, use ' + 'an array instead.\n' + - // Fiber gives a slightly better stack with the nearest host components - (ReactDOMFeatureFlags.useFiber ? ' in div (at **)\n' : '') + + ' in div (at **)\n' + ' in Foo (at **)', ); }); @@ -458,8 +444,7 @@ describe('ReactComponent', () => { 'Objects are not valid as a React child (found: object with keys ' + '{x, y, z}). If you meant to render a collection of children, use ' + 'an array instead.' + - // Fiber gives a slightly better stack with the nearest host components - (ReactDOMFeatureFlags.useFiber ? '\n in div (at **)' : ''), + '\n in div (at **)', ); }); @@ -486,79 +471,76 @@ describe('ReactComponent', () => { 'Objects are not valid as a React child (found: object with keys ' + '{a, b, c}). If you meant to render a collection of children, use ' + 'an array instead.\n' + - // Fiber gives a slightly better stack with the nearest host components - (ReactDOMFeatureFlags.useFiber ? ' in div (at **)\n' : '') + + ' in div (at **)\n' + ' in Foo (at **)', ); }); - if (ReactDOMFeatureFlags.useFiber) { - describe('with new features', () => { - it('warns on function as a return value from a function', () => { - function Foo() { - return Foo; - } - spyOn(console, 'error'); - var container = document.createElement('div'); - ReactDOM.render(, container); - expectDev(console.error.calls.count()).toBe(1); - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( - 'Warning: Functions are not valid as a React child. This may happen if ' + - 'you return a Component instead of from render. ' + - 'Or maybe you meant to call this function rather than return it.\n' + - ' in Foo (at **)', - ); - }); + describe('with new features', () => { + it('warns on function as a return value from a function', () => { + function Foo() { + return Foo; + } + spyOn(console, 'error'); + var container = document.createElement('div'); + ReactDOM.render(, container); + expectDev(console.error.calls.count()).toBe(1); + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( + 'Warning: Functions are not valid as a React child. This may happen if ' + + 'you return a Component instead of from render. ' + + 'Or maybe you meant to call this function rather than return it.\n' + + ' in Foo (at **)', + ); + }); - it('warns on function as a return value from a class', () => { - class Foo extends React.Component { - render() { - return Foo; - } + it('warns on function as a return value from a class', () => { + class Foo extends React.Component { + render() { + return Foo; } - spyOn(console, 'error'); - var container = document.createElement('div'); - ReactDOM.render(, container); - expectDev(console.error.calls.count()).toBe(1); - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( - 'Warning: Functions are not valid as a React child. This may happen if ' + - 'you return a Component instead of from render. ' + - 'Or maybe you meant to call this function rather than return it.\n' + - ' in Foo (at **)', - ); - }); + } + spyOn(console, 'error'); + var container = document.createElement('div'); + ReactDOM.render(, container); + expectDev(console.error.calls.count()).toBe(1); + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( + 'Warning: Functions are not valid as a React child. This may happen if ' + + 'you return a Component instead of from render. ' + + 'Or maybe you meant to call this function rather than return it.\n' + + ' in Foo (at **)', + ); + }); - it('warns on function as a child to host component', () => { - function Foo() { - return
{Foo}
; - } - spyOn(console, 'error'); - var container = document.createElement('div'); - ReactDOM.render(, container); - expectDev(console.error.calls.count()).toBe(1); - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( - 'Warning: Functions are not valid as a React child. This may happen if ' + - 'you return a Component instead of from render. ' + - 'Or maybe you meant to call this function rather than return it.\n' + - ' in span (at **)\n' + - ' in div (at **)\n' + - ' in Foo (at **)', - ); - }); + it('warns on function as a child to host component', () => { + function Foo() { + return
{Foo}
; + } + spyOn(console, 'error'); + var container = document.createElement('div'); + ReactDOM.render(, container); + expectDev(console.error.calls.count()).toBe(1); + expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe( + 'Warning: Functions are not valid as a React child. This may happen if ' + + 'you return a Component instead of from render. ' + + 'Or maybe you meant to call this function rather than return it.\n' + + ' in span (at **)\n' + + ' in div (at **)\n' + + ' in Foo (at **)', + ); + }); - it('does not warn for function-as-a-child that gets resolved', () => { - function Bar(props) { - return props.children(); - } - function Foo() { - return {() => 'Hello'}; - } - spyOn(console, 'error'); - var container = document.createElement('div'); - ReactDOM.render(, container); - expect(container.innerHTML).toBe('Hello'); - expectDev(console.error.calls.count()).toBe(0); - }); + it('does not warn for function-as-a-child that gets resolved', () => { + function Bar(props) { + return props.children(); + } + function Foo() { + return {() => 'Hello'}; + } + spyOn(console, 'error'); + var container = document.createElement('div'); + ReactDOM.render(, container); + expect(container.innerHTML).toBe('Hello'); + expectDev(console.error.calls.count()).toBe(0); }); - } + }); }); diff --git a/src/renderers/__tests__/ReactComponentTreeHook-test.js b/src/renderers/__tests__/ReactComponentTreeHook-test.js index 76a55135497..06b1fe07a7c 100644 --- a/src/renderers/__tests__/ReactComponentTreeHook-test.js +++ b/src/renderers/__tests__/ReactComponentTreeHook-test.js @@ -9,13 +9,9 @@ 'use strict'; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var describeStack = ReactDOMFeatureFlags.useFiber ? describe.skip : describe; - -describe('ReactComponentTreeHook', () => { +describe.skip('ReactComponentTreeHook', () => { var React; var ReactDOM; - var ReactDOMServer; var ReactInstanceMap; var ReactComponentTreeHook; var ReactDebugCurrentFiber; @@ -26,7 +22,6 @@ describe('ReactComponentTreeHook', () => { React = require('react'); ReactDOM = require('react-dom'); - ReactDOMServer = require('react-dom/server'); ReactInstanceMap = require('ReactInstanceMap'); ReactDebugCurrentFiber = require('ReactDebugCurrentFiber'); ReactComponentTreeHook = require('ReactComponentTreeHook'); @@ -37,9 +32,8 @@ describe('ReactComponentTreeHook', () => { describe('stack addenda', () => { it('gets created', () => { function getAddendum(element) { - var addendum = ReactDOMFeatureFlags.useFiber - ? ReactDebugCurrentFiber.getCurrentFiberStackAddendum() || '' - : ReactComponentTreeHook.getCurrentStackAddendum(); + var addendum = + ReactDebugCurrentFiber.getCurrentFiberStackAddendum() || ''; return addendum.replace(/\(at .+?:\d+\)/g, '(at **)'); } @@ -107,68 +101,6 @@ describe('ReactComponentTreeHook', () => { // Make sure owner is fetched for the top element too. // expectDev(getAddendum(rOwnedByQ)).toBe('\n in R (created by Q)'); }); - - // These are features and regression tests that only affect - // the Stack implementation of the stack addendum. - if (!ReactDOMFeatureFlags.useFiber) { - it('can be retrieved by ID', () => { - function getAddendum(id) { - var addendum = ReactComponentTreeHook.getStackAddendumByID(id); - return addendum.replace(/\(at .+?:\d+\)/g, '(at **)'); - } - - class Q extends React.Component { - render() { - return null; - } - } - - var q = ReactDOM.render(, document.createElement('div')); - expectDev(getAddendum(ReactInstanceMap.get(q)._debugID)).toBe( - '\n in Q (at **)', - ); - - spyOn(console, 'error'); - getAddendum(-17); - expectDev(console.error.calls.count()).toBe(1); - expectDev(console.error.calls.argsFor(0)[0]).toBe( - 'Warning: ReactComponentTreeHook: Missing React element for ' + - 'debugID -17 when building stack', - ); - }); - - it('is created during mounting', () => { - // https://github.com/facebook/react/issues/7187 - var el = document.createElement('div'); - var portalEl = document.createElement('div'); - class Foo extends React.Component { - componentWillMount() { - ReactDOM.render(
, portalEl); - } - render() { - return
; - } - } - ReactDOM.render(, el); - }); - - it('is created when calling renderToString during render', () => { - // https://github.com/facebook/react/issues/7190 - var el = document.createElement('div'); - class Foo extends React.Component { - render() { - return ( -
-
- {ReactDOMServer.renderToString(
)} -
-
- ); - } - } - ReactDOM.render(, el); - }); - } }); // The rest of this file is not relevant for Fiber. @@ -227,7 +159,7 @@ describe('ReactComponentTreeHook', () => { expectWrapperTreeToEqual(null); } - describeStack('mount', () => { + describe('mount', () => { it('uses displayName or Unknown for classic components', () => { class Foo extends React.Component { render() { @@ -699,7 +631,7 @@ describe('ReactComponentTreeHook', () => { }); }); - describeStack('update', () => { + describe('update', () => { describe('host component', () => { it('updates text of a single text child', () => { var elementBefore =
Hi.
; @@ -1919,7 +1851,7 @@ describe('ReactComponentTreeHook', () => { }); }); - describeStack('misc', () => { + describe('misc', () => { it('tracks owner correctly', () => { class Foo extends React.Component { render() { @@ -2089,7 +2021,7 @@ describe('ReactComponentTreeHook', () => { }); }); - describeStack('in environment without Map, Set and Array.from', () => { + describe('in environment without Map, Set and Array.from', () => { var realMap; var realSet; var realArrayFrom; @@ -2107,7 +2039,6 @@ describe('ReactComponentTreeHook', () => { React = require('react'); ReactDOM = require('react-dom'); - ReactDOMServer = require('react-dom/server'); ReactInstanceMap = require('ReactInstanceMap'); ReactComponentTreeHook = require('ReactComponentTreeHook'); ReactComponentTreeTestUtils = require('ReactComponentTreeTestUtils'); diff --git a/src/renderers/__tests__/ReactCompositeComponent-test.js b/src/renderers/__tests__/ReactCompositeComponent-test.js index 2fb20852167..75f3e8060f3 100644 --- a/src/renderers/__tests__/ReactCompositeComponent-test.js +++ b/src/renderers/__tests__/ReactCompositeComponent-test.js @@ -13,7 +13,6 @@ var ChildUpdates; var MorphingComponent; var React; var ReactDOM; -var ReactDOMFeatureFlags; var ReactDOMServer; var ReactCurrentOwner; var ReactTestUtils; @@ -26,7 +25,6 @@ describe('ReactCompositeComponent', () => { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); ReactDOMServer = require('react-dom/server'); ReactCurrentOwner = require('react') .__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner; @@ -123,25 +121,19 @@ describe('ReactCompositeComponent', () => { var container = document.createElement('div'); container.innerHTML = markup; ReactDOM.render(, container); - if (ReactDOMFeatureFlags.useFiber) { - expectDev(console.warn.calls.count()).toBe(1); - expectDev(console.warn.calls.argsFor(0)[0]).toContain( - 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + - 'will stop working in React v17. Replace the ReactDOM.render() call ' + - 'with ReactDOM.hydrate() if you want React to attach to the server HTML.', - ); - } else { - expectDev(console.warn.calls.count()).toBe(0); - } + expectDev(console.warn.calls.count()).toBe(1); + expectDev(console.warn.calls.argsFor(0)[0]).toContain( + 'render(): Calling ReactDOM.render() to hydrate server-rendered markup ' + + 'will stop working in React v17. Replace the ReactDOM.render() call ' + + 'with ReactDOM.hydrate() if you want React to attach to the server HTML.', + ); // New explicit API console.warn.calls.reset(); - if (ReactDOMFeatureFlags.useFiber) { - container = document.createElement('div'); - container.innerHTML = markup; - ReactDOM.hydrate(, container); - expectDev(console.warn.calls.count()).toBe(0); - } + container = document.createElement('div'); + container.innerHTML = markup; + ReactDOM.hydrate(, container); + expectDev(console.warn.calls.count()).toBe(0); }); it('should react to state changes from callbacks', () => { diff --git a/src/renderers/__tests__/ReactCompositeComponentState-test.js b/src/renderers/__tests__/ReactCompositeComponentState-test.js index 277dd119cab..e2e04fa70fe 100644 --- a/src/renderers/__tests__/ReactCompositeComponentState-test.js +++ b/src/renderers/__tests__/ReactCompositeComponentState-test.js @@ -11,7 +11,6 @@ var React; var ReactDOM; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var TestComponent; @@ -179,17 +178,9 @@ describe('ReactCompositeComponent-state', () => { // setState({color:'green'}) only enqueues a pending state. ['componentWillReceiveProps-end', 'yellow'], // pending state queue is processed - ]; - - if (ReactDOMFeatureFlags.useFiber) { - // In Stack, this is never called because replaceState drops all updates - // from the queue. In Fiber, we keep updates in the queue to support + // We keep updates in the queue to support // replaceState(prevState => newState). - // TODO: Fix Stack to match Fiber. - expected.push(['before-setState-receiveProps', 'yellow']); - } - - expected.push( + ['before-setState-receiveProps', 'yellow'], ['before-setState-again-receiveProps', undefined], ['after-setState-receiveProps', 'green'], ['shouldComponentUpdate-currentState', 'yellow'], @@ -220,7 +211,7 @@ describe('ReactCompositeComponent-state', () => { // unmountComponent() // state is available within `componentWillUnmount()` ['componentWillUnmount', 'blue'], - ); + ]; expect(stateListener.mock.calls.join('\n')).toEqual(expected.join('\n')); }); @@ -289,20 +280,7 @@ describe('ReactCompositeComponent-state', () => { child.setState({bar: false}); }); // We expect the same thing to happen if we bail out in the middle. - expect(ops).toEqual( - ReactDOMFeatureFlags.useFiber - ? [ - // Fiber works as expected - 'child did update', - 'parent did update', - ] - : [ - // Stack treats these as two separate updates and therefore the order - // is inverse. - 'parent did update', - 'child did update', - ], - ); + expect(ops).toEqual(['child did update', 'parent did update']); }); it('should batch unmounts', () => { @@ -445,45 +423,43 @@ describe('ReactCompositeComponent-state', () => { ); }); - if (ReactDOMFeatureFlags.useFiber) { - it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => { - spyOn(console, 'error'); - - let ops = []; - class Test extends React.Component { - state = {step: 1, extra: true}; - componentWillMount() { - this.setState({step: 2}, () => { - // Tests that earlier setState callbacks are not dropped - ops.push( - `callback -- step: ${this.state.step}, extra: ${!!this.state.extra}`, - ); - }); - // Treat like replaceState - this.state = {step: 3}; - } - render() { + it('should treat assigning to this.state inside cWM as a replaceState, with a warning', () => { + spyOn(console, 'error'); + + let ops = []; + class Test extends React.Component { + state = {step: 1, extra: true}; + componentWillMount() { + this.setState({step: 2}, () => { + // Tests that earlier setState callbacks are not dropped ops.push( - `render -- step: ${this.state.step}, extra: ${!!this.state.extra}`, + `callback -- step: ${this.state.step}, extra: ${!!this.state.extra}`, ); - return null; - } + }); + // Treat like replaceState + this.state = {step: 3}; } + render() { + ops.push( + `render -- step: ${this.state.step}, extra: ${!!this.state.extra}`, + ); + return null; + } + } - // Mount - const container = document.createElement('div'); - ReactDOM.render(, container); - - expect(ops).toEqual([ - 'render -- step: 3, extra: false', - 'callback -- step: 3, extra: false', - ]); - expect(console.error.calls.count()).toEqual(1); - expect(console.error.calls.argsFor(0)[0]).toEqual( - 'Warning: Test.componentWillMount(): Assigning directly to ' + - "this.state is deprecated (except inside a component's constructor). " + - 'Use setState instead.', - ); - }); - } + // Mount + const container = document.createElement('div'); + ReactDOM.render(, container); + + expect(ops).toEqual([ + 'render -- step: 3, extra: false', + 'callback -- step: 3, extra: false', + ]); + expect(console.error.calls.count()).toEqual(1); + expect(console.error.calls.argsFor(0)[0]).toEqual( + 'Warning: Test.componentWillMount(): Assigning directly to ' + + "this.state is deprecated (except inside a component's constructor). " + + 'Use setState instead.', + ); + }); }); diff --git a/src/renderers/__tests__/ReactEmptyComponent-test.js b/src/renderers/__tests__/ReactEmptyComponent-test.js index d911008ba00..e8a61a6833f 100644 --- a/src/renderers/__tests__/ReactEmptyComponent-test.js +++ b/src/renderers/__tests__/ReactEmptyComponent-test.js @@ -14,8 +14,6 @@ var ReactDOM; var ReactTestUtils; var TogglingComponent; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); - var log; describe('ReactEmptyComponent', () => { @@ -69,20 +67,18 @@ describe('ReactEmptyComponent', () => { expect(container2.children.length).toBe(0); }); - if (ReactDOMFeatureFlags.useFiber) { - it('should still throw when rendering to undefined', () => { - class Component extends React.Component { - render() {} - } + it('should still throw when rendering to undefined', () => { + class Component extends React.Component { + render() {} + } - expect(function() { - ReactTestUtils.renderIntoDocument(); - }).toThrowError( - 'Component(...): Nothing was returned from render. This usually means a return statement is missing. ' + - 'Or, to render nothing, return null.', - ); - }); - } + expect(function() { + ReactTestUtils.renderIntoDocument(); + }).toThrowError( + 'Component(...): Nothing was returned from render. This usually means a return statement is missing. ' + + 'Or, to render nothing, return null.', + ); + }); it('should be able to switch between rendering null and a normal tag', () => { var instance1 = ( @@ -233,15 +229,8 @@ describe('ReactEmptyComponent', () => { it('can render null at the top level', () => { var div = document.createElement('div'); - if (ReactDOMFeatureFlags.useFiber) { - ReactDOM.render(null, div); - expect(div.innerHTML).toBe(''); - } else { - // Stack does not implement this. - expect(function() { - ReactDOM.render(null, div); - }).toThrowError('ReactDOM.render(): Invalid component element.'); - } + ReactDOM.render(null, div); + expect(div.innerHTML).toBe(''); }); it('does not break when updating during mount', () => { @@ -293,21 +282,11 @@ describe('ReactEmptyComponent', () => { ReactDOM.render(, container); var noscript1 = container.firstChild; - if (ReactDOMFeatureFlags.useFiber) { - expect(noscript1).toBe(null); - } else { - expect(noscript1.nodeName).toBe('#comment'); - } + expect(noscript1).toBe(null); // This update shouldn't create a DOM node ReactDOM.render(, container); var noscript2 = container.firstChild; - if (ReactDOMFeatureFlags.useFiber) { - expect(noscript1).toBe(null); - } else { - expect(noscript2.nodeName).toBe('#comment'); - } - - expect(noscript1).toBe(noscript2); + expect(noscript2).toBe(null); }); }); diff --git a/src/renderers/__tests__/ReactErrorBoundaries-test.js b/src/renderers/__tests__/ReactErrorBoundaries-test.js index 826faf37f9d..0442b570ef8 100644 --- a/src/renderers/__tests__/ReactErrorBoundaries-test.js +++ b/src/renderers/__tests__/ReactErrorBoundaries-test.js @@ -9,8 +9,6 @@ 'use strict'; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); - var PropTypes; var React; var ReactDOM; @@ -399,12 +397,7 @@ describe('ReactErrorBoundaries', () => { log.push('NoopErrorBoundary componentWillUnmount'); } componentDidCatch() { - if (ReactDOMFeatureFlags.useFiber) { - log.push('NoopErrorBoundary componentDidCatch'); - } else { - // In Stack, not calling setState() is treated as a rethrow. - log.push('NoopErrorBoundary componentDidCatch [*]'); - } + log.push('NoopErrorBoundary componentDidCatch'); } }; @@ -507,14 +500,9 @@ describe('ReactErrorBoundaries', () => { log.push('RetryErrorBoundary componentWillUnmount'); } componentDidCatch(error) { - if (ReactDOMFeatureFlags.useFiber) { - log.push('RetryErrorBoundary componentDidCatch [!]'); - // In Fiber, calling setState() (and failing) is treated as a rethrow. - this.setState({}); - } else { - log.push('RetryErrorBoundary componentDidCatch [*]'); - // In Stack, not calling setState() is treated as a rethrow. - } + log.push('RetryErrorBoundary componentDidCatch [!]'); + // In Fiber, calling setState() (and failing) is treated as a rethrow. + this.setState({}); } }; @@ -639,22 +627,13 @@ describe('ReactErrorBoundaries', () => { 'BrokenRender constructor', 'BrokenRender componentWillMount', 'BrokenRender render [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Fiber mounts with null children before capturing error - 'ErrorBoundary componentDidMount', - // Catch and render an error message - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - // Catch and render an error message - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidMount', - ]), + // Fiber mounts with null children before capturing error + 'ErrorBoundary componentDidMount', + // Catch and render an error message + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -676,22 +655,13 @@ describe('ReactErrorBoundaries', () => { 'ErrorBoundary componentWillMount', 'ErrorBoundary render success', 'BrokenConstructor constructor [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Fiber mounts with null children before capturing error - 'ErrorBoundary componentDidMount', - // Catch and render an error message - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - // Catch and render an error message - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidMount', - ]), + // Fiber mounts with null children before capturing error + 'ErrorBoundary componentDidMount', + // Catch and render an error message + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -714,20 +684,11 @@ describe('ReactErrorBoundaries', () => { 'ErrorBoundary render success', 'BrokenComponentWillMount constructor', 'BrokenComponentWillMount componentWillMount [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - 'ErrorBoundary componentDidMount', - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - // Catch and render an error message - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidMount', - ]), + 'ErrorBoundary componentDidMount', + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -806,29 +767,15 @@ describe('ReactErrorBoundaries', () => { 'BrokenRender constructor', 'BrokenRender componentWillMount', 'BrokenRender render [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - 'ErrorBoundary componentDidMount', - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorMessage constructor', - 'ErrorMessage componentWillMount', - 'ErrorMessage render', - 'ErrorMessage componentDidMount', - 'ErrorBoundary componentDidUpdate', - ] - : [ - // Handle the error: - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary render error', - // Mount the error message: - 'ErrorMessage constructor', - 'ErrorMessage componentWillMount', - 'ErrorMessage render', - 'ErrorMessage componentDidMount', - 'ErrorBoundary componentDidMount', - ]), + 'ErrorBoundary componentDidMount', + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorMessage constructor', + 'ErrorMessage componentWillMount', + 'ErrorMessage render', + 'ErrorMessage componentDidMount', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -860,39 +807,22 @@ describe('ReactErrorBoundaries', () => { 'BrokenRender constructor', 'BrokenRender componentWillMount', 'BrokenRender render [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // In Fiber, failed error boundaries render null before attempting to recover - 'RetryErrorBoundary componentDidMount', - 'RetryErrorBoundary componentDidCatch [!]', - 'ErrorBoundary componentDidMount', - // Retry - 'RetryErrorBoundary render', - 'BrokenRender constructor', - 'BrokenRender componentWillMount', - 'BrokenRender render [!]', - // This time, the error propagates to the higher boundary - 'RetryErrorBoundary componentWillUnmount', - 'ErrorBoundary componentDidCatch', - // Render the error - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - // The first error boundary catches the error. - // However, it doesn't adjust its state so next render will also fail. - 'RetryErrorBoundary componentDidCatch [*]', - 'RetryErrorBoundary render', - 'BrokenRender constructor', - 'BrokenRender componentWillMount', - 'BrokenRender render [!]', - // This time, the error propagates to the higher boundary - 'ErrorBoundary componentDidCatch', - // Render the error - 'ErrorBoundary render error', - 'ErrorBoundary componentDidMount', - ]), + // In Fiber, failed error boundaries render null before attempting to recover + 'RetryErrorBoundary componentDidMount', + 'RetryErrorBoundary componentDidCatch [!]', + 'ErrorBoundary componentDidMount', + // Retry + 'RetryErrorBoundary render', + 'BrokenRender constructor', + 'BrokenRender componentWillMount', + 'BrokenRender render [!]', + // This time, the error propagates to the higher boundary + 'RetryErrorBoundary componentWillUnmount', + 'ErrorBoundary componentDidCatch', + // Render the error + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -916,20 +846,11 @@ describe('ReactErrorBoundaries', () => { 'BrokenComponentWillMountErrorBoundary constructor', 'BrokenComponentWillMountErrorBoundary componentWillMount [!]', // The error propagates to the higher boundary - ...(ReactDOMFeatureFlags.useFiber - ? [ - 'ErrorBoundary componentDidMount', - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Render the error - 'ErrorBoundary render error', - 'ErrorBoundary componentDidMount', - ]), + 'ErrorBoundary componentDidMount', + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -960,31 +881,19 @@ describe('ReactErrorBoundaries', () => { 'BrokenRender render [!]', // The first error boundary catches the error // It adjusts state but throws displaying the message - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Finish mounting with null children - 'BrokenRenderErrorBoundary componentDidMount', - // Attempt to handle the error - 'BrokenRenderErrorBoundary componentDidCatch', - 'ErrorBoundary componentDidMount', - 'BrokenRenderErrorBoundary render error [!]', - // Boundary fails with new error, propagate to next boundary - 'BrokenRenderErrorBoundary componentWillUnmount', - // Attempt to handle the error again - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'BrokenRenderErrorBoundary componentDidCatch', - 'BrokenRenderErrorBoundary render error [!]', - // The error propagates to the higher boundary - 'ErrorBoundary componentDidCatch', - // Render the error - 'ErrorBoundary render error', - 'ErrorBoundary componentDidMount', - ]), + // Finish mounting with null children + 'BrokenRenderErrorBoundary componentDidMount', + // Attempt to handle the error + 'BrokenRenderErrorBoundary componentDidCatch', + 'ErrorBoundary componentDidMount', + 'BrokenRenderErrorBoundary render error [!]', + // Boundary fails with new error, propagate to next boundary + 'BrokenRenderErrorBoundary componentWillUnmount', + // Attempt to handle the error again + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1015,23 +924,14 @@ describe('ReactErrorBoundaries', () => { 'BrokenRender constructor', 'BrokenRender componentWillMount', 'BrokenRender render [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Finish mounting with null children - 'ErrorBoundary componentDidMount', - // Handle the error - 'ErrorBoundary componentDidCatch', - // Render the error message - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Render the error message - 'ErrorBoundary render error', - 'ErrorBoundary componentDidMount', - ]), + // Finish mounting with null children + 'ErrorBoundary componentDidMount', + // Handle the error + 'ErrorBoundary componentDidCatch', + // Render the error message + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1064,28 +964,15 @@ describe('ReactErrorBoundaries', () => { 'BrokenRender componentWillMount', 'BrokenRender render [!]', // Handle error: - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Finish mounting with null children - 'ErrorBoundary componentDidMount', - // Handle the error - 'ErrorBoundary componentDidCatch', - // Render the error message - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'Error message ref is set to [object HTMLDivElement]', - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Stack reconciler resets ref on update, as it doesn't know ref was never set. - // This is unnecessary, and Fiber doesn't do it: - 'Child ref is set to null', - 'ErrorBoundary render error', - // Ref to error message should get set: - 'Error message ref is set to [object HTMLDivElement]', - 'ErrorBoundary componentDidMount', - ]), + // Finish mounting with null children + 'ErrorBoundary componentDidMount', + // Handle the error + 'ErrorBoundary componentDidCatch', + // Render the error message + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'Error message ref is set to [object HTMLDivElement]', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1149,26 +1036,15 @@ describe('ReactErrorBoundaries', () => { 'Normal2 render', // BrokenConstructor will abort rendering: 'BrokenConstructor constructor [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Finish updating with null children - 'Normal componentWillUnmount', - 'ErrorBoundary componentDidUpdate', - // Handle the error - 'ErrorBoundary componentDidCatch', - // Render the error message - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Stack unmounts first, then renders: - 'Normal componentWillUnmount', - 'ErrorBoundary render error', - // Normal2 does not get lifefycle because it was never mounted - 'ErrorBoundary componentDidUpdate', - ]), + // Finish updating with null children + 'Normal componentWillUnmount', + 'ErrorBoundary componentDidUpdate', + // Handle the error + 'ErrorBoundary componentDidCatch', + // Render the error message + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1209,26 +1085,15 @@ describe('ReactErrorBoundaries', () => { // BrokenComponentWillMount will abort rendering: 'BrokenComponentWillMount constructor', 'BrokenComponentWillMount componentWillMount [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Finish updating with null children - 'Normal componentWillUnmount', - 'ErrorBoundary componentDidUpdate', - // Handle the error - 'ErrorBoundary componentDidCatch', - // Render the error message - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Stack unmounts first, then renders: - 'Normal componentWillUnmount', - 'ErrorBoundary render error', - // Normal2 does not get lifefycle because it was never mounted - 'ErrorBoundary componentDidUpdate', - ]), + // Finish updating with null children + 'Normal componentWillUnmount', + 'ErrorBoundary componentDidUpdate', + // Handle the error + 'ErrorBoundary componentDidCatch', + // Render the error message + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1264,26 +1129,15 @@ describe('ReactErrorBoundaries', () => { 'Normal render', // BrokenComponentWillReceiveProps will abort rendering: 'BrokenComponentWillReceiveProps componentWillReceiveProps [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Finish updating with null children - 'Normal componentWillUnmount', - 'BrokenComponentWillReceiveProps componentWillUnmount', - 'ErrorBoundary componentDidUpdate', - // Handle the error - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Stack unmounts first, then renders: - 'Normal componentWillUnmount', - 'BrokenComponentWillReceiveProps componentWillUnmount', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ]), + // Finish updating with null children + 'Normal componentWillUnmount', + 'BrokenComponentWillReceiveProps componentWillUnmount', + 'ErrorBoundary componentDidUpdate', + // Handle the error + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1320,26 +1174,15 @@ describe('ReactErrorBoundaries', () => { // BrokenComponentWillUpdate will abort rendering: 'BrokenComponentWillUpdate componentWillReceiveProps', 'BrokenComponentWillUpdate componentWillUpdate [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Finish updating with null children - 'Normal componentWillUnmount', - 'BrokenComponentWillUpdate componentWillUnmount', - 'ErrorBoundary componentDidUpdate', - // Handle the error - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Stack unmounts first, then renders: - 'Normal componentWillUnmount', - 'BrokenComponentWillUpdate componentWillUnmount', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ]), + // Finish updating with null children + 'Normal componentWillUnmount', + 'BrokenComponentWillUpdate componentWillUnmount', + 'ErrorBoundary componentDidUpdate', + // Handle the error + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1381,25 +1224,14 @@ describe('ReactErrorBoundaries', () => { 'BrokenRender constructor', 'BrokenRender componentWillMount', 'BrokenRender render [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Finish updating with null children - 'Normal componentWillUnmount', - 'ErrorBoundary componentDidUpdate', - // Handle the error - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Stack unmounts first, then renders: - 'Normal componentWillUnmount', - 'ErrorBoundary render error', - // Normal2 does not get lifefycle because it was never mounted - 'ErrorBoundary componentDidUpdate', - ]), + // Finish updating with null children + 'Normal componentWillUnmount', + 'ErrorBoundary componentDidUpdate', + // Handle the error + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1451,22 +1283,13 @@ describe('ReactErrorBoundaries', () => { 'BrokenRender constructor', 'BrokenRender componentWillMount', 'BrokenRender render [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Finish updating with null children - 'Child1 ref is set to null', - 'ErrorBoundary componentDidUpdate', - // Handle the error - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Stack resets ref first, renders later - 'Child1 ref is set to null', - 'ErrorBoundary render error', - ]), + // Finish updating with null children + 'Child1 ref is set to null', + 'ErrorBoundary componentDidUpdate', + // Handle the error + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', 'Error message ref is set to [object HTMLDivElement]', // Child2 ref is never set because its mounting aborted 'ErrorBoundary componentDidUpdate', @@ -1509,35 +1332,22 @@ describe('ReactErrorBoundaries', () => { 'BrokenComponentWillUnmount render', // Unmounting throws: 'BrokenComponentWillUnmount componentWillUnmount [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Fiber proceeds with lifecycles despite errors - 'Normal componentWillUnmount', - // The components have updated in this phase - 'BrokenComponentWillUnmount componentDidUpdate', - 'ErrorBoundary componentDidUpdate', - // Now that commit phase is done, Fiber unmounts the boundary's children - 'BrokenComponentWillUnmount componentWillUnmount [!]', - 'ErrorBoundary componentDidCatch', - // The initial render was aborted, so - // Fiber retries from the root. - 'ErrorBoundary componentWillUpdate', - // Render an error now (stack will do it later) - 'ErrorBoundary render error', - // Attempt to unmount previous child: - // Done - 'ErrorBoundary componentDidUpdate', - ] - : [ - // Stack will handle error immediately - 'ErrorBoundary componentDidCatch', - // Attempt to unmount previous children: - 'BrokenComponentWillUnmount componentWillUnmount [!]', - 'Normal componentWillUnmount', - // Render an error now (Fiber will do it earlier) - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ]), + // Fiber proceeds with lifecycles despite errors + 'Normal componentWillUnmount', + // The components have updated in this phase + 'BrokenComponentWillUnmount componentDidUpdate', + 'ErrorBoundary componentDidUpdate', + // Now that commit phase is done, Fiber unmounts the boundary's children + 'BrokenComponentWillUnmount componentWillUnmount [!]', + 'ErrorBoundary componentDidCatch', + // The initial render was aborted, so + // Fiber retries from the root. + 'ErrorBoundary componentWillUpdate', + // Render an error now (stack will do it later) + 'ErrorBoundary render error', + // Attempt to unmount previous child: + // Done + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1580,33 +1390,21 @@ describe('ReactErrorBoundaries', () => { 'BrokenComponentWillUnmount render', // Unmounting throws: 'BrokenComponentWillUnmount componentWillUnmount [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Fiber proceeds with lifecycles despite errors - 'BrokenComponentWillUnmount componentDidUpdate', - 'Normal componentDidUpdate', - 'ErrorBoundary componentDidUpdate', - 'Normal componentWillUnmount', - 'BrokenComponentWillUnmount componentWillUnmount [!]', - // Now that commit phase is done, Fiber handles errors - 'ErrorBoundary componentDidCatch', - // The initial render was aborted, so - // Fiber retries from the root. - 'ErrorBoundary componentWillUpdate', - // Render an error now (stack will do it later) - 'ErrorBoundary render error', - // Done - 'ErrorBoundary componentDidUpdate', - ] - : [ - 'ErrorBoundary componentDidCatch', - // Attempt to unmount previous children: - 'Normal componentWillUnmount', - 'BrokenComponentWillUnmount componentWillUnmount [!]', - // Stack calls lifecycles first, then renders. - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ]), + // Fiber proceeds with lifecycles despite errors + 'BrokenComponentWillUnmount componentDidUpdate', + 'Normal componentDidUpdate', + 'ErrorBoundary componentDidUpdate', + 'Normal componentWillUnmount', + 'BrokenComponentWillUnmount componentWillUnmount [!]', + // Now that commit phase is done, Fiber handles errors + 'ErrorBoundary componentDidCatch', + // The initial render was aborted, so + // Fiber retries from the root. + 'ErrorBoundary componentWillUpdate', + // Render an error now (stack will do it later) + 'ErrorBoundary render error', + // Done + 'ErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1660,31 +1458,19 @@ describe('ReactErrorBoundaries', () => { 'InnerErrorBoundary render success', // Try unmounting child 'BrokenComponentWillUnmount componentWillUnmount [!]', - ...(ReactDOMFeatureFlags.useFiber - ? [ - // Fiber proceeds with lifecycles despite errors - // Inner and outer boundaries have updated in this phase - 'InnerErrorBoundary componentDidUpdate', - 'OuterErrorBoundary componentDidUpdate', - // Now that commit phase is done, Fiber handles errors - // Only inner boundary receives the error: - 'InnerErrorBoundary componentDidCatch', - 'InnerErrorBoundary componentWillUpdate', - // Render an error now - 'InnerErrorBoundary render error', - // In Fiber, this was a local update to the - // inner boundary so only its hook fires - 'InnerErrorBoundary componentDidUpdate', - ] - : [ - // Stack will handle error immediately - 'InnerErrorBoundary componentDidCatch', - 'InnerErrorBoundary render error', - // In stack, this was a part of the update to the - // outer boundary so both lifecycles fire - 'InnerErrorBoundary componentDidUpdate', - 'OuterErrorBoundary componentDidUpdate', - ]), + // Fiber proceeds with lifecycles despite errors + // Inner and outer boundaries have updated in this phase + 'InnerErrorBoundary componentDidUpdate', + 'OuterErrorBoundary componentDidUpdate', + // Now that commit phase is done, Fiber handles errors + // Only inner boundary receives the error: + 'InnerErrorBoundary componentDidCatch', + 'InnerErrorBoundary componentWillUpdate', + // Render an error now + 'InnerErrorBoundary render error', + // In Fiber, this was a local update to the + // inner boundary so only its hook fires + 'InnerErrorBoundary componentDidUpdate', ]); log.length = 0; @@ -1856,374 +1642,371 @@ describe('ReactErrorBoundaries', () => { expect(log).toEqual(['ErrorBoundary componentWillUnmount']); }); - // The tests below implement new features in Fiber. - if (ReactDOMFeatureFlags.useFiber) { - it('catches errors originating downstream', () => { - var fail = false; - class Stateful extends React.Component { - state = {shouldThrow: false}; + it('catches errors originating downstream', () => { + var fail = false; + class Stateful extends React.Component { + state = {shouldThrow: false}; - render() { - if (fail) { - log.push('Stateful render [!]'); - throw new Error('Hello'); - } - return
{this.props.children}
; + render() { + if (fail) { + log.push('Stateful render [!]'); + throw new Error('Hello'); } + return
{this.props.children}
; } + } - var statefulInst; - var container = document.createElement('div'); - ReactDOM.render( - - (statefulInst = inst)} /> - , - container, - ); - - log.length = 0; - expect(() => { - fail = true; - statefulInst.forceUpdate(); - }).not.toThrow(); - - expect(log).toEqual([ - 'Stateful render [!]', - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ]); - - log.length = 0; - ReactDOM.unmountComponentAtNode(container); - expect(log).toEqual(['ErrorBoundary componentWillUnmount']); - }); - - it('catches errors in componentDidMount', () => { - var container = document.createElement('div'); - ReactDOM.render( - - - - - - - , - container, - ); - expect(log).toEqual([ - 'ErrorBoundary constructor', - 'ErrorBoundary componentWillMount', - 'ErrorBoundary render success', - 'BrokenComponentWillUnmount constructor', - 'BrokenComponentWillUnmount componentWillMount', - 'BrokenComponentWillUnmount render', - 'Normal constructor', - 'Normal componentWillMount', - 'Normal render', - 'BrokenComponentDidMount constructor', - 'BrokenComponentDidMount componentWillMount', - 'BrokenComponentDidMount render', - 'LastChild constructor', - 'LastChild componentWillMount', - 'LastChild render', - // Start flushing didMount queue - 'Normal componentDidMount', - 'BrokenComponentWillUnmount componentDidMount', - 'BrokenComponentDidMount componentDidMount [!]', - // Continue despite the error - 'LastChild componentDidMount', - 'ErrorBoundary componentDidMount', - // Now we are ready to handle the error - // Safely unmount every child - 'BrokenComponentWillUnmount componentWillUnmount [!]', - // Continue unmounting safely despite any errors - 'Normal componentWillUnmount', - 'BrokenComponentDidMount componentWillUnmount', - 'LastChild componentWillUnmount', - // Handle the error - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - // The update has finished - 'ErrorBoundary componentDidUpdate', - ]); - - log.length = 0; - ReactDOM.unmountComponentAtNode(container); - expect(log).toEqual(['ErrorBoundary componentWillUnmount']); - }); - - it('catches errors in componentDidUpdate', () => { - var container = document.createElement('div'); - ReactDOM.render( - - - , - container, - ); - - log.length = 0; - ReactDOM.render( - - - , - container, - ); - expect(log).toEqual([ - 'ErrorBoundary componentWillReceiveProps', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render success', - 'BrokenComponentDidUpdate componentWillReceiveProps', - 'BrokenComponentDidUpdate componentWillUpdate', - 'BrokenComponentDidUpdate render', - // All lifecycles run - 'BrokenComponentDidUpdate componentDidUpdate [!]', - 'ErrorBoundary componentDidUpdate', - 'BrokenComponentDidUpdate componentWillUnmount', - // Then, error is handled - 'ErrorBoundary componentDidCatch', - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ]); - - log.length = 0; - ReactDOM.unmountComponentAtNode(container); - expect(log).toEqual(['ErrorBoundary componentWillUnmount']); - }); - - it('propagates errors inside boundary during componentDidMount', () => { - var container = document.createElement('div'); - ReactDOM.render( - - ( -
- We should never catch our own error: {error.message}. -
- )} - /> -
, - container, - ); - expect(container.firstChild.textContent).toBe('Caught an error: Hello.'); - expect(log).toEqual([ - 'ErrorBoundary constructor', - 'ErrorBoundary componentWillMount', - 'ErrorBoundary render success', - 'BrokenComponentDidMountErrorBoundary constructor', - 'BrokenComponentDidMountErrorBoundary componentWillMount', - 'BrokenComponentDidMountErrorBoundary render success', - 'BrokenComponentDidMountErrorBoundary componentDidMount [!]', - // Fiber proceeds with the hooks - 'ErrorBoundary componentDidMount', - 'BrokenComponentDidMountErrorBoundary componentWillUnmount', - // The error propagates to the higher boundary - 'ErrorBoundary componentDidCatch', - // Fiber retries from the root - 'ErrorBoundary componentWillUpdate', - 'ErrorBoundary render error', - 'ErrorBoundary componentDidUpdate', - ]); - - log.length = 0; - ReactDOM.unmountComponentAtNode(container); - expect(log).toEqual(['ErrorBoundary componentWillUnmount']); - }); - - it('lets different boundaries catch their own first errors', () => { - function renderUnmountError(error) { - return
Caught an unmounting error: {error.message}.
; - } - function renderUpdateError(error) { - return
Caught an updating error: {error.message}.
; - } - - var container = document.createElement('div'); - ReactDOM.render( - - - - - - - - - - , - container, - ); - - log.length = 0; - ReactDOM.render( - - - - - - - , - container, - ); - - expect(container.firstChild.textContent).toBe( - 'Caught an unmounting error: E1.' + 'Caught an updating error: E3.', - ); - expect(log).toEqual([ - // Begin update phase - 'OuterErrorBoundary componentWillReceiveProps', - 'OuterErrorBoundary componentWillUpdate', - 'OuterErrorBoundary render success', - 'InnerUnmountBoundary componentWillReceiveProps', - 'InnerUnmountBoundary componentWillUpdate', - 'InnerUnmountBoundary render success', - 'InnerUpdateBoundary componentWillReceiveProps', - 'InnerUpdateBoundary componentWillUpdate', - 'InnerUpdateBoundary render success', - // First come the updates - 'BrokenComponentDidUpdate componentWillReceiveProps', - 'BrokenComponentDidUpdate componentWillUpdate', - 'BrokenComponentDidUpdate render', - 'BrokenComponentDidUpdate componentWillReceiveProps', - 'BrokenComponentDidUpdate componentWillUpdate', - 'BrokenComponentDidUpdate render', - // We're in commit phase now, deleting - 'BrokenComponentWillUnmount componentWillUnmount [!]', - 'BrokenComponentWillUnmount componentWillUnmount [!]', - // Continue despite errors, handle them after commit is done - 'InnerUnmountBoundary componentDidUpdate', - // We're still in commit phase, now calling update lifecycles - 'BrokenComponentDidUpdate componentDidUpdate [!]', - // Again, continue despite errors, we'll handle them later - 'BrokenComponentDidUpdate componentDidUpdate [!]', - 'InnerUpdateBoundary componentDidUpdate', - 'OuterErrorBoundary componentDidUpdate', - // After the commit phase, attempt to recover from any errors that - // were captured - 'BrokenComponentDidUpdate componentWillUnmount', - 'BrokenComponentDidUpdate componentWillUnmount', - 'InnerUnmountBoundary componentDidCatch', - 'InnerUpdateBoundary componentDidCatch', - 'InnerUnmountBoundary componentWillUpdate', - 'InnerUnmountBoundary render error', - 'InnerUpdateBoundary componentWillUpdate', - 'InnerUpdateBoundary render error', - 'InnerUnmountBoundary componentDidUpdate', - 'InnerUpdateBoundary componentDidUpdate', - ]); - - log.length = 0; - ReactDOM.unmountComponentAtNode(container); - expect(log).toEqual([ - 'OuterErrorBoundary componentWillUnmount', - 'InnerUnmountBoundary componentWillUnmount', - 'InnerUpdateBoundary componentWillUnmount', - ]); - }); - - it('discards a bad root if the root component fails', () => { - spyOn(console, 'error'); - - const X = null; - const Y = undefined; - let err1; - let err2; - - try { - let container = document.createElement('div'); - ReactDOM.render(, container); - } catch (err) { - err1 = err; - } - try { - let container = document.createElement('div'); - ReactDOM.render(, container); - } catch (err) { - err2 = err; - } - - expect(err1.message).toMatch(/got: null/); - expect(err2.message).toMatch(/got: undefined/); - }); - - it('renders empty output if error boundary does not handle the error', () => { - var container = document.createElement('div'); - ReactDOM.render( -
- Sibling - - - -
, - container, - ); - expect(container.firstChild.textContent).toBe('Sibling'); - expect(log).toEqual([ - 'NoopErrorBoundary constructor', - 'NoopErrorBoundary componentWillMount', - 'NoopErrorBoundary render', - 'BrokenRender constructor', - 'BrokenRender componentWillMount', - 'BrokenRender render [!]', - // In Fiber, noop error boundaries render null - 'NoopErrorBoundary componentDidMount', - 'NoopErrorBoundary componentDidCatch', - // Nothing happens. - ]); - - log.length = 0; - ReactDOM.unmountComponentAtNode(container); - expect(log).toEqual(['NoopErrorBoundary componentWillUnmount']); - }); + var statefulInst; + var container = document.createElement('div'); + ReactDOM.render( + + (statefulInst = inst)} /> + , + container, + ); - it('passes first error when two errors happen in commit', () => { - const errors = []; - let caughtError; - class Parent extends React.Component { - render() { - return ; - } - componentDidMount() { - errors.push('parent sad'); - throw new Error('parent sad'); - } + log.length = 0; + expect(() => { + fail = true; + statefulInst.forceUpdate(); + }).not.toThrow(); + + expect(log).toEqual([ + 'Stateful render [!]', + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', + ]); + + log.length = 0; + ReactDOM.unmountComponentAtNode(container); + expect(log).toEqual(['ErrorBoundary componentWillUnmount']); + }); + + it('catches errors in componentDidMount', () => { + var container = document.createElement('div'); + ReactDOM.render( + + + + + + + , + container, + ); + expect(log).toEqual([ + 'ErrorBoundary constructor', + 'ErrorBoundary componentWillMount', + 'ErrorBoundary render success', + 'BrokenComponentWillUnmount constructor', + 'BrokenComponentWillUnmount componentWillMount', + 'BrokenComponentWillUnmount render', + 'Normal constructor', + 'Normal componentWillMount', + 'Normal render', + 'BrokenComponentDidMount constructor', + 'BrokenComponentDidMount componentWillMount', + 'BrokenComponentDidMount render', + 'LastChild constructor', + 'LastChild componentWillMount', + 'LastChild render', + // Start flushing didMount queue + 'Normal componentDidMount', + 'BrokenComponentWillUnmount componentDidMount', + 'BrokenComponentDidMount componentDidMount [!]', + // Continue despite the error + 'LastChild componentDidMount', + 'ErrorBoundary componentDidMount', + // Now we are ready to handle the error + // Safely unmount every child + 'BrokenComponentWillUnmount componentWillUnmount [!]', + // Continue unmounting safely despite any errors + 'Normal componentWillUnmount', + 'BrokenComponentDidMount componentWillUnmount', + 'LastChild componentWillUnmount', + // Handle the error + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + // The update has finished + 'ErrorBoundary componentDidUpdate', + ]); + + log.length = 0; + ReactDOM.unmountComponentAtNode(container); + expect(log).toEqual(['ErrorBoundary componentWillUnmount']); + }); + + it('catches errors in componentDidUpdate', () => { + var container = document.createElement('div'); + ReactDOM.render( + + + , + container, + ); + + log.length = 0; + ReactDOM.render( + + + , + container, + ); + expect(log).toEqual([ + 'ErrorBoundary componentWillReceiveProps', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render success', + 'BrokenComponentDidUpdate componentWillReceiveProps', + 'BrokenComponentDidUpdate componentWillUpdate', + 'BrokenComponentDidUpdate render', + // All lifecycles run + 'BrokenComponentDidUpdate componentDidUpdate [!]', + 'ErrorBoundary componentDidUpdate', + 'BrokenComponentDidUpdate componentWillUnmount', + // Then, error is handled + 'ErrorBoundary componentDidCatch', + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', + ]); + + log.length = 0; + ReactDOM.unmountComponentAtNode(container); + expect(log).toEqual(['ErrorBoundary componentWillUnmount']); + }); + + it('propagates errors inside boundary during componentDidMount', () => { + var container = document.createElement('div'); + ReactDOM.render( + + ( +
+ We should never catch our own error: {error.message}. +
+ )} + /> +
, + container, + ); + expect(container.firstChild.textContent).toBe('Caught an error: Hello.'); + expect(log).toEqual([ + 'ErrorBoundary constructor', + 'ErrorBoundary componentWillMount', + 'ErrorBoundary render success', + 'BrokenComponentDidMountErrorBoundary constructor', + 'BrokenComponentDidMountErrorBoundary componentWillMount', + 'BrokenComponentDidMountErrorBoundary render success', + 'BrokenComponentDidMountErrorBoundary componentDidMount [!]', + // Fiber proceeds with the hooks + 'ErrorBoundary componentDidMount', + 'BrokenComponentDidMountErrorBoundary componentWillUnmount', + // The error propagates to the higher boundary + 'ErrorBoundary componentDidCatch', + // Fiber retries from the root + 'ErrorBoundary componentWillUpdate', + 'ErrorBoundary render error', + 'ErrorBoundary componentDidUpdate', + ]); + + log.length = 0; + ReactDOM.unmountComponentAtNode(container); + expect(log).toEqual(['ErrorBoundary componentWillUnmount']); + }); + + it('lets different boundaries catch their own first errors', () => { + function renderUnmountError(error) { + return
Caught an unmounting error: {error.message}.
; + } + function renderUpdateError(error) { + return
Caught an updating error: {error.message}.
; + } + + var container = document.createElement('div'); + ReactDOM.render( + + + + + + + + + + , + container, + ); + + log.length = 0; + ReactDOM.render( + + + + + + + , + container, + ); + + expect(container.firstChild.textContent).toBe( + 'Caught an unmounting error: E1.' + 'Caught an updating error: E3.', + ); + expect(log).toEqual([ + // Begin update phase + 'OuterErrorBoundary componentWillReceiveProps', + 'OuterErrorBoundary componentWillUpdate', + 'OuterErrorBoundary render success', + 'InnerUnmountBoundary componentWillReceiveProps', + 'InnerUnmountBoundary componentWillUpdate', + 'InnerUnmountBoundary render success', + 'InnerUpdateBoundary componentWillReceiveProps', + 'InnerUpdateBoundary componentWillUpdate', + 'InnerUpdateBoundary render success', + // First come the updates + 'BrokenComponentDidUpdate componentWillReceiveProps', + 'BrokenComponentDidUpdate componentWillUpdate', + 'BrokenComponentDidUpdate render', + 'BrokenComponentDidUpdate componentWillReceiveProps', + 'BrokenComponentDidUpdate componentWillUpdate', + 'BrokenComponentDidUpdate render', + // We're in commit phase now, deleting + 'BrokenComponentWillUnmount componentWillUnmount [!]', + 'BrokenComponentWillUnmount componentWillUnmount [!]', + // Continue despite errors, handle them after commit is done + 'InnerUnmountBoundary componentDidUpdate', + // We're still in commit phase, now calling update lifecycles + 'BrokenComponentDidUpdate componentDidUpdate [!]', + // Again, continue despite errors, we'll handle them later + 'BrokenComponentDidUpdate componentDidUpdate [!]', + 'InnerUpdateBoundary componentDidUpdate', + 'OuterErrorBoundary componentDidUpdate', + // After the commit phase, attempt to recover from any errors that + // were captured + 'BrokenComponentDidUpdate componentWillUnmount', + 'BrokenComponentDidUpdate componentWillUnmount', + 'InnerUnmountBoundary componentDidCatch', + 'InnerUpdateBoundary componentDidCatch', + 'InnerUnmountBoundary componentWillUpdate', + 'InnerUnmountBoundary render error', + 'InnerUpdateBoundary componentWillUpdate', + 'InnerUpdateBoundary render error', + 'InnerUnmountBoundary componentDidUpdate', + 'InnerUpdateBoundary componentDidUpdate', + ]); + + log.length = 0; + ReactDOM.unmountComponentAtNode(container); + expect(log).toEqual([ + 'OuterErrorBoundary componentWillUnmount', + 'InnerUnmountBoundary componentWillUnmount', + 'InnerUpdateBoundary componentWillUnmount', + ]); + }); + + it('discards a bad root if the root component fails', () => { + spyOn(console, 'error'); + + const X = null; + const Y = undefined; + let err1; + let err2; + + try { + let container = document.createElement('div'); + ReactDOM.render(, container); + } catch (err) { + err1 = err; + } + try { + let container = document.createElement('div'); + ReactDOM.render(, container); + } catch (err) { + err2 = err; + } + + expect(err1.message).toMatch(/got: null/); + expect(err2.message).toMatch(/got: undefined/); + }); + + it('renders empty output if error boundary does not handle the error', () => { + var container = document.createElement('div'); + ReactDOM.render( +
+ Sibling + + + +
, + container, + ); + expect(container.firstChild.textContent).toBe('Sibling'); + expect(log).toEqual([ + 'NoopErrorBoundary constructor', + 'NoopErrorBoundary componentWillMount', + 'NoopErrorBoundary render', + 'BrokenRender constructor', + 'BrokenRender componentWillMount', + 'BrokenRender render [!]', + // In Fiber, noop error boundaries render null + 'NoopErrorBoundary componentDidMount', + 'NoopErrorBoundary componentDidCatch', + // Nothing happens. + ]); + + log.length = 0; + ReactDOM.unmountComponentAtNode(container); + expect(log).toEqual(['NoopErrorBoundary componentWillUnmount']); + }); + + it('passes first error when two errors happen in commit', () => { + const errors = []; + let caughtError; + class Parent extends React.Component { + render() { + return ; } - class Child extends React.Component { - render() { - return
; - } - componentDidMount() { - errors.push('child sad'); - throw new Error('child sad'); - } + componentDidMount() { + errors.push('parent sad'); + throw new Error('parent sad'); } - - var container = document.createElement('div'); - try { - // Here, we test the behavior where there is no error boundary and we - // delegate to the host root. - ReactDOM.render(, container); - } catch (e) { - if (e.message !== 'parent sad' && e.message !== 'child sad') { - throw e; - } - caughtError = e; + } + class Child extends React.Component { + render() { + return
; + } + componentDidMount() { + errors.push('child sad'); + throw new Error('child sad'); } + } - expect(errors).toEqual(['child sad', 'parent sad']); - // Error should be the first thrown - expect(caughtError.message).toBe('child sad'); - }); - } + var container = document.createElement('div'); + try { + // Here, we test the behavior where there is no error boundary and we + // delegate to the host root. + ReactDOM.render(, container); + } catch (e) { + if (e.message !== 'parent sad' && e.message !== 'child sad') { + throw e; + } + caughtError = e; + } + + expect(errors).toEqual(['child sad', 'parent sad']); + // Error should be the first thrown + expect(caughtError.message).toBe('child sad'); + }); }); diff --git a/src/renderers/__tests__/ReactHostOperationHistoryHook-test.js b/src/renderers/__tests__/ReactHostOperationHistoryHook-test.js index 9eb0d4b6ea3..891144b92a8 100644 --- a/src/renderers/__tests__/ReactHostOperationHistoryHook-test.js +++ b/src/renderers/__tests__/ReactHostOperationHistoryHook-test.js @@ -9,12 +9,9 @@ 'use strict'; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var describeStack = ReactDOMFeatureFlags.useFiber ? describe.skip : describe; - // This is only used by ReactPerf which is currently not supported on Fiber. // Use browser timeline integration instead. -describeStack('ReactHostOperationHistoryHook', () => { +describe.skip('ReactHostOperationHistoryHook', () => { var React; var ReactPerf; var ReactDOM; diff --git a/src/renderers/__tests__/ReactMultiChild-test.js b/src/renderers/__tests__/ReactMultiChild-test.js index 4217a63561c..960e513dcfe 100644 --- a/src/renderers/__tests__/ReactMultiChild-test.js +++ b/src/renderers/__tests__/ReactMultiChild-test.js @@ -16,13 +16,11 @@ describe('ReactMultiChild', () => { var React; var ReactDOM; - var ReactDOMFeatureFlags; beforeEach(() => { jest.resetModules(); React = require('react'); ReactDOM = require('react-dom'); - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); }); describe('reconciliation', () => { @@ -280,8 +278,7 @@ describe('ReactMultiChild', () => { 'Warning: Using Maps as children is unsupported and will likely yield ' + 'unexpected results. Convert it to a sequence/iterable of keyed ' + 'ReactElements instead.\n' + - // Fiber gives a slightly better stack with the nearest host components - (ReactDOMFeatureFlags.useFiber ? ' in div (at **)\n' : '') + + ' in div (at **)\n' + ' in Parent (at **)', ); }); @@ -368,23 +365,12 @@ describe('ReactMultiChild', () => { 'oneA componentDidMount', 'twoA componentDidMount', - ...(ReactDOMFeatureFlags.useFiber - ? [ - 'oneB componentWillMount', - 'oneB render', - 'twoB componentWillMount', - 'twoB render', - 'oneA componentWillUnmount', - 'twoA componentWillUnmount', - ] - : [ - 'oneB componentWillMount', - 'oneB render', - 'oneA componentWillUnmount', - 'twoB componentWillMount', - 'twoB render', - 'twoA componentWillUnmount', - ]), + 'oneB componentWillMount', + 'oneB render', + 'twoB componentWillMount', + 'twoB render', + 'oneA componentWillUnmount', + 'twoA componentWillUnmount', 'oneB componentDidMount', 'twoB componentDidMount', diff --git a/src/renderers/__tests__/ReactMultiChildText-test.js b/src/renderers/__tests__/ReactMultiChildText-test.js index 850c16bb140..2d35886b472 100644 --- a/src/renderers/__tests__/ReactMultiChildText-test.js +++ b/src/renderers/__tests__/ReactMultiChildText-test.js @@ -11,7 +11,6 @@ var React = require('react'); var ReactDOM = require('react-dom'); -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var ReactTestUtils = require('react-dom/test-utils'); // Helpers @@ -48,43 +47,16 @@ var expectChildren = function(container, children) { expect(textNode.data).toBe('' + children); } } else { - var openingCommentNode; - var closingCommentNode; var mountIndex = 0; for (var i = 0; i < children.length; i++) { var child = children[i]; if (typeof child === 'string') { - if (ReactDOMFeatureFlags.useFiber) { - textNode = outerNode.childNodes[mountIndex]; - expect(textNode.nodeType).toBe(3); - expect(textNode.data).toBe('' + child); - mountIndex++; - } else { - openingCommentNode = outerNode.childNodes[mountIndex]; - - expect(openingCommentNode.nodeType).toBe(8); - expect(openingCommentNode.nodeValue).toMatch(/ react-text: [0-9]+ /); - - if (child === '') { - textNode = null; - closingCommentNode = openingCommentNode.nextSibling; - mountIndex += 2; - } else { - textNode = openingCommentNode.nextSibling; - closingCommentNode = textNode.nextSibling; - mountIndex += 3; - } - - if (textNode) { - expect(textNode.nodeType).toBe(3); - expect(textNode.data).toBe('' + child); - } - - expect(closingCommentNode.nodeType).toBe(8); - expect(closingCommentNode.nodeValue).toBe(' /react-text '); - } + textNode = outerNode.childNodes[mountIndex]; + expect(textNode.nodeType).toBe(3); + expect(textNode.data).toBe('' + child); + mountIndex++; } else { var elementDOMNode = outerNode.childNodes[mountIndex]; expect(elementDOMNode.tagName).toBe('DIV'); @@ -189,20 +161,14 @@ describe('ReactMultiChildText', () => { [true,
{1.2}{''}{
}{'foo'}
, true, 1.2], [
, '1.2'], ['', 'foo',
{true}{
}{1.2}{''}
, 'foo'], ['', 'foo',
, 'foo'], ]); - if (ReactDOMFeatureFlags.useFiber) { - expectDev(console.error.calls.count()).toBe(2); - expectDev(console.error.calls.argsFor(0)[0]).toContain( - 'Warning: Each child in an array or iterator should have a unique "key" prop.', - ); - expectDev(console.error.calls.argsFor(1)[0]).toContain( - 'Warning: Each child in an array or iterator should have a unique "key" prop.', - ); - } else { - expectDev(console.error.calls.count()).toBe(1); - expectDev(console.error.calls.argsFor(0)[0]).toContain( - 'Warning: Each child in an array or iterator should have a unique "key" prop.', - ); - } + + expectDev(console.error.calls.count()).toBe(2); + expectDev(console.error.calls.argsFor(0)[0]).toContain( + 'Warning: Each child in an array or iterator should have a unique "key" prop.', + ); + expectDev(console.error.calls.argsFor(1)[0]).toContain( + 'Warning: Each child in an array or iterator should have a unique "key" prop.', + ); }); it('should throw if rendering both HTML and children', () => { diff --git a/src/renderers/__tests__/ReactPerf-test.js b/src/renderers/__tests__/ReactPerf-test.js index ac222c0e19e..b09519a1982 100644 --- a/src/renderers/__tests__/ReactPerf-test.js +++ b/src/renderers/__tests__/ReactPerf-test.js @@ -9,12 +9,9 @@ 'use strict'; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); -var describeStack = ReactDOMFeatureFlags.useFiber ? describe.skip : describe; - // ReactPerf is currently not supported on Fiber. // Use browser timeline integration instead. -describeStack('ReactPerf', () => { +describe.skip('ReactPerf', () => { var React; var ReactDOM; var ReactPerf; diff --git a/src/renderers/__tests__/ReactStatelessComponent-test.js b/src/renderers/__tests__/ReactStatelessComponent-test.js index 92811c618f9..c8d67c7bbfc 100644 --- a/src/renderers/__tests__/ReactStatelessComponent-test.js +++ b/src/renderers/__tests__/ReactStatelessComponent-test.js @@ -14,8 +14,6 @@ var React; var ReactDOM; var ReactTestUtils; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); - function StatelessComponent(props) { return
{props.name}
; } @@ -118,54 +116,22 @@ describe('ReactStatelessComponent', () => { ReactDOM.render(, container); - // Stack and Fiber differ in terms of they show warnings - if (ReactDOMFeatureFlags.useFiber) { - expectDev(console.error.calls.count()).toBe(1); - expectDev(console.error.calls.argsFor(0)[0]).toContain( - 'StatelessComponentWithChildContext(...): childContextTypes cannot ' + - 'be defined on a functional component.', - ); - } else { - expectDev(console.error.calls.count()).toBe(2); - expectDev(console.error.calls.argsFor(0)[0]).toContain( - 'StatelessComponentWithChildContext(...): childContextTypes cannot ' + - 'be defined on a functional component.', - ); - expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe( - 'Warning: StatelessComponentWithChildContext.childContextTypes is specified ' + - 'but there is no getChildContext() method on the instance. You can either ' + - 'define getChildContext() on StatelessComponentWithChildContext or remove ' + - 'childContextTypes from it.', - ); - } + expectDev(console.error.calls.count()).toBe(1); + expectDev(console.error.calls.argsFor(0)[0]).toContain( + 'StatelessComponentWithChildContext(...): childContextTypes cannot ' + + 'be defined on a functional component.', + ); }); - if (!ReactDOMFeatureFlags.useFiber) { - // Stack doesn't support fragments - it('should throw when stateless component returns array', () => { - function NotAComponent() { - return [
,
]; - } - expect(function() { - ReactTestUtils.renderIntoDocument(
); - }).toThrowError( - 'NotAComponent(...): A valid React element (or null) must be returned. ' + - 'You may have returned undefined, an array or some other invalid object.', - ); - }); - } - - if (ReactDOMFeatureFlags.useFiber) { - it('should throw when stateless component returns undefined', () => { - function NotAComponent() {} - expect(function() { - ReactTestUtils.renderIntoDocument(
); - }).toThrowError( - 'NotAComponent(...): Nothing was returned from render. ' + - 'This usually means a return statement is missing. Or, to render nothing, return null.', - ); - }); - } + it('should throw when stateless component returns undefined', () => { + function NotAComponent() {} + expect(function() { + ReactTestUtils.renderIntoDocument(
); + }).toThrowError( + 'NotAComponent(...): Nothing was returned from render. ' + + 'This usually means a return statement is missing. Or, to render nothing, return null.', + ); + }); it('should throw on string refs in pure functions', () => { function Child() { diff --git a/src/renderers/__tests__/ReactUpdates-test.js b/src/renderers/__tests__/ReactUpdates-test.js index 0124b20e049..fdfb92c5327 100644 --- a/src/renderers/__tests__/ReactUpdates-test.js +++ b/src/renderers/__tests__/ReactUpdates-test.js @@ -12,13 +12,11 @@ var React; var ReactDOM; var ReactTestUtils; -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); describe('ReactUpdates', () => { beforeEach(() => { React = require('react'); ReactDOM = require('react-dom'); - ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); ReactTestUtils = require('react-dom/test-utils'); }); @@ -519,9 +517,7 @@ describe('ReactUpdates', () => { render() { var portal = null; // If we're using Fiber, we use Portals instead to achieve this. - if (ReactDOMFeatureFlags.useFiber) { - portal = ReactDOM.createPortal( (b = n)} />, bContainer); - } + portal = ReactDOM.createPortal( (b = n)} />, bContainer); return
A{this.state.x}{portal}
; } } @@ -535,10 +531,6 @@ describe('ReactUpdates', () => { } a = ReactTestUtils.renderIntoDocument(); - if (!ReactDOMFeatureFlags.useFiber) { - ReactDOM.render( (b = n)} />, bContainer); - } - ReactDOM.unstable_batchedUpdates(function() { a.setState({x: 1}); b.setState({x: 1}); @@ -1178,35 +1170,33 @@ describe('ReactUpdates', () => { }).toThrow('Maximum'); }); - if (ReactDOMFeatureFlags.useFiber) { - it('does not fall into an infinite error loop', () => { - function BadRender() { - throw new Error('error'); - } + it('does not fall into an infinite error loop', () => { + function BadRender() { + throw new Error('error'); + } - class ErrorBoundary extends React.Component { - componentDidCatch() { - this.props.parent.remount(); - } - render() { - return ; - } + class ErrorBoundary extends React.Component { + componentDidCatch() { + this.props.parent.remount(); + } + render() { + return ; } + } - class NonTerminating extends React.Component { - state = {step: 0}; - remount() { - this.setState(state => ({step: state.step + 1})); - } - render() { - return ; - } + class NonTerminating extends React.Component { + state = {step: 0}; + remount() { + this.setState(state => ({step: state.step + 1})); + } + render() { + return ; } + } - const container = document.createElement('div'); - expect(() => { - ReactDOM.render(, container); - }).toThrow('Maximum'); - }); - } + const container = document.createElement('div'); + expect(() => { + ReactDOM.render(, container); + }).toThrow('Maximum'); + }); }); diff --git a/src/renderers/__tests__/multiple-copies-of-react-test.js b/src/renderers/__tests__/multiple-copies-of-react-test.js index 0cd96adf0fd..9d4d30888ad 100644 --- a/src/renderers/__tests__/multiple-copies-of-react-test.js +++ b/src/renderers/__tests__/multiple-copies-of-react-test.js @@ -10,7 +10,6 @@ 'use strict'; let React = require('react'); -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var ReactTestUtils = require('react-dom/test-utils'); class TextWithStringRef extends React.Component { @@ -27,23 +26,12 @@ class TextWithStringRef extends React.Component { describe('when different React version is used with string ref', () => { it('throws the "Refs must have owner" warning', () => { - if (ReactDOMFeatureFlags.useFiber) { - expect(() => { - ReactTestUtils.renderIntoDocument(); - }).toThrow( - 'Element ref was specified as a string (foo) but no owner was set.' + - ' You may have multiple copies of React loaded. (details: ' + - 'https://fb.me/react-refs-must-have-owner).', - ); - } else { - expect(() => { - ReactTestUtils.renderIntoDocument(); - }).toThrow( - 'Only a ReactOwner can have refs. You might be adding a ref to a ' + - "component that was not created inside a component's `render` " + - 'method, or you have multiple copies of React loaded ' + - '(details: https://fb.me/react-refs-must-have-owner)', - ); - } + expect(() => { + ReactTestUtils.renderIntoDocument(); + }).toThrow( + 'Element ref was specified as a string (foo) but no owner was set.' + + ' You may have multiple copies of React loaded. (details: ' + + 'https://fb.me/react-refs-must-have-owner).', + ); }); }); diff --git a/src/renderers/__tests__/refs-test.js b/src/renderers/__tests__/refs-test.js index 424b9667e8d..314e5075ca1 100644 --- a/src/renderers/__tests__/refs-test.js +++ b/src/renderers/__tests__/refs-test.js @@ -10,7 +10,6 @@ 'use strict'; var React = require('react'); -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var ReactTestUtils = require('react-dom/test-utils'); /** @@ -348,40 +347,38 @@ describe('root level refs', () => { expect(ref).toHaveBeenCalledTimes(2); expect(ref.mock.calls[1][0]).toBe(null); - if (ReactDOMFeatureFlags.useFiber) { - // fragment - inst = null; - ref = jest.fn(value => (inst = value)); - var divInst = null; - var ref2 = jest.fn(value => (divInst = value)); - result = ReactDOM.render( - [, 5,
Hello
], - container, - ); + // fragment + inst = null; + ref = jest.fn(value => (inst = value)); + var divInst = null; + var ref2 = jest.fn(value => (divInst = value)); + result = ReactDOM.render( + [, 5,
Hello
], + container, + ); - // first call should be `Comp` - expect(ref).toHaveBeenCalledTimes(1); - expect(ref.mock.calls[0][0]).toBeInstanceOf(Comp); - expect(result).toBe(ref.mock.calls[0][0]); + // first call should be `Comp` + expect(ref).toHaveBeenCalledTimes(1); + expect(ref.mock.calls[0][0]).toBeInstanceOf(Comp); + expect(result).toBe(ref.mock.calls[0][0]); - expect(ref2).toHaveBeenCalledTimes(1); - expect(divInst).toBeInstanceOf(HTMLDivElement); - expect(result).not.toBe(divInst); + expect(ref2).toHaveBeenCalledTimes(1); + expect(divInst).toBeInstanceOf(HTMLDivElement); + expect(result).not.toBe(divInst); - ReactDOM.unmountComponentAtNode(container); - expect(ref).toHaveBeenCalledTimes(2); - expect(ref.mock.calls[1][0]).toBe(null); - expect(ref2).toHaveBeenCalledTimes(2); - expect(ref2.mock.calls[1][0]).toBe(null); + ReactDOM.unmountComponentAtNode(container); + expect(ref).toHaveBeenCalledTimes(2); + expect(ref.mock.calls[1][0]).toBe(null); + expect(ref2).toHaveBeenCalledTimes(2); + expect(ref2.mock.calls[1][0]).toBe(null); - // null - result = ReactDOM.render(null, container); - expect(result).toBe(null); + // null + result = ReactDOM.render(null, container); + expect(result).toBe(null); - // primitives - result = ReactDOM.render(5, container); - expect(result).toBeInstanceOf(Text); - } + // primitives + result = ReactDOM.render(5, container); + expect(result).toBeInstanceOf(Text); }); }); @@ -397,28 +394,6 @@ describe('creating element with ref in constructor', () => { } } - var devErrorMessage = - 'addComponentAsRefTo(...): Only a ReactOwner can have refs. You might ' + - "be adding a ref to a component that was not created inside a component's " + - '`render` method, or you have multiple copies of React loaded ' + - '(details: https://fb.me/react-refs-must-have-owner).'; - - var prodErrorMessage = - 'Minified React error #119; visit ' + - 'http://facebook.github.io/react/docs/error-decoder.html?invariant=119 for the full message ' + - 'or use the non-minified dev environment for full errors and additional helpful warnings.'; - - var fiberDevErrorMessage = - 'Element ref was specified as a string (p) but no owner was ' + - 'set. You may have multiple copies of React loaded. ' + - '(details: https://fb.me/react-refs-must-have-owner).'; - - var fiberProdErrorMessage = - 'Minified React error #149; visit ' + - 'http://facebook.github.io/react/docs/error-decoder.html?invariant=149&args[]=p ' + - 'for the full message or use the non-minified dev environment for full errors and additional ' + - 'helpful warnings.'; - it('throws an error when __DEV__ = true', () => { ReactTestUtils = require('react-dom/test-utils'); @@ -429,7 +404,9 @@ describe('creating element with ref in constructor', () => { expect(function() { ReactTestUtils.renderIntoDocument(); }).toThrowError( - ReactDOMFeatureFlags.useFiber ? fiberDevErrorMessage : devErrorMessage, + 'Element ref was specified as a string (p) but no owner was ' + + 'set. You may have multiple copies of React loaded. ' + + '(details: https://fb.me/react-refs-must-have-owner).', ); } finally { __DEV__ = originalDev; @@ -446,9 +423,10 @@ describe('creating element with ref in constructor', () => { expect(function() { ReactTestUtils.renderIntoDocument(); }).toThrowError( - ReactDOMFeatureFlags.useFiber - ? fiberProdErrorMessage - : prodErrorMessage, + 'Minified React error #149; visit ' + + 'http://facebook.github.io/react/docs/error-decoder.html?invariant=149&args[]=p ' + + 'for the full message or use the non-minified dev environment for full errors and additional ' + + 'helpful warnings.', ); } finally { __DEV__ = originalDev; diff --git a/src/renderers/dom/__tests__/ReactDOMProduction-test.js b/src/renderers/dom/__tests__/ReactDOMProduction-test.js index 74470dc1442..570a59a7ebf 100644 --- a/src/renderers/dom/__tests__/ReactDOMProduction-test.js +++ b/src/renderers/dom/__tests__/ReactDOMProduction-test.js @@ -9,8 +9,6 @@ 'use strict'; describe('ReactDOMProduction', () => { - var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); - var React; var ReactDOM; var ReactDOMServer; @@ -195,7 +193,7 @@ describe('ReactDOMProduction', () => { }); it('should throw with an error code in production', () => { - const errorCode = ReactDOMFeatureFlags.useFiber ? 152 : 109; + const errorCode = 152; expect(function() { class Component extends React.Component { render() { @@ -235,57 +233,55 @@ describe('ReactDOMProduction', () => { } }); - if (ReactDOMFeatureFlags.useFiber) { - // This test is originally from ReactDOMFiber-test but we replicate it here - // to avoid production-only regressions because of host context differences - // in dev and prod. - it('should keep track of namespace across portals in production', () => { - var svgEls, htmlEls; - var expectSVG = {ref: el => svgEls.push(el)}; - var expectHTML = {ref: el => htmlEls.push(el)}; - var usePortal = function(tree) { - return ReactDOM.createPortal(tree, document.createElement('div')); - }; - var assertNamespacesMatch = function(tree) { - var container = document.createElement('div'); - svgEls = []; - htmlEls = []; - ReactDOM.render(tree, container); - svgEls.forEach(el => { - expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg'); - }); - htmlEls.forEach(el => { - expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml'); - }); - ReactDOM.unmountComponentAtNode(container); - expect(container.innerHTML).toBe(''); - }; + // This test is originally from ReactDOMFiber-test but we replicate it here + // to avoid production-only regressions because of host context differences + // in dev and prod. + it('should keep track of namespace across portals in production', () => { + var svgEls, htmlEls; + var expectSVG = {ref: el => svgEls.push(el)}; + var expectHTML = {ref: el => htmlEls.push(el)}; + var usePortal = function(tree) { + return ReactDOM.createPortal(tree, document.createElement('div')); + }; + var assertNamespacesMatch = function(tree) { + var container = document.createElement('div'); + svgEls = []; + htmlEls = []; + ReactDOM.render(tree, container); + svgEls.forEach(el => { + expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg'); + }); + htmlEls.forEach(el => { + expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml'); + }); + ReactDOM.unmountComponentAtNode(container); + expect(container.innerHTML).toBe(''); + }; - assertNamespacesMatch( -
- - -

- {usePortal( + assertNamespacesMatch( +

+ + +

+ {usePortal( + + - - - -

- - {usePortal(

)} - - - , - )} -

- - - -

- , - ); - }); - } + +

+ + {usePortal(

)} + + + , + )} +

+ + + +

+ , + ); + }); }); diff --git a/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js b/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js index 5e2f5f63c2f..b5424c268f7 100644 --- a/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js +++ b/src/renderers/dom/fiber/__tests__/ReactDOMFiber-test.js @@ -11,7 +11,6 @@ var React = require('react'); var ReactDOM = require('react-dom'); -var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags'); var ReactTestUtils = require('react-dom/test-utils'); var PropTypes = require('prop-types'); @@ -74,1076 +73,1072 @@ describe('ReactDOMFiber', () => { expect(called).toEqual(true); }); - if (ReactDOMFeatureFlags.useFiber) { - it('should render a component returning strings directly from render', () => { - const Text = ({value}) => value; + it('should render a component returning strings directly from render', () => { + const Text = ({value}) => value; - ReactDOM.render(, container); - expect(container.textContent).toEqual('foo'); - }); + ReactDOM.render(, container); + expect(container.textContent).toEqual('foo'); + }); - it('should render a component returning numbers directly from render', () => { - const Text = ({value}) => value; + it('should render a component returning numbers directly from render', () => { + const Text = ({value}) => value; - ReactDOM.render(, container); + ReactDOM.render(, container); - expect(container.textContent).toEqual('10'); - }); + expect(container.textContent).toEqual('10'); + }); - it('finds the DOM Text node of a string child', () => { - class Text extends React.Component { - render() { - return this.props.value; - } + it('finds the DOM Text node of a string child', () => { + class Text extends React.Component { + render() { + return this.props.value; } + } - let instance = null; - ReactDOM.render( - (instance = ref)} />, - container, - ); + let instance = null; + ReactDOM.render( + (instance = ref)} />, + container, + ); - const textNode = ReactDOM.findDOMNode(instance); - expect(textNode).toBe(container.firstChild); - expect(textNode.nodeType).toBe(3); - expect(textNode.nodeValue).toBe('foo'); - }); + const textNode = ReactDOM.findDOMNode(instance); + expect(textNode).toBe(container.firstChild); + expect(textNode.nodeType).toBe(3); + expect(textNode.nodeValue).toBe('foo'); + }); - it('finds the first child when a component returns a fragment', () => { - class Fragment extends React.Component { - render() { - return [

, ]; - } + it('finds the first child when a component returns a fragment', () => { + class Fragment extends React.Component { + render() { + return [
, ]; } + } - let instance = null; - ReactDOM.render( (instance = ref)} />, container); + let instance = null; + ReactDOM.render( (instance = ref)} />, container); - expect(container.childNodes.length).toBe(2); + expect(container.childNodes.length).toBe(2); - const firstNode = ReactDOM.findDOMNode(instance); - expect(firstNode).toBe(container.firstChild); - expect(firstNode.tagName).toBe('DIV'); - }); + const firstNode = ReactDOM.findDOMNode(instance); + expect(firstNode).toBe(container.firstChild); + expect(firstNode.tagName).toBe('DIV'); + }); - it('finds the first child even when fragment is nested', () => { - class Wrapper extends React.Component { - render() { - return this.props.children; - } + it('finds the first child even when fragment is nested', () => { + class Wrapper extends React.Component { + render() { + return this.props.children; } + } - class Fragment extends React.Component { - render() { - return [
, ]; - } + class Fragment extends React.Component { + render() { + return [
, ]; } + } - let instance = null; - ReactDOM.render( (instance = ref)} />, container); + let instance = null; + ReactDOM.render( (instance = ref)} />, container); - expect(container.childNodes.length).toBe(2); + expect(container.childNodes.length).toBe(2); - const firstNode = ReactDOM.findDOMNode(instance); - expect(firstNode).toBe(container.firstChild); - expect(firstNode.tagName).toBe('DIV'); - }); + const firstNode = ReactDOM.findDOMNode(instance); + expect(firstNode).toBe(container.firstChild); + expect(firstNode.tagName).toBe('DIV'); + }); - it('finds the first child even when first child renders null', () => { - class NullComponent extends React.Component { - render() { - return null; - } + it('finds the first child even when first child renders null', () => { + class NullComponent extends React.Component { + render() { + return null; } + } - class Fragment extends React.Component { - render() { - return [,
, ]; - } + class Fragment extends React.Component { + render() { + return [,
, ]; } + } - let instance = null; - ReactDOM.render( (instance = ref)} />, container); + let instance = null; + ReactDOM.render( (instance = ref)} />, container); - expect(container.childNodes.length).toBe(2); + expect(container.childNodes.length).toBe(2); - const firstNode = ReactDOM.findDOMNode(instance); - expect(firstNode).toBe(container.firstChild); - expect(firstNode.tagName).toBe('DIV'); - }); - } - - if (ReactDOMFeatureFlags.useFiber) { - var svgEls, htmlEls, mathEls; - var expectSVG = {ref: el => svgEls.push(el)}; - var expectHTML = {ref: el => htmlEls.push(el)}; - var expectMath = {ref: el => mathEls.push(el)}; - - var usePortal = function(tree) { - return ReactDOM.createPortal(tree, document.createElement('div')); - }; - - var assertNamespacesMatch = function(tree) { - container = document.createElement('div'); - svgEls = []; - htmlEls = []; - mathEls = []; - - ReactDOM.render(tree, container); - svgEls.forEach(el => { - expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg'); - }); - htmlEls.forEach(el => { - expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml'); - }); - mathEls.forEach(el => { - expect(el.namespaceURI).toBe('http://www.w3.org/1998/Math/MathML'); - }); - - ReactDOM.unmountComponentAtNode(container); - expect(container.innerHTML).toBe(''); - }; - - it('should render one portal', () => { - var portalContainer = document.createElement('div'); - - ReactDOM.render( -
- {ReactDOM.createPortal(
portal
, portalContainer)} -
, - container, - ); - expect(portalContainer.innerHTML).toBe('
portal
'); - expect(container.innerHTML).toBe('
'); + const firstNode = ReactDOM.findDOMNode(instance); + expect(firstNode).toBe(container.firstChild); + expect(firstNode.tagName).toBe('DIV'); + }); - ReactDOM.unmountComponentAtNode(container); - expect(portalContainer.innerHTML).toBe(''); - expect(container.innerHTML).toBe(''); - }); + var svgEls, htmlEls, mathEls; + var expectSVG = {ref: el => svgEls.push(el)}; + var expectHTML = {ref: el => htmlEls.push(el)}; + var expectMath = {ref: el => mathEls.push(el)}; - // TODO: remove in React 17 - it('should support unstable_createPortal alias', () => { - var portalContainer = document.createElement('div'); + var usePortal = function(tree) { + return ReactDOM.createPortal(tree, document.createElement('div')); + }; - ReactDOM.render( -
- {ReactDOM.unstable_createPortal(
portal
, portalContainer)} -
, - container, - ); - expect(portalContainer.innerHTML).toBe('
portal
'); - expect(container.innerHTML).toBe('
'); + var assertNamespacesMatch = function(tree) { + container = document.createElement('div'); + svgEls = []; + htmlEls = []; + mathEls = []; - ReactDOM.unmountComponentAtNode(container); - expect(portalContainer.innerHTML).toBe(''); - expect(container.innerHTML).toBe(''); + ReactDOM.render(tree, container); + svgEls.forEach(el => { + expect(el.namespaceURI).toBe('http://www.w3.org/2000/svg'); + }); + htmlEls.forEach(el => { + expect(el.namespaceURI).toBe('http://www.w3.org/1999/xhtml'); + }); + mathEls.forEach(el => { + expect(el.namespaceURI).toBe('http://www.w3.org/1998/Math/MathML'); }); - it('should render many portals', () => { - var portalContainer1 = document.createElement('div'); - var portalContainer2 = document.createElement('div'); - - var ops = []; - class Child extends React.Component { - componentDidMount() { - ops.push(`${this.props.name} componentDidMount`); - } - componentDidUpdate() { - ops.push(`${this.props.name} componentDidUpdate`); - } - componentWillUnmount() { - ops.push(`${this.props.name} componentWillUnmount`); - } - render() { - return
{this.props.name}
; - } - } + ReactDOM.unmountComponentAtNode(container); + expect(container.innerHTML).toBe(''); + }; + + it('should render one portal', () => { + var portalContainer = document.createElement('div'); + + ReactDOM.render( +
+ {ReactDOM.createPortal(
portal
, portalContainer)} +
, + container, + ); + expect(portalContainer.innerHTML).toBe('
portal
'); + expect(container.innerHTML).toBe('
'); + + ReactDOM.unmountComponentAtNode(container); + expect(portalContainer.innerHTML).toBe(''); + expect(container.innerHTML).toBe(''); + }); - class Parent extends React.Component { - componentDidMount() { - ops.push(`Parent:${this.props.step} componentDidMount`); - } - componentDidUpdate() { - ops.push(`Parent:${this.props.step} componentDidUpdate`); - } - componentWillUnmount() { - ops.push(`Parent:${this.props.step} componentWillUnmount`); - } - render() { - const {step} = this.props; - return [ - , - ReactDOM.createPortal( - , - portalContainer1, - ), - , - ReactDOM.createPortal( - [ - , - , - ], - portalContainer2, - ), - ]; - } - } + // TODO: remove in React 17 + it('should support unstable_createPortal alias', () => { + var portalContainer = document.createElement('div'); + + ReactDOM.render( +
+ {ReactDOM.unstable_createPortal(
portal
, portalContainer)} +
, + container, + ); + expect(portalContainer.innerHTML).toBe('
portal
'); + expect(container.innerHTML).toBe('
'); + + ReactDOM.unmountComponentAtNode(container); + expect(portalContainer.innerHTML).toBe(''); + expect(container.innerHTML).toBe(''); + }); - ReactDOM.render(, container); - expect(portalContainer1.innerHTML).toBe('
portal1[0]:a
'); - expect(portalContainer2.innerHTML).toBe( - '
portal2[0]:a
portal2[1]:a
', - ); - expect(container.innerHTML).toBe( - '
normal[0]:a
normal[1]:a
', - ); - expect(ops).toEqual([ - 'normal[0]:a componentDidMount', - 'portal1[0]:a componentDidMount', - 'normal[1]:a componentDidMount', - 'portal2[0]:a componentDidMount', - 'portal2[1]:a componentDidMount', - 'Parent:a componentDidMount', - ]); - - ops.length = 0; - ReactDOM.render(, container); - expect(portalContainer1.innerHTML).toBe('
portal1[0]:b
'); - expect(portalContainer2.innerHTML).toBe( - '
portal2[0]:b
portal2[1]:b
', - ); - expect(container.innerHTML).toBe( - '
normal[0]:b
normal[1]:b
', - ); - expect(ops).toEqual([ - 'normal[0]:b componentDidUpdate', - 'portal1[0]:b componentDidUpdate', - 'normal[1]:b componentDidUpdate', - 'portal2[0]:b componentDidUpdate', - 'portal2[1]:b componentDidUpdate', - 'Parent:b componentDidUpdate', - ]); - - ops.length = 0; - ReactDOM.unmountComponentAtNode(container); - expect(portalContainer1.innerHTML).toBe(''); - expect(portalContainer2.innerHTML).toBe(''); - expect(container.innerHTML).toBe(''); - expect(ops).toEqual([ - 'Parent:b componentWillUnmount', - 'normal[0]:b componentWillUnmount', - 'portal1[0]:b componentWillUnmount', - 'normal[1]:b componentWillUnmount', - 'portal2[0]:b componentWillUnmount', - 'portal2[1]:b componentWillUnmount', - ]); - }); + it('should render many portals', () => { + var portalContainer1 = document.createElement('div'); + var portalContainer2 = document.createElement('div'); - it('should render nested portals', () => { - var portalContainer1 = document.createElement('div'); - var portalContainer2 = document.createElement('div'); - var portalContainer3 = document.createElement('div'); + var ops = []; + class Child extends React.Component { + componentDidMount() { + ops.push(`${this.props.name} componentDidMount`); + } + componentDidUpdate() { + ops.push(`${this.props.name} componentDidUpdate`); + } + componentWillUnmount() { + ops.push(`${this.props.name} componentWillUnmount`); + } + render() { + return
{this.props.name}
; + } + } - ReactDOM.render( - [ -
normal[0]
, + class Parent extends React.Component { + componentDidMount() { + ops.push(`Parent:${this.props.step} componentDidMount`); + } + componentDidUpdate() { + ops.push(`Parent:${this.props.step} componentDidUpdate`); + } + componentWillUnmount() { + ops.push(`Parent:${this.props.step} componentWillUnmount`); + } + render() { + const {step} = this.props; + return [ + , + ReactDOM.createPortal( + , + portalContainer1, + ), + , ReactDOM.createPortal( [ -
portal1[0]
, - ReactDOM.createPortal( -
portal2[0]
, - portalContainer2, - ), - ReactDOM.createPortal( -
portal3[0]
, - portalContainer3, - ), -
portal1[1]
, + , + , ], - portalContainer1, + portalContainer2, ), -
normal[1]
, - ], - container, - ); - expect(portalContainer1.innerHTML).toBe( - '
portal1[0]
portal1[1]
', - ); - expect(portalContainer2.innerHTML).toBe('
portal2[0]
'); - expect(portalContainer3.innerHTML).toBe('
portal3[0]
'); - expect(container.innerHTML).toBe( - '
normal[0]
normal[1]
', - ); + ]; + } + } - ReactDOM.unmountComponentAtNode(container); - expect(portalContainer1.innerHTML).toBe(''); - expect(portalContainer2.innerHTML).toBe(''); - expect(portalContainer3.innerHTML).toBe(''); - expect(container.innerHTML).toBe(''); - }); + ReactDOM.render(, container); + expect(portalContainer1.innerHTML).toBe('
portal1[0]:a
'); + expect(portalContainer2.innerHTML).toBe( + '
portal2[0]:a
portal2[1]:a
', + ); + expect(container.innerHTML).toBe( + '
normal[0]:a
normal[1]:a
', + ); + expect(ops).toEqual([ + 'normal[0]:a componentDidMount', + 'portal1[0]:a componentDidMount', + 'normal[1]:a componentDidMount', + 'portal2[0]:a componentDidMount', + 'portal2[1]:a componentDidMount', + 'Parent:a componentDidMount', + ]); + + ops.length = 0; + ReactDOM.render(, container); + expect(portalContainer1.innerHTML).toBe('
portal1[0]:b
'); + expect(portalContainer2.innerHTML).toBe( + '
portal2[0]:b
portal2[1]:b
', + ); + expect(container.innerHTML).toBe( + '
normal[0]:b
normal[1]:b
', + ); + expect(ops).toEqual([ + 'normal[0]:b componentDidUpdate', + 'portal1[0]:b componentDidUpdate', + 'normal[1]:b componentDidUpdate', + 'portal2[0]:b componentDidUpdate', + 'portal2[1]:b componentDidUpdate', + 'Parent:b componentDidUpdate', + ]); + + ops.length = 0; + ReactDOM.unmountComponentAtNode(container); + expect(portalContainer1.innerHTML).toBe(''); + expect(portalContainer2.innerHTML).toBe(''); + expect(container.innerHTML).toBe(''); + expect(ops).toEqual([ + 'Parent:b componentWillUnmount', + 'normal[0]:b componentWillUnmount', + 'portal1[0]:b componentWillUnmount', + 'normal[1]:b componentWillUnmount', + 'portal2[0]:b componentWillUnmount', + 'portal2[1]:b componentWillUnmount', + ]); + }); - it('should reconcile portal children', () => { - var portalContainer = document.createElement('div'); + it('should render nested portals', () => { + var portalContainer1 = document.createElement('div'); + var portalContainer2 = document.createElement('div'); + var portalContainer3 = document.createElement('div'); + + ReactDOM.render( + [ +
normal[0]
, + ReactDOM.createPortal( + [ +
portal1[0]
, + ReactDOM.createPortal( +
portal2[0]
, + portalContainer2, + ), + ReactDOM.createPortal( +
portal3[0]
, + portalContainer3, + ), +
portal1[1]
, + ], + portalContainer1, + ), +
normal[1]
, + ], + container, + ); + expect(portalContainer1.innerHTML).toBe( + '
portal1[0]
portal1[1]
', + ); + expect(portalContainer2.innerHTML).toBe('
portal2[0]
'); + expect(portalContainer3.innerHTML).toBe('
portal3[0]
'); + expect(container.innerHTML).toBe( + '
normal[0]
normal[1]
', + ); + + ReactDOM.unmountComponentAtNode(container); + expect(portalContainer1.innerHTML).toBe(''); + expect(portalContainer2.innerHTML).toBe(''); + expect(portalContainer3.innerHTML).toBe(''); + expect(container.innerHTML).toBe(''); + }); - ReactDOM.render( -
- {ReactDOM.createPortal(
portal:1
, portalContainer)} -
, - container, - ); - expect(portalContainer.innerHTML).toBe('
portal:1
'); - expect(container.innerHTML).toBe('
'); - - ReactDOM.render( -
- {ReactDOM.createPortal(
portal:2
, portalContainer)} -
, - container, - ); - expect(portalContainer.innerHTML).toBe('
portal:2
'); - expect(container.innerHTML).toBe('
'); - - ReactDOM.render( -
- {ReactDOM.createPortal(

portal:3

, portalContainer)} -
, - container, - ); - expect(portalContainer.innerHTML).toBe('

portal:3

'); - expect(container.innerHTML).toBe('
'); - - ReactDOM.render( -
- {ReactDOM.createPortal(['Hi', 'Bye'], portalContainer)} -
, - container, - ); - expect(portalContainer.innerHTML).toBe('HiBye'); - expect(container.innerHTML).toBe('
'); - - ReactDOM.render( -
- {ReactDOM.createPortal(['Bye', 'Hi'], portalContainer)} -
, - container, - ); - expect(portalContainer.innerHTML).toBe('ByeHi'); - expect(container.innerHTML).toBe('
'); - - ReactDOM.render( -
- {ReactDOM.createPortal(null, portalContainer)} -
, - container, - ); - expect(portalContainer.innerHTML).toBe(''); - expect(container.innerHTML).toBe('
'); - }); + it('should reconcile portal children', () => { + var portalContainer = document.createElement('div'); + + ReactDOM.render( +
+ {ReactDOM.createPortal(
portal:1
, portalContainer)} +
, + container, + ); + expect(portalContainer.innerHTML).toBe('
portal:1
'); + expect(container.innerHTML).toBe('
'); + + ReactDOM.render( +
+ {ReactDOM.createPortal(
portal:2
, portalContainer)} +
, + container, + ); + expect(portalContainer.innerHTML).toBe('
portal:2
'); + expect(container.innerHTML).toBe('
'); + + ReactDOM.render( +
+ {ReactDOM.createPortal(

portal:3

, portalContainer)} +
, + container, + ); + expect(portalContainer.innerHTML).toBe('

portal:3

'); + expect(container.innerHTML).toBe('
'); + + ReactDOM.render( +
+ {ReactDOM.createPortal(['Hi', 'Bye'], portalContainer)} +
, + container, + ); + expect(portalContainer.innerHTML).toBe('HiBye'); + expect(container.innerHTML).toBe('
'); + + ReactDOM.render( +
+ {ReactDOM.createPortal(['Bye', 'Hi'], portalContainer)} +
, + container, + ); + expect(portalContainer.innerHTML).toBe('ByeHi'); + expect(container.innerHTML).toBe('
'); + + ReactDOM.render( +
+ {ReactDOM.createPortal(null, portalContainer)} +
, + container, + ); + expect(portalContainer.innerHTML).toBe(''); + expect(container.innerHTML).toBe('
'); + }); - it('should keep track of namespace across portals (simple)', () => { - assertNamespacesMatch( - - - {usePortal(
)} - - , - ); - assertNamespacesMatch( - - - {usePortal(
)} - - , - ); - assertNamespacesMatch( -
-

- {usePortal( - - - , - )} -

-

, - ); - }); + it('should keep track of namespace across portals (simple)', () => { + assertNamespacesMatch( + + + {usePortal(
)} + + , + ); + assertNamespacesMatch( + + + {usePortal(
)} + + , + ); + assertNamespacesMatch( +
+

+ {usePortal( + + + , + )} +

+

, + ); + }); - it('should keep track of namespace across portals (medium)', () => { - assertNamespacesMatch( - - - {usePortal(
)} - - {usePortal(
)} - - , - ); - assertNamespacesMatch( -
- - - {usePortal( - - - , - )} - -

-

, - ); - assertNamespacesMatch( + it('should keep track of namespace across portals (medium)', () => { + assertNamespacesMatch( + + + {usePortal(
)} + + {usePortal(
)} + + , + ); + assertNamespacesMatch( +
{usePortal( - -

- - - -

- - - , - )} - - , - ); - assertNamespacesMatch( -

- {usePortal( - - {usePortal(
)} - , )} -

-

, - ); - assertNamespacesMatch( - + +

+

, + ); + assertNamespacesMatch( + + + {usePortal( + + + +

+ + + +

+ + + , + )} + + , + ); + assertNamespacesMatch( +

+ {usePortal( {usePortal(
)} - + , + )} +

+

, + ); + assertNamespacesMatch( + + + {usePortal(
)} - , - ); - }); + + + , + ); + }); - it('should keep track of namespace across portals (complex)', () => { - assertNamespacesMatch( -
- {usePortal( - - - , - )} -

+ it('should keep track of namespace across portals (complex)', () => { + assertNamespacesMatch( +

+ {usePortal( - + , + )} +

+ + + + - - - -

-

, - ); - assertNamespacesMatch( -
+ + +

+

, + ); + assertNamespacesMatch( +
+ - - - {usePortal( + + {usePortal( + + - - - - - , - )} - - -

- {usePortal(

)} -

- - + + + , + )} - -

- , - ); - assertNamespacesMatch( -

-

- {usePortal( - - - - - -

- - {usePortal(

)} - - - , - )} + {usePortal(

)}

- -

- , - ); - }); - - it('should unwind namespaces on uncaught errors', () => { - function BrokenRender() { - throw new Error('Hello'); - } - - expect(() => { - assertNamespacesMatch( - - - , - ); - }).toThrow('Hello'); - assertNamespacesMatch(

); - }); - - it('should unwind namespaces on caught errors', () => { - function BrokenRender() { - throw new Error('Hello'); - } - - class ErrorBoundary extends React.Component { - state = {error: null}; - componentDidCatch(error) { - this.setState({error}); - } - render() { - if (this.state.error) { - return

; - } - return this.props.children; - } - } - - assertNamespacesMatch( + + +

+

, + ); + assertNamespacesMatch( +
- - - - - +

+ {usePortal( + + + + + +

+ + {usePortal(

)} + + + , + )} +

- , - ); - assertNamespacesMatch(

); - }); - - it('should unwind namespaces on caught errors in a portal', () => { - function BrokenRender() { - throw new Error('Hello'); - } + +

+

, + ); + }); - class ErrorBoundary extends React.Component { - state = {error: null}; - componentDidCatch(error) { - this.setState({error}); - } - render() { - if (this.state.error) { - return ; - } - return this.props.children; - } - } + it('should unwind namespaces on uncaught errors', () => { + function BrokenRender() { + throw new Error('Hello'); + } + expect(() => { assertNamespacesMatch( - - {usePortal( -
- - ) - -
, - )} - - {usePortal(
)} + , ); - }); - - it('should pass portal context when rendering subtree elsewhere', () => { - var portalContainer = document.createElement('div'); + }).toThrow('Hello'); + assertNamespacesMatch(
); + }); - class Component extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - }; + it('should unwind namespaces on caught errors', () => { + function BrokenRender() { + throw new Error('Hello'); + } - render() { - return
{this.context.foo}
; + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); + } + render() { + if (this.state.error) { + return

; } + return this.props.children; } + } - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string.isRequired, - }; + assertNamespacesMatch( + + + + + + + + + + , + ); + assertNamespacesMatch(

); + }); - getChildContext() { - return { - foo: 'bar', - }; - } + it('should unwind namespaces on caught errors in a portal', () => { + function BrokenRender() { + throw new Error('Hello'); + } - render() { - return ReactDOM.createPortal(, portalContainer); + class ErrorBoundary extends React.Component { + state = {error: null}; + componentDidCatch(error) { + this.setState({error}); + } + render() { + if (this.state.error) { + return ; } + return this.props.children; } + } - ReactDOM.render(, container); - expect(container.innerHTML).toBe(''); - expect(portalContainer.innerHTML).toBe('
bar
'); - }); + assertNamespacesMatch( + + + {usePortal( +
+ + ) + +
, + )} + + {usePortal(
)} + , + ); + }); - it('should update portal context if it changes due to setState', () => { - var portalContainer = document.createElement('div'); + it('should pass portal context when rendering subtree elsewhere', () => { + var portalContainer = document.createElement('div'); - class Component extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - getFoo: PropTypes.func.isRequired, - }; + class Component extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired, + }; - render() { - return
{this.context.foo + '-' + this.context.getFoo()}
; - } + render() { + return
{this.context.foo}
; } + } - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string.isRequired, - getFoo: PropTypes.func.isRequired, - }; + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string.isRequired, + }; - state = { - bar: 'initial', + getChildContext() { + return { + foo: 'bar', }; + } - getChildContext() { - return { - foo: this.state.bar, - getFoo: () => this.state.bar, - }; - } - - render() { - return ReactDOM.createPortal(, portalContainer); - } + render() { + return ReactDOM.createPortal(, portalContainer); } + } - var instance = ReactDOM.render(, container); - expect(portalContainer.innerHTML).toBe('
initial-initial
'); - expect(container.innerHTML).toBe(''); - instance.setState({bar: 'changed'}); - expect(portalContainer.innerHTML).toBe('
changed-changed
'); - expect(container.innerHTML).toBe(''); - }); + ReactDOM.render(, container); + expect(container.innerHTML).toBe(''); + expect(portalContainer.innerHTML).toBe('
bar
'); + }); - it('should update portal context if it changes due to re-render', () => { - var portalContainer = document.createElement('div'); + it('should update portal context if it changes due to setState', () => { + var portalContainer = document.createElement('div'); - class Component extends React.Component { - static contextTypes = { - foo: PropTypes.string.isRequired, - getFoo: PropTypes.func.isRequired, - }; + class Component extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired, + getFoo: PropTypes.func.isRequired, + }; - render() { - return
{this.context.foo + '-' + this.context.getFoo()}
; - } + render() { + return
{this.context.foo + '-' + this.context.getFoo()}
; } + } - class Parent extends React.Component { - static childContextTypes = { - foo: PropTypes.string.isRequired, - getFoo: PropTypes.func.isRequired, - }; + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string.isRequired, + getFoo: PropTypes.func.isRequired, + }; - getChildContext() { - return { - foo: this.props.bar, - getFoo: () => this.props.bar, - }; - } + state = { + bar: 'initial', + }; - render() { - return ReactDOM.createPortal(, portalContainer); - } + getChildContext() { + return { + foo: this.state.bar, + getFoo: () => this.state.bar, + }; } - ReactDOM.render(, container); - expect(portalContainer.innerHTML).toBe('
initial-initial
'); - expect(container.innerHTML).toBe(''); - ReactDOM.render(, container); - expect(portalContainer.innerHTML).toBe('
changed-changed
'); - expect(container.innerHTML).toBe(''); - }); - - it('findDOMNode should find dom element after expanding a fragment', () => { - class MyNode extends React.Component { - render() { - return !this.props.flag - ? [
] - : [,
]; - } + render() { + return ReactDOM.createPortal(, portalContainer); } + } - var myNodeA = ReactDOM.render(, container); - var a = ReactDOM.findDOMNode(myNodeA); - expect(a.tagName).toBe('DIV'); + var instance = ReactDOM.render(, container); + expect(portalContainer.innerHTML).toBe('
initial-initial
'); + expect(container.innerHTML).toBe(''); + instance.setState({bar: 'changed'}); + expect(portalContainer.innerHTML).toBe('
changed-changed
'); + expect(container.innerHTML).toBe(''); + }); - var myNodeB = ReactDOM.render(, container); - expect(myNodeA === myNodeB).toBe(true); + it('should update portal context if it changes due to re-render', () => { + var portalContainer = document.createElement('div'); - var b = ReactDOM.findDOMNode(myNodeB); - expect(b.tagName).toBe('SPAN'); - }); + class Component extends React.Component { + static contextTypes = { + foo: PropTypes.string.isRequired, + getFoo: PropTypes.func.isRequired, + }; - it('should bubble events from the portal to the parent', () => { - var portalContainer = document.createElement('div'); + render() { + return
{this.context.foo + '-' + this.context.getFoo()}
; + } + } - var ops = []; - var portal = null; + class Parent extends React.Component { + static childContextTypes = { + foo: PropTypes.string.isRequired, + getFoo: PropTypes.func.isRequired, + }; - ReactDOM.render( -
ops.push('parent clicked')}> - {ReactDOM.createPortal( -
ops.push('portal clicked')} - ref={n => (portal = n)}> - portal -
, - portalContainer, - )} -
, - container, - ); + getChildContext() { + return { + foo: this.props.bar, + getFoo: () => this.props.bar, + }; + } - expect(portal.tagName).toBe('DIV'); + render() { + return ReactDOM.createPortal(, portalContainer); + } + } - var fakeNativeEvent = {}; - ReactTestUtils.simulateNativeEventOnNode( - 'topClick', - portal, - fakeNativeEvent, - ); + ReactDOM.render(, container); + expect(portalContainer.innerHTML).toBe('
initial-initial
'); + expect(container.innerHTML).toBe(''); + ReactDOM.render(, container); + expect(portalContainer.innerHTML).toBe('
changed-changed
'); + expect(container.innerHTML).toBe(''); + }); - expect(ops).toEqual(['portal clicked', 'parent clicked']); - }); + it('findDOMNode should find dom element after expanding a fragment', () => { + class MyNode extends React.Component { + render() { + return !this.props.flag + ? [
] + : [,
]; + } + } - it('should not onMouseLeave when staying in the portal', () => { - var portalContainer = document.createElement('div'); + var myNodeA = ReactDOM.render(, container); + var a = ReactDOM.findDOMNode(myNodeA); + expect(a.tagName).toBe('DIV'); - var ops = []; - var firstTarget = null; - var secondTarget = null; - var thirdTarget = null; + var myNodeB = ReactDOM.render(, container); + expect(myNodeA === myNodeB).toBe(true); - function simulateMouseMove(from, to) { - if (from) { - ReactTestUtils.simulateNativeEventOnNode('topMouseOut', from, { - target: from, - relatedTarget: to, - }); - } - if (to) { - ReactTestUtils.simulateNativeEventOnNode('topMouseOver', to, { - target: to, - relatedTarget: from, - }); - } - } + var b = ReactDOM.findDOMNode(myNodeB); + expect(b.tagName).toBe('SPAN'); + }); - ReactDOM.render( -
-
ops.push('enter parent')} - onMouseLeave={() => ops.push('leave parent')}> -
(firstTarget = n)} /> - {ReactDOM.createPortal( -
ops.push('enter portal')} - onMouseLeave={() => ops.push('leave portal')} - ref={n => (secondTarget = n)}> - portal -
, - portalContainer, - )} -
-
(thirdTarget = n)} /> -
, - container, - ); + it('should bubble events from the portal to the parent', () => { + var portalContainer = document.createElement('div'); - simulateMouseMove(null, firstTarget); - expect(ops).toEqual(['enter parent']); + var ops = []; + var portal = null; - ops = []; + ReactDOM.render( +
ops.push('parent clicked')}> + {ReactDOM.createPortal( +
ops.push('portal clicked')} + ref={n => (portal = n)}> + portal +
, + portalContainer, + )} +
, + container, + ); + + expect(portal.tagName).toBe('DIV'); + + var fakeNativeEvent = {}; + ReactTestUtils.simulateNativeEventOnNode( + 'topClick', + portal, + fakeNativeEvent, + ); + + expect(ops).toEqual(['portal clicked', 'parent clicked']); + }); - simulateMouseMove(firstTarget, secondTarget); - expect(ops).toEqual([ - // Parent did not invoke leave because we're still inside the portal. - 'enter portal', - ]); + it('should not onMouseLeave when staying in the portal', () => { + var portalContainer = document.createElement('div'); - ops = []; + var ops = []; + var firstTarget = null; + var secondTarget = null; + var thirdTarget = null; - simulateMouseMove(secondTarget, thirdTarget); - expect(ops).toEqual([ - 'leave portal', - 'leave parent', // Only when we leave the portal does onMouseLeave fire. - ]); - }); + function simulateMouseMove(from, to) { + if (from) { + ReactTestUtils.simulateNativeEventOnNode('topMouseOut', from, { + target: from, + relatedTarget: to, + }); + } + if (to) { + ReactTestUtils.simulateNativeEventOnNode('topMouseOver', to, { + target: to, + relatedTarget: from, + }); + } + } - it('should throw on bad createPortal argument', () => { - expect(() => { - ReactDOM.createPortal(
portal
, null); - }).toThrow('Target container is not a DOM element.'); - expect(() => { - ReactDOM.createPortal(
portal
, document.createTextNode('hi')); - }).toThrow('Target container is not a DOM element.'); - }); + ReactDOM.render( +
+
ops.push('enter parent')} + onMouseLeave={() => ops.push('leave parent')}> +
(firstTarget = n)} /> + {ReactDOM.createPortal( +
ops.push('enter portal')} + onMouseLeave={() => ops.push('leave portal')} + ref={n => (secondTarget = n)}> + portal +
, + portalContainer, + )} +
+
(thirdTarget = n)} /> +
, + container, + ); + + simulateMouseMove(null, firstTarget); + expect(ops).toEqual(['enter parent']); + + ops = []; + + simulateMouseMove(firstTarget, secondTarget); + expect(ops).toEqual([ + // Parent did not invoke leave because we're still inside the portal. + 'enter portal', + ]); + + ops = []; + + simulateMouseMove(secondTarget, thirdTarget); + expect(ops).toEqual([ + 'leave portal', + 'leave parent', // Only when we leave the portal does onMouseLeave fire. + ]); + }); - it('should warn for non-functional event listeners', () => { - spyOn(console, 'error'); - class Example extends React.Component { - render() { - return
; - } + it('should throw on bad createPortal argument', () => { + expect(() => { + ReactDOM.createPortal(
portal
, null); + }).toThrow('Target container is not a DOM element.'); + expect(() => { + ReactDOM.createPortal(
portal
, document.createTextNode('hi')); + }).toThrow('Target container is not a DOM element.'); + }); + + it('should warn for non-functional event listeners', () => { + spyOn(console, 'error'); + class Example extends React.Component { + render() { + return
; } - ReactDOM.render(, container); - expectDev(console.error.calls.count()).toBe(1); - expectDev( - normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]), - ).toContain( - 'Expected `onClick` listener to be a function, instead got a value of `string` type.\n' + - ' in div (at **)\n' + - ' in Example (at **)', - ); - }); + } + ReactDOM.render(, container); + expectDev(console.error.calls.count()).toBe(1); + expectDev( + normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]), + ).toContain( + 'Expected `onClick` listener to be a function, instead got a value of `string` type.\n' + + ' in div (at **)\n' + + ' in Example (at **)', + ); + }); - it('should not update event handlers until commit', () => { - let ops = []; - const handlerA = () => ops.push('A'); - const handlerB = () => ops.push('B'); + it('should not update event handlers until commit', () => { + let ops = []; + const handlerA = () => ops.push('A'); + const handlerB = () => ops.push('B'); - class Example extends React.Component { - state = {flip: false, count: 0}; - flip() { - this.setState({flip: true, count: this.state.count + 1}); - } - tick() { - this.setState({count: this.state.count + 1}); - } - render() { - const useB = !this.props.forceA && this.state.flip; - return
; - } + class Example extends React.Component { + state = {flip: false, count: 0}; + flip() { + this.setState({flip: true, count: this.state.count + 1}); } - - class Click extends React.Component { - constructor() { - super(); - click(node); - } - render() { - return null; - } + tick() { + this.setState({count: this.state.count + 1}); } + render() { + const useB = !this.props.forceA && this.state.flip; + return
; + } + } - let inst; - ReactDOM.render([ (inst = n)} />], container); - const node = container.firstChild; - expect(node.tagName).toEqual('DIV'); - - function click(target) { - var fakeNativeEvent = {}; - ReactTestUtils.simulateNativeEventOnNode( - 'topClick', - target, - fakeNativeEvent, - ); + class Click extends React.Component { + constructor() { + super(); + click(node); } + render() { + return null; + } + } - click(node); + let inst; + ReactDOM.render([ (inst = n)} />], container); + const node = container.firstChild; + expect(node.tagName).toEqual('DIV'); - expect(ops).toEqual(['A']); - ops = []; + function click(target) { + var fakeNativeEvent = {}; + ReactTestUtils.simulateNativeEventOnNode( + 'topClick', + target, + fakeNativeEvent, + ); + } - // Render with the other event handler. - inst.flip(); + click(node); - click(node); + expect(ops).toEqual(['A']); + ops = []; - expect(ops).toEqual(['B']); - ops = []; + // Render with the other event handler. + inst.flip(); - // Rerender without changing any props. - inst.tick(); + click(node); - click(node); + expect(ops).toEqual(['B']); + ops = []; - expect(ops).toEqual(['B']); - ops = []; + // Rerender without changing any props. + inst.tick(); - // Render a flip back to the A handler. The second component invokes the - // click handler during render to simulate a click during an aborted - // render. I use this hack because at current time we don't have a way to - // test aborted ReactDOM renders. - ReactDOM.render( - [, ], - container, - ); + click(node); - // Because the new click handler has not yet committed, we should still - // invoke B. - expect(ops).toEqual(['B']); - ops = []; + expect(ops).toEqual(['B']); + ops = []; - // Any click that happens after commit, should invoke A. - click(node); - expect(ops).toEqual(['A']); - }); + // Render a flip back to the A handler. The second component invokes the + // click handler during render to simulate a click during an aborted + // render. I use this hack because at current time we don't have a way to + // test aborted ReactDOM renders. + ReactDOM.render( + [, ], + container, + ); - it('should not crash encountering low-priority tree', () => { - ReactDOM.render( -