power-linter 0.1.1 → 0.1.2
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/package.json
CHANGED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const STABLE_ID_ATTRIBUTE_NAME = "data-test";
|
|
2
|
+
const COMPONENT_TEST_NAME = "data-testid";
|
|
3
|
+
const KEYS_COUNTS_MAP_MAX_SIZE = 1000;
|
|
4
|
+
|
|
5
|
+
const repeatedKeysCountsMap = new Map();
|
|
6
|
+
|
|
7
|
+
export const clearRepeatedKeysCountsMap = () => {
|
|
8
|
+
repeatedKeysCountsMap.clear();
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function encodeStableId(str) {
|
|
12
|
+
let hash = 0;
|
|
13
|
+
for (let i = 0; i < str.length; i += 4) {
|
|
14
|
+
// eslint-disable-next-line no-bitwise
|
|
15
|
+
hash = (hash * 31 + str.charCodeAt(i)) >>> 0; // 32-битный хеш
|
|
16
|
+
}
|
|
17
|
+
const base36 = hash.toString(36);
|
|
18
|
+
return base36.padStart(7, "0");
|
|
19
|
+
}
|
|
20
|
+
// собирает data-testid предков элемента до рутового компонента в строку
|
|
21
|
+
function collectHashesByParentsTestIds(el) {
|
|
22
|
+
const segments = [];
|
|
23
|
+
let node = el;
|
|
24
|
+
while (node && node.nodeType === Node.ELEMENT_NODE) {
|
|
25
|
+
if (node.hasAttribute(COMPONENT_TEST_NAME)) {
|
|
26
|
+
const testID = node.getAttribute(COMPONENT_TEST_NAME);
|
|
27
|
+
if (segments[segments.length - 1] !== testID) {
|
|
28
|
+
const testIDHash = testID.replace(/[_aeiouy-]/gi, "");
|
|
29
|
+
segments.push(testIDHash);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (
|
|
33
|
+
node.getAttribute &&
|
|
34
|
+
node.getAttribute(COMPONENT_TEST_NAME) === "app-root"
|
|
35
|
+
) {
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
node = node.parentElement;
|
|
39
|
+
}
|
|
40
|
+
return segments.join("");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Собираем текущие максимальные индексы для уже проставленных data-test
|
|
44
|
+
function buildCountsForExistedDataTests(rootNode) {
|
|
45
|
+
const existingDataTestNodes = rootNode.querySelectorAll(
|
|
46
|
+
`[${STABLE_ID_ATTRIBUTE_NAME}]`,
|
|
47
|
+
);
|
|
48
|
+
existingDataTestNodes.forEach((node) => {
|
|
49
|
+
const dataTestValue = node.getAttribute(STABLE_ID_ATTRIBUTE_NAME);
|
|
50
|
+
const dataTestParts = dataTestValue.split("::"); // [existingDataTestID, encodedID, index]
|
|
51
|
+
const groupedID = `${dataTestParts[0]}::${dataTestParts[1]}`;
|
|
52
|
+
const index = Number(dataTestParts[2]) || 0;
|
|
53
|
+
const prev = repeatedKeysCountsMap.get(groupedID) ?? 0;
|
|
54
|
+
if (index + 1 > prev) {
|
|
55
|
+
repeatedKeysCountsMap.set(groupedID, index + 1);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function applyDataTestIDsToDom(addedRootNode = document.body) {
|
|
61
|
+
if (!addedRootNode) return;
|
|
62
|
+
buildCountsForExistedDataTests(addedRootNode);
|
|
63
|
+
if (repeatedKeysCountsMap.size >= KEYS_COUNTS_MAP_MAX_SIZE) {
|
|
64
|
+
clearRepeatedKeysCountsMap();
|
|
65
|
+
}
|
|
66
|
+
const allAddedNodes = [];
|
|
67
|
+
if (!addedRootNode.hasAttribute(STABLE_ID_ATTRIBUTE_NAME)) {
|
|
68
|
+
allAddedNodes.push(addedRootNode);
|
|
69
|
+
}
|
|
70
|
+
const allNotDataTestChildren = addedRootNode.querySelectorAll(
|
|
71
|
+
`*:not([${STABLE_ID_ATTRIBUTE_NAME}])`,
|
|
72
|
+
);
|
|
73
|
+
allAddedNodes.push(...allNotDataTestChildren);
|
|
74
|
+
if (!allAddedNodes.length) return;
|
|
75
|
+
allAddedNodes.forEach((node) => {
|
|
76
|
+
let existingDataTestID = "";
|
|
77
|
+
if (node.hasAttribute(COMPONENT_TEST_NAME)) {
|
|
78
|
+
existingDataTestID = node.getAttribute(COMPONENT_TEST_NAME);
|
|
79
|
+
}
|
|
80
|
+
const fullHashChain = collectHashesByParentsTestIds(node);
|
|
81
|
+
const encodedID = encodeStableId(fullHashChain);
|
|
82
|
+
const groupedID = `${existingDataTestID}::${encodedID}`;
|
|
83
|
+
const nextIndex = repeatedKeysCountsMap.get(groupedID) ?? 0;
|
|
84
|
+
repeatedKeysCountsMap.set(groupedID, nextIndex + 1);
|
|
85
|
+
const resultID = `${groupedID}::${nextIndex}`;
|
|
86
|
+
node.setAttribute(STABLE_ID_ATTRIBUTE_NAME, resultID);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const clearNodeMapKeys = (removingRootNode = document.body) => {
|
|
91
|
+
const allRemovingNodes = [];
|
|
92
|
+
if (removingRootNode.hasAttribute(STABLE_ID_ATTRIBUTE_NAME)) {
|
|
93
|
+
allRemovingNodes.push(removingRootNode);
|
|
94
|
+
}
|
|
95
|
+
const allDataTestChildren = removingRootNode.querySelectorAll(
|
|
96
|
+
`[${STABLE_ID_ATTRIBUTE_NAME}]`,
|
|
97
|
+
);
|
|
98
|
+
allRemovingNodes.push(...allDataTestChildren);
|
|
99
|
+
allRemovingNodes.forEach((node) => {
|
|
100
|
+
const dataTestValueArray = node
|
|
101
|
+
.getAttribute(STABLE_ID_ATTRIBUTE_NAME)
|
|
102
|
+
.split("::");
|
|
103
|
+
dataTestValueArray.pop();
|
|
104
|
+
const nodeKey = dataTestValueArray.join("::");
|
|
105
|
+
const keyCount = repeatedKeysCountsMap.get(nodeKey);
|
|
106
|
+
if (keyCount) {
|
|
107
|
+
repeatedKeysCountsMap.set(nodeKey, keyCount - 1);
|
|
108
|
+
} else {
|
|
109
|
+
repeatedKeysCountsMap.delete(nodeKey);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
export function initStableIds() {
|
|
115
|
+
if (typeof document === "undefined") return;
|
|
116
|
+
applyDataTestIDsToDom();
|
|
117
|
+
const observer = new MutationObserver((mutations) => {
|
|
118
|
+
mutations.forEach((mutationRecord) => {
|
|
119
|
+
mutationRecord.removedNodes.forEach((node) => {
|
|
120
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
121
|
+
clearNodeMapKeys(node);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
mutationRecord.addedNodes.forEach((node) => {
|
|
125
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
|
126
|
+
applyDataTestIDsToDom(node);
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
observer.observe(document.body, {
|
|
132
|
+
childList: true,
|
|
133
|
+
subtree: true,
|
|
134
|
+
});
|
|
135
|
+
}
|
package/src/index.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
const eslintConfig = require(
|
|
2
|
-
const prettierConfig = require(
|
|
1
|
+
const eslintConfig = require("./eslint/config");
|
|
2
|
+
const prettierConfig = require("./prettier/config");
|
|
3
|
+
const {
|
|
4
|
+
initStableIds,
|
|
5
|
+
clearRepeatedKeysCountsMap,
|
|
6
|
+
} = require("../scripts/addDataTestAttribute/addDataTestInDOM.js");
|
|
3
7
|
|
|
4
8
|
module.exports = {
|
|
5
|
-
|
|
6
|
-
|
|
9
|
+
eslintConfig,
|
|
10
|
+
prettierConfig,
|
|
11
|
+
initStableIds,
|
|
12
|
+
clearRepeatedKeysCountsMap,
|
|
7
13
|
};
|