UNPKG

enzyme

Version:

JavaScript Testing utilities for React

1,367 lines (1,166 loc) 215 kB
'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;