UNPKG

enzyme

Version:

JavaScript Testing utilities for React

502 lines (406 loc) 16.1 kB
Object.defineProperty(exports, "__esModule", { value: true }); exports.SELECTOR = exports.isCompoundSelector = exports.ITERATOR_SYMBOL = undefined; 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 _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); /* eslint no-use-before-define:0 */ exports.internalInstance = internalInstance; exports.isFunctionalComponent = isFunctionalComponent; exports.isCustomComponentElement = isCustomComponentElement; exports.propsOfNode = propsOfNode; exports.typeOfNode = typeOfNode; exports.getNode = getNode; exports.nodeHasType = nodeHasType; exports.childrenMatch = childrenMatch; exports.childrenEqual = childrenEqual; exports.nodeMatches = nodeMatches; exports.nodeEqual = nodeEqual; exports.containsChildrenSubArray = containsChildrenSubArray; exports.childrenToSimplifiedArray = childrenToSimplifiedArray; exports.isReactElementAlike = isReactElementAlike; exports.propFromEvent = propFromEvent; exports.withSetStateAllowed = withSetStateAllowed; exports.splitSelector = splitSelector; exports.isPseudoClassSelector = isPseudoClassSelector; exports.selectorError = selectorError; exports.selectorType = selectorType; exports.AND = AND; exports.coercePropValue = coercePropValue; exports.nodeHasProperty = nodeHasProperty; exports.mapNativeEventNames = mapNativeEventNames; exports.displayNameOfNode = displayNameOfNode; var _object = require('object.assign'); var _object2 = _interopRequireDefault(_object); var _isEqual = require('lodash/isEqual'); var _isEqual2 = _interopRequireDefault(_isEqual); var _react = require('react'); var _react2 = _interopRequireDefault(_react); var _objectIs = require('object-is'); var _objectIs2 = _interopRequireDefault(_objectIs); var _v = require('uuid/v4'); var _v2 = _interopRequireDefault(_v); var _object3 = require('object.entries'); var _object4 = _interopRequireDefault(_object3); var _functionPrototype = require('function.prototype.name'); var _functionPrototype2 = _interopRequireDefault(_functionPrototype); var _reactCompat = require('./react-compat'); var _version = require('./version'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 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 ITERATOR_SYMBOL = exports.ITERATOR_SYMBOL = typeof Symbol === 'function' && Symbol.iterator; function internalInstanceKey(node) { return Object.keys(Object(node)).filter(function (key) { return key.match(/^__reactInternalInstance\$/); })[0]; } function internalInstance(inst) { return inst._reactInternalInstance || inst[internalInstanceKey(inst)]; } function isFunctionalComponent(inst) { return !!inst && !!inst.constructor && typeof inst.constructor === 'function' && (0, _functionPrototype2['default'])(inst.constructor) === 'StatelessComponent'; } function isCustomComponentElement(inst) { return !!inst && _react2['default'].isValidElement(inst) && typeof inst.type === 'function'; } function propsOfNode(node) { if (_version.REACT013 && node && node._store) { return node._store.props || {}; } if (node && node._reactInternalComponent && node._reactInternalComponent._currentElement) { return node._reactInternalComponent._currentElement.props || {}; } if (node && node._currentElement) { return node._currentElement.props || {}; } if (_version.REACT15 && node) { if (internalInstance(node) && internalInstance(node)._currentElement) { return internalInstance(node)._currentElement.props || {}; } } return node && node.props || {}; } function typeOfNode(node) { return node ? node.type : null; } function getNode(node) { return (0, _reactCompat.isDOMComponent)(node) ? (0, _reactCompat.findDOMNode)(node) : node; } function nodeHasType(node, type) { if (!type || !node) return false; if (!node.type) return false; if (typeof node.type === 'string') return node.type === type; return (typeof node.type === 'function' ? (0, _functionPrototype2['default'])(node.type) === type : node.type.name === type) || node.type.displayName === type; } function internalChildrenCompare(a, b, lenComp, isLoose) { var nodeCompare = isLoose ? nodeMatches : nodeEqual; if (a === b) return true; if (!Array.isArray(a) && !Array.isArray(b)) { return nodeCompare(a, b, lenComp); } if (!a && !b) return true; if (a.length !== b.length) return false; if (a.length === 0 && b.length === 0) return true; for (var i = 0; i < a.length; i += 1) { if (!nodeCompare(a[i], b[i], lenComp)) return false; } return true; } function childrenMatch(a, b, lenComp) { return internalChildrenCompare(a, b, lenComp, true); } function childrenEqual(a, b, lenComp) { return internalChildrenCompare(a, b, lenComp, false); } function removeNullaryReducer(acc, _ref) { var _ref2 = _slicedToArray(_ref, 2), key = _ref2[0], value = _ref2[1]; var addition = value == null ? {} : _defineProperty({}, key, value); return (0, _object2['default'])({}, acc, addition); } function internalNodeCompare(a, b, lenComp, isLoose) { if (a === b) return true; if (!a || !b) return false; if (a.type !== b.type) return false; var left = propsOfNode(a); var right = propsOfNode(b); if (isLoose) { left = (0, _object4['default'])(left).reduce(removeNullaryReducer, {}); right = (0, _object4['default'])(right).reduce(removeNullaryReducer, {}); } var leftKeys = Object.keys(left); for (var i = 0; i < leftKeys.length; i += 1) { var prop = leftKeys[i]; // we will check children later if (prop === 'children') { // continue; } else if (!(prop in right)) { return false; } else if (right[prop] === left[prop]) { // continue; } else if (_typeof(right[prop]) === _typeof(left[prop]) && _typeof(left[prop]) === 'object') { if (!(0, _isEqual2['default'])(left[prop], right[prop])) return false; } else { return false; } } var leftHasChildren = 'children' in left; var rightHasChildren = 'children' in right; var childCompare = isLoose ? childrenMatch : childrenEqual; if (leftHasChildren || rightHasChildren) { if (!childCompare(childrenToSimplifiedArray(left.children), childrenToSimplifiedArray(right.children), lenComp)) { return false; } } if (!isTextualNode(a)) { var rightKeys = Object.keys(right); return lenComp(leftKeys.length - leftHasChildren, rightKeys.length - rightHasChildren); } return false; } function nodeMatches(a, b) { var lenComp = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _objectIs2['default']; return internalNodeCompare(a, b, lenComp, true); } function nodeEqual(a, b) { var lenComp = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _objectIs2['default']; return internalNodeCompare(a, b, lenComp, false); } function containsChildrenSubArray(match, node, subArray) { var children = childrenOfNode(node); var checker = function checker(_, i) { return arraysEqual(match, children.slice(i, i + subArray.length), subArray); }; return children.some(checker); } function arraysEqual(match, left, right) { return left.length === right.length && left.every(function (el, i) { return match(el, right[i]); }); } function childrenToSimplifiedArray(nodeChildren) { var childrenArray = (0, _reactCompat.childrenToArray)(nodeChildren); var simplifiedArray = []; for (var i = 0; i < childrenArray.length; i += 1) { var child = childrenArray[i]; var previousChild = simplifiedArray.pop(); if (previousChild === undefined) { simplifiedArray.push(child); } else if (isTextualNode(child) && isTextualNode(previousChild)) { simplifiedArray.push(previousChild + child); } else { simplifiedArray.push(previousChild); simplifiedArray.push(child); } } return simplifiedArray; } function childrenOfNode(node) { var props = propsOfNode(node); var children = props.children; return (0, _reactCompat.childrenToArray)(children); } function isTextualNode(node) { return typeof node === 'string' || typeof node === 'number'; } function isReactElementAlike(arg) { return _react2['default'].isValidElement(arg) || isTextualNode(arg) || Array.isArray(arg); } // 'click' => 'onClick' // 'mouseEnter' => 'onMouseEnter' function propFromEvent(event) { var nativeEvent = mapNativeEventNames(event); return 'on' + String(nativeEvent[0].toUpperCase()) + String(nativeEvent.slice(1)); } function withSetStateAllowed(fn) { // NOTE(lmr): // this is currently here to circumvent a React bug where `setState()` is // not allowed without global being defined. var cleanup = false; if (typeof global.document === 'undefined') { cleanup = true; global.document = {}; } fn(); if (cleanup) { // This works around a bug in node/jest in that developers aren't able to // delete things from global when running in a node vm. global.document = undefined; delete global.document; } } function splitSelector(selector) { // step 1: make a map of all quoted strings with a uuid var quotedSegments = selector.split(/[^" ]+|("[^"]*")|.*/g).filter(Boolean).reduce(function (obj, match) { return (0, _object2['default'])({}, obj, _defineProperty({}, match, (0, _v2['default'])())); }, {}); var splits = selector // step 2: replace all quoted strings with the uuid, so we don't have to properly parse them .replace(/[^" ]+|("[^"]*")|.*/g, function (x) { return quotedSegments[x] || x; }) // step 3: split as best we can without a proper parser .split(/(?=\.|\[.*])|(?=#|\[#.*])/) // step 4: restore the quoted strings by swapping back the uuid's for the original segments .map(function (selectorSegment) { var restoredSegment = selectorSegment; (0, _object4['default'])(quotedSegments).forEach(function (_ref4) { var _ref5 = _slicedToArray(_ref4, 2), k = _ref5[0], v = _ref5[1]; restoredSegment = restoredSegment.replace(v, k); }); return restoredSegment; }); if (splits.length === 1 && splits[0] === selector) { // splitSelector expects selector to be "splittable" throw new TypeError('Enzyme::Selector received what appears to be a malformed string selector'); } return splits; } var containsQuotes = /'|"/; var containsColon = /:/; function isPseudoClassSelector(selector) { if (containsColon.test(selector)) { if (!containsQuotes.test(selector)) { return true; } var tokens = selector.split(containsQuotes); return tokens.some(function (token, i) { return containsColon.test(token) && i % 2 === 0; }); } return false; } function selectorError(selector) { var type = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; return new TypeError('Enzyme received a ' + String(type) + ' CSS selector (\'' + String(selector) + '\') that it does not currently support'); } var isCompoundSelector = exports.isCompoundSelector = /^[.#]?-?[_a-z]+[_a-z0-9-]*[.[#]/i; var isPropSelector = /^\[.*]$/; var SELECTOR = exports.SELECTOR = { CLASS_TYPE: 0, ID_TYPE: 1, PROP_TYPE: 2 }; function selectorType(selector) { if (isPseudoClassSelector(selector)) { throw selectorError(selector, 'pseudo-class'); } if (selector[0] === '.') { return SELECTOR.CLASS_TYPE; } else if (selector[0] === '#') { return SELECTOR.ID_TYPE; } else if (isPropSelector.test(selector)) { return SELECTOR.PROP_TYPE; } return undefined; } function AND(fns) { var fnsReversed = fns.slice().reverse(); return function (x) { return fnsReversed.every(function (fn) { return fn(x); }); }; } function coercePropValue(propName, propValue) { // can be undefined if (propValue === undefined) { return propValue; } // can be the empty string if (propValue === '') { return propValue; } if (propValue === 'NaN') { return NaN; } if (propValue === 'null') { return null; } var trimmedValue = propValue.trim(); // if propValue includes quotes, it should be // treated as a string // eslint override pending https://github.com/eslint/eslint/issues/7472 // eslint-disable-next-line no-useless-escape if (/^(['"]).*\1$/.test(trimmedValue)) { return trimmedValue.slice(1, -1); } var numericPropValue = +trimmedValue; // if parseInt is not NaN, then we've wanted a number if (!(0, _objectIs2['default'])(NaN, numericPropValue)) { return numericPropValue; } // coerce to boolean if (trimmedValue === 'true') return true; if (trimmedValue === 'false') return false; // user provided an unquoted string value throw new TypeError('Enzyme::Unable to parse selector \'[' + String(propName) + '=' + String(propValue) + ']\'. ' + ('Perhaps you forgot to escape a string? Try \'[' + String(propName) + '="' + String(trimmedValue) + '"]\' instead.')); } function nodeHasProperty(node, propKey, stringifiedPropValue) { var nodeProps = propsOfNode(node); var descriptor = Object.getOwnPropertyDescriptor(nodeProps, propKey); if (descriptor && descriptor.get) { return false; } var nodePropValue = nodeProps[propKey]; var propValue = coercePropValue(propKey, stringifiedPropValue); if (nodePropValue === undefined) { return false; } if (propValue !== undefined) { return (0, _objectIs2['default'])(nodePropValue, propValue); } return Object.prototype.hasOwnProperty.call(nodeProps, propKey); } function mapNativeEventNames(event) { var nativeToReactEventMap = { compositionend: 'compositionEnd', compositionstart: 'compositionStart', compositionupdate: 'compositionUpdate', keydown: 'keyDown', keyup: 'keyUp', keypress: 'keyPress', contextmenu: 'contextMenu', dblclick: 'doubleClick', doubleclick: 'doubleClick', // kept for legacy. TODO: remove with next major. dragend: 'dragEnd', dragenter: 'dragEnter', dragexist: 'dragExit', dragleave: 'dragLeave', dragover: 'dragOver', dragstart: 'dragStart', mousedown: 'mouseDown', mousemove: 'mouseMove', mouseout: 'mouseOut', mouseover: 'mouseOver', mouseup: 'mouseUp', touchcancel: 'touchCancel', touchend: 'touchEnd', touchmove: 'touchMove', touchstart: 'touchStart', canplay: 'canPlay', canplaythrough: 'canPlayThrough', durationchange: 'durationChange', loadeddata: 'loadedData', loadedmetadata: 'loadedMetadata', loadstart: 'loadStart', ratechange: 'rateChange', timeupdate: 'timeUpdate', volumechange: 'volumeChange', beforeinput: 'beforeInput' }; if (!_version.REACT013) { // these could not be simulated in React 0.13: // https://github.com/facebook/react/issues/1297 nativeToReactEventMap.mouseenter = 'mouseEnter'; nativeToReactEventMap.mouseleave = 'mouseLeave'; } return nativeToReactEventMap[event] || event; } function displayNameOfNode(node) { var type = node.type; if (!type) return null; return type.displayName || (typeof type === 'function' ? (0, _functionPrototype2['default'])(type) : type.name || type); }