@dev-blinq/cucumber_client 1.0.1427-dev → 1.0.1427-stage
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/bin/assets/bundled_scripts/recorder.js +73 -73
- package/bin/assets/preload/css_gen.js +10 -10
- package/bin/assets/preload/toolbar.js +27 -29
- package/bin/assets/preload/unique_locators.js +1 -1
- package/bin/assets/preload/yaml.js +288 -275
- package/bin/assets/scripts/aria_snapshot.js +223 -220
- package/bin/assets/scripts/dom_attr.js +329 -329
- package/bin/assets/scripts/dom_parent.js +169 -174
- package/bin/assets/scripts/event_utils.js +94 -94
- package/bin/assets/scripts/pw.js +2050 -1949
- package/bin/assets/scripts/recorder.js +70 -45
- package/bin/assets/scripts/snapshot_capturer.js +147 -147
- package/bin/assets/scripts/unique_locators.js +170 -49
- package/bin/assets/scripts/yaml.js +796 -783
- package/bin/assets/templates/_hooks_template.txt +6 -2
- package/bin/assets/templates/utils_template.txt +16 -16
- package/bin/client/code_cleanup/find_step_definition_references.js +0 -1
- package/bin/client/code_gen/api_codegen.js +2 -2
- package/bin/client/code_gen/code_inversion.js +63 -2
- package/bin/client/code_gen/function_signature.js +4 -0
- package/bin/client/code_gen/page_reflection.js +52 -11
- package/bin/client/code_gen/playwright_codeget.js +25 -3
- package/bin/client/cucumber/feature_data.js +2 -2
- package/bin/client/cucumber/project_to_document.js +8 -2
- package/bin/client/cucumber/steps_definitions.js +19 -3
- package/bin/client/local_agent.js +3 -2
- package/bin/client/parse_feature_file.js +23 -26
- package/bin/client/playground/projects/env.json +2 -2
- package/bin/client/recorderv3/bvt_init.js +363 -0
- package/bin/client/recorderv3/bvt_recorder.js +1008 -47
- package/bin/client/recorderv3/implemented_steps.js +2 -0
- package/bin/client/recorderv3/index.js +3 -283
- package/bin/client/recorderv3/scriptTest.js +1 -1
- package/bin/client/recorderv3/services.js +818 -142
- package/bin/client/recorderv3/step_runner.js +31 -8
- package/bin/client/recorderv3/step_utils.js +510 -39
- package/bin/client/recorderv3/update_feature.js +32 -13
- package/bin/client/recorderv3/wbr_entry.js +61 -0
- package/bin/client/recording.js +1 -0
- package/bin/client/upload-service.js +4 -2
- package/bin/client/utils/socket_logger.js +1 -1
- package/bin/index.js +4 -1
- package/package.json +6 -4
|
@@ -1,372 +1,372 @@
|
|
|
1
|
-
|
|
2
1
|
class DOM_Attr {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (isUnique) {
|
|
31
|
-
uniqueAttrs[attrName] = attrValue;
|
|
32
|
-
}
|
|
2
|
+
/**
|
|
3
|
+
* Finds attributes that are unique to the target element
|
|
4
|
+
* @param {Element} target - Target element
|
|
5
|
+
* @param {Element[]} similarElements - Array of similar elements to compare against
|
|
6
|
+
* @returns {Object} - Object with unique attribute names as keys and their values
|
|
7
|
+
*/
|
|
8
|
+
findUniqueAttributes(target, similarElements) {
|
|
9
|
+
if (!target || !Array.isArray(similarElements)) return {};
|
|
10
|
+
|
|
11
|
+
const targetAttrs = getElementAttributes(target);
|
|
12
|
+
const uniqueAttrs = {};
|
|
13
|
+
|
|
14
|
+
// Check each attribute of the target
|
|
15
|
+
for (const [attrName, attrValue] of Object.entries(targetAttrs)) {
|
|
16
|
+
let isUnique = true;
|
|
17
|
+
|
|
18
|
+
// Check if any similar element has the same attribute with the same value
|
|
19
|
+
for (const element of similarElements) {
|
|
20
|
+
if (element === target) continue; // Skip self
|
|
21
|
+
|
|
22
|
+
const elementAttrs = getElementAttributes(element);
|
|
23
|
+
if (elementAttrs[attrName] === attrValue) {
|
|
24
|
+
isUnique = false;
|
|
25
|
+
break;
|
|
33
26
|
}
|
|
27
|
+
}
|
|
34
28
|
|
|
35
|
-
|
|
29
|
+
if (isUnique) {
|
|
30
|
+
uniqueAttrs[attrName] = attrValue;
|
|
31
|
+
}
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
* @param {Element} target - Target element
|
|
41
|
-
* @param {Element[]} similarElements - Array of similar elements to compare against
|
|
42
|
-
* @returns {Object} - Object with attribute names that only target has
|
|
43
|
-
*/
|
|
44
|
-
findExclusiveAttributes(target, similarElements) {
|
|
45
|
-
if (!target || !Array.isArray(similarElements)) return {};
|
|
46
|
-
|
|
47
|
-
const targetAttrs = getElementAttributes(target);
|
|
48
|
-
const exclusiveAttrs = {};
|
|
49
|
-
|
|
50
|
-
// Check each attribute of the target
|
|
51
|
-
for (const [attrName, attrValue] of Object.entries(targetAttrs)) {
|
|
52
|
-
let hasAttribute = false;
|
|
53
|
-
|
|
54
|
-
// Check if any similar element has this attribute (regardless of value)
|
|
55
|
-
for (const element of similarElements) {
|
|
56
|
-
if (element === target) continue; // Skip self
|
|
57
|
-
|
|
58
|
-
if (element.hasAttribute(attrName)) {
|
|
59
|
-
hasAttribute = true;
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
if (!hasAttribute) {
|
|
65
|
-
exclusiveAttrs[attrName] = attrValue;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
34
|
+
return uniqueAttrs;
|
|
35
|
+
}
|
|
68
36
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
findUniqueClassNames(target, similarElements) {
|
|
78
|
-
if (!target || !Array.isArray(similarElements)) return new Set();
|
|
79
|
-
|
|
80
|
-
const targetClasses = getElementClassNames(target);
|
|
81
|
-
const uniqueClasses = new Set();
|
|
82
|
-
|
|
83
|
-
// Check each class of the target
|
|
84
|
-
for (const className of targetClasses) {
|
|
85
|
-
let isUnique = true;
|
|
86
|
-
|
|
87
|
-
// Check if any similar element has the same class
|
|
88
|
-
for (const element of similarElements) {
|
|
89
|
-
if (element === target) continue; // Skip self
|
|
90
|
-
|
|
91
|
-
const elementClasses = getElementClassNames(element);
|
|
92
|
-
if (elementClasses.has(className)) {
|
|
93
|
-
isUnique = false;
|
|
94
|
-
break;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
if (isUnique) {
|
|
99
|
-
uniqueClasses.add(className);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
37
|
+
/**
|
|
38
|
+
* Finds attributes that exist on target but not on similar elements
|
|
39
|
+
* @param {Element} target - Target element
|
|
40
|
+
* @param {Element[]} similarElements - Array of similar elements to compare against
|
|
41
|
+
* @returns {Object} - Object with attribute names that only target has
|
|
42
|
+
*/
|
|
43
|
+
findExclusiveAttributes(target, similarElements) {
|
|
44
|
+
if (!target || !Array.isArray(similarElements)) return {};
|
|
102
45
|
|
|
103
|
-
|
|
104
|
-
}
|
|
46
|
+
const targetAttrs = getElementAttributes(target);
|
|
47
|
+
const exclusiveAttrs = {};
|
|
105
48
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
* @param {Element[]} similarElements - Array of similar elements to compare against
|
|
110
|
-
* @returns {Object} - Object with attribute names as keys and comparison info as values
|
|
111
|
-
*/
|
|
112
|
-
findDifferentValueAttributes(target, similarElements) {
|
|
113
|
-
if (!target || !Array.isArray(similarElements)) return {};
|
|
114
|
-
|
|
115
|
-
const targetAttrs = getElementAttributes(target);
|
|
116
|
-
const differentAttrs = {};
|
|
117
|
-
|
|
118
|
-
// Get all attributes from all elements
|
|
119
|
-
const allAttributes = new Set(Object.keys(targetAttrs));
|
|
120
|
-
similarElements.forEach(element => {
|
|
121
|
-
if (element !== target) {
|
|
122
|
-
const attrs = getElementAttributes(element);
|
|
123
|
-
Object.keys(attrs).forEach(attr => allAttributes.add(attr));
|
|
124
|
-
}
|
|
125
|
-
});
|
|
49
|
+
// Check each attribute of the target
|
|
50
|
+
for (const [attrName, attrValue] of Object.entries(targetAttrs)) {
|
|
51
|
+
let hasAttribute = false;
|
|
126
52
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const attrs = getElementAttributes(element);
|
|
135
|
-
const value = attrs[attrName];
|
|
136
|
-
if (value !== undefined && value !== targetValue) {
|
|
137
|
-
otherValues.push(value);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
|
|
142
|
-
if (otherValues.length > 0) {
|
|
143
|
-
differentAttrs[attrName] = {
|
|
144
|
-
targetValue: targetValue || null,
|
|
145
|
-
otherValues: [...new Set(otherValues)] // Remove duplicates
|
|
146
|
-
};
|
|
147
|
-
}
|
|
53
|
+
// Check if any similar element has this attribute (regardless of value)
|
|
54
|
+
for (const element of similarElements) {
|
|
55
|
+
if (element === target) continue; // Skip self
|
|
56
|
+
|
|
57
|
+
if (element.hasAttribute(attrName)) {
|
|
58
|
+
hasAttribute = true;
|
|
59
|
+
break;
|
|
148
60
|
}
|
|
61
|
+
}
|
|
149
62
|
|
|
150
|
-
|
|
63
|
+
if (!hasAttribute) {
|
|
64
|
+
exclusiveAttrs[attrName] = attrValue;
|
|
65
|
+
}
|
|
151
66
|
}
|
|
152
67
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
68
|
+
return exclusiveAttrs;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Finds classnames that are unique to the target element
|
|
72
|
+
* @param {Element} target - Target element
|
|
73
|
+
* @param {Element[]} similarElements - Array of similar elements to compare against
|
|
74
|
+
* @returns {Set<string>} - Set of unique classnames
|
|
75
|
+
*/
|
|
76
|
+
findUniqueClassNames(target, similarElements) {
|
|
77
|
+
if (!target || !Array.isArray(similarElements)) return new Set();
|
|
78
|
+
|
|
79
|
+
const targetClasses = getElementClassNames(target);
|
|
80
|
+
const uniqueClasses = new Set();
|
|
81
|
+
|
|
82
|
+
// Check each class of the target
|
|
83
|
+
for (const className of targetClasses) {
|
|
84
|
+
let isUnique = true;
|
|
85
|
+
|
|
86
|
+
// Check if any similar element has the same class
|
|
87
|
+
for (const element of similarElements) {
|
|
88
|
+
if (element === target) continue; // Skip self
|
|
89
|
+
|
|
90
|
+
const elementClasses = getElementClassNames(element);
|
|
91
|
+
if (elementClasses.has(className)) {
|
|
92
|
+
isUnique = false;
|
|
93
|
+
break;
|
|
179
94
|
}
|
|
95
|
+
}
|
|
180
96
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
97
|
+
if (isUnique) {
|
|
98
|
+
uniqueClasses.add(className);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
184
101
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
102
|
+
return uniqueClasses;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Finds attributes with different values between target and similar elements
|
|
107
|
+
* @param {Element} target - Target element
|
|
108
|
+
* @param {Element[]} similarElements - Array of similar elements to compare against
|
|
109
|
+
* @returns {Object} - Object with attribute names as keys and comparison info as values
|
|
110
|
+
*/
|
|
111
|
+
findDifferentValueAttributes(target, similarElements) {
|
|
112
|
+
if (!target || !Array.isArray(similarElements)) return {};
|
|
113
|
+
|
|
114
|
+
const targetAttrs = getElementAttributes(target);
|
|
115
|
+
const differentAttrs = {};
|
|
116
|
+
|
|
117
|
+
// Get all attributes from all elements
|
|
118
|
+
const allAttributes = new Set(Object.keys(targetAttrs));
|
|
119
|
+
similarElements.forEach((element) => {
|
|
120
|
+
if (element !== target) {
|
|
121
|
+
const attrs = getElementAttributes(element);
|
|
122
|
+
Object.keys(attrs).forEach((attr) => allAttributes.add(attr));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// Check each attribute
|
|
127
|
+
for (const attrName of allAttributes) {
|
|
128
|
+
const targetValue = targetAttrs[attrName];
|
|
129
|
+
const otherValues = [];
|
|
130
|
+
|
|
131
|
+
similarElements.forEach((element) => {
|
|
132
|
+
if (element !== target) {
|
|
133
|
+
const attrs = getElementAttributes(element);
|
|
134
|
+
const value = attrs[attrName];
|
|
135
|
+
if (value !== undefined && value !== targetValue) {
|
|
136
|
+
otherValues.push(value);
|
|
137
|
+
}
|
|
201
138
|
}
|
|
139
|
+
});
|
|
202
140
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (ignoreAriaAttributes && attrName.startsWith('aria-')) return true;
|
|
208
|
-
return false;
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// Filter attributes for target and similar elements
|
|
212
|
-
const filterAttributes = (attrs) => {
|
|
213
|
-
const filtered = {};
|
|
214
|
-
for (const [name, value] of Object.entries(attrs)) {
|
|
215
|
-
if (!shouldIgnoreAttribute(name)) {
|
|
216
|
-
filtered[name] = value;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
return filtered;
|
|
141
|
+
if (otherValues.length > 0) {
|
|
142
|
+
differentAttrs[attrName] = {
|
|
143
|
+
targetValue: targetValue || null,
|
|
144
|
+
otherValues: [...new Set(otherValues)], // Remove duplicates
|
|
220
145
|
};
|
|
146
|
+
}
|
|
147
|
+
}
|
|
221
148
|
|
|
222
|
-
|
|
223
|
-
|
|
149
|
+
return differentAttrs;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Main function: Finds all differentiating attributes and classnames
|
|
154
|
+
* @param {Element} target - Target element to find differentiators for
|
|
155
|
+
* @param {Element[]} similarElements - Array of similar elements to compare against
|
|
156
|
+
* @param {Object} options - Options for the comparison
|
|
157
|
+
* @param {boolean} options.includeComputedStyles - Whether to include computed style differences
|
|
158
|
+
* @param {string[]} options.styleProperties - Array of CSS properties to check if includeComputedStyles is true
|
|
159
|
+
* @param {boolean} options.ignoreDataAttributes - Whether to ignore data-* attributes
|
|
160
|
+
* @param {boolean} options.ignoreAriaAttributes - Whether to ignore aria-* attributes
|
|
161
|
+
* @param {string[]} options.ignoreAttributes - Array of attribute names to ignore
|
|
162
|
+
* @param {boolean} options.caseSensitiveClasses - Whether class comparison should be case sensitive
|
|
163
|
+
* @returns {Object} - Comprehensive differentiating information
|
|
164
|
+
*/
|
|
165
|
+
findDifferentiatingAttributes(target, similarElements, options = {}) {
|
|
166
|
+
const {
|
|
167
|
+
includeComputedStyles = false,
|
|
168
|
+
styleProperties = ["color", "background-color", "font-size", "display", "position"],
|
|
169
|
+
ignoreDataAttributes = false,
|
|
170
|
+
ignoreAriaAttributes = false,
|
|
171
|
+
ignoreAttributes = [],
|
|
172
|
+
caseSensitiveClasses = true,
|
|
173
|
+
} = options;
|
|
174
|
+
|
|
175
|
+
// Validate inputs
|
|
176
|
+
if (!target) {
|
|
177
|
+
throw new Error("Target element is required");
|
|
178
|
+
}
|
|
224
179
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
180
|
+
if (!Array.isArray(similarElements)) {
|
|
181
|
+
throw new Error("similarElements must be an array");
|
|
182
|
+
}
|
|
228
183
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
184
|
+
// Filter out the target from similar elements if it exists
|
|
185
|
+
const filteredSimilarElements = similarElements.filter((el) => el !== target);
|
|
186
|
+
|
|
187
|
+
if (filteredSimilarElements.length === 0) {
|
|
188
|
+
return {
|
|
189
|
+
uniqueAttributes: getElementAttributes(target),
|
|
190
|
+
exclusiveAttributes: getElementAttributes(target),
|
|
191
|
+
uniqueClassNames: getElementClassNames(target),
|
|
192
|
+
differentValueAttributes: {},
|
|
193
|
+
computedStyleDifferences: {},
|
|
194
|
+
summary: {
|
|
195
|
+
hasUniqueAttributes: Object.keys(getElementAttributes(target)).length > 0,
|
|
196
|
+
hasUniqueClasses: getElementClassNames(target).size > 0,
|
|
197
|
+
totalDifferences: Object.keys(getElementAttributes(target)).length + getElementClassNames(target).size,
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
}
|
|
232
201
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
uniqueAttributes[attrName] = attrValue;
|
|
248
|
-
}
|
|
202
|
+
// Helper function to filter attributes based on options
|
|
203
|
+
const shouldIgnoreAttribute = (attrName) => {
|
|
204
|
+
if (ignoreAttributes.includes(attrName)) return true;
|
|
205
|
+
if (ignoreDataAttributes && attrName.startsWith("data-")) return true;
|
|
206
|
+
if (ignoreAriaAttributes && attrName.startsWith("aria-")) return true;
|
|
207
|
+
return false;
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Filter attributes for target and similar elements
|
|
211
|
+
const filterAttributes = (attrs) => {
|
|
212
|
+
const filtered = {};
|
|
213
|
+
for (const [name, value] of Object.entries(attrs)) {
|
|
214
|
+
if (!shouldIgnoreAttribute(name)) {
|
|
215
|
+
filtered[name] = value;
|
|
249
216
|
}
|
|
217
|
+
}
|
|
218
|
+
return filtered;
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
// Get filtered attributes
|
|
222
|
+
const targetAttrs = filterAttributes(getElementAttributes(target));
|
|
223
|
+
|
|
224
|
+
// Temporarily override attribute getting to use filtered attributes
|
|
225
|
+
const originalTarget = { ...target };
|
|
226
|
+
const originalGetElementAttributes = getElementAttributes;
|
|
227
|
+
|
|
228
|
+
const getFilteredAttributes = (element) => {
|
|
229
|
+
return filterAttributes(originalGetElementAttributes(element));
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
// Find unique attributes (same name and value combination is unique)
|
|
233
|
+
const uniqueAttributes = {};
|
|
234
|
+
for (const [attrName, attrValue] of Object.entries(targetAttrs)) {
|
|
235
|
+
let isUnique = true;
|
|
236
|
+
|
|
237
|
+
for (const element of filteredSimilarElements) {
|
|
238
|
+
const elementAttrs = getFilteredAttributes(element);
|
|
239
|
+
if (elementAttrs[attrName] === attrValue) {
|
|
240
|
+
isUnique = false;
|
|
241
|
+
break;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (isUnique) {
|
|
246
|
+
uniqueAttributes[attrName] = attrValue;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Find exclusive attributes (target has, others don't)
|
|
251
|
+
const exclusiveAttributes = {};
|
|
252
|
+
for (const [attrName, attrValue] of Object.entries(targetAttrs)) {
|
|
253
|
+
let hasAttribute = false;
|
|
250
254
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
for (const element of filteredSimilarElements) {
|
|
257
|
-
if (element.hasAttribute(attrName) && !shouldIgnoreAttribute(attrName)) {
|
|
258
|
-
hasAttribute = true;
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
if (!hasAttribute) {
|
|
264
|
-
exclusiveAttributes[attrName] = attrValue;
|
|
265
|
-
}
|
|
255
|
+
for (const element of filteredSimilarElements) {
|
|
256
|
+
if (element.hasAttribute(attrName) && !shouldIgnoreAttribute(attrName)) {
|
|
257
|
+
hasAttribute = true;
|
|
258
|
+
break;
|
|
266
259
|
}
|
|
260
|
+
}
|
|
267
261
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
262
|
+
if (!hasAttribute) {
|
|
263
|
+
exclusiveAttributes[attrName] = attrValue;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
271
266
|
|
|
272
|
-
|
|
273
|
-
|
|
267
|
+
// Find unique classnames
|
|
268
|
+
const targetClasses = getElementClassNames(target);
|
|
269
|
+
const uniqueClassNames = new Set();
|
|
274
270
|
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
const hasClass = caseSensitiveClasses
|
|
278
|
-
? elementClasses.has(className)
|
|
279
|
-
: Array.from(elementClasses).some(cls => cls.toLowerCase() === className.toLowerCase());
|
|
271
|
+
for (const className of targetClasses) {
|
|
272
|
+
let isUnique = true;
|
|
280
273
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
274
|
+
for (const element of filteredSimilarElements) {
|
|
275
|
+
const elementClasses = getElementClassNames(element);
|
|
276
|
+
const hasClass = caseSensitiveClasses
|
|
277
|
+
? elementClasses.has(className)
|
|
278
|
+
: Array.from(elementClasses).some((cls) => cls.toLowerCase() === className.toLowerCase());
|
|
286
279
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
280
|
+
if (hasClass) {
|
|
281
|
+
isUnique = false;
|
|
282
|
+
break;
|
|
290
283
|
}
|
|
284
|
+
}
|
|
291
285
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
286
|
+
if (isUnique) {
|
|
287
|
+
uniqueClassNames.add(className);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
295
290
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
});
|
|
291
|
+
// Find different value attributes
|
|
292
|
+
const differentValueAttributes = {};
|
|
293
|
+
const allAttributes = new Set(Object.keys(targetAttrs));
|
|
300
294
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
filteredSimilarElements.forEach(element => {
|
|
306
|
-
const attrs = getFilteredAttributes(element);
|
|
307
|
-
const value = attrs[attrName];
|
|
308
|
-
if (value !== undefined && value !== targetValue) {
|
|
309
|
-
otherValues.push(value);
|
|
310
|
-
}
|
|
311
|
-
});
|
|
312
|
-
|
|
313
|
-
if (otherValues.length > 0) {
|
|
314
|
-
differentValueAttributes[attrName] = {
|
|
315
|
-
targetValue: targetValue || null,
|
|
316
|
-
otherValues: [...new Set(otherValues)]
|
|
317
|
-
};
|
|
318
|
-
}
|
|
319
|
-
}
|
|
295
|
+
filteredSimilarElements.forEach((element) => {
|
|
296
|
+
const attrs = getFilteredAttributes(element);
|
|
297
|
+
Object.keys(attrs).forEach((attr) => allAttributes.add(attr));
|
|
298
|
+
});
|
|
320
299
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
filteredSimilarElements.forEach(element => {
|
|
331
|
-
const elementStyles = getComputedStyles(element, [property]);
|
|
332
|
-
const value = elementStyles[property];
|
|
333
|
-
if (value && value !== targetValue) {
|
|
334
|
-
otherValues.push(value);
|
|
335
|
-
}
|
|
336
|
-
});
|
|
337
|
-
|
|
338
|
-
if (otherValues.length > 0) {
|
|
339
|
-
computedStyleDifferences[property] = {
|
|
340
|
-
targetValue,
|
|
341
|
-
otherValues: [...new Set(otherValues)]
|
|
342
|
-
};
|
|
343
|
-
}
|
|
344
|
-
}
|
|
300
|
+
for (const attrName of allAttributes) {
|
|
301
|
+
const targetValue = targetAttrs[attrName];
|
|
302
|
+
const otherValues = [];
|
|
303
|
+
|
|
304
|
+
filteredSimilarElements.forEach((element) => {
|
|
305
|
+
const attrs = getFilteredAttributes(element);
|
|
306
|
+
const value = attrs[attrName];
|
|
307
|
+
if (value !== undefined && value !== targetValue) {
|
|
308
|
+
otherValues.push(value);
|
|
345
309
|
}
|
|
310
|
+
});
|
|
346
311
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
hasUniqueClasses: uniqueClassNames.size > 0,
|
|
352
|
-
hasDifferentValues: Object.keys(differentValueAttributes).length > 0,
|
|
353
|
-
hasStyleDifferences: Object.keys(computedStyleDifferences).length > 0,
|
|
354
|
-
totalDifferences: Object.keys(uniqueAttributes).length +
|
|
355
|
-
uniqueClassNames.size +
|
|
356
|
-
Object.keys(differentValueAttributes).length +
|
|
357
|
-
Object.keys(computedStyleDifferences).length,
|
|
358
|
-
comparedAgainst: filteredSimilarElements.length
|
|
312
|
+
if (otherValues.length > 0) {
|
|
313
|
+
differentValueAttributes[attrName] = {
|
|
314
|
+
targetValue: targetValue || null,
|
|
315
|
+
otherValues: [...new Set(otherValues)],
|
|
359
316
|
};
|
|
317
|
+
}
|
|
318
|
+
}
|
|
360
319
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
320
|
+
// Computed style differences
|
|
321
|
+
let computedStyleDifferences = {};
|
|
322
|
+
if (includeComputedStyles) {
|
|
323
|
+
const targetStyles = getComputedStyles(target, styleProperties);
|
|
324
|
+
|
|
325
|
+
for (const property of styleProperties) {
|
|
326
|
+
const targetValue = targetStyles[property];
|
|
327
|
+
const otherValues = [];
|
|
328
|
+
|
|
329
|
+
filteredSimilarElements.forEach((element) => {
|
|
330
|
+
const elementStyles = getComputedStyles(element, [property]);
|
|
331
|
+
const value = elementStyles[property];
|
|
332
|
+
if (value && value !== targetValue) {
|
|
333
|
+
otherValues.push(value);
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
if (otherValues.length > 0) {
|
|
338
|
+
computedStyleDifferences[property] = {
|
|
339
|
+
targetValue,
|
|
340
|
+
otherValues: [...new Set(otherValues)],
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
369
344
|
}
|
|
345
|
+
|
|
346
|
+
// Generate summary
|
|
347
|
+
const summary = {
|
|
348
|
+
hasUniqueAttributes: Object.keys(uniqueAttributes).length > 0,
|
|
349
|
+
hasExclusiveAttributes: Object.keys(exclusiveAttributes).length > 0,
|
|
350
|
+
hasUniqueClasses: uniqueClassNames.size > 0,
|
|
351
|
+
hasDifferentValues: Object.keys(differentValueAttributes).length > 0,
|
|
352
|
+
hasStyleDifferences: Object.keys(computedStyleDifferences).length > 0,
|
|
353
|
+
totalDifferences:
|
|
354
|
+
Object.keys(uniqueAttributes).length +
|
|
355
|
+
uniqueClassNames.size +
|
|
356
|
+
Object.keys(differentValueAttributes).length +
|
|
357
|
+
Object.keys(computedStyleDifferences).length,
|
|
358
|
+
comparedAgainst: filteredSimilarElements.length,
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
return {
|
|
362
|
+
uniqueAttributes,
|
|
363
|
+
exclusiveAttributes,
|
|
364
|
+
uniqueClassNames,
|
|
365
|
+
differentValueAttributes,
|
|
366
|
+
computedStyleDifferences,
|
|
367
|
+
summary,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
-
export default DOM_Attr;
|
|
372
|
+
export default DOM_Attr;
|