@cypress/unique-selector 1.1.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/getAttribute.js +13 -20
- package/lib/getAttributes.js +4 -2
- package/lib/getClasses.js +14 -8
- package/lib/getID.js +3 -2
- package/lib/getName.js +3 -2
- package/lib/getNthChild.js +3 -2
- package/lib/getTag.js +9 -2
- package/lib/index.js +90 -28
- package/package.json +1 -1
package/lib/getAttribute.js
CHANGED
|
@@ -1,33 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
'use strict';
|
|
2
2
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
/**
|
|
7
7
|
* Returns the {attr} selector of the element
|
|
8
|
-
* @param {
|
|
9
|
-
* @param { String }
|
|
8
|
+
* @param { Element } el - The element.
|
|
9
|
+
* @param { String } attribute - The attribute name.
|
|
10
|
+
* @param { Function } filter
|
|
10
11
|
* @return { String | null } - The {attr} selector of the element.
|
|
11
12
|
*/
|
|
13
|
+
var getAttributeSelector = exports.getAttributeSelector = function getAttributeSelector(el, attribute, filter) {
|
|
14
|
+
var attributeValue = el.getAttribute(attribute);
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
var _attributes$i = attributes[i],
|
|
17
|
-
nodeName = _attributes$i.nodeName,
|
|
18
|
-
value = _attributes$i.value;
|
|
19
|
-
|
|
20
|
-
// if this matches our selector
|
|
21
|
-
|
|
22
|
-
if (nodeName === selectorType) {
|
|
23
|
-
if (value) {
|
|
24
|
-
// if we have value that needs quotes
|
|
25
|
-
return "[" + nodeName + "=\"" + value + "\"]";
|
|
26
|
-
}
|
|
16
|
+
if (attributeValue === null || filter && !filter('attribute', attribute, attributeValue)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
27
19
|
|
|
28
|
-
|
|
29
|
-
|
|
20
|
+
if (attributeValue) {
|
|
21
|
+
// if we have value that needs quotes
|
|
22
|
+
return '[' + attribute + '="' + attributeValue + '"]';
|
|
30
23
|
}
|
|
31
24
|
|
|
32
|
-
return
|
|
25
|
+
return '[' + attribute + ']';
|
|
33
26
|
};
|
package/lib/getAttributes.js
CHANGED
|
@@ -9,18 +9,20 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr
|
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Returns the Attribute selectors of the element
|
|
12
|
-
* @param {
|
|
12
|
+
* @param { Element } element
|
|
13
13
|
* @param { Array } array of attributes to ignore
|
|
14
|
+
* @param { Function } filter
|
|
14
15
|
* @return { Array }
|
|
15
16
|
*/
|
|
16
17
|
function getAttributes(el) {
|
|
17
18
|
var attributesToIgnore = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['id', 'class', 'length'];
|
|
19
|
+
var filter = arguments[2];
|
|
18
20
|
var attributes = el.attributes;
|
|
19
21
|
|
|
20
22
|
var attrs = [].concat(_toConsumableArray(attributes));
|
|
21
23
|
|
|
22
24
|
return attrs.reduce(function (sum, next) {
|
|
23
|
-
if (!(attributesToIgnore.indexOf(next.nodeName) > -1)) {
|
|
25
|
+
if (!(attributesToIgnore.indexOf(next.nodeName) > -1) && (!filter || filter('attribute', next.nodeName, next.value))) {
|
|
24
26
|
sum.push('[' + next.nodeName + '="' + next.value + '"]');
|
|
25
27
|
}
|
|
26
28
|
return sum;
|
package/lib/getClasses.js
CHANGED
|
@@ -11,34 +11,40 @@ require('css.escape');
|
|
|
11
11
|
/**
|
|
12
12
|
* Get class names for an element
|
|
13
13
|
*
|
|
14
|
-
* @
|
|
14
|
+
* @param { Element } el
|
|
15
|
+
* @param { Function } filter
|
|
15
16
|
* @return { Array }
|
|
16
17
|
*/
|
|
17
|
-
function getClasses(el) {
|
|
18
|
+
function getClasses(el, filter) {
|
|
18
19
|
if (!el.hasAttribute('class')) {
|
|
19
20
|
return [];
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
try {
|
|
23
|
-
return Array.prototype.slice.call(el.classList)
|
|
24
|
+
return Array.prototype.slice.call(el.classList).filter(function (cls) {
|
|
25
|
+
return !filter || filter('attribute', 'class', cls);
|
|
26
|
+
});
|
|
24
27
|
} catch (e) {
|
|
25
28
|
var className = el.getAttribute('class');
|
|
26
29
|
|
|
27
30
|
// remove duplicate and leading/trailing whitespaces
|
|
28
|
-
className = className.trim()
|
|
31
|
+
className = className.trim();
|
|
29
32
|
|
|
30
|
-
// split into separate classnames
|
|
31
|
-
return className.split(
|
|
33
|
+
// split into separate classnames, perform filtering
|
|
34
|
+
return className.split(/\s+/g).filter(function (cls) {
|
|
35
|
+
return !filter || filter('class', 'class', cls);
|
|
36
|
+
});
|
|
32
37
|
}
|
|
33
38
|
}
|
|
34
39
|
|
|
35
40
|
/**
|
|
36
41
|
* Returns the Class selectors of the element
|
|
37
42
|
* @param { Object } element
|
|
43
|
+
* @param { Function } filter
|
|
38
44
|
* @return { Array }
|
|
39
45
|
*/
|
|
40
|
-
function getClassSelectors(el) {
|
|
41
|
-
var classList = getClasses(el).filter(Boolean);
|
|
46
|
+
function getClassSelectors(el, filter) {
|
|
47
|
+
var classList = getClasses(el, filter).filter(Boolean);
|
|
42
48
|
return classList.map(function (cl) {
|
|
43
49
|
return '.' + CSS.escape(cl);
|
|
44
50
|
});
|
package/lib/getID.js
CHANGED
|
@@ -10,12 +10,13 @@ require('css.escape');
|
|
|
10
10
|
/**
|
|
11
11
|
* Returns the Tag of the element
|
|
12
12
|
* @param { Object } element
|
|
13
|
+
* @param { Function } filter
|
|
13
14
|
* @return { String }
|
|
14
15
|
*/
|
|
15
|
-
function getID(el) {
|
|
16
|
+
function getID(el, filter) {
|
|
16
17
|
var id = el.getAttribute('id');
|
|
17
18
|
|
|
18
|
-
if (id !== null && id !== '') {
|
|
19
|
+
if (id !== null && id !== '' && (!filter || filter('attribute', 'id', id))) {
|
|
19
20
|
return '#' + CSS.escape(id);
|
|
20
21
|
}
|
|
21
22
|
return null;
|
package/lib/getName.js
CHANGED
|
@@ -7,12 +7,13 @@ exports.getName = getName;
|
|
|
7
7
|
/**
|
|
8
8
|
* Returns the `name` attribute of the element (if one exists)
|
|
9
9
|
* @param { Object } element
|
|
10
|
+
* @param { Function } filter
|
|
10
11
|
* @return { String }
|
|
11
12
|
*/
|
|
12
|
-
function getName(el) {
|
|
13
|
+
function getName(el, filter) {
|
|
13
14
|
var name = el.getAttribute('name');
|
|
14
15
|
|
|
15
|
-
if (name !== null && name !== '') {
|
|
16
|
+
if (name !== null && name !== '' && (!filter || filter('attribute', 'name', name))) {
|
|
16
17
|
return '[name="' + name + '"]';
|
|
17
18
|
}
|
|
18
19
|
return null;
|
package/lib/getNthChild.js
CHANGED
|
@@ -10,9 +10,10 @@ var _isElement = require('./isElement');
|
|
|
10
10
|
/**
|
|
11
11
|
* Returns the selectors based on the position of the element relative to its siblings
|
|
12
12
|
* @param { Object } element
|
|
13
|
+
* @param { Function } filter
|
|
13
14
|
* @return { Array }
|
|
14
15
|
*/
|
|
15
|
-
function getNthChild(element) {
|
|
16
|
+
function getNthChild(element, filter) {
|
|
16
17
|
var counter = 0;
|
|
17
18
|
var k = void 0;
|
|
18
19
|
var sibling = void 0;
|
|
@@ -27,7 +28,7 @@ function getNthChild(element) {
|
|
|
27
28
|
sibling = childNodes[k];
|
|
28
29
|
if ((0, _isElement.isElement)(sibling)) {
|
|
29
30
|
counter++;
|
|
30
|
-
if (sibling === element) {
|
|
31
|
+
if (sibling === element && (!filter || filter('nth-child', 'nth-child', counter))) {
|
|
31
32
|
return ':nth-child(' + counter + ')';
|
|
32
33
|
}
|
|
33
34
|
}
|
package/lib/getTag.js
CHANGED
|
@@ -7,8 +7,15 @@ exports.getTag = getTag;
|
|
|
7
7
|
/**
|
|
8
8
|
* Returns the Tag of the element
|
|
9
9
|
* @param { Object } element
|
|
10
|
+
* @param { Function } filter
|
|
10
11
|
* @return { String }
|
|
11
12
|
*/
|
|
12
|
-
function getTag(el) {
|
|
13
|
-
|
|
13
|
+
function getTag(el, filter) {
|
|
14
|
+
var tagName = el.tagName.toLowerCase().replace(/:/g, '\\:');
|
|
15
|
+
|
|
16
|
+
if (filter && !filter('tag', 'tag', tagName)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return tagName;
|
|
14
21
|
}
|
package/lib/index.js
CHANGED
|
@@ -32,26 +32,76 @@ function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr
|
|
|
32
32
|
var dataRegex = /^data-.+/;
|
|
33
33
|
var attrRegex = /^attribute:(.+)/m;
|
|
34
34
|
|
|
35
|
+
/**
|
|
36
|
+
* @typedef Filter
|
|
37
|
+
* @type {Function}
|
|
38
|
+
* @param {string} type - the trait being considered ('attribute', 'tag', 'nth-child').
|
|
39
|
+
* @param {string} key - your trait key (for 'attribute' will be the attribute name, for others will typically be the same as 'type').
|
|
40
|
+
* @param {string} value - the trait value.
|
|
41
|
+
* @returns {boolean} whether this trait can be used when building the selector (true = allow). Defaults to 'true' if no value returned.
|
|
42
|
+
*/
|
|
43
|
+
|
|
35
44
|
/**
|
|
36
45
|
* Returns all the selectors of the element
|
|
37
46
|
* @param { Object } element
|
|
38
47
|
* @return { Object }
|
|
39
48
|
*/
|
|
40
|
-
function getAllSelectors(el, selectors, attributesToIgnore) {
|
|
49
|
+
function getAllSelectors(el, selectors, attributesToIgnore, filter) {
|
|
50
|
+
var consolidatedAttributesToIgnore = [].concat(_toConsumableArray(attributesToIgnore));
|
|
51
|
+
var nonAttributeSelectors = [];
|
|
52
|
+
var _iteratorNormalCompletion = true;
|
|
53
|
+
var _didIteratorError = false;
|
|
54
|
+
var _iteratorError = undefined;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
for (var _iterator = selectors[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
58
|
+
var selectorType = _step.value;
|
|
59
|
+
|
|
60
|
+
if (dataRegex.test(selectorType)) {
|
|
61
|
+
consolidatedAttributesToIgnore.push(selectorType);
|
|
62
|
+
} else if (attrRegex.test(selectorType)) {
|
|
63
|
+
consolidatedAttributesToIgnore.push(selectorType.replace(attrRegex, '$1'));
|
|
64
|
+
} else {
|
|
65
|
+
nonAttributeSelectors.push(selectorType);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
} catch (err) {
|
|
69
|
+
_didIteratorError = true;
|
|
70
|
+
_iteratorError = err;
|
|
71
|
+
} finally {
|
|
72
|
+
try {
|
|
73
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
74
|
+
_iterator.return();
|
|
75
|
+
}
|
|
76
|
+
} finally {
|
|
77
|
+
if (_didIteratorError) {
|
|
78
|
+
throw _iteratorError;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
41
83
|
var funcs = {
|
|
42
|
-
'tag':
|
|
43
|
-
|
|
84
|
+
'tag': function tag(elem) {
|
|
85
|
+
return (0, _getTag.getTag)(elem, filter);
|
|
86
|
+
},
|
|
87
|
+
'nth-child': function nthChild(elem) {
|
|
88
|
+
return (0, _getNthChild.getNthChild)(elem, filter);
|
|
89
|
+
},
|
|
44
90
|
'attributes': function attributes(elem) {
|
|
45
|
-
return (0, _getAttributes.getAttributes)(elem,
|
|
91
|
+
return (0, _getAttributes.getAttributes)(elem, consolidatedAttributesToIgnore, filter);
|
|
46
92
|
},
|
|
47
|
-
'class':
|
|
48
|
-
|
|
49
|
-
|
|
93
|
+
'class': function _class(elem) {
|
|
94
|
+
return (0, _getClasses.getClassSelectors)(elem, filter);
|
|
95
|
+
},
|
|
96
|
+
'id': function id(elem) {
|
|
97
|
+
return (0, _getID.getID)(elem, filter);
|
|
98
|
+
},
|
|
99
|
+
'name': function name(elem) {
|
|
100
|
+
return (0, _getName.getName)(elem, filter);
|
|
101
|
+
}
|
|
50
102
|
};
|
|
51
103
|
|
|
52
|
-
return
|
|
53
|
-
return !dataRegex.test(selector) && !attrRegex.test(selector);
|
|
54
|
-
}).reduce(function (res, next) {
|
|
104
|
+
return nonAttributeSelectors.reduce(function (res, next) {
|
|
55
105
|
res[next] = funcs[next](el);
|
|
56
106
|
return res;
|
|
57
107
|
}, {});
|
|
@@ -119,20 +169,18 @@ function getUniqueCombination(element, items, tag) {
|
|
|
119
169
|
* @param { Array } options
|
|
120
170
|
* @return { String }
|
|
121
171
|
*/
|
|
122
|
-
function getUniqueSelector(element, selectorTypes, attributesToIgnore) {
|
|
172
|
+
function getUniqueSelector(element, selectorTypes, attributesToIgnore, filter) {
|
|
123
173
|
var foundSelector = void 0;
|
|
124
174
|
|
|
125
|
-
var
|
|
175
|
+
var elementSelectors = getAllSelectors(element, selectorTypes, attributesToIgnore, filter);
|
|
126
176
|
|
|
127
|
-
var
|
|
128
|
-
|
|
129
|
-
var
|
|
130
|
-
var _didIteratorError = false;
|
|
131
|
-
var _iteratorError = undefined;
|
|
177
|
+
var _iteratorNormalCompletion2 = true;
|
|
178
|
+
var _didIteratorError2 = false;
|
|
179
|
+
var _iteratorError2 = undefined;
|
|
132
180
|
|
|
133
181
|
try {
|
|
134
|
-
for (var
|
|
135
|
-
var selectorType =
|
|
182
|
+
for (var _iterator2 = selectorTypes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
183
|
+
var selectorType = _step2.value;
|
|
136
184
|
|
|
137
185
|
var selector = elementSelectors[selectorType];
|
|
138
186
|
|
|
@@ -141,8 +189,7 @@ function getUniqueSelector(element, selectorTypes, attributesToIgnore) {
|
|
|
141
189
|
var isAttributeSelectorType = !isDataAttributeSelectorType && attrRegex.test(selectorType);
|
|
142
190
|
if (isDataAttributeSelectorType || isAttributeSelectorType) {
|
|
143
191
|
var attributeToQuery = isDataAttributeSelectorType ? selectorType : selectorType.replace(attrRegex, '$1');
|
|
144
|
-
var attributeSelector = (0, _getAttribute.
|
|
145
|
-
|
|
192
|
+
var attributeSelector = (0, _getAttribute.getAttributeSelector)(element, attributeToQuery, filter);
|
|
146
193
|
// if we found a selector via attribute
|
|
147
194
|
if (attributeSelector) {
|
|
148
195
|
selector = attributeSelector;
|
|
@@ -179,16 +226,16 @@ function getUniqueSelector(element, selectorTypes, attributesToIgnore) {
|
|
|
179
226
|
}
|
|
180
227
|
}
|
|
181
228
|
} catch (err) {
|
|
182
|
-
|
|
183
|
-
|
|
229
|
+
_didIteratorError2 = true;
|
|
230
|
+
_iteratorError2 = err;
|
|
184
231
|
} finally {
|
|
185
232
|
try {
|
|
186
|
-
if (!
|
|
187
|
-
|
|
233
|
+
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
234
|
+
_iterator2.return();
|
|
188
235
|
}
|
|
189
236
|
} finally {
|
|
190
|
-
if (
|
|
191
|
-
throw
|
|
237
|
+
if (_didIteratorError2) {
|
|
238
|
+
throw _iteratorError2;
|
|
192
239
|
}
|
|
193
240
|
}
|
|
194
241
|
}
|
|
@@ -200,6 +247,12 @@ function getUniqueSelector(element, selectorTypes, attributesToIgnore) {
|
|
|
200
247
|
* Generate unique CSS selector for given DOM element
|
|
201
248
|
*
|
|
202
249
|
* @param {Element} el
|
|
250
|
+
* @param {Object} options (optional) Customize various behaviors of selector generation
|
|
251
|
+
* @param {String[]} options.selectorTypes Specify the set of traits to leverage when building selectors in precedence order
|
|
252
|
+
* @param {String[]} options.attributesToIgnore Specify a set of attributes to *not* leverage when building selectors
|
|
253
|
+
* @param {Filter} options.filter Provide a filter function to conditionally reject various traits when building selectors.
|
|
254
|
+
* @param {Map<Element, String>} options.selectorCache Provide a cache to improve performance of repeated selector generation - it is the responsibility of the caller to handle cache invalidation. Caching is performed using the input Element as key. This cache handles Element -> Selector caching.
|
|
255
|
+
* @param {Map<String, Boolean>} options.isUniqueCache Provide a cache to improve performance of repeated selector generation - it is the responsibility of the caller to handle cache invalidation. Caching is performed using the input Element as key. This cache handles Selector -> isUnique caching.
|
|
203
256
|
* @return {String}
|
|
204
257
|
* @api private
|
|
205
258
|
*/
|
|
@@ -210,9 +263,18 @@ function unique(el) {
|
|
|
210
263
|
selectorTypes = _options$selectorType === undefined ? ['id', 'name', 'class', 'tag', 'nth-child'] : _options$selectorType,
|
|
211
264
|
_options$attributesTo = options.attributesToIgnore,
|
|
212
265
|
attributesToIgnore = _options$attributesTo === undefined ? ['id', 'class', 'length'] : _options$attributesTo,
|
|
266
|
+
filter = options.filter,
|
|
213
267
|
selectorCache = options.selectorCache,
|
|
214
268
|
isUniqueCache = options.isUniqueCache;
|
|
269
|
+
// If filter was provided wrap it to ensure a default value of `true` is returned if the provided function fails to return a value
|
|
215
270
|
|
|
271
|
+
var normalizedFilter = filter && function (type, key, value) {
|
|
272
|
+
var result = filter(type, key, value);
|
|
273
|
+
if (result === null || result === undefined) {
|
|
274
|
+
return true;
|
|
275
|
+
}
|
|
276
|
+
return result;
|
|
277
|
+
};
|
|
216
278
|
var allSelectors = [];
|
|
217
279
|
|
|
218
280
|
var currentElement = el;
|
|
@@ -220,7 +282,7 @@ function unique(el) {
|
|
|
220
282
|
var selector = selectorCache ? selectorCache.get(currentElement) : undefined;
|
|
221
283
|
|
|
222
284
|
if (!selector) {
|
|
223
|
-
selector = getUniqueSelector(currentElement, selectorTypes, attributesToIgnore);
|
|
285
|
+
selector = getUniqueSelector(currentElement, selectorTypes, attributesToIgnore, normalizedFilter);
|
|
224
286
|
if (selectorCache) {
|
|
225
287
|
selectorCache.set(currentElement, selector);
|
|
226
288
|
}
|
package/package.json
CHANGED