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

This commit is contained in:
Dan Abramov
2017-09-27 17:14:27 +01:00
committed by GitHub
parent f50ff7e5d1
commit a947d3f92f
33 changed files with 2767 additions and 3711 deletions

View File

@@ -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 [<div />, <div />];
}
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 [<div />, <div />];
}
}
ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
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 [<div key="foo" />, <div key="bar" />];
}
}
ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
expectDev(console.error.calls.count()).toBe(0);
});
it('warns for keys for arrays at the top level', () => {
spyOn(console, 'error');
ReactTestUtils.renderIntoDocument([<div />, <div />]);
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.' +
'\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 [<div key="foo" />, <div key="bar" />];
}
}
ReactTestUtils.renderIntoDocument(<ComponentReturningArray />);
expectDev(console.error.calls.count()).toBe(0);
});
it('warns for keys for arrays at the top level', () => {
spyOn(console, 'error');
ReactTestUtils.renderIntoDocument([<div />, <div />]);
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.',
);
});
});
});

View File

@@ -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', () => {

View File

@@ -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', () => {

View File

@@ -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() {
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(<Foo />, 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 <Component /> 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;
}
spyOn(console, 'error');
var container = document.createElement('div');
ReactDOM.render(<Foo />, 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 <Component /> 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;
}
}
spyOn(console, 'error');
var container = document.createElement('div');
ReactDOM.render(<Foo />, 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 <Component /> 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 <div><span>{Foo}</span></div>;
}
spyOn(console, 'error');
var container = document.createElement('div');
ReactDOM.render(<Foo />, 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 <Component /> 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 <Bar>{() => 'Hello'}</Bar>;
}
spyOn(console, 'error');
var container = document.createElement('div');
ReactDOM.render(<Foo />, container);
expect(container.innerHTML).toBe('Hello');
expectDev(console.error.calls.count()).toBe(0);
});
}
spyOn(console, 'error');
var container = document.createElement('div');
ReactDOM.render(<Foo />, 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 <Component /> 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 <div><span>{Foo}</span></div>;
}
spyOn(console, 'error');
var container = document.createElement('div');
ReactDOM.render(<Foo />, 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 <Component /> 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 <Bar>{() => 'Hello'}</Bar>;
}
spyOn(console, 'error');
var container = document.createElement('div');
ReactDOM.render(<Foo />, container);
expect(container.innerHTML).toBe('Hello');
expectDev(console.error.calls.count()).toBe(0);
});
});
});

View File

@@ -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(<Q />, 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(<div />, portalEl);
}
render() {
return <div><div /></div>;
}
}
ReactDOM.render(<Foo />, 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 (
<div>
<div>
{ReactDOMServer.renderToString(<div />)}
</div>
</div>
);
}
}
ReactDOM.render(<Foo />, 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 = <div>Hi.</div>;
@@ -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');

View File

@@ -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(<Parent />, 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(<Parent />, container);
expectDev(console.warn.calls.count()).toBe(0);
}
container = document.createElement('div');
container.innerHTML = markup;
ReactDOM.hydrate(<Parent />, container);
expectDev(console.warn.calls.count()).toBe(0);
});
it('should react to state changes from callbacks', () => {

View File

@@ -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');
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() {
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(<Test />, container);
// Mount
const container = document.createElement('div');
ReactDOM.render(<Test />, 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.',
);
});
}
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.',
);
});
});

View File

@@ -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(<Component />);
}).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(<Component />);
}).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(<Empty />, 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(<Empty />, 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);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -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;

View File

@@ -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',

View File

@@ -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, <div>{1.2}{''}{<div />}{'foo'}</div>, true, 1.2], [<div />, '1.2'],
['', 'foo', <div>{true}{<div />}{1.2}{''}</div>, 'foo'], ['', 'foo', <div />, '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', () => {

View File

@@ -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;

View File

@@ -14,8 +14,6 @@ var React;
var ReactDOM;
var ReactTestUtils;
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
function StatelessComponent(props) {
return <div>{props.name}</div>;
}
@@ -118,54 +116,22 @@ describe('ReactStatelessComponent', () => {
ReactDOM.render(<StatelessComponentWithChildContext name="A" />, 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 [<div />, <div />];
}
expect(function() {
ReactTestUtils.renderIntoDocument(<div><NotAComponent /></div>);
}).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(<div><NotAComponent /></div>);
}).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(<div><NotAComponent /></div>);
}).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() {

View File

@@ -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 ref={n => (b = n)} />, bContainer);
}
portal = ReactDOM.createPortal(<B ref={n => (b = n)} />, bContainer);
return <div>A{this.state.x}{portal}</div>;
}
}
@@ -535,10 +531,6 @@ describe('ReactUpdates', () => {
}
a = ReactTestUtils.renderIntoDocument(<A />);
if (!ReactDOMFeatureFlags.useFiber) {
ReactDOM.render(<B ref={n => (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 <BadRender />;
}
class ErrorBoundary extends React.Component {
componentDidCatch() {
this.props.parent.remount();
}
class NonTerminating extends React.Component {
state = {step: 0};
remount() {
this.setState(state => ({step: state.step + 1}));
}
render() {
return <ErrorBoundary key={this.state.step} parent={this} />;
}
render() {
return <BadRender />;
}
}
const container = document.createElement('div');
expect(() => {
ReactDOM.render(<NonTerminating />, container);
}).toThrow('Maximum');
});
}
class NonTerminating extends React.Component {
state = {step: 0};
remount() {
this.setState(state => ({step: state.step + 1}));
}
render() {
return <ErrorBoundary key={this.state.step} parent={this} />;
}
}
const container = document.createElement('div');
expect(() => {
ReactDOM.render(<NonTerminating />, container);
}).toThrow('Maximum');
});
});

View File

@@ -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(<TextWithStringRef />);
}).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(<TextWithStringRef />);
}).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(<TextWithStringRef />);
}).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).',
);
});
});

View File

@@ -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(
[<Comp ref={ref} key="a" />, 5, <div ref={ref2} key="b">Hello</div>],
container,
);
// fragment
inst = null;
ref = jest.fn(value => (inst = value));
var divInst = null;
var ref2 = jest.fn(value => (divInst = value));
result = ReactDOM.render(
[<Comp ref={ref} key="a" />, 5, <div ref={ref2} key="b">Hello</div>],
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(<RefTest />);
}).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(<RefTest />);
}).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;

View File

@@ -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(
<div {...expectHTML}>
<svg {...expectSVG}>
<foreignObject {...expectSVG}>
<p {...expectHTML} />
{usePortal(
assertNamespacesMatch(
<div {...expectHTML}>
<svg {...expectSVG}>
<foreignObject {...expectSVG}>
<p {...expectHTML} />
{usePortal(
<svg {...expectSVG}>
<image {...expectSVG} />
<svg {...expectSVG}>
<image {...expectSVG} />
<svg {...expectSVG}>
<image {...expectSVG} />
<foreignObject {...expectSVG}>
<p {...expectHTML} />
</foreignObject>
{usePortal(<p {...expectHTML} />)}
</svg>
<image {...expectSVG} />
</svg>,
)}
<p {...expectHTML} />
</foreignObject>
<image {...expectSVG} />
</svg>
<p {...expectHTML} />
</div>,
);
});
}
<foreignObject {...expectSVG}>
<p {...expectHTML} />
</foreignObject>
{usePortal(<p {...expectHTML} />)}
</svg>
<image {...expectSVG} />
</svg>,
)}
<p {...expectHTML} />
</foreignObject>
<image {...expectSVG} />
</svg>
<p {...expectHTML} />
</div>,
);
});
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,4 @@
var React = require('react');
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var ReactFeatureFlags = require('ReactFeatureFlags');
var ReactDOM;
@@ -25,270 +24,268 @@ describe('ReactDOMFiberAsync', () => {
expect(ops).toEqual(['Hi', 'Bye']);
});
if (ReactDOMFeatureFlags.useFiber) {
describe('with feature flag disabled', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('ReactFeatureFlags');
container = document.createElement('div');
ReactFeatureFlags.enableAsyncSubtreeAPI = false;
ReactDOM = require('react-dom');
});
it('renders synchronously', () => {
ReactDOM.render(
<AsyncComponent><div>Hi</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('Hi');
ReactDOM.render(
<AsyncComponent><div>Bye</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('Bye');
});
describe('with feature flag disabled', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('ReactFeatureFlags');
container = document.createElement('div');
ReactFeatureFlags.enableAsyncSubtreeAPI = false;
ReactDOM = require('react-dom');
});
describe('with feature flag enabled', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('ReactFeatureFlags');
container = document.createElement('div');
ReactFeatureFlags.enableAsyncSubtreeAPI = true;
ReactDOM = require('react-dom');
});
it('renders synchronously', () => {
ReactDOM.render(
<AsyncComponent><div>Hi</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('Hi');
it('AsyncComponent at the root makes the entire tree async', () => {
ReactDOM.render(
<AsyncComponent><div>Hi</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('Hi');
ReactDOM.render(
<AsyncComponent><div>Bye</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('Bye');
});
});
ReactDOM.render(
<AsyncComponent><div>Bye</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('Hi');
jest.runAllTimers();
expect(container.textContent).toEqual('Bye');
});
describe('with feature flag enabled', () => {
beforeEach(() => {
jest.resetModules();
ReactFeatureFlags = require('ReactFeatureFlags');
container = document.createElement('div');
ReactFeatureFlags.enableAsyncSubtreeAPI = true;
ReactDOM = require('react-dom');
});
it('updates inside an async tree are async by default', () => {
let instance;
class Component extends React.Component {
state = {step: 0};
render() {
instance = this;
return <div>{this.state.step}</div>;
}
it('AsyncComponent at the root makes the entire tree async', () => {
ReactDOM.render(
<AsyncComponent><div>Hi</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('Hi');
ReactDOM.render(
<AsyncComponent><div>Bye</div></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('Hi');
jest.runAllTimers();
expect(container.textContent).toEqual('Bye');
});
it('updates inside an async tree are async by default', () => {
let instance;
class Component extends React.Component {
state = {step: 0};
render() {
instance = this;
return <div>{this.state.step}</div>;
}
}
ReactDOM.render(
<AsyncComponent><Component /></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('0');
ReactDOM.render(
<AsyncComponent><Component /></AsyncComponent>,
container,
);
expect(container.textContent).toEqual('');
jest.runAllTimers();
expect(container.textContent).toEqual('0');
instance.setState({step: 1});
expect(container.textContent).toEqual('0');
jest.runAllTimers();
expect(container.textContent).toEqual('1');
});
instance.setState({step: 1});
expect(container.textContent).toEqual('0');
jest.runAllTimers();
expect(container.textContent).toEqual('1');
});
it('AsyncComponent creates an async subtree', () => {
let instance;
class Component extends React.unstable_AsyncComponent {
state = {step: 0};
render() {
instance = this;
return <div>{this.state.step}</div>;
}
it('AsyncComponent creates an async subtree', () => {
let instance;
class Component extends React.unstable_AsyncComponent {
state = {step: 0};
render() {
instance = this;
return <div>{this.state.step}</div>;
}
}
ReactDOM.render(<div><Component /></div>, container);
jest.runAllTimers();
ReactDOM.render(<div><Component /></div>, container);
jest.runAllTimers();
instance.setState({step: 1});
expect(container.textContent).toEqual('0');
jest.runAllTimers();
expect(container.textContent).toEqual('1');
});
instance.setState({step: 1});
expect(container.textContent).toEqual('0');
jest.runAllTimers();
expect(container.textContent).toEqual('1');
});
it('updates inside an async subtree are async by default', () => {
class Component extends React.unstable_AsyncComponent {
render() {
return <Child />;
}
it('updates inside an async subtree are async by default', () => {
class Component extends React.unstable_AsyncComponent {
render() {
return <Child />;
}
}
let instance;
class Child extends React.Component {
state = {step: 0};
render() {
instance = this;
return <div>{this.state.step}</div>;
}
let instance;
class Child extends React.Component {
state = {step: 0};
render() {
instance = this;
return <div>{this.state.step}</div>;
}
}
ReactDOM.render(<div><Component /></div>, container);
jest.runAllTimers();
ReactDOM.render(<div><Component /></div>, container);
jest.runAllTimers();
instance.setState({step: 1});
expect(container.textContent).toEqual('0');
jest.runAllTimers();
expect(container.textContent).toEqual('1');
});
instance.setState({step: 1});
expect(container.textContent).toEqual('0');
jest.runAllTimers();
expect(container.textContent).toEqual('1');
});
it('flushSync batches sync updates and flushes them at the end of the batch', () => {
let ops = [];
let instance;
it('flushSync batches sync updates and flushes them at the end of the batch', () => {
let ops = [];
let instance;
class Component extends React.Component {
state = {text: ''};
push(val) {
this.setState(state => ({text: state.text + val}));
}
componentDidUpdate() {
ops.push(this.state.text);
}
render() {
instance = this;
return <span>{this.state.text}</span>;
}
class Component extends React.Component {
state = {text: ''};
push(val) {
this.setState(state => ({text: state.text + val}));
}
componentDidUpdate() {
ops.push(this.state.text);
}
render() {
instance = this;
return <span>{this.state.text}</span>;
}
}
ReactDOM.render(<Component />, container);
ReactDOM.render(<Component />, container);
instance.push('A');
expect(ops).toEqual(['A']);
instance.push('A');
expect(ops).toEqual(['A']);
expect(container.textContent).toEqual('A');
ReactDOM.flushSync(() => {
instance.push('B');
instance.push('C');
// Not flushed yet
expect(container.textContent).toEqual('A');
expect(ops).toEqual(['A']);
});
expect(container.textContent).toEqual('ABC');
expect(ops).toEqual(['A', 'ABC']);
instance.push('D');
expect(container.textContent).toEqual('ABCD');
expect(ops).toEqual(['A', 'ABC', 'ABCD']);
});
it('flushSync flushes updates even if nested inside another flushSync', () => {
let ops = [];
let instance;
class Component extends React.Component {
state = {text: ''};
push(val) {
this.setState(state => ({text: state.text + val}));
}
componentDidUpdate() {
ops.push(this.state.text);
}
render() {
instance = this;
return <span>{this.state.text}</span>;
}
}
ReactDOM.render(<Component />, container);
instance.push('A');
expect(ops).toEqual(['A']);
expect(container.textContent).toEqual('A');
ReactDOM.flushSync(() => {
instance.push('B');
instance.push('C');
// Not flushed yet
expect(container.textContent).toEqual('A');
expect(ops).toEqual(['A']);
ReactDOM.flushSync(() => {
instance.push('B');
instance.push('C');
// Not flushed yet
expect(container.textContent).toEqual('A');
expect(ops).toEqual(['A']);
});
expect(container.textContent).toEqual('ABC');
expect(ops).toEqual(['A', 'ABC']);
instance.push('D');
expect(container.textContent).toEqual('ABCD');
expect(ops).toEqual(['A', 'ABC', 'ABCD']);
});
it('flushSync flushes updates even if nested inside another flushSync', () => {
let ops = [];
let instance;
class Component extends React.Component {
state = {text: ''};
push(val) {
this.setState(state => ({text: state.text + val}));
}
componentDidUpdate() {
ops.push(this.state.text);
}
render() {
instance = this;
return <span>{this.state.text}</span>;
}
}
ReactDOM.render(<Component />, container);
instance.push('A');
expect(ops).toEqual(['A']);
expect(container.textContent).toEqual('A');
ReactDOM.flushSync(() => {
instance.push('B');
instance.push('C');
// Not flushed yet
expect(container.textContent).toEqual('A');
expect(ops).toEqual(['A']);
ReactDOM.flushSync(() => {
instance.push('D');
});
// The nested flushSync caused everything to flush.
expect(container.textContent).toEqual('ABCD');
expect(ops).toEqual(['A', 'ABCD']);
instance.push('D');
});
// The nested flushSync caused everything to flush.
expect(container.textContent).toEqual('ABCD');
expect(ops).toEqual(['A', 'ABCD']);
});
it('flushSync throws if already performing work', () => {
class Component extends React.Component {
componentDidUpdate() {
ReactDOM.flushSync(() => {});
}
render() {
return null;
}
}
// Initial mount
ReactDOM.render(<Component />, container);
// Update
expect(() => ReactDOM.render(<Component />, container)).toThrow(
'flushSync was called from inside a lifecycle method',
);
});
it('flushSync flushes updates before end of the tick', () => {
let ops = [];
let instance;
class Component extends React.unstable_AsyncComponent {
state = {text: ''};
push(val) {
this.setState(state => ({text: state.text + val}));
}
componentDidUpdate() {
ops.push(this.state.text);
}
render() {
instance = this;
return <span>{this.state.text}</span>;
}
}
ReactDOM.render(<Component />, container);
jest.runAllTimers();
// Updates are async by default
instance.push('A');
expect(ops).toEqual([]);
expect(container.textContent).toEqual('');
ReactDOM.flushSync(() => {
instance.push('B');
instance.push('C');
// Not flushed yet
expect(container.textContent).toEqual('');
expect(ops).toEqual([]);
});
// Only the active updates have flushed
expect(container.textContent).toEqual('BC');
expect(ops).toEqual(['BC']);
instance.push('D');
expect(container.textContent).toEqual('BC');
expect(ops).toEqual(['BC']);
// Flush the async updates
jest.runAllTimers();
expect(container.textContent).toEqual('BCAD');
expect(ops).toEqual(['BC', 'BCAD']);
});
expect(container.textContent).toEqual('ABCD');
expect(ops).toEqual(['A', 'ABCD']);
});
}
it('flushSync throws if already performing work', () => {
class Component extends React.Component {
componentDidUpdate() {
ReactDOM.flushSync(() => {});
}
render() {
return null;
}
}
// Initial mount
ReactDOM.render(<Component />, container);
// Update
expect(() => ReactDOM.render(<Component />, container)).toThrow(
'flushSync was called from inside a lifecycle method',
);
});
it('flushSync flushes updates before end of the tick', () => {
let ops = [];
let instance;
class Component extends React.unstable_AsyncComponent {
state = {text: ''};
push(val) {
this.setState(state => ({text: state.text + val}));
}
componentDidUpdate() {
ops.push(this.state.text);
}
render() {
instance = this;
return <span>{this.state.text}</span>;
}
}
ReactDOM.render(<Component />, container);
jest.runAllTimers();
// Updates are async by default
instance.push('A');
expect(ops).toEqual([]);
expect(container.textContent).toEqual('');
ReactDOM.flushSync(() => {
instance.push('B');
instance.push('C');
// Not flushed yet
expect(container.textContent).toEqual('');
expect(ops).toEqual([]);
});
// Only the active updates have flushed
expect(container.textContent).toEqual('BC');
expect(ops).toEqual(['BC']);
instance.push('D');
expect(container.textContent).toEqual('BC');
expect(ops).toEqual(['BC']);
// Flush the async updates
jest.runAllTimers();
expect(container.textContent).toEqual('BCAD');
expect(ops).toEqual(['BC', 'BCAD']);
});
});
});

View File

@@ -15,7 +15,6 @@ describe('ReactDOMComponent', () => {
var ReactDOM;
var ReactDOMServer;
var inputValueTracking;
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
function normalizeCodeLocInfo(str) {
return str && str.replace(/\(at .+?:\d+\)/g, '(at **)');
@@ -25,7 +24,6 @@ describe('ReactDOMComponent', () => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');
// TODO: can we express this test with only public API?
@@ -940,9 +938,7 @@ describe('ReactDOMComponent', () => {
'<hasOwnProperty /> is using uppercase HTML',
);
expectDev(console.error.calls.argsFor(3)[0]).toContain(
ReactDOMFeatureFlags.useFiber
? 'The tag <hasOwnProperty> is unrecognized in this browser'
: 'The tag <hasownproperty> is unrecognized in this browser',
'The tag <hasOwnProperty> is unrecognized in this browser',
);
});
@@ -1323,36 +1319,6 @@ describe('ReactDOMComponent', () => {
});
describe('unmountComponent', () => {
// Fiber does not have a clean-up phase for host components; relies on GC
if (!ReactDOMFeatureFlags.useFiber) {
it('should clean up input value tracking', () => {
var container = document.createElement('div');
var node = ReactDOM.render(
<input type="text" defaultValue="foo" />,
container,
);
var tracker = inputValueTracking._getTrackerFromNode(node);
spyOn(tracker, 'stopTracking');
ReactDOM.unmountComponentAtNode(container);
expect(tracker.stopTracking.calls.count()).toBe(1);
});
it('should clean up input textarea tracking', () => {
var container = document.createElement('div');
var node = ReactDOM.render(<textarea defaultValue="foo" />, container);
var tracker = inputValueTracking._getTrackerFromNode(node);
spyOn(tracker, 'stopTracking');
ReactDOM.unmountComponentAtNode(container);
expect(tracker.stopTracking.calls.count()).toBe(1);
});
}
it('unmounts children before unsetting DOM node info', () => {
class Inner extends React.Component {
render() {
@@ -1402,15 +1368,12 @@ describe('ReactDOMComponent', () => {
spyOn(console, 'error');
ReactTestUtils.renderIntoDocument(<div><tr /><tr /></div>);
var addendum = ReactDOMFeatureFlags.useFiber
? '\n in tr (at **)' + '\n in div (at **)'
: ' See div > tr.';
expectDev(console.error.calls.count()).toBe(1);
expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
'<div>.' +
addendum,
'\n in tr (at **)' +
'\n in div (at **)',
);
});
@@ -1419,16 +1382,13 @@ describe('ReactDOMComponent', () => {
var p = document.createElement('p');
ReactDOM.render(<span><p /></span>, p);
var addendum = ReactDOMFeatureFlags.useFiber
? // There is no outer `p` here because root container is not part of the stack.
'\n in p (at **)' + '\n in span (at **)'
: ' See p > ... > p.';
expectDev(console.error.calls.count()).toBe(1);
expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: validateDOMNesting(...): <p> cannot appear as a descendant ' +
'of <p>.' +
addendum,
// There is no outer `p` here because root container is not part of the stack.
'\n in p (at **)' +
'\n in span (at **)',
);
});
@@ -1450,39 +1410,31 @@ describe('ReactDOMComponent', () => {
ReactTestUtils.renderIntoDocument(<Foo />);
expectDev(console.error.calls.count()).toBe(3);
var addendum1 = ReactDOMFeatureFlags.useFiber
? '\n in tr (at **)' +
'\n in Row (at **)' +
'\n in table (at **)' +
'\n in Foo (at **)'
: ' See Foo > table > Row > tr.';
expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(0)[0])).toBe(
'Warning: validateDOMNesting(...): <tr> cannot appear as a child of ' +
'<table>. Add a <tbody> to your code to match the DOM tree generated ' +
'by the browser.' +
addendum1,
'\n in tr (at **)' +
'\n in Row (at **)' +
'\n in table (at **)' +
'\n in Foo (at **)',
);
var addendum2 = ReactDOMFeatureFlags.useFiber
? '\n in tr (at **)' +
'\n in Row (at **)' +
'\n in table (at **)' +
'\n in Foo (at **)'
: ' See Row > tr > #text.';
expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(1)[0])).toBe(
'Warning: validateDOMNesting(...): Text nodes cannot appear as a ' +
'child of <tr>.' +
addendum2,
'\n in tr (at **)' +
'\n in Row (at **)' +
'\n in table (at **)' +
'\n in Foo (at **)',
);
var addendum3 = ReactDOMFeatureFlags.useFiber
? '\n in table (at **)' + '\n in Foo (at **)'
: ' See Foo > table > #text.';
expectDev(normalizeCodeLocInfo(console.error.calls.argsFor(2)[0])).toBe(
'Warning: validateDOMNesting(...): Whitespace text nodes cannot ' +
"appear as a child of <table>. Make sure you don't have any extra " +
'whitespace between tags on each line of your source code.' +
addendum3,
'\n in table (at **)' +
'\n in Foo (at **)',
);
});
@@ -1518,13 +1470,11 @@ describe('ReactDOMComponent', () => {
expectDev(
normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
).toContain(
ReactDOMFeatureFlags.useFiber
? '\n in tr (at **)' +
'\n in Row (at **)' +
'\n in FancyRow (at **)' +
'\n in table (at **)' +
'\n in Viz1 (at **)'
: 'See Viz1 > table > FancyRow > Row > tr.',
'\n in tr (at **)' +
'\n in Row (at **)' +
'\n in FancyRow (at **)' +
'\n in table (at **)' +
'\n in Viz1 (at **)',
);
function Viz2() {
@@ -1538,15 +1488,13 @@ describe('ReactDOMComponent', () => {
expectDev(
normalizeCodeLocInfo(console.error.calls.argsFor(1)[0]),
).toContain(
ReactDOMFeatureFlags.useFiber
? '\n in tr (at **)' +
'\n in Row (at **)' +
'\n in FancyRow (at **)' +
'\n in table (at **)' +
'\n in Table (at **)' +
'\n in FancyTable (at **)' +
'\n in Viz2 (at **)'
: 'See Viz2 > FancyTable > Table > table > FancyRow > Row > tr.',
'\n in tr (at **)' +
'\n in Row (at **)' +
'\n in FancyRow (at **)' +
'\n in table (at **)' +
'\n in Table (at **)' +
'\n in FancyTable (at **)' +
'\n in Viz2 (at **)',
);
ReactTestUtils.renderIntoDocument(<FancyTable><FancyRow /></FancyTable>);
@@ -1554,14 +1502,12 @@ describe('ReactDOMComponent', () => {
expectDev(
normalizeCodeLocInfo(console.error.calls.argsFor(2)[0]),
).toContain(
ReactDOMFeatureFlags.useFiber
? '\n in tr (at **)' +
'\n in Row (at **)' +
'\n in FancyRow (at **)' +
'\n in table (at **)' +
'\n in Table (at **)' +
'\n in FancyTable (at **)'
: 'See FancyTable > Table > table > FancyRow > Row > tr.',
'\n in tr (at **)' +
'\n in Row (at **)' +
'\n in FancyRow (at **)' +
'\n in table (at **)' +
'\n in Table (at **)' +
'\n in FancyTable (at **)',
);
ReactTestUtils.renderIntoDocument(<table><FancyRow /></table>);
@@ -1569,12 +1515,10 @@ describe('ReactDOMComponent', () => {
expectDev(
normalizeCodeLocInfo(console.error.calls.argsFor(3)[0]),
).toContain(
ReactDOMFeatureFlags.useFiber
? '\n in tr (at **)' +
'\n in Row (at **)' +
'\n in FancyRow (at **)' +
'\n in table (at **)'
: 'See table > FancyRow > Row > tr.',
'\n in tr (at **)' +
'\n in Row (at **)' +
'\n in FancyRow (at **)' +
'\n in table (at **)',
);
ReactTestUtils.renderIntoDocument(<FancyTable><tr /></FancyTable>);
@@ -1582,12 +1526,10 @@ describe('ReactDOMComponent', () => {
expectDev(
normalizeCodeLocInfo(console.error.calls.argsFor(4)[0]),
).toContain(
ReactDOMFeatureFlags.useFiber
? '\n in tr (at **)' +
'\n in table (at **)' +
'\n in Table (at **)' +
'\n in FancyTable (at **)'
: 'See FancyTable > Table > table > tr.',
'\n in tr (at **)' +
'\n in table (at **)' +
'\n in Table (at **)' +
'\n in FancyTable (at **)',
);
class Link extends React.Component {
@@ -1601,13 +1543,11 @@ describe('ReactDOMComponent', () => {
expectDev(
normalizeCodeLocInfo(console.error.calls.argsFor(5)[0]),
).toContain(
ReactDOMFeatureFlags.useFiber
? '\n in a (at **)' +
'\n in Link (at **)' +
'\n in div (at **)' +
'\n in a (at **)' +
'\n in Link (at **)'
: 'See Link > a > ... > Link > a.',
'\n in a (at **)' +
'\n in Link (at **)' +
'\n in div (at **)' +
'\n in a (at **)' +
'\n in Link (at **)',
);
});
@@ -2294,42 +2234,37 @@ describe('ReactDOMComponent', () => {
});
});
if (ReactDOMFeatureFlags.useFiber) {
// This is currently broken (and has been broken for a while).
// We had a fix based on reading namespace, but it was too convoluted.
// TODO: a proper fix that would happen at the diffing stage.
describe('Hyphenated SVG elements', function() {
it('the font-face element is not a custom element', function() {
spyOn(console, 'error');
var el = ReactTestUtils.renderIntoDocument(
<svg><font-face x-height={false} /></svg>,
);
describe('Hyphenated SVG elements', function() {
it('the font-face element is not a custom element', function() {
spyOn(console, 'error');
var el = ReactTestUtils.renderIntoDocument(
<svg><font-face x-height={false} /></svg>,
);
expect(el.querySelector('font-face').hasAttribute('x-height')).toBe(
false,
);
expect(el.querySelector('font-face').hasAttribute('x-height')).toBe(
false,
);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
);
});
it('the font-face element does not allow unknown boolean values', function() {
spyOn(console, 'error');
var el = ReactTestUtils.renderIntoDocument(
<svg><font-face whatever={false} /></svg>,
);
expect(el.querySelector('font-face').hasAttribute('whatever')).toBe(
false,
);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Warning: Received `false` for non-boolean attribute `whatever`.',
);
});
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Warning: Invalid DOM property `x-height`. Did you mean `xHeight`',
);
});
}
it('the font-face element does not allow unknown boolean values', function() {
spyOn(console, 'error');
var el = ReactTestUtils.renderIntoDocument(
<svg><font-face whatever={false} /></svg>,
);
expect(el.querySelector('font-face').hasAttribute('whatever')).toBe(
false,
);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Warning: Received `false` for non-boolean attribute `whatever`.',
);
});
});
});

View File

@@ -9,8 +9,6 @@
'use strict';
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
describe('ReactDOMComponentTree', () => {
var React;
var ReactDOM;
@@ -21,11 +19,7 @@ describe('ReactDOMComponentTree', () => {
var container = document.createElement('div');
// Force server-rendering path:
container.innerHTML = ReactDOMServer.renderToString(elt);
if (ReactDOMFeatureFlags.useFiber) {
return ReactDOM.hydrate(elt, container);
} else {
return ReactDOM.render(elt, container);
}
return ReactDOM.hydrate(elt, container);
}
function getTypeOf(instance) {

View File

@@ -9,8 +9,6 @@
'use strict';
let ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
let ExecutionEnvironment;
let PropTypes;
let React;
@@ -21,7 +19,6 @@ let ReactTestUtils;
const stream = require('stream');
const TEXT_NODE_TYPE = 3;
const COMMENT_NODE_TYPE = 8;
// Helper functions for rendering tests
// ====================================
@@ -29,7 +26,7 @@ const COMMENT_NODE_TYPE = 8;
// promisified version of ReactDOM.render()
function asyncReactDOMRender(reactElement, domElement, forceHydrate) {
return new Promise(resolve => {
if (forceHydrate && ReactDOMFeatureFlags.useFiber) {
if (forceHydrate) {
ReactDOM.hydrate(reactElement, domElement);
} else {
ReactDOM.render(reactElement, domElement);
@@ -213,9 +210,7 @@ const clientRenderOnBadMarkup = async (element, errorCount = 0) => {
// as that will not work in the server string scenario.
function itRenders(desc, testFn) {
it(`renders ${desc} with server string render`, () => testFn(serverRender));
if (ReactDOMFeatureFlags.useFiber) {
it(`renders ${desc} with server stream render`, () => testFn(streamRender));
}
it(`renders ${desc} with server stream render`, () => testFn(streamRender));
itClientRenders(desc, testFn);
}
@@ -324,7 +319,6 @@ function resetModules() {
PropTypes = require('prop-types');
React = require('react');
ReactDOM = require('react-dom');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactTestUtils = require('react-dom/test-utils');
// Now we reset the modules again to load the server renderer.
@@ -358,97 +352,95 @@ describe('ReactDOMServerIntegration', () => {
expect(e.firstChild.tagName).toBe('BR');
});
if (ReactDOMFeatureFlags.useFiber) {
itRenders('a string', async render => {
let e = await render('Hello');
expect(e.nodeType).toBe(3);
expect(e.nodeValue).toMatch('Hello');
});
itRenders('a string', async render => {
let e = await render('Hello');
expect(e.nodeType).toBe(3);
expect(e.nodeValue).toMatch('Hello');
});
itRenders('a number', async render => {
let e = await render(42);
expect(e.nodeType).toBe(3);
expect(e.nodeValue).toMatch('42');
});
itRenders('a number', async render => {
let e = await render(42);
expect(e.nodeType).toBe(3);
expect(e.nodeValue).toMatch('42');
});
itRenders('an array with one child', async render => {
let e = await render([<div key={1}>text1</div>]);
let parent = e.parentNode;
expect(parent.childNodes[0].tagName).toBe('DIV');
});
itRenders('an array with one child', async render => {
let e = await render([<div key={1}>text1</div>]);
let parent = e.parentNode;
expect(parent.childNodes[0].tagName).toBe('DIV');
});
itRenders('an array with several children', async render => {
let Header = props => {
return <p>header</p>;
};
let Footer = props => {
return [<h2 key={1}>footer</h2>, <h3 key={2}>about</h3>];
};
let e = await render([
<div key={1}>text1</div>,
<span key={2}>text2</span>,
<Header key={3} />,
<Footer key={4} />,
]);
let parent = e.parentNode;
expect(parent.childNodes[0].tagName).toBe('DIV');
expect(parent.childNodes[1].tagName).toBe('SPAN');
expect(parent.childNodes[2].tagName).toBe('P');
expect(parent.childNodes[3].tagName).toBe('H2');
expect(parent.childNodes[4].tagName).toBe('H3');
});
itRenders('an array with several children', async render => {
let Header = props => {
return <p>header</p>;
};
let Footer = props => {
return [<h2 key={1}>footer</h2>, <h3 key={2}>about</h3>];
};
let e = await render([
<div key={1}>text1</div>,
<span key={2}>text2</span>,
<Header key={3} />,
<Footer key={4} />,
]);
let parent = e.parentNode;
expect(parent.childNodes[0].tagName).toBe('DIV');
expect(parent.childNodes[1].tagName).toBe('SPAN');
expect(parent.childNodes[2].tagName).toBe('P');
expect(parent.childNodes[3].tagName).toBe('H2');
expect(parent.childNodes[4].tagName).toBe('H3');
});
itRenders('a nested array', async render => {
let e = await render([
[<div key={1}>text1</div>],
<span key={1}>text2</span>,
[[[null, <p key={1} />], false]],
]);
let parent = e.parentNode;
expect(parent.childNodes[0].tagName).toBe('DIV');
expect(parent.childNodes[1].tagName).toBe('SPAN');
expect(parent.childNodes[2].tagName).toBe('P');
});
itRenders('a nested array', async render => {
let e = await render([
[<div key={1}>text1</div>],
<span key={1}>text2</span>,
[[[null, <p key={1} />], false]],
]);
let parent = e.parentNode;
expect(parent.childNodes[0].tagName).toBe('DIV');
expect(parent.childNodes[1].tagName).toBe('SPAN');
expect(parent.childNodes[2].tagName).toBe('P');
});
itRenders('an iterable', async render => {
const threeDivIterable = {
'@@iterator': function() {
var i = 0;
return {
next: function() {
if (i++ < 3) {
return {value: <div key={i} />, done: false};
} else {
return {value: undefined, done: true};
}
},
};
},
};
let e = await render(threeDivIterable);
let parent = e.parentNode;
expect(parent.childNodes.length).toBe(3);
expect(parent.childNodes[0].tagName).toBe('DIV');
expect(parent.childNodes[1].tagName).toBe('DIV');
expect(parent.childNodes[2].tagName).toBe('DIV');
});
itRenders('an iterable', async render => {
const threeDivIterable = {
'@@iterator': function() {
var i = 0;
return {
next: function() {
if (i++ < 3) {
return {value: <div key={i} />, done: false};
} else {
return {value: undefined, done: true};
}
},
};
},
};
let e = await render(threeDivIterable);
let parent = e.parentNode;
expect(parent.childNodes.length).toBe(3);
expect(parent.childNodes[0].tagName).toBe('DIV');
expect(parent.childNodes[1].tagName).toBe('DIV');
expect(parent.childNodes[2].tagName).toBe('DIV');
});
itRenders('emptyish values', async render => {
let e = await render(0);
expect(e.nodeType).toBe(TEXT_NODE_TYPE);
expect(e.nodeValue).toMatch('0');
itRenders('emptyish values', async render => {
let e = await render(0);
expect(e.nodeType).toBe(TEXT_NODE_TYPE);
expect(e.nodeValue).toMatch('0');
// Empty string is special because client renders a node
// but server returns empty HTML. So we compare parent text.
expect((await render(<div>{''}</div>)).textContent).toBe('');
// Empty string is special because client renders a node
// but server returns empty HTML. So we compare parent text.
expect((await render(<div>{''}</div>)).textContent).toBe('');
expect(await render([])).toBe(null);
expect(await render(false)).toBe(null);
expect(await render(true)).toBe(null);
expect(await render(undefined)).toBe(null);
expect(await render([[[false]], undefined])).toBe(null);
});
}
expect(await render([])).toBe(null);
expect(await render(false)).toBe(null);
expect(await render(true)).toBe(null);
expect(await render(undefined)).toBe(null);
expect(await render([[[false]], undefined])).toBe(null);
});
});
describe('property to attribute mapping', function() {
@@ -999,23 +991,7 @@ describe('ReactDOMServerIntegration', () => {
}
function expectTextNode(node, text) {
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, a React text node is just a DOM text node.
expectNode(node, TEXT_NODE_TYPE, text);
} else {
// with Stack, a React text node is a DOM text node surrounded by
// react-text comment nodes.
expectNode(node, COMMENT_NODE_TYPE, / react-text: [0-9]+ /);
if (text.length > 0) {
node = node.nextSibling;
expectNode(node, TEXT_NODE_TYPE, text);
}
expectNode(node.nextSibling, COMMENT_NODE_TYPE, / \/react-text /);
}
}
function expectEmptyNode(node) {
expectNode(node, COMMENT_NODE_TYPE, / react-empty: [0-9]+ /);
expectNode(node, TEXT_NODE_TYPE, text);
}
describe('text children', function() {
@@ -1040,74 +1016,45 @@ describe('ReactDOMServerIntegration', () => {
itRenders('a div with multiple empty text children', async render => {
const e = await render(<div>{''}{''}{''}</div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are just three separate text node children,
// each of which is blank.
if (render === serverRender || render === streamRender) {
// For plain server markup result we should have no text nodes if
// they're all empty.
expect(e.childNodes.length).toBe(0);
expect(e.textContent).toBe('');
} else {
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], '');
expectTextNode(e.childNodes[1], '');
expectTextNode(e.childNodes[2], '');
}
if (render === serverRender || render === streamRender) {
// For plain server markup result we should have no text nodes if
// they're all empty.
expect(e.childNodes.length).toBe(0);
expect(e.textContent).toBe('');
} else {
// with Stack, there are six react-text comment nodes.
expect(e.childNodes.length).toBe(6);
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], '');
expectTextNode(e.childNodes[1], '');
expectTextNode(e.childNodes[2], '');
expectTextNode(e.childNodes[4], '');
}
});
itRenders('a div with multiple whitespace children', async render => {
const e = await render(<div>{' '}{' '}{' '}</div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are normally just three text nodes
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
// For plain server markup result we have comments between.
// If we're able to hydrate, they remain.
expect(e.childNodes.length).toBe(5);
expectTextNode(e.childNodes[0], ' ');
expectTextNode(e.childNodes[2], ' ');
expectTextNode(e.childNodes[4], ' ');
} else {
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], ' ');
expectTextNode(e.childNodes[1], ' ');
expectTextNode(e.childNodes[2], ' ');
}
} else {
// with Stack, each of the text nodes is surrounded by react-text
// comment nodes, making 9 nodes in total.
expect(e.childNodes.length).toBe(9);
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
// For plain server markup result we have comments between.
// If we're able to hydrate, they remain.
expect(e.childNodes.length).toBe(5);
expectTextNode(e.childNodes[0], ' ');
expectTextNode(e.childNodes[3], ' ');
expectTextNode(e.childNodes[6], ' ');
expectTextNode(e.childNodes[2], ' ');
expectTextNode(e.childNodes[4], ' ');
} else {
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], ' ');
expectTextNode(e.childNodes[1], ' ');
expectTextNode(e.childNodes[2], ' ');
}
});
itRenders('a div with text sibling to a node', async render => {
const e = await render(<div>Text<span>More Text</span></div>);
let spanNode;
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are only two children, the "Text" text node and
// the span element.
expect(e.childNodes.length).toBe(2);
spanNode = e.childNodes[1];
} else {
// with Stack, there are four children, a "Text" text node surrounded
// by react-text comment nodes, and the span element.
expect(e.childNodes.length).toBe(4);
spanNode = e.childNodes[3];
}
expect(e.childNodes.length).toBe(2);
spanNode = e.childNodes[1];
expectTextNode(e.childNodes[0], 'Text');
expect(spanNode.tagName).toBe('SPAN');
expect(spanNode.childNodes.length).toBe(1);
@@ -1130,72 +1077,44 @@ describe('ReactDOMServerIntegration', () => {
itRenders('a leading blank child with a text sibling', async render => {
const e = await render(<div>{''}foo</div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are just two text nodes.
if (render === serverRender || render === streamRender) {
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
} else {
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], '');
expectTextNode(e.childNodes[1], 'foo');
}
if (render === serverRender || render === streamRender) {
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
} else {
// with Stack, there are five nodes: two react-text comment nodes
// without any text between them, and the text node foo surrounded
// by react-text comment nodes.
expect(e.childNodes.length).toBe(5);
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], '');
expectTextNode(e.childNodes[2], 'foo');
expectTextNode(e.childNodes[1], 'foo');
}
});
itRenders('a trailing blank child with a text sibling', async render => {
const e = await render(<div>foo{''}</div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are just two text nodes.
if (render === serverRender || render === streamRender) {
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
} else {
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[1], '');
}
} else {
// with Stack, there are five nodes: the text node foo surrounded
// by react-text comment nodes, and two react-text comment nodes
// without any text between them.
expect(e.childNodes.length).toBe(5);
// with Fiber, there are just two text nodes.
if (render === serverRender || render === streamRender) {
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[3], '');
} else {
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[1], '');
}
});
itRenders('an element with two text children', async render => {
const e = await render(<div>{'foo'}{'bar'}</div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are just two text nodes.
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
// In the server render output there's a comment between them.
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[2], 'bar');
} else {
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[1], 'bar');
}
} else {
// with Stack, there are six nodes: two text nodes, each surrounded
// by react-text comment nodes.
expect(e.childNodes.length).toBe(6);
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
// In the server render output there's a comment between them.
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[3], 'bar');
expectTextNode(e.childNodes[2], 'bar');
} else {
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[1], 'bar');
}
});
});
@@ -1214,28 +1133,20 @@ describe('ReactDOMServerIntegration', () => {
itRenders('an element with number and text children', async render => {
const e = await render(<div>{'foo'}{40}</div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are just two text nodes.
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
// In the server markup there's a comment between.
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[2], '40');
} else {
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[1], '40');
}
} else {
// with Stack, there are six nodes: two text nodes, each surrounded
// by react-text comment nodes.
expect(e.childNodes.length).toBe(6);
// with Fiber, there are just two text nodes.
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
// In the server markup there's a comment between.
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[3], '40');
expectTextNode(e.childNodes[2], '40');
} else {
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], 'foo');
expectTextNode(e.childNodes[1], '40');
}
});
});
@@ -1259,54 +1170,25 @@ describe('ReactDOMServerIntegration', () => {
itRenders('a null component children as empty', async render => {
const NullComponent = () => null;
const e = await render(<div><NullComponent /></div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, an empty component results in no markup.
expect(e.childNodes.length).toBe(0);
} else {
// with Stack, an empty component results in one react-empty comment
// node.
expect(e.childNodes.length).toBe(1);
expectEmptyNode(e.firstChild);
}
expect(e.childNodes.length).toBe(0);
});
itRenders('null children as blank', async render => {
const e = await render(<div>{null}foo</div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there is just one text node.
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
} else {
// with Stack, there's a text node surronded by react-text comment nodes.
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], 'foo');
}
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
});
itRenders('false children as blank', async render => {
const e = await render(<div>{false}foo</div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there is just one text node.
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
} else {
// with Stack, there's a text node surronded by react-text comment nodes.
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], 'foo');
}
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
});
itRenders('null and false children together as blank', async render => {
const e = await render(<div>{false}{null}foo{null}{false}</div>);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there is just one text node.
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
} else {
// with Stack, there's a text node surronded by react-text comment nodes.
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], 'foo');
}
expect(e.childNodes.length).toBe(1);
expectTextNode(e.childNodes[0], 'foo');
});
itRenders('only null and false children as blank', async render => {
@@ -1522,21 +1404,10 @@ describe('ReactDOMServerIntegration', () => {
);
expect(e.id).toBe('parent');
let child1, child2, textNode;
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are three children: the child1 element, a
// single space text node, and the child2 element.
expect(e.childNodes.length).toBe(3);
child1 = e.childNodes[0];
textNode = e.childNodes[1];
child2 = e.childNodes[2];
} else {
// with Stack, there are five children: the child1 element, a single
// space surrounded by react-text comments, and the child2 element.
expect(e.childNodes.length).toBe(5);
child1 = e.childNodes[0];
textNode = e.childNodes[1];
child2 = e.childNodes[4];
}
expect(e.childNodes.length).toBe(3);
child1 = e.childNodes[0];
textNode = e.childNodes[1];
child2 = e.childNodes[2];
expect(child1.id).toBe('child1');
expect(child1.childNodes.length).toBe(0);
expectTextNode(textNode, ' ');
@@ -1551,22 +1422,10 @@ describe('ReactDOMServerIntegration', () => {
// prettier-ignore
const e = await render(<div id="parent"> <div id="child" /> </div>); // eslint-disable-line no-multi-spaces
let textNode1, child, textNode2;
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are three children: a one-space text node, the
// child element, and a two-space text node.
expect(e.childNodes.length).toBe(3);
textNode1 = e.childNodes[0];
child = e.childNodes[1];
textNode2 = e.childNodes[2];
} else {
// with Stack, there are 7 children: a one-space text node surrounded
// by react-text comments, the child element, and a two-space text node
// surrounded by react-text comments.
expect(e.childNodes.length).toBe(7);
textNode1 = e.childNodes[0];
child = e.childNodes[3];
textNode2 = e.childNodes[4];
}
expect(e.childNodes.length).toBe(3);
textNode1 = e.childNodes[0];
child = e.childNodes[1];
textNode2 = e.childNodes[2];
expect(e.id).toBe('parent');
expectTextNode(textNode1, ' ');
expect(child.id).toBe('child');
@@ -1575,33 +1434,31 @@ describe('ReactDOMServerIntegration', () => {
},
);
if (ReactDOMFeatureFlags.useFiber) {
itRenders('a composite with multiple children', async render => {
const Component = props => props.children;
const e = await render(
<Component>{['a', 'b', [undefined], [[false, 'c']]]}</Component>,
);
itRenders('a composite with multiple children', async render => {
const Component = props => props.children;
const e = await render(
<Component>{['a', 'b', [undefined], [[false, 'c']]]}</Component>,
);
let parent = e.parentNode;
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
// For plain server markup result we have comments between.
// If we're able to hydrate, they remain.
expect(parent.childNodes.length).toBe(5);
expectTextNode(parent.childNodes[0], 'a');
expectTextNode(parent.childNodes[2], 'b');
expectTextNode(parent.childNodes[4], 'c');
} else {
expect(parent.childNodes.length).toBe(3);
expectTextNode(parent.childNodes[0], 'a');
expectTextNode(parent.childNodes[1], 'b');
expectTextNode(parent.childNodes[2], 'c');
}
});
}
let parent = e.parentNode;
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
// For plain server markup result we have comments between.
// If we're able to hydrate, they remain.
expect(parent.childNodes.length).toBe(5);
expectTextNode(parent.childNodes[0], 'a');
expectTextNode(parent.childNodes[2], 'b');
expectTextNode(parent.childNodes[4], 'c');
} else {
expect(parent.childNodes.length).toBe(3);
expectTextNode(parent.childNodes[0], 'a');
expectTextNode(parent.childNodes[1], 'b');
expectTextNode(parent.childNodes[2], 'c');
}
});
});
describe('escaping >, <, and &', function() {
@@ -1615,27 +1472,18 @@ describe('ReactDOMServerIntegration', () => {
const e = await render(
<div>{'<span>Text1&quot;</span>'}{'<span>Text2&quot;</span>'}</div>,
);
if (ReactDOMFeatureFlags.useFiber) {
// with Fiber, there are just two text nodes.
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
expectTextNode(e.childNodes[2], '<span>Text2&quot;</span>');
} else {
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
expectTextNode(e.childNodes[1], '<span>Text2&quot;</span>');
}
} else {
// with Stack there are six nodes: two text nodes each surrounded by
// two react-text comment nodes.
expect(e.childNodes.length).toBe(6);
if (
render === serverRender ||
render === clientRenderOnServerString ||
render === streamRender
) {
expect(e.childNodes.length).toBe(3);
expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
expectTextNode(e.childNodes[3], '<span>Text2&quot;</span>');
expectTextNode(e.childNodes[2], '<span>Text2&quot;</span>');
} else {
expect(e.childNodes.length).toBe(2);
expectTextNode(e.childNodes[0], '<span>Text1&quot;</span>');
expectTextNode(e.childNodes[1], '<span>Text2&quot;</span>');
}
});
});
@@ -1647,12 +1495,9 @@ describe('ReactDOMServerIntegration', () => {
const UndefinedComponent = () => undefined;
await render(<UndefinedComponent />, 1);
},
ReactDOMFeatureFlags.useFiber
? 'UndefinedComponent(...): Nothing was returned from render. ' +
'This usually means a return statement is missing. Or, to ' +
'render nothing, return null.'
: 'A valid React element (or null) must be returned. ' +
'You may have returned undefined, an array or some other invalid object.',
'UndefinedComponent(...): Nothing was returned from render. ' +
'This usually means a return statement is missing. Or, to ' +
'render nothing, return null.',
);
itThrowsWhenRendering(
@@ -1665,12 +1510,9 @@ describe('ReactDOMServerIntegration', () => {
}
await render(<UndefinedComponent />, 1);
},
ReactDOMFeatureFlags.useFiber
? 'UndefinedComponent(...): Nothing was returned from render. ' +
'This usually means a return statement is missing. Or, to ' +
'render nothing, return null.'
: 'A valid React element (or null) must be returned. ' +
'You may have returned undefined, an array or some other invalid object.',
'UndefinedComponent(...): Nothing was returned from render. ' +
'This usually means a return statement is missing. Or, to ' +
'render nothing, return null.',
);
itThrowsWhenRendering(
@@ -1679,12 +1521,9 @@ describe('ReactDOMServerIntegration', () => {
const ObjectComponent = () => ({x: 123});
await render(<ObjectComponent />, 1);
},
ReactDOMFeatureFlags.useFiber
? 'Objects are not valid as a React child (found: object with keys ' +
'{x}). If you meant to render a collection of children, use ' +
'an array instead.'
: 'A valid React element (or null) must be returned. ' +
'You may have returned undefined, an array or some other invalid object.',
'Objects are not valid as a React child (found: object with keys ' +
'{x}). If you meant to render a collection of children, use ' +
'an array instead.',
);
itThrowsWhenRendering(
@@ -1697,12 +1536,9 @@ describe('ReactDOMServerIntegration', () => {
}
await render(<ObjectComponent />, 1);
},
ReactDOMFeatureFlags.useFiber
? 'Objects are not valid as a React child (found: object with keys ' +
'{x}). If you meant to render a collection of children, use ' +
'an array instead.'
: 'A valid React element (or null) must be returned. ' +
'You may have returned undefined, an array or some other invalid object.',
'Objects are not valid as a React child (found: object with keys ' +
'{x}). If you meant to render a collection of children, use ' +
'an array instead.',
);
itThrowsWhenRendering(
@@ -1710,11 +1546,9 @@ describe('ReactDOMServerIntegration', () => {
async render => {
await render({x: 123});
},
ReactDOMFeatureFlags.useFiber
? 'Objects are not valid as a React child (found: object with keys ' +
'{x}). If you meant to render a collection of children, use ' +
'an array instead.'
: 'Invalid component element.',
'Objects are not valid as a React child (found: object with keys ' +
'{x}). If you meant to render a collection of children, use ' +
'an array instead.',
);
});
});
@@ -2734,12 +2568,7 @@ describe('ReactDOMServerIntegration', () => {
));
it('can distinguish an empty component from an empty text component', () =>
(ReactDOMFeatureFlags.useFiber
? expectMarkupMatch
: expectMarkupMismatch)(
<div><EmptyComponent /></div>,
<div>{''}</div>,
));
expectMarkupMatch(<div><EmptyComponent /></div>, <div>{''}</div>));
});
// Markup Mismatches: misc

View File

@@ -12,7 +12,6 @@
var React;
var ReactDOM;
var ReactDOMServer;
var ReactDOMFeatureFlags;
// In standard React, TextComponent keeps track of different Text templates
// using comments. However, in React Fiber, those comments are not outputted due
@@ -28,7 +27,6 @@ describe('ReactDOMTextComponent', () => {
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
});
it('updates a mounted text component in place', () => {
@@ -117,11 +115,7 @@ describe('ReactDOMTextComponent', () => {
var reactEl = <div>{'foo'}{'bar'}{'baz'}</div>;
el.innerHTML = ReactDOMServer.renderToString(reactEl);
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(reactEl, el);
} else {
ReactDOM.render(reactEl, el);
}
ReactDOM.hydrate(reactEl, el);
expect(el.textContent).toBe('foobarbaz');
ReactDOM.unmountComponentAtNode(el);
@@ -129,11 +123,7 @@ describe('ReactDOMTextComponent', () => {
reactEl = <div>{''}{''}{''}</div>;
el.innerHTML = ReactDOMServer.renderToString(reactEl);
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(reactEl, el);
} else {
ReactDOM.render(reactEl, el);
}
ReactDOM.hydrate(reactEl, el);
expect(el.textContent).toBe('');
});

View File

@@ -9,7 +9,6 @@
'use strict';
const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
const {COMMENT_NODE} = require('HTMLNodeType');
const invariant = require('invariant');
@@ -66,24 +65,22 @@ describe('ReactMount', () => {
});
});
if (ReactDOMFeatureFlags.useFiber) {
it('warns when given a factory', () => {
spyOn(console, 'error');
class Component extends React.Component {
render() {
return <div />;
}
it('warns when given a factory', () => {
spyOn(console, 'error');
class Component extends React.Component {
render() {
return <div />;
}
}
ReactTestUtils.renderIntoDocument(Component);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Functions are not valid as a React child. ' +
'This may happen if you return a Component instead of <Component /> from render. ' +
'Or maybe you meant to call this function rather than return it.',
);
});
}
ReactTestUtils.renderIntoDocument(Component);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Functions are not valid as a React child. ' +
'This may happen if you return a Component instead of <Component /> from render. ' +
'Or maybe you meant to call this function rather than return it.',
);
});
it('should render different components in same root', () => {
var container = document.createElement('container');
@@ -144,21 +141,11 @@ describe('ReactMount', () => {
container.innerHTML = ReactDOMServer.renderToString(<div />) + ' ';
spyOn(console, 'error');
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(<div />, container);
} else {
ReactDOM.render(<div />, container);
}
ReactDOM.hydrate(<div />, container);
expectDev(console.error.calls.count()).toBe(1);
if (ReactDOMFeatureFlags.useFiber) {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Did not expect server HTML to contain the text node " " in <container>.',
);
} else {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Target node has markup rendered by React, but there are unrelated nodes as well.',
);
}
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Did not expect server HTML to contain the text node " " in <container>.',
);
});
it('should warn if mounting into right padded rendered markup', () => {
@@ -166,21 +153,11 @@ describe('ReactMount', () => {
container.innerHTML = ' ' + ReactDOMServer.renderToString(<div />);
spyOn(console, 'error');
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(<div />, container);
} else {
ReactDOM.render(<div />, container);
}
ReactDOM.hydrate(<div />, container);
expectDev(console.error.calls.count()).toBe(1);
if (ReactDOMFeatureFlags.useFiber) {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Did not expect server HTML to contain the text node " " in <container>.',
);
} else {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Target node has markup rendered by React, but there are unrelated nodes as well.',
);
}
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Did not expect server HTML to contain the text node " " in <container>.',
);
});
it('should not warn if mounting into non-empty node', () => {
@@ -213,29 +190,15 @@ describe('ReactMount', () => {
div.innerHTML = markup;
spyOn(console, 'error');
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.hydrate(
<div>This markup contains an nbsp entity: &nbsp; client text</div>,
div,
);
} else {
ReactDOM.render(
<div>This markup contains an nbsp entity: &nbsp; client text</div>,
div,
);
}
ReactDOM.hydrate(
<div>This markup contains an nbsp entity: &nbsp; client text</div>,
div,
);
expectDev(console.error.calls.count()).toBe(1);
if (ReactDOMFeatureFlags.useFiber) {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Server: "This markup contains an nbsp entity:   server text" ' +
'Client: "This markup contains an nbsp entity:   client text"',
);
} else {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
' (client) nbsp entity: &nbsp; client text</div>\n' +
' (server) nbsp entity: &nbsp; server text</div>',
);
}
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Server: "This markup contains an nbsp entity:   server text" ' +
'Client: "This markup contains an nbsp entity:   client text"',
);
});
if (WebComponents !== undefined) {
@@ -388,41 +351,39 @@ describe('ReactMount', () => {
expect(container2.textContent).toEqual('a!');
});
if (ReactDOMFeatureFlags.useFiber) {
describe('mount point is a comment node', () => {
let containerDiv;
let mountPoint;
describe('mount point is a comment node', () => {
let containerDiv;
let mountPoint;
beforeEach(() => {
containerDiv = document.createElement('div');
containerDiv.innerHTML = 'A<!-- react-mount-point-unstable -->B';
mountPoint = containerDiv.childNodes[1];
invariant(mountPoint.nodeType === COMMENT_NODE, 'Expected comment');
});
it('renders at a comment node', () => {
function Char(props) {
return props.children;
}
function list(chars) {
return chars.split('').map(c => <Char key={c}>{c}</Char>);
}
ReactDOM.render(list('aeiou'), mountPoint);
expect(containerDiv.innerHTML).toBe(
'Aaeiou<!-- react-mount-point-unstable -->B',
);
ReactDOM.render(list('yea'), mountPoint);
expect(containerDiv.innerHTML).toBe(
'Ayea<!-- react-mount-point-unstable -->B',
);
ReactDOM.render(list(''), mountPoint);
expect(containerDiv.innerHTML).toBe(
'A<!-- react-mount-point-unstable -->B',
);
});
beforeEach(() => {
containerDiv = document.createElement('div');
containerDiv.innerHTML = 'A<!-- react-mount-point-unstable -->B';
mountPoint = containerDiv.childNodes[1];
invariant(mountPoint.nodeType === COMMENT_NODE, 'Expected comment');
});
}
it('renders at a comment node', () => {
function Char(props) {
return props.children;
}
function list(chars) {
return chars.split('').map(c => <Char key={c}>{c}</Char>);
}
ReactDOM.render(list('aeiou'), mountPoint);
expect(containerDiv.innerHTML).toBe(
'Aaeiou<!-- react-mount-point-unstable -->B',
);
ReactDOM.render(list('yea'), mountPoint);
expect(containerDiv.innerHTML).toBe(
'Ayea<!-- react-mount-point-unstable -->B',
);
ReactDOM.render(list(''), mountPoint);
expect(containerDiv.innerHTML).toBe(
'A<!-- react-mount-point-unstable -->B',
);
});
});
});

View File

@@ -15,15 +15,6 @@ var ReactDOMServer;
var getTestDocument;
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var UNMOUNT_INVARIANT_MESSAGE =
'<html> tried to unmount. ' +
'Because of cross-browser quirks it is impossible to unmount some ' +
'top-level components (eg <html>, <head>, and <body>) reliably and ' +
'efficiently. To fix this, have a single top-level component that ' +
'never unmounts render these elements.';
describe('rendering React components at document', () => {
beforeEach(() => {
jest.resetModules();
@@ -36,16 +27,12 @@ describe('rendering React components at document', () => {
describe('with old implicit hydration API', () => {
function expectDeprecationWarningWithFiber() {
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.',
);
}
it('should be able to adopt server markup', () => {
@@ -101,17 +88,9 @@ describe('rendering React components at document', () => {
ReactDOM.render(<Root />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
if (ReactDOMFeatureFlags.useFiber) {
// In Fiber this actually works. It might not be a good idea though.
ReactDOM.unmountComponentAtNode(testDocument);
expect(testDocument.firstChild).toBe(null);
} else {
expect(function() {
ReactDOM.unmountComponentAtNode(testDocument);
}).toThrowError(UNMOUNT_INVARIANT_MESSAGE);
expect(testDocument.body.innerHTML).toBe('Hello world');
}
// In Fiber this actually works. It might not be a good idea though.
ReactDOM.unmountComponentAtNode(testDocument);
expect(testDocument.firstChild).toBe(null);
expectDeprecationWarningWithFiber();
});
@@ -152,23 +131,12 @@ describe('rendering React components at document', () => {
var testDocument = getTestDocument(markup);
ReactDOM.render(<Component />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
// Reactive update
if (ReactDOMFeatureFlags.useFiber) {
// This works but is probably a bad idea.
ReactDOM.render(<Component2 />, testDocument);
expect(testDocument.body.innerHTML).toBe('Goodbye world');
} else {
expect(function() {
ReactDOM.render(<Component2 />, testDocument);
}).toThrowError(UNMOUNT_INVARIANT_MESSAGE);
expect(testDocument.body.innerHTML).toBe('Hello world');
}
// This works but is probably a bad idea.
ReactDOM.render(<Component2 />, testDocument);
expect(testDocument.body.innerHTML).toBe('Goodbye world');
expectDeprecationWarningWithFiber();
});
@@ -231,38 +199,20 @@ describe('rendering React components at document', () => {
);
var testDocument = getTestDocument(markup);
if (ReactDOMFeatureFlags.useFiber) {
spyOn(console, 'warn');
spyOn(console, 'error');
ReactDOM.render(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
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.',
);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Warning: Text content did not match.',
);
} else {
expect(function() {
// Notice the text is different!
ReactDOM.render(<Component text="Hello world" />, testDocument);
}).toThrowError(
"You're trying to render a component to the document using " +
'server rendering but the checksum was invalid. This usually ' +
'means you rendered a different component type or props on ' +
'the client from the one on the server, or your render() methods ' +
'are impure. React cannot handle this case due to cross-browser ' +
'quirks by rendering at the document root. You should look for ' +
'environment dependent code in your components and ensure ' +
'the props are the same client and server side:\n' +
' (client) dy data-reactid="4">Hello world</body></\n' +
' (server) dy data-reactid="4">Goodbye world</body>',
);
}
spyOn(console, 'warn');
spyOn(console, 'error');
ReactDOM.render(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
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.',
);
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Warning: Text content did not match.',
);
});
it('should throw on full document render w/ no markup', () => {
@@ -283,19 +233,8 @@ describe('rendering React components at document', () => {
}
}
if (ReactDOMFeatureFlags.useFiber) {
ReactDOM.render(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
} else {
expect(function() {
ReactDOM.render(<Component />, testDocument);
}).toThrowError(
"You're trying to render a component to the document but you didn't " +
"use server rendering. We can't do this without using server " +
'rendering due to cross-browser quirks. See ' +
'ReactDOMServer.renderToString() for server rendering.',
);
}
ReactDOM.render(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
// We don't expect a warning about new hydration API here because
// we aren't sure if the user meant to hydrate or replace the document.
// We would see a warning if the document had React-rendered HTML in it.
@@ -323,221 +262,219 @@ describe('rendering React components at document', () => {
});
});
if (ReactDOMFeatureFlags.useFiber) {
describe('with new explicit hydration API', () => {
it('should be able to adopt server markup', () => {
class Root extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
{'Hello ' + this.props.hello}
</body>
</html>
);
}
describe('with new explicit hydration API', () => {
it('should be able to adopt server markup', () => {
class Root extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
{'Hello ' + this.props.hello}
</body>
</html>
);
}
}
var markup = ReactDOMServer.renderToString(<Root hello="world" />);
var testDocument = getTestDocument(markup);
var body = testDocument.body;
var markup = ReactDOMServer.renderToString(<Root hello="world" />);
var testDocument = getTestDocument(markup);
var body = testDocument.body;
ReactDOM.hydrate(<Root hello="world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
ReactDOM.hydrate(<Root hello="world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
ReactDOM.hydrate(<Root hello="moon" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello moon');
ReactDOM.hydrate(<Root hello="moon" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello moon');
expect(body === testDocument.body).toBe(true);
});
it('should not be able to unmount component from document node', () => {
class Root extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
Hello world
</body>
</html>
);
}
}
var markup = ReactDOMServer.renderToString(<Root />);
var testDocument = getTestDocument(markup);
ReactDOM.hydrate(<Root />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
// In Fiber this actually works. It might not be a good idea though.
ReactDOM.unmountComponentAtNode(testDocument);
expect(testDocument.firstChild).toBe(null);
});
it('should not be able to switch root constructors', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
Hello world
</body>
</html>
);
}
}
class Component2 extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
Goodbye world
</body>
</html>
);
}
}
var markup = ReactDOMServer.renderToString(<Component />);
var testDocument = getTestDocument(markup);
ReactDOM.hydrate(<Component />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
// This works but is probably a bad idea.
ReactDOM.hydrate(<Component2 />, testDocument);
expect(testDocument.body.innerHTML).toBe('Goodbye world');
});
it('should be able to mount into document', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
{this.props.text}
</body>
</html>
);
}
}
var markup = ReactDOMServer.renderToString(
<Component text="Hello world" />,
);
var testDocument = getTestDocument(markup);
ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
});
it('renders over an existing text child without throwing', () => {
spyOn(console, 'error');
const container = document.createElement('div');
container.textContent = 'potato';
ReactDOM.hydrate(<div>parsnip</div>, container);
expect(container.textContent).toBe('parsnip');
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Did not expect server HTML to contain the text node "potato" in <div>.',
);
});
it('should give helpful errors on state desync', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
{this.props.text}
</body>
</html>
);
}
}
var markup = ReactDOMServer.renderToString(
<Component text="Goodbye world" />,
);
var testDocument = getTestDocument(markup);
spyOn(console, 'error');
ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Warning: Text content did not match.',
);
});
it('should render w/ no markup to full document', () => {
spyOn(console, 'error');
var testDocument = getTestDocument();
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
{this.props.text}
</body>
</html>
);
}
}
ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
// getTestDocument() has an extra <meta> that we didn't render.
'Did not expect server HTML to contain a <meta> in <head>.',
);
});
it('supports findDOMNode on full-page components', () => {
var tree = (
<html>
<head>
<title>Hello World</title>
</head>
<body>
Hello world
</body>
</html>
);
var markup = ReactDOMServer.renderToString(tree);
var testDocument = getTestDocument(markup);
var component = ReactDOM.hydrate(tree, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
expect(ReactDOM.findDOMNode(component).tagName).toBe('HTML');
});
expect(body === testDocument.body).toBe(true);
});
}
it('should not be able to unmount component from document node', () => {
class Root extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
Hello world
</body>
</html>
);
}
}
var markup = ReactDOMServer.renderToString(<Root />);
var testDocument = getTestDocument(markup);
ReactDOM.hydrate(<Root />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
// In Fiber this actually works. It might not be a good idea though.
ReactDOM.unmountComponentAtNode(testDocument);
expect(testDocument.firstChild).toBe(null);
});
it('should not be able to switch root constructors', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
Hello world
</body>
</html>
);
}
}
class Component2 extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
Goodbye world
</body>
</html>
);
}
}
var markup = ReactDOMServer.renderToString(<Component />);
var testDocument = getTestDocument(markup);
ReactDOM.hydrate(<Component />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
// This works but is probably a bad idea.
ReactDOM.hydrate(<Component2 />, testDocument);
expect(testDocument.body.innerHTML).toBe('Goodbye world');
});
it('should be able to mount into document', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
{this.props.text}
</body>
</html>
);
}
}
var markup = ReactDOMServer.renderToString(
<Component text="Hello world" />,
);
var testDocument = getTestDocument(markup);
ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
});
it('renders over an existing text child without throwing', () => {
spyOn(console, 'error');
const container = document.createElement('div');
container.textContent = 'potato';
ReactDOM.hydrate(<div>parsnip</div>, container);
expect(container.textContent).toBe('parsnip');
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Did not expect server HTML to contain the text node "potato" in <div>.',
);
});
it('should give helpful errors on state desync', () => {
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
{this.props.text}
</body>
</html>
);
}
}
var markup = ReactDOMServer.renderToString(
<Component text="Goodbye world" />,
);
var testDocument = getTestDocument(markup);
spyOn(console, 'error');
ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Warning: Text content did not match.',
);
});
it('should render w/ no markup to full document', () => {
spyOn(console, 'error');
var testDocument = getTestDocument();
class Component extends React.Component {
render() {
return (
<html>
<head>
<title>Hello World</title>
</head>
<body>
{this.props.text}
</body>
</html>
);
}
}
ReactDOM.hydrate(<Component text="Hello world" />, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
expectDev(console.error.calls.count()).toBe(1);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
// getTestDocument() has an extra <meta> that we didn't render.
'Did not expect server HTML to contain a <meta> in <head>.',
);
});
it('supports findDOMNode on full-page components', () => {
var tree = (
<html>
<head>
<title>Hello World</title>
</head>
<body>
Hello world
</body>
</html>
);
var markup = ReactDOMServer.renderToString(tree);
var testDocument = getTestDocument(markup);
var component = ReactDOM.hydrate(tree, testDocument);
expect(testDocument.body.innerHTML).toBe('Hello world');
expect(ReactDOM.findDOMNode(component).tagName).toBe('HTML');
});
});
});

View File

@@ -16,9 +16,6 @@ var ReactDOMServer;
var ReactTestUtils;
var PropTypes;
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var ID_ATTRIBUTE_NAME;
var ROOT_ATTRIBUTE_NAME;
describe('ReactDOMServer', () => {
@@ -34,7 +31,6 @@ describe('ReactDOMServer', () => {
ReactDOMServer = require('react-dom/server');
var DOMProperty = require('DOMProperty');
ID_ATTRIBUTE_NAME = DOMProperty.ID_ATTRIBUTE_NAME;
ROOT_ATTRIBUTE_NAME = DOMProperty.ROOT_ATTRIBUTE_NAME;
});
@@ -72,11 +68,7 @@ describe('ReactDOMServer', () => {
}
var response = ReactDOMServer.renderToString(<NullComponent />);
if (ReactDOMFeatureFlags.useFiber) {
expect(response).toBe('');
} else {
expect(response).toBe('<!-- react-empty: 1 -->');
}
expect(response).toBe('');
});
// TODO: Test that listeners are not registered onto any document/container.
@@ -103,10 +95,7 @@ describe('ReactDOMServer', () => {
'>' +
'<span' +
'>' +
(ReactDOMFeatureFlags.useFiber
? 'My name is <!-- -->child'
: '<!-- react-text: [0-9]+ -->My name is <!-- /react-text -->' +
'<!-- react-text: [0-9]+ -->child<!-- /react-text -->') +
'My name is <!-- -->child' +
'</span>' +
'</div>',
),
@@ -165,14 +154,8 @@ describe('ReactDOMServer', () => {
'<span ' +
ROOT_ATTRIBUTE_NAME +
'=""' +
(ReactDOMFeatureFlags.useFiber
? ''
: ' ' + ID_ATTRIBUTE_NAME + '="[^"]*"') +
'>' +
(ReactDOMFeatureFlags.useFiber
? 'Component name: <!-- -->TestComponent'
: '<!-- react-text: [0-9]+ -->Component name: <!-- /react-text -->' +
'<!-- react-text: [0-9]+ -->TestComponent<!-- /react-text -->') +
'Component name: <!-- -->TestComponent' +
'</span>',
),
);
@@ -247,24 +230,14 @@ describe('ReactDOMServer', () => {
var instance = ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(3);
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.',
);
console.warn.calls.reset();
var expectedMarkup = lastMarkup;
if (ReactDOMFeatureFlags.useFiber) {
var reactComments = /<!-- \/?react-text(: \d+)? -->/g;
expectedMarkup = expectedMarkup.replace(reactComments, '');
}
expect(element.innerHTML).toBe(expectedMarkup);
expect(element.innerHTML).toBe(lastMarkup);
// Ensure the events system works after mount into server markup
expect(numClicks).toEqual(0);
@@ -280,18 +253,9 @@ describe('ReactDOMServer', () => {
instance = ReactDOM.render(<TestComponent name="y" />, element);
expect(mountCount).toEqual(4);
expectDev(console.error.calls.count()).toBe(1);
if (ReactDOMFeatureFlags.useFiber) {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Text content did not match. Server: "x" Client: "y"',
);
} else {
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'(client) -- react-text: 3 -->y<!-- /react-text --',
);
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'(server) -- react-text: 3 -->x<!-- /react-text --',
);
}
expectDev(console.error.calls.argsFor(0)[0]).toContain(
'Text content did not match. Server: "x" Client: "y"',
);
console.error.calls.reset();
expect(element.innerHTML.length > 0).toBe(true);
expect(element.innerHTML).not.toEqual(lastMarkup);
@@ -304,104 +268,94 @@ describe('ReactDOMServer', () => {
expectDev(console.error.calls.count()).toBe(0);
});
if (ReactDOMFeatureFlags.useFiber) {
it('should have the correct mounting behavior (new hydrate API)', () => {
spyOn(console, 'error');
// This test is testing client-side behavior.
ExecutionEnvironment.canUseDOM = true;
it('should have the correct mounting behavior (new hydrate API)', () => {
spyOn(console, 'error');
// This test is testing client-side behavior.
ExecutionEnvironment.canUseDOM = true;
var mountCount = 0;
var numClicks = 0;
var mountCount = 0;
var numClicks = 0;
class TestComponent extends React.Component {
componentDidMount() {
mountCount++;
}
click = () => {
numClicks++;
};
render() {
return (
<span ref="span" onClick={this.click}>
Name: {this.props.name}
</span>
);
}
class TestComponent extends React.Component {
componentDidMount() {
mountCount++;
}
var element = document.createElement('div');
ReactDOM.render(<TestComponent />, element);
click = () => {
numClicks++;
};
var lastMarkup = element.innerHTML;
// Exercise the update path. Markup should not change,
// but some lifecycle methods should be run again.
ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(1);
// Unmount and remount. We should get another mount event and
// we should get different markup, as the IDs are unique each time.
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');
ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(2);
expect(element.innerHTML).not.toEqual(lastMarkup);
// Now kill the node and render it on top of server-rendered markup, as if
// we used server rendering. We should mount again, but the markup should
// be unchanged. We will append a sentinel at the end of innerHTML to be
// sure that innerHTML was not changed.
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');
ExecutionEnvironment.canUseDOM = false;
lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
ExecutionEnvironment.canUseDOM = true;
element.innerHTML = lastMarkup;
var instance = ReactDOM.hydrate(<TestComponent name="x" />, element);
expect(mountCount).toEqual(3);
var expectedMarkup = lastMarkup;
if (ReactDOMFeatureFlags.useFiber) {
var reactComments = /<!-- \/?react-text(: \d+)? -->/g;
expectedMarkup = expectedMarkup.replace(reactComments, '');
render() {
return (
<span ref="span" onClick={this.click}>
Name: {this.props.name}
</span>
);
}
expect(element.innerHTML).toBe(expectedMarkup);
}
// Ensure the events system works after mount into server markup
expect(numClicks).toEqual(0);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
expect(numClicks).toEqual(1);
var element = document.createElement('div');
ReactDOM.render(<TestComponent />, element);
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');
var lastMarkup = element.innerHTML;
// Now simulate a situation where the app is not idempotent. React should
// warn but do the right thing.
element.innerHTML = lastMarkup;
instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
expect(mountCount).toEqual(4);
expectDev(console.error.calls.count()).toBe(1);
expect(element.innerHTML.length > 0).toBe(true);
expect(element.innerHTML).not.toEqual(lastMarkup);
// Exercise the update path. Markup should not change,
// but some lifecycle methods should be run again.
ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(1);
// Ensure the events system works after markup mismatch.
expect(numClicks).toEqual(1);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
expect(numClicks).toEqual(2);
});
}
// Unmount and remount. We should get another mount event and
// we should get different markup, as the IDs are unique each time.
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');
ReactDOM.render(<TestComponent name="x" />, element);
expect(mountCount).toEqual(2);
expect(element.innerHTML).not.toEqual(lastMarkup);
// Now kill the node and render it on top of server-rendered markup, as if
// we used server rendering. We should mount again, but the markup should
// be unchanged. We will append a sentinel at the end of innerHTML to be
// sure that innerHTML was not changed.
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');
ExecutionEnvironment.canUseDOM = false;
lastMarkup = ReactDOMServer.renderToString(<TestComponent name="x" />);
ExecutionEnvironment.canUseDOM = true;
element.innerHTML = lastMarkup;
var instance = ReactDOM.hydrate(<TestComponent name="x" />, element);
expect(mountCount).toEqual(3);
expect(element.innerHTML).toBe(lastMarkup);
// Ensure the events system works after mount into server markup
expect(numClicks).toEqual(0);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
expect(numClicks).toEqual(1);
ReactDOM.unmountComponentAtNode(element);
expect(element.innerHTML).toEqual('');
// Now simulate a situation where the app is not idempotent. React should
// warn but do the right thing.
element.innerHTML = lastMarkup;
instance = ReactDOM.hydrate(<TestComponent name="y" />, element);
expect(mountCount).toEqual(4);
expectDev(console.error.calls.count()).toBe(1);
expect(element.innerHTML.length > 0).toBe(true);
expect(element.innerHTML).not.toEqual(lastMarkup);
// Ensure the events system works after markup mismatch.
expect(numClicks).toEqual(1);
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance.refs.span));
expect(numClicks).toEqual(2);
});
it('should throw with silly args', () => {
expect(
ReactDOMServer.renderToString.bind(ReactDOMServer, {x: 123}),
).toThrowError(
ReactDOMFeatureFlags.useFiber
? 'Objects are not valid as a React child (found: object with keys {x})'
: 'renderToString(): Invalid component element.',
'Objects are not valid as a React child (found: object with keys {x})',
);
});
});
@@ -515,9 +469,7 @@ describe('ReactDOMServer', () => {
expect(
ReactDOMServer.renderToStaticMarkup.bind(ReactDOMServer, {x: 123}),
).toThrowError(
ReactDOMFeatureFlags.useFiber
? 'Objects are not valid as a React child (found: object with keys {x})'
: 'renderToStaticMarkup(): Invalid component element.',
'Objects are not valid as a React child (found: object with keys {x})',
);
});

View File

@@ -13,8 +13,6 @@ var React;
var ReactDOMServer;
var ReactDOMServerBrowser;
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
describe('ReactServerRenderingBrowser', () => {
beforeEach(() => {
jest.resetModules();
@@ -53,18 +51,16 @@ describe('ReactServerRenderingBrowser', () => {
);
});
if (ReactDOMFeatureFlags.useFiber) {
it('throws meaningfully for server-only APIs', () => {
expect(() => ReactDOMServerBrowser.renderToNodeStream(<div />)).toThrow(
'ReactDOMServer.renderToNodeStream(): The streaming API is not available ' +
'in the browser. Use ReactDOMServer.renderToString() instead.',
);
expect(() =>
ReactDOMServerBrowser.renderToStaticNodeStream(<div />),
).toThrow(
'ReactDOMServer.renderToStaticNodeStream(): The streaming API is not available ' +
'in the browser. Use ReactDOMServer.renderToStaticMarkup() instead.',
);
});
}
it('throws meaningfully for server-only APIs', () => {
expect(() => ReactDOMServerBrowser.renderToNodeStream(<div />)).toThrow(
'ReactDOMServer.renderToNodeStream(): The streaming API is not available ' +
'in the browser. Use ReactDOMServer.renderToString() instead.',
);
expect(() =>
ReactDOMServerBrowser.renderToStaticNodeStream(<div />),
).toThrow(
'ReactDOMServer.renderToStaticNodeStream(): The streaming API is not available ' +
'in the browser. Use ReactDOMServer.renderToStaticMarkup() instead.',
);
});
});

View File

@@ -12,7 +12,6 @@
var React = require('react');
var PropTypes = require('prop-types');
var ReactDOM = require('react-dom');
var ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
var ReactTestUtils = require('react-dom/test-utils');
var renderSubtreeIntoContainer = require('react-dom')
.unstable_renderSubtreeIntoContainer;
@@ -299,26 +298,24 @@ describe('renderSubtreeIntoContainer', () => {
expect(portal2.textContent).toBe('foo');
});
if (ReactDOMFeatureFlags.useFiber) {
it('fails gracefully when mixing React 15 and 16', () => {
class C extends React.Component {
render() {
return <div />;
}
it('fails gracefully when mixing React 15 and 16', () => {
class C extends React.Component {
render() {
return <div />;
}
const c = ReactDOM.render(<C />, document.createElement('div'));
// React 15 calls this:
// https://github.com/facebook/react/blob/77b71fc3c4/src/renderers/dom/client/ReactMount.js#L478-L479
expect(() => {
c._reactInternalInstance._processChildContext({});
}).toThrow(
'_processChildContext is not available in React 16+. This likely ' +
'means you have multiple copies of React and are attempting to nest ' +
'a React 15 tree inside a React 16 tree using ' +
"unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
'make sure you have only one copy of React (and ideally, switch to ' +
'ReactDOM.createPortal).',
);
});
}
}
const c = ReactDOM.render(<C />, document.createElement('div'));
// React 15 calls this:
// https://github.com/facebook/react/blob/77b71fc3c4/src/renderers/dom/client/ReactMount.js#L478-L479
expect(() => {
c._reactInternalInstance._processChildContext({});
}).toThrow(
'_processChildContext is not available in React 16+. This likely ' +
'means you have multiple copies of React and are attempting to nest ' +
'a React 15 tree inside a React 16 tree using ' +
"unstable_renderSubtreeIntoContainer, which isn't supported. Try to " +
'make sure you have only one copy of React (and ideally, switch to ' +
'ReactDOM.createPortal).',
);
});
});

View File

@@ -15,7 +15,6 @@ describe('ReactDOMInput', () => {
var React;
var ReactDOM;
var ReactDOMServer;
var ReactDOMFeatureFlags;
var ReactTestUtils;
var inputValueTracking;
@@ -35,7 +34,6 @@ describe('ReactDOMInput', () => {
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactTestUtils = require('react-dom/test-utils');
// TODO: can we express this test with only public API?
inputValueTracking = require('inputValueTracking');
@@ -1187,7 +1185,6 @@ describe('ReactDOMInput', () => {
<input value="0" type="range" min="0" max="100" step="1" />,
);
expect(log).toEqual([
...(ReactDOMFeatureFlags.useFiber ? [] : ['set data-reactroot']),
'set type',
'set step',
'set min',
@@ -1251,9 +1248,6 @@ describe('ReactDOMInput', () => {
<input type="date" defaultValue="1980-01-01" />,
);
expect(log).toEqual([
...(ReactDOMFeatureFlags.useFiber
? []
: ['node.setAttribute("data-reactroot", "")']),
'node.setAttribute("type", "date")',
'node.setAttribute("value", "1980-01-01")',
'node.value = ""',

View File

@@ -16,13 +16,11 @@ describe('ReactDOMOption', () => {
var React;
var ReactDOM;
var ReactDOMFeatureFlags;
var ReactTestUtils;
beforeEach(() => {
React = require('react');
ReactDOM = require('react-dom');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
ReactTestUtils = require('react-dom/test-utils');
});
@@ -45,11 +43,9 @@ describe('ReactDOMOption', () => {
expectDev(
normalizeCodeLocInfo(console.error.calls.argsFor(0)[0]),
).toContain(
ReactDOMFeatureFlags.useFiber
? '<div> cannot appear as a child of <option>.\n' +
' in div (at **)\n' +
' in option (at **)'
: 'Only strings and numbers are supported as <option> children.',
'<div> cannot appear as a child of <option>.\n' +
' in div (at **)\n' +
' in option (at **)',
);
});

View File

@@ -12,7 +12,6 @@
let createRenderer;
let React;
let ReactDOM;
let ReactDOMFeatureFlags;
let ReactDOMServer;
let ReactTestUtils;
@@ -23,7 +22,6 @@ describe('ReactTestUtils', () => {
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');
ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
});
it('can scryRenderedDOMComponentsWithClass with TextComponent', () => {
@@ -173,9 +171,7 @@ describe('ReactTestUtils', () => {
const markup = ReactDOMServer.renderToString(<Root />);
const testDocument = getTestDocument(markup);
const component = ReactDOMFeatureFlags.useFiber
? ReactDOM.hydrate(<Root />, testDocument)
: ReactDOM.render(<Root />, testDocument);
const component = ReactDOM.hydrate(<Root />, testDocument);
expect(component.refs.html.tagName).toBe('HTML');
expect(component.refs.head.tagName).toBe('HEAD');

View File

@@ -9,10 +9,7 @@
'use strict';
const ReactDOMFeatureFlags = require('ReactDOMFeatureFlags');
const describeFiber = ReactDOMFeatureFlags.useFiber ? describe : xdescribe;
describeFiber('ReactDOMFrameScheduling', () => {
describe('ReactDOMFrameScheduling', () => {
it('warns when requestAnimationFrame is not polyfilled in the browser', () => {
const previousRAF = global.requestAnimationFrame;
try {