ff-dom 1.0.16 → 1.0.18-beta.1

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.
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "emitDeclarationOnly": true,
5
+ "declaration": true,
6
+ "declarationDir": "dist/types/browser",
7
+ "outDir": "dist/browser",
8
+ },
9
+ "exclude": ["src/node"],
10
+ "include": ["src/browser"]
11
+ }
package/tsconfig.json CHANGED
@@ -1,14 +1,18 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2019", // or "es2020", "es2021", etc.
4
- "lib": ["dom", "es2019"],
5
- "module": "commonjs",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true,
9
- "forceConsistentCasingInFileNames": true,
10
- "outDir": "./dist"
11
- },
12
- "include": ["**/*.ts"],
13
- "exclude": ["node_modules"]
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "lib": ["dom", "ESNext"],
5
+ "moduleResolution": "bundler",
6
+ "module": "esnext",
7
+ "allowImportingTsExtensions": true,
8
+ "noEmit": false,
9
+
10
+ "strict": true,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "allowSyntheticDefaultImports": true,
15
+ },
16
+ "include": ["**/*.ts", "test/node.spec.js"],
17
+ "exclude": ["node_modules", "rollup.config.js"]
14
18
  }
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "emitDeclarationOnly": true,
5
+ "declaration": true,
6
+ "outDir": "dist/node",
7
+ "declarationDir": "dist/types/node"
8
+ },
9
+ "exclude": ["src/browser"],
10
+ "include": ["src/node", "src/node/xpath.d.ts"]
11
+ }
package/dist/index.js DELETED
@@ -1,5 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getElementsFromHTML = void 0;
4
- const getElementsFromHTML_1 = require("./utils/getElementsFromHTML");
5
- Object.defineProperty(exports, "getElementsFromHTML", { enumerable: true, get: function () { return getElementsFromHTML_1.getElementsFromHTML; } });
@@ -1,532 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getElementsFromHTML = void 0;
4
- //@ts-nocheck
5
- const jsdom_1 = require("jsdom");
6
- const isUnique = (xpathResult) => {
7
- return xpathResult && xpathResult.snapshotLength === 1;
8
- };
9
- const getElementFromShadowRoot = (element, selector) => {
10
- const shadowRoot = element.shadowRoot;
11
- if (shadowRoot && !selector.includes("dynamic")) {
12
- return shadowRoot.querySelector(selector);
13
- }
14
- return null;
15
- };
16
- const isSVGElement = (element) => {
17
- return (typeof window !== "undefined" &&
18
- typeof SVGElement !== "undefined" &&
19
- element instanceof SVGElement);
20
- };
21
- const getXPath = (element, xpath) => {
22
- const window = element.ownerDocument.defaultView;
23
- if (!window)
24
- return null;
25
- const xpathEvaluator = new window.XPathEvaluator();
26
- const xpathResult = xpathEvaluator.evaluate(xpath, element.ownerDocument, null, window.XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
27
- return isUnique(xpathResult) ? xpath : null;
28
- };
29
- const getId = (element) => {
30
- return (element === null || element === void 0 ? void 0 : element.id) || null;
31
- };
32
- const getClassName = (element) => {
33
- return element.className || null;
34
- };
35
- const getVisibleText = (element) => {
36
- var _a;
37
- return ((_a = element.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || null;
38
- };
39
- const getXPathByText = (element) => {
40
- const text = getVisibleText(element);
41
- if (text) {
42
- const xpath = `//${element.nodeName.toLowerCase()}[text()='${text}']`;
43
- return getXPath(element, xpath);
44
- }
45
- return null;
46
- };
47
- const getXPathById = (element) => {
48
- if (element.id) {
49
- const xpath = `//${element.nodeName.toLowerCase()}[@id='${element.id}']`;
50
- return getXPath(element, xpath);
51
- }
52
- return null;
53
- };
54
- const getXPathByClass = (element) => {
55
- const classNames = element.classList;
56
- if (classNames.length > 0) {
57
- const xpath = `//${element.nodeName.toLowerCase()}[@class='${classNames[0]}']`;
58
- return getXPath(element, xpath);
59
- }
60
- return null;
61
- };
62
- const getXpathByName = (element) => {
63
- const selector = element.nodeName.toLowerCase();
64
- const elementEl = element;
65
- if (elementEl.hasAttribute("name")) {
66
- const attrValue = elementEl.getAttribute("name");
67
- const xpath = `//${selector}[@name='${attrValue}']`;
68
- return getXPath(element, xpath) || null;
69
- }
70
- return null;
71
- };
72
- const getName = (element) => {
73
- const elementEl = element;
74
- if (elementEl.hasAttribute("name")) {
75
- const attrValue = elementEl.getAttribute("name");
76
- const name = `${attrValue}`;
77
- return name || null;
78
- }
79
- return null;
80
- };
81
- const getXpathByPlaceholder = (element) => {
82
- const selector = element.nodeName.toLowerCase();
83
- const elementEl = element;
84
- if (elementEl.hasAttribute("placeholder")) {
85
- const attrValue = elementEl.getAttribute("placeholder");
86
- const xpath = `//${selector}[@placeholder='${attrValue}']`;
87
- return getXPath(element, xpath) || null;
88
- }
89
- return null;
90
- };
91
- const getXpathByType = (element) => {
92
- const selector = element.nodeName.toLowerCase();
93
- const elementEl = element;
94
- if (elementEl.hasAttribute("type")) {
95
- const attrValue = elementEl.getAttribute("type");
96
- const xpath = `//${selector}[@type='${attrValue}']`;
97
- return getXPath(element, xpath) || null;
98
- }
99
- return null;
100
- };
101
- const getXPathAbsolute = (element) => {
102
- const path = [];
103
- while (element && element.nodeType === 1) {
104
- // ELEMENT_NODE
105
- let selector = element.nodeName.toLowerCase();
106
- let sibling = element;
107
- let siblingCount = 1;
108
- while ((sibling = sibling.previousElementSibling)) {
109
- if (sibling.nodeName.toLowerCase() === element.nodeName.toLowerCase()) {
110
- siblingCount++;
111
- }
112
- }
113
- if (siblingCount > 1) {
114
- selector += `[${siblingCount}]`;
115
- }
116
- path.unshift(selector);
117
- element = element.parentNode;
118
- }
119
- return "//" + path.join("/");
120
- };
121
- 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",
132
- ];
133
- const normalizeXPath = (xpath) => {
134
- xpath = xpath.replace(/text\(\)\s*=\s*['"](.+?)['"]/g, "normalize-space(.)='$1'");
135
- xpath = xpath.replace(/\.\s*=\s*['"](.+?)['"]/g, "normalize-space(.)='$1'");
136
- return xpath;
137
- };
138
- function getElementFromXPath(tempDiv, xpath) {
139
- let currentElement = tempDiv;
140
- const window = currentElement.ownerDocument.defaultView;
141
- if (!window)
142
- return null;
143
- const xpathEvaluator = new window.XPathEvaluator();
144
- const xpathResult = xpathEvaluator.evaluate(xpath, currentElement.ownerDocument, //here even tempDiv can be passed
145
- null, window.XPathResult.FIRST_ORDERED_NODE_TYPE, null);
146
- return xpathResult.singleNodeValue;
147
- }
148
- function checkReferenceElementIsValid(locator, relation, tempDiv) {
149
- if (locator.includes(relation)) {
150
- const locatotSplitArray = locator.split(relation);
151
- const sourceLoc = locatotSplitArray[0].trim();
152
- let currentElement = tempDiv;
153
- const window = currentElement.ownerDocument.defaultView;
154
- if (!window)
155
- return null;
156
- if (!locator.includes("dynamic")) {
157
- const xpathEvaluator = new window.XPathEvaluator();
158
- const xpathResult = xpathEvaluator.evaluate(sourceLoc, currentElement.ownerDocument, null, window.XPathResult.FIRST_ORDERED_NODE_TYPE, null);
159
- const sourceElement = xpathResult.singleNodeValue;
160
- if (sourceElement) {
161
- const xpathResultComplete = xpathEvaluator.evaluate(locator, currentElement.ownerDocument, null, window.XPathResult.FIRST_ORDERED_NODE_TYPE, null);
162
- const completeElement = xpathResultComplete.singleNodeValue;
163
- let relativeXpath;
164
- if (completeElement) {
165
- relativeXpath = locator;
166
- return relativeXpath;
167
- }
168
- else {
169
- console.error("Complete Locator is Invalid:", locator);
170
- relativeXpath = locator;
171
- return relativeXpath;
172
- }
173
- }
174
- else {
175
- console.error("Source Locator Not Found:", sourceLoc);
176
- }
177
- }
178
- }
179
- return null;
180
- }
181
- const getElementsFromHTML = (name, desc, type, locators, isShared, projectId, projectType, isRecorded, folder, parentId, parentName, platform, licenseId, licenseType, userId, htmlString) => {
182
- var _a;
183
- const virtualConsole = new jsdom_1.VirtualConsole();
184
- const dom = new jsdom_1.JSDOM(htmlString, {
185
- resources: "usable",
186
- runScripts: "outside-only", // Prevents inline script execution in JSDOM
187
- pretendToBeVisual: true,
188
- virtualConsole,
189
- includeNodeLocations: true,
190
- });
191
- const document = dom.window.document;
192
- global.SVGElement = dom.window.SVGElement;
193
- const tempDiv = document.createElement("div");
194
- const elementsToRemove = document.querySelectorAll("script, style, link[rel='stylesheet'], meta, noscript, embed, object, param, source, svg");
195
- if (elementsToRemove) {
196
- elementsToRemove.forEach((tag) => {
197
- tag.remove();
198
- });
199
- }
200
- tempDiv.innerHTML = document.body.innerHTML;
201
- let finalLocators = [];
202
- locators: for (const locator of locators) {
203
- try {
204
- const isRecorded = String(locator.isRecorded || "");
205
- const recordedNLocators = locators.filter((l) => l.isRecorded === "N");
206
- if (recordedNLocators.length > 0) {
207
- 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
- });
216
- }
217
- }
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
- });
232
- continue;
233
- }
234
- if (isShared.includes("Y")) {
235
- break locators;
236
- }
237
- for (const relation of relations) {
238
- try {
239
- let targetElement = null;
240
- if (locator.value.startsWith("iframe")) {
241
- const iframe = tempDiv.querySelector(locator.value);
242
- if (iframe) {
243
- const iframeDocument = iframe.contentDocument || ((_a = iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.document);
244
- if (iframeDocument) {
245
- targetElement = iframeDocument.querySelector(locator.value.slice(6));
246
- }
247
- }
248
- }
249
- else {
250
- const selectors = locator.value.split(">>>"); // Custom delimiter for shadow DOM
251
- let currentElement = tempDiv;
252
- for (const selector of selectors) {
253
- if (currentElement) {
254
- const trimmedSelector = selector.trim();
255
- if (locator.name.includes("id") ||
256
- trimmedSelector.startsWith("#")) {
257
- targetElement = currentElement.querySelector("#" + trimmedSelector);
258
- }
259
- else if (locator.name.includes("className") ||
260
- trimmedSelector.startsWith(".")) {
261
- targetElement = currentElement.querySelector("." + trimmedSelector);
262
- }
263
- else if ((locator.name.includes("xpath") ||
264
- trimmedSelector.startsWith("//")) &&
265
- !locator.type.match("dynamic")) {
266
- if (tempDiv.innerHTML) {
267
- const normalizedXPath = normalizeXPath(trimmedSelector);
268
- targetElement = getElementFromXPath(tempDiv, normalizedXPath);
269
- if (targetElement) {
270
- finalLocators.push({
271
- name: locator.name,
272
- type: locator.type,
273
- value: trimmedSelector,
274
- reference: locator.reference,
275
- status: locator.status,
276
- isRecorded: String(locator.isRecorded).includes("N")
277
- ? "N"
278
- : "Y",
279
- });
280
- }
281
- }
282
- }
283
- else {
284
- targetElement = currentElement.querySelector(trimmedSelector);
285
- if (!targetElement) {
286
- targetElement = getElementFromShadowRoot(currentElement, trimmedSelector);
287
- }
288
- }
289
- if (!targetElement &&
290
- isSVGElement(currentElement) &&
291
- !locator.type.match("dynamic")) {
292
- targetElement = currentElement.querySelector(trimmedSelector);
293
- }
294
- currentElement = targetElement;
295
- }
296
- else {
297
- console.error("Element not found at:", selector);
298
- break;
299
- }
300
- }
301
- }
302
- 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")));
307
- };
308
- 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 },
316
- ];
317
- if (targetElement) {
318
- const idValue = getId(targetElement);
319
- if (idValue && !locatorExists("id", idValue)) {
320
- 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
- }
342
- });
343
- }
344
- if (getVisibleText(targetElement)) {
345
- const textValue = getVisibleText(targetElement);
346
- 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
- }
368
- });
369
- }
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
- }
395
- });
396
- }
397
- }
398
- const classValue = getClassName(targetElement);
399
- if (classValue &&
400
- classValue.trim() !== "" &&
401
- !locatorExists("className", classValue)) {
402
- 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
- }
424
- });
425
- }
426
- xpathFunctions.forEach((fn) => {
427
- 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
- }
449
- });
450
- });
451
- for (const locator of locators) {
452
- try {
453
- let relativeXpath = null;
454
- if (locator.value) {
455
- for (const relation of relations) {
456
- if (locator.value.includes(relation)) {
457
- relativeXpath = checkReferenceElementIsValid(locator.value, relation, tempDiv);
458
- if (relativeXpath) {
459
- finalLocators.push({
460
- name: "xpath",
461
- type: locator.type,
462
- value: relativeXpath,
463
- reference: locator.reference,
464
- status: locator.status,
465
- isRecorded: locator.isRecorded !== "" ||
466
- locator.isRecorded !== null
467
- ? locator.isRecorded
468
- : "Y",
469
- });
470
- break;
471
- }
472
- }
473
- }
474
- }
475
- }
476
- catch (error) {
477
- console.error("Error processing locator:", locator, error);
478
- }
479
- }
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, "'");
485
- }
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());
495
- const jsonResult = [
496
- {
497
- name: `${name}`,
498
- desc: `${desc}`,
499
- type: `${type}`,
500
- locators: ffLoc.filter((locator) => locator.hasOwnProperty("value") &&
501
- (locator === null || locator === void 0 ? void 0 : locator.value) !== null &&
502
- locator.value !== ""),
503
- isShared: `${isShared}`,
504
- projectId: `${projectId}`,
505
- projectType: `${projectType}`,
506
- isRecorded: `${isRecorded}`,
507
- folder: `${folder}`,
508
- parentId: `${parentId}`,
509
- parentName: `${parentName}`,
510
- platform: `${platform}`,
511
- licenseId: `${licenseId}`,
512
- licenseType: `${licenseType}`,
513
- userId: `${userId}`,
514
- },
515
- ];
516
- return jsonResult;
517
- }
518
- }
519
- catch (error) {
520
- console.error("Error processing locator:", locator, error);
521
- continue;
522
- }
523
- }
524
- }
525
- catch (error) {
526
- console.error("Error processing locator:", locator, error);
527
- continue;
528
- }
529
- }
530
- return null;
531
- };
532
- exports.getElementsFromHTML = getElementsFromHTML;
package/src/index.ts DELETED
@@ -1,3 +0,0 @@
1
- import { getElementsFromHTML } from "./utils/getElementsFromHTML";
2
-
3
- export { getElementsFromHTML };