ff-dom 1.0.16 → 1.0.17

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.
@@ -1,21 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getElementsFromHTML = void 0;
4
- //@ts-nocheck
5
4
  const jsdom_1 = require("jsdom");
6
5
  const isUnique = (xpathResult) => {
7
6
  return xpathResult && xpathResult.snapshotLength === 1;
8
7
  };
9
8
  const getElementFromShadowRoot = (element, selector) => {
10
9
  const shadowRoot = element.shadowRoot;
11
- if (shadowRoot && !selector.includes("dynamic")) {
10
+ if (shadowRoot && !selector.includes('dynamic')) {
12
11
  return shadowRoot.querySelector(selector);
13
12
  }
14
13
  return null;
15
14
  };
16
15
  const isSVGElement = (element) => {
17
- return (typeof window !== "undefined" &&
18
- typeof SVGElement !== "undefined" &&
16
+ return (typeof window !== 'undefined' &&
17
+ typeof SVGElement !== 'undefined' &&
19
18
  element instanceof SVGElement);
20
19
  };
21
20
  const getXPath = (element, xpath) => {
@@ -62,8 +61,8 @@ const getXPathByClass = (element) => {
62
61
  const getXpathByName = (element) => {
63
62
  const selector = element.nodeName.toLowerCase();
64
63
  const elementEl = element;
65
- if (elementEl.hasAttribute("name")) {
66
- const attrValue = elementEl.getAttribute("name");
64
+ if (elementEl.hasAttribute('name')) {
65
+ const attrValue = elementEl.getAttribute('name');
67
66
  const xpath = `//${selector}[@name='${attrValue}']`;
68
67
  return getXPath(element, xpath) || null;
69
68
  }
@@ -71,8 +70,8 @@ const getXpathByName = (element) => {
71
70
  };
72
71
  const getName = (element) => {
73
72
  const elementEl = element;
74
- if (elementEl.hasAttribute("name")) {
75
- const attrValue = elementEl.getAttribute("name");
73
+ if (elementEl.hasAttribute('name')) {
74
+ const attrValue = elementEl.getAttribute('name');
76
75
  const name = `${attrValue}`;
77
76
  return name || null;
78
77
  }
@@ -81,8 +80,8 @@ const getName = (element) => {
81
80
  const getXpathByPlaceholder = (element) => {
82
81
  const selector = element.nodeName.toLowerCase();
83
82
  const elementEl = element;
84
- if (elementEl.hasAttribute("placeholder")) {
85
- const attrValue = elementEl.getAttribute("placeholder");
83
+ if (elementEl.hasAttribute('placeholder')) {
84
+ const attrValue = elementEl.getAttribute('placeholder');
86
85
  const xpath = `//${selector}[@placeholder='${attrValue}']`;
87
86
  return getXPath(element, xpath) || null;
88
87
  }
@@ -91,8 +90,8 @@ const getXpathByPlaceholder = (element) => {
91
90
  const getXpathByType = (element) => {
92
91
  const selector = element.nodeName.toLowerCase();
93
92
  const elementEl = element;
94
- if (elementEl.hasAttribute("type")) {
95
- const attrValue = elementEl.getAttribute("type");
93
+ if (elementEl.hasAttribute('type')) {
94
+ const attrValue = elementEl.getAttribute('type');
96
95
  const xpath = `//${selector}[@type='${attrValue}']`;
97
96
  return getXPath(element, xpath) || null;
98
97
  }
@@ -116,22 +115,25 @@ const getXPathAbsolute = (element) => {
116
115
  path.unshift(selector);
117
116
  element = element.parentNode;
118
117
  }
119
- return "//" + path.join("/");
118
+ return '//' + path.join('/');
120
119
  };
121
120
  const relations = [
122
- "/preceding-sibling",
123
- "/following-sibling",
124
- "/parent",
125
- "/descendant",
126
- "/ancestor",
127
- "/self",
128
- "/ancestor-or-self",
129
- "/child",
130
- "/preceding",
131
- "/following",
121
+ '/preceding-sibling',
122
+ '/following-sibling',
123
+ '/parent',
124
+ '/descendant',
125
+ '/ancestor',
126
+ '/self',
127
+ '/ancestor-or-self',
128
+ '/child',
129
+ '/preceding',
130
+ '/following',
132
131
  ];
133
132
  const normalizeXPath = (xpath) => {
133
+ // Replace text() comparisons like text() = 'something' with normalize-space(.) = 'something'
134
+ // to avoid issues with whitespace differences
134
135
  xpath = xpath.replace(/text\(\)\s*=\s*['"](.+?)['"]/g, "normalize-space(.)='$1'");
136
+ // Replace direct . = 'something' comparisons with normalize-space(.) = 'something' for consistent text matching
135
137
  xpath = xpath.replace(/\.\s*=\s*['"](.+?)['"]/g, "normalize-space(.)='$1'");
136
138
  return xpath;
137
139
  };
@@ -153,7 +155,7 @@ function checkReferenceElementIsValid(locator, relation, tempDiv) {
153
155
  const window = currentElement.ownerDocument.defaultView;
154
156
  if (!window)
155
157
  return null;
156
- if (!locator.includes("dynamic")) {
158
+ if (!locator.includes('dynamic')) {
157
159
  const xpathEvaluator = new window.XPathEvaluator();
158
160
  const xpathResult = xpathEvaluator.evaluate(sourceLoc, currentElement.ownerDocument, null, window.XPathResult.FIRST_ORDERED_NODE_TYPE, null);
159
161
  const sourceElement = xpathResult.singleNodeValue;
@@ -166,13 +168,13 @@ function checkReferenceElementIsValid(locator, relation, tempDiv) {
166
168
  return relativeXpath;
167
169
  }
168
170
  else {
169
- console.error("Complete Locator is Invalid:", locator);
171
+ console.error('Complete Locator is Invalid:', locator);
170
172
  relativeXpath = locator;
171
173
  return relativeXpath;
172
174
  }
173
175
  }
174
176
  else {
175
- console.error("Source Locator Not Found:", sourceLoc);
177
+ console.error('Source Locator Not Found:', sourceLoc);
176
178
  }
177
179
  }
178
180
  }
@@ -182,15 +184,15 @@ const getElementsFromHTML = (name, desc, type, locators, isShared, projectId, pr
182
184
  var _a;
183
185
  const virtualConsole = new jsdom_1.VirtualConsole();
184
186
  const dom = new jsdom_1.JSDOM(htmlString, {
185
- resources: "usable",
186
- runScripts: "outside-only", // Prevents inline script execution in JSDOM
187
+ resources: 'usable',
188
+ runScripts: 'outside-only', // Prevents inline script execution in JSDOM
187
189
  pretendToBeVisual: true,
188
190
  virtualConsole,
189
191
  includeNodeLocations: true,
190
192
  });
191
193
  const document = dom.window.document;
192
194
  global.SVGElement = dom.window.SVGElement;
193
- const tempDiv = document.createElement("div");
195
+ const tempDiv = document.createElement('div');
194
196
  const elementsToRemove = document.querySelectorAll("script, style, link[rel='stylesheet'], meta, noscript, embed, object, param, source, svg");
195
197
  if (elementsToRemove) {
196
198
  elementsToRemove.forEach((tag) => {
@@ -198,46 +200,82 @@ const getElementsFromHTML = (name, desc, type, locators, isShared, projectId, pr
198
200
  });
199
201
  }
200
202
  tempDiv.innerHTML = document.body.innerHTML;
203
+ const finalLocatorsSet = new Set();
201
204
  let finalLocators = [];
205
+ function createLocator(base, overrides = {}) {
206
+ var _a, _b, _c, _d, _e, _f;
207
+ const newLocator = {
208
+ name: (_a = overrides.name) !== null && _a !== void 0 ? _a : base === null || base === void 0 ? void 0 : base.name,
209
+ type: (_b = overrides.type) !== null && _b !== void 0 ? _b : base === null || base === void 0 ? void 0 : base.type,
210
+ value: (_c = overrides.value) !== null && _c !== void 0 ? _c : base === null || base === void 0 ? void 0 : base.value,
211
+ reference: (_d = overrides.reference) !== null && _d !== void 0 ? _d : base === null || base === void 0 ? void 0 : base.reference,
212
+ status: (_e = overrides.status) !== null && _e !== void 0 ? _e : base === null || base === void 0 ? void 0 : base.status,
213
+ isRecorded: (_f = overrides.isRecorded) !== null && _f !== void 0 ? _f : base === null || base === void 0 ? void 0 : base.isRecorded,
214
+ };
215
+ if (overrides.hasOwnProperty('isSelfHealed')) {
216
+ newLocator.isSelfHealed = overrides.isSelfHealed;
217
+ }
218
+ else if (base === null || base === void 0 ? void 0 : base.hasOwnProperty('isSelfHealed')) {
219
+ newLocator.isSelfHealed = base.isSelfHealed;
220
+ }
221
+ pushUniqueLocator(newLocator);
222
+ }
223
+ function pushUniqueLocator(obj) {
224
+ const key = `${obj.name}:${obj.value}`;
225
+ if (!finalLocatorsSet.has(key)) {
226
+ finalLocatorsSet.add(key);
227
+ finalLocators.push(obj);
228
+ }
229
+ }
230
+ /** Locator Value Cleaner (Handles Special Scenarios) **/
231
+ const cleanLocatorValue = (val, type, isRecorded) => {
232
+ if (!val)
233
+ return null;
234
+ let cleaned = val.trim();
235
+ // Return null for empty or literal "null"
236
+ if (!cleaned || cleaned.toLowerCase() === 'null')
237
+ return null;
238
+ // Unescape any escaped quotes
239
+ cleaned = cleaned.replace(/\\"/g, '"').replace(/\\'/g, "'");
240
+ // Remove surrounding single or double quotes
241
+ cleaned = cleaned.replace(/^['"](.+?)['"]$/, '$1');
242
+ // Replace double single quotes with a single quote inside XPath
243
+ cleaned = cleaned.replace(/''/g, "'");
244
+ // Normalize double quotes in XPath attribute selectors [@id="" -> [@id='']
245
+ cleaned = cleaned.replace(/\[@(id|name)=['"]{2}(.+?)['"]{2}\]/g, "[@$1='$2']");
246
+ // For DOM selectors (id or name), remove ALL quotes
247
+ if (type === 'id' || type === 'name') {
248
+ cleaned = cleaned.replace(/['"]/g, '').trim();
249
+ }
250
+ if (type === 'xpath' && isRecorded === 'Y' && !val.startsWith('//'))
251
+ return null;
252
+ // Final check for empty strings
253
+ if (!cleaned || /^['"]{2}$/.test(cleaned))
254
+ return null;
255
+ return cleaned;
256
+ };
202
257
  locators: for (const locator of locators) {
203
258
  try {
204
- const isRecorded = String(locator.isRecorded || "");
205
- const recordedNLocators = locators.filter((l) => l.isRecorded === "N");
259
+ const isRecorded = String(locator.isRecorded || '');
260
+ const recordedNLocators = locators.filter((l) => l.isRecorded === 'N');
206
261
  if (recordedNLocators.length > 0) {
207
262
  for (const locator of recordedNLocators) {
208
- finalLocators.push({
209
- name: locator.name,
210
- type: locator.type,
211
- value: locator.value,
212
- reference: locator.reference,
213
- status: locator.status,
214
- isRecorded: locator.isRecorded,
215
- });
263
+ createLocator(locator);
216
264
  }
217
265
  }
218
- let relativeXpath = null;
219
- const isDynamic = String(locator.value || locator.type || "");
220
- if (isDynamic.includes("dynamic") ||
221
- isDynamic.match("dynamic") ||
222
- isDynamic.includes("{") ||
223
- isDynamic.includes("}")) {
224
- finalLocators.push({
225
- name: locator.name,
226
- type: locator.type,
227
- value: locator.value,
228
- reference: locator.reference,
229
- status: locator.status,
230
- isRecorded: locator.isRecorded,
231
- });
266
+ const isDynamic = String(locator.value || locator.type || '');
267
+ if (isDynamic.includes('dynamic') || isDynamic.match('dynamic') ||
268
+ isDynamic.includes('{') || isDynamic.includes('}')) {
269
+ createLocator(locator);
232
270
  continue;
233
271
  }
234
- if (isShared.includes("Y")) {
272
+ if (isShared.includes('Y')) {
235
273
  break locators;
236
274
  }
237
275
  for (const relation of relations) {
238
276
  try {
239
277
  let targetElement = null;
240
- if (locator.value.startsWith("iframe")) {
278
+ if (locator.value.startsWith('iframe')) {
241
279
  const iframe = tempDiv.querySelector(locator.value);
242
280
  if (iframe) {
243
281
  const iframeDocument = iframe.contentDocument || ((_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.document);
@@ -247,35 +285,26 @@ const getElementsFromHTML = (name, desc, type, locators, isShared, projectId, pr
247
285
  }
248
286
  }
249
287
  else {
250
- const selectors = locator.value.split(">>>"); // Custom delimiter for shadow DOM
288
+ const selectors = locator.value.split('>>>'); // Custom delimiter for shadow DOM
251
289
  let currentElement = tempDiv;
252
290
  for (const selector of selectors) {
253
291
  if (currentElement) {
254
292
  const trimmedSelector = selector.trim();
255
- if (locator.name.includes("id") ||
256
- trimmedSelector.startsWith("#")) {
257
- targetElement = currentElement.querySelector("#" + trimmedSelector);
293
+ if (locator.name.includes('id') || trimmedSelector.startsWith('#')) {
294
+ targetElement = currentElement.querySelector('#' + trimmedSelector);
258
295
  }
259
- else if (locator.name.includes("className") ||
260
- trimmedSelector.startsWith(".")) {
261
- targetElement = currentElement.querySelector("." + trimmedSelector);
296
+ else if (locator.name.includes('className') || trimmedSelector.startsWith('.')) {
297
+ targetElement = currentElement.querySelector('.' + trimmedSelector);
262
298
  }
263
- else if ((locator.name.includes("xpath") ||
264
- trimmedSelector.startsWith("//")) &&
265
- !locator.type.match("dynamic")) {
299
+ else if ((locator.name.includes('xpath') || trimmedSelector.startsWith('//')) &&
300
+ !locator.type.match('dynamic')) {
266
301
  if (tempDiv.innerHTML) {
267
302
  const normalizedXPath = normalizeXPath(trimmedSelector);
268
303
  targetElement = getElementFromXPath(tempDiv, normalizedXPath);
269
304
  if (targetElement) {
270
- finalLocators.push({
271
- name: locator.name,
272
- type: locator.type,
305
+ createLocator(locator, {
273
306
  value: trimmedSelector,
274
- reference: locator.reference,
275
- status: locator.status,
276
- isRecorded: String(locator.isRecorded).includes("N")
277
- ? "N"
278
- : "Y",
307
+ isRecorded: String(locator.isRecorded).includes('N') ? 'N' : 'Y',
279
308
  });
280
309
  }
281
310
  }
@@ -286,186 +315,101 @@ const getElementsFromHTML = (name, desc, type, locators, isShared, projectId, pr
286
315
  targetElement = getElementFromShadowRoot(currentElement, trimmedSelector);
287
316
  }
288
317
  }
289
- if (!targetElement &&
290
- isSVGElement(currentElement) &&
291
- !locator.type.match("dynamic")) {
318
+ if (!targetElement && isSVGElement(currentElement) && !locator.type.match('dynamic')) {
292
319
  targetElement = currentElement.querySelector(trimmedSelector);
293
320
  }
294
321
  currentElement = targetElement;
295
322
  }
296
323
  else {
297
- console.error("Element not found at:", selector);
324
+ console.error('Element not found at:', selector);
298
325
  break;
299
326
  }
300
327
  }
301
328
  }
302
329
  const locatorExists = (name, value) => {
303
- return finalLocators.some((loc) => loc.name === name &&
304
- loc.value === value &&
305
- (!loc.value.includes("dynamic") ||
306
- !locator.type.match("dynamic")));
330
+ const key = `${name}:${value}`;
331
+ return finalLocatorsSet.has(key);
307
332
  };
308
333
  const xpathFunctions = [
309
- { name: "xpath", value: getXPathByText },
310
- { name: "xpath", value: getXPathById },
311
- { name: "xpath", value: getXPathByClass },
312
- { name: "xpath", value: getXPathAbsolute },
313
- { name: "xpath", value: getXpathByName },
314
- { name: "xpath", value: getXpathByPlaceholder },
315
- { name: "xpath", value: getXpathByType },
334
+ { name: 'xpath', value: getXPathByText },
335
+ { name: 'xpath', value: getXPathById },
336
+ { name: 'xpath', value: getXPathByClass },
337
+ { name: 'xpath', value: getXPathAbsolute },
338
+ { name: 'xpath', value: getXpathByName },
339
+ { name: 'xpath', value: getXpathByPlaceholder },
340
+ { name: 'xpath', value: getXpathByType },
316
341
  ];
317
342
  if (targetElement) {
318
343
  const idValue = getId(targetElement);
319
- if (idValue && !locatorExists("id", idValue)) {
344
+ if (idValue && !locatorExists('id', idValue)) {
320
345
  locators.forEach((loc) => {
321
- if (locator.value === idValue && locator.name === "id") {
322
- finalLocators.push({
323
- name: "id",
324
- type: locator.type,
325
- value: idValue,
326
- reference: locator.reference,
327
- status: locator.status,
328
- isRecorded: locator.isRecorded,
329
- });
330
- }
331
- else {
332
- finalLocators.push({
333
- name: "id",
334
- type: locator.type,
335
- value: idValue,
336
- reference: locator.reference,
337
- status: locator.status,
338
- isRecorded: "Y",
339
- isSelfHealed: "Y",
340
- });
341
- }
346
+ createLocator(loc, {
347
+ name: 'id',
348
+ value: idValue,
349
+ isRecorded: loc.value === idValue && loc.name === 'id' ? loc.isRecorded : 'Y',
350
+ isSelfHealed: loc.value === idValue && loc.name === 'id' ? loc.isSelfHealed : 'Y'
351
+ });
342
352
  });
343
353
  }
344
- if (getVisibleText(targetElement)) {
345
- const textValue = getVisibleText(targetElement);
354
+ const textValue = getVisibleText(targetElement);
355
+ if (textValue) {
346
356
  locators.forEach((loc) => {
347
- if (loc.value === textValue) {
348
- finalLocators.push({
349
- name: "linkText",
350
- type: "static",
351
- value: textValue,
352
- reference: locator.reference,
353
- status: locator.status,
354
- isRecorded: locator.isRecorded,
355
- });
356
- }
357
- else {
358
- finalLocators.push({
359
- name: "linkText",
360
- type: "static",
361
- value: textValue,
362
- reference: locator.reference,
363
- status: locator.status,
364
- isRecorded: "Y",
365
- isSelfHealed: "Y",
366
- });
367
- }
357
+ createLocator(loc, {
358
+ name: 'linkText',
359
+ type: 'static',
360
+ value: textValue,
361
+ isRecorded: loc.value === textValue ? loc.isRecorded : 'Y',
362
+ isSelfHealed: loc.value === textValue ? loc.isSelfHealed : 'Y',
363
+ });
368
364
  });
369
365
  }
370
- if (getName(targetElement)) {
371
- const nameLocator = getName(targetElement);
372
- if (nameLocator && !locatorExists("name", nameLocator)) {
373
- locators.forEach((loc) => {
374
- if (loc.value === nameLocator && locator.name === "name") {
375
- finalLocators.push({
376
- name: "name",
377
- type: "static",
378
- value: nameLocator,
379
- reference: locator.reference,
380
- status: locator.status,
381
- isRecorded: locator.isRecorded,
382
- });
383
- }
384
- else {
385
- finalLocators.push({
386
- name: "name",
387
- type: "static",
388
- value: nameLocator,
389
- reference: locator.reference,
390
- status: locator.status,
391
- isRecorded: "Y",
392
- isSelfHealed: "Y",
393
- });
394
- }
366
+ const nameLocator = getName(targetElement);
367
+ if (nameLocator && !locatorExists('name', nameLocator)) {
368
+ locators.forEach((loc) => {
369
+ createLocator(loc, {
370
+ name: 'name',
371
+ type: 'static',
372
+ value: nameLocator,
373
+ isRecorded: loc.value === nameLocator && loc.name === 'name' ? loc.isRecorded : 'Y',
374
+ isSelfHealed: loc.value === nameLocator && loc.name === 'name' ? loc.isSelfHealed : 'Y',
395
375
  });
396
- }
376
+ });
397
377
  }
398
378
  const classValue = getClassName(targetElement);
399
- if (classValue &&
400
- classValue.trim() !== "" &&
401
- !locatorExists("className", classValue)) {
379
+ if (classValue && classValue.trim() !== '' && !locatorExists('className', classValue)) {
402
380
  locators.forEach((loc) => {
403
- if (loc.value === classValue && locator.name === "className") {
404
- finalLocators.push({
405
- name: "className",
406
- type: locator.type,
407
- value: classValue,
408
- reference: locator.reference,
409
- status: locator.status,
410
- isRecorded: locator.isRecorded,
411
- });
412
- }
413
- else {
414
- finalLocators.push({
415
- name: "className",
416
- type: locator.type,
417
- value: classValue,
418
- reference: locator.reference,
419
- status: locator.status,
420
- isRecorded: "Y",
421
- isSelfHealed: "Y",
422
- });
423
- }
381
+ createLocator(loc, {
382
+ name: 'className',
383
+ value: classValue,
384
+ isRecorded: loc.value === classValue && loc.name === 'className' ? loc.isRecorded : 'Y',
385
+ isSelfHealed: loc.value === classValue && loc.name === 'className' ? loc.isSelfHealed : 'Y',
386
+ });
424
387
  });
425
388
  }
426
389
  xpathFunctions.forEach((fn) => {
390
+ const fnValue = fn.value(targetElement);
427
391
  locators.forEach((loc) => {
428
- if (loc.value === fn.value(targetElement)) {
429
- finalLocators.push({
430
- name: fn.name,
431
- type: locator.type,
432
- value: fn.value(targetElement),
433
- reference: locator.reference,
434
- status: locator.status,
435
- isRecorded: locator.isRecorded,
436
- });
437
- }
438
- else {
439
- finalLocators.push({
440
- name: fn.name,
441
- type: locator.type,
442
- value: fn.value(targetElement),
443
- reference: locator.reference,
444
- status: locator.status,
445
- isRecorded: "Y",
446
- isSelfHealed: "Y",
447
- });
448
- }
392
+ createLocator(loc, {
393
+ name: fn.name,
394
+ value: fnValue,
395
+ isRecorded: loc.value === fnValue ? loc.isRecorded : 'Y',
396
+ isSelfHealed: loc.value === fnValue ? loc.isSelfHealed : 'Y',
397
+ });
449
398
  });
450
399
  });
451
400
  for (const locator of locators) {
452
401
  try {
453
- let relativeXpath = null;
454
- if (locator.value) {
402
+ for (const loc of locators) {
403
+ if (!loc.value)
404
+ continue;
455
405
  for (const relation of relations) {
456
- if (locator.value.includes(relation)) {
457
- relativeXpath = checkReferenceElementIsValid(locator.value, relation, tempDiv);
406
+ if (loc.value.includes(relation)) {
407
+ const relativeXpath = checkReferenceElementIsValid(loc.value, relation, tempDiv);
458
408
  if (relativeXpath) {
459
- finalLocators.push({
460
- name: "xpath",
461
- type: locator.type,
409
+ createLocator(loc, {
410
+ name: 'xpath',
462
411
  value: relativeXpath,
463
- reference: locator.reference,
464
- status: locator.status,
465
- isRecorded: locator.isRecorded !== "" ||
466
- locator.isRecorded !== null
467
- ? locator.isRecorded
468
- : "Y",
412
+ isRecorded: locator.isRecorded !== '' && locator.isRecorded !== null ? locator.isRecorded : 'Y',
469
413
  });
470
414
  break;
471
415
  }
@@ -474,32 +418,25 @@ const getElementsFromHTML = (name, desc, type, locators, isShared, projectId, pr
474
418
  }
475
419
  }
476
420
  catch (error) {
477
- console.error("Error processing locator:", locator, error);
421
+ console.error('Error processing locator:', locator, error);
478
422
  }
479
423
  }
480
- let uniqueLocators = new Map();
481
- finalLocators.forEach((obj) => {
482
- let val = obj.value;
483
- if (obj.isRecorded === 'Y' && val) {
484
- val = String(val).replace(/"/g, "'").replace(/\\'/g, "'");
424
+ const finalAutoHealedLocators = finalLocators.map((obj) => ({
425
+ ...obj,
426
+ value: cleanLocatorValue(obj.value, obj.name, obj.isRecorded),
427
+ }));
428
+ const finalUniqueAutoHealedLocators = finalAutoHealedLocators.reduce((unique, locator) => {
429
+ if (locator.value && !unique.some((l) => l.value === locator.value)) {
430
+ unique.push(locator);
485
431
  }
486
- // filter out redundant/invalid values
487
- if (!val || val === 'null' || val === "''")
488
- return;
489
- // ensure unique entries
490
- if (!uniqueLocators.has(val)) {
491
- uniqueLocators.set(val, { ...obj, value: val });
492
- }
493
- });
494
- let ffLoc = Array.from(uniqueLocators.values());
432
+ return unique;
433
+ }, []);
495
434
  const jsonResult = [
496
435
  {
497
436
  name: `${name}`,
498
437
  desc: `${desc}`,
499
438
  type: `${type}`,
500
- locators: ffLoc.filter((locator) => locator.hasOwnProperty("value") &&
501
- (locator === null || locator === void 0 ? void 0 : locator.value) !== null &&
502
- locator.value !== ""),
439
+ locators: finalUniqueAutoHealedLocators.filter((locator) => (locator === null || locator === void 0 ? void 0 : locator.value) != null && locator.value !== ''),
503
440
  isShared: `${isShared}`,
504
441
  projectId: `${projectId}`,
505
442
  projectType: `${projectType}`,
@@ -517,13 +454,13 @@ const getElementsFromHTML = (name, desc, type, locators, isShared, projectId, pr
517
454
  }
518
455
  }
519
456
  catch (error) {
520
- console.error("Error processing locator:", locator, error);
457
+ console.error('Error processing locator:', locator, error);
521
458
  continue;
522
459
  }
523
460
  }
524
461
  }
525
462
  catch (error) {
526
- console.error("Error processing locator:", locator, error);
463
+ console.error('Error processing locator:', locator, error);
527
464
  continue;
528
465
  }
529
466
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ff-dom",
3
- "version": "1.0.16",
3
+ "version": "1.0.17",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",