@samitouri / QOS-React / commits / 66f2097f33

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;