Re-add tests unintentionally deleted in #9209 (#9225)

These tests are useful and don't test addons specifically.
I moved them to appropriate places in the codebase.
This commit is contained in:
Dan Abramov
2017-03-20 19:38:54 +00:00
committed by GitHub
parent 34e4352cba
commit e15a481773
4 changed files with 470 additions and 0 deletions
+13
View File
@@ -466,6 +466,8 @@ src/renderers/__tests__/ReactComponent-test.js
* fires the callback after a component is rendered
* throws usefully when rendering badly-typed elements
* includes owner name in the error about badly-typed elements
* throws if a plain object is used as a child
* throws if a plain object even if it is in an owner
src/renderers/__tests__/ReactComponentLifeCycle-test.js
* should not reuse an instance when it has been unmounted
@@ -526,6 +528,8 @@ src/renderers/__tests__/ReactCompositeComponent-test.js
* should warn when mutated props are passed
* should only call componentWillUnmount once
* prepares new child before unmounting old
* respects a shallow shouldComponentUpdate implementation
* does not do a deep comparison for a shallow shouldComponentUpdate implementation
src/renderers/__tests__/ReactCompositeComponentDOMMinimalism-test.js
* should not render extra nodes for non-interpolated text
@@ -1151,6 +1155,15 @@ src/renderers/dom/shared/__tests__/quoteAttributeValueForBrowser-test.js
* should escape number to string
* should escape string
src/renderers/dom/shared/__tests__/renderSubtreeIntoContainer-test.js
* should pass context when rendering subtree elsewhere
* should throw if parentComponent is invalid
* should update context if it changes due to setState
* should update context if it changes due to re-render
* should render portal with non-context-provider parent
* should get context through non-context-provider parent
* should get context through middle non-context-provider layer
src/renderers/dom/shared/__tests__/validateDOMNesting-test.js
* allows any tag with no context
* allows valid nestings
@@ -380,4 +380,38 @@ describe('ReactComponent', () => {
// One warning for each element creation
expectDev(console.error.calls.count()).toBe(1);
});
it('throws if a plain object is used as a child', () => {
var children = {
x: <span />,
y: <span />,
z: <span />,
};
var element = <div>{[children]}</div>;
var container = document.createElement('div');
expect(() => ReactDOM.render(element, container)).toThrowError(
'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.',
);
});
it('throws if a plain object even if it is in an owner', () => {
class Foo extends React.Component {
render() {
var children = {
a: <span />,
b: <span />,
c: <span />,
};
return <div>{[children]}</div>;
}
}
var container = document.createElement('div');
expect(() => ReactDOM.render(<Foo />, container)).toThrowError(
'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\nCheck the render method of `Foo`.',
);
});
});
@@ -20,6 +20,8 @@ var ReactDOMServer;
var ReactCurrentOwner;
var ReactPropTypes;
var ReactTestUtils;
var shallowEqual;
var shallowCompare;
describe('ReactCompositeComponent', () => {
beforeEach(() => {
@@ -31,6 +33,12 @@ describe('ReactCompositeComponent', () => {
ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
ReactPropTypes = require('ReactPropTypes');
ReactTestUtils = require('ReactTestUtils');
shallowEqual = require('fbjs/lib/shallowEqual');
shallowCompare = function(instance, nextProps, nextState) {
return !shallowEqual(instance.props, nextProps) ||
!shallowEqual(instance.state, nextState);
};
MorphingComponent = class extends React.Component {
state = {activated: false};
@@ -1396,4 +1404,119 @@ describe('ReactCompositeComponent', () => {
'B componentDidMount',
]);
});
it('respects a shallow shouldComponentUpdate implementation', () => {
var renderCalls = 0;
class PlasticWrap extends React.Component {
constructor(props, context) {
super(props, context);
this.state = {
color: 'green',
};
}
render() {
return <Apple color={this.state.color} ref="apple" />;
}
}
class Apple extends React.Component {
state = {
cut: false,
slices: 1,
};
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
}
cut() {
this.setState({
cut: true,
slices: 10,
});
}
eatSlice() {
this.setState({
slices: this.state.slices - 1,
});
}
render() {
renderCalls++;
return <div />;
}
}
var container = document.createElement('div');
var instance = ReactDOM.render(<PlasticWrap />, container);
expect(renderCalls).toBe(1);
// Do not re-render based on props
instance.setState({color: 'green'});
expect(renderCalls).toBe(1);
// Re-render based on props
instance.setState({color: 'red'});
expect(renderCalls).toBe(2);
// Re-render base on state
instance.refs.apple.cut();
expect(renderCalls).toBe(3);
// No re-render based on state
instance.refs.apple.cut();
expect(renderCalls).toBe(3);
// Re-render based on state again
instance.refs.apple.eatSlice();
expect(renderCalls).toBe(4);
});
it('does not do a deep comparison for a shallow shouldComponentUpdate implementation', () => {
function getInitialState() {
return {
foo: [1, 2, 3],
bar: {a: 4, b: 5, c: 6},
};
}
var renderCalls = 0;
var initialSettings = getInitialState();
class Component extends React.Component {
state = initialSettings;
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState);
}
render() {
renderCalls++;
return <div />;
}
}
var container = document.createElement('div');
var instance = ReactDOM.render(<Component />, container);
expect(renderCalls).toBe(1);
// Do not re-render if state is equal
var settings = {
foo: initialSettings.foo,
bar: initialSettings.bar,
};
instance.setState(settings);
expect(renderCalls).toBe(1);
// Re-render because one field changed
initialSettings.foo = [1, 2, 3];
instance.setState(initialSettings);
expect(renderCalls).toBe(2);
// Re-render because the object changed
instance.setState(getInitialState());
expect(renderCalls).toBe(3);
});
});
@@ -0,0 +1,300 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/
'use strict';
var React = require('react');
var ReactDOM = require('react-dom');
var ReactTestUtils = require('ReactTestUtils');
var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer');
describe('renderSubtreeIntoContainer', () => {
it('should pass context when rendering subtree elsewhere', () => {
var portal = document.createElement('div');
class Component extends React.Component {
static contextTypes = {
foo: React.PropTypes.string.isRequired,
};
render() {
return <div>{this.context.foo}</div>;
}
}
class Parent extends React.Component {
static childContextTypes = {
foo: React.PropTypes.string.isRequired,
};
getChildContext() {
return {
foo: 'bar',
};
}
render() {
return null;
}
componentDidMount() {
expect(
function() {
renderSubtreeIntoContainer(this, <Component />, portal);
}.bind(this),
).not.toThrow();
}
}
ReactTestUtils.renderIntoDocument(<Parent />);
expect(portal.firstChild.innerHTML).toBe('bar');
});
it('should throw if parentComponent is invalid', () => {
var portal = document.createElement('div');
class Component extends React.Component {
static contextTypes = {
foo: React.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: React.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', () => {
var container = document.createElement('div');
document.body.appendChild(container);
var portal = document.createElement('div');
class Component extends React.Component {
static contextTypes = {
foo: React.PropTypes.string.isRequired,
getFoo: React.PropTypes.func.isRequired,
};
render() {
return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
}
}
class Parent extends React.Component {
static childContextTypes = {
foo: React.PropTypes.string.isRequired,
getFoo: React.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);
}
}
var 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', () => {
var container = document.createElement('div');
document.body.appendChild(container);
var portal = document.createElement('div');
class Component extends React.Component {
static contextTypes = {
foo: React.PropTypes.string.isRequired,
getFoo: React.PropTypes.func.isRequired,
};
render() {
return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
}
}
class Parent extends React.Component {
static childContextTypes = {
foo: React.PropTypes.string.isRequired,
getFoo: React.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', () => {
var container = document.createElement('div');
document.body.appendChild(container);
var 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', () => {
var container = document.createElement('div');
document.body.appendChild(container);
var portal = document.createElement('div');
class Parent extends React.Component {
render() {
return <Middle />;
}
getChildContext() {
return {value: this.props.value};
}
static childContextTypes = {
value: React.PropTypes.string.isRequired,
};
}
class Middle extends React.Component {
render() {
return null;
}
componentDidMount() {
renderSubtreeIntoContainer(this, <Child />, portal);
}
}
class Child extends React.Component {
static contextTypes = {
value: React.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', () => {
var container = document.createElement('div');
document.body.appendChild(container);
var portal1 = document.createElement('div');
var 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: React.PropTypes.string.isRequired,
};
}
class Middle extends React.Component {
render() {
return null;
}
componentDidMount() {
renderSubtreeIntoContainer(this, <Child />, portal2);
}
}
class Child extends React.Component {
static contextTypes = {
value: React.PropTypes.string.isRequired,
};
render() {
return <div>{this.context.value}</div>;
}
}
ReactDOM.render(<Parent value="foo" />, container);
expect(portal2.textContent).toBe('foo');
});
});