ff-dom 1.0.15 → 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.
- package/dist/utils/getElementsFromHTML.js +174 -242
- package/package.json +1 -1
- package/src/utils/getElementsFromHTML.ts +204 -298
|
@@ -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(
|
|
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 !==
|
|
18
|
-
typeof SVGElement !==
|
|
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(
|
|
66
|
-
const attrValue = elementEl.getAttribute(
|
|
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(
|
|
75
|
-
const attrValue = elementEl.getAttribute(
|
|
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(
|
|
85
|
-
const attrValue = elementEl.getAttribute(
|
|
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(
|
|
95
|
-
const attrValue = elementEl.getAttribute(
|
|
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
|
|
118
|
+
return '//' + path.join('/');
|
|
120
119
|
};
|
|
121
120
|
const relations = [
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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:
|
|
186
|
-
runScripts:
|
|
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(
|
|
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 ===
|
|
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
|
-
|
|
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
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
256
|
-
|
|
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(
|
|
260
|
-
|
|
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(
|
|
264
|
-
|
|
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
|
-
|
|
271
|
-
name: locator.name,
|
|
272
|
-
type: locator.type,
|
|
305
|
+
createLocator(locator, {
|
|
273
306
|
value: trimmedSelector,
|
|
274
|
-
|
|
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(
|
|
324
|
+
console.error('Element not found at:', selector);
|
|
298
325
|
break;
|
|
299
326
|
}
|
|
300
327
|
}
|
|
301
328
|
}
|
|
302
329
|
const locatorExists = (name, value) => {
|
|
303
|
-
|
|
304
|
-
|
|
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:
|
|
310
|
-
{ name:
|
|
311
|
-
{ name:
|
|
312
|
-
{ name:
|
|
313
|
-
{ name:
|
|
314
|
-
{ name:
|
|
315
|
-
{ name:
|
|
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(
|
|
344
|
+
if (idValue && !locatorExists('id', idValue)) {
|
|
320
345
|
locators.forEach((loc) => {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
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
|
-
|
|
345
|
-
|
|
354
|
+
const textValue = getVisibleText(targetElement);
|
|
355
|
+
if (textValue) {
|
|
346
356
|
locators.forEach((loc) => {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
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
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
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
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
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
|
-
|
|
454
|
-
|
|
402
|
+
for (const loc of locators) {
|
|
403
|
+
if (!loc.value)
|
|
404
|
+
continue;
|
|
455
405
|
for (const relation of relations) {
|
|
456
|
-
if (
|
|
457
|
-
relativeXpath = checkReferenceElementIsValid(
|
|
406
|
+
if (loc.value.includes(relation)) {
|
|
407
|
+
const relativeXpath = checkReferenceElementIsValid(loc.value, relation, tempDiv);
|
|
458
408
|
if (relativeXpath) {
|
|
459
|
-
|
|
460
|
-
name:
|
|
461
|
-
type: locator.type,
|
|
409
|
+
createLocator(loc, {
|
|
410
|
+
name: 'xpath',
|
|
462
411
|
value: relativeXpath,
|
|
463
|
-
|
|
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,37 +418,25 @@ const getElementsFromHTML = (name, desc, type, locators, isShared, projectId, pr
|
|
|
474
418
|
}
|
|
475
419
|
}
|
|
476
420
|
catch (error) {
|
|
477
|
-
console.error(
|
|
421
|
+
console.error('Error processing locator:', locator, error);
|
|
478
422
|
}
|
|
479
423
|
}
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
}
|
|
488
|
-
});
|
|
489
|
-
finalLocators.forEach((loc) => {
|
|
490
|
-
if (loc.isRecorded === "N") {
|
|
491
|
-
uniqueLocators.set(loc.value, loc);
|
|
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);
|
|
492
431
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
uniqueLocators.set(loc.value, loc);
|
|
496
|
-
}
|
|
497
|
-
}
|
|
498
|
-
});
|
|
499
|
-
let ffLoc = Array.from(uniqueLocators.values());
|
|
432
|
+
return unique;
|
|
433
|
+
}, []);
|
|
500
434
|
const jsonResult = [
|
|
501
435
|
{
|
|
502
436
|
name: `${name}`,
|
|
503
437
|
desc: `${desc}`,
|
|
504
438
|
type: `${type}`,
|
|
505
|
-
locators:
|
|
506
|
-
(locator === null || locator === void 0 ? void 0 : locator.value) !== null &&
|
|
507
|
-
locator.value !== ""),
|
|
439
|
+
locators: finalUniqueAutoHealedLocators.filter((locator) => (locator === null || locator === void 0 ? void 0 : locator.value) != null && locator.value !== ''),
|
|
508
440
|
isShared: `${isShared}`,
|
|
509
441
|
projectId: `${projectId}`,
|
|
510
442
|
projectType: `${projectType}`,
|
|
@@ -522,13 +454,13 @@ const getElementsFromHTML = (name, desc, type, locators, isShared, projectId, pr
|
|
|
522
454
|
}
|
|
523
455
|
}
|
|
524
456
|
catch (error) {
|
|
525
|
-
console.error(
|
|
457
|
+
console.error('Error processing locator:', locator, error);
|
|
526
458
|
continue;
|
|
527
459
|
}
|
|
528
460
|
}
|
|
529
461
|
}
|
|
530
462
|
catch (error) {
|
|
531
|
-
console.error(
|
|
463
|
+
console.error('Error processing locator:', locator, error);
|
|
532
464
|
continue;
|
|
533
465
|
}
|
|
534
466
|
}
|