ff-automationv2 2.2.34-beta.3 → 2.2.35
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/automation/actions/executor.d.ts +1 -1
- package/dist/automation/actions/executor.js +3 -2
- package/dist/automation/actions/interaction/shadowDom/shadowDomUpload.js +6 -1
- package/dist/automation/actions/interaction/upload/uploadfile.js +6 -1
- package/dist/core/helpers/elementSelectorResolver.d.ts +1 -1
- package/dist/core/helpers/elementSelectorResolver.js +3 -1
- package/dist/core/main/actionHandlerFactory.js +1 -1
- package/dist/core/main/runAutomationScript.js +14 -5
- package/dist/tests/test12.d.ts +1 -0
- package/dist/tests/test12.js +34 -33
- package/dist/utils/helpers/uploadPathHelper.d.ts +1 -0
- package/dist/utils/helpers/uploadPathHelper.js +19 -0
- package/dist/utils/iframesExtraction/findElementUsingFF.js +15 -1
- package/dist/utils/iframesExtraction/iframeInterface.d.ts +1 -0
- package/package.json +1 -1
|
@@ -19,7 +19,7 @@ export declare class ActionExecutor implements IActionExecutor {
|
|
|
19
19
|
navigate(url: string): Promise<void>;
|
|
20
20
|
goBack(): Promise<void>;
|
|
21
21
|
refresh(): Promise<void>;
|
|
22
|
-
switchToFrame(selector: string, frames: FrameMeta[], elementName: string, elementType: string, pageDOM: string, fireflinkIndex: string, usedElementNames?: string[]): Promise<void>;
|
|
22
|
+
switchToFrame(selector: string, frames: FrameMeta[], elementName: string, elementType: string, pageDOM: string, fireflinkIndex: string, usedElementNames?: string[], skipRecording?: boolean): Promise<void>;
|
|
23
23
|
wait(time: string): Promise<void>;
|
|
24
24
|
click(pageDOM: string, selector: string, fireflinkIndex: string, elementName: string, elementType: string): Promise<void>;
|
|
25
25
|
clickUsingJS(pageDOM: string, selector: string, fireflinkIndex: string, elementName: string, elementType: string): Promise<void>;
|
|
@@ -424,7 +424,7 @@ class ActionExecutor {
|
|
|
424
424
|
throw error;
|
|
425
425
|
}
|
|
426
426
|
}
|
|
427
|
-
async switchToFrame(selector, frames, elementName, elementType, pageDOM, fireflinkIndex, usedElementNames) {
|
|
427
|
+
async switchToFrame(selector, frames, elementName, elementType, pageDOM, fireflinkIndex, usedElementNames, skipRecording) {
|
|
428
428
|
try {
|
|
429
429
|
await (0, findElementUsingFF_js_1.findElementUsingFF)({
|
|
430
430
|
driver: this.driver,
|
|
@@ -437,7 +437,8 @@ class ActionExecutor {
|
|
|
437
437
|
fireflinkIndex,
|
|
438
438
|
elementGetter: this.elementGetter,
|
|
439
439
|
platform: this.platform,
|
|
440
|
-
usedElementNames
|
|
440
|
+
usedElementNames,
|
|
441
|
+
skipRecording
|
|
441
442
|
});
|
|
442
443
|
}
|
|
443
444
|
catch (error) {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.uploadFileShadowElement = uploadFileShadowElement;
|
|
4
|
+
const uploadPathHelper_js_1 = require("../../../../utils/helpers/uploadPathHelper.js");
|
|
4
5
|
const appendScriptData_js_1 = require("../../../../fireflinkData/fireflinkScript/appendScriptData.js");
|
|
5
6
|
const generateJsPath_js_1 = require("../../../../utils/JsPathGenerator/generateJsPath.js");
|
|
6
7
|
const logData_js_1 = require("../../../../utils/logger/logData.js");
|
|
@@ -64,12 +65,16 @@ async function uploadFileShadowElement(args) {
|
|
|
64
65
|
if (!fileInput) {
|
|
65
66
|
throw new Error(`Shadow DOM file input not found. Selector: "${args.selector}"`);
|
|
66
67
|
}
|
|
67
|
-
|
|
68
|
+
let remoteFilePath;
|
|
68
69
|
try {
|
|
70
|
+
remoteFilePath = await args.driver.uploadFile((0, uploadPathHelper_js_1.resolveUploadPath)(args.value));
|
|
69
71
|
await fileInput.setValue(remoteFilePath);
|
|
70
72
|
}
|
|
71
73
|
catch (err) {
|
|
72
74
|
logData_js_1.logger.info(`setValue failed on shadow file input, trying addValue: ${err.message}`);
|
|
75
|
+
if (!remoteFilePath) {
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
73
78
|
await fileInput.addValue(remoteFilePath);
|
|
74
79
|
}
|
|
75
80
|
//Dispatch change and input events so custom UI components update their state
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.uploadFilePathUsingRobot = uploadFilePathUsingRobot;
|
|
4
|
+
const uploadPathHelper_js_1 = require("../../../../utils/helpers/uploadPathHelper.js");
|
|
4
5
|
const enterActionHelper_js_1 = require("../../../../utils/helpers/enterActionHelper.js");
|
|
5
6
|
const logData_js_1 = require("../../../../utils/logger/logData.js");
|
|
6
7
|
const appendScriptData_js_1 = require("../../../../fireflinkData/fireflinkScript/appendScriptData.js");
|
|
@@ -8,11 +9,15 @@ async function uploadFilePathUsingRobot(args) {
|
|
|
8
9
|
try {
|
|
9
10
|
const { ffNumber, xpathPath } = (0, enterActionHelper_js_1.enterActionHelper)(args.pageDOM, args.fireflinkIndex);
|
|
10
11
|
const fileInput = await args.driver.$(xpathPath);
|
|
11
|
-
|
|
12
|
+
let remoteFilePath;
|
|
12
13
|
try {
|
|
14
|
+
remoteFilePath = await args.driver.uploadFile((0, uploadPathHelper_js_1.resolveUploadPath)(args.value));
|
|
13
15
|
await fileInput.setValue(remoteFilePath);
|
|
14
16
|
}
|
|
15
17
|
catch (error) {
|
|
18
|
+
if (!remoteFilePath) {
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
16
21
|
logData_js_1.logger.info(`Error in setValue, using addValue instead: ${error.message}`);
|
|
17
22
|
await fileInput.addValue(remoteFilePath);
|
|
18
23
|
}
|
|
@@ -10,4 +10,4 @@ export declare function resolveElementSelector(doc: any, domInfo: any, fireflink
|
|
|
10
10
|
doc: any;
|
|
11
11
|
domInfo: any;
|
|
12
12
|
}>;
|
|
13
|
-
export declare function handleShadowAndIframe(driver: any, stepAction: string, selector: string, fireflinkIndex: string, domInfo: any, actionHandlers: Record<string, any>, elementName: string, elementType: string, usedElementNames?: string[]): Promise<ShadowIframeResult>;
|
|
13
|
+
export declare function handleShadowAndIframe(driver: any, stepAction: string, selector: string, fireflinkIndex: string, domInfo: any, actionHandlers: Record<string, any>, elementName: string, elementType: string, usedElementNames?: string[], skipRecording?: boolean): Promise<ShadowIframeResult>;
|
|
@@ -63,7 +63,7 @@ async function resolveElementSelector(doc, domInfo, fireflinkIndex, platform, dr
|
|
|
63
63
|
selector = selector.replace(/\/div\[0\]\/div\[0\]/g, "/div[1]").replace(/\/div\[0\]/g, "/div[1]");
|
|
64
64
|
return { selector, doc: updatedDoc, domInfo: updatedDomInfo };
|
|
65
65
|
}
|
|
66
|
-
async function handleShadowAndIframe(driver, stepAction, selector, fireflinkIndex, domInfo, actionHandlers, elementName, elementType, usedElementNames) {
|
|
66
|
+
async function handleShadowAndIframe(driver, stepAction, selector, fireflinkIndex, domInfo, actionHandlers, elementName, elementType, usedElementNames, skipRecording) {
|
|
67
67
|
let updatedStepAction = stepAction;
|
|
68
68
|
let updatedSelector = selector;
|
|
69
69
|
let iframeDomHtml = null;
|
|
@@ -83,6 +83,7 @@ async function handleShadowAndIframe(driver, stepAction, selector, fireflinkInde
|
|
|
83
83
|
pageDOM: domInfo.dom,
|
|
84
84
|
fireflinkIndex,
|
|
85
85
|
usedElementNames,
|
|
86
|
+
skipRecording,
|
|
86
87
|
});
|
|
87
88
|
return {
|
|
88
89
|
stepAction: updatedStepAction,
|
|
@@ -116,6 +117,7 @@ async function handleShadowAndIframe(driver, stepAction, selector, fireflinkInde
|
|
|
116
117
|
pageDOM: domInfo.dom,
|
|
117
118
|
fireflinkIndex,
|
|
118
119
|
usedElementNames,
|
|
120
|
+
skipRecording,
|
|
119
121
|
});
|
|
120
122
|
return {
|
|
121
123
|
stepAction: updatedStepAction,
|
|
@@ -389,7 +389,7 @@ function createActionHandlers(context) {
|
|
|
389
389
|
await executor.wait(result.value);
|
|
390
390
|
}),
|
|
391
391
|
switchToFrame: withExecutor(async (executor, result) => {
|
|
392
|
-
await executor.switchToFrame(result.selector, result.frames, result.elementName, result.elementType, result.pageDOM, result.fireflinkIndex, result.usedElementNames);
|
|
392
|
+
await executor.switchToFrame(result.selector, result.frames, result.elementName, result.elementType, result.pageDOM, result.fireflinkIndex, result.usedElementNames, result.skipRecording);
|
|
393
393
|
}),
|
|
394
394
|
Click: withExecutor(async (executor, result) => {
|
|
395
395
|
await executor.click(result.pageDOM, result.selector, result.fireflinkIndex, result.elementName, result.elementType);
|
|
@@ -110,12 +110,17 @@ class AutomationRunner {
|
|
|
110
110
|
if (context?.mobileSession) {
|
|
111
111
|
const mobileDriver = await context.mobileSession.getCurrentDriver().catch(() => null);
|
|
112
112
|
if (mobileDriver?.sessionId) {
|
|
113
|
-
if (
|
|
114
|
-
|
|
113
|
+
if (request.isCloud) {
|
|
114
|
+
if (currentPlatform === "ios") {
|
|
115
|
+
logData_js_1.logger.info("iOS session is not deleted");
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
await mobileDriver.deleteSession();
|
|
119
|
+
logData_js_1.logger.info("Mobile session deleted successfully");
|
|
120
|
+
}
|
|
115
121
|
}
|
|
116
122
|
else {
|
|
117
|
-
|
|
118
|
-
logData_js_1.logger.info("Mobile session deleted successfully");
|
|
123
|
+
logData_js_1.logger.info("Local mobile session kept alive.");
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
126
|
}
|
|
@@ -560,6 +565,7 @@ class AutomationRunner {
|
|
|
560
565
|
continue;
|
|
561
566
|
}
|
|
562
567
|
baseStepActionBeforeFallback = stepAction;
|
|
568
|
+
let iframeSwitchRecorded = false;
|
|
563
569
|
for (let fallbackAttempt = 1; fallbackAttempt <= 2; fallbackAttempt++) {
|
|
564
570
|
let shadowIframeResult = null;
|
|
565
571
|
try {
|
|
@@ -622,7 +628,10 @@ class AutomationRunner {
|
|
|
622
628
|
logData_js_1.logger.info(`Unable to resolve xpath for ${fireflinkIndex}`);
|
|
623
629
|
throw new Error(`Unable to resolve xpath for ${fireflinkIndex}`);
|
|
624
630
|
}
|
|
625
|
-
shadowIframeResult = await (0, elementSelectorResolver_js_1.handleShadowAndIframe)(driver, stepAction, selector, fireflinkIndex, domInfo, actionHandlers, currentElementName || result.response.elementName, stepResult.response.elementType, usedElementNames);
|
|
631
|
+
shadowIframeResult = await (0, elementSelectorResolver_js_1.handleShadowAndIframe)(driver, stepAction, selector, fireflinkIndex, domInfo, actionHandlers, currentElementName || result.response.elementName, stepResult.response.elementType, usedElementNames, iframeSwitchRecorded);
|
|
632
|
+
if (shadowIframeResult?.isIframe) {
|
|
633
|
+
iframeSwitchRecorded = true;
|
|
634
|
+
}
|
|
626
635
|
if (!shadowIframeResult.handled) {
|
|
627
636
|
return;
|
|
628
637
|
}
|
package/dist/tests/test12.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/tests/test12.js
CHANGED
|
@@ -1,34 +1,35 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
// import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
// import { verify } from "node:crypto";
|
|
7
|
+
const index_js_1 = require("../index.js");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const certPath = path_1.default.join("D:", "Bitbucket", "ai-chat-backend", "Certificates", "V3keystore.jks");
|
|
11
|
+
const ssl = {
|
|
12
|
+
ca: [fs_1.default.readFileSync(certPath, "utf-8")],
|
|
13
|
+
rejectUnauthorized: false,
|
|
14
|
+
};
|
|
15
|
+
const runner = new index_js_1.AutomationRunner();
|
|
16
|
+
runner.run({
|
|
17
|
+
hasMoreSteps: false,
|
|
18
|
+
userStory: [
|
|
19
|
+
["Launch the browser"],
|
|
20
|
+
["navigate to url https://www.indiamart.com"],
|
|
21
|
+
["wait for 20 seconds"],
|
|
22
|
+
["get otp from INDMRT message within 60 seconds and wait for 60 seconds"],
|
|
23
|
+
["Enter OTP into enter otp boxes"],
|
|
24
|
+
["wait for 10 seconds"],
|
|
25
|
+
["Close the browser"]
|
|
26
|
+
],
|
|
27
|
+
scenario: false,
|
|
28
|
+
sslConfig: ssl,
|
|
29
|
+
scriptType: "android",
|
|
30
|
+
broker: ["test3.fireflink.com:9092"], "scriptName": "script", "type": "Web & Mobile", "projectId": "PJT1006", "testCaseId": "SCR1806", kafkaMessageKey: 'lohit',
|
|
31
|
+
"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",
|
|
32
|
+
"serverHost": "https://sg-app.fireflink.com", "adbPath": "C:\\FireFlinkClient\\resources\\bin\\win\\android-sdk\\platform-tools\\adb.exe",
|
|
33
|
+
topic: "test-topic",
|
|
34
|
+
aiExecutionAction: ""
|
|
35
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const resolveUploadPath: (rawPath: string) => string;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveUploadPath = void 0;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const resolveUploadPath = (rawPath) => {
|
|
10
|
+
const filePath = node_path_1.default.normalize(rawPath.trim().replace(/^["']|["']$/g, ""));
|
|
11
|
+
if (!node_fs_1.default.existsSync(filePath)) {
|
|
12
|
+
throw new Error(`Upload file not found: "${filePath}"`);
|
|
13
|
+
}
|
|
14
|
+
if (!node_fs_1.default.statSync(filePath).isFile()) {
|
|
15
|
+
throw new Error(`Upload path is not a file: "${filePath}"`);
|
|
16
|
+
}
|
|
17
|
+
return filePath;
|
|
18
|
+
};
|
|
19
|
+
exports.resolveUploadPath = resolveUploadPath;
|
|
@@ -7,9 +7,23 @@ const elementSelectorResolver_js_1 = require("../../core/helpers/elementSelector
|
|
|
7
7
|
const findElementUsingFF = async (args) => {
|
|
8
8
|
await args.driver.switchFrame(null);
|
|
9
9
|
const usedElementNames = args.usedElementNames || [];
|
|
10
|
-
|
|
10
|
+
const uniqueFrames = [];
|
|
11
|
+
for (const frame of args.frames || []) {
|
|
12
|
+
const isDuplicate = uniqueFrames.some(existing => (frame.id && existing.id === frame.id) ||
|
|
13
|
+
(frame.name && existing.name === frame.name) ||
|
|
14
|
+
(frame.title && existing.title === frame.title) ||
|
|
15
|
+
(frame.aria && existing.aria === frame.aria) ||
|
|
16
|
+
(frame.src && existing.src === frame.src));
|
|
17
|
+
if (!isDuplicate) {
|
|
18
|
+
uniqueFrames.push(frame);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
for (const frameMeta of uniqueFrames) {
|
|
11
22
|
const xpathUsed = await (0, switchToIframeByMeta_js_1.switchToIframeByMeta)(args.driver, frameMeta);
|
|
12
23
|
if (xpathUsed) {
|
|
24
|
+
if (args.skipRecording) {
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
13
27
|
const baseName = args.elementName ? args.elementName.trim().replace(/\s+/g, "-") : args.elementName;
|
|
14
28
|
const finalElementName = ((0, elementSelectorResolver_js_1.generateUniqueName)(baseName, usedElementNames, "-iframe") || baseName)?.replace(/\s+/g, "-");
|
|
15
29
|
(0, appendScriptData_js_1.appendScriptData)({
|