mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
bec7599067
* Migrate conditional tests to gate pragma
I searched through the codebase for this pattern:
```js
describe('test suite', () => {
if (!__EXPERIMENTAL__) { // or some other condition
test("empty test so Jest doesn't complain", () => {});
return;
}
// Unless we're in experimental mode, none of the tests in this block
// will run.
})
```
and converted them to the `@gate` pragma instead.
The reason this pattern isn't preferred is because you end up disabling
more tests than you need to.
* Add flag for www release channels
Using a heuristic where I check a flag that is known to only be enabled
in www. I left a TODO to instead set the release channel explicitly in
each test config.
333 lines
8.9 KiB
JavaScript
333 lines
8.9 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @emails react-core
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const React = require('react');
|
|
const PropTypes = require('prop-types');
|
|
const ReactDOM = require('react-dom');
|
|
const ReactTestUtils = require('react-dom/test-utils');
|
|
const renderSubtreeIntoContainer = require('react-dom')
|
|
.unstable_renderSubtreeIntoContainer;
|
|
|
|
const ReactFeatureFlags = require('shared/ReactFeatureFlags');
|
|
|
|
describe('renderSubtreeIntoContainer', () => {
|
|
it('should pass context when rendering subtree elsewhere', () => {
|
|
const portal = document.createElement('div');
|
|
|
|
class Component extends React.Component {
|
|
static contextTypes = {
|
|
foo: PropTypes.string.isRequired,
|
|
};
|
|
|
|
render() {
|
|
return <div>{this.context.foo}</div>;
|
|
}
|
|
}
|
|
|
|
class Parent extends React.Component {
|
|
static childContextTypes = {
|
|
foo: PropTypes.string.isRequired,
|
|
};
|
|
|
|
getChildContext() {
|
|
return {
|
|
foo: 'bar',
|
|
};
|
|
}
|
|
|
|
render() {
|
|
return null;
|
|
}
|
|
|
|
componentDidMount() {
|
|
if (ReactFeatureFlags.warnUnstableRenderSubtreeIntoContainer) {
|
|
expect(
|
|
function() {
|
|
renderSubtreeIntoContainer(this, <Component />, portal);
|
|
}.bind(this),
|
|
).toWarnDev(
|
|
'ReactDOM.unstable_renderSubtreeIntoContainer() is deprecated and ' +
|
|
'will be removed in a future major release. Consider using React Portals instead.',
|
|
);
|
|
} else {
|
|
renderSubtreeIntoContainer(this, <Component />, portal);
|
|
}
|
|
}
|
|
}
|
|
|
|
ReactTestUtils.renderIntoDocument(<Parent />);
|
|
expect(portal.firstChild.innerHTML).toBe('bar');
|
|
});
|
|
|
|
it('should throw if parentComponent is invalid', () => {
|
|
const portal = document.createElement('div');
|
|
|
|
class Component extends React.Component {
|
|
static contextTypes = {
|
|
foo: PropTypes.string.isRequired,
|
|
};
|
|
|
|
render() {
|
|
return <div>{this.context.foo}</div>;
|
|
}
|
|
}
|
|
|
|
// ESLint is confused here and thinks Parent is unused, presumably because
|
|
// it is only used inside of the class body?
|
|
// eslint-disable-next-line no-unused-vars
|
|
class Parent extends React.Component {
|
|
static childContextTypes = {
|
|
foo: PropTypes.string.isRequired,
|
|
};
|
|
|
|
getChildContext() {
|
|
return {
|
|
foo: 'bar',
|
|
};
|
|
}
|
|
|
|
render() {
|
|
return null;
|
|
}
|
|
|
|
componentDidMount() {
|
|
expect(function() {
|
|
renderSubtreeIntoContainer(<Parent />, <Component />, portal);
|
|
}).toThrowError('parentComponentmust be a valid React Component');
|
|
}
|
|
}
|
|
});
|
|
|
|
it('should update context if it changes due to setState', () => {
|
|
const container = document.createElement('div');
|
|
document.body.appendChild(container);
|
|
const portal = document.createElement('div');
|
|
|
|
class Component extends React.Component {
|
|
static contextTypes = {
|
|
foo: PropTypes.string.isRequired,
|
|
getFoo: PropTypes.func.isRequired,
|
|
};
|
|
|
|
render() {
|
|
return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
|
|
}
|
|
}
|
|
|
|
class Parent extends React.Component {
|
|
static childContextTypes = {
|
|
foo: PropTypes.string.isRequired,
|
|
getFoo: PropTypes.func.isRequired,
|
|
};
|
|
|
|
state = {
|
|
bar: 'initial',
|
|
};
|
|
|
|
getChildContext() {
|
|
return {
|
|
foo: this.state.bar,
|
|
getFoo: () => this.state.bar,
|
|
};
|
|
}
|
|
|
|
render() {
|
|
return null;
|
|
}
|
|
|
|
componentDidMount() {
|
|
renderSubtreeIntoContainer(this, <Component />, portal);
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
renderSubtreeIntoContainer(this, <Component />, portal);
|
|
}
|
|
}
|
|
|
|
const instance = ReactDOM.render(<Parent />, container);
|
|
expect(portal.firstChild.innerHTML).toBe('initial-initial');
|
|
instance.setState({bar: 'changed'});
|
|
expect(portal.firstChild.innerHTML).toBe('changed-changed');
|
|
});
|
|
|
|
it('should update context if it changes due to re-render', () => {
|
|
const container = document.createElement('div');
|
|
document.body.appendChild(container);
|
|
const portal = document.createElement('div');
|
|
|
|
class Component extends React.Component {
|
|
static contextTypes = {
|
|
foo: PropTypes.string.isRequired,
|
|
getFoo: PropTypes.func.isRequired,
|
|
};
|
|
|
|
render() {
|
|
return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
|
|
}
|
|
}
|
|
|
|
class Parent extends React.Component {
|
|
static childContextTypes = {
|
|
foo: PropTypes.string.isRequired,
|
|
getFoo: PropTypes.func.isRequired,
|
|
};
|
|
|
|
getChildContext() {
|
|
return {
|
|
foo: this.props.bar,
|
|
getFoo: () => this.props.bar,
|
|
};
|
|
}
|
|
|
|
render() {
|
|
return null;
|
|
}
|
|
|
|
componentDidMount() {
|
|
renderSubtreeIntoContainer(this, <Component />, portal);
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
renderSubtreeIntoContainer(this, <Component />, portal);
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(<Parent bar="initial" />, container);
|
|
expect(portal.firstChild.innerHTML).toBe('initial-initial');
|
|
ReactDOM.render(<Parent bar="changed" />, container);
|
|
expect(portal.firstChild.innerHTML).toBe('changed-changed');
|
|
});
|
|
|
|
it('should render portal with non-context-provider parent', () => {
|
|
const container = document.createElement('div');
|
|
document.body.appendChild(container);
|
|
const portal = document.createElement('div');
|
|
|
|
class Parent extends React.Component {
|
|
render() {
|
|
return null;
|
|
}
|
|
|
|
componentDidMount() {
|
|
renderSubtreeIntoContainer(this, <div>hello</div>, portal);
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(<Parent bar="initial" />, container);
|
|
expect(portal.firstChild.innerHTML).toBe('hello');
|
|
});
|
|
|
|
it('should get context through non-context-provider parent', () => {
|
|
const container = document.createElement('div');
|
|
document.body.appendChild(container);
|
|
const portal = document.createElement('div');
|
|
|
|
class Parent extends React.Component {
|
|
render() {
|
|
return <Middle />;
|
|
}
|
|
getChildContext() {
|
|
return {value: this.props.value};
|
|
}
|
|
static childContextTypes = {
|
|
value: PropTypes.string.isRequired,
|
|
};
|
|
}
|
|
|
|
class Middle extends React.Component {
|
|
render() {
|
|
return null;
|
|
}
|
|
componentDidMount() {
|
|
renderSubtreeIntoContainer(this, <Child />, portal);
|
|
}
|
|
}
|
|
|
|
class Child extends React.Component {
|
|
static contextTypes = {
|
|
value: PropTypes.string.isRequired,
|
|
};
|
|
render() {
|
|
return <div>{this.context.value}</div>;
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(<Parent value="foo" />, container);
|
|
expect(portal.textContent).toBe('foo');
|
|
});
|
|
|
|
it('should get context through middle non-context-provider layer', () => {
|
|
const container = document.createElement('div');
|
|
document.body.appendChild(container);
|
|
const portal1 = document.createElement('div');
|
|
const portal2 = document.createElement('div');
|
|
|
|
class Parent extends React.Component {
|
|
render() {
|
|
return null;
|
|
}
|
|
getChildContext() {
|
|
return {value: this.props.value};
|
|
}
|
|
componentDidMount() {
|
|
renderSubtreeIntoContainer(this, <Middle />, portal1);
|
|
}
|
|
static childContextTypes = {
|
|
value: PropTypes.string.isRequired,
|
|
};
|
|
}
|
|
|
|
class Middle extends React.Component {
|
|
render() {
|
|
return null;
|
|
}
|
|
componentDidMount() {
|
|
renderSubtreeIntoContainer(this, <Child />, portal2);
|
|
}
|
|
}
|
|
|
|
class Child extends React.Component {
|
|
static contextTypes = {
|
|
value: PropTypes.string.isRequired,
|
|
};
|
|
render() {
|
|
return <div>{this.context.value}</div>;
|
|
}
|
|
}
|
|
|
|
ReactDOM.render(<Parent value="foo" />, container);
|
|
expect(portal2.textContent).toBe('foo');
|
|
});
|
|
|
|
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(
|
|
__DEV__
|
|
? '_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).'
|
|
: "Cannot read property '_processChildContext' of undefined",
|
|
);
|
|
});
|
|
});
|