mirror of
https://github.com/zebrajr/react.git
synced 2026-01-15 12:15:22 +00:00
Remove usage of ReactDOMFeatureFlags.useFiber, assuming true (#10796)
This commit is contained in:
@@ -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.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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).',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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`.',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"</span>'}{'<span>Text2"</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"</span>');
|
||||
expectTextNode(e.childNodes[2], '<span>Text2"</span>');
|
||||
} else {
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
expectTextNode(e.childNodes[0], '<span>Text1"</span>');
|
||||
expectTextNode(e.childNodes[1], '<span>Text2"</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"</span>');
|
||||
expectTextNode(e.childNodes[3], '<span>Text2"</span>');
|
||||
expectTextNode(e.childNodes[2], '<span>Text2"</span>');
|
||||
} else {
|
||||
expect(e.childNodes.length).toBe(2);
|
||||
expectTextNode(e.childNodes[0], '<span>Text1"</span>');
|
||||
expectTextNode(e.childNodes[1], '<span>Text2"</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
|
||||
|
||||
@@ -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('');
|
||||
});
|
||||
|
||||
|
||||
@@ -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: client text</div>,
|
||||
div,
|
||||
);
|
||||
} else {
|
||||
ReactDOM.render(
|
||||
<div>This markup contains an nbsp entity: client text</div>,
|
||||
div,
|
||||
);
|
||||
}
|
||||
ReactDOM.hydrate(
|
||||
<div>This markup contains an nbsp entity: 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: client text</div>\n' +
|
||||
' (server) nbsp entity: 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',
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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})',
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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.',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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).',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 = ""',
|
||||
|
||||
@@ -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 **)',
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user