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
+});