Add react-create-class integration tests
These are mostly copied from the old ReactClass-test module.
Andrew Clark committed
Mar 27, 2017 at 16:34 UTC
ce489262a1ee34340440e55a0b99ea6918e19e7a
3 files changed
+460
scripts/fiber/tests-passing.txt
+18
@@ -90,6 +90,24 @@ src/isomorphic/classic/__tests__/ReactContextValidator-test.js
90
* should warn (but not error) if getChildContext method is missing
91
* should pass parent context if getChildContext method is missing
92
93
+src/isomorphic/classic/class/__tests__/react-create-class-integration-test.js
94
+* should throw when `render` is not specified
95
+* should copy prop types onto the Constructor
96
+* should warn on invalid prop types
97
+* should warn on invalid context types
98
+* should throw on invalid child context types
99
+* should warn when mispelling shouldComponentUpdate
100
+* should warn when mispelling componentWillReceiveProps
101
+* should throw if a reserved property is in statics
102
+* should support statics
103
+* should work with object getInitialState() return values
104
+* renders based on context getInitialState
105
+* should throw with non-object getInitialState() return values
106
+* should work with a null getInitialState() return value
107
+* should throw when using legacy factories
108
+* replaceState and callback works
109
+* isMounted works
110
+
111
src/isomorphic/classic/element/__tests__/ReactElement-test.js
112
* uses the fallback value when in an environment without Symbol
113
* returns a complete element according to spec
src/isomorphic/classic/class/__tests__/react-create-class-integration-test.js
new
+436
@@ -0,0 +1,436 @@
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;
15
+var ReactDOM;
16
+var ReactTestUtils;
17
+var ReactCreateClass;
18
+
19
+describe('react-create-class-integration', () => {
20
+ beforeEach(() => {
21
+ React = require('React');
22
+ ReactDOM = require('ReactDOM');
23
+ ReactTestUtils = require('ReactTestUtils');
24
+ var ReactCreateClassFactory = require('react-create-class/factory');
25
+ ReactCreateClass = ReactCreateClassFactory(
26
+ React.Component,
27
+ React.isValidElement,
28
+ require('ReactNoopUpdateQueue'),
29
+ );
30
+ });
31
+
32
+ it('should throw when `render` is not specified', () => {
33
+ expect(function() {
34
+ ReactCreateClass({});
35
+ }).toThrowError(
36
+ 'createClass(...): Class specification must implement a `render` method.',
37
+ );
38
+ });
39
+
40
+ // TODO: Update babel-plugin-transform-react-display-name
41
+ xit('should copy `displayName` onto the Constructor', () => {
42
+ var TestComponent = ReactCreateClass({
43
+ render: function() {
44
+ return <div />;
45
+ },
46
+ });
47
+
48
+ expect(TestComponent.displayName).toBe('TestComponent');
49
+ });
50
+
51
+ it('should copy prop types onto the Constructor', () => {
52
+ var propValidator = jest.fn();
53
+ var TestComponent = ReactCreateClass({
54
+ propTypes: {
55
+ value: propValidator,
56
+ },
57
+ render: function() {
58
+ return <div />;
59
+ },
60
+ });
61
+
62
+ expect(TestComponent.propTypes).toBeDefined();
63
+ expect(TestComponent.propTypes.value).toBe(propValidator);
64
+ });
65
+
66
+ it('should warn on invalid prop types', () => {
67
+ spyOn(console, 'error');
68
+ ReactCreateClass({
69
+ displayName: 'Component',
70
+ propTypes: {
71
+ prop: null,
72
+ },
73
+ render: function() {
74
+ return <span>{this.props.prop}</span>;
75
+ },
76
+ });
77
+ expect(console.error.calls.count()).toBe(1);
78
+ expect(console.error.calls.argsFor(0)[0]).toBe(
79
+ 'Warning: Component: prop type `prop` is invalid; ' +
80
+ 'it must be a function, usually from React.PropTypes.',
81
+ );
82
+ });
83
+
84
+ it('should warn on invalid context types', () => {
85
+ spyOn(console, 'error');
86
+ ReactCreateClass({
87
+ displayName: 'Component',
88
+ contextTypes: {
89
+ prop: null,
90
+ },
91
+ render: function() {
92
+ return <span>{this.props.prop}</span>;
93
+ },
94
+ });
95
+ expect(console.error.calls.count()).toBe(1);
96
+ expect(console.error.calls.argsFor(0)[0]).toBe(
97
+ 'Warning: Component: context type `prop` is invalid; ' +
98
+ 'it must be a function, usually from React.PropTypes.',
99
+ );
100
+ });
101
+
102
+ it('should throw on invalid child context types', () => {
103
+ spyOn(console, 'error');
104
+ ReactCreateClass({
105
+ displayName: 'Component',
106
+ childContextTypes: {
107
+ prop: null,
108
+ },
109
+ render: function() {
110
+ return <span>{this.props.prop}</span>;
111
+ },
112
+ });
113
+ expect(console.error.calls.count()).toBe(1);
114
+ expect(console.error.calls.argsFor(0)[0]).toBe(
115
+ 'Warning: Component: child context type `prop` is invalid; ' +
116
+ 'it must be a function, usually from React.PropTypes.',
117
+ );
118
+ });
119
+
120
+ it('should warn when mispelling shouldComponentUpdate', () => {
121
+ spyOn(console, 'error');
122
+
123
+ ReactCreateClass({
124
+ componentShouldUpdate: function() {
125
+ return false;
126
+ },
127
+ render: function() {
128
+ return <div />;
129
+ },
130
+ });
131
+ expect(console.error.calls.count()).toBe(1);
132
+ expect(console.error.calls.argsFor(0)[0]).toBe(
133
+ 'Warning: A component has a method called componentShouldUpdate(). Did you ' +
134
+ 'mean shouldComponentUpdate()? The name is phrased as a question ' +
135
+ 'because the function is expected to return a value.',
136
+ );
137
+
138
+ ReactCreateClass({
139
+ displayName: 'NamedComponent',
140
+ componentShouldUpdate: function() {
141
+ return false;
142
+ },
143
+ render: function() {
144
+ return <div />;
145
+ },
146
+ });
147
+ expect(console.error.calls.count()).toBe(2);
148
+ expect(console.error.calls.argsFor(1)[0]).toBe(
149
+ 'Warning: NamedComponent has a method called componentShouldUpdate(). Did you ' +
150
+ 'mean shouldComponentUpdate()? The name is phrased as a question ' +
151
+ 'because the function is expected to return a value.',
152
+ );
153
+ });
154
+
155
+ it('should warn when mispelling componentWillReceiveProps', () => {
156
+ spyOn(console, 'error');
157
+ ReactCreateClass({
158
+ componentWillRecieveProps: function() {
159
+ return false;
160
+ },
161
+ render: function() {
162
+ return <div />;
163
+ },
164
+ });
165
+ expect(console.error.calls.count()).toBe(1);
166
+ expect(console.error.calls.argsFor(0)[0]).toBe(
167
+ 'Warning: A component has a method called componentWillRecieveProps(). Did you ' +
168
+ 'mean componentWillReceiveProps()?',
169
+ );
170
+ });
171
+
172
+ it('should throw if a reserved property is in statics', () => {
173
+ expect(function() {
174
+ ReactCreateClass({
175
+ statics: {
176
+ getDefaultProps: function() {
177
+ return {
178
+ foo: 0,
179
+ };
180
+ },
181
+ },
182
+
183
+ render: function() {
184
+ return <span />;
185
+ },
186
+ });
187
+ }).toThrowError(
188
+ 'ReactClass: You are attempting to define a reserved property, ' +
189
+ '`getDefaultProps`, that shouldn\'t be on the "statics" key. Define ' +
190
+ 'it as an instance property instead; it will still be accessible on ' +
191
+ 'the constructor.',
192
+ );
193
+ });
194
+
195
+ // TODO: Consider actually moving these to statics or drop this unit test.
196
+
197
+ xit('should warn when using deprecated non-static spec keys', () => {
198
+ spyOn(console, 'error');
199
+ ReactCreateClass({
200
+ mixins: [{}],
201
+ propTypes: {
202
+ foo: React.PropTypes.string,
203
+ },
204
+ contextTypes: {
205
+ foo: React.PropTypes.string,
206
+ },
207
+ childContextTypes: {
208
+ foo: React.PropTypes.string,
209
+ },
210
+ render: function() {
211
+ return <div />;
212
+ },
213
+ });
214
+ expect(console.error.calls.count()).toBe(4);
215
+ expect(console.error.calls.argsFor(0)[0]).toBe(
216
+ 'createClass(...): `mixins` is now a static property and should ' +
217
+ 'be defined inside "statics".',
218
+ );
219
+ expect(console.error.calls.argsFor(1)[0]).toBe(
220
+ 'createClass(...): `propTypes` is now a static property and should ' +
221
+ 'be defined inside "statics".',
222
+ );
223
+ expect(console.error.calls.argsFor(2)[0]).toBe(
224
+ 'createClass(...): `contextTypes` is now a static property and ' +
225
+ 'should be defined inside "statics".',
226
+ );
227
+ expect(console.error.calls.argsFor(3)[0]).toBe(
228
+ 'createClass(...): `childContextTypes` is now a static property and ' +
229
+ 'should be defined inside "statics".',
230
+ );
231
+ });
232
+
233
+ it('should support statics', () => {
234
+ var Component = ReactCreateClass({
235
+ statics: {
236
+ abc: 'def',
237
+ def: 0,
238
+ ghi: null,
239
+ jkl: 'mno',
240
+ pqr: function() {
241
+ return this;
242
+ },
243
+ },
244
+
245
+ render: function() {
246
+ return <span />;
247
+ },
248
+ });
249
+ var instance = <Component />;
250
+ instance = ReactTestUtils.renderIntoDocument(instance);
251
+ expect(instance.constructor.abc).toBe('def');
252
+ expect(Component.abc).toBe('def');
253
+ expect(instance.constructor.def).toBe(0);
254
+ expect(Component.def).toBe(0);
255
+ expect(instance.constructor.ghi).toBe(null);
256
+ expect(Component.ghi).toBe(null);
257
+ expect(instance.constructor.jkl).toBe('mno');
258
+ expect(Component.jkl).toBe('mno');
259
+ expect(instance.constructor.pqr()).toBe(Component);
260
+ expect(Component.pqr()).toBe(Component);
261
+ });
262
+
263
+ it('should work with object getInitialState() return values', () => {
264
+ var Component = ReactCreateClass({
265
+ getInitialState: function() {
266
+ return {
267
+ occupation: 'clown',
268
+ };
269
+ },
270
+ render: function() {
271
+ return <span />;
272
+ },
273
+ });
274
+ var instance = <Component />;
275
+ instance = ReactTestUtils.renderIntoDocument(instance);
276
+ expect(instance.state.occupation).toEqual('clown');
277
+ });
278
+
279
+ it('renders based on context getInitialState', () => {
280
+ var Foo = ReactCreateClass({
281
+ contextTypes: {
282
+ className: React.PropTypes.string,
283
+ },
284
+ getInitialState() {
285
+ return {className: this.context.className};
286
+ },
287
+ render() {
288
+ return <span className={this.state.className} />;
289
+ },
290
+ });
291
+
292
+ var Outer = ReactCreateClass({
293
+ childContextTypes: {
294
+ className: React.PropTypes.string,
295
+ },
296
+ getChildContext() {
297
+ return {className: 'foo'};
298
+ },
299
+ render() {
300
+ return <Foo />;
301
+ },
302
+ });
303
+
304
+ var container = document.createElement('div');
305
+ ReactDOM.render(<Outer />, container);
306
+ expect(container.firstChild.className).toBe('foo');
307
+ });
308
+
309
+ it('should throw with non-object getInitialState() return values', () => {
310
+ [['an array'], 'a string', 1234].forEach(function(state) {
311
+ var Component = ReactCreateClass({
312
+ getInitialState: function() {
313
+ return state;
314
+ },
315
+ render: function() {
316
+ return <span />;
317
+ },
318
+ });
319
+ var instance = <Component />;
320
+ expect(function() {
321
+ instance = ReactTestUtils.renderIntoDocument(instance);
322
+ }).toThrowError(
323
+ 'Component.getInitialState(): must return an object or null',
324
+ );
325
+ });
326
+ });
327
+
328
+ it('should work with a null getInitialState() return value', () => {
329
+ var Component = ReactCreateClass({
330
+ getInitialState: function() {
331
+ return null;
332
+ },
333
+ render: function() {
334
+ return <span />;
335
+ },
336
+ });
337
+ expect(() =>
338
+ ReactTestUtils.renderIntoDocument(<Component />)).not.toThrow();
339
+ });
340
+
341
+ it('should throw when using legacy factories', () => {
342
+ spyOn(console, 'error');
343
+ var Component = ReactCreateClass({
344
+ render() {
345
+ return <div />;
346
+ },
347
+ });
348
+
349
+ expect(() => Component()).toThrow();
350
+ expect(console.error.calls.count()).toBe(1);
351
+ expect(console.error.calls.argsFor(0)[0]).toBe(
352
+ 'Warning: Something is calling a React component directly. Use a ' +
353
+ 'factory or JSX instead. See: https://fb.me/react-legacyfactory',
354
+ );
355
+ });
356
+
357
+ it('replaceState and callback works', () => {
358
+ var ops = [];
359
+ var Component = ReactCreateClass({
360
+ getInitialState() {
361
+ return {step: 0};
362
+ },
363
+ render() {
364
+ ops.push('Render: ' + this.state.step);
365
+ return <div />;
366
+ },
367
+ });
368
+
369
+ var instance = ReactTestUtils.renderIntoDocument(<Component />);
370
+ instance.replaceState({step: 1}, () => {
371
+ ops.push('Callback: ' + instance.state.step);
372
+ });
373
+ expect(ops).toEqual(['Render: 0', 'Render: 1', 'Callback: 1']);
374
+ });
375
+
376
+ it('isMounted works', () => {
377
+ spyOn(console, 'error');
378
+
379
+ var ops = [];
380
+ var instance;
381
+ var Component = ReactCreateClass({
382
+ displayName: 'MyComponent',
383
+ log(name) {
384
+ ops.push(`${name}: ${this.isMounted()}`);
385
+ },
386
+ getInitialState() {
387
+ this.log('getInitialState');
388
+ return {};
389
+ },
390
+ componentWillMount() {
391
+ this.log('componentWillMount');
392
+ },
393
+ componentDidMount() {
394
+ this.log('componentDidMount');
395
+ },
396
+ componentWillUpdate() {
397
+ this.log('componentWillUpdate');
398
+ },
399
+ componentDidUpdate() {
400
+ this.log('componentDidUpdate');
401
+ },
402
+ componentWillUnmount() {
403
+ this.log('componentWillUnmount');
404
+ },
405
+ render() {
406
+ instance = this;
407
+ this.log('render');
408
+ return <div />;
409
+ },
410
+ });
411
+
412
+ var container = document.createElement('div');
413
+ ReactDOM.render(<Component />, container);
414
+ ReactDOM.render(<Component />, container);
415
+ ReactDOM.unmountComponentAtNode(container);
416
+ instance.log('after unmount');
417
+ expect(ops).toEqual([
418
+ 'getInitialState: false',
419
+ 'componentWillMount: false',
420
+ 'render: false',
421
+ 'componentDidMount: true',
422
+ 'componentWillUpdate: true',
423
+ 'render: true',
424
+ 'componentDidUpdate: true',
425
+ 'componentWillUnmount: false',
426
+ 'after unmount: false',
427
+ ]);
428
+
429
+ expect(console.error.calls.count()).toBe(1);
430
+ expect(console.error.calls.argsFor(0)[0]).toEqual(
431
+ 'Warning: MyComponent: isMounted is deprecated. Instead, make sure to ' +
432
+ 'clean up subscriptions and pending requests in componentWillUnmount ' +
433
+ 'to prevent memory leaks.',
434
+ );
435
+ });
436
+});
yarn.lock
+6
@@ -4004,6 +4004,12 @@ rc@^1.1.7:
4004
minimist "^1.2.0"
4005
strip-json-comments "~2.0.1"
4006
4007
+react-create-class@15.5.0-alpha.7:
4008
+ version "15.5.0-alpha.7"
4009
+ resolved "https://registry.yarnpkg.com/react-create-class/-/react-create-class-15.5.0-alpha.7.tgz#a2bf8846ab0f0e86799e8a2c2346d7a87941a350"
4010
+ dependencies:
4011
+ fbjs "^0.8.9"
4012
+
4013
read-only-stream@^2.0.0:
4014
version "2.0.0"
4015
resolved "https://registry.yarnpkg.com/read-only-stream/-/read-only-stream-2.0.0.tgz#2724fd6a8113d73764ac288d4386270c1dbf17f0"