Skip to content

Commit

Permalink
Refactor ShallowRenderer (facebook#7739)
Browse files Browse the repository at this point in the history
* Separate ReactShallowRenderer from TestUtils

* Use ES classes to ReactShallowRenderer
  • Loading branch information
koba04 authored and gaearon committed Oct 4, 2016
1 parent f96237c commit 85b0377
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 136 deletions.
139 changes: 139 additions & 0 deletions src/test/ReactShallowRenderer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactShallowRenderer
*/

'use strict';

var React = require('React');
var ReactDefaultInjection = require('ReactDefaultInjection');
var ReactCompositeComponent = require('ReactCompositeComponent');
var ReactReconciler = require('ReactReconciler');
var ReactUpdates = require('ReactUpdates');

var emptyObject = require('emptyObject');
var invariant = require('invariant');

var nextDebugID = 1;

class NoopInternalComponent {
constructor(element) {
this._renderedOutput = element;
this._currentElement = element;

if (__DEV__) {
this._debugID = nextDebugID++;
}
}
mountComponent() {}
receiveComponent(element) {
this._renderedOutput = element;
this._currentElement = element;
}
unmountComponent() {}
getHostNode() {
return undefined;
}
getPublicInstance() {
return null;
}
}

var ShallowComponentWrapper = function(element) {
// TODO: Consolidate with instantiateReactComponent
if (__DEV__) {
this._debugID = nextDebugID++;
}

this.construct(element);
};
Object.assign(
ShallowComponentWrapper.prototype,
ReactCompositeComponent, {
_constructComponent:
ReactCompositeComponent._constructComponentWithoutOwner,
_instantiateReactComponent: function(element) {
return new NoopInternalComponent(element);
},
_replaceNodeWithMarkup: function() {},
_renderValidatedComponent:
ReactCompositeComponent
._renderValidatedComponentWithoutOwnerOrContext,
}
);

function _batchedRender(renderer, element, context) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true);
renderer._render(element, transaction, context);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}

class ReactShallowRenderer {
_instance = null;
getMountedInstance() {
return this._instance ? this._instance._instance : null;
}
render(element, context) {
// Ensure we've done the default injections. This might not be true in the
// case of a simple test that only requires React and the TestUtils in
// conjunction with an inline-requires transform.
ReactDefaultInjection.inject();

invariant(
React.isValidElement(element),
'ReactShallowRenderer render(): Invalid component element.%s',
typeof element === 'function' ?
' Instead of passing a component class, make sure to instantiate ' +
'it by passing it to React.createElement.' :
''
);
invariant(
typeof element.type !== 'string',
'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
'components, not primitives (%s). Instead of calling `.render(el)` and ' +
'inspecting the rendered output, look at `el.props` directly instead.',
element.type
);

if (!context) {
context = emptyObject;
}
ReactUpdates.batchedUpdates(_batchedRender, this, element, context);

return this.getRenderOutput();
}
getRenderOutput() {
return (
(this._instance && this._instance._renderedComponent &&
this._instance._renderedComponent._renderedOutput)
|| null
);
}
unmount() {
if (this._instance) {
ReactReconciler.unmountComponent(this._instance, false);
}
}
_render(element, transaction, context) {
if (this._instance) {
ReactReconciler.receiveComponent(
this._instance,
element,
transaction,
context
);
} else {
var instance = new ShallowComponentWrapper(element);
ReactReconciler.mountComponent(instance, transaction, null, null, context, 0);
this._instance = instance;
}
}
}

module.exports = ReactShallowRenderer;
137 changes: 1 addition & 136 deletions src/test/ReactTestUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,14 @@ var EventPluginHub = require('EventPluginHub');
var EventPluginRegistry = require('EventPluginRegistry');
var EventPropagators = require('EventPropagators');
var React = require('React');
var ReactDefaultInjection = require('ReactDefaultInjection');
var ReactDOM = require('ReactDOM');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
var ReactCompositeComponent = require('ReactCompositeComponent');
var ReactInstanceMap = require('ReactInstanceMap');
var ReactReconciler = require('ReactReconciler');
var ReactUpdates = require('ReactUpdates');
var SyntheticEvent = require('SyntheticEvent');
var ReactShallowRenderer = require('ReactShallowRenderer');

var emptyObject = require('emptyObject');
var findDOMNode = require('findDOMNode');
var invariant = require('invariant');

Expand Down Expand Up @@ -365,138 +362,6 @@ var ReactTestUtils = {
SimulateNative: {},
};

/**
* @class ReactShallowRenderer
*/
var ReactShallowRenderer = function() {
this._instance = null;
};

ReactShallowRenderer.prototype.getMountedInstance = function() {
return this._instance ? this._instance._instance : null;
};

var nextDebugID = 1;

var NoopInternalComponent = function(element) {
this._renderedOutput = element;
this._currentElement = element;

if (__DEV__) {
this._debugID = nextDebugID++;
}
};

NoopInternalComponent.prototype = {

mountComponent: function() {
},

receiveComponent: function(element) {
this._renderedOutput = element;
this._currentElement = element;
},

getHostNode: function() {
return undefined;
},

unmountComponent: function() {
},

getPublicInstance: function() {
return null;
},
};

var ShallowComponentWrapper = function(element) {
// TODO: Consolidate with instantiateReactComponent
if (__DEV__) {
this._debugID = nextDebugID++;
}

this.construct(element);
};
Object.assign(
ShallowComponentWrapper.prototype,
ReactCompositeComponent, {
_constructComponent:
ReactCompositeComponent._constructComponentWithoutOwner,
_instantiateReactComponent: function(element) {
return new NoopInternalComponent(element);
},
_replaceNodeWithMarkup: function() {},
_renderValidatedComponent:
ReactCompositeComponent
._renderValidatedComponentWithoutOwnerOrContext,
}
);

ReactShallowRenderer.prototype.render = function(element, context) {
// Ensure we've done the default injections. This might not be true in the
// case of a simple test that only requires React and the TestUtils in
// conjunction with an inline-requires transform.
ReactDefaultInjection.inject();

invariant(
React.isValidElement(element),
'ReactShallowRenderer render(): Invalid component element.%s',
typeof element === 'function' ?
' Instead of passing a component class, make sure to instantiate ' +
'it by passing it to React.createElement.' :
''
);
invariant(
typeof element.type !== 'string',
'ReactShallowRenderer render(): Shallow rendering works only with custom ' +
'components, not primitives (%s). Instead of calling `.render(el)` and ' +
'inspecting the rendered output, look at `el.props` directly instead.',
element.type
);

if (!context) {
context = emptyObject;
}
ReactUpdates.batchedUpdates(_batchedRender, this, element, context);

return this.getRenderOutput();
};

function _batchedRender(renderer, element, context) {
var transaction = ReactUpdates.ReactReconcileTransaction.getPooled(true);
renderer._render(element, transaction, context);
ReactUpdates.ReactReconcileTransaction.release(transaction);
}

ReactShallowRenderer.prototype.getRenderOutput = function() {
return (
(this._instance && this._instance._renderedComponent &&
this._instance._renderedComponent._renderedOutput)
|| null
);
};

ReactShallowRenderer.prototype.unmount = function() {
if (this._instance) {
ReactReconciler.unmountComponent(this._instance, false);
}
};

ReactShallowRenderer.prototype._render = function(element, transaction, context) {
if (this._instance) {
ReactReconciler.receiveComponent(
this._instance,
element,
transaction,
context
);
} else {
var instance = new ShallowComponentWrapper(element);
ReactReconciler.mountComponent(instance, transaction, null, null, context, 0);
this._instance = instance;
}
};

/**
* Exports:
*
Expand Down

0 comments on commit 85b0377

Please sign in to comment.