'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); 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"); } }; }(); exports.buildPredicate = buildPredicate; exports.reduceTreeBySelector = reduceTreeBySelector; exports.reduceTreesBySelector = reduceTreesBySelector; var _rstSelectorParser = require('rst-selector-parser'); var _object = require('object.values'); var _object2 = _interopRequireDefault(_object); var _arrayPrototype = require('array.prototype.flat'); var _arrayPrototype2 = _interopRequireDefault(_arrayPrototype); var _objectIs = require('object-is'); var _objectIs2 = _interopRequireDefault(_objectIs); var _has = require('has'); var _has2 = _interopRequireDefault(_has); var _byConstructor = require('html-element-map/byConstructor'); var _byConstructor2 = _interopRequireDefault(_byConstructor); var _RSTTraversal = require('./RSTTraversal'); var _Utils = require('./Utils'); var _getAdapter = require('./getAdapter'); var _getAdapter2 = _interopRequireDefault(_getAdapter); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } 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); } } // our CSS selector parser instance var parser = (0, _rstSelectorParser.createParser)(); // Combinators that allow you to chance selectors var CHILD = 'childCombinator'; var ADJACENT_SIBLING = 'adjacentSiblingCombinator'; var GENERAL_SIBLING = 'generalSiblingCombinator'; var DESCENDANT = 'descendantCombinator'; // Selectors for targeting elements var SELECTOR = 'selector'; var TYPE_SELECTOR = 'typeSelector'; var CLASS_SELECTOR = 'classSelector'; var ID_SELECTOR = 'idSelector'; var UNIVERSAL_SELECTOR = 'universalSelector'; var ATTRIBUTE_PRESENCE = 'attributePresenceSelector'; var ATTRIBUTE_VALUE = 'attributeValueSelector'; // @TODO we dont support these, throw if they are used var PSEUDO_CLASS = 'pseudoClassSelector'; var PSEUDO_ELEMENT = 'pseudoElementSelector'; var EXACT_ATTRIBUTE_OPERATOR = '='; var WHITELIST_ATTRIBUTE_OPERATOR = '~='; var HYPHENATED_ATTRIBUTE_OPERATOR = '|='; var PREFIX_ATTRIBUTE_OPERATOR = '^='; var SUFFIX_ATTRIBUTE_OPERATOR = '$='; var SUBSTRING_ATTRIBUTE_OPERATOR = '*='; function unique(arr) { return [].concat(_toConsumableArray(new Set(arr))); } /** * Calls reduce on a array of nodes with the passed * function, returning only unique results. * @param {Function} fn * @param {Array} nodes */ function uniqueReduce(fn, nodes) { return unique(nodes.reduce(fn, [])); } /** * Takes a CSS selector and returns a set of tokens parsed * by scalpel. * @param {String} selector */ function safelyGenerateTokens(selector) { try { return parser.parse(selector); } catch (err) { throw new Error('Failed to parse selector: ' + String(selector)); } } function matchAttributeSelector(node, token) { var operator = token.operator, value = token.value, name = token.name; var nodeProps = (0, _Utils.propsOfNode)(node); var descriptor = Object.getOwnPropertyDescriptor(nodeProps, name); if (descriptor && descriptor.get) { return false; } var nodePropValue = nodeProps[name]; if (typeof nodePropValue === 'undefined') { return false; } if (token.type === ATTRIBUTE_PRESENCE) { return (0, _has2['default'])(nodeProps, token.name); } // Only the exact value operator ("=") can match non-strings if (typeof nodePropValue !== 'string' || typeof value !== 'string') { if (operator !== EXACT_ATTRIBUTE_OPERATOR) { return false; } } switch (operator) { /** * Represents an element with the att attribute whose value is exactly "val". * @example * [attr="val"] matches attr="val" */ case EXACT_ATTRIBUTE_OPERATOR: return (0, _objectIs2['default'])(nodePropValue, value); /** * Represents an element with the att attribute whose value is a whitespace-separated * list of words, one of which is exactly * @example * [rel~="copyright"] matches rel="copyright other" */ case WHITELIST_ATTRIBUTE_OPERATOR: return nodePropValue.split(' ').indexOf(value) !== -1; /** * Represents an element with the att attribute, its value either being exactly the * value or beginning with the value immediately followed by "-" * @example * [hreflang|="en"] matches hreflang="en-US" */ case HYPHENATED_ATTRIBUTE_OPERATOR: return nodePropValue === value || nodePropValue.startsWith(String(value) + '-'); /** * Represents an element with the att attribute whose value begins with the prefix value. * If the value is the empty string then the selector does not represent anything. * @example * [type^="image"] matches type="imageobject" */ case PREFIX_ATTRIBUTE_OPERATOR: return value === '' ? false : nodePropValue.slice(0, value.length) === value; /** * Represents an element with the att attribute whose value ends with the suffix value. * If the value is the empty string then the selector does not represent anything. * @example * [type$="image"] matches type="imageobject" */ case SUFFIX_ATTRIBUTE_OPERATOR: return value === '' ? false : nodePropValue.slice(-value.length) === value; /** * Represents an element with the att attribute whose value contains at least one * instance of the value. If value is the empty string then the * selector does not represent anything. * @example * [title*="hello"] matches title="well hello there" */ case SUBSTRING_ATTRIBUTE_OPERATOR: return value === '' ? false : nodePropValue.indexOf(value) !== -1; default: throw new Error('Enzyme::Selector: Unknown attribute selector operator "' + String(operator) + '"'); } } function matchPseudoSelector(node, token, root) { var name = token.name, parameters = token.parameters; if (name === 'not') { // eslint-disable-next-line no-use-before-define return parameters.every(function (selector) { return reduceTreeBySelector(selector, node).length === 0; }); } if (name === 'empty') { return (0, _RSTTraversal.treeFilter)(node, function (n) { return n !== node; }).length === 0; } if (name === 'first-child') { var _findParentNode = (0, _RSTTraversal.findParentNode)(root, node), rendered = _findParentNode.rendered; var _rendered = _slicedToArray(rendered, 1), firstChild = _rendered[0]; return firstChild === node; } if (name === 'last-child') { var _findParentNode2 = (0, _RSTTraversal.findParentNode)(root, node), _rendered2 = _findParentNode2.rendered; return _rendered2[_rendered2.length - 1] === node; } if (name === 'focus') { if (typeof document === 'undefined') { throw new Error('Enzyme::Selector does not support the ":focus" pseudo-element without a global `document`.'); } var adapter = (0, _getAdapter2['default'])(); /* eslint-env browser */ return document.activeElement && adapter.nodeToHostNode(node) === document.activeElement; } throw new TypeError('Enzyme::Selector does not support the "' + String(token.name) + '" pseudo-element or pseudo-class selectors.'); } /** * Takes a node and a token and determines if the node * matches the predicate defined by the token. * @param {Node} node * @param {Token} token */ function nodeMatchesToken(node, token, root) { if (node === null || typeof node === 'string') { return false; } switch (token.type) { /** * Match every node * @example '*' matches every node */ case UNIVERSAL_SELECTOR: return true; /** * Match against the className prop * @example '.active' matches
*/ case CLASS_SELECTOR: return (0, _RSTTraversal.hasClassName)(node, token.name); /** * Simple type matching * @example 'div' matches
*/ case TYPE_SELECTOR: return (0, _Utils.nodeHasType)(node, token.name); /** * Match against the `id` prop * @example '#nav' matches