@cypress/unique-selector 1.0.0 → 1.2.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 +25 -0
- 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 +91 -36
- package/package.json +1 -1
- package/lib/getData.js +0 -33
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
/**
|
|
7
|
+
* Returns the {attr} selector of the element
|
|
8
|
+
* @param { Element } el - The element.
|
|
9
|
+
* @param { String } attribute - The attribute name.
|
|
10
|
+
* @return { String | null } - The {attr} selector of the element.
|
|
11
|
+
*/
|
|
12
|
+
var getAttributeSelector = exports.getAttributeSelector = function getAttributeSelector(el, attribute) {
|
|
13
|
+
var attributeValue = el.getAttribute(attribute);
|
|
14
|
+
|
|
15
|
+
if (attributeValue === null) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
if (attributeValue) {
|
|
20
|
+
// if we have value that needs quotes
|
|
21
|
+
return "[" + attribute + "=\"" + attributeValue + "\"]";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return "[" + attribute + "]";
|
|
25
|
+
};
|
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('attributes', 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('class', '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('id', '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('name', '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
|
@@ -23,34 +23,76 @@ var _isUnique = require('./isUnique');
|
|
|
23
23
|
|
|
24
24
|
var _getParents = require('./getParents');
|
|
25
25
|
|
|
26
|
-
var
|
|
26
|
+
var _getAttribute = require('./getAttribute');
|
|
27
27
|
|
|
28
28
|
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); } } /**
|
|
29
29
|
* Expose `unique`
|
|
30
30
|
*/
|
|
31
31
|
|
|
32
|
-
var dataRegex = /^data
|
|
32
|
+
var dataRegex = /^data-.+/;
|
|
33
|
+
var attrRegex = /^attribute:(.+)/m;
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Returns all the selectors of the element
|
|
36
37
|
* @param { Object } element
|
|
37
38
|
* @return { Object }
|
|
38
39
|
*/
|
|
39
|
-
function getAllSelectors(el, selectors, attributesToIgnore) {
|
|
40
|
+
function getAllSelectors(el, selectors, attributesToIgnore, filters) {
|
|
41
|
+
var consolidatedAttributesToIgnore = [].concat(_toConsumableArray(attributesToIgnore));
|
|
42
|
+
var nonAttributeSelectors = [];
|
|
43
|
+
var _iteratorNormalCompletion = true;
|
|
44
|
+
var _didIteratorError = false;
|
|
45
|
+
var _iteratorError = undefined;
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
for (var _iterator = selectors[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
49
|
+
var selectorType = _step.value;
|
|
50
|
+
|
|
51
|
+
if (dataRegex.test(selectorType)) {
|
|
52
|
+
consolidatedAttributesToIgnore.push(selectorType);
|
|
53
|
+
} else if (attrRegex.test(selectorType)) {
|
|
54
|
+
consolidatedAttributesToIgnore.push(selectorType.replace(attrRegex, '$1'));
|
|
55
|
+
} else {
|
|
56
|
+
nonAttributeSelectors.push(selectorType);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch (err) {
|
|
60
|
+
_didIteratorError = true;
|
|
61
|
+
_iteratorError = err;
|
|
62
|
+
} finally {
|
|
63
|
+
try {
|
|
64
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
65
|
+
_iterator.return();
|
|
66
|
+
}
|
|
67
|
+
} finally {
|
|
68
|
+
if (_didIteratorError) {
|
|
69
|
+
throw _iteratorError;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
40
74
|
var funcs = {
|
|
41
|
-
'tag':
|
|
42
|
-
|
|
75
|
+
'tag': function tag(elem) {
|
|
76
|
+
return (0, _getTag.getTag)(elem, filters.tag);
|
|
77
|
+
},
|
|
78
|
+
'nth-child': function nthChild(elem) {
|
|
79
|
+
return (0, _getNthChild.getNthChild)(elem, filters.nthChild);
|
|
80
|
+
},
|
|
43
81
|
'attributes': function attributes(elem) {
|
|
44
|
-
return (0, _getAttributes.getAttributes)(elem,
|
|
82
|
+
return (0, _getAttributes.getAttributes)(elem, consolidatedAttributesToIgnore, filters.attributes);
|
|
83
|
+
},
|
|
84
|
+
'class': function _class(elem) {
|
|
85
|
+
return (0, _getClasses.getClassSelectors)(elem, filters.class);
|
|
45
86
|
},
|
|
46
|
-
'
|
|
47
|
-
|
|
48
|
-
|
|
87
|
+
'id': function id(elem) {
|
|
88
|
+
return (0, _getID.getID)(elem, filters.id);
|
|
89
|
+
},
|
|
90
|
+
'name': function name(elem) {
|
|
91
|
+
return (0, _getName.getName)(elem, filters.name);
|
|
92
|
+
}
|
|
49
93
|
};
|
|
50
94
|
|
|
51
|
-
return
|
|
52
|
-
return !dataRegex.test(selector);
|
|
53
|
-
}).reduce(function (res, next) {
|
|
95
|
+
return nonAttributeSelectors.reduce(function (res, next) {
|
|
54
96
|
res[next] = funcs[next](el);
|
|
55
97
|
return res;
|
|
56
98
|
}, {});
|
|
@@ -118,38 +160,40 @@ function getUniqueCombination(element, items, tag) {
|
|
|
118
160
|
* @param { Array } options
|
|
119
161
|
* @return { String }
|
|
120
162
|
*/
|
|
121
|
-
function getUniqueSelector(element, selectorTypes, attributesToIgnore) {
|
|
163
|
+
function getUniqueSelector(element, selectorTypes, attributesToIgnore, filters) {
|
|
122
164
|
var foundSelector = void 0;
|
|
123
165
|
|
|
124
|
-
var
|
|
166
|
+
var elementSelectors = getAllSelectors(element, selectorTypes, attributesToIgnore, filters);
|
|
125
167
|
|
|
126
|
-
var
|
|
127
|
-
|
|
128
|
-
var
|
|
129
|
-
var _didIteratorError = false;
|
|
130
|
-
var _iteratorError = undefined;
|
|
168
|
+
var _iteratorNormalCompletion2 = true;
|
|
169
|
+
var _didIteratorError2 = false;
|
|
170
|
+
var _iteratorError2 = undefined;
|
|
131
171
|
|
|
132
172
|
try {
|
|
133
|
-
for (var
|
|
134
|
-
var selectorType =
|
|
173
|
+
for (var _iterator2 = selectorTypes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
|
174
|
+
var selectorType = _step2.value;
|
|
135
175
|
|
|
136
176
|
var selector = elementSelectors[selectorType];
|
|
137
177
|
|
|
138
178
|
// if we are a data attribute
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
179
|
+
var isDataAttributeSelectorType = dataRegex.test(selectorType);
|
|
180
|
+
var isAttributeSelectorType = !isDataAttributeSelectorType && attrRegex.test(selectorType);
|
|
181
|
+
if (isDataAttributeSelectorType || isAttributeSelectorType) {
|
|
182
|
+
var attributeToQuery = isDataAttributeSelectorType ? selectorType : selectorType.replace(attrRegex, '$1');
|
|
183
|
+
var attributeValue = element.getAttribute(attributeToQuery);
|
|
184
|
+
var attributeFilter = filters[selectorType];
|
|
185
|
+
|
|
186
|
+
// if we found a selector via attribute
|
|
187
|
+
if (attributeValue !== null && (!attributeFilter || attributeFilter(selectorType, attributeToQuery, attributeValue))) {
|
|
188
|
+
selector = (0, _getAttribute.getAttributeSelector)(element, attributeToQuery);
|
|
189
|
+
selectorType = 'attribute';
|
|
146
190
|
}
|
|
147
191
|
}
|
|
148
192
|
|
|
149
193
|
if (!Boolean(selector)) continue;
|
|
150
194
|
|
|
151
195
|
switch (selectorType) {
|
|
152
|
-
case '
|
|
196
|
+
case 'attribute':
|
|
153
197
|
case 'id':
|
|
154
198
|
case 'name':
|
|
155
199
|
case 'tag':
|
|
@@ -175,16 +219,16 @@ function getUniqueSelector(element, selectorTypes, attributesToIgnore) {
|
|
|
175
219
|
}
|
|
176
220
|
}
|
|
177
221
|
} catch (err) {
|
|
178
|
-
|
|
179
|
-
|
|
222
|
+
_didIteratorError2 = true;
|
|
223
|
+
_iteratorError2 = err;
|
|
180
224
|
} finally {
|
|
181
225
|
try {
|
|
182
|
-
if (!
|
|
183
|
-
|
|
226
|
+
if (!_iteratorNormalCompletion2 && _iterator2.return) {
|
|
227
|
+
_iterator2.return();
|
|
184
228
|
}
|
|
185
229
|
} finally {
|
|
186
|
-
if (
|
|
187
|
-
throw
|
|
230
|
+
if (_didIteratorError2) {
|
|
231
|
+
throw _iteratorError2;
|
|
188
232
|
}
|
|
189
233
|
}
|
|
190
234
|
}
|
|
@@ -196,6 +240,15 @@ function getUniqueSelector(element, selectorTypes, attributesToIgnore) {
|
|
|
196
240
|
* Generate unique CSS selector for given DOM element
|
|
197
241
|
*
|
|
198
242
|
* @param {Element} el
|
|
243
|
+
* @param {Object} options (optional) Customize various behaviors of selector generation
|
|
244
|
+
* @param {String[]} options.selectorTypes Specify the set of traits to leverage when building selectors in precedence order
|
|
245
|
+
* @param {String[]} options.attributesToIgnore Specify a set of attributes to *not* leverage when building selectors
|
|
246
|
+
* @param {Object} options.filters Specify a set of filter functions to conditionally reject various traits when building selectors. Keys correspond to a `selectorTypes` entry, values should be a function accepting three parameters:
|
|
247
|
+
* * selectorType: The selector type/category being generated
|
|
248
|
+
* * key: The key being evaluated - this will typically match the `selectorType` except in aggregate types like `attributes`
|
|
249
|
+
* * value: The value to consider. Returning `true` will allow its use in selector generation, `false` will prevent.
|
|
250
|
+
* @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.
|
|
251
|
+
* @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.
|
|
199
252
|
* @return {String}
|
|
200
253
|
* @api private
|
|
201
254
|
*/
|
|
@@ -206,6 +259,8 @@ function unique(el) {
|
|
|
206
259
|
selectorTypes = _options$selectorType === undefined ? ['id', 'name', 'class', 'tag', 'nth-child'] : _options$selectorType,
|
|
207
260
|
_options$attributesTo = options.attributesToIgnore,
|
|
208
261
|
attributesToIgnore = _options$attributesTo === undefined ? ['id', 'class', 'length'] : _options$attributesTo,
|
|
262
|
+
_options$filters = options.filters,
|
|
263
|
+
filters = _options$filters === undefined ? {} : _options$filters,
|
|
209
264
|
selectorCache = options.selectorCache,
|
|
210
265
|
isUniqueCache = options.isUniqueCache;
|
|
211
266
|
|
|
@@ -216,7 +271,7 @@ function unique(el) {
|
|
|
216
271
|
var selector = selectorCache ? selectorCache.get(currentElement) : undefined;
|
|
217
272
|
|
|
218
273
|
if (!selector) {
|
|
219
|
-
selector = getUniqueSelector(currentElement, selectorTypes, attributesToIgnore);
|
|
274
|
+
selector = getUniqueSelector(currentElement, selectorTypes, attributesToIgnore, filters);
|
|
220
275
|
if (selectorCache) {
|
|
221
276
|
selectorCache.set(currentElement, selector);
|
|
222
277
|
}
|
package/package.json
CHANGED
package/lib/getData.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
/**
|
|
7
|
-
* Returns the data-{dataAttr} selector of the element
|
|
8
|
-
* @param { String } selectorType - The type of selector to return.
|
|
9
|
-
* @param { String } attributes - The attributes of the element.
|
|
10
|
-
* @return { String | null } - The data-{dataAttr} selector of the element.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
var getData = exports.getData = function getData(selectorType, attributes) {
|
|
14
|
-
for (var i = 0; i < attributes.length; i++) {
|
|
15
|
-
// extract node name + value
|
|
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
|
-
}
|
|
27
|
-
|
|
28
|
-
return "[" + nodeName + "]";
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
return null;
|
|
33
|
-
};
|