ff-automationv2 2.2.29 → 2.2.31
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/ai/llmcalls/llmAction.js +49 -13
- package/dist/ai/llmcalls/parseLlmOputput.js +5 -5
- package/dist/ai/llmprompts/promptRegistry.js +4 -0
- package/dist/ai/llmprompts/systemPrompts/actionExtractorPrompt.js +39 -11
- package/dist/ai/llmprompts/systemPrompts/fireflinkElementIndexExtactors.js +6 -6
- package/dist/ai/llmprompts/systemPrompts/fireflinkElementIndexExtractor_Mob.js +32 -0
- package/dist/ai/llmprompts/systemPrompts/mobileKeywordExtractor.js +17 -8
- package/dist/ai/llmprompts/systemPrompts/otpExtractorPrompt.d.ts +1 -0
- package/dist/ai/llmprompts/systemPrompts/otpExtractorPrompt.js +22 -0
- package/dist/ai/llmprompts/systemPrompts/verifyActionExtractorPrompt.js +3 -1
- package/dist/ai/llmprompts/systemPrompts/visionPrompt.js +14 -6
- package/dist/ai/llmprompts/systemPrompts/visionPromptMobile.js +3 -0
- package/dist/ai/llmprompts/userPrompts/userPrompt.js +1 -0
- package/dist/automation/actions/executor.d.ts +4 -0
- package/dist/automation/actions/executor.js +20 -0
- package/dist/automation/actions/interaction/clear/clear.js +1 -1
- package/dist/automation/actions/interaction/clear/clearAndEnter.js +1 -1
- package/dist/automation/actions/interaction/click/click.js +10 -3
- package/dist/automation/actions/interaction/click/clickNtimes.js +1 -1
- package/dist/automation/actions/interaction/click/doubleClick.js +1 -1
- package/dist/automation/actions/interaction/click/rightClick.js +1 -1
- package/dist/automation/actions/interaction/elementlessActions/getOtp.d.ts +2 -0
- package/dist/automation/actions/interaction/elementlessActions/getOtp.js +101 -0
- package/dist/automation/actions/interaction/enterActions/enterInput.js +11 -6
- package/dist/automation/actions/interaction/enterActions/enterInputAndPress.js +2 -2
- package/dist/automation/actions/interaction/enterActions/enterusingJs.js +2 -2
- package/dist/automation/actions/interaction/enterActions/waitAndEnter.js +1 -1
- package/dist/automation/actions/interaction/upload/uploadfile.js +1 -1
- package/dist/automation/actions/interaction/verify/VerifyNavigateURL.js +38 -11
- package/dist/automation/actions/interface/FireFlinkResponseSchema.d.ts +12 -0
- package/dist/automation/actions/interface/FireFlinkResponseSchema.js +19 -3
- package/dist/automation/actions/interface/getOtpInterface.d.ts +15 -0
- package/dist/automation/actions/interface/getOtpInterface.js +2 -0
- package/dist/automation/browserSession/initiateBrowserSession.d.ts +1 -1
- package/dist/automation/browserSession/initiateBrowserSession.js +15 -7
- package/dist/automation/driverState.d.ts +2 -0
- package/dist/automation/driverState.js +4 -0
- package/dist/automation/mobileSession/initiateMobileSession.d.ts +4 -5
- package/dist/automation/mobileSession/initiateMobileSession.js +83 -19
- package/dist/core/constants/allAction.js +5 -1
- package/dist/core/constants/supportedActions.js +5 -2
- package/dist/core/interfaces/actionInterface.d.ts +4 -0
- package/dist/core/interfaces/browserConfigurationInterface.d.ts +0 -4
- package/dist/core/interfaces/browserConfigurationInterface.js +0 -1
- package/dist/core/interfaces/executionDetails.d.ts +6 -3
- package/dist/core/interfaces/fireflinkScriptPayloadInterface.d.ts +22 -16
- package/dist/core/interfaces/llmResponseInterface.d.ts +1 -0
- package/dist/core/interfaces/promptInterface.d.ts +1 -0
- package/dist/core/main/SessionManager.d.ts +9 -0
- package/dist/core/main/SessionManager.js +23 -0
- package/dist/core/main/actionHandlerFactory.js +261 -64
- package/dist/core/main/executionContext.d.ts +7 -1
- package/dist/core/main/executionContext.js +94 -3
- package/dist/core/main/runAutomationScript.d.ts +1 -0
- package/dist/core/main/runAutomationScript.js +912 -304
- package/dist/core/main/stepProcessor.js +7 -2
- package/dist/core/types/browserType.d.ts +3 -3
- package/dist/core/types/browserType.js +2 -2
- package/dist/core/types/messageType.d.ts +13 -0
- package/dist/core/types/messageType.js +15 -0
- package/dist/core/types/promptType.d.ts +2 -1
- package/dist/core/types/promptType.js +1 -0
- package/dist/domAnalysis/searchBest.js +15 -16
- package/dist/domAnalysis/simplifyAndFlatten.js +6 -4
- package/dist/fireflinkData/fireflinkLocators/getListOfLocators.d.ts +3 -0
- package/dist/fireflinkData/fireflinkLocators/getListOfLocators.js +23 -15
- package/dist/fireflinkData/fireflinkLocators/parser/getElementsFromHTML.d.ts +3 -3
- package/dist/fireflinkData/fireflinkLocators/parser/getElementsFromHTML.js +134 -118
- package/dist/fireflinkData/fireflinkLocators/types/locator.d.ts +11 -1
- package/dist/fireflinkData/fireflinkLocators/utils/androidSelector.d.ts +1 -1
- package/dist/fireflinkData/fireflinkLocators/utils/androidSelector.js +35 -9
- package/dist/fireflinkData/fireflinkLocators/utils/cssSelector.d.ts +10 -10
- package/dist/fireflinkData/fireflinkLocators/utils/cssSelector.js +56 -59
- package/dist/fireflinkData/fireflinkLocators/utils/iosSelector.d.ts +1 -1
- package/dist/fireflinkData/fireflinkLocators/utils/iosSelector.js +53 -29
- package/dist/fireflinkData/fireflinkLocators/utils/referenceXpath.d.ts +8 -8
- package/dist/fireflinkData/fireflinkLocators/utils/referenceXpath.js +113 -190
- package/dist/fireflinkData/fireflinkLocators/utils/xpath.d.ts +17 -26
- package/dist/fireflinkData/fireflinkLocators/utils/xpath.js +314 -786
- package/dist/fireflinkData/fireflinkLocators/utils/xpathHelpers.d.ts +68 -70
- package/dist/fireflinkData/fireflinkLocators/utils/xpathHelpers.js +498 -517
- package/dist/fireflinkData/fireflinkScript/appendScriptData.d.ts +1 -1
- package/dist/fireflinkData/fireflinkScript/appendScriptData.js +5 -8
- package/dist/fireflinkData/fireflinkScript/scriptGenrationData.d.ts +2 -2
- package/dist/fireflinkData/fireflinkScript/scriptGenrationData.js +17 -27
- package/dist/llmConfig/llmConfiguration.js +13 -6
- package/dist/service/api/fireflinkApi.service.d.ts +1 -1
- package/dist/service/api/fireflinkApi.service.js +1 -2
- package/dist/service/fireflink.service.d.ts +3 -3
- package/dist/service/fireflink.service.js +23 -21
- package/dist/service/kafka/fireflinkKafka.service.d.ts +1 -2
- package/dist/service/kafka/fireflinkKafka.service.js +6 -4
- package/dist/tests/test12.d.ts +0 -1
- package/dist/tests/test12.js +65 -78
- package/dist/tests/testkaf.d.ts +0 -1
- package/dist/tests/testkaf.js +51 -55
- package/dist/utils/InstancesDetails/getInstancesInfo.js +1 -1
- package/dist/utils/browserCap/capability.d.ts +5 -1
- package/dist/utils/browserCap/capability.js +91 -15
- package/dist/utils/helpers/domSanitizer.d.ts +1 -0
- package/dist/utils/helpers/domSanitizer.js +9 -0
- package/dist/utils/helpers/enterActionHelper.js +23 -4
- package/dist/utils/helpers/xpathcreation.js +2 -2
- package/dist/utils/javascript/jsFindElement.d.ts +1 -1
- package/dist/utils/javascript/jsFindElement.js +2 -2
- package/dist/utils/newTab/newTabTracker.js +18 -6
- package/dist/utils/workers/LocatorWorkerInterface.d.ts +22 -0
- package/dist/utils/workers/LocatorWorkerInterface.js +2 -0
- package/dist/utils/workers/LocatorWorkerPool.d.ts +13 -0
- package/dist/utils/workers/LocatorWorkerPool.js +89 -0
- package/dist/utils/workers/locatorWorker.d.ts +1 -0
- package/dist/utils/workers/locatorWorker.js +44 -0
- package/package.json +3 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.getAiElementsFromHTML = void 0;
|
|
4
4
|
const xpath_js_1 = require("../utils/xpath.js");
|
|
5
5
|
const cssSelector_js_1 = require("../utils/cssSelector.js");
|
|
6
6
|
const jsdom_1 = require("jsdom");
|
|
7
7
|
const xpathHelpers_js_1 = require("../utils/xpathHelpers.js");
|
|
8
|
+
const domSanitizer_js_1 = require("../../../utils/helpers/domSanitizer.js");
|
|
8
9
|
const logData_js_1 = require("../../../utils/logger/logData.js");
|
|
9
|
-
const
|
|
10
|
+
const getAiElementFromCssSelector = (docmt, selector) => {
|
|
10
11
|
try {
|
|
11
12
|
const found = docmt.querySelector(selector);
|
|
12
13
|
if (found)
|
|
@@ -16,12 +17,12 @@ const getElementFromCssSelector = (docmt, selector) => {
|
|
|
16
17
|
console.error("Invalid CSS selector:", selector, error);
|
|
17
18
|
return null;
|
|
18
19
|
}
|
|
19
|
-
return
|
|
20
|
+
return getAiElementFromShadowRoot(docmt.body, selector);
|
|
20
21
|
};
|
|
21
|
-
const
|
|
22
|
+
const isAiCssSelectorLocator = (locator) => {
|
|
22
23
|
return locator.name?.toLowerCase().includes("cssselector") ?? false;
|
|
23
24
|
};
|
|
24
|
-
const
|
|
25
|
+
const getAiElementFromShadowRoot = (el, selector) => {
|
|
25
26
|
const shadowRoot = el.shadowRoot;
|
|
26
27
|
if (shadowRoot && !selector.includes("dynamic")) {
|
|
27
28
|
return shadowRoot.querySelector(selector);
|
|
@@ -32,7 +33,7 @@ const getElementFromShadowRoot = (el, selector) => {
|
|
|
32
33
|
if (elements[i].shadowRoot) {
|
|
33
34
|
const { shadowRoot } = elements[i];
|
|
34
35
|
if (shadowRoot) {
|
|
35
|
-
const nestedElement =
|
|
36
|
+
const nestedElement = getAiElementFromShadowRoot(shadowRoot, selector);
|
|
36
37
|
if (nestedElement) {
|
|
37
38
|
return nestedElement;
|
|
38
39
|
}
|
|
@@ -48,32 +49,32 @@ const getElementFromShadowRoot = (el, selector) => {
|
|
|
48
49
|
}
|
|
49
50
|
return null;
|
|
50
51
|
};
|
|
51
|
-
const
|
|
52
|
+
const getaiId = (element) => {
|
|
52
53
|
return element?.id || null;
|
|
53
54
|
};
|
|
54
|
-
const
|
|
55
|
-
|
|
55
|
+
const getAiClassName = (element) => {
|
|
56
|
+
const svgClass = element.ownerDocument?.defaultView?.SVGElement;
|
|
57
|
+
if (svgClass && element instanceof svgClass) {
|
|
56
58
|
const cls = element.className;
|
|
57
59
|
if (typeof cls === "string") {
|
|
58
60
|
return cls;
|
|
59
61
|
}
|
|
60
62
|
return cls?.baseVal || null;
|
|
61
63
|
}
|
|
62
|
-
return element.className || null;
|
|
64
|
+
return element.getAttribute("class") || element.className || null;
|
|
63
65
|
};
|
|
64
|
-
const
|
|
66
|
+
const getAiVisibleText = (element) => {
|
|
65
67
|
return element.textContent?.trim() || null;
|
|
66
68
|
};
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const attrValue = elementEl.getAttribute("name");
|
|
69
|
+
const getAiName = (element) => {
|
|
70
|
+
if (element.hasAttribute("name")) {
|
|
71
|
+
const attrValue = element.getAttribute("name");
|
|
71
72
|
const name = `${attrValue}`;
|
|
72
73
|
return name || null;
|
|
73
74
|
}
|
|
74
75
|
return null;
|
|
75
76
|
};
|
|
76
|
-
const
|
|
77
|
+
const aiRelations = [
|
|
77
78
|
"/preceding-sibling",
|
|
78
79
|
"/following-sibling",
|
|
79
80
|
"/parent",
|
|
@@ -85,26 +86,23 @@ const relations = [
|
|
|
85
86
|
"/preceding",
|
|
86
87
|
"/following"
|
|
87
88
|
];
|
|
88
|
-
function
|
|
89
|
+
function getAiElementFromXPath(docmt, xpath, platform) {
|
|
89
90
|
if (platform !== "web") {
|
|
90
91
|
const ffMatch = xpath.match(/^\/\/\*\[@FF-inspecter="([^"]+)"\]$/);
|
|
91
92
|
if (ffMatch) {
|
|
92
93
|
return docmt.querySelector(`[FF-inspecter="${ffMatch[1]}"]`);
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
|
-
return (0, xpathHelpers_js_1.
|
|
96
|
+
return (0, xpathHelpers_js_1.evaluateAiXPath)(xpath, docmt, "first").singleNodeValue;
|
|
96
97
|
}
|
|
97
|
-
function
|
|
98
|
+
function checkAiReferenceElementIsValid(locator, relation, docmt) {
|
|
98
99
|
if (locator.includes(relation)) {
|
|
99
100
|
const locatotSplitArray = locator.split(relation);
|
|
100
101
|
const sourceLoc = locatotSplitArray[0].trim();
|
|
101
|
-
const window = docmt.defaultView;
|
|
102
|
-
if (!window)
|
|
103
|
-
return null;
|
|
104
102
|
if (!locator.includes("dynamic")) {
|
|
105
|
-
const sourceElement = (0, xpathHelpers_js_1.
|
|
103
|
+
const sourceElement = (0, xpathHelpers_js_1.evaluateAiXPath)(sourceLoc, docmt, "first").singleNodeValue;
|
|
106
104
|
if (sourceElement) {
|
|
107
|
-
const completeElement = (0, xpathHelpers_js_1.
|
|
105
|
+
const completeElement = (0, xpathHelpers_js_1.evaluateAiXPath)(locator, docmt, "first").singleNodeValue;
|
|
108
106
|
let relativeXpath;
|
|
109
107
|
if (completeElement) {
|
|
110
108
|
relativeXpath = locator;
|
|
@@ -123,12 +121,12 @@ function checkReferenceElementIsValid(locator, relation, docmt) {
|
|
|
123
121
|
}
|
|
124
122
|
return null;
|
|
125
123
|
}
|
|
126
|
-
const
|
|
124
|
+
const getAiElementsFromHTML = (record, htmlString, platform) => {
|
|
127
125
|
const virtualConsole = new jsdom_1.VirtualConsole();
|
|
128
126
|
let docmt;
|
|
129
127
|
if (platform === "web") {
|
|
130
128
|
try {
|
|
131
|
-
const dom = new jsdom_1.JSDOM(htmlString, {
|
|
129
|
+
const dom = new jsdom_1.JSDOM((0, domSanitizer_js_1.sanitizeWebDom)(htmlString), {
|
|
132
130
|
resources: "usable",
|
|
133
131
|
runScripts: "outside-only",
|
|
134
132
|
pretendToBeVisual: true,
|
|
@@ -138,7 +136,7 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
138
136
|
docmt = dom.window.document;
|
|
139
137
|
}
|
|
140
138
|
catch (e) {
|
|
141
|
-
logData_js_1.logger.error("Unable to parse
|
|
139
|
+
logData_js_1.logger.error("Unable to parse HTML : ", e.message);
|
|
142
140
|
return null;
|
|
143
141
|
}
|
|
144
142
|
}
|
|
@@ -155,15 +153,15 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
155
153
|
return null;
|
|
156
154
|
}
|
|
157
155
|
}
|
|
158
|
-
const
|
|
159
|
-
if (
|
|
160
|
-
|
|
156
|
+
const aiElementsToRemove = docmt.querySelectorAll("script, style, link[rel='stylesheet'], meta, noscript, embed, object, param, source");
|
|
157
|
+
if (aiElementsToRemove) {
|
|
158
|
+
aiElementsToRemove.forEach((tag) => {
|
|
161
159
|
tag.remove();
|
|
162
160
|
});
|
|
163
161
|
}
|
|
164
|
-
const
|
|
162
|
+
const finalAiLocatorsSet = new Set();
|
|
165
163
|
const finalLocators = [];
|
|
166
|
-
function
|
|
164
|
+
function createAiLocator(base, overrides = {}) {
|
|
167
165
|
const newLocator = {
|
|
168
166
|
name: overrides.name ?? base?.name,
|
|
169
167
|
type: overrides.type ?? base?.type,
|
|
@@ -172,12 +170,12 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
172
170
|
status: overrides.status ?? base?.status,
|
|
173
171
|
isRecorded: overrides.isRecorded ?? base?.isRecorded
|
|
174
172
|
};
|
|
175
|
-
newLocator.
|
|
176
|
-
|
|
173
|
+
newLocator.isAiRecorded = "Y";
|
|
174
|
+
pushAiUniqueLocator(newLocator);
|
|
177
175
|
}
|
|
178
|
-
function
|
|
179
|
-
if (
|
|
180
|
-
return
|
|
176
|
+
function resolveAiElement(ctx, locator, selector) {
|
|
177
|
+
if (isAiCssSelectorLocator(locator)) {
|
|
178
|
+
return getAiElementFromCssSelector(ctx, selector);
|
|
181
179
|
}
|
|
182
180
|
else if (locator.name.includes("id") || selector.startsWith("#")) {
|
|
183
181
|
return ctx.querySelector("#" + (0, xpathHelpers_js_1.escapeAttrValue)(selector));
|
|
@@ -200,11 +198,11 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
200
198
|
}
|
|
201
199
|
else if ((locator.name.includes("xpath") || selector.startsWith("//")) &&
|
|
202
200
|
!locator.type.match("dynamic")) {
|
|
203
|
-
const el =
|
|
201
|
+
const el = getAiElementFromXPath(ctx, selector, platform);
|
|
204
202
|
if (el) {
|
|
205
|
-
|
|
203
|
+
createAiLocator(locator, {
|
|
206
204
|
value: selector,
|
|
207
|
-
isRecorded:
|
|
205
|
+
isRecorded: "N"
|
|
208
206
|
});
|
|
209
207
|
}
|
|
210
208
|
return el;
|
|
@@ -213,14 +211,14 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
213
211
|
return ctx.querySelector(selector);
|
|
214
212
|
}
|
|
215
213
|
}
|
|
216
|
-
function
|
|
214
|
+
function aiFindInIframes(docmt, locator, selector) {
|
|
217
215
|
const iframes = docmt.querySelectorAll("iframe");
|
|
218
216
|
for (const iframe of iframes) {
|
|
219
217
|
try {
|
|
220
218
|
const iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
|
|
221
219
|
if (!iframeDoc)
|
|
222
220
|
continue;
|
|
223
|
-
const el =
|
|
221
|
+
const el = resolveAiElement(iframeDoc, locator, selector);
|
|
224
222
|
if (el)
|
|
225
223
|
return el;
|
|
226
224
|
}
|
|
@@ -230,15 +228,15 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
230
228
|
}
|
|
231
229
|
return null;
|
|
232
230
|
}
|
|
233
|
-
function
|
|
231
|
+
function pushAiUniqueLocator(obj) {
|
|
234
232
|
const key = `${obj.name}:${obj.value}`;
|
|
235
|
-
if (!
|
|
236
|
-
|
|
233
|
+
if (!finalAiLocatorsSet.has(key)) {
|
|
234
|
+
finalAiLocatorsSet.add(key);
|
|
237
235
|
finalLocators.push(obj);
|
|
238
236
|
}
|
|
239
237
|
}
|
|
240
238
|
/** Locator Value Cleaner (Handles Special Scenarios) **/
|
|
241
|
-
const
|
|
239
|
+
const cleanAiLocatorValue = (val, type) => {
|
|
242
240
|
if (!val)
|
|
243
241
|
return null;
|
|
244
242
|
if (val.toLowerCase().includes('ff-inspecter') || val.toLowerCase().includes('ff-inspect')) {
|
|
@@ -266,7 +264,7 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
266
264
|
if (type === "id" || type === "name") {
|
|
267
265
|
cleaned = cleaned.replace(/['"]/g, "").trim();
|
|
268
266
|
}
|
|
269
|
-
if (platform === "web" && type === "xpath" &&
|
|
267
|
+
if (platform === "web" && type === "xpath" && !val.startsWith("//"))
|
|
270
268
|
return null;
|
|
271
269
|
// Final check for empty strings
|
|
272
270
|
if (!cleaned || /^['"]{2}$/.test(cleaned))
|
|
@@ -278,7 +276,7 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
278
276
|
const recordedNLocators = record.locators.filter((l) => l.isRecorded === "N");
|
|
279
277
|
if (recordedNLocators.length > 0) {
|
|
280
278
|
for (const locator of recordedNLocators) {
|
|
281
|
-
|
|
279
|
+
createAiLocator(locator);
|
|
282
280
|
}
|
|
283
281
|
}
|
|
284
282
|
const isDynamic = String(locator.value || locator.type || "");
|
|
@@ -286,7 +284,7 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
286
284
|
isDynamic.match("dynamic") ||
|
|
287
285
|
isDynamic.includes("{") ||
|
|
288
286
|
isDynamic.includes("}")) {
|
|
289
|
-
|
|
287
|
+
createAiLocator(locator);
|
|
290
288
|
continue;
|
|
291
289
|
}
|
|
292
290
|
// if (record.isShared.includes("Y")) {
|
|
@@ -302,110 +300,110 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
302
300
|
}
|
|
303
301
|
const trimmedSelector = selector.trim();
|
|
304
302
|
//normal DOM
|
|
305
|
-
targetElement =
|
|
303
|
+
targetElement = resolveAiElement(docmt, locator, trimmedSelector);
|
|
306
304
|
//iframe (if not found)
|
|
307
305
|
if (!targetElement && platform === "web") {
|
|
308
|
-
targetElement =
|
|
306
|
+
targetElement = aiFindInIframes(docmt, locator, trimmedSelector);
|
|
309
307
|
}
|
|
310
308
|
//shadow DOM (if still not found)
|
|
311
309
|
if (!targetElement && platform === "web") {
|
|
312
|
-
targetElement =
|
|
310
|
+
targetElement = getAiElementFromShadowRoot(docmt.body, trimmedSelector);
|
|
313
311
|
}
|
|
314
312
|
if (!targetElement) {
|
|
315
313
|
console.warn("Locator failed:", trimmedSelector);
|
|
316
314
|
continue locators;
|
|
317
315
|
}
|
|
318
316
|
}
|
|
319
|
-
const
|
|
317
|
+
const locatorAiExists = (name, value) => {
|
|
320
318
|
const key = `${name}:${value}`;
|
|
321
|
-
return
|
|
319
|
+
return finalAiLocatorsSet.has(key);
|
|
322
320
|
};
|
|
323
321
|
if (targetElement) {
|
|
324
322
|
const excludedAttributes = [];
|
|
325
|
-
const idValue =
|
|
323
|
+
const idValue = getaiId(targetElement);
|
|
326
324
|
const ffXpath = targetElement.getAttribute("ff-xpath");
|
|
327
325
|
if (idValue &&
|
|
328
|
-
!
|
|
329
|
-
!(0, xpathHelpers_js_1.
|
|
326
|
+
!locatorAiExists("id", idValue) &&
|
|
327
|
+
!(0, xpathHelpers_js_1.isAiNumberExist)(idValue)) {
|
|
330
328
|
const prevId = record.locators.find((l) => l.name === "id");
|
|
331
|
-
if ((0, xpathHelpers_js_1.
|
|
329
|
+
if ((0, xpathHelpers_js_1.isAiUniqueInDOM)(docmt, "id", idValue, targetElement)) {
|
|
332
330
|
excludedAttributes.push("id");
|
|
333
|
-
|
|
331
|
+
createAiLocator(prevId, {
|
|
334
332
|
name: "id",
|
|
335
333
|
type: "static",
|
|
336
|
-
isRecorded: "
|
|
334
|
+
isRecorded: "N",
|
|
337
335
|
value: idValue
|
|
338
336
|
});
|
|
339
337
|
}
|
|
340
338
|
}
|
|
341
339
|
const tagName = targetElement.tagName;
|
|
342
|
-
if (tagName && !
|
|
340
|
+
if (tagName && platform === "web" && !locatorAiExists("tagName", tagName)) {
|
|
343
341
|
const prevTag = record.locators.find((l) => l.name === "tagName");
|
|
344
|
-
if ((0, xpathHelpers_js_1.
|
|
342
|
+
if ((0, xpathHelpers_js_1.isAiUniqueInDOM)(docmt, "tagName", tagName, targetElement)) {
|
|
345
343
|
excludedAttributes.push("tagName");
|
|
346
|
-
|
|
344
|
+
createAiLocator(prevTag, {
|
|
347
345
|
name: "tagName",
|
|
348
346
|
type: "static",
|
|
349
|
-
isRecorded: "
|
|
347
|
+
isRecorded: "N",
|
|
350
348
|
value: tagName
|
|
351
349
|
});
|
|
352
350
|
}
|
|
353
351
|
}
|
|
354
|
-
const textValue =
|
|
352
|
+
const textValue = getAiVisibleText(targetElement);
|
|
355
353
|
if (targetElement.tagName.toLowerCase() === "a" &&
|
|
356
354
|
textValue &&
|
|
357
|
-
!(0, xpathHelpers_js_1.
|
|
355
|
+
!(0, xpathHelpers_js_1.isAiNumberExist)(textValue)) {
|
|
358
356
|
const prevLinkText = record.locators.find((l) => l.name === "linkText");
|
|
359
|
-
if ((0, xpathHelpers_js_1.
|
|
357
|
+
if ((0, xpathHelpers_js_1.isAiUniqueInDOM)(docmt, "linkText", textValue, targetElement)) {
|
|
360
358
|
excludedAttributes.push("linkText");
|
|
361
|
-
|
|
359
|
+
createAiLocator(prevLinkText, {
|
|
362
360
|
name: "linkText",
|
|
363
361
|
type: "static",
|
|
364
|
-
isRecorded: "
|
|
362
|
+
isRecorded: "N",
|
|
365
363
|
value: textValue
|
|
366
364
|
});
|
|
367
365
|
}
|
|
368
366
|
}
|
|
369
|
-
const nameLocator =
|
|
367
|
+
const nameLocator = getAiName(targetElement);
|
|
370
368
|
if (nameLocator &&
|
|
371
|
-
!
|
|
372
|
-
!(0, xpathHelpers_js_1.
|
|
369
|
+
!locatorAiExists("name", nameLocator) &&
|
|
370
|
+
!(0, xpathHelpers_js_1.isAiNumberExist)(nameLocator)) {
|
|
373
371
|
const prevName = record.locators.find((l) => l.name === "name");
|
|
374
|
-
if ((0, xpathHelpers_js_1.
|
|
372
|
+
if ((0, xpathHelpers_js_1.isAiUniqueInDOM)(docmt, "name", nameLocator, targetElement)) {
|
|
375
373
|
excludedAttributes.push("name");
|
|
376
|
-
|
|
374
|
+
createAiLocator(prevName, {
|
|
377
375
|
name: "name",
|
|
378
376
|
type: "static",
|
|
379
|
-
isRecorded: "
|
|
377
|
+
isRecorded: "N",
|
|
380
378
|
value: nameLocator
|
|
381
379
|
});
|
|
382
380
|
}
|
|
383
381
|
}
|
|
384
|
-
const classValue =
|
|
382
|
+
const classValue = getAiClassName(targetElement);
|
|
385
383
|
if (classValue &&
|
|
386
384
|
classValue.trim() !== "" &&
|
|
387
385
|
!classValue.includes(" ") &&
|
|
388
|
-
!
|
|
389
|
-
!(0, xpathHelpers_js_1.
|
|
386
|
+
!locatorAiExists("className", classValue) &&
|
|
387
|
+
!(0, xpathHelpers_js_1.isAiNumberExist)(classValue)) {
|
|
390
388
|
const prevClassLocator = record.locators.find((l) => l.name === "className");
|
|
391
|
-
if ((0, xpathHelpers_js_1.
|
|
389
|
+
if ((0, xpathHelpers_js_1.isAiUniqueInDOM)(docmt, "className", classValue, targetElement)) {
|
|
392
390
|
excludedAttributes.push("className");
|
|
393
|
-
|
|
391
|
+
createAiLocator(prevClassLocator, {
|
|
394
392
|
name: "className",
|
|
395
393
|
type: "static",
|
|
396
|
-
isRecorded: "
|
|
394
|
+
isRecorded: "N",
|
|
397
395
|
value: classValue
|
|
398
396
|
});
|
|
399
397
|
}
|
|
400
398
|
}
|
|
401
|
-
(0, cssSelector_js_1.
|
|
399
|
+
(0, cssSelector_js_1.parseAiCssSelectors)(targetElement, "single").forEach((cssSelector) => {
|
|
402
400
|
if (cssSelector.value &&
|
|
403
|
-
!
|
|
404
|
-
|
|
401
|
+
!locatorAiExists("cssSelector", cssSelector.value)) {
|
|
402
|
+
createAiLocator(undefined, {
|
|
405
403
|
name: "cssSelector",
|
|
406
404
|
value: cssSelector.value,
|
|
407
405
|
type: "static",
|
|
408
|
-
isRecorded: "
|
|
406
|
+
isRecorded: "N"
|
|
409
407
|
});
|
|
410
408
|
}
|
|
411
409
|
});
|
|
@@ -415,7 +413,7 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
415
413
|
let xpathResults = [];
|
|
416
414
|
try {
|
|
417
415
|
xpathResults =
|
|
418
|
-
(0, xpath_js_1.
|
|
416
|
+
(0, xpath_js_1.aiParseDOM)(targetElement, docmt, true, includedAttributes, platform) ??
|
|
419
417
|
[];
|
|
420
418
|
}
|
|
421
419
|
catch (error) {
|
|
@@ -428,27 +426,35 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
428
426
|
if (!result.value)
|
|
429
427
|
return;
|
|
430
428
|
if (result.key === "iosClassChain") {
|
|
431
|
-
|
|
432
|
-
name: "
|
|
429
|
+
createAiLocator(undefined, {
|
|
430
|
+
name: "iOSClassChain",
|
|
433
431
|
value: result.value,
|
|
434
432
|
type: "static",
|
|
435
|
-
isRecorded: "
|
|
433
|
+
isRecorded: "N"
|
|
436
434
|
});
|
|
437
435
|
}
|
|
438
436
|
else if (result.key === "iosPredicateString") {
|
|
439
|
-
|
|
440
|
-
name: "
|
|
437
|
+
createAiLocator(undefined, {
|
|
438
|
+
name: "iOSNsPredicateString",
|
|
441
439
|
value: result.value,
|
|
442
440
|
type: "static",
|
|
443
|
-
isRecorded: "
|
|
441
|
+
isRecorded: "N"
|
|
444
442
|
});
|
|
445
443
|
}
|
|
444
|
+
// else if (result.key === "iosAccessibilityID") {
|
|
445
|
+
// createAiLocator(undefined, {
|
|
446
|
+
// name: "accessibilityId",
|
|
447
|
+
// value: result.value,
|
|
448
|
+
// type: "static",
|
|
449
|
+
// isRecorded: "N"
|
|
450
|
+
// });
|
|
451
|
+
// }
|
|
446
452
|
else {
|
|
447
|
-
|
|
453
|
+
createAiLocator(undefined, {
|
|
448
454
|
name: "xpath",
|
|
449
455
|
value: result.value,
|
|
450
456
|
type: "static",
|
|
451
|
-
isRecorded: "
|
|
457
|
+
isRecorded: "N"
|
|
452
458
|
});
|
|
453
459
|
}
|
|
454
460
|
});
|
|
@@ -456,17 +462,14 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
456
462
|
for (const loc of record.locators) {
|
|
457
463
|
if (!loc.value)
|
|
458
464
|
continue;
|
|
459
|
-
for (const relation of
|
|
465
|
+
for (const relation of aiRelations) {
|
|
460
466
|
if (loc.value.includes(relation)) {
|
|
461
|
-
const relativeXpath =
|
|
467
|
+
const relativeXpath = checkAiReferenceElementIsValid(loc.value, relation, docmt);
|
|
462
468
|
if (relativeXpath) {
|
|
463
|
-
|
|
469
|
+
createAiLocator(loc, {
|
|
464
470
|
name: "xpath",
|
|
465
471
|
value: relativeXpath,
|
|
466
|
-
isRecorded:
|
|
467
|
-
locator.isRecorded !== null
|
|
468
|
-
? locator.isRecorded
|
|
469
|
-
: "Y"
|
|
472
|
+
isRecorded: "N"
|
|
470
473
|
});
|
|
471
474
|
break;
|
|
472
475
|
}
|
|
@@ -479,14 +482,14 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
479
482
|
}
|
|
480
483
|
if (finalLocators.length < 5) {
|
|
481
484
|
const fallbackCandidates = [
|
|
482
|
-
{ name: "id", value:
|
|
483
|
-
{ name: "name", value:
|
|
484
|
-
{ name: "className", value:
|
|
485
|
+
{ name: "id", value: getaiId(targetElement) },
|
|
486
|
+
{ name: "name", value: getAiName(targetElement) },
|
|
487
|
+
{ name: "className", value: getAiClassName(targetElement) },
|
|
485
488
|
{ name: "tagName", value: targetElement.tagName },
|
|
486
489
|
{
|
|
487
490
|
name: "linkText",
|
|
488
491
|
value: targetElement.tagName.toLowerCase() === "a"
|
|
489
|
-
?
|
|
492
|
+
? getAiVisibleText(targetElement)
|
|
490
493
|
: null
|
|
491
494
|
}
|
|
492
495
|
];
|
|
@@ -496,46 +499,59 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
496
499
|
const { name, value } = candidate;
|
|
497
500
|
if (!value)
|
|
498
501
|
continue;
|
|
499
|
-
if ((0, xpathHelpers_js_1.
|
|
502
|
+
if ((0, xpathHelpers_js_1.isAiNumberExist)(value))
|
|
500
503
|
continue;
|
|
501
504
|
if (name === "className" && value.includes(" "))
|
|
502
505
|
continue;
|
|
503
|
-
if ((0, xpathHelpers_js_1.
|
|
504
|
-
|
|
506
|
+
if ((0, xpathHelpers_js_1.isAiUniqueInDOM)(docmt, name, value, targetElement)) {
|
|
507
|
+
createAiLocator(undefined, {
|
|
505
508
|
name,
|
|
506
509
|
type: "static",
|
|
507
510
|
value,
|
|
508
|
-
isRecorded: "
|
|
509
|
-
|
|
511
|
+
isRecorded: "N",
|
|
512
|
+
isAiRecorded: "Y"
|
|
510
513
|
});
|
|
511
514
|
}
|
|
512
515
|
}
|
|
513
516
|
if (finalLocators.length < 5) {
|
|
514
|
-
(0, cssSelector_js_1.
|
|
517
|
+
(0, cssSelector_js_1.parseAiCssSelectors)(targetElement, "multiple").forEach((cssSelector) => {
|
|
515
518
|
if (finalLocators.length > 4)
|
|
516
519
|
return;
|
|
517
520
|
if (!cssSelector.value)
|
|
518
521
|
return;
|
|
519
|
-
if (!(0, xpathHelpers_js_1.
|
|
522
|
+
if (!(0, xpathHelpers_js_1.isAiUniqueInDOM)(docmt, "cssSelector", cssSelector.value, targetElement)) {
|
|
520
523
|
return;
|
|
521
524
|
}
|
|
522
|
-
|
|
525
|
+
createAiLocator(undefined, {
|
|
523
526
|
name: "cssSelector",
|
|
524
527
|
type: "static",
|
|
525
528
|
value: cssSelector.value,
|
|
526
|
-
isRecorded: "
|
|
527
|
-
|
|
529
|
+
isRecorded: "N",
|
|
530
|
+
isAiRecorded: "Y"
|
|
528
531
|
});
|
|
529
532
|
});
|
|
530
533
|
}
|
|
531
534
|
}
|
|
532
535
|
const finalAutoHealedLocators = finalLocators.map((obj) => ({
|
|
533
536
|
...obj,
|
|
534
|
-
value:
|
|
537
|
+
value: cleanAiLocatorValue(obj.value, obj.name)
|
|
535
538
|
}));
|
|
539
|
+
const getLocatorPriority = (name) => {
|
|
540
|
+
const lower = (name || "").toLowerCase();
|
|
541
|
+
if (lower === "id")
|
|
542
|
+
return 1;
|
|
543
|
+
if (lower === "name")
|
|
544
|
+
return 2;
|
|
545
|
+
if (lower === "xpath")
|
|
546
|
+
return 3;
|
|
547
|
+
return 4;
|
|
548
|
+
};
|
|
549
|
+
const validLocators = finalAutoHealedLocators
|
|
550
|
+
.filter((locator) => locator?.value != null && locator.value !== "")
|
|
551
|
+
.sort((a, b) => getLocatorPriority(a.name) - getLocatorPriority(b.name));
|
|
536
552
|
const jsonResult = [
|
|
537
553
|
{
|
|
538
|
-
locators:
|
|
554
|
+
locators: validLocators,
|
|
539
555
|
// isShared: `${record.isShared}`,
|
|
540
556
|
}
|
|
541
557
|
];
|
|
@@ -554,4 +570,4 @@ const getElementsFromHTML = (record, htmlString, platform) => {
|
|
|
554
570
|
}
|
|
555
571
|
return null;
|
|
556
572
|
};
|
|
557
|
-
exports.
|
|
573
|
+
exports.getAiElementsFromHTML = getAiElementsFromHTML;
|
|
@@ -5,7 +5,7 @@ export interface Locator {
|
|
|
5
5
|
reference: string;
|
|
6
6
|
status: string;
|
|
7
7
|
isRecorded: string;
|
|
8
|
-
|
|
8
|
+
isAiRecorded?: string;
|
|
9
9
|
}
|
|
10
10
|
export interface ElementRecord {
|
|
11
11
|
locators: Locator[];
|
|
@@ -15,3 +15,13 @@ export type XPathEntry = {
|
|
|
15
15
|
key: string;
|
|
16
16
|
value: string;
|
|
17
17
|
};
|
|
18
|
+
export interface AiDomContext {
|
|
19
|
+
tag: string;
|
|
20
|
+
attributes: Attr[];
|
|
21
|
+
normalizedText: string;
|
|
22
|
+
filteredText: string;
|
|
23
|
+
parent: Element | null;
|
|
24
|
+
ancestors: Element[];
|
|
25
|
+
previousSiblings: Element[];
|
|
26
|
+
nextSiblings: Element[];
|
|
27
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { XPathEntry } from "../types/locator.js";
|
|
2
|
-
export declare const
|
|
2
|
+
export declare const generateAiAndroidLocators: (element: HTMLElement | Element, docmt: Document, isTarget: boolean) => XPathEntry[];
|