@samitouri / QOS-React / commits / e15a481773

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.

Dan Abramov committed Mar 20, 2017 at 19:38 UTC e15a481773a4ec3e1e85fc1b2e1d34dd007d4b4e
4 files changed +470
scripts/fiber/tests-passing.txt
+13
@@ -466,6 +466,8 @@ src/renderers/__tests__/ReactComponent-test.js
466 * fires the callback after a component is rendered
467 * throws usefully when rendering badly-typed elements
468 * includes owner name in the error about badly-typed elements
469 +* throws if a plain object is used as a child
470 +* throws if a plain object even if it is in an owner
471
472 src/renderers/__tests__/ReactComponentLifeCycle-test.js
473 * should not reuse an instance when it has been unmounted
@@ -526,6 +528,8 @@ src/renderers/__tests__/ReactCompositeComponent-test.js
528 * should warn when mutated props are passed
529 * should only call componentWillUnmount once
530 * prepares new child before unmounting old
531 +* respects a shallow shouldComponentUpdate implementation
532 +* does not do a deep comparison for a shallow shouldComponentUpdate implementation
533
534 src/renderers/__tests__/ReactCompositeComponentDOMMinimalism-test.js
535 * should not render extra nodes for non-interpolated text
@@ -1151,6 +1155,15 @@ src/renderers/dom/shared/__tests__/quoteAttributeValueForBrowser-test.js
1155 * should escape number to string
1156 * should escape string
1157
1158 +src/renderers/dom/shared/__tests__/renderSubtreeIntoContainer-test.js
1159 +* should pass context when rendering subtree elsewhere
1160 +* should throw if parentComponent is invalid
1161 +* should update context if it changes due to setState
1162 +* should update context if it changes due to re-render
1163 +* should render portal with non-context-provider parent
1164 +* should get context through non-context-provider parent
1165 +* should get context through middle non-context-provider layer
1166 +
1167 src/renderers/dom/shared/__tests__/validateDOMNesting-test.js
1168 * allows any tag with no context
1169 * allows valid nestings
src/renderers/__tests__/ReactComponent-test.js
+34
@@ -380,4 +380,38 @@ describe('ReactComponent', () => {
380 // One warning for each element creation
381 expectDev(console.error.calls.count()).toBe(1);
382 });
383 +
384 + it('throws if a plain object is used as a child', () => {
385 + var children = {
386 + x: <span />,
387 + y: <span />,
388 + z: <span />,
389 + };
390 + var element = <div>{[children]}</div>;
391 + var container = document.createElement('div');
392 + expect(() => ReactDOM.render(element, container)).toThrowError(
393 + 'Objects are not valid as a React child (found: object with keys ' +
394 + '{x, y, z}). If you meant to render a collection of children, use an ' +
395 + 'array instead.',
396 + );
397 + });
398 +
399 + it('throws if a plain object even if it is in an owner', () => {
400 + class Foo extends React.Component {
401 + render() {
402 + var children = {
403 + a: <span />,
404 + b: <span />,
405 + c: <span />,
406 + };
407 + return <div>{[children]}</div>;
408 + }
409 + }
410 + var container = document.createElement('div');
411 + expect(() => ReactDOM.render(<Foo />, container)).toThrowError(
412 + 'Objects are not valid as a React child (found: object with keys ' +
413 + '{a, b, c}). If you meant to render a collection of children, use an ' +
414 + 'array instead.\n\nCheck the render method of `Foo`.',
415 + );
416 + });
417 });
src/renderers/__tests__/ReactCompositeComponent-test.js
+123
@@ -20,6 +20,8 @@ var ReactDOMServer;
20 var ReactCurrentOwner;
21 var ReactPropTypes;
22 var ReactTestUtils;
23 +var shallowEqual;
24 +var shallowCompare;
25
26 describe('ReactCompositeComponent', () => {
27 beforeEach(() => {
@@ -31,6 +33,12 @@ describe('ReactCompositeComponent', () => {
33 ReactCurrentOwner = require('react/lib/ReactCurrentOwner');
34 ReactPropTypes = require('ReactPropTypes');
35 ReactTestUtils = require('ReactTestUtils');
36 + shallowEqual = require('fbjs/lib/shallowEqual');
37 +
38 + shallowCompare = function(instance, nextProps, nextState) {
39 + return !shallowEqual(instance.props, nextProps) ||
40 + !shallowEqual(instance.state, nextState);
41 + };
42
43 MorphingComponent = class extends React.Component {
44 state = {activated: false};
@@ -1396,4 +1404,119 @@ describe('ReactCompositeComponent', () => {
1404 'B componentDidMount',
1405 ]);
1406 });
1407 +
1408 + it('respects a shallow shouldComponentUpdate implementation', () => {
1409 + var renderCalls = 0;
1410 + class PlasticWrap extends React.Component {
1411 + constructor(props, context) {
1412 + super(props, context);
1413 + this.state = {
1414 + color: 'green',
1415 + };
1416 + }
1417 +
1418 + render() {
1419 + return <Apple color={this.state.color} ref="apple" />;
1420 + }
1421 + }
1422 +
1423 + class Apple extends React.Component {
1424 + state = {
1425 + cut: false,
1426 + slices: 1,
1427 + };
1428 +
1429 + shouldComponentUpdate(nextProps, nextState) {
1430 + return shallowCompare(this, nextProps, nextState);
1431 + }
1432 +
1433 + cut() {
1434 + this.setState({
1435 + cut: true,
1436 + slices: 10,
1437 + });
1438 + }
1439 +
1440 + eatSlice() {
1441 + this.setState({
1442 + slices: this.state.slices - 1,
1443 + });
1444 + }
1445 +
1446 + render() {
1447 + renderCalls++;
1448 + return <div />;
1449 + }
1450 + }
1451 +
1452 + var container = document.createElement('div');
1453 + var instance = ReactDOM.render(<PlasticWrap />, container);
1454 + expect(renderCalls).toBe(1);
1455 +
1456 + // Do not re-render based on props
1457 + instance.setState({color: 'green'});
1458 + expect(renderCalls).toBe(1);
1459 +
1460 + // Re-render based on props
1461 + instance.setState({color: 'red'});
1462 + expect(renderCalls).toBe(2);
1463 +
1464 + // Re-render base on state
1465 + instance.refs.apple.cut();
1466 + expect(renderCalls).toBe(3);
1467 +
1468 + // No re-render based on state
1469 + instance.refs.apple.cut();
1470 + expect(renderCalls).toBe(3);
1471 +
1472 + // Re-render based on state again
1473 + instance.refs.apple.eatSlice();
1474 + expect(renderCalls).toBe(4);
1475 + });
1476 +
1477 + it('does not do a deep comparison for a shallow shouldComponentUpdate implementation', () => {
1478 + function getInitialState() {
1479 + return {
1480 + foo: [1, 2, 3],
1481 + bar: {a: 4, b: 5, c: 6},
1482 + };
1483 + }
1484 +
1485 + var renderCalls = 0;
1486 + var initialSettings = getInitialState();
1487 +
1488 + class Component extends React.Component {
1489 + state = initialSettings;
1490 +
1491 + shouldComponentUpdate(nextProps, nextState) {
1492 + return shallowCompare(this, nextProps, nextState);
1493 + }
1494 +
1495 + render() {
1496 + renderCalls++;
1497 + return <div />;
1498 + }
1499 + }
1500 +
1501 + var container = document.createElement('div');
1502 + var instance = ReactDOM.render(<Component />, container);
1503 + expect(renderCalls).toBe(1);
1504 +
1505 + // Do not re-render if state is equal
1506 + var settings = {
1507 + foo: initialSettings.foo,
1508 + bar: initialSettings.bar,
1509 + };
1510 + instance.setState(settings);
1511 + expect(renderCalls).toBe(1);
1512 +
1513 + // Re-render because one field changed
1514 + initialSettings.foo = [1, 2, 3];
1515 + instance.setState(initialSettings);
1516 + expect(renderCalls).toBe(2);
1517 +
1518 + // Re-render because the object changed
1519 + instance.setState(getInitialState());
1520 + expect(renderCalls).toBe(3);
1521 + });
1522 });
src/renderers/dom/shared/__tests__/renderSubtreeIntoContainer-test.js new
+300
@@ -0,0 +1,300 @@
1 +/**
2 + * Copyright 2013-present, Facebook, Inc.
3 + * All rights reserved.
4 + *
5 + * This source code is licensed under the BSD-style license found in the
6 + * LICENSE file in the root directory of this source tree. An additional grant
7 + * of patent rights can be found in the PATENTS file in the same directory.
8 + *
9 + * @emails react-core
10 + */
11 +
12 +'use strict';
13 +
14 +var React = require('react');
15 +var ReactDOM = require('react-dom');
16 +var ReactTestUtils = require('ReactTestUtils');
17 +var renderSubtreeIntoContainer = require('renderSubtreeIntoContainer');
18 +
19 +describe('renderSubtreeIntoContainer', () => {
20 + it('should pass context when rendering subtree elsewhere', () => {
21 + var portal = document.createElement('div');
22 +
23 + class Component extends React.Component {
24 + static contextTypes = {
25 + foo: React.PropTypes.string.isRequired,
26 + };
27 +
28 + render() {
29 + return <div>{this.context.foo}</div>;
30 + }
31 + }
32 +
33 + class Parent extends React.Component {
34 + static childContextTypes = {
35 + foo: React.PropTypes.string.isRequired,
36 + };
37 +
38 + getChildContext() {
39 + return {
40 + foo: 'bar',
41 + };
42 + }
43 +
44 + render() {
45 + return null;
46 + }
47 +
48 + componentDidMount() {
49 + expect(
50 + function() {
51 + renderSubtreeIntoContainer(this, <Component />, portal);
52 + }.bind(this),
53 + ).not.toThrow();
54 + }
55 + }
56 +
57 + ReactTestUtils.renderIntoDocument(<Parent />);
58 + expect(portal.firstChild.innerHTML).toBe('bar');
59 + });
60 +
61 + it('should throw if parentComponent is invalid', () => {
62 + var portal = document.createElement('div');
63 +
64 + class Component extends React.Component {
65 + static contextTypes = {
66 + foo: React.PropTypes.string.isRequired,
67 + };
68 +
69 + render() {
70 + return <div>{this.context.foo}</div>;
71 + }
72 + }
73 +
74 + // ESLint is confused here and thinks Parent is unused, presumably because
75 + // it is only used inside of the class body?
76 + // eslint-disable-next-line no-unused-vars
77 + class Parent extends React.Component {
78 + static childContextTypes = {
79 + foo: React.PropTypes.string.isRequired,
80 + };
81 +
82 + getChildContext() {
83 + return {
84 + foo: 'bar',
85 + };
86 + }
87 +
88 + render() {
89 + return null;
90 + }
91 +
92 + componentDidMount() {
93 + expect(function() {
94 + renderSubtreeIntoContainer(<Parent />, <Component />, portal);
95 + }).toThrowError('parentComponentmust be a valid React Component');
96 + }
97 + }
98 + });
99 +
100 + it('should update context if it changes due to setState', () => {
101 + var container = document.createElement('div');
102 + document.body.appendChild(container);
103 + var portal = document.createElement('div');
104 +
105 + class Component extends React.Component {
106 + static contextTypes = {
107 + foo: React.PropTypes.string.isRequired,
108 + getFoo: React.PropTypes.func.isRequired,
109 + };
110 +
111 + render() {
112 + return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
113 + }
114 + }
115 +
116 + class Parent extends React.Component {
117 + static childContextTypes = {
118 + foo: React.PropTypes.string.isRequired,
119 + getFoo: React.PropTypes.func.isRequired,
120 + };
121 +
122 + state = {
123 + bar: 'initial',
124 + };
125 +
126 + getChildContext() {
127 + return {
128 + foo: this.state.bar,
129 + getFoo: () => this.state.bar,
130 + };
131 + }
132 +
133 + render() {
134 + return null;
135 + }
136 +
137 + componentDidMount() {
138 + renderSubtreeIntoContainer(this, <Component />, portal);
139 + }
140 +
141 + componentDidUpdate() {
142 + renderSubtreeIntoContainer(this, <Component />, portal);
143 + }
144 + }
145 +
146 + var instance = ReactDOM.render(<Parent />, container);
147 + expect(portal.firstChild.innerHTML).toBe('initial-initial');
148 + instance.setState({bar: 'changed'});
149 + expect(portal.firstChild.innerHTML).toBe('changed-changed');
150 + });
151 +
152 + it('should update context if it changes due to re-render', () => {
153 + var container = document.createElement('div');
154 + document.body.appendChild(container);
155 + var portal = document.createElement('div');
156 +
157 + class Component extends React.Component {
158 + static contextTypes = {
159 + foo: React.PropTypes.string.isRequired,
160 + getFoo: React.PropTypes.func.isRequired,
161 + };
162 +
163 + render() {
164 + return <div>{this.context.foo + '-' + this.context.getFoo()}</div>;
165 + }
166 + }
167 +
168 + class Parent extends React.Component {
169 + static childContextTypes = {
170 + foo: React.PropTypes.string.isRequired,
171 + getFoo: React.PropTypes.func.isRequired,
172 + };
173 +
174 + getChildContext() {
175 + return {
176 + foo: this.props.bar,
177 + getFoo: () => this.props.bar,
178 + };
179 + }
180 +
181 + render() {
182 + return null;
183 + }
184 +
185 + componentDidMount() {
186 + renderSubtreeIntoContainer(this, <Component />, portal);
187 + }
188 +
189 + componentDidUpdate() {
190 + renderSubtreeIntoContainer(this, <Component />, portal);
191 + }
192 + }
193 +
194 + ReactDOM.render(<Parent bar="initial" />, container);
195 + expect(portal.firstChild.innerHTML).toBe('initial-initial');
196 + ReactDOM.render(<Parent bar="changed" />, container);
197 + expect(portal.firstChild.innerHTML).toBe('changed-changed');
198 + });
199 +
200 + it('should render portal with non-context-provider parent', () => {
201 + var container = document.createElement('div');
202 + document.body.appendChild(container);
203 + var portal = document.createElement('div');
204 +
205 + class Parent extends React.Component {
206 + render() {
207 + return null;
208 + }
209 +
210 + componentDidMount() {
211 + renderSubtreeIntoContainer(this, <div>hello</div>, portal);
212 + }
213 + }
214 +
215 + ReactDOM.render(<Parent bar="initial" />, container);
216 + expect(portal.firstChild.innerHTML).toBe('hello');
217 + });
218 +
219 + it('should get context through non-context-provider parent', () => {
220 + var container = document.createElement('div');
221 + document.body.appendChild(container);
222 + var portal = document.createElement('div');
223 +
224 + class Parent extends React.Component {
225 + render() {
226 + return <Middle />;
227 + }
228 + getChildContext() {
229 + return {value: this.props.value};
230 + }
231 + static childContextTypes = {
232 + value: React.PropTypes.string.isRequired,
233 + };
234 + }
235 +
236 + class Middle extends React.Component {
237 + render() {
238 + return null;
239 + }
240 + componentDidMount() {
241 + renderSubtreeIntoContainer(this, <Child />, portal);
242 + }
243 + }
244 +
245 + class Child extends React.Component {
246 + static contextTypes = {
247 + value: React.PropTypes.string.isRequired,
248 + };
249 + render() {
250 + return <div>{this.context.value}</div>;
251 + }
252 + }
253 +
254 + ReactDOM.render(<Parent value="foo" />, container);
255 + expect(portal.textContent).toBe('foo');
256 + });
257 +
258 + it('should get context through middle non-context-provider layer', () => {
259 + var container = document.createElement('div');
260 + document.body.appendChild(container);
261 + var portal1 = document.createElement('div');
262 + var portal2 = document.createElement('div');
263 +
264 + class Parent extends React.Component {
265 + render() {
266 + return null;
267 + }
268 + getChildContext() {
269 + return {value: this.props.value};
270 + }
271 + componentDidMount() {
272 + renderSubtreeIntoContainer(this, <Middle />, portal1);
273 + }
274 + static childContextTypes = {
275 + value: React.PropTypes.string.isRequired,
276 + };
277 + }
278 +
279 + class Middle extends React.Component {
280 + render() {
281 + return null;
282 + }
283 + componentDidMount() {
284 + renderSubtreeIntoContainer(this, <Child />, portal2);
285 + }
286 + }
287 +
288 + class Child extends React.Component {
289 + static contextTypes = {
290 + value: React.PropTypes.string.isRequired,
291 + };
292 + render() {
293 + return <div>{this.context.value}</div>;
294 + }
295 + }
296 +
297 + ReactDOM.render(<Parent value="foo" />, container);
298 + expect(portal2.textContent).toBe('foo');
299 + });
300 +});