enzyme
Version:
JavaScript Testing utilities for React
1,367 lines (1,166 loc) • 215 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _get = function () {
function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }
return get;
}();
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
var _object = require('object.assign');
var _object2 = _interopRequireDefault(_object);
var _arrayPrototype = require('array.prototype.flat');
var _arrayPrototype2 = _interopRequireDefault(_arrayPrototype);
var _has = require('has');
var _has2 = _interopRequireDefault(_has);
var _enzymeShallowEqual = require('enzyme-shallow-equal');
var _enzymeShallowEqual2 = _interopRequireDefault(_enzymeShallowEqual);
var _Utils = require('./Utils');
var _getAdapter = require('./getAdapter');
var _getAdapter2 = _interopRequireDefault(_getAdapter);
var _Debug = require('./Debug');
var _RSTTraversal = require('./RSTTraversal');
var _selectors = require('./selectors');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
var NODE = (0, _Utils.sym)('__node__');
var NODES = (0, _Utils.sym)('__nodes__');
var RENDERER = (0, _Utils.sym)('__renderer__');
var UNRENDERED = (0, _Utils.sym)('__unrendered__');
var ROOT = (0, _Utils.sym)('__root__');
var OPTIONS = (0, _Utils.sym)('__options__');
var SET_STATE = (0, _Utils.sym)('__setState__');
var ROOT_NODES = (0, _Utils.sym)('__rootNodes__');
var CHILD_CONTEXT = (0, _Utils.sym)('__childContext__');
var WRAPPING_COMPONENT = (0, _Utils.sym)('__wrappingComponent__');
var PRIMARY_WRAPPER = (0, _Utils.sym)('__primaryWrapper__');
var ROOT_FINDER = (0, _Utils.sym)('__rootFinder__');
var PROVIDER_VALUES = (0, _Utils.sym)('__providerValues__');
/**
* Finds all nodes in the current wrapper nodes' render trees that match the provided predicate
* function.
*
* @param {ShallowWrapper} wrapper
* @param {Function} predicate
* @param {Function} filter
* @returns {ShallowWrapper}
*/
function findWhereUnwrapped(wrapper, predicate) {
var filter = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _RSTTraversal.treeFilter;
return wrapper.flatMap(function (n) {
return filter(n.getNodeInternal(), predicate);
});
}
/**
* Returns a new wrapper instance with only the nodes of the current wrapper instance that match
* the provided predicate function.
*
* @param {ShallowWrapper} wrapper
* @param {Function} predicate
* @returns {ShallowWrapper}
*/
function filterWhereUnwrapped(wrapper, predicate) {
return wrapper.wrap(wrapper.getNodesInternal().filter(predicate).filter(Boolean));
}
/**
* Ensure options passed to ShallowWrapper are valid. Throws otherwise.
* @param {Object} options
*/
function validateOptions(options) {
var lifecycleExperimental = options.lifecycleExperimental,
disableLifecycleMethods = options.disableLifecycleMethods,
enableComponentDidUpdateOnSetState = options.enableComponentDidUpdateOnSetState,
supportPrevContextArgumentOfComponentDidUpdate = options.supportPrevContextArgumentOfComponentDidUpdate,
lifecycles = options.lifecycles;
if (typeof lifecycleExperimental !== 'undefined' && typeof lifecycleExperimental !== 'boolean') {
throw new Error('lifecycleExperimental must be either true or false if provided');
}
if (typeof disableLifecycleMethods !== 'undefined' && typeof disableLifecycleMethods !== 'boolean') {
throw new Error('disableLifecycleMethods must be either true or false if provided');
}
if (lifecycleExperimental != null && disableLifecycleMethods != null && lifecycleExperimental === disableLifecycleMethods) {
throw new Error('lifecycleExperimental and disableLifecycleMethods cannot be set to the same value');
}
if (typeof enableComponentDidUpdateOnSetState !== 'undefined' && lifecycles.componentDidUpdate && lifecycles.componentDidUpdate.onSetState !== enableComponentDidUpdateOnSetState) {
throw new TypeError('the legacy enableComponentDidUpdateOnSetState option should be matched by `lifecycles: { componentDidUpdate: { onSetState: true } }`, for compatibility');
}
if (typeof supportPrevContextArgumentOfComponentDidUpdate !== 'undefined' && lifecycles.componentDidUpdate && lifecycles.componentDidUpdate.prevContext !== supportPrevContextArgumentOfComponentDidUpdate) {
throw new TypeError('the legacy supportPrevContextArgumentOfComponentDidUpdate option should be matched by `lifecycles: { componentDidUpdate: { prevContext: true } }`, for compatibility');
}
}
function getAdapterLifecycles(_ref) {
var options = _ref.options;
var _options$lifecycles = options.lifecycles,
lifecycles = _options$lifecycles === undefined ? {} : _options$lifecycles,
enableComponentDidUpdateOnSetState = options.enableComponentDidUpdateOnSetState,
supportPrevContextArgumentOfComponentDidUpdate = options.supportPrevContextArgumentOfComponentDidUpdate;
var hasLegacySetStateArg = typeof enableComponentDidUpdateOnSetState !== 'undefined';
var hasLegacyPrevContextArg = typeof supportPrevContextArgumentOfComponentDidUpdate !== 'undefined';
var componentDidUpdate = hasLegacySetStateArg || hasLegacyPrevContextArg ? (0, _object2['default'])({}, hasLegacySetStateArg && {
onSetState: !!enableComponentDidUpdateOnSetState
}, hasLegacyPrevContextArg && {
prevContext: !!supportPrevContextArgumentOfComponentDidUpdate
}) : null;
var originalGDSFP = lifecycles.getDerivedStateFromProps;
var getDerivedStateFromProps = originalGDSFP ? {
hasShouldComponentUpdateBug: !!originalGDSFP.hasShouldComponentUpdateBug
} : false;
return (0, _object2['default'])({}, lifecycles, {
setState: (0, _object2['default'])({}, lifecycles.setState),
getChildContext: (0, _object2['default'])({
calledByRenderer: true
}, lifecycles.getChildContext)
}, componentDidUpdate && { componentDidUpdate: componentDidUpdate }, {
getDerivedStateFromProps: getDerivedStateFromProps
});
}
function getRootNode(node) {
if (node.nodeType === 'host') {
return node;
}
return node.rendered;
}
function getRootNodeInternal(wrapper) {
if (wrapper[ROOT].length !== 1) {
throw new Error('getRootNodeInternal(wrapper) can only be called when wrapper wraps one node');
}
if (wrapper[ROOT] !== wrapper) {
return wrapper[ROOT_NODES][0];
}
return wrapper[ROOT][NODE];
}
function nodeParents(wrapper, node) {
return (0, _RSTTraversal.parentsOfNode)(node, getRootNodeInternal(wrapper));
}
function privateSetNodes(wrapper, nodes) {
if (!Array.isArray(nodes)) {
(0, _Utils.privateSet)(wrapper, NODE, nodes);
(0, _Utils.privateSet)(wrapper, NODES, [nodes]);
} else {
(0, _Utils.privateSet)(wrapper, NODE, nodes[0]);
(0, _Utils.privateSet)(wrapper, NODES, nodes);
}
(0, _Utils.privateSet)(wrapper, 'length', wrapper[NODES].length);
}
function pureComponentShouldComponentUpdate(prevProps, props, prevState, state) {
return !(0, _enzymeShallowEqual2['default'])(prevProps, props) || !(0, _enzymeShallowEqual2['default'])(prevState, state);
}
function isPureComponent(instance) {
return instance && instance.isPureReactComponent;
}
function getChildContext(node, hierarchy, renderer) {
var instance = node.instance,
Component = node.type;
var componentName = (0, _Utils.displayNameOfNode)(node);
// Warn like react if childContextTypes is not defined:
// https://github.com/facebook/react/blob/1454a8be03794f5e0b23a7e7696cbbbdcf8b0f5d/packages/react-dom/src/server/ReactPartialRenderer.js#L639-L646
if (_typeof(Component.childContextTypes) !== 'object') {
// eslint-disable-next-line no-console
console.warn(String(componentName) + '.getChildContext(): childContextTypes must be defined in order to use getChildContext().');
return {};
}
// Check childContextTypes like react:
// https://github.com/facebook/react/blob/1454a8be03794f5e0b23a7e7696cbbbdcf8b0f5d/packages/react-dom/src/server/ReactPartialRenderer.js#L630-L637
var childContext = instance.getChildContext();
Object.keys(childContext).forEach(function (key) {
if (!(key in Component.childContextTypes)) {
throw new Error(String(componentName) + '.getChildContext(): key "' + String(key) + '" is not defined in childContextTypes.');
}
});
if (typeof renderer.checkPropTypes === 'function') {
renderer.checkPropTypes(Component.childContextTypes, childContext, 'child context', hierarchy);
}
return childContext;
}
function spyOnGetChildContextInitialRender(nodes, adapter) {
if (!(0, _Utils.isCustomComponentElement)(nodes, adapter) || !nodes.type.prototype || typeof nodes.type.prototype.getChildContext !== 'function') {
return null;
}
return (0, _Utils.spyMethod)(nodes.type.prototype, 'getChildContext');
}
function privateSetChildContext(adapter, wrapper, instance, renderedNode, getChildContextSpy) {
var renderer = wrapper[RENDERER];
// We only support parent-based context.
if (adapter.options.legacyContextMode !== 'parent') {
return;
}
if (getChildContextSpy) {
(0, _Utils.privateSet)(wrapper, CHILD_CONTEXT, getChildContextSpy.getLastReturnValue());
getChildContextSpy.restore();
} else if (typeof instance.getChildContext === 'function') {
// If there's no spy but getChildContext is a function, that means our renderer
// is not going to call it for us, so we need to call it ourselves.
var nodeHierarchy = [wrapper[NODE]].concat(nodeParents(wrapper, wrapper[NODE]));
var childContext = getChildContext(renderedNode, nodeHierarchy, renderer);
(0, _Utils.privateSet)(wrapper, CHILD_CONTEXT, childContext);
} else {
(0, _Utils.privateSet)(wrapper, CHILD_CONTEXT, null);
}
}
function mockSCUIfgDSFPReturnNonNull(node, state) {
var getDerivedStateFromProps = node.type.getDerivedStateFromProps;
if (typeof getDerivedStateFromProps === 'function') {
// we try to fix a React shallow renderer bug here.
// (facebook/react#14607, which has been fixed in react 16.8):
// when gDSFP return derived state, it will set instance state in shallow renderer before SCU,
// this will cause `this.state` in sCU be the updated state, which is wrong behavior.
// so we have to wrap sCU to pass the old state to original sCU.
var instance = node.instance;
var _spyMethod = (0, _Utils.spyMethod)(instance, 'shouldComponentUpdate', function (originalSCU) {
return function () {
function shouldComponentUpdate() {
instance.state = state;
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
var sCUResult = originalSCU.apply(instance, args);
var nextState = args[1];
instance.state = nextState;
restore();
return sCUResult;
}
return shouldComponentUpdate;
}();
}),
restore = _spyMethod.restore;
}
}
/**
* Recursively dive()s every custom component in a wrapper until
* the target component is found.
*
* @param {ShallowWrapper} wrapper A ShallowWrapper to search
* @param {ComponentType} target A react custom component that, when found, will end recursion
* @param {Adapter} adapter An Enzyme adapter
* @returns {ShallowWrapper|undefined} A ShallowWrapper for the target, or
* undefined if it can't be found
*/
function deepRender(wrapper, target, adapter) {
var node = wrapper[NODE];
var element = node && adapter.nodeToElement(node);
if (wrapper.type() === target) {
return wrapper.dive();
}
if (element && (0, _Utils.isCustomComponentElement)(element, adapter)) {
return deepRender(wrapper.dive(), target, adapter);
}
var children = wrapper.children();
for (var i = 0; i < children.length; i += 1) {
var found = deepRender(children.at(i), target, adapter);
if (typeof found !== 'undefined') {
return found;
}
}
return undefined;
}
/**
* Deep-renders the `wrappingComponent` and returns the context that should
* be accessible to the primary wrapper.
*
* @param {WrappingComponentWrapper} wrapper The `WrappingComponentWrapper` for a
* `wrappingComponent`
* @param {Adapter} adapter An Enzyme adapter
* @returns {object} An object containing an object of legacy context values and a Map of
* `createContext()` Provider values.
*/
function getContextFromWrappingComponent(wrapper, adapter) {
var rootFinder = deepRender(wrapper, wrapper[ROOT_FINDER], adapter);
if (!rootFinder) {
throw new Error('`wrappingComponent` must render its children!');
}
return {
legacyContext: rootFinder[OPTIONS].context,
providerValues: rootFinder[PROVIDER_VALUES]
};
}
/**
* Makes options specifically for `ShallowWrapper`. Most of the logic here is around rendering
* a `wrappingComponent` (if one was provided) and adding the child context of that component
* to `options.context`.
*
* @param {ReactElement} nodes the nodes passed to `ShallowWrapper`
* @param {ShallowWrapper} root this `ShallowWrapper`'s parent. If this is passed, options are
* not transformed.
* @param {*} passedOptions the options passed to `ShallowWrapper`.
* @param {*} wrapper the `ShallowWrapper` itself
* @returns {Object} the decorated and transformed options
*/
function makeShallowOptions(nodes, root, passedOptions, wrapper) {
var options = (0, _Utils.makeOptions)(passedOptions);
var adapter = (0, _getAdapter2['default'])(passedOptions);
(0, _Utils.privateSet)(options, PROVIDER_VALUES, passedOptions[PROVIDER_VALUES]);
if (root || !(0, _Utils.isCustomComponent)(options.wrappingComponent, adapter)) {
return options;
}
if (typeof adapter.wrapWithWrappingComponent !== 'function') {
throw new TypeError('your adapter does not support `wrappingComponent`. Try upgrading it!');
}
var _adapter$wrapWithWrap = adapter.wrapWithWrappingComponent(nodes, options),
wrappedNode = _adapter$wrapWithWrap.node,
RootFinder = _adapter$wrapWithWrap.RootFinder;
// eslint-disable-next-line no-use-before-define
var wrappingComponent = new WrappingComponentWrapper(wrappedNode, wrapper, RootFinder);
var _getContextFromWrappi = getContextFromWrappingComponent(wrappingComponent, adapter),
wrappingComponentLegacyContext = _getContextFromWrappi.legacyContext,
wrappingComponentProviderValues = _getContextFromWrappi.providerValues;
(0, _Utils.privateSet)(wrapper, WRAPPING_COMPONENT, wrappingComponent);
return (0, _object2['default'])({}, options, _defineProperty({
context: (0, _object2['default'])({}, options.context, wrappingComponentLegacyContext)
}, PROVIDER_VALUES, wrappingComponentProviderValues));
}
function makeInheritedChildOptions(wrapper) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
var childOptions = (0, _object2['default'])({}, wrapper[OPTIONS], options, {
context: options.context || (0, _object2['default'])({}, wrapper[OPTIONS].context, wrapper[ROOT][CHILD_CONTEXT])
});
(0, _Utils.privateSet)(childOptions, PROVIDER_VALUES, wrapper[ROOT][PROVIDER_VALUES]);
return childOptions;
}
/**
* @class ShallowWrapper
*/
var ShallowWrapper = function () {
function ShallowWrapper(nodes, root) {
var _this = this;
var passedOptions = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
_classCallCheck(this, ShallowWrapper);
validateOptions(passedOptions);
var options = makeShallowOptions(nodes, root, passedOptions, this);
var adapter = (0, _getAdapter2['default'])(options);
var lifecycles = getAdapterLifecycles(adapter);
// mounting a ShallowRender component
if (!root) {
if (!adapter.isValidElement(nodes)) {
throw new TypeError('ShallowWrapper can only wrap valid elements');
}
var getChildContextSpy = lifecycles.getChildContext.calledByRenderer ? spyOnGetChildContextInitialRender(nodes, adapter) : null;
(0, _Utils.privateSet)(this, ROOT, this);
(0, _Utils.privateSet)(this, UNRENDERED, nodes);
var renderer = adapter.createRenderer((0, _object2['default'])({ mode: 'shallow' }, options));
(0, _Utils.privateSet)(this, RENDERER, renderer);
var providerValues = new Map(options[PROVIDER_VALUES] || []);
this[RENDERER].render(nodes, options.context, { providerValues: providerValues });
var renderedNode = this[RENDERER].getNode();
privateSetNodes(this, getRootNode(renderedNode));
(0, _Utils.privateSet)(this, OPTIONS, options);
(0, _Utils.privateSet)(this, PROVIDER_VALUES, providerValues);
var instance = renderedNode.instance;
if (instance && !options.disableLifecycleMethods) {
// Ensure to call componentDidUpdate when instance.setState is called
if (lifecycles.componentDidUpdate.onSetState && !instance[SET_STATE]) {
(0, _Utils.privateSet)(instance, SET_STATE, instance.setState);
instance.setState = function (updater) {
var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
return _this.setState.apply(_this, _toConsumableArray(callback == null ? [updater] : [updater, callback]));
};
}
if (typeof instance.componentDidMount === 'function') {
this[RENDERER].batchedUpdates(function () {
instance.componentDidMount();
});
}
privateSetChildContext(adapter, this, instance, renderedNode, getChildContextSpy);
}
// creating a child component through enzyme's ShallowWrapper APIs.
} else {
(0, _Utils.privateSet)(this, ROOT, root);
(0, _Utils.privateSet)(this, UNRENDERED, null);
(0, _Utils.privateSet)(this, RENDERER, root[RENDERER]);
privateSetNodes(this, nodes);
(0, _Utils.privateSet)(this, OPTIONS, root[OPTIONS]);
(0, _Utils.privateSet)(this, ROOT_NODES, root[NODES]);
(0, _Utils.privateSet)(this, PROVIDER_VALUES, null);
}
}
/**
* Returns the root wrapper
*
* @return {ShallowWrapper}
*/
_createClass(ShallowWrapper, [{
key: 'root',
value: function () {
function root() {
return this[ROOT];
}
return root;
}()
/**
* Returns the wrapped component.
*
* @return {ReactComponent}
*/
}, {
key: 'getNodeInternal',
value: function () {
function getNodeInternal() {
if (this.length !== 1) {
throw new Error('ShallowWrapper::getNode() can only be called when wrapping one node');
}
if (this[ROOT] === this) {
this.update();
}
return this[NODE];
}
return getNodeInternal;
}()
/**
* Returns the the wrapped components.
*
* @return {Array<ReactComponent>}
*/
}, {
key: 'getNodesInternal',
value: function () {
function getNodesInternal() {
if (this[ROOT] === this && this.length === 1) {
this.update();
}
return this[NODES];
}
return getNodesInternal;
}()
/**
* Returns the wrapped ReactElement.
*
* @return {ReactElement}
*/
}, {
key: 'getElement',
value: function () {
function getElement() {
var _this2 = this;
return this.single('getElement', function (n) {
return (0, _getAdapter2['default'])(_this2[OPTIONS]).nodeToElement(n);
});
}
return getElement;
}()
/**
* Returns the wrapped ReactElements.
*
* @return {Array<ReactElement>}
*/
}, {
key: 'getElements',
value: function () {
function getElements() {
var _this3 = this;
return this.getNodesInternal().map(function (n) {
return (0, _getAdapter2['default'])(_this3[OPTIONS]).nodeToElement(n);
});
}
return getElements;
}()
// eslint-disable-next-line class-methods-use-this
}, {
key: 'getNode',
value: function () {
function getNode() {
throw new Error('ShallowWrapper::getNode() is no longer supported. Use ShallowWrapper::getElement() instead');
}
return getNode;
}()
// eslint-disable-next-line class-methods-use-this
}, {
key: 'getNodes',
value: function () {
function getNodes() {
throw new Error('ShallowWrapper::getNodes() is no longer supported. Use ShallowWrapper::getElements() instead');
}
return getNodes;
}()
/**
* Gets the instance of the component being rendered as the root node passed into `shallow()`.
*
* NOTE: can only be called on a wrapper instance that is also the root instance.
*
* Example:
* ```
* const wrapper = shallow(<MyComponent />);
* const inst = wrapper.instance();
* expect(inst).to.be.instanceOf(MyComponent);
* ```
* @returns {ReactComponent}
*/
}, {
key: 'instance',
value: function () {
function instance() {
if (this[ROOT] !== this) {
throw new Error('ShallowWrapper::instance() can only be called on the root');
}
return this[RENDERER].getNode().instance;
}
return instance;
}()
/**
* If a `wrappingComponent` was passed in `options`, this methods returns a `ShallowWrapper`
* around the rendered `wrappingComponent`. This `ShallowWrapper` can be used to update the
* `wrappingComponent`'s props, state, etc.
*
* @returns ShallowWrapper
*/
}, {
key: 'getWrappingComponent',
value: function () {
function getWrappingComponent() {
if (this[ROOT] !== this) {
throw new Error('ShallowWrapper::getWrappingComponent() can only be called on the root');
}
if (!this[OPTIONS].wrappingComponent) {
throw new Error('ShallowWrapper::getWrappingComponent() can only be called on a wrapper that was originally passed a `wrappingComponent` option');
}
return this[WRAPPING_COMPONENT];
}
return getWrappingComponent;
}()
/**
* Forces a re-render. Useful to run before checking the render output if something external
* may be updating the state of the component somewhere.
*
* NOTE: can only be called on a wrapper instance that is also the root instance.
*
* @returns {ShallowWrapper}
*/
}, {
key: 'update',
value: function () {
function update() {
if (this[ROOT] !== this) {
throw new Error('ShallowWrapper::update() can only be called on the root');
}
if (this.length !== 1) {
throw new Error('ShallowWrapper::update() can only be called when wrapping one node');
}
privateSetNodes(this, getRootNode(this[RENDERER].getNode()));
return this;
}
return update;
}()
/**
* A method that unmounts the component. This can be used to simulate a component going through
* and unmount/mount lifecycle.
* @returns {ShallowWrapper}
*/
}, {
key: 'unmount',
value: function () {
function unmount() {
this[RENDERER].unmount();
if (this[ROOT][WRAPPING_COMPONENT]) {
this[ROOT][WRAPPING_COMPONENT].unmount();
}
return this;
}
return unmount;
}()
/**
* A method is for re-render with new props and context.
* This calls componentDidUpdate method if disableLifecycleMethods is not enabled.
*
* NOTE: can only be called on a wrapper instance that is also the root instance.
*
* @param {Object} props
* @param {Object} context
* @returns {ShallowWrapper}
*/
}, {
key: 'rerender',
value: function () {
function rerender(props, context) {
var _this4 = this;
var adapter = (0, _getAdapter2['default'])(this[OPTIONS]);
this.single('rerender', function () {
(0, _Utils.withSetStateAllowed)(function () {
// NOTE(lmr): In react 16, instances will be null for SFCs, but
// rerendering with props/context is still a valid thing to do. In
// this case, state will be undefined, but props/context will exist.
var node = _this4[RENDERER].getNode();
var instance = node.instance || {};
var type = node.type || {};
var state = instance.state;
var prevProps = instance.props || _this4[UNRENDERED].props;
var prevContext = instance.context || _this4[OPTIONS].context;
var nextContext = context || prevContext;
if (context) {
_this4[OPTIONS] = (0, _object2['default'])({}, _this4[OPTIONS], { context: nextContext });
}
_this4[RENDERER].batchedUpdates(function () {
// When shouldComponentUpdate returns false we shouldn't call componentDidUpdate.
// so we spy shouldComponentUpdate to get the result.
var lifecycles = getAdapterLifecycles(adapter);
var shouldRender = true;
var shouldComponentUpdateSpy = void 0;
var getChildContextSpy = void 0;
if (!_this4[OPTIONS].disableLifecycleMethods && instance) {
if (typeof instance.shouldComponentUpdate === 'function') {
var gDSFP = lifecycles.getDerivedStateFromProps;
if (gDSFP && gDSFP.hasShouldComponentUpdateBug) {
mockSCUIfgDSFPReturnNonNull(node, state);
}
shouldComponentUpdateSpy = (0, _Utils.spyMethod)(instance, 'shouldComponentUpdate');
}
if (lifecycles.getChildContext.calledByRenderer && typeof instance.getChildContext === 'function') {
getChildContextSpy = (0, _Utils.spyMethod)(instance, 'getChildContext');
}
}
if (!shouldComponentUpdateSpy && isPureComponent(instance)) {
shouldRender = pureComponentShouldComponentUpdate(prevProps, props, state, instance.state);
}
if (props) _this4[UNRENDERED] = (0, _Utils.cloneElement)(adapter, _this4[UNRENDERED], props);
_this4[RENDERER].render(_this4[UNRENDERED], nextContext, {
providerValues: _this4[PROVIDER_VALUES]
});
if (shouldComponentUpdateSpy) {
shouldRender = shouldComponentUpdateSpy.getLastReturnValue();
shouldComponentUpdateSpy.restore();
}
if (shouldRender && !_this4[OPTIONS].disableLifecycleMethods && instance) {
privateSetChildContext(adapter, _this4, instance, node, getChildContextSpy);
if (lifecycles.getSnapshotBeforeUpdate) {
var snapshot = void 0;
if (typeof instance.getSnapshotBeforeUpdate === 'function') {
snapshot = instance.getSnapshotBeforeUpdate(prevProps, state);
}
if (lifecycles.componentDidUpdate && typeof instance.componentDidUpdate === 'function' && (!state || (0, _enzymeShallowEqual2['default'])(state, _this4.instance().state) || typeof type.getDerivedStateFromProps === 'function')) {
instance.componentDidUpdate(prevProps, state, snapshot);
}
} else if (lifecycles.componentDidUpdate && typeof instance.componentDidUpdate === 'function') {
if (lifecycles.componentDidUpdate.prevContext) {
instance.componentDidUpdate(prevProps, state, prevContext);
} else if (!state || (0, _enzymeShallowEqual2['default'])(_this4.instance().state, state)) {
instance.componentDidUpdate(prevProps, state);
}
}
// If it doesn't need to rerender, update only its props.
} else if (!(0, _enzymeShallowEqual2['default'])(props, instance.props)) {
instance.props = (Object.freeze || Object)((0, _object2['default'])({}, instance.props, props));
}
_this4.update();
});
});
});
return this;
}
return rerender;
}()
/**
* A method that sets the props of the root component, and re-renders. Useful for when you are
* wanting to test how the component behaves over time with changing props. Calling this, for
* instance, will call the `componentWillReceiveProps` lifecycle method.
*
* Similar to `setState`, this method accepts a props object and will merge it in with the already
* existing props.
*
* NOTE: can only be called on a wrapper instance that is also the root instance.
*
* @param {Object} props object
* @param {Function} cb - callback function
* @returns {ShallowWrapper}
*/
}, {
key: 'setProps',
value: function () {
function setProps(props) {
var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
if (this[ROOT] !== this) {
throw new Error('ShallowWrapper::setProps() can only be called on the root');
}
if (arguments.length > 1 && typeof callback !== 'function') {
throw new TypeError('ReactWrapper::setProps() expects a function as its second argument');
}
this.rerender(props);
if (callback) {
callback();
}
return this;
}
return setProps;
}()
/**
* A method to invoke `setState` on the root component instance similar to how you might in the
* definition of the component, and re-renders. This method is useful for testing your component
* in hard to achieve states, however should be used sparingly. If possible, you should utilize
* your component's external API in order to get it into whatever state you want to test, in order
* to be as accurate of a test as possible. This is not always practical, however.
*
* NOTE: can only be called on a wrapper instance that is also the root instance.
*
* @param {Object} state to merge
* @param {Function} cb - callback function
* @returns {ShallowWrapper}
*/
}, {
key: 'setState',
value: function () {
function setState(state) {
var _this5 = this;
var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
if (this[ROOT] !== this) {
throw new Error('ShallowWrapper::setState() can only be called on the root');
}
if (this.instance() === null || this[RENDERER].getNode().nodeType !== 'class') {
throw new Error('ShallowWrapper::setState() can only be called on class components');
}
if (arguments.length > 1 && typeof callback !== 'function') {
throw new TypeError('ReactWrapper::setState() expects a function as its second argument');
}
this.single('setState', function () {
(0, _Utils.withSetStateAllowed)(function () {
var adapter = (0, _getAdapter2['default'])(_this5[OPTIONS]);
var lifecycles = getAdapterLifecycles(adapter);
var node = _this5[RENDERER].getNode();
var instance = node.instance;
var prevProps = instance.props;
var prevState = instance.state;
var prevContext = instance.context;
var statePayload = typeof state === 'function' ? state.call(instance, prevState, prevProps) : state;
// returning null or undefined prevents the update in React 16+
// https://github.com/facebook/react/pull/12756
var maybeHasUpdate = !lifecycles.setState.skipsComponentDidUpdateOnNullish || statePayload != null;
// When shouldComponentUpdate returns false we shouldn't call componentDidUpdate.
// so we spy shouldComponentUpdate to get the result.
var shouldComponentUpdateSpy = void 0;
var getChildContextSpy = void 0;
var shouldRender = true;
if (!_this5[OPTIONS].disableLifecycleMethods && instance) {
if (lifecycles.componentDidUpdate && lifecycles.componentDidUpdate.onSetState && typeof instance.shouldComponentUpdate === 'function') {
var gDSFP = lifecycles.getDerivedStateFromProps;
if (gDSFP && gDSFP.hasShouldComponentUpdateBug) {
mockSCUIfgDSFPReturnNonNull(node, state);
}
shouldComponentUpdateSpy = (0, _Utils.spyMethod)(instance, 'shouldComponentUpdate');
}
if (lifecycles.getChildContext.calledByRenderer && typeof instance.getChildContext === 'function') {
getChildContextSpy = (0, _Utils.spyMethod)(instance, 'getChildContext');
}
}
if (!shouldComponentUpdateSpy && isPureComponent(instance)) {
shouldRender = pureComponentShouldComponentUpdate(prevProps, instance.props, prevState, (0, _object2['default'])({}, prevState, statePayload));
}
// We don't pass the setState callback here
// to guarantee to call the callback after finishing the render
if (instance[SET_STATE]) {
instance[SET_STATE](statePayload);
} else {
instance.setState(statePayload);
}
if (shouldComponentUpdateSpy) {
shouldRender = shouldComponentUpdateSpy.getLastReturnValue();
shouldComponentUpdateSpy.restore();
}
if (maybeHasUpdate && shouldRender && !_this5[OPTIONS].disableLifecycleMethods) {
privateSetChildContext(adapter, _this5, instance, node, getChildContextSpy);
if (lifecycles.componentDidUpdate && lifecycles.componentDidUpdate.onSetState) {
if (lifecycles.getSnapshotBeforeUpdate && typeof instance.getSnapshotBeforeUpdate === 'function') {
var snapshot = instance.getSnapshotBeforeUpdate(prevProps, prevState);
if (typeof instance.componentDidUpdate === 'function') {
instance.componentDidUpdate(prevProps, prevState, snapshot);
}
} else if (typeof instance.componentDidUpdate === 'function') {
if (lifecycles.componentDidUpdate.prevContext) {
instance.componentDidUpdate(prevProps, prevState, prevContext);
} else {
instance.componentDidUpdate(prevProps, prevState);
}
}
}
}
_this5.update();
// call the setState callback
if (callback) {
if (adapter.invokeSetStateCallback) {
adapter.invokeSetStateCallback(instance, callback);
} else {
callback.call(instance);
}
}
});
});
return this;
}
return setState;
}()
/**
* A method that sets the context of the root component, and re-renders. Useful for when you are
* wanting to test how the component behaves over time with changing contexts.
*
* NOTE: can only be called on a wrapper instance that is also the root instance.
*
* @param {Object} context object
* @returns {ShallowWrapper}
*/
}, {
key: 'setContext',
value: function () {
function setContext(context) {
if (this[ROOT] !== this) {
throw new Error('ShallowWrapper::setContext() can only be called on the root');
}
if (!this[OPTIONS].context) {
throw new Error('ShallowWrapper::setContext() can only be called on a wrapper that was originally passed a context option');
}
return this.rerender(null, context);
}
return setContext;
}()
/**
* Whether or not a given react element exists in the shallow render tree.
*
* Example:
* ```
* const wrapper = shallow(<MyComponent />);
* expect(wrapper.contains(<div className="foo bar" />)).to.equal(true);
* ```
*
* @param {ReactElement|Array<ReactElement>} nodeOrNodes
* @returns {Boolean}
*/
}, {
key: 'contains',
value: function () {
function contains(nodeOrNodes) {
var adapter = (0, _getAdapter2['default'])(this[OPTIONS]);
if (!(0, _Utils.isReactElementAlike)(nodeOrNodes, adapter)) {
throw new Error('ShallowWrapper::contains() can only be called with a ReactElement (or an array of them), a string, or a number as an argument.');
}
var predicate = Array.isArray(nodeOrNodes) ? function (other) {
return (0, _Utils.containsChildrenSubArray)(_Utils.nodeEqual, other, nodeOrNodes.map(function (node) {
return adapter.elementToNode(node);
}));
} : function (other) {
return (0, _Utils.nodeEqual)(adapter.elementToNode(nodeOrNodes), other);
};
return findWhereUnwrapped(this, predicate).length > 0;
}
return contains;
}()
/**
* Whether or not a given react element exists in the shallow render tree.
* Match is based on the expected element and not on wrappers element.
* It will determine if one of the wrappers element "looks like" the expected
* element by checking if all props of the expected element are present
* on the wrappers element and equals to each other.
*
* Example:
* ```
* // MyComponent outputs <div><div class="foo">Hello</div></div>
* const wrapper = shallow(<MyComponent />);
* expect(wrapper.containsMatchingElement(<div>Hello</div>)).to.equal(true);
* ```
*
* @param {ReactElement} node
* @returns {Boolean}
*/
}, {
key: 'containsMatchingElement',
value: function () {
function containsMatchingElement(node) {
var adapter = (0, _getAdapter2['default'])(this[OPTIONS]);
var rstNode = adapter.elementToNode(node);
var predicate = function () {
function predicate(other) {
return (0, _Utils.nodeMatches)(rstNode, other, function (a, b) {
return a <= b;
});
}
return predicate;
}();
return findWhereUnwrapped(this, predicate).length > 0;
}
return containsMatchingElement;
}()
/**
* Whether or not all the given react elements exist in the shallow render tree.
* Match is based on the expected element and not on wrappers element.
* It will determine if one of the wrappers element "looks like" the expected
* element by checking if all props of the expected element are present
* on the wrappers element and equals to each other.
*
* Example:
* ```
* const wrapper = shallow(<MyComponent />);
* expect(wrapper.containsAllMatchingElements([
* <div>Hello</div>,
* <div>Goodbye</div>,
* ])).to.equal(true);
* ```
*
* @param {Array<ReactElement>} nodes
* @returns {Boolean}
*/
}, {
key: 'containsAllMatchingElements',
value: function () {
function containsAllMatchingElements(nodes) {
var _this6 = this;
if (!Array.isArray(nodes)) {
throw new TypeError('nodes should be an Array');
}
return nodes.every(function (node) {
return _this6.containsMatchingElement(node);
});
}
return containsAllMatchingElements;
}()
/**
* Whether or not one of the given react elements exists in the shallow render tree.
* Match is based on the expected element and not on wrappers element.
* It will determine if one of the wrappers element "looks like" the expected
* element by checking if all props of the expected element are present
* on the wrappers element and equals to each other.
*
* Example:
* ```
* const wrapper = shallow(<MyComponent />);
* expect(wrapper.containsAnyMatchingElements([
* <div>Hello</div>,
* <div>Goodbye</div>,
* ])).to.equal(true);
* ```
*
* @param {Array<ReactElement>} nodes
* @returns {Boolean}
*/
}, {
key: 'containsAnyMatchingElements',
value: function () {
function containsAnyMatchingElements(nodes) {
var _this7 = this;
return Array.isArray(nodes) && nodes.some(function (node) {
return _this7.containsMatchingElement(node);
});
}
return containsAnyMatchingElements;
}()
/**
* Whether or not a given react element exists in the render tree.
*
* Example:
* ```
* const wrapper = shallow(<MyComponent />);
* expect(wrapper.contains(<div className="foo bar" />)).to.equal(true);
* ```
*
* @param {ReactElement} node
* @returns {Boolean}
*/
}, {
key: 'equals',
value: function () {
function equals(node) {
var _this8 = this;
return this.single('equals', function () {
return (0, _Utils.nodeEqual)(_this8.getNodeInternal(), node);
});
}
return equals;
}()
/**
* Whether or not a given react element matches the render tree.
* Match is based on the expected element and not on wrapper root node.
* It will determine if the wrapper root node "looks like" the expected
* element by checking if all props of the expected element are present
* on the wrapper root node and equals to each other.
*
* Example:
* ```
* // MyComponent outputs <div class="foo">Hello</div>
* const wrapper = shallow(<MyComponent />);
* expect(wrapper.matchesElement(<div>Hello</div>)).to.equal(true);
* ```
*
* @param {ReactElement} node
* @returns {Boolean}
*/
}, {
key: 'matchesElement',
value: function () {
function matchesElement(node) {
var _this9 = this;
return this.single('matchesElement', function () {
var adapter = (0, _getAdapter2['default'])(_this9[OPTIONS]);
var rstNode = adapter.elementToNode(node);
return (0, _Utils.nodeMatches)(rstNode, _this9.getNodeInternal(), function (a, b) {
return a <= b;
});
});
}
return matchesElement;
}()
/**
* Finds every node in the render tree of the current wrapper that matches the provided selector.
*
* @param {EnzymeSelector} selector
* @returns {ShallowWrapper}
*/
}, {
key: 'find',
value: function () {
function find(selector) {
return this.wrap((0, _selectors.reduceTreesBySelector)(selector, this.getNodesInternal()));
}
return find;
}()
/**
* Returns whether or not current node matches a provided selector.
*
* NOTE: can only be called on a wrapper of a single node.
*
* @param {EnzymeSelector} selector
* @returns {boolean}
*/
}, {
key: 'is',
value: function () {
function is(selector) {
var predicate = (0, _selectors.buildPredicate)(selector);
return this.single('is', function (n) {
return predicate(n);
});
}
return is;
}()
/**
* Returns true if the component rendered nothing, i.e., null or false.
*
* @returns {boolean}
*/
}, {
key: 'isEmptyRender',
value: function () {
function isEmptyRender() {
var nodes = this.getNodesInternal();
return nodes.every(function (n) {
return (0, _Utils.isEmptyValue)(n);
});
}
return isEmptyRender;
}()
/**
* Returns a new wrapper instance with only the nodes of the current wrapper instance that match
* the provided predicate function. The predicate should receive a wrapped node as its first
* argument.
*
* @param {Function} predicate
* @returns {ShallowWrapper}
*/
}, {
key: 'filterWhere',
value: function () {
function filterWhere(predicate) {
var _this10 = this;
return filterWhereUnwrapped(this, function (n) {
return predicate(_this10.wrap(n));
});
}
return filterWhere;
}()
/**
* Returns a new wrapper instance with only the nodes of the current wrapper instance that match
* the provided selector.
*
* @param {EnzymeSelector} selector
* @returns {ShallowWrapper}
*/
}, {
key: 'filter',
value: function () {
function filter(selector) {
var predicate = (0, _selectors.buildPredicate)(selector);
return filterWhereUnwrapped(this, predicate);
}
return filter;
}()
/**
* Returns a new wrapper instance with only the nodes of the current wrapper that did not match
* the provided selector. Essentially the inverse of `filter`.
*
* @param {EnzymeSelector} selector
* @returns {ShallowWrapper}
*/
}, {
key: 'not',
value: function () {
function not(selector) {
var predicate = (0, _selectors.buildPredicate)(selector);
return filterWhereUnwrapped(this, function (n) {
return !predicate(n);
});
}
return not;
}()
/**
* Returns a string of the rendered text of the current render tree. This function should be
* looked at with skepticism if being used to test what the actual HTML output of the component
* will be. If that is what you would like to test, use enzyme's `render` function instead.
*
* NOTE: can only be called on a wrapper of a single node.
*
* @returns {String}
*/
}, {
key: 'text',
value: function () {
function text() {
return this.single('text', _RSTTraversal.getTextFromNode);
}
return text;
}()
/**
* Returns the HTML of the node.
*
* NOTE: can only be called on a wrapper of a single node.
*
* @returns {String}
*/
}, {
key: 'html',
value: function () {
function html() {
var _this11 = this;
return this.single('html', function (n) {
if (_this11.type() === null) return null;
var adapter = (0, _getAdapter2['default'])(_this11[OPTIONS]);
var renderer = adapter.createRenderer((0, _object2['default'])({}, _this11[OPTIONS], { mode: 'string' }));
return renderer.render(adapter.nodeToElement(n));
});
}
return html;
}()
/**
* Returns the current node rendered to HTML and wrapped in a CheerioWrapper.
*
* NOTE: can only be called on a wrapper of a single node.
*
* @returns {CheerioWrapper}
*/
}, {
key: 'render',
value: function () {
function render() {
var html = this.html();
return (0, _Utils.loadCheerioRoot)(html);
}
return render;
}()
/**
* Used to simulate events. Pass an eventname and (optionally) event arguments. This method of
* testing events should be met with some skepticism.
*
* @param {String} event
* @param {Array} args
* @returns {ShallowWrapper}
*/
}, {
key: 'simulate',
value: function () {
function simulate(event) {
var _this12 = this;
for (var _len2 = arguments.length, args = Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1;