Shallow renderer and test utils bundles (#9426)
Shallow renderer and test utils bundles Adds new bundles introduced with React 15.5 release to master (and 16 alpha) react-dom/test-utils: This new bundle contains what used to be react-addons-test-utils. This bundle shares things from react-dom rather than duplicates them. A temporary createRenderer method has been left behind as a way to access the new shallow renderer. This is for the ReactNative release cycle only and should be going away before the final release. react-test-renderer/shallow: This new shallow renderer is almost entirely stand-alone (in that it doesn't use the React reconciler or scheduler). The only touch points are ReactElement and prop/context validation. This renderer is stack and fiber compatible.
Brian Vaughn committed
Apr 19, 2017 at 16:45 UTC
66f2097f33a3084c79aa536e11d3ca09195bc320
24 files changed
+755
-341
packages/react-dom/package.json
+1
@@ -27,6 +27,7 @@
27
"README.md",
28
"index.js",
29
"server.js",
30
+ "test-utils.js",
31
"cjs/",
32
"umd/"
33
],
packages/react-dom/test-utils.js
new
+7
@@ -0,0 +1,7 @@
1
+'use strict';
2
+
3
+if (process.env.NODE_ENV === 'production') {
4
+ throw Error('test-utils is not available in production mode.');
5
+} else {
6
+ module.exports = require('./cjs/react-dom-test-utils.development');
7
+}
packages/react-test-renderer/index.js
+5
-1
@@ -1,3 +1,7 @@
1
'use strict';
2
3
-module.exports = require('./cjs/react-test-renderer.development');
3
+if (process.env.NODE_ENV === 'production') {
4
+ throw Error('test renderer is not available in production mode.');
5
+} else {
6
+ module.exports = require('./cjs/react-test-renderer.development');
7
+}
packages/react-test-renderer/package.json
+1
@@ -26,6 +26,7 @@
26
"PATENTS",
27
"README.md",
28
"index.js",
29
+ "shallow.js",
30
"cjs/"
31
]
32
}
packages/react-test-renderer/shallow.js
new
+7
@@ -0,0 +1,7 @@
1
+'use strict';
2
+
3
+if (process.env.NODE_ENV === 'production') {
4
+ throw Error('shallow renderer is not available in production mode.');
5
+} else {
6
+ module.exports = require('./cjs/react-test-renderer-shallow.development');
7
+}
scripts/fiber/tests-passing.txt
+35
-26
@@ -1562,6 +1562,41 @@ src/renderers/dom/shared/wrappers/__tests__/ReactDOMTextarea-test.js
1562
* should warn if value is null
1563
* should warn if value and defaultValue are specified
1564
1565
+src/renderers/dom/test/__tests__/ReactTestUtils-test.js
1566
+* should call all of the lifecycle hooks
1567
+* should only render 1 level deep
1568
+* should have shallow rendering
1569
+* should enable shouldComponentUpdate to prevent a re-render
1570
+* should shallow render a functional component
1571
+* should throw for invalid elements
1572
+* should have shallow unmounting
1573
+* can shallow render to null
1574
+* can shallow render with a ref
1575
+* lets you update shallowly rendered components
1576
+* can access the mounted component instance
1577
+* can shallowly render components with contextTypes
1578
+* can shallowly render components with ref as function
1579
+* can setState in componentWillMount when shallow rendering
1580
+* can setState in componentWillReceiveProps when shallow rendering
1581
+* can setState with an updater function
1582
+* can pass context when shallowly rendering
1583
+* should track context across updates
1584
+* can fail context when shallowly rendering
1585
+* should warn about propTypes (but only once)
1586
+* can scryRenderedDOMComponentsWithClass with TextComponent
1587
+* can scryRenderedDOMComponentsWithClass with className contains \n
1588
+* can scryRenderedDOMComponentsWithClass with multiple classes
1589
+* traverses children in the correct order
1590
+* should support injected wrapper components as DOM components
1591
+* should change the value of an input field
1592
+* should change the value of an input field in a component
1593
+* should throw when attempting to use ReactTestUtils.Simulate with shallow rendering
1594
+* should not warn when simulating events with extra properties
1595
+* can scry with stateless components involved
1596
+* should enable rendering of cloned element
1597
+* should set the type of the event
1598
+* should work with renderIntoDocument
1599
+
1600
src/renderers/native/__tests__/ReactNativeAttributePayload-test.js
1601
* should work with simple example
1602
* should skip fields that are equal
@@ -1871,32 +1906,6 @@ src/shared/utils/__tests__/PooledClass-test.js
1906
src/shared/utils/__tests__/reactProdInvariant-test.js
1907
* should throw with the correct number of `%s`s in the URL
1908
1874
-src/test/__tests__/ReactTestUtils-test.js
1875
-* should have shallow rendering
1876
-* should shallow render a functional component
1877
-* should throw for invalid elements
1878
-* should have shallow unmounting
1879
-* can shallow render to null
1880
-* can shallow render with a ref
1881
-* lets you update shallowly rendered components
1882
-* can access the mounted component instance
1883
-* can shallowly render components with contextTypes
1884
-* can shallowly render components with ref as function
1885
-* can setState in componentWillMount when shallow rendering
1886
-* can pass context when shallowly rendering
1887
-* can fail context when shallowly rendering
1888
-* can scryRenderedDOMComponentsWithClass with TextComponent
1889
-* can scryRenderedDOMComponentsWithClass with className contains \n
1890
-* can scryRenderedDOMComponentsWithClass with multiple classes
1891
-* traverses children in the correct order
1892
-* should support injected wrapper components as DOM components
1893
-* should change the value of an input field
1894
-* should change the value of an input field in a component
1895
-* should throw when attempting to use ReactTestUtils.Simulate with shallow rendering
1896
-* should not warn when simulating events with extra properties
1897
-* can scry with stateless components involved
1898
-* should set the type of the event
1899
-
1909
src/test/__tests__/reactComponentExpect-test.js
1910
* should match composite components
1911
* should match empty DOM components
scripts/rollup/bundles.js
+56
-11
@@ -89,9 +89,7 @@ const bundles = [
89
paths: [
90
'src/renderers/dom/**/*.js',
91
'src/renderers/shared/**/*.js',
92
- 'src/test/**/*.js', // ReactTestUtils is currently very coupled to DOM.
92
94
- 'src/isomorphic/classic/types/checkPropTypes.js',
93
'src/ReactVersion.js',
94
'src/shared/**/*.js',
95
],
@@ -118,9 +116,41 @@ const bundles = [
116
paths: [
117
'src/renderers/dom/**/*.js',
118
'src/renderers/shared/**/*.js',
121
- 'src/test/**/*.js', // ReactTestUtils is currently very coupled to DOM.
119
123
- 'src/isomorphic/classic/types/checkPropTypes.js',
120
+ 'src/ReactVersion.js',
121
+ 'src/shared/**/*.js',
122
+ ],
123
+ },
124
+ {
125
+ babelOpts: babelOptsReact,
126
+ bundleTypes: [FB_DEV, NODE_DEV],
127
+ config: {
128
+ destDir: 'build/',
129
+ globals: {
130
+ react: 'React',
131
+ },
132
+ moduleName: 'ReactTestUtils',
133
+ sourceMap: false,
134
+ },
135
+ entry: 'src/renderers/dom/test/ReactTestUtils',
136
+ externals: [
137
+ 'prop-types',
138
+ 'prop-types/checkPropTypes',
139
+ 'react',
140
+ 'react-dom',
141
+ 'react-test-renderer', // TODO (bvaughn) Remove this dependency before 16.0.0
142
+ ],
143
+ fbEntry: 'src/renderers/dom/test/ReactTestUtils',
144
+ hasteName: 'ReactTestUtils',
145
+ isRenderer: true,
146
+ label: 'test-utils',
147
+ manglePropertiesOnProd: false,
148
+ name: 'react-dom/test-utils',
149
+ paths: [
150
+ 'src/renderers/dom/test/**/*.js',
151
+ 'src/renderers/shared/**/*.js',
152
+ 'src/renderers/testing/**/*.js', // TODO (bvaughn) Remove this dependency before 16.0.0
153
+
154
'src/ReactVersion.js',
155
'src/shared/**/*.js',
156
],
@@ -151,7 +181,6 @@ const bundles = [
181
'src/renderers/dom/**/*.js',
182
'src/renderers/shared/**/*.js',
183
154
- 'src/isomorphic/classic/types/checkPropTypes.js',
184
'src/ReactVersion.js',
185
'src/shared/**/*.js',
186
],
@@ -190,7 +219,6 @@ const bundles = [
219
'src/renderers/art/**/*.js',
220
'src/renderers/shared/**/*.js',
221
193
- 'src/isomorphic/classic/types/checkPropTypes.js',
222
'src/ReactVersion.js',
223
'src/shared/**/*.js',
224
],
@@ -226,7 +254,6 @@ const bundles = [
254
'src/renderers/art/**/*.js',
255
'src/renderers/shared/**/*.js',
256
229
- 'src/isomorphic/classic/types/checkPropTypes.js',
257
'src/ReactVersion.js',
258
'src/shared/**/*.js',
259
],
@@ -328,12 +355,10 @@ const bundles = [
355
'src/renderers/shared/**/*.js',
356
'src/renderers/testing/**/*.js',
357
331
- 'src/isomorphic/classic/types/checkPropTypes.js',
358
'src/ReactVersion.js',
359
'src/shared/**/*.js',
360
],
361
},
336
-
362
{
363
babelOpts: babelOptsReact,
364
bundleTypes: [FB_DEV],
@@ -355,11 +380,32 @@ const bundles = [
380
'src/renderers/shared/**/*.js',
381
'src/renderers/testing/**/*.js',
382
358
- 'src/isomorphic/classic/types/checkPropTypes.js',
383
'src/ReactVersion.js',
384
'src/shared/**/*.js',
385
],
386
},
387
+ {
388
+ babelOpts: babelOptsReact,
389
+ bundleTypes: [FB_DEV, NODE_DEV],
390
+ config: {
391
+ destDir: 'build/',
392
+ moduleName: 'ReactShallowRenderer',
393
+ sourceMap: false,
394
+ },
395
+ entry: 'src/renderers/testing/ReactShallowRenderer',
396
+ externals: [
397
+ 'react-dom',
398
+ 'prop-types/checkPropTypes',
399
+ 'react-test-renderer',
400
+ ],
401
+ fbEntry: 'src/renderers/testing/ReactShallowRenderer',
402
+ hasteName: 'ReactShallowRenderer',
403
+ isRenderer: true,
404
+ label: 'shallow-renderer',
405
+ manglePropertiesOnProd: false,
406
+ name: 'react-test-renderer/shallow',
407
+ paths: ['src/renderers/shared/**/*.js', 'src/renderers/testing/**/*.js'],
408
+ },
409
410
/******* React Noop Renderer (used only for fixtures/fiber-debugger) *******/
411
{
@@ -383,7 +429,6 @@ const bundles = [
429
'src/renderers/noop/**/*.js',
430
'src/renderers/shared/**/*.js',
431
386
- 'src/isomorphic/classic/types/checkPropTypes.js',
432
'src/ReactVersion.js',
433
'src/shared/**/*.js',
434
],
scripts/rollup/shims/facebook-www/ReactTestUtils.js
deleted
-18
@@ -1,18 +0,0 @@
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
- * @providesModule ReactTestUtils
10
- */
11
-
12
-'use strict';
13
-
14
-const {
15
- __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
16
-} = require('ReactDOM-fb');
17
-
18
-module.exports = __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactTestUtils;
src/fb/ReactDOMFBEntry.js
+2
-3
@@ -43,10 +43,9 @@ Object.assign(ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, {
43
44
if (__DEV__) {
45
Object.assign(ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED, {
46
- // ReactPerf and ReactTestUtils currently only work with the DOM renderer
47
- // so we expose them from here, but only in DEV mode.
46
+ // ReactPerf currently only works with the DOM renderer
47
+ // so we expose it here, but only in DEV mode.
48
ReactPerf: require('ReactPerf'),
49
- ReactTestUtils: require('ReactTestUtils'),
49
});
50
}
51
src/fb/ReactDOMFiberFBEntry.js
-11
@@ -45,15 +45,4 @@ Object.assign(
45
},
46
);
47
48
-if (__DEV__) {
49
- Object.assign(
50
- ReactDOMFiber.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
51
- {
52
- // ReactPerf and ReactTestUtils currently only work with the DOM renderer
53
- // so we expose them from here, but only in DEV mode.
54
- ReactTestUtils: require('ReactTestUtils'),
55
- },
56
- );
57
-}
58
-
48
module.exports = ReactDOMFiber;
src/isomorphic/React.js
+3
-3
@@ -19,7 +19,7 @@ var ReactPropTypes = require('ReactPropTypes');
19
var ReactVersion = require('ReactVersion');
20
21
var onlyChild = require('onlyChild');
22
-var checkPropTypes = require('checkPropTypes');
22
+var checkPropTypes = require('prop-types/checkPropTypes');
23
var createReactClass = require('createClass');
24
25
var createElement = ReactElement.createElement;
@@ -57,7 +57,7 @@ var React = {
57
cloneElement: cloneElement,
58
isValidElement: ReactElement.isValidElement,
59
60
- // TODO (bvaughn) Remove these getters in 16.0.0-alpha.10
60
+ // TODO (bvaughn) Remove these getters before 16.0.0
61
PropTypes: ReactPropTypes,
62
checkPropTypes: checkPropTypes,
63
createClass: createReactClass,
@@ -100,7 +100,7 @@ if (__DEV__) {
100
return mixin;
101
};
102
103
- // TODO (bvaughn) Remove both of these deprecation warnings in 16.0.0-alpha.10
103
+ // TODO (bvaughn) Remove both of these deprecation warnings before 16.0.0
104
if (canDefineProperty) {
105
Object.defineProperty(React, 'checkPropTypes', {
106
get() {
src/isomorphic/classic/element/ReactElementValidator.js
+1
-1
@@ -26,7 +26,7 @@ var getComponentName = require('getComponentName');
26
var getIteratorFn = require('getIteratorFn');
27
28
if (__DEV__) {
29
- var checkPropTypes = require('checkPropTypes');
29
+ var checkPropTypes = require('prop-types/checkPropTypes');
30
var warning = require('fbjs/lib/warning');
31
var ReactDebugCurrentFrame = require('ReactDebugCurrentFrame');
32
var {
src/isomorphic/classic/types/__tests__/ReactPropTypes-test.js
+1
-1
@@ -21,7 +21,7 @@ var MyComponent;
21
22
function resetWarningCache() {
23
jest.resetModules();
24
- checkPropTypes = require('checkPropTypes');
24
+ checkPropTypes = require('prop-types/checkPropTypes');
25
}
26
27
function getPropTypeWarningMessage(propTypes, object, componentName) {
src/isomorphic/classic/types/checkPropTypes.js
deleted
-14
@@ -1,14 +0,0 @@
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
- * @providesModule checkPropTypes
10
- */
11
-
12
-'use strict';
13
-
14
-module.exports = require('prop-types/checkPropTypes');
src/node_modules/react-test-renderer/index.js
renamed
+1
-1
@@ -6,4 +6,4 @@
6
7
'use strict';
8
9
-module.exports = require('checkPropTypes');
9
+module.exports = require('ReactTestRenderer');
src/renderers/dom/ReactDOM.js
+6
@@ -42,6 +42,12 @@ var ReactDOM = {
42
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
43
// For TapEventPlugin which is popular in open source
44
EventPluginHub: require('EventPluginHub'),
45
+ // Used by test-utils
46
+ EventPluginRegistry: require('EventPluginRegistry'),
47
+ EventPropagators: require('EventPropagators'),
48
+ ReactControlledComponent: require('ReactControlledComponent'),
49
+ ReactDOMComponentTree,
50
+ ReactBrowserEventEmitter: require('ReactBrowserEventEmitter'),
51
},
52
};
53
src/renderers/dom/fiber/ReactDOMFiber.js
+6
@@ -542,6 +542,12 @@ var ReactDOM = {
542
__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED: {
543
// For TapEventPlugin which is popular in open source
544
EventPluginHub: require('EventPluginHub'),
545
+ // Used by test-utils
546
+ EventPluginRegistry: require('EventPluginRegistry'),
547
+ EventPropagators: require('EventPropagators'),
548
+ ReactControlledComponent,
549
+ ReactDOMComponentTree,
550
+ ReactBrowserEventEmitter,
551
},
552
};
553
src/renderers/dom/test/ReactTestUtils.js
renamed
+31
-14
@@ -12,23 +12,26 @@
12
'use strict';
13
14
var EventConstants = require('EventConstants');
15
-var EventPluginHub = require('EventPluginHub');
16
-var EventPluginRegistry = require('EventPluginRegistry');
17
-var EventPropagators = require('EventPropagators');
15
var React = require('react');
19
-var ReactControlledComponent = require('ReactControlledComponent');
20
-var ReactDOM = require('ReactDOM');
21
-var ReactDOMComponentTree = require('ReactDOMComponentTree');
22
-var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
16
+var ReactDOM = require('react-dom');
17
var ReactFiberTreeReflection = require('ReactFiberTreeReflection');
18
var ReactInstanceMap = require('ReactInstanceMap');
19
+var ReactShallowRenderer = require('ReactShallowRenderer'); // TODO (bvaughn) Remove this import before 16.0.0
20
var ReactTypeOfWork = require('ReactTypeOfWork');
26
-var ReactGenericBatching = require('ReactGenericBatching');
21
var SyntheticEvent = require('SyntheticEvent');
28
-var ReactShallowRenderer = require('ReactShallowRenderer');
22
30
-var findDOMNode = require('findDOMNode');
23
var invariant = require('fbjs/lib/invariant');
24
+var warning = require('fbjs/lib/warning');
25
+
26
+var {findDOMNode} = ReactDOM;
27
+var {
28
+ EventPluginHub,
29
+ EventPluginRegistry,
30
+ EventPropagators,
31
+ ReactBrowserEventEmitter,
32
+ ReactControlledComponent,
33
+ ReactDOMComponentTree,
34
+} = ReactDOM.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
35
36
var topLevelTypes = EventConstants.topLevelTypes;
37
var {
@@ -38,6 +41,20 @@ var {
41
HostText,
42
} = ReactTypeOfWork;
43
44
+// TODO (bvaughn) Remove this warning before 16.0.0
45
+// It's only being added for temporary deprecation notice in RN.
46
+let warnedAboutShallowRenderer = false;
47
+function createRendererWithWarning() {
48
+ warning(
49
+ warnedAboutShallowRenderer,
50
+ 'Shallow renderer has been moved to react-test-renderer/shallow. ' +
51
+ 'Update references to remove this warning. ' +
52
+ 'TestUtils.createRenderer will be removed completely in React 16.',
53
+ );
54
+ warnedAboutShallowRenderer = true;
55
+ return new ReactShallowRenderer();
56
+}
57
+
58
function Event(suffix) {}
59
60
/**
@@ -403,9 +420,9 @@ var ReactTestUtils = {
420
};
421
},
422
406
- createRenderer: function() {
407
- return new ReactShallowRenderer();
408
- },
423
+ // TODO (bvaughn) Remove this warning accessor before 16.0.0.
424
+ // It's only being added for temporary deprecation notice in RN.
425
+ createRenderer: createRendererWithWarning,
426
427
Simulate: null,
428
SimulateNative: {},
@@ -462,7 +479,7 @@ function makeSimulator(eventType) {
479
EventPropagators.accumulateDirectDispatches(event);
480
}
481
465
- ReactGenericBatching.batchedUpdates(function() {
482
+ ReactDOM.unstable_batchedUpdates(function() {
483
// Normally extractEvent enqueues a state restore, but we'll just always
484
// do that since we we're by-passing it here.
485
ReactControlledComponent.enqueueStateRestore(node);
src/renderers/dom/test/__tests__/ReactTestUtils-test.js
renamed
+317
-68
@@ -11,14 +11,16 @@
11
12
'use strict';
13
14
-var PropTypes;
15
-var React;
16
-var ReactDOM;
17
-var ReactDOMServer;
18
-var ReactTestUtils;
14
+let createRenderer;
15
+let PropTypes;
16
+let React;
17
+let ReactDOM;
18
+let ReactDOMServer;
19
+let ReactTestUtils;
20
21
describe('ReactTestUtils', () => {
22
beforeEach(() => {
23
+ createRenderer = require('ReactShallowRenderer').createRenderer;
24
PropTypes = require('prop-types');
25
React = require('react');
26
ReactDOM = require('react-dom');
@@ -26,6 +28,66 @@ describe('ReactTestUtils', () => {
28
ReactTestUtils = require('ReactTestUtils');
29
});
30
31
+ it('should call all of the lifecycle hooks', () => {
32
+ const logs = [];
33
+ const logger = message => () => logs.push(message) || true;
34
+
35
+ class SomeComponent extends React.Component {
36
+ componentWillMount = logger('componentWillMount');
37
+ componentDidMount = logger('componentDidMount');
38
+ componentWillReceiveProps = logger('componentWillReceiveProps');
39
+ shouldComponentUpdate = logger('shouldComponentUpdate');
40
+ componentWillUpdate = logger('componentWillUpdate');
41
+ componentDidUpdate = logger('componentDidUpdate');
42
+ componentWillUnmount = logger('componentWillUnmount');
43
+ render() {
44
+ return <div />;
45
+ }
46
+ }
47
+
48
+ const shallowRenderer = createRenderer();
49
+ shallowRenderer.render(<SomeComponent foo={1} />);
50
+
51
+ // Calling cDU might lead to problems with host component references.
52
+ // Since our components aren't really mounted, refs won't be available.
53
+ expect(logs).toEqual(['componentWillMount']);
54
+
55
+ logs.splice(0);
56
+
57
+ const instance = shallowRenderer.getMountedInstance();
58
+ instance.setState({});
59
+
60
+ // The previous shallow renderer triggered cDU for setState() calls.
61
+ expect(logs).toEqual([
62
+ 'shouldComponentUpdate',
63
+ 'componentWillUpdate',
64
+ 'componentDidUpdate',
65
+ ]);
66
+
67
+ logs.splice(0);
68
+
69
+ shallowRenderer.render(<SomeComponent foo={2} />);
70
+
71
+ // The previous shallow renderer did not trigger cDU for props changes.
72
+ expect(logs).toEqual([
73
+ 'componentWillReceiveProps',
74
+ 'shouldComponentUpdate',
75
+ 'componentWillUpdate',
76
+ ]);
77
+ });
78
+
79
+ it('should only render 1 level deep', () => {
80
+ function Parent() {
81
+ return <div><Child /></div>;
82
+ }
83
+ function Child() {
84
+ throw Error('This component should not render');
85
+ }
86
+
87
+ const shallowRenderer = createRenderer();
88
+ shallowRenderer.render(React.createElement(Parent));
89
+ });
90
+
91
it('should have shallow rendering', () => {
92
class SomeComponent extends React.Component {
93
render() {
@@ -38,8 +100,8 @@ describe('ReactTestUtils', () => {
100
}
101
}
102
41
- var shallowRenderer = ReactTestUtils.createRenderer();
42
- var result = shallowRenderer.render(<SomeComponent />);
103
+ const shallowRenderer = createRenderer();
104
+ const result = shallowRenderer.render(<SomeComponent />);
105
106
expect(result.type).toBe('div');
107
expect(result.props.children).toEqual([
@@ -48,21 +110,52 @@ describe('ReactTestUtils', () => {
110
]);
111
});
112
113
+ it('should enable shouldComponentUpdate to prevent a re-render', () => {
114
+ let renderCounter = 0;
115
+ class SimpleComponent extends React.Component {
116
+ state = {update: false};
117
+ shouldComponentUpdate(nextProps, nextState) {
118
+ return this.state.update !== nextState.update;
119
+ }
120
+ render() {
121
+ renderCounter++;
122
+ return <div>{`${renderCounter}`}</div>;
123
+ }
124
+ }
125
+
126
+ const shallowRenderer = createRenderer();
127
+ shallowRenderer.render(<SimpleComponent />);
128
+ expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
129
+
130
+ const instance = shallowRenderer.getMountedInstance();
131
+ instance.setState({update: false});
132
+ expect(shallowRenderer.getRenderOutput()).toEqual(<div>1</div>);
133
+
134
+ instance.setState({update: true});
135
+ expect(shallowRenderer.getRenderOutput()).toEqual(<div>2</div>);
136
+ });
137
+
138
it('should shallow render a functional component', () => {
52
- function SomeComponent() {
139
+ function SomeComponent(props, context) {
140
return (
141
<div>
142
+ <div>{props.foo}</div>
143
+ <div>{context.bar}</div>
144
<span className="child1" />
145
<span className="child2" />
146
</div>
147
);
148
}
149
61
- var shallowRenderer = ReactTestUtils.createRenderer();
62
- var result = shallowRenderer.render(<SomeComponent />);
150
+ const shallowRenderer = createRenderer();
151
+ const result = shallowRenderer.render(<SomeComponent foo={'FOO'} />, {
152
+ bar: 'BAR',
153
+ });
154
155
expect(result.type).toBe('div');
156
expect(result.props.children).toEqual([
157
+ <div>FOO</div>,
158
+ <div>BAR</div>,
159
<span className="child1" />,
160
<span className="child2" />,
161
]);
@@ -75,7 +168,7 @@ describe('ReactTestUtils', () => {
168
}
169
}
170
78
- var shallowRenderer = ReactTestUtils.createRenderer();
171
+ const shallowRenderer = createRenderer();
172
expect(() => shallowRenderer.render(SomeComponent)).toThrowError(
173
'ReactShallowRenderer render(): Invalid component element. Instead of ' +
174
'passing a component class, make sure to instantiate it by passing it ' +
@@ -90,7 +183,7 @@ describe('ReactTestUtils', () => {
183
});
184
185
it('should have shallow unmounting', () => {
93
- var componentWillUnmount = jest.fn();
186
+ const componentWillUnmount = jest.fn();
187
188
class SomeComponent extends React.Component {
189
componentWillUnmount = componentWillUnmount;
@@ -99,7 +192,7 @@ describe('ReactTestUtils', () => {
192
}
193
}
194
102
- var shallowRenderer = ReactTestUtils.createRenderer();
195
+ const shallowRenderer = createRenderer();
196
shallowRenderer.render(<SomeComponent />);
197
shallowRenderer.unmount();
198
@@ -113,8 +206,8 @@ describe('ReactTestUtils', () => {
206
}
207
}
208
116
- var shallowRenderer = ReactTestUtils.createRenderer();
117
- var result = shallowRenderer.render(<SomeComponent />);
209
+ const shallowRenderer = createRenderer();
210
+ const result = shallowRenderer.render(<SomeComponent />);
211
212
expect(result).toBe(null);
213
});
@@ -126,7 +219,7 @@ describe('ReactTestUtils', () => {
219
}
220
}
221
129
- var shallowRenderer = ReactTestUtils.createRenderer();
222
+ const shallowRenderer = createRenderer();
223
// Shouldn't crash.
224
shallowRenderer.render(<SomeComponent />);
225
});
@@ -140,7 +233,7 @@ describe('ReactTestUtils', () => {
233
};
234
235
render() {
143
- var className = this.state.clicked ? 'was-clicked' : '';
236
+ const className = this.state.clicked ? 'was-clicked' : '';
237
238
if (this.props.aNew === 'prop') {
239
return (
@@ -159,21 +252,21 @@ describe('ReactTestUtils', () => {
252
}
253
}
254
162
- var shallowRenderer = ReactTestUtils.createRenderer();
163
- var result = shallowRenderer.render(<SomeComponent />);
255
+ const shallowRenderer = createRenderer();
256
+ const result = shallowRenderer.render(<SomeComponent />);
257
expect(result.type).toBe('div');
258
expect(result.props.children).toEqual([
259
<span className="child1" />,
260
<span className="child2" />,
261
]);
262
170
- var updatedResult = shallowRenderer.render(<SomeComponent aNew="prop" />);
263
+ const updatedResult = shallowRenderer.render(<SomeComponent aNew="prop" />);
264
expect(updatedResult.type).toBe('a');
265
173
- var mockEvent = {};
266
+ const mockEvent = {};
267
updatedResult.props.onClick(mockEvent);
268
176
- var updatedResultCausedByClick = shallowRenderer.getRenderOutput();
269
+ const updatedResultCausedByClick = shallowRenderer.getRenderOutput();
270
expect(updatedResultCausedByClick.type).toBe('a');
271
expect(updatedResultCausedByClick.props.className).toBe('was-clicked');
272
});
@@ -189,7 +282,7 @@ describe('ReactTestUtils', () => {
282
}
283
}
284
192
- var shallowRenderer = ReactTestUtils.createRenderer();
285
+ const shallowRenderer = createRenderer();
286
shallowRenderer.render(<SimpleComponent n={5} />);
287
expect(shallowRenderer.getMountedInstance().someMethod()).toEqual(5);
288
});
@@ -205,8 +298,8 @@ describe('ReactTestUtils', () => {
298
}
299
}
300
208
- var shallowRenderer = ReactTestUtils.createRenderer();
209
- var result = shallowRenderer.render(<SimpleComponent />);
301
+ const shallowRenderer = createRenderer();
302
+ const result = shallowRenderer.render(<SimpleComponent />);
303
expect(result).toEqual(<div />);
304
});
305
@@ -229,9 +322,9 @@ describe('ReactTestUtils', () => {
322
}
323
}
324
232
- var shallowRenderer = ReactTestUtils.createRenderer();
325
+ const shallowRenderer = createRenderer();
326
shallowRenderer.render(<SimpleComponent />);
234
- var result = shallowRenderer.getRenderOutput();
327
+ let result = shallowRenderer.getRenderOutput();
328
expect(result.type).toEqual('div');
329
expect(result.props.className).toEqual('');
330
result.props.onClick();
@@ -252,11 +345,66 @@ describe('ReactTestUtils', () => {
345
}
346
}
347
255
- var shallowRenderer = ReactTestUtils.createRenderer();
256
- var result = shallowRenderer.render(<SimpleComponent />);
348
+ const shallowRenderer = createRenderer();
349
+ const result = shallowRenderer.render(<SimpleComponent />);
350
expect(result).toEqual(<div>doovy</div>);
351
});
352
353
+ it('can setState in componentWillReceiveProps when shallow rendering', () => {
354
+ class SimpleComponent extends React.Component {
355
+ state = {count: 0};
356
+
357
+ componentWillReceiveProps(nextProps) {
358
+ if (nextProps.updateState) {
359
+ this.setState({count: 1});
360
+ }
361
+ }
362
+
363
+ render() {
364
+ return <div>{this.state.count}</div>;
365
+ }
366
+ }
367
+
368
+ const shallowRenderer = createRenderer();
369
+ let result = shallowRenderer.render(
370
+ <SimpleComponent updateState={false} />,
371
+ );
372
+ expect(result.props.children).toEqual(0);
373
+
374
+ result = shallowRenderer.render(<SimpleComponent updateState={true} />);
375
+ expect(result.props.children).toEqual(1);
376
+ });
377
+
378
+ it('can setState with an updater function', () => {
379
+ let instance;
380
+
381
+ class SimpleComponent extends React.Component {
382
+ state = {
383
+ counter: 0,
384
+ };
385
+
386
+ render() {
387
+ instance = this;
388
+ return (
389
+ <button ref="button" onClick={this.onClick}>
390
+ {this.state.counter}
391
+ </button>
392
+ );
393
+ }
394
+ }
395
+
396
+ const shallowRenderer = createRenderer();
397
+ let result = shallowRenderer.render(<SimpleComponent defaultCount={1} />);
398
+ expect(result.props.children).toEqual(0);
399
+
400
+ instance.setState((state, props) => {
401
+ return {counter: props.defaultCount + 1};
402
+ });
403
+
404
+ result = shallowRenderer.getRenderOutput();
405
+ expect(result.props.children).toEqual(2);
406
+ });
407
+
408
it('can pass context when shallowly rendering', () => {
409
class SimpleComponent extends React.Component {
410
static contextTypes = {
@@ -268,13 +416,41 @@ describe('ReactTestUtils', () => {
416
}
417
}
418
271
- var shallowRenderer = ReactTestUtils.createRenderer();
272
- var result = shallowRenderer.render(<SimpleComponent />, {
419
+ const shallowRenderer = createRenderer();
420
+ const result = shallowRenderer.render(<SimpleComponent />, {
421
name: 'foo',
422
});
423
expect(result).toEqual(<div>foo</div>);
424
});
425
426
+ it('should track context across updates', () => {
427
+ class SimpleComponent extends React.Component {
428
+ static contextTypes = {
429
+ foo: PropTypes.string,
430
+ };
431
+
432
+ state = {
433
+ bar: 'bar',
434
+ };
435
+
436
+ render() {
437
+ return <div>{`${this.context.foo}:${this.state.bar}`}</div>;
438
+ }
439
+ }
440
+
441
+ const shallowRenderer = createRenderer();
442
+ let result = shallowRenderer.render(<SimpleComponent />, {
443
+ foo: 'foo',
444
+ });
445
+ expect(result).toEqual(<div>foo:bar</div>);
446
+
447
+ const instance = shallowRenderer.getMountedInstance();
448
+ instance.setState({bar: 'baz'});
449
+
450
+ result = shallowRenderer.getRenderOutput();
451
+ expect(result).toEqual(<div>foo:baz</div>);
452
+ });
453
+
454
it('can fail context when shallowly rendering', () => {
455
spyOn(console, 'error');
456
@@ -288,7 +464,7 @@ describe('ReactTestUtils', () => {
464
}
465
}
466
291
- var shallowRenderer = ReactTestUtils.createRenderer();
467
+ const shallowRenderer = createRenderer();
468
shallowRenderer.render(<SimpleComponent />);
469
expectDev(console.error.calls.count()).toBe(1);
470
expect(
@@ -300,6 +476,32 @@ describe('ReactTestUtils', () => {
476
);
477
});
478
479
+ it('should warn about propTypes (but only once)', () => {
480
+ spyOn(console, 'error');
481
+
482
+ class SimpleComponent extends React.Component {
483
+ render() {
484
+ return React.createElement('div', null, this.props.name);
485
+ }
486
+ }
487
+
488
+ SimpleComponent.propTypes = {
489
+ name: PropTypes.string.isRequired,
490
+ };
491
+
492
+ const shallowRenderer = createRenderer();
493
+ shallowRenderer.render(React.createElement(SimpleComponent, {name: 123}));
494
+
495
+ expect(console.error.calls.count()).toBe(1);
496
+ expect(
497
+ console.error.calls.argsFor(0)[0].replace(/\(at .+?:\d+\)/g, '(at **)'),
498
+ ).toBe(
499
+ 'Warning: Failed prop type: Invalid prop `name` of type `number` ' +
500
+ 'supplied to `SimpleComponent`, expected `string`.\n' +
501
+ ' in SimpleComponent',
502
+ );
503
+ });
504
+
505
it('can scryRenderedDOMComponentsWithClass with TextComponent', () => {
506
class Wrapper extends React.Component {
507
render() {
@@ -307,8 +509,8 @@ describe('ReactTestUtils', () => {
509
}
510
}
511
310
- var renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
311
- var scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass(
512
+ const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
513
+ const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass(
514
renderedComponent,
515
'NonExistentClass',
516
);
@@ -322,8 +524,8 @@ describe('ReactTestUtils', () => {
524
}
525
}
526
325
- var renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
326
- var scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass(
527
+ const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
528
+ const scryResults = ReactTestUtils.scryRenderedDOMComponentsWithClass(
529
renderedComponent,
530
'x',
531
);
@@ -337,20 +539,20 @@ describe('ReactTestUtils', () => {
539
}
540
}
541
340
- var renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
341
- var scryResults1 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
542
+ const renderedComponent = ReactTestUtils.renderIntoDocument(<Wrapper />);
543
+ const scryResults1 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
544
renderedComponent,
545
'x y',
546
);
547
expect(scryResults1.length).toBe(1);
548
347
- var scryResults2 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
549
+ const scryResults2 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
550
renderedComponent,
551
'x z',
552
);
553
expect(scryResults2.length).toBe(1);
554
353
- var scryResults3 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
555
+ const scryResults3 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
556
renderedComponent,
557
['x', 'y'],
558
);
@@ -359,13 +561,13 @@ describe('ReactTestUtils', () => {
561
expect(scryResults1[0]).toBe(scryResults2[0]);
562
expect(scryResults1[0]).toBe(scryResults3[0]);
563
362
- var scryResults4 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
564
+ const scryResults4 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
565
renderedComponent,
566
['x', 'a'],
567
);
568
expect(scryResults4.length).toBe(0);
569
368
- var scryResults5 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
570
+ const scryResults5 = ReactTestUtils.scryRenderedDOMComponentsWithClass(
571
renderedComponent,
572
['x a'],
573
);
@@ -379,7 +581,7 @@ describe('ReactTestUtils', () => {
581
}
582
}
583
382
- var container = document.createElement('div');
584
+ const container = document.createElement('div');
585
ReactDOM.render(
586
<Wrapper>
587
{null}
@@ -387,7 +589,7 @@ describe('ReactTestUtils', () => {
589
</Wrapper>,
590
container,
591
);
390
- var tree = ReactDOM.render(
592
+ const tree = ReactDOM.render(
593
<Wrapper>
594
<div>orange</div>
595
<div>purple</div>
@@ -395,7 +597,7 @@ describe('ReactTestUtils', () => {
597
container,
598
);
599
398
- var log = [];
600
+ const log = [];
601
ReactTestUtils.findAllInRenderedTree(tree, function(child) {
602
if (ReactTestUtils.isDOMComponent(child)) {
603
log.push(ReactDOM.findDOMNode(child).textContent);
@@ -407,9 +609,9 @@ describe('ReactTestUtils', () => {
609
});
610
611
it('should support injected wrapper components as DOM components', () => {
410
- var getTestDocument = require('getTestDocument');
612
+ const getTestDocument = require('getTestDocument');
613
412
- var injectedDOMComponents = [
614
+ const injectedDOMComponents = [
615
'button',
616
'form',
617
'iframe',
@@ -421,7 +623,7 @@ describe('ReactTestUtils', () => {
623
];
624
625
injectedDOMComponents.forEach(function(type) {
424
- var testComponent = ReactTestUtils.renderIntoDocument(
626
+ const testComponent = ReactTestUtils.renderIntoDocument(
627
React.createElement(type),
628
);
629
expect(testComponent.tagName).toBe(type.toUpperCase());
@@ -445,9 +647,9 @@ describe('ReactTestUtils', () => {
647
}
648
}
649
448
- var markup = ReactDOMServer.renderToString(<Root />);
449
- var testDocument = getTestDocument(markup);
450
- var component = ReactDOM.render(<Root />, testDocument);
650
+ const markup = ReactDOMServer.renderToString(<Root />);
651
+ const testDocument = getTestDocument(markup);
652
+ const component = ReactDOM.render(<Root />, testDocument);
653
654
expect(component.refs.html.tagName).toBe('HTML');
655
expect(component.refs.head.tagName).toBe('HEAD');
@@ -458,19 +660,19 @@ describe('ReactTestUtils', () => {
660
});
661
662
it('should change the value of an input field', () => {
461
- var obj = {
663
+ const obj = {
664
handler: function(e) {
665
e.persist();
666
},
667
};
668
spyOn(obj, 'handler').and.callThrough();
467
- var container = document.createElement('div');
468
- var instance = ReactDOM.render(
669
+ const container = document.createElement('div');
670
+ const instance = ReactDOM.render(
671
<input type="text" onChange={obj.handler} />,
672
container,
673
);
674
473
- var node = ReactDOM.findDOMNode(instance);
675
+ const node = ReactDOM.findDOMNode(instance);
676
node.value = 'giraffe';
677
ReactTestUtils.Simulate.change(node);
678
@@ -490,19 +692,19 @@ describe('ReactTestUtils', () => {
692
}
693
}
694
493
- var obj = {
695
+ const obj = {
696
handler: function(e) {
697
e.persist();
698
},
699
};
700
spyOn(obj, 'handler').and.callThrough();
499
- var container = document.createElement('div');
500
- var instance = ReactDOM.render(
701
+ const container = document.createElement('div');
702
+ const instance = ReactDOM.render(
703
<SomeComponent handleChange={obj.handler} />,
704
container,
705
);
706
505
- var node = ReactDOM.findDOMNode(instance.refs.input);
707
+ const node = ReactDOM.findDOMNode(instance.refs.input);
708
node.value = 'zebra';
709
ReactTestUtils.Simulate.change(node);
710
@@ -522,9 +724,9 @@ describe('ReactTestUtils', () => {
724
}
725
}
726
525
- var handler = jasmine.createSpy('spy');
526
- var shallowRenderer = ReactTestUtils.createRenderer();
527
- var result = shallowRenderer.render(
727
+ const handler = jasmine.createSpy('spy');
728
+ const shallowRenderer = createRenderer();
729
+ const result = shallowRenderer.render(
730
<SomeComponent handleClick={handler} />,
731
);
732
@@ -538,7 +740,7 @@ describe('ReactTestUtils', () => {
740
it('should not warn when simulating events with extra properties', () => {
741
spyOn(console, 'error');
742
541
- var CLIENT_X = 100;
743
+ const CLIENT_X = 100;
744
745
class Component extends React.Component {
746
handleClick = e => {
@@ -550,8 +752,8 @@ describe('ReactTestUtils', () => {
752
}
753
}
754
553
- var element = document.createElement('div');
554
- var instance = ReactDOM.render(<Component />, element);
755
+ const element = document.createElement('div');
756
+ const instance = ReactDOM.render(<Component />, element);
757
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(instance), {
758
clientX: CLIENT_X,
759
});
@@ -559,7 +761,7 @@ describe('ReactTestUtils', () => {
761
});
762
763
it('can scry with stateless components involved', () => {
562
- var Stateless = () => <div><hr /></div>;
764
+ const Stateless = () => <div><hr /></div>;
765
766
class SomeComponent extends React.Component {
767
render() {
@@ -572,11 +774,36 @@ describe('ReactTestUtils', () => {
774
}
775
}
776
575
- var inst = ReactTestUtils.renderIntoDocument(<SomeComponent />);
576
- var hrs = ReactTestUtils.scryRenderedDOMComponentsWithTag(inst, 'hr');
777
+ const inst = ReactTestUtils.renderIntoDocument(<SomeComponent />);
778
+ const hrs = ReactTestUtils.scryRenderedDOMComponentsWithTag(inst, 'hr');
779
expect(hrs.length).toBe(2);
780
});
781
782
+ it('should enable rendering of cloned element', () => {
783
+ class SimpleComponent extends React.Component {
784
+ constructor(props) {
785
+ super(props);
786
+
787
+ this.state = {
788
+ bar: 'bar',
789
+ };
790
+ }
791
+
792
+ render() {
793
+ return <div>{`${this.props.foo}:${this.state.bar}`}</div>;
794
+ }
795
+ }
796
+
797
+ const shallowRenderer = createRenderer();
798
+ let result = shallowRenderer.render(<SimpleComponent foo="foo" />);
799
+ expect(result).toEqual(<div>foo:bar</div>);
800
+
801
+ const instance = shallowRenderer.getMountedInstance();
802
+ const cloned = React.cloneElement(instance, {foo: 'baz'});
803
+ result = shallowRenderer.render(cloned);
804
+ expect(result).toEqual(<div>baz:bar</div>);
805
+ });
806
+
807
describe('Simulate', () => {
808
it('should set the type of the event', () => {
809
let event;
@@ -594,5 +821,27 @@ describe('ReactTestUtils', () => {
821
expect(event.type).toBe('keydown');
822
expect(event.nativeEvent.type).toBe('keydown');
823
});
824
+
825
+ it('should work with renderIntoDocument', () => {
826
+ const onChange = jest.fn();
827
+
828
+ class MyComponent extends React.Component {
829
+ render() {
830
+ return <div><input type="text" onChange={onChange} /></div>;
831
+ }
832
+ }
833
+
834
+ const instance = ReactTestUtils.renderIntoDocument(<MyComponent />);
835
+ const input = ReactTestUtils.findRenderedDOMComponentWithTag(
836
+ instance,
837
+ 'input',
838
+ );
839
+ input.value = 'giraffe';
840
+ ReactTestUtils.Simulate.change(input);
841
+
842
+ expect(onChange).toHaveBeenCalledWith(
843
+ jasmine.objectContaining({target: input}),
844
+ );
845
+ });
846
});
847
});
src/renderers/shared/fiber/ReactFiberContext.js
+1
-1
@@ -15,7 +15,7 @@
15
import type {Fiber} from 'ReactFiber';
16
import type {StackCursor} from 'ReactFiberStack';
17
18
-var checkPropTypes = require('checkPropTypes');
18
+var checkPropTypes = require('prop-types/checkPropTypes');
19
var emptyObject = require('fbjs/lib/emptyObject');
20
var getComponentName = require('getComponentName');
21
var invariant = require('fbjs/lib/invariant');
src/renderers/shared/stack/reconciler/ReactCompositeComponent.js
+1
-1
@@ -27,7 +27,7 @@ if (__DEV__) {
27
var warningAboutMissingGetChildContext = {};
28
}
29
30
-var checkPropTypes = require('checkPropTypes');
30
+var checkPropTypes = require('prop-types/checkPropTypes');
31
var emptyObject = require('fbjs/lib/emptyObject');
32
var invariant = require('fbjs/lib/invariant');
33
var shallowEqual = require('fbjs/lib/shallowEqual');
src/renderers/testing/ReactShallowRenderer.js
new
+238
@@ -0,0 +1,238 @@
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
+ * @providesModule ReactShallowRenderer
10
+ * @preventMunge
11
+ */
12
+
13
+'use strict';
14
+
15
+const checkPropTypes = require('prop-types/checkPropTypes');
16
+const React = require('react');
17
+
18
+const emptyObject = require('fbjs/lib/emptyObject');
19
+const invariant = require('fbjs/lib/invariant');
20
+
21
+const {ReactDebugCurrentFrame} = require('ReactGlobalSharedState');
22
+
23
+class ReactShallowRenderer {
24
+ static createRenderer = function() {
25
+ return new ReactShallowRenderer();
26
+ };
27
+
28
+ constructor() {
29
+ this._context = null;
30
+ this._element = null;
31
+ this._instance = null;
32
+ this._newState = null;
33
+ this._rendered = null;
34
+ this._rendering = false;
35
+ this._updater = new Updater(this);
36
+ }
37
+
38
+ getMountedInstance() {
39
+ return this._instance;
40
+ }
41
+
42
+ getRenderOutput() {
43
+ return this._rendered;
44
+ }
45
+
46
+ render(element, context = emptyObject) {
47
+ invariant(
48
+ React.isValidElement(element),
49
+ 'ReactShallowRenderer render(): Invalid component element.%s',
50
+ typeof element === 'function'
51
+ ? ' Instead of passing a component class, make sure to instantiate ' +
52
+ 'it by passing it to React.createElement.'
53
+ : '',
54
+ );
55
+ invariant(
56
+ typeof element.type !== 'string',
57
+ 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
58
+ 'components, not primitives (%s). Instead of calling `.render(el)` and ' +
59
+ 'inspecting the rendered output, look at `el.props` directly instead.',
60
+ element.type,
61
+ );
62
+
63
+ if (this._rendering) {
64
+ return;
65
+ }
66
+
67
+ this._rendering = true;
68
+ this._element = element;
69
+ this._context = context;
70
+
71
+ if (this._instance) {
72
+ this._updateClassComponent(element.props, context);
73
+ } else {
74
+ if (shouldConstruct(element.type)) {
75
+ this._instance = new element.type(
76
+ element.props,
77
+ context,
78
+ this._updater,
79
+ );
80
+
81
+ if (element.type.hasOwnProperty('contextTypes')) {
82
+ ReactDebugCurrentFrame.element = element;
83
+
84
+ checkPropTypes(
85
+ element.type.contextTypes,
86
+ context,
87
+ 'context',
88
+ getName(element.type, this._instance),
89
+ ReactDebugCurrentFrame.getStackAddendum,
90
+ );
91
+
92
+ ReactDebugCurrentFrame.element = null;
93
+ }
94
+
95
+ this._mountClassComponent(element.props, context);
96
+ } else {
97
+ this._rendered = element.type(element.props, context);
98
+ }
99
+ }
100
+
101
+ this._rendering = false;
102
+
103
+ return this.getRenderOutput();
104
+ }
105
+
106
+ unmount() {
107
+ if (this._instance) {
108
+ if (typeof this._instance.componentWillUnmount === 'function') {
109
+ this._instance.componentWillUnmount();
110
+ }
111
+ }
112
+
113
+ this._context = null;
114
+ this._element = null;
115
+ this._newState = null;
116
+ this._rendered = null;
117
+ this._instance = null;
118
+ }
119
+
120
+ _mountClassComponent(props, context) {
121
+ this._instance.context = context;
122
+ this._instance.props = props;
123
+ this._instance.state = this._instance.state || emptyObject;
124
+ this._instance.updater = this._updater;
125
+
126
+ if (typeof this._instance.componentWillMount === 'function') {
127
+ const beforeState = this._newState;
128
+
129
+ this._instance.componentWillMount();
130
+
131
+ // setState may have been called during cWM
132
+ if (beforeState !== this._newState) {
133
+ this._instance.state = this._newState || emptyObject;
134
+ }
135
+ }
136
+
137
+ this._rendered = this._instance.render();
138
+
139
+ // Calling cDU might lead to problems with host component references.
140
+ // Since our components aren't really mounted, refs won't be available.
141
+ // if (typeof this._instance.componentDidMount === 'function') {
142
+ // this._instance.componentDidMount();
143
+ // }
144
+ }
145
+
146
+ _updateClassComponent(props, context) {
147
+ const oldProps = this._instance.props;
148
+ const oldState = this._instance.state;
149
+
150
+ if (
151
+ oldProps !== props &&
152
+ typeof this._instance.componentWillReceiveProps === 'function'
153
+ ) {
154
+ this._instance.componentWillReceiveProps(props);
155
+ }
156
+
157
+ // Read state after cWRP in case it calls setState
158
+ // Fallback to previous instance state to support rendering React.cloneElement()
159
+ const state = this._newState || this._instance.state || emptyObject;
160
+
161
+ if (typeof this._instance.shouldComponentUpdate === 'function') {
162
+ if (
163
+ this._instance.shouldComponentUpdate(props, state, context) === false
164
+ ) {
165
+ this._instance.context = context;
166
+ this._instance.props = props;
167
+ this._instance.state = state;
168
+
169
+ return;
170
+ }
171
+ }
172
+
173
+ if (typeof this._instance.componentWillUpdate === 'function') {
174
+ this._instance.componentWillUpdate(props, state, context);
175
+ }
176
+
177
+ this._instance.context = context;
178
+ this._instance.props = props;
179
+ this._instance.state = state;
180
+
181
+ this._rendered = this._instance.render();
182
+
183
+ // The 15.x shallow renderer triggered cDU for setState() calls only.
184
+ if (
185
+ oldState !== state &&
186
+ typeof this._instance.componentDidUpdate === 'function'
187
+ ) {
188
+ this._instance.componentDidUpdate(oldProps, oldState);
189
+ }
190
+ }
191
+}
192
+
193
+class Updater {
194
+ constructor(renderer) {
195
+ this._renderer = renderer;
196
+ }
197
+
198
+ isMounted(publicInstance) {
199
+ return !!this._renderer._element;
200
+ }
201
+
202
+ enqueueForceUpdate(publicInstance, callback, callerName) {
203
+ this._renderer.render(this._renderer._element, this._renderer._context);
204
+ }
205
+
206
+ enqueueReplaceState(publicInstance, completeState, callback, callerName) {
207
+ this._renderer._newState = completeState;
208
+ this._renderer.render(this._renderer._element, this._renderer._context);
209
+ }
210
+
211
+ enqueueSetState(publicInstance, partialState, callback, callerName) {
212
+ if (typeof partialState === 'function') {
213
+ partialState = partialState(publicInstance.state, publicInstance.props);
214
+ }
215
+
216
+ this._renderer._newState = {
217
+ ...publicInstance.state,
218
+ ...partialState,
219
+ };
220
+
221
+ this._renderer.render(this._renderer._element, this._renderer._context);
222
+ }
223
+}
224
+
225
+function getName(type, instance) {
226
+ var constructor = instance && instance.constructor;
227
+ return type.displayName ||
228
+ (constructor && constructor.displayName) ||
229
+ type.name ||
230
+ (constructor && constructor.name) ||
231
+ null;
232
+}
233
+
234
+function shouldConstruct(Component) {
235
+ return !!(Component.prototype && Component.prototype.isReactComponent);
236
+}
237
+
238
+module.exports = ReactShallowRenderer;
src/renderers/testing/stack/ReactTestRendererStack.js
+35
-22
@@ -25,8 +25,8 @@ var ReactTestTextComponent = require('ReactTestTextComponent');
25
var ReactTestEmptyComponent = require('ReactTestEmptyComponent');
26
var invariant = require('fbjs/lib/invariant');
27
28
-import type {ReactElement} from 'ReactElementType';
28
import type {ReactInstance} from 'ReactInstanceType';
29
+import type {TestRendererOptions} from 'ReactTestMount';
30
import type {ReactText} from 'ReactTypes';
31
32
type ReactTestRendererJSON = {
@@ -36,6 +36,31 @@ type ReactTestRendererJSON = {
36
$$typeof?: any,
37
};
38
39
+let injected = false;
40
+function inject() {
41
+ if (injected) {
42
+ return;
43
+ }
44
+
45
+ injected = true;
46
+
47
+ ReactUpdates.injection.injectReconcileTransaction(
48
+ ReactTestReconcileTransaction,
49
+ );
50
+ ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
51
+
52
+ ReactHostComponent.injection.injectGenericComponentClass(ReactTestComponent);
53
+ ReactHostComponent.injection.injectTextComponentClass(ReactTestTextComponent);
54
+ ReactEmptyComponent.injection.injectEmptyComponentFactory(function() {
55
+ return new ReactTestEmptyComponent();
56
+ });
57
+
58
+ ReactComponentEnvironment.injection.injectEnvironment({
59
+ processChildrenUpdates: function() {},
60
+ replaceNodeWithMarkup: function() {},
61
+ });
62
+}
63
+
64
/**
65
* Drill down (through composites and empty components) until we get a native or
66
* native text component.
@@ -54,13 +79,13 @@ function getRenderedHostOrTextFromComponent(component) {
79
var UNSET = {};
80
81
class ReactTestComponent {
57
- _currentElement: ReactElement;
82
+ _currentElement: ReactElement<any>;
83
_renderedChildren: null | Object;
84
_topLevelWrapper: null | ReactInstance;
85
_hostContainerInfo: null | Object;
86
_nodeMock: Object;
87
63
- constructor(element: ReactElement) {
88
+ constructor(element: ReactElement<any>) {
89
this._currentElement = element;
90
this._renderedChildren = null;
91
this._topLevelWrapper = null;
@@ -82,7 +107,7 @@ class ReactTestComponent {
107
}
108
109
receiveComponent(
85
- nextElement: ReactElement,
110
+ nextElement: ReactElement<any>,
111
transaction: ReactTestReconcileTransaction,
112
context: Object,
113
) {
@@ -113,7 +138,7 @@ class ReactTestComponent {
138
}
139
}
140
var object: ReactTestRendererJSON = {
116
- type: this._currentElement.type,
141
+ type: ((this._currentElement.type: any): string),
142
props: props,
143
children: childrenJSON.length ? childrenJSON : null,
144
};
@@ -134,24 +159,12 @@ Object.assign(ReactTestComponent.prototype, ReactMultiChild);
159
160
// =============================================================================
161
137
-ReactUpdates.injection.injectReconcileTransaction(
138
- ReactTestReconcileTransaction,
139
-);
140
-ReactUpdates.injection.injectBatchingStrategy(ReactDefaultBatchingStrategy);
141
-
142
-ReactHostComponent.injection.injectGenericComponentClass(ReactTestComponent);
143
-ReactHostComponent.injection.injectTextComponentClass(ReactTestTextComponent);
144
-ReactEmptyComponent.injection.injectEmptyComponentFactory(function() {
145
- return new ReactTestEmptyComponent();
146
-});
147
-
148
-ReactComponentEnvironment.injection.injectEnvironment({
149
- processChildrenUpdates: function() {},
150
- replaceNodeWithMarkup: function() {},
151
-});
152
-
162
var ReactTestRenderer = {
154
- create: ReactTestMount.render,
163
+ create: (element: ReactElement<any>, options?: TestRendererOptions) => {
164
+ inject();
165
+
166
+ return ReactTestMount.render(element, options);
167
+ },
168
/* eslint-disable camelcase */
169
unstable_batchedUpdates: ReactUpdates.batchedUpdates,
170
/* eslint-enable camelcase */
src/test/ReactShallowRenderer.js
deleted
-145
@@ -1,145 +0,0 @@
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
- * @providesModule ReactShallowRenderer
10
- * @preventMunge
11
- */
12
-
13
-'use strict';
14
-
15
-var React = require('react');
16
-var ReactDOMInjection = require('ReactDOMInjection');
17
-var ReactDOMStackInjection = require('ReactDOMStackInjection');
18
-var ReactCompositeComponent = require('ReactCompositeComponent');
19
-var ReactReconciler = require('ReactReconciler');
20
-var ReactUpdates = require('ReactUpdates');
21
-
22
-var emptyObject = require('fbjs/lib/emptyObject');
23
-var getNextDebugID = require('getNextDebugID');
24
-var invariant = require('fbjs/lib/invariant');
25
-
26
-class NoopInternalComponent {
27
- constructor(element) {
28
- this._renderedOutput = element;
29
- this._currentElement = element;
30
-
31
- if (__DEV__) {
32
- this._debugID = getNextDebugID();
33
- }
34
- }
35
- mountComponent() {}
36
- receiveComponent(element) {
37
- this._renderedOutput = element;
38
- this._currentElement = element;
39
- }
40
- unmountComponent() {}
41
- getHostNode() {
42
- return undefined;
43
- }
44
- getPublicInstance() {
45
- return null;
46
- }
47
-}
48
-
49
-var ShallowComponentWrapper = function(element) {
50
- // TODO: Consolidate with instantiateReactComponent
51
- if (__DEV__) {
52
- this._debugID = getNextDebugID();
53
- }
54
-
55
- this.construct(element);
56
-};
57
-Object.assign(ShallowComponentWrapper.prototype, ReactCompositeComponent, {
58
- _constructComponent: ReactCompositeComponent._constructComponentWithoutOwner,
59
- _instantiateReactComponent: function(element) {
60
- return new NoopInternalComponent(element);
61
- },
62
- _replaceNodeWithMarkup: function() {},
63
- _renderValidatedComponent: ReactCompositeComponent._renderValidatedComponentWithoutOwnerOrContext,
64
-});
65
-
66
-function _batchedRender(renderer, element, context) {
67
- var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true);
68
- renderer._render(element, transaction, context);
69
- ReactUpdates.ReactReconcileTransaction.release(transaction);
70
-}
71
-
72
-class ReactShallowRenderer {
73
- _instance = null;
74
- getMountedInstance() {
75
- return this._instance ? this._instance._instance : null;
76
- }
77
- render(element, context) {
78
- // Ensure we've done the default injections. This might not be true in the
79
- // case of a simple test that only requires React and the TestUtils in
80
- // conjunction with an inline-requires transform.
81
- ReactDOMInjection.inject();
82
- ReactDOMStackInjection.inject();
83
-
84
- invariant(
85
- React.isValidElement(element),
86
- 'ReactShallowRenderer render(): Invalid component element.%s',
87
- typeof element === 'function'
88
- ? ' Instead of passing a component class, make sure to instantiate ' +
89
- 'it by passing it to React.createElement.'
90
- : '',
91
- );
92
- invariant(
93
- typeof element.type !== 'string',
94
- 'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
95
- 'components, not primitives (%s). Instead of calling `.render(el)` and ' +
96
- 'inspecting the rendered output, look at `el.props` directly instead.',
97
- element.type,
98
- );
99
-
100
- if (!context) {
101
- context = emptyObject;
102
- }
103
- ReactUpdates.batchedUpdates(_batchedRender, this, element, context);
104
-
105
- return this.getRenderOutput();
106
- }
107
- getRenderOutput() {
108
- return (this._instance &&
109
- this._instance._renderedComponent &&
110
- this._instance._renderedComponent._renderedOutput) ||
111
- null;
112
- }
113
- unmount() {
114
- if (this._instance) {
115
- ReactReconciler.unmountComponent(
116
- this._instance,
117
- false /* safely */,
118
- false /* skipLifecycle */,
119
- );
120
- }
121
- }
122
- _render(element, transaction, context) {
123
- if (this._instance) {
124
- ReactReconciler.receiveComponent(
125
- this._instance,
126
- element,
127
- transaction,
128
- context,
129
- );
130
- } else {
131
- var instance = new ShallowComponentWrapper(element);
132
- ReactReconciler.mountComponent(
133
- instance,
134
- transaction,
135
- null,
136
- null,
137
- context,
138
- 0,
139
- );
140
- this._instance = instance;
141
- }
142
- }
143
-}
144
-
145
-module.exports = ReactShallowRenderer;