ff-automationv2 2.2.33 → 2.2.34-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.
- package/dist/ai/llmprompts/llmPromptTypes/systemPromptTypes.d.ts +2 -0
- package/dist/ai/llmprompts/promptRegistry.js +4 -0
- package/dist/ai/llmprompts/systemPrompts/multichannelKeywordExtractor.d.ts +6 -0
- package/dist/ai/llmprompts/systemPrompts/multichannelKeywordExtractor.js +86 -0
- package/dist/ai/llmprompts/userPrompts/userPrompt.js +1 -0
- package/dist/automation/actions/interaction/clear/clear.js +4 -1
- package/dist/automation/actions/interaction/clear/clearAndEnter.js +4 -1
- package/dist/automation/actions/interaction/elementlessActions/getOtp.js +2 -2
- package/dist/automation/actions/interaction/enterActions/EnterInputIntoElementFromClipBoardInterface_mob.js +4 -1
- package/dist/automation/actions/interaction/enterActions/enterInput.js +5 -2
- package/dist/automation/actions/interaction/enterActions/enterInputAndPress.js +4 -1
- package/dist/automation/actions/interaction/enterActions/waitAndEnter.js +4 -1
- package/dist/automation/actions/interaction/get/getText.js +9 -2
- package/dist/automation/actions/interaction/iframe/switchToDefaultContent.js +1 -1
- package/dist/automation/actions/interaction/shadowDom/shadowDomUpload.js +88 -10
- package/dist/automation/actions/interaction/verify/VerifyElementNotContainsText.js +4 -1
- package/dist/automation/actions/interaction/verify/verifypartialtext.js +9 -2
- package/dist/automation/actions/interaction/verify/verifytext.js +9 -2
- package/dist/automation/actions/interface/FireFlinkResponseSchema.d.ts +30 -0
- package/dist/automation/actions/interface/FireFlinkResponseSchema.js +8 -1
- package/dist/core/helpers/elementSelectorResolver.d.ts +1 -1
- package/dist/core/helpers/elementSelectorResolver.js +3 -2
- package/dist/core/helpers/runnerDomPipeline.d.ts +2 -2
- package/dist/core/helpers/runnerDomPipeline.js +20 -9
- package/dist/core/helpers/visionFallbackHandler.js +4 -4
- package/dist/core/interfaces/automationRunnerInterface.d.ts +1 -0
- package/dist/core/interfaces/executionDetails.d.ts +2 -1
- package/dist/core/interfaces/fireflinkScriptPayloadInterface.d.ts +1 -3
- package/dist/core/interfaces/promptInterface.d.ts +5 -0
- package/dist/core/main/actionHandlerFactory.js +3 -3
- package/dist/core/main/executionContext.d.ts +2 -0
- package/dist/core/main/executionContext.js +18 -3
- package/dist/core/main/runAutomationScript.d.ts +2 -0
- package/dist/core/main/runAutomationScript.js +78 -20
- package/dist/core/types/promptType.d.ts +1 -0
- package/dist/core/types/promptType.js +1 -0
- package/dist/fireflinkData/fireflinkLocators/getListOfLocators.d.ts +2 -1
- package/dist/fireflinkData/fireflinkLocators/getListOfLocators.js +2 -2
- package/dist/fireflinkData/fireflinkScript/appendScriptData.js +61 -14
- package/dist/fireflinkData/fireflinkScript/scriptGenrationData.d.ts +1 -0
- package/dist/fireflinkData/fireflinkScript/scriptGenrationData.js +1 -0
- package/dist/imageAnalysisWeb/annotateScreenshot.d.ts +1 -1
- package/dist/imageAnalysisWeb/annotateScreenshot.js +24 -10
- package/dist/imageAnalysisWeb/generateAnnotatedScreenshots.js +5 -2
- package/dist/tests/senario.d.ts +0 -0
- package/dist/tests/senario.js +1120 -0
- package/dist/tests/test12.js +30 -62
- package/dist/utils/DomExtraction/jsForAttributeInjection.js +23 -0
- package/dist/utils/JsPathGenerator/generateJsPath.d.ts +2 -0
- package/dist/utils/JsPathGenerator/generateJsPath.js +169 -0
- package/dist/utils/helpers/xpathcreation.js +3 -0
- package/dist/utils/iframesExtraction/findElementUsingFF.js +1 -2
- package/dist/utils/iframesExtraction/switchToIframeByMeta.js +1 -1
- package/dist/utils/workers/locatorWorker.js +3 -1
- package/package.json +1 -1
package/dist/tests/test12.js
CHANGED
|
@@ -1,66 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
// // import { verify } from "node:crypto";
|
|
2
3
|
// import { AutomationRunner } from "../index.js"
|
|
3
|
-
// import
|
|
4
|
+
// import { Kafka, Producer } from "kafkajs";
|
|
5
|
+
// import path from "path";
|
|
6
|
+
// import fs from "fs";
|
|
7
|
+
// const certPath = path.join(
|
|
8
|
+
// "D:", "Bitbucket", "ai-chat-backend", "Certificates", "V3keystore.jks"
|
|
9
|
+
// );
|
|
10
|
+
// const ssl = {
|
|
11
|
+
// ca: [fs.readFileSync(certPath, "utf-8")],
|
|
12
|
+
// rejectUnauthorized: false,
|
|
13
|
+
// }
|
|
4
14
|
// const runner = new AutomationRunner();
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
//
|
|
24
|
-
// webSocketId: "",
|
|
25
|
-
// generatedBy: "",
|
|
26
|
-
// licenseType: "C-Professional",
|
|
27
|
-
// licenseId: "LIC1026612",
|
|
28
|
-
// userId: "USR10150300",
|
|
29
|
-
// projectType: "Web",
|
|
30
|
-
// browserName: "Microsoft Edge",
|
|
31
|
-
// kafkaMessageKey: kafkaMessageKey,
|
|
32
|
-
// serverData: {},
|
|
33
|
-
// type: "sdfg",
|
|
34
|
-
// scriptName: "url",
|
|
35
|
-
// scriptType: "android",
|
|
36
|
-
// projectId: "PJT1001",
|
|
37
|
-
// testCaseId: "SCR1208",
|
|
38
|
-
// promptId: "3f6e123c-8ec6-47c1-90e1-1a2f53b49251",
|
|
39
|
-
// userName: "default-user",
|
|
40
|
-
// appiumPort: 0,
|
|
41
|
-
// platformVersion: "",
|
|
42
|
-
// realDeviceId: "",
|
|
43
|
-
// isTerminate: false,
|
|
44
|
-
// isCloud: false,
|
|
45
|
-
// cloudConfig: {
|
|
46
|
-
// accessKey: "yfAa_w199cJge2Gezsgb2Jnu7hKdqER1tExY5kKvgSvXfi1lRUelFsbTFI5pMq7zQuwwNXXnsLTkjKhvWcKDWsUoGLi9KjUV6gbUNDhqj0jWYFyW9h_0LIDEg5qXigwZdqzQqAvWwHin3gBhzGndeROtixv_VFhnseGSWRAHTajZzE-Zl0d8BP6gl0TEBUfoNgqBtAQVDLP6wAQzPfcyjjGFsdHkXWeHFkKmA78D7rKlnNuN2WgjIUlhYheDRDeqZ98Ig_PyS1GbUvjwJ73L38sr_cD4Y9lrKyWJV-m5RmtKhmkqRM8uk5gEfjzgwy30",
|
|
47
|
-
// hostname: "devicefarm.fireflink.com",
|
|
48
|
-
// licenseId: "LIC1026612",
|
|
49
|
-
// projectName: "demo",
|
|
50
|
-
// protocol: "https",
|
|
51
|
-
// path: "backend/fireflinkcloud/wd/hub"
|
|
52
|
-
// },
|
|
53
|
-
// capabilities: {
|
|
54
|
-
// browserName: "chrome",
|
|
55
|
-
// platformName: "Windows 11",
|
|
56
|
-
// browserVersion: "146"
|
|
57
|
-
// },
|
|
58
|
-
// token: "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJNTnNvS1VYQkxLS3BPblpWUUxaZWhwcGhobHdJSldCYUF4dDRkaXMwNlcwIn0.eyJleHAiOjE3ODA0Njk3OTEsImlhdCI6MTc4MDM4MzM5MSwianRpIjoib25ydHJvOjc3YjJjZDNmLWQwOTMtNDA3Yy04ODk1LTVmMWQ3NTIyMjE3NSIsImlzcyI6Imh0dHBzOi8vYXBwLnYzLmZpcmVmbGluay5jb206MzAxMDEvcmVhbG1zL0ZpcmVGbGluayIsImF1ZCI6InRhcmdldC1jbGllbnQiLCJzdWIiOiJmOjg4OTI5YjAwLTczYjgtNDc0Zi1iYmJjLWRiMmUxYTNlMDc5ODpyYXNobWkudEBmaXJlZmxpbmsuY29tIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoiZmxpbmstc2VydmljZSIsInNpZCI6ImUzNjE2MzFiLWJkMjItNGZiMS1hOWY1LTQ4YzU3MjQ0NWNhNCIsImFjciI6IjEiLCJhbGxvd2VkLW9yaWdpbnMiOlsiIl0sInJlYWxtX2FjY2VzcyI6eyJyb2xlcyI6WyJvZmZsaW5lX2FjY2VzcyIsInVtYV9hdXRob3JpemF0aW9uIl19LCJzY29wZSI6InByb2ZpbGUgZW1haWwiLCJjdXJyZW50TGljZW5zZUlkIjoiTElDMjYxMTAiLCJlbWFpbF92ZXJpZmllZCI6ZmFsc2UsImN1cnJlbnRQcml2aWxlZ2UiOiJBZG1pbiIsImZ1bGxOYW1lIjoiUmFzaG1pIiwiYWN0aXZhdGlvblN0YXR1cyI6IkFDVElWRSIsInByaXZpbGVnZSI6IkFkbWluIiwibGljZW5zZU5hbWUiOiJGaXJlZmxpbmtfQnVsay1MSUMyNjExMCIsInByZWZlcnJlZF91c2VybmFtZSI6IlJhc2htaSIsInVzZXJOYW1lIjoicmFzaG1pLnRAZmlyZWZsaW5rLmNvbSIsImJpbGxpbmdDeWNsZSI6IlllYXJseSIsImlkIjoiVVNSMTAxNTAzMTQiLCJsaWNlbnNlSWQiOiJMSUMyNjExMCIsImVtYWlsIjoicmFzaG1pLnRAZmlyZWZsaW5rLmNvbSJ9.RcOhUQMtDAd6LibDR0Iws5WWK4EwfgXiTWxn1GP-bHz3W4CFd1ap_LIzAFX2RLe2dbKpbhiVfDAdU7MmYt10wQdz3PArWVXolpBQWNJecJhibYZCp9dQwMvBiDBQ_5i6AS4K_PhDlpwXkh2o0vuBfrYu4jVl1vOPfbLodxq3Tj8SfngD8_1q0_CXKbmBTXmsVESs43f4XXT2T7wa6t2ErbIwW38FLur2UmLab3UnVYJdqv7fjkrBrGqQBMzLDeQZwAWmtAWpWFGf9dMWhJyBxu-Al88VOIVZVxDwVnXLMg-DeIv_H1lk8x2AlyG_WVxlFVRkWNTztKMvXtc2GXB5ug"
|
|
59
|
-
// })
|
|
60
|
-
// res.json({
|
|
61
|
-
// message: "User story received successfully",
|
|
62
|
-
// });
|
|
63
|
-
// });
|
|
64
|
-
// app.listen(3000, () => {
|
|
65
|
-
// console.log("Server running on port 3000");
|
|
15
|
+
// runner.run({
|
|
16
|
+
// // hasMoreSteps: false,
|
|
17
|
+
// userStory: [
|
|
18
|
+
// ["Launch the browser"],
|
|
19
|
+
// ["navigate to url https://www.indiamart.com"],
|
|
20
|
+
// ["wait for 20 seconds"],
|
|
21
|
+
// ["get otp from INDMRT message within 60 seconds and wait for 60 seconds"],
|
|
22
|
+
// ["Enter OTP into enter otp boxes"],
|
|
23
|
+
// ["wait for 10 seconds"],
|
|
24
|
+
// ["Close the browser"]
|
|
25
|
+
// ],
|
|
26
|
+
// scenario: false,
|
|
27
|
+
// sslConfig: ssl,
|
|
28
|
+
// scriptType: "android",
|
|
29
|
+
// broker: ["test3.fireflink.com:9092"], "scriptName": "script", "type": "Web & Mobile", "projectId": "PJT1006", "testCaseId": "SCR1806", kafkaMessageKey: 'lohit',
|
|
30
|
+
// "promptId": "a8fba64a-4d91-422c-bdfd-a7609576a63e", "generatedBy": "", "webSocketId": "", "licenseType": "C-Professional", "licenseId": "LIC2026980", "userId": "USR26860", "projectType": "Web", "serverData": "https://performance.fireflink.com", "pageDetails": { "Web": { "pageId": "PAG3e09350f-4917-4c80-8ce5-a08e97fe314e", "pageName": "ai-Page" } }, "userName": "jai", isTerminate: false, "appiumPort": 4723, "realDeviceId": "", "browserName": "Google Chrome", "token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJIUlpZV2NoTURZWXVQaDF3TDkxb3drS3Q1OUtuNC1sOFRJcXF3a2RBV2pjIn0.eyJleHAiOjE3ODA1NzkwNzEsImlhdCI6MTc4MDQ5MjY3MSwianRpIjoib25ydHJvOmYxYzJiNjg2LTJiZGMtNGIxOS04ZGVhLTMwMjhhNDkxZDY5NCIsImlzcyI6Imh0dHA6Ly8xNDguNzIuMTY4LjE2OjMwMTA3L3JlYWxtcy9GaXJlRmxpbmstdGVzdDEiLCJhdWQiOiJ0YXJnZXQtY2xpZW50Iiwic3ViIjoiZjo4ODkyOWIwMC03M2I4LTQ3NGYtYmJiYy1kYjJlMWEzZTA3OTg6amFpYWthc2hAeW9wbWFpbC5jb20iLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJmbGluay1zZXJ2aWNlIiwic2lkIjoiZWRiMDgwZDctNzhiYi00NzU5LTgyM2EtMTM1ZDUzYTM5NmQzIiwiYWNyIjoiMSIsImFsbG93ZWQtb3JpZ2lucyI6WyIiXSwicmVhbG1fYWNjZXNzIjp7InJvbGVzIjpbIm9mZmxpbmVfYWNjZXNzIiwidW1hX2F1dGhvcml6YXRpb24iXX0sInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsImN1cnJlbnRMaWNlbnNlSWQiOiJMSUMzOTIyIiwiZW1haWxfdmVyaWZpZWQiOmZhbHNlLCJjdXJyZW50UHJpdmlsZWdlIjoiQWRtaW4iLCJmdWxsTmFtZSI6ImphaSIsImFjdGl2YXRpb25TdGF0dXMiOiJBQ1RJVkUiLCJwcml2aWxlZ2UiOiJBZG1pbiIsImxpY2Vuc2VOYW1lIjoiRmlyZUZsaW5rIC0gTElDMzkyMnciLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJqYWkiLCJ1c2VyTmFtZSI6ImphaWFrYXNoQHlvcG1haWwuY29tIiwiYmlsbGluZ0N5Y2xlIjoiWWVhcmx5IiwiaWQiOiJVU1IyNjc1MiIsImxpY2Vuc2VJZCI6IkxJQzM5MjIiLCJlbWFpbCI6ImphaWFrYXNoQHlvcG1haWwuY29tIn0.LPBCnEKBi8Yi4Qktpd_oW46-P7s0iQ0AejZeXRMIa8aUwfwY8gNl3McIrKLBVqPL4NKYe_05aMaiepvjkolu7RWh120mX90ZdNJuMUjg0WuRaM90xNjmy1YK94mBt0mphKK04u1K4trvhvgW2hTt4rvGRq_oEvH2E_2YqYw310TuP-guqxuZqTv_IJ6HFtvMJkV9L3A579juICZfGdS53-KuZwqn7jA-73nJZBzPJyMucaPz42s36RWpDmfs7MX0-Fcv8LH7FvCjCs63GvCC-tdGoh5m0MRCiElJocWlw9jabsPHymEt5-nemnXIZIon2675wYbxVx99mHA1VBPD2w", "sessionPath": "session", "platform": "android", "platformVersion": "", "env": "test1",
|
|
31
|
+
// "serverHost": "https://sg-app.fireflink.com", "adbPath": "C:\\FireFlinkClient\\resources\\bin\\win\\android-sdk\\platform-tools\\adb.exe",
|
|
32
|
+
// topic: "test-topic",
|
|
33
|
+
// aiExecutionAction: ""
|
|
66
34
|
// });
|
|
@@ -136,6 +136,7 @@ async function getReactAnnotatedDOM(browser) {
|
|
|
136
136
|
path = framePath + path;
|
|
137
137
|
}
|
|
138
138
|
}
|
|
139
|
+
path = path.replace(/\/div\[0\]\/div\[0\]/g, "/div[1]").replace(/\/div\[0\]/g, "/div[1]");
|
|
139
140
|
return path;
|
|
140
141
|
}
|
|
141
142
|
// ==========================================================
|
|
@@ -219,6 +220,27 @@ async function getReactAnnotatedDOM(browser) {
|
|
|
219
220
|
logData_js_1.logger.error("Attribute injection failed", error);
|
|
220
221
|
}
|
|
221
222
|
}
|
|
223
|
+
const voidElements = new Set([
|
|
224
|
+
"area",
|
|
225
|
+
"base",
|
|
226
|
+
"br",
|
|
227
|
+
"col",
|
|
228
|
+
"embed",
|
|
229
|
+
"hr",
|
|
230
|
+
"img",
|
|
231
|
+
"input",
|
|
232
|
+
"link",
|
|
233
|
+
"meta",
|
|
234
|
+
"param",
|
|
235
|
+
"source",
|
|
236
|
+
"track",
|
|
237
|
+
"wbr"
|
|
238
|
+
]);
|
|
239
|
+
// Void elements cannot have children or closing tags
|
|
240
|
+
if (voidElements.has(tag)) {
|
|
241
|
+
return html;
|
|
242
|
+
}
|
|
243
|
+
// Normal children
|
|
222
244
|
el.childNodes.forEach(child => {
|
|
223
245
|
html += serializeNode(child);
|
|
224
246
|
});
|
|
@@ -355,6 +377,7 @@ function isSelectRelated(node) {
|
|
|
355
377
|
path = framePath + path;
|
|
356
378
|
}
|
|
357
379
|
}
|
|
380
|
+
path = path.replace(/\/div\[0\]\/div\[0\]/g, "/div[1]").replace(/\/div\[0\]/g, "/div[1]");
|
|
358
381
|
return path;
|
|
359
382
|
}
|
|
360
383
|
function walk(root) {
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.convertToCssSelector = convertToCssSelector;
|
|
4
|
+
exports.generateJsPath = generateJsPath;
|
|
5
|
+
//Converts XPath or FireFlink selector fragment to a clean CSS selector.
|
|
6
|
+
function convertToCssSelector(pathFragment) {
|
|
7
|
+
let s = pathFragment.trim().replace(/^\/+/, "");
|
|
8
|
+
// Remove redundant html/body prefix for top-level selectors
|
|
9
|
+
s = s.replace(/^html(\[\d+\])?\/body(\[\d+\])?\//i, "");
|
|
10
|
+
// Convert tag[n] -> tag or tag:nth-of-type(n)
|
|
11
|
+
s = s.replace(/([a-zA-Z0-9_-]+)\[(\d+)\]/g, (_, tag, index) => {
|
|
12
|
+
return index === "1" ? tag : `${tag}:nth-of-type(${index})`;
|
|
13
|
+
});
|
|
14
|
+
s = s.replace(/\//g, " > ");
|
|
15
|
+
return s;
|
|
16
|
+
}
|
|
17
|
+
//jspath generation
|
|
18
|
+
function generateJsPath(selector, finalSelectorOverride) {
|
|
19
|
+
if (typeof selector !== 'string' || selector.trim().length === 0) {
|
|
20
|
+
throw new TypeError('generateJsPath: "selector" must be a non-empty string.');
|
|
21
|
+
}
|
|
22
|
+
const SHADOW_TOKEN = '/shadow/';
|
|
23
|
+
const trimmedSelector = selector.trim();
|
|
24
|
+
if (trimmedSelector.startsWith(SHADOW_TOKEN)) {
|
|
25
|
+
throw new Error('generateJsPath: selector cannot start with "/shadow/" — a host element path is required before it.');
|
|
26
|
+
}
|
|
27
|
+
if (trimmedSelector.endsWith(SHADOW_TOKEN)) {
|
|
28
|
+
throw new Error('generateJsPath: selector cannot end with "/shadow/" — a path is required after it.');
|
|
29
|
+
}
|
|
30
|
+
const rawChunks = trimmedSelector
|
|
31
|
+
.split(SHADOW_TOKEN)
|
|
32
|
+
.map((c) => c.trim())
|
|
33
|
+
.filter((c) => c.length > 0);
|
|
34
|
+
if (rawChunks.length === 0) {
|
|
35
|
+
throw new Error('generateJsPath: no valid path segments found in selector.');
|
|
36
|
+
}
|
|
37
|
+
const cssChunks = rawChunks.map((chunk) => xpathChunkToCss(chunk));
|
|
38
|
+
if (typeof finalSelectorOverride === 'string' && finalSelectorOverride.trim().length > 0) {
|
|
39
|
+
cssChunks[cssChunks.length - 1] = finalSelectorOverride.trim();
|
|
40
|
+
}
|
|
41
|
+
let jsPath = `document.querySelector(${toQuotedSelector(cssChunks[0])})`;
|
|
42
|
+
for (let i = 1; i < cssChunks.length; i++) {
|
|
43
|
+
jsPath += `.shadowRoot.querySelector(${toQuotedSelector(cssChunks[i])})`;
|
|
44
|
+
}
|
|
45
|
+
return jsPath;
|
|
46
|
+
}
|
|
47
|
+
//Helpers
|
|
48
|
+
function toQuotedSelector(cssSelector) {
|
|
49
|
+
const cleanSelector = cssSelector.replace(/\[([\w\-:]+)\s*=\s*['"]([^'"]+)['"]\]/g, '[$1=$2]');
|
|
50
|
+
return `'${cleanSelector}'`;
|
|
51
|
+
}
|
|
52
|
+
//Nodes
|
|
53
|
+
function xpathChunkToCss(chunk) {
|
|
54
|
+
const { nodes, combinators } = tokenizeXPath(chunk);
|
|
55
|
+
if (nodes.length === 0) {
|
|
56
|
+
throw new Error(`generateJsPath: unable to parse path segment "${chunk}".`);
|
|
57
|
+
}
|
|
58
|
+
let css = xpathNodeToCss(nodes[0]);
|
|
59
|
+
for (let i = 1; i < nodes.length; i++) {
|
|
60
|
+
const combinator = combinators[i] === 'descendant' ? ' ' : ' > ';
|
|
61
|
+
css += combinator + xpathNodeToCss(nodes[i]);
|
|
62
|
+
}
|
|
63
|
+
return css;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Splits an XPath chunk into node segments plus the axis combinator
|
|
67
|
+
* ('child' | 'descendant') that precedes each node. Bracket contents are
|
|
68
|
+
* treated as opaque so a `/` inside a predicate does not cause an
|
|
69
|
+
*/
|
|
70
|
+
function tokenizeXPath(path) {
|
|
71
|
+
const nodes = [];
|
|
72
|
+
const combinators = [];
|
|
73
|
+
let depth = 0;
|
|
74
|
+
let current = '';
|
|
75
|
+
let pendingCombinator;
|
|
76
|
+
let i = 0;
|
|
77
|
+
const pushCurrent = () => {
|
|
78
|
+
const trimmed = current.trim();
|
|
79
|
+
if (trimmed.length > 0 && trimmed !== '.') {
|
|
80
|
+
nodes.push(trimmed);
|
|
81
|
+
combinators.push(pendingCombinator);
|
|
82
|
+
}
|
|
83
|
+
current = '';
|
|
84
|
+
};
|
|
85
|
+
while (i < path.length) {
|
|
86
|
+
const ch = path[i];
|
|
87
|
+
if (ch === '[')
|
|
88
|
+
depth++;
|
|
89
|
+
if (ch === ']')
|
|
90
|
+
depth--;
|
|
91
|
+
if (ch === '/' && depth === 0) {
|
|
92
|
+
pushCurrent();
|
|
93
|
+
if (path[i + 1] === '/') {
|
|
94
|
+
pendingCombinator = 'descendant';
|
|
95
|
+
i += 2;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
pendingCombinator = 'child';
|
|
100
|
+
i += 1;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
current += ch;
|
|
105
|
+
i += 1;
|
|
106
|
+
}
|
|
107
|
+
pushCurrent();
|
|
108
|
+
return { nodes, combinators };
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Converts a single XPath node (e.g. `div[1]`, `input[@id='x']`,
|
|
112
|
+
* `span[contains(@class,'icon')]`) into an equivalent CSS selector
|
|
113
|
+
* fragment, always preserving positional predicates as `:nth-of-type()`.
|
|
114
|
+
*/
|
|
115
|
+
function xpathNodeToCss(node) {
|
|
116
|
+
const match = node.match(/^(\*|[A-Za-z_][\w\-.:]*)((?:\[[^\]]*\])*)$/);
|
|
117
|
+
// Fallback: if the node doesn't match the expected shape, pass it
|
|
118
|
+
// through as-is rather than silently dropping information.
|
|
119
|
+
if (!match) {
|
|
120
|
+
return node;
|
|
121
|
+
}
|
|
122
|
+
const tag = match[1];
|
|
123
|
+
const predicatesStr = match[2] || '';
|
|
124
|
+
const predicates = predicatesStr.match(/\[[^\]]*\]/g) || [];
|
|
125
|
+
const attrSelectors = [];
|
|
126
|
+
let nthIndex = null;
|
|
127
|
+
for (const pred of predicates) {
|
|
128
|
+
const inner = pred.slice(1, -1).trim();
|
|
129
|
+
// [@attr='value'] or [@attr="value"]
|
|
130
|
+
const attrEqMatch = inner.match(/^@([\w\-:]+)\s*=\s*(['"])(.*)\2$/);
|
|
131
|
+
// contains(@attr, 'value')
|
|
132
|
+
const containsMatch = inner.match(/^contains\(\s*@([\w\-:]+)\s*,\s*(['"])(.*)\2\s*\)$/i);
|
|
133
|
+
// position()=n
|
|
134
|
+
const positionMatch = inner.match(/^position\(\)\s*=\s*(\d+)$/i);
|
|
135
|
+
if (attrEqMatch) {
|
|
136
|
+
const attrName = attrEqMatch[1];
|
|
137
|
+
const attrValue = escapeCssString(attrEqMatch[3]);
|
|
138
|
+
attrSelectors.push(`[${attrName}='${attrValue}']`);
|
|
139
|
+
}
|
|
140
|
+
else if (containsMatch) {
|
|
141
|
+
const attrName = containsMatch[1];
|
|
142
|
+
const attrValue = escapeCssString(containsMatch[3]);
|
|
143
|
+
attrSelectors.push(`[${attrName}*='${attrValue}']`);
|
|
144
|
+
}
|
|
145
|
+
else if (/^\d+$/.test(inner)) {
|
|
146
|
+
nthIndex = inner;
|
|
147
|
+
}
|
|
148
|
+
else if (positionMatch) {
|
|
149
|
+
nthIndex = positionMatch[1];
|
|
150
|
+
}
|
|
151
|
+
else if (/^last\(\)$/i.test(inner)) {
|
|
152
|
+
nthIndex = 'last';
|
|
153
|
+
}
|
|
154
|
+
// Unrecognized predicates (e.g. text()-based) are skipped: they have
|
|
155
|
+
// no reliable CSS equivalent and are not safe to guess at.
|
|
156
|
+
}
|
|
157
|
+
let css = tag;
|
|
158
|
+
css += attrSelectors.join('');
|
|
159
|
+
if (nthIndex === 'last') {
|
|
160
|
+
css += ':nth-last-of-type(1)';
|
|
161
|
+
}
|
|
162
|
+
else if (nthIndex) {
|
|
163
|
+
css += `:nth-of-type(${nthIndex})`;
|
|
164
|
+
}
|
|
165
|
+
return css;
|
|
166
|
+
}
|
|
167
|
+
function escapeCssString(value) {
|
|
168
|
+
return value.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
169
|
+
}
|
|
@@ -32,6 +32,9 @@ function getWebXPathByFFIndex(annotatedDom, fireflinkIndex) {
|
|
|
32
32
|
}
|
|
33
33
|
function getFFIndexAndXPath(annotatedDom, fireflinkIndex, platform) {
|
|
34
34
|
try {
|
|
35
|
+
if (annotatedDom?.includes("ff-inspect")) {
|
|
36
|
+
return getWebXPathByFFIndex(annotatedDom, fireflinkIndex);
|
|
37
|
+
}
|
|
35
38
|
const parser = new xmldom_1.DOMParser();
|
|
36
39
|
const xmlDoc = parser.parseFromString(annotatedDom, "text/xml");
|
|
37
40
|
let xpathQuery = "";
|
|
@@ -4,7 +4,7 @@ exports.findElementUsingFF = void 0;
|
|
|
4
4
|
const appendScriptData_js_1 = require("../../fireflinkData/fireflinkScript/appendScriptData.js");
|
|
5
5
|
const switchToIframeByMeta_js_1 = require("./switchToIframeByMeta.js");
|
|
6
6
|
const findElementUsingFF = async (args) => {
|
|
7
|
-
await args.driver.
|
|
7
|
+
await args.driver.switchFrame(null);
|
|
8
8
|
const usedElementNames = [];
|
|
9
9
|
for (const frameMeta of args.frames) {
|
|
10
10
|
const xpathUsed = await (0, switchToIframeByMeta_js_1.switchToIframeByMeta)(args.driver, frameMeta);
|
|
@@ -27,7 +27,6 @@ const findElementUsingFF = async (args) => {
|
|
|
27
27
|
stepInputs: [],
|
|
28
28
|
pageDOM: args.pageDOM,
|
|
29
29
|
elementGetter: args.elementGetter,
|
|
30
|
-
ffNumber: args.fireflinkIndex,
|
|
31
30
|
elementName: finalElementName,
|
|
32
31
|
elementType: args.elementType,
|
|
33
32
|
selector: xpathUsed,
|
|
@@ -15,7 +15,9 @@ port.on("message", (data) => {
|
|
|
15
15
|
(0, logData_js_1.initLogger)(sessionPath);
|
|
16
16
|
loggerInitialized = true;
|
|
17
17
|
}
|
|
18
|
-
const formattedAttribute = attribute.
|
|
18
|
+
const formattedAttribute = attribute.startsWith("[")
|
|
19
|
+
? attribute.replace("[", "//*[@")
|
|
20
|
+
: attribute;
|
|
19
21
|
const result = (0, getElementsFromHTML_js_1.getAiElementsFromHTML)({
|
|
20
22
|
locators: [
|
|
21
23
|
{
|