@cypress/unique-selector 0.0.5
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/README.md +109 -0
- package/lib/getAttributes.js +28 -0
- package/lib/getClasses.js +45 -0
- package/lib/getCombinations.js +45 -0
- package/lib/getData.js +33 -0
- package/lib/getID.js +22 -0
- package/lib/getNthChild.js +37 -0
- package/lib/getParents.js +24 -0
- package/lib/getTag.js +14 -0
- package/lib/index.js +238 -0
- package/lib/isElement.js +22 -0
- package/lib/isUnique.js +21 -0
- package/package.json +59 -0
package/README.md
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
unique-selector
|
|
2
|
+
===============
|
|
3
|
+
|
|
4
|
+
[](https://travis-ci.org/ericclemmons/unique-selector)
|
|
5
|
+
[]()
|
|
6
|
+
[]()
|
|
7
|
+
|
|
8
|
+
Given a DOM node, return a unique CSS selector matching only that element.
|
|
9
|
+
This is particularly useful when tracking in custom variables in analytics:
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
document.addEventListener('click', function(event) {
|
|
13
|
+
var selector = unique(event.target);
|
|
14
|
+
|
|
15
|
+
_gaq.push(['_trackEvent', 'Engagement', 'Click', selector]);
|
|
16
|
+
}, false);
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
Installation
|
|
20
|
+
------------
|
|
21
|
+
|
|
22
|
+
[](https://nodei.co/npm/unique-selector/)
|
|
23
|
+
|
|
24
|
+
Options
|
|
25
|
+
------------
|
|
26
|
+
e.g.1 DomElement = `<span id="test"></span>`
|
|
27
|
+
|
|
28
|
+
```
|
|
29
|
+
import unique from 'unique-selector';
|
|
30
|
+
|
|
31
|
+
// Optional Options
|
|
32
|
+
options = {
|
|
33
|
+
// Array of selector types based on which the unique selector will generate
|
|
34
|
+
selectorTypes : [ 'ID', 'Class', 'Tag', 'NthChild' ]
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
unique( DomElement, options ); // #test
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
e.g.2 DomElement = `<span test="2"></span>`
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
import unique from 'unique-selector';
|
|
44
|
+
|
|
45
|
+
// Optional Options
|
|
46
|
+
options = {
|
|
47
|
+
// Array of selector types based on which the unique selector will be generate
|
|
48
|
+
selectorTypes : [ 'Attributes' ]
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
unique( DomElement, options ); // [test="2"]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
Tests
|
|
56
|
+
-----
|
|
57
|
+
|
|
58
|
+
$ npm run test
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
Contributing
|
|
62
|
+
-----
|
|
63
|
+
Feel free to open issues, make suggestions or send PRs.
|
|
64
|
+
This project adheres to the Contributor Covenant [code of conduct](http://contributor-covenant.org/). By participating, you are expected to uphold this code.
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
Contact
|
|
68
|
+
-----
|
|
69
|
+
|
|
70
|
+
Avraam Mavridis : [@avraamakis](https://twitter.com/avraamakis)
|
|
71
|
+
|
|
72
|
+
Eric Clemmons : [@ericclemmons](https://twitter.com/ericclemmons)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
Releases
|
|
76
|
+
--------
|
|
77
|
+
- v0.1.0
|
|
78
|
+
|
|
79
|
+
- Big refactor/rewrite using es6
|
|
80
|
+
- More test + Change the test script
|
|
81
|
+
- Script to compile es6 to es5 using babel
|
|
82
|
+
- Ability to pass options for the **selectorTypes** based on which the unique selector will be generated, see the options
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
- v0.0.4
|
|
86
|
+
|
|
87
|
+
- Handle extra whitespace in `className` ([#9](https://github.com/ericclemmons/unique-selector/pull/9))
|
|
88
|
+
|
|
89
|
+
- v0.0.3
|
|
90
|
+
|
|
91
|
+
- Add support for various `<form>` elements ([#2](https://github.com/ericclemmons/unique-selector/issues/2))
|
|
92
|
+
|
|
93
|
+
- v0.0.2
|
|
94
|
+
|
|
95
|
+
- Fix bug with `nth-child` calculation
|
|
96
|
+
|
|
97
|
+
- v0.0.1
|
|
98
|
+
|
|
99
|
+
- Initial release
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
License
|
|
104
|
+
-------
|
|
105
|
+
|
|
106
|
+
MIT
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
[](https://bitdeli.com/free "Bitdeli Badge")
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getAttributes = getAttributes;
|
|
7
|
+
|
|
8
|
+
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); } }
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the Attribute selectors of the element
|
|
12
|
+
* @param { DOM Element } element
|
|
13
|
+
* @param { Array } array of attributes to ignore
|
|
14
|
+
* @return { Array }
|
|
15
|
+
*/
|
|
16
|
+
function getAttributes(el) {
|
|
17
|
+
var attributesToIgnore = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ['id', 'class', 'length'];
|
|
18
|
+
var attributes = el.attributes;
|
|
19
|
+
|
|
20
|
+
var attrs = [].concat(_toConsumableArray(attributes));
|
|
21
|
+
|
|
22
|
+
return attrs.reduce(function (sum, next) {
|
|
23
|
+
if (!(attributesToIgnore.indexOf(next.nodeName) > -1)) {
|
|
24
|
+
sum.push('[' + next.nodeName + '="' + next.value + '"]');
|
|
25
|
+
}
|
|
26
|
+
return sum;
|
|
27
|
+
}, []);
|
|
28
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getClasses = getClasses;
|
|
7
|
+
exports.getClassSelectors = getClassSelectors;
|
|
8
|
+
|
|
9
|
+
require('css.escape');
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get class names for an element
|
|
13
|
+
*
|
|
14
|
+
* @pararm { Element } el
|
|
15
|
+
* @return { Array }
|
|
16
|
+
*/
|
|
17
|
+
function getClasses(el) {
|
|
18
|
+
if (!el.hasAttribute('class')) {
|
|
19
|
+
return [];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
return Array.prototype.slice.call(el.classList);
|
|
24
|
+
} catch (e) {
|
|
25
|
+
var className = el.getAttribute('class');
|
|
26
|
+
|
|
27
|
+
// remove duplicate and leading/trailing whitespaces
|
|
28
|
+
className = className.trim().replace(/\s+/g, ' ');
|
|
29
|
+
|
|
30
|
+
// split into separate classnames
|
|
31
|
+
return className.split(' ');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns the Class selectors of the element
|
|
37
|
+
* @param { Object } element
|
|
38
|
+
* @return { Array }
|
|
39
|
+
*/
|
|
40
|
+
function getClassSelectors(el) {
|
|
41
|
+
var classList = getClasses(el).filter(Boolean);
|
|
42
|
+
return classList.map(function (cl) {
|
|
43
|
+
return '.' + CSS.escape(cl);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getCombinations = getCombinations;
|
|
7
|
+
/**
|
|
8
|
+
* Recursively combinate items.
|
|
9
|
+
* @param { Array } result
|
|
10
|
+
* @param { Array } items
|
|
11
|
+
* @param { Array } data
|
|
12
|
+
* @param { Number } start
|
|
13
|
+
* @param { Number } end
|
|
14
|
+
* @param { Number } index
|
|
15
|
+
* @param { Number } k
|
|
16
|
+
*/
|
|
17
|
+
function kCombinations(result, items, data, start, end, index, k) {
|
|
18
|
+
if (index === k) {
|
|
19
|
+
result.push(data.slice(0, index).join(''));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
for (var i = start; i <= end && end - i + 1 >= k - index; ++i) {
|
|
24
|
+
data[index] = items[i];
|
|
25
|
+
kCombinations(result, items, data, i + 1, end, index + 1, k);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Returns all the possible selector combinations
|
|
31
|
+
* @param { Array } items
|
|
32
|
+
* @param { Number } k
|
|
33
|
+
* @return { Array }
|
|
34
|
+
*/
|
|
35
|
+
function getCombinations(items, k) {
|
|
36
|
+
var result = [],
|
|
37
|
+
n = items.length,
|
|
38
|
+
data = [];
|
|
39
|
+
|
|
40
|
+
for (var l = 1; l <= k; ++l) {
|
|
41
|
+
kCombinations(result, items, data, 0, n - 1, 0, l);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return result;
|
|
45
|
+
}
|
package/lib/getData.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
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
|
+
};
|
package/lib/getID.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getID = getID;
|
|
7
|
+
|
|
8
|
+
require('css.escape');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the Tag of the element
|
|
12
|
+
* @param { Object } element
|
|
13
|
+
* @return { String }
|
|
14
|
+
*/
|
|
15
|
+
function getID(el) {
|
|
16
|
+
var id = el.getAttribute('id');
|
|
17
|
+
|
|
18
|
+
if (id !== null && id !== '') {
|
|
19
|
+
return '#' + CSS.escape(id);
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getNthChild = getNthChild;
|
|
7
|
+
|
|
8
|
+
var _isElement = require('./isElement');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the selectors based on the position of the element relative to its siblings
|
|
12
|
+
* @param { Object } element
|
|
13
|
+
* @return { Array }
|
|
14
|
+
*/
|
|
15
|
+
function getNthChild(element) {
|
|
16
|
+
var counter = 0;
|
|
17
|
+
var k = void 0;
|
|
18
|
+
var sibling = void 0;
|
|
19
|
+
var parentNode = element.parentNode;
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if (Boolean(parentNode)) {
|
|
23
|
+
var childNodes = parentNode.childNodes;
|
|
24
|
+
|
|
25
|
+
var len = childNodes.length;
|
|
26
|
+
for (k = 0; k < len; k++) {
|
|
27
|
+
sibling = childNodes[k];
|
|
28
|
+
if ((0, _isElement.isElement)(sibling)) {
|
|
29
|
+
counter++;
|
|
30
|
+
if (sibling === element) {
|
|
31
|
+
return ':nth-child(' + counter + ')';
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getParents = getParents;
|
|
7
|
+
|
|
8
|
+
var _isElement = require('./isElement');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns all the element and all of its parents
|
|
12
|
+
* @param { DOM Element }
|
|
13
|
+
* @return { Array of DOM elements }
|
|
14
|
+
*/
|
|
15
|
+
function getParents(el) {
|
|
16
|
+
var parents = [];
|
|
17
|
+
var currentElement = el;
|
|
18
|
+
while ((0, _isElement.isElement)(currentElement)) {
|
|
19
|
+
parents.push(currentElement);
|
|
20
|
+
currentElement = currentElement.parentNode;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return parents;
|
|
24
|
+
}
|
package/lib/getTag.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.getTag = getTag;
|
|
7
|
+
/**
|
|
8
|
+
* Returns the Tag of the element
|
|
9
|
+
* @param { Object } element
|
|
10
|
+
* @return { String }
|
|
11
|
+
*/
|
|
12
|
+
function getTag(el) {
|
|
13
|
+
return el.tagName.toLowerCase().replace(/:/g, '\\:');
|
|
14
|
+
}
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = unique;
|
|
7
|
+
|
|
8
|
+
var _getID = require('./getID');
|
|
9
|
+
|
|
10
|
+
var _getClasses = require('./getClasses');
|
|
11
|
+
|
|
12
|
+
var _getCombinations = require('./getCombinations');
|
|
13
|
+
|
|
14
|
+
var _getAttributes = require('./getAttributes');
|
|
15
|
+
|
|
16
|
+
var _getNthChild = require('./getNthChild');
|
|
17
|
+
|
|
18
|
+
var _getTag = require('./getTag');
|
|
19
|
+
|
|
20
|
+
var _isUnique = require('./isUnique');
|
|
21
|
+
|
|
22
|
+
var _getParents = require('./getParents');
|
|
23
|
+
|
|
24
|
+
var _getData = require('./getData');
|
|
25
|
+
|
|
26
|
+
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); } } /**
|
|
27
|
+
* Expose `unique`
|
|
28
|
+
*/
|
|
29
|
+
|
|
30
|
+
var dataRegex = /^data-(.+)/;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Returns all the selectors of the element
|
|
34
|
+
* @param { Object } element
|
|
35
|
+
* @return { Object }
|
|
36
|
+
*/
|
|
37
|
+
function getAllSelectors(el, selectors, attributesToIgnore) {
|
|
38
|
+
var funcs = {
|
|
39
|
+
'tag': _getTag.getTag,
|
|
40
|
+
'nth-child': _getNthChild.getNthChild,
|
|
41
|
+
'attributes': function attributes(elem) {
|
|
42
|
+
return (0, _getAttributes.getAttributes)(elem, attributesToIgnore);
|
|
43
|
+
},
|
|
44
|
+
'class': _getClasses.getClassSelectors,
|
|
45
|
+
'id': _getID.getID
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return selectors.filter(function (selector) {
|
|
49
|
+
return !dataRegex.test(selector);
|
|
50
|
+
}).reduce(function (res, next) {
|
|
51
|
+
res[next] = funcs[next](el);
|
|
52
|
+
return res;
|
|
53
|
+
}, {});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Tests uniqueNess of the element inside its parent
|
|
58
|
+
* @param { Object } element
|
|
59
|
+
* @param { String } Selectors
|
|
60
|
+
* @return { Boolean }
|
|
61
|
+
*/
|
|
62
|
+
function testUniqueness(element, selector) {
|
|
63
|
+
var parentNode = element.parentNode;
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
var elements = parentNode.querySelectorAll(selector);
|
|
67
|
+
return elements.length === 1 && elements[0] === element;
|
|
68
|
+
} catch (e) {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Tests all selectors for uniqueness and returns the first unique selector.
|
|
75
|
+
* @param { Object } element
|
|
76
|
+
* @param { Array } selectors
|
|
77
|
+
* @return { String }
|
|
78
|
+
*/
|
|
79
|
+
function getFirstUnique(element, selectors) {
|
|
80
|
+
return selectors.find(testUniqueness.bind(null, element));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Checks all the possible selectors of an element to find one unique and return it
|
|
85
|
+
* @param { Object } element
|
|
86
|
+
* @param { Array } items
|
|
87
|
+
* @param { String } tag
|
|
88
|
+
* @return { String }
|
|
89
|
+
*/
|
|
90
|
+
function getUniqueCombination(element, items, tag) {
|
|
91
|
+
var combinations = (0, _getCombinations.getCombinations)(items, 3),
|
|
92
|
+
firstUnique = getFirstUnique(element, combinations);
|
|
93
|
+
|
|
94
|
+
if (Boolean(firstUnique)) {
|
|
95
|
+
return firstUnique;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (Boolean(tag)) {
|
|
99
|
+
combinations = combinations.map(function (combination) {
|
|
100
|
+
return tag + combination;
|
|
101
|
+
});
|
|
102
|
+
firstUnique = getFirstUnique(element, combinations);
|
|
103
|
+
|
|
104
|
+
if (Boolean(firstUnique)) {
|
|
105
|
+
return firstUnique;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Returns a uniqueSelector based on the passed options
|
|
114
|
+
* @param { DOM } element
|
|
115
|
+
* @param { Array } options
|
|
116
|
+
* @return { String }
|
|
117
|
+
*/
|
|
118
|
+
function getUniqueSelector(element, selectorTypes, attributesToIgnore) {
|
|
119
|
+
var foundSelector = void 0;
|
|
120
|
+
|
|
121
|
+
var attributes = [].concat(_toConsumableArray(element.attributes));
|
|
122
|
+
|
|
123
|
+
var elementSelectors = getAllSelectors(element, selectorTypes, attributesToIgnore);
|
|
124
|
+
|
|
125
|
+
var _iteratorNormalCompletion = true;
|
|
126
|
+
var _didIteratorError = false;
|
|
127
|
+
var _iteratorError = undefined;
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
for (var _iterator = selectorTypes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
|
131
|
+
var selectorType = _step.value;
|
|
132
|
+
|
|
133
|
+
var selector = elementSelectors[selectorType];
|
|
134
|
+
|
|
135
|
+
// if we are a data attribute
|
|
136
|
+
if (dataRegex.test(selectorType)) {
|
|
137
|
+
var dataSelector = (0, _getData.getData)(selectorType, attributes);
|
|
138
|
+
|
|
139
|
+
// if we found a selector via data attributes
|
|
140
|
+
if (dataSelector) {
|
|
141
|
+
selector = dataSelector;
|
|
142
|
+
selectorType = 'data';
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!Boolean(selector)) continue;
|
|
147
|
+
|
|
148
|
+
switch (selectorType) {
|
|
149
|
+
case 'data':
|
|
150
|
+
case 'id':
|
|
151
|
+
case 'tag':
|
|
152
|
+
if (testUniqueness(element, selector)) {
|
|
153
|
+
return selector;
|
|
154
|
+
}
|
|
155
|
+
break;
|
|
156
|
+
case 'class':
|
|
157
|
+
case 'attributes':
|
|
158
|
+
if (selector.length) {
|
|
159
|
+
foundSelector = getUniqueCombination(element, selector, elementSelectors.tag);
|
|
160
|
+
if (foundSelector) {
|
|
161
|
+
return foundSelector;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
break;
|
|
165
|
+
|
|
166
|
+
case 'nth-child':
|
|
167
|
+
return selector;
|
|
168
|
+
|
|
169
|
+
default:
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
_didIteratorError = true;
|
|
175
|
+
_iteratorError = err;
|
|
176
|
+
} finally {
|
|
177
|
+
try {
|
|
178
|
+
if (!_iteratorNormalCompletion && _iterator.return) {
|
|
179
|
+
_iterator.return();
|
|
180
|
+
}
|
|
181
|
+
} finally {
|
|
182
|
+
if (_didIteratorError) {
|
|
183
|
+
throw _iteratorError;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return '*';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Generate unique CSS selector for given DOM element
|
|
193
|
+
*
|
|
194
|
+
* @param {Element} el
|
|
195
|
+
* @return {String}
|
|
196
|
+
* @api private
|
|
197
|
+
*/
|
|
198
|
+
|
|
199
|
+
function unique(el) {
|
|
200
|
+
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
|
201
|
+
var _options$selectorType = options.selectorTypes,
|
|
202
|
+
selectorTypes = _options$selectorType === undefined ? ['id', 'class', 'tag', 'nth-child'] : _options$selectorType,
|
|
203
|
+
_options$attributesTo = options.attributesToIgnore,
|
|
204
|
+
attributesToIgnore = _options$attributesTo === undefined ? ['id', 'class', 'length'] : _options$attributesTo,
|
|
205
|
+
selectorCache = options.selectorCache,
|
|
206
|
+
isUniqueCache = options.isUniqueCache;
|
|
207
|
+
|
|
208
|
+
var allSelectors = [];
|
|
209
|
+
|
|
210
|
+
var currentElement = el;
|
|
211
|
+
while (currentElement) {
|
|
212
|
+
var selector = selectorCache ? selectorCache.get(currentElement) : undefined;
|
|
213
|
+
|
|
214
|
+
if (!selector) {
|
|
215
|
+
selector = getUniqueSelector(currentElement, selectorTypes, attributesToIgnore);
|
|
216
|
+
if (selectorCache) {
|
|
217
|
+
selectorCache.set(currentElement, selector);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
allSelectors.unshift(selector);
|
|
222
|
+
var maybeUniqueSelector = allSelectors.join(' > ');
|
|
223
|
+
var isUniqueSelector = isUniqueCache ? isUniqueCache.get(maybeUniqueSelector) : undefined;
|
|
224
|
+
if (isUniqueSelector === undefined) {
|
|
225
|
+
isUniqueSelector = (0, _isUnique.isUnique)(el, maybeUniqueSelector);
|
|
226
|
+
if (isUniqueCache) {
|
|
227
|
+
isUniqueCache.set(maybeUniqueSelector, isUniqueSelector);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (isUniqueSelector) {
|
|
232
|
+
return maybeUniqueSelector;
|
|
233
|
+
}
|
|
234
|
+
currentElement = currentElement.parentNode;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return null;
|
|
238
|
+
}
|
package/lib/isElement.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
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; };
|
|
8
|
+
|
|
9
|
+
exports.isElement = isElement;
|
|
10
|
+
/**
|
|
11
|
+
* Determines if the passed el is a DOM element
|
|
12
|
+
*/
|
|
13
|
+
function isElement(el) {
|
|
14
|
+
var isElem = void 0;
|
|
15
|
+
|
|
16
|
+
if ((typeof HTMLElement === 'undefined' ? 'undefined' : _typeof(HTMLElement)) === 'object') {
|
|
17
|
+
isElem = el instanceof HTMLElement;
|
|
18
|
+
} else {
|
|
19
|
+
isElem = !!el && (typeof el === 'undefined' ? 'undefined' : _typeof(el)) === 'object' && el.nodeType === 1 && typeof el.nodeName === 'string';
|
|
20
|
+
}
|
|
21
|
+
return isElem;
|
|
22
|
+
}
|
package/lib/isUnique.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.isUnique = isUnique;
|
|
7
|
+
/**
|
|
8
|
+
* Checks if the selector is unique
|
|
9
|
+
* @param { Object } element
|
|
10
|
+
* @param { String } selector
|
|
11
|
+
* @return { Array }
|
|
12
|
+
*/
|
|
13
|
+
function isUnique(el, selector) {
|
|
14
|
+
if (!Boolean(selector)) return false;
|
|
15
|
+
try {
|
|
16
|
+
var elems = el.ownerDocument.querySelectorAll(selector);
|
|
17
|
+
return elems.length === 1 && elems[0] === el;
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cypress/unique-selector",
|
|
3
|
+
"version": "0.0.5",
|
|
4
|
+
"description": "Given a DOM node, return a unique CSS selector matching only that element",
|
|
5
|
+
"main": "./lib/index.js",
|
|
6
|
+
"jsnext:main": "./src/index.js",
|
|
7
|
+
"directories": {
|
|
8
|
+
"test": "test"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "npm run compile",
|
|
12
|
+
"test": "npm run compile && mocha --ignore-leaks",
|
|
13
|
+
"compile": "babel -d lib/ src/",
|
|
14
|
+
"prepublishOnly": "npm run compile",
|
|
15
|
+
"watch": "npm-scripts-watcher"
|
|
16
|
+
},
|
|
17
|
+
"watch": {
|
|
18
|
+
"src/**/*.js": [
|
|
19
|
+
"compile"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git://github.com/cypress-io/unique-selector.git"
|
|
25
|
+
},
|
|
26
|
+
"keywords": [
|
|
27
|
+
"dom",
|
|
28
|
+
"html",
|
|
29
|
+
"css",
|
|
30
|
+
"selector",
|
|
31
|
+
"event"
|
|
32
|
+
],
|
|
33
|
+
"author": "Cypress",
|
|
34
|
+
"license": "MIT",
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"babel-cli": "^6.26.0",
|
|
37
|
+
"babel-core": "^6.26.3",
|
|
38
|
+
"babel-preset-latest": "^6.24.1",
|
|
39
|
+
"babel-register": "^6.26.0",
|
|
40
|
+
"chai": "^4.3.4",
|
|
41
|
+
"component": "~1.1.0",
|
|
42
|
+
"eslint": "^8.0.1",
|
|
43
|
+
"eslint-config-airbnb": "^18.2.1",
|
|
44
|
+
"eslint-config-airbnb-lite": "^1.0.4",
|
|
45
|
+
"eslint-plugin-react": "^7.26.1",
|
|
46
|
+
"jquery": "^3.6.0",
|
|
47
|
+
"jsdom": "^18.0.0",
|
|
48
|
+
"mocha": "~9.1.3",
|
|
49
|
+
"mocha-jsdom": "^2.0.0",
|
|
50
|
+
"mocha-phantomjs": "~4.1.0",
|
|
51
|
+
"npm-scripts-watcher": "^1.0.2"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"css.escape": "^1.5.1"
|
|
55
|
+
},
|
|
56
|
+
"files": [
|
|
57
|
+
"lib"
|
|
58
|
+
]
|
|
59
|
+
}
|