@dev-blinq/cucumber_client 1.0.1431-dev → 1.0.1431-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.
Files changed (43) hide show
  1. package/bin/assets/bundled_scripts/recorder.js +73 -73
  2. package/bin/assets/preload/css_gen.js +10 -10
  3. package/bin/assets/preload/toolbar.js +27 -29
  4. package/bin/assets/preload/unique_locators.js +1 -1
  5. package/bin/assets/preload/yaml.js +288 -275
  6. package/bin/assets/scripts/aria_snapshot.js +223 -220
  7. package/bin/assets/scripts/dom_attr.js +329 -329
  8. package/bin/assets/scripts/dom_parent.js +169 -174
  9. package/bin/assets/scripts/event_utils.js +94 -94
  10. package/bin/assets/scripts/pw.js +2050 -1949
  11. package/bin/assets/scripts/recorder.js +70 -45
  12. package/bin/assets/scripts/snapshot_capturer.js +147 -147
  13. package/bin/assets/scripts/unique_locators.js +170 -49
  14. package/bin/assets/scripts/yaml.js +796 -783
  15. package/bin/assets/templates/_hooks_template.txt +6 -2
  16. package/bin/assets/templates/utils_template.txt +16 -16
  17. package/bin/client/code_cleanup/find_step_definition_references.js +0 -1
  18. package/bin/client/code_gen/api_codegen.js +2 -2
  19. package/bin/client/code_gen/code_inversion.js +63 -2
  20. package/bin/client/code_gen/function_signature.js +4 -0
  21. package/bin/client/code_gen/page_reflection.js +52 -11
  22. package/bin/client/code_gen/playwright_codeget.js +28 -22
  23. package/bin/client/cucumber/feature_data.js +2 -2
  24. package/bin/client/cucumber/project_to_document.js +8 -2
  25. package/bin/client/cucumber/steps_definitions.js +19 -3
  26. package/bin/client/local_agent.js +3 -2
  27. package/bin/client/parse_feature_file.js +23 -26
  28. package/bin/client/playground/projects/env.json +2 -2
  29. package/bin/client/recorderv3/bvt_init.js +363 -0
  30. package/bin/client/recorderv3/bvt_recorder.js +1009 -47
  31. package/bin/client/recorderv3/implemented_steps.js +2 -0
  32. package/bin/client/recorderv3/index.js +3 -283
  33. package/bin/client/recorderv3/scriptTest.js +1 -1
  34. package/bin/client/recorderv3/services.js +818 -142
  35. package/bin/client/recorderv3/step_runner.js +28 -8
  36. package/bin/client/recorderv3/step_utils.js +514 -39
  37. package/bin/client/recorderv3/update_feature.js +32 -13
  38. package/bin/client/recorderv3/wbr_entry.js +61 -0
  39. package/bin/client/recording.js +1 -0
  40. package/bin/client/upload-service.js +4 -2
  41. package/bin/client/utils/socket_logger.js +1 -1
  42. package/bin/index.js +4 -1
  43. package/package.json +6 -4
@@ -1,372 +1,372 @@
1
-
2
1
  class DOM_Attr {
3
- /**
4
- * Finds attributes that are unique to the target element
5
- * @param {Element} target - Target element
6
- * @param {Element[]} similarElements - Array of similar elements to compare against
7
- * @returns {Object} - Object with unique attribute names as keys and their values
8
- */
9
- findUniqueAttributes(target, similarElements) {
10
- if (!target || !Array.isArray(similarElements)) return {};
11
-
12
- const targetAttrs = getElementAttributes(target);
13
- const uniqueAttrs = {};
14
-
15
- // Check each attribute of the target
16
- for (const [attrName, attrValue] of Object.entries(targetAttrs)) {
17
- let isUnique = true;
18
-
19
- // Check if any similar element has the same attribute with the same value
20
- for (const element of similarElements) {
21
- if (element === target) continue; // Skip self
22
-
23
- const elementAttrs = getElementAttributes(element);
24
- if (elementAttrs[attrName] === attrValue) {
25
- isUnique = false;
26
- break;
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
- return uniqueAttrs;
29
+ if (isUnique) {
30
+ uniqueAttrs[attrName] = attrValue;
31
+ }
36
32
  }
37
33
 
38
- /**
39
- * Finds attributes that exist on target but not on similar elements
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
- return exclusiveAttrs;
70
- }
71
- /**
72
- * Finds classnames that are unique to the target element
73
- * @param {Element} target - Target element
74
- * @param {Element[]} similarElements - Array of similar elements to compare against
75
- * @returns {Set<string>} - Set of unique classnames
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
- return uniqueClasses;
104
- }
46
+ const targetAttrs = getElementAttributes(target);
47
+ const exclusiveAttrs = {};
105
48
 
106
- /**
107
- * Finds attributes with different values between target and similar elements
108
- * @param {Element} target - Target element
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
- // Check each attribute
128
- for (const attrName of allAttributes) {
129
- const targetValue = targetAttrs[attrName];
130
- const otherValues = [];
131
-
132
- similarElements.forEach(element => {
133
- if (element !== target) {
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
- return differentAttrs;
63
+ if (!hasAttribute) {
64
+ exclusiveAttrs[attrName] = attrValue;
65
+ }
151
66
  }
152
67
 
153
- /**
154
- * Main function: Finds all differentiating attributes and classnames
155
- * @param {Element} target - Target element to find differentiators for
156
- * @param {Element[]} similarElements - Array of similar elements to compare against
157
- * @param {Object} options - Options for the comparison
158
- * @param {boolean} options.includeComputedStyles - Whether to include computed style differences
159
- * @param {string[]} options.styleProperties - Array of CSS properties to check if includeComputedStyles is true
160
- * @param {boolean} options.ignoreDataAttributes - Whether to ignore data-* attributes
161
- * @param {boolean} options.ignoreAriaAttributes - Whether to ignore aria-* attributes
162
- * @param {string[]} options.ignoreAttributes - Array of attribute names to ignore
163
- * @param {boolean} options.caseSensitiveClasses - Whether class comparison should be case sensitive
164
- * @returns {Object} - Comprehensive differentiating information
165
- */
166
- findDifferentiatingAttributes(target, similarElements, options = {}) {
167
- const {
168
- includeComputedStyles = false,
169
- styleProperties = ['color', 'background-color', 'font-size', 'display', 'position'],
170
- ignoreDataAttributes = false,
171
- ignoreAriaAttributes = false,
172
- ignoreAttributes = [],
173
- caseSensitiveClasses = true
174
- } = options;
175
-
176
- // Validate inputs
177
- if (!target) {
178
- throw new Error('Target element is required');
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
- if (!Array.isArray(similarElements)) {
182
- throw new Error('similarElements must be an array');
183
- }
97
+ if (isUnique) {
98
+ uniqueClasses.add(className);
99
+ }
100
+ }
184
101
 
185
- // Filter out the target from similar elements if it exists
186
- const filteredSimilarElements = similarElements.filter(el => el !== target);
187
-
188
- if (filteredSimilarElements.length === 0) {
189
- return {
190
- uniqueAttributes: getElementAttributes(target),
191
- exclusiveAttributes: getElementAttributes(target),
192
- uniqueClassNames: getElementClassNames(target),
193
- differentValueAttributes: {},
194
- computedStyleDifferences: {},
195
- summary: {
196
- hasUniqueAttributes: Object.keys(getElementAttributes(target)).length > 0,
197
- hasUniqueClasses: getElementClassNames(target).size > 0,
198
- totalDifferences: Object.keys(getElementAttributes(target)).length + getElementClassNames(target).size
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
- // Helper function to filter attributes based on options
204
- const shouldIgnoreAttribute = (attrName) => {
205
- if (ignoreAttributes.includes(attrName)) return true;
206
- if (ignoreDataAttributes && attrName.startsWith('data-')) return true;
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
- // Get filtered attributes
223
- const targetAttrs = filterAttributes(getElementAttributes(target));
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
- // Temporarily override attribute getting to use filtered attributes
226
- const originalTarget = { ...target };
227
- const originalGetElementAttributes = getElementAttributes;
180
+ if (!Array.isArray(similarElements)) {
181
+ throw new Error("similarElements must be an array");
182
+ }
228
183
 
229
- const getFilteredAttributes = (element) => {
230
- return filterAttributes(originalGetElementAttributes(element));
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
- // Find unique attributes (same name and value combination is unique)
234
- const uniqueAttributes = {};
235
- for (const [attrName, attrValue] of Object.entries(targetAttrs)) {
236
- let isUnique = true;
237
-
238
- for (const element of filteredSimilarElements) {
239
- const elementAttrs = getFilteredAttributes(element);
240
- if (elementAttrs[attrName] === attrValue) {
241
- isUnique = false;
242
- break;
243
- }
244
- }
245
-
246
- if (isUnique) {
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
- // Find exclusive attributes (target has, others don't)
252
- const exclusiveAttributes = {};
253
- for (const [attrName, attrValue] of Object.entries(targetAttrs)) {
254
- let hasAttribute = false;
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
- // Find unique classnames
269
- const targetClasses = getElementClassNames(target);
270
- const uniqueClassNames = new Set();
262
+ if (!hasAttribute) {
263
+ exclusiveAttributes[attrName] = attrValue;
264
+ }
265
+ }
271
266
 
272
- for (const className of targetClasses) {
273
- let isUnique = true;
267
+ // Find unique classnames
268
+ const targetClasses = getElementClassNames(target);
269
+ const uniqueClassNames = new Set();
274
270
 
275
- for (const element of filteredSimilarElements) {
276
- const elementClasses = getElementClassNames(element);
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
- if (hasClass) {
282
- isUnique = false;
283
- break;
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
- if (isUnique) {
288
- uniqueClassNames.add(className);
289
- }
280
+ if (hasClass) {
281
+ isUnique = false;
282
+ break;
290
283
  }
284
+ }
291
285
 
292
- // Find different value attributes
293
- const differentValueAttributes = {};
294
- const allAttributes = new Set(Object.keys(targetAttrs));
286
+ if (isUnique) {
287
+ uniqueClassNames.add(className);
288
+ }
289
+ }
295
290
 
296
- filteredSimilarElements.forEach(element => {
297
- const attrs = getFilteredAttributes(element);
298
- Object.keys(attrs).forEach(attr => allAttributes.add(attr));
299
- });
291
+ // Find different value attributes
292
+ const differentValueAttributes = {};
293
+ const allAttributes = new Set(Object.keys(targetAttrs));
300
294
 
301
- for (const attrName of allAttributes) {
302
- const targetValue = targetAttrs[attrName];
303
- const otherValues = [];
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
- // Computed style differences
322
- let computedStyleDifferences = {};
323
- if (includeComputedStyles) {
324
- const targetStyles = getComputedStyles(target, styleProperties);
325
-
326
- for (const property of styleProperties) {
327
- const targetValue = targetStyles[property];
328
- const otherValues = [];
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
- // Generate summary
348
- const summary = {
349
- hasUniqueAttributes: Object.keys(uniqueAttributes).length > 0,
350
- hasExclusiveAttributes: Object.keys(exclusiveAttributes).length > 0,
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
- return {
362
- uniqueAttributes,
363
- exclusiveAttributes,
364
- uniqueClassNames,
365
- differentValueAttributes,
366
- computedStyleDifferences,
367
- summary
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;