ff-automationv2 2.2.33 → 2.2.34-beta.0
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/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/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/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/main/runAutomationScript.js +13 -3
- package/dist/fireflinkData/fireflinkLocators/getListOfLocators.d.ts +2 -1
- package/dist/fireflinkData/fireflinkLocators/getListOfLocators.js +2 -2
- package/dist/fireflinkData/fireflinkScript/appendScriptData.js +42 -6
- 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.d.ts +1 -0
- package/dist/tests/test12.js +34 -65
- package/dist/utils/DomExtraction/jsForAttributeInjection.js +23 -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
|
@@ -9,7 +9,10 @@ async function clear(args) {
|
|
|
9
9
|
case "android":
|
|
10
10
|
case "ios":
|
|
11
11
|
try {
|
|
12
|
-
const
|
|
12
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
13
|
+
const { ffNumber, xpathPath } = isWebView
|
|
14
|
+
? (0, enterActionHelper_js_1.enterActionHelper)(args.pageDOM, args.fireflinkIndex)
|
|
15
|
+
: (0, enterActionHelper_js_1.enterActionHelperMob)(args.pageDOM, args.fireflinkIndex);
|
|
13
16
|
await args.driver.$(xpathPath).clearValue();
|
|
14
17
|
const ffElementNumber = `Fire-Flink-${ffNumber}`;
|
|
15
18
|
(0, appendScriptData_js_1.appendScriptData)({
|
|
@@ -10,7 +10,10 @@ async function clearAndEnter(args) {
|
|
|
10
10
|
case "android":
|
|
11
11
|
case "ios":
|
|
12
12
|
try {
|
|
13
|
-
const
|
|
13
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
14
|
+
const { ffNumber, xpathPath } = isWebView
|
|
15
|
+
? (0, enterActionHelper_js_1.enterActionHelper)(args.pageDOM, args.fireflinkIndex)
|
|
16
|
+
: (0, enterActionHelper_js_1.enterActionHelperMob)(args.pageDOM, args.fireflinkIndex);
|
|
14
17
|
const element = args.driver.$(xpathPath);
|
|
15
18
|
await element.clearValue();
|
|
16
19
|
await element.setValue(args.value);
|
|
@@ -46,7 +46,7 @@ async function GetOtpAction(args) {
|
|
|
46
46
|
(0, appendScriptData_js_1.appendScriptData)({
|
|
47
47
|
scriptDataAppender: args.scriptDataAppender,
|
|
48
48
|
nlpName: "MOB_FetchOtpFromMessages",
|
|
49
|
-
stepInputs: [existingSms.address, timeFrom + "s", waitTime],
|
|
49
|
+
stepInputs: [existingSms.address, timeFrom + "s", waitTime, _regex],
|
|
50
50
|
platform: "android",
|
|
51
51
|
returnTypeVariableName: args.returnTypeVariableName
|
|
52
52
|
});
|
|
@@ -84,7 +84,7 @@ async function GetOtpAction(args) {
|
|
|
84
84
|
(0, appendScriptData_js_1.appendScriptData)({
|
|
85
85
|
scriptDataAppender: args.scriptDataAppender,
|
|
86
86
|
nlpName: "MOB_FetchOtpFromMessages",
|
|
87
|
-
stepInputs: [latestSms.address, timeFrom + "s", waitTime],
|
|
87
|
+
stepInputs: [latestSms.address, timeFrom + "s", waitTime, regex],
|
|
88
88
|
platform: "android",
|
|
89
89
|
returnTypeVariableName: args.returnTypeVariableName
|
|
90
90
|
});
|
|
@@ -7,7 +7,10 @@ const logData_js_1 = require("../../../../utils/logger/logData.js");
|
|
|
7
7
|
const saveStepInputData_js_1 = require("../../../../utils/helpers/saveStepInputData.js");
|
|
8
8
|
async function MOB_EnterInputIntoElementFromClipBoard(args) {
|
|
9
9
|
try {
|
|
10
|
-
const
|
|
10
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
11
|
+
const { ffNumber, xpathPath } = isWebView
|
|
12
|
+
? (0, enterActionHelper_js_1.enterActionHelper)(args.pageDOM, args.fireflinkIndex)
|
|
13
|
+
: (0, enterActionHelper_js_1.enterActionHelperMob)(args.pageDOM, args.fireflinkIndex);
|
|
11
14
|
const element = await args.driver.$(xpathPath);
|
|
12
15
|
const base64Value = await args.driver.getClipboard('plaintext');
|
|
13
16
|
const value = Buffer.from(base64Value, 'base64').toString('utf-8');
|
|
@@ -44,7 +44,10 @@ async function enterInput(args) {
|
|
|
44
44
|
case "android":
|
|
45
45
|
case "ios":
|
|
46
46
|
try {
|
|
47
|
-
const
|
|
47
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
48
|
+
const { ffNumber, xpathPath } = isWebView
|
|
49
|
+
? (0, enterActionHelper_js_1.enterActionHelper)(args.pageDOM, args.fireflinkIndex)
|
|
50
|
+
: (0, enterActionHelper_js_1.enterActionHelperMob)(args.pageDOM, args.fireflinkIndex);
|
|
48
51
|
const element = await args.driver.$(xpathPath);
|
|
49
52
|
if (args.stepIndex + 1 < args.filteredSteps.length &&
|
|
50
53
|
args.filteredSteps[args.stepIndex].toLowerCase().includes("enter")) {
|
|
@@ -54,7 +57,7 @@ async function enterInput(args) {
|
|
|
54
57
|
else {
|
|
55
58
|
await element.setValue(args.value);
|
|
56
59
|
}
|
|
57
|
-
if (args.platform === "android") {
|
|
60
|
+
if (args.platform === "android" && !isWebView) {
|
|
58
61
|
try {
|
|
59
62
|
const text = await element.getAttribute("text");
|
|
60
63
|
if (text !== args.value) {
|
|
@@ -8,7 +8,10 @@ async function enterInputAndPress(args) {
|
|
|
8
8
|
switch (args.platform) {
|
|
9
9
|
case "android":
|
|
10
10
|
try {
|
|
11
|
-
const
|
|
11
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
12
|
+
const { ffNumber, xpathPath } = isWebView
|
|
13
|
+
? (0, enterActionHelper_js_1.enterActionHelper)(args.pageDOM, args.fireflinkIndex)
|
|
14
|
+
: (0, enterActionHelper_js_1.enterActionHelperMob)(args.pageDOM, args.fireflinkIndex);
|
|
12
15
|
const element = await args.driver.$(xpathPath);
|
|
13
16
|
const parts = String(args.value).split(",");
|
|
14
17
|
const inputValue = parts[0]?.trim() || "";
|
|
@@ -19,7 +19,10 @@ async function waitAndEnter(args) {
|
|
|
19
19
|
// ---------------- ANDROID ----------------
|
|
20
20
|
case "android":
|
|
21
21
|
try {
|
|
22
|
-
const
|
|
22
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
23
|
+
const { ffNumber, xpathPath } = isWebView
|
|
24
|
+
? (0, enterActionHelper_js_1.enterActionHelper)(args.pageDOM, args.fireflinkIndex)
|
|
25
|
+
: (0, enterActionHelper_js_1.enterActionHelperMob)(args.pageDOM, args.fireflinkIndex);
|
|
23
26
|
const { inputValue, waitTimeSeconds } = parseValue(args.value);
|
|
24
27
|
const element = await args.driver.$(xpathPath);
|
|
25
28
|
// Wait before typing
|
|
@@ -11,8 +11,15 @@ const getText = async (args) => {
|
|
|
11
11
|
case "ios":
|
|
12
12
|
try {
|
|
13
13
|
const element = await args.driver.$(args.selector);
|
|
14
|
-
const
|
|
15
|
-
|
|
14
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
15
|
+
let _text = null;
|
|
16
|
+
if (isWebView) {
|
|
17
|
+
_text = await element.getText();
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
const attrName = args.platform === "ios" ? "value" : "text";
|
|
21
|
+
_text = await element.getAttribute(attrName);
|
|
22
|
+
}
|
|
16
23
|
logData_js_1.logger.info("Text", _text);
|
|
17
24
|
(0, saveStepInputData_js_1.saveStepInputData)(args, _text);
|
|
18
25
|
(0, appendScriptData_js_1.appendScriptData)({
|
|
@@ -4,7 +4,7 @@ exports.switchToDefaultContent = switchToDefaultContent;
|
|
|
4
4
|
const appendScriptData_js_1 = require("../../../../fireflinkData/fireflinkScript/appendScriptData.js");
|
|
5
5
|
async function switchToDefaultContent(args) {
|
|
6
6
|
try {
|
|
7
|
-
await args.driver.
|
|
7
|
+
await args.driver.switchFrame(null);
|
|
8
8
|
(0, appendScriptData_js_1.appendScriptData)({
|
|
9
9
|
scriptDataAppender: args.scriptDataAppender,
|
|
10
10
|
nlpName: "SwitchToDefaultContent"
|
|
@@ -7,7 +7,10 @@ async function verifyElementNotContainsText(args) {
|
|
|
7
7
|
case "android":
|
|
8
8
|
try {
|
|
9
9
|
const element = await args.driver.$(args.selector);
|
|
10
|
-
const
|
|
10
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
11
|
+
const expected = isWebView
|
|
12
|
+
? (await element.getText()) || ""
|
|
13
|
+
: (await element.getAttribute("text")) || "";
|
|
11
14
|
const caseSensitive = false;
|
|
12
15
|
if (expected == args.value) {
|
|
13
16
|
throw new Error(`Element do contain the text. Expected: not containing "${args.value}", Actual: contains "${expected}"`);
|
|
@@ -10,8 +10,15 @@ async function verifyPartialText(args) {
|
|
|
10
10
|
case "ios":
|
|
11
11
|
try {
|
|
12
12
|
const element = await args.driver.$(args.selector);
|
|
13
|
-
const
|
|
14
|
-
|
|
13
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
14
|
+
let actual;
|
|
15
|
+
if (isWebView) {
|
|
16
|
+
actual = (await element.getText()) || "";
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const attributeName = args.platform === "ios" ? "value" : "text";
|
|
20
|
+
actual = (await element.getAttribute(attributeName)) || "";
|
|
21
|
+
}
|
|
15
22
|
const expected = args.value;
|
|
16
23
|
if (!actual?.includes(expected)) {
|
|
17
24
|
throw new Error(`Element does not contain the expected text. Expected to contain: "${args.value}", Actual: "${actual}"`);
|
|
@@ -10,8 +10,15 @@ async function verifyText(args) {
|
|
|
10
10
|
case "ios":
|
|
11
11
|
try {
|
|
12
12
|
const element = await args.driver.$(args.selector);
|
|
13
|
-
const
|
|
14
|
-
|
|
13
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
14
|
+
let text;
|
|
15
|
+
if (isWebView) {
|
|
16
|
+
text = (await element.getText()) || "";
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const attributeName = args.platform === "ios" ? "value" : "text";
|
|
20
|
+
text = (await element.getAttribute(attributeName)) || "";
|
|
21
|
+
}
|
|
15
22
|
const expected = (args.value || "").toLowerCase();
|
|
16
23
|
const isMatched = text.toLowerCase().trim() === expected.trim();
|
|
17
24
|
logData_js_1.logger.info("text", { text });
|
|
@@ -3,7 +3,7 @@ import { ShadowIframeResult } from "../interfaces/automationRunnerInterface.js";
|
|
|
3
3
|
export { ShadowIframeResult };
|
|
4
4
|
export declare function generateUniqueName(currentName: string | undefined, usedNames: string[]): string | undefined;
|
|
5
5
|
export declare function normalizeSvgSelector(selector: string): string;
|
|
6
|
-
export declare function resolveElementSelector(doc: any, domInfo: any, fireflinkIndex: string, platform: PlatformType, driver: any): Promise<{
|
|
6
|
+
export declare function resolveElementSelector(doc: any, domInfo: any, fireflinkIndex: string, platform: PlatformType, driver: any, fallbackAttempt: number, driverContext?: any): Promise<{
|
|
7
7
|
selector: string;
|
|
8
8
|
doc: any;
|
|
9
9
|
domInfo: any;
|
|
@@ -31,11 +31,11 @@ function normalizeSvgSelector(selector) {
|
|
|
31
31
|
}
|
|
32
32
|
return selector;
|
|
33
33
|
}
|
|
34
|
-
async function resolveElementSelector(doc, domInfo, fireflinkIndex, platform, driver) {
|
|
34
|
+
async function resolveElementSelector(doc, domInfo, fireflinkIndex, platform, driver, fallbackAttempt, driverContext) {
|
|
35
35
|
let selector;
|
|
36
36
|
let updatedDoc = doc;
|
|
37
37
|
let updatedDomInfo = domInfo;
|
|
38
|
-
if (platform === "web") {
|
|
38
|
+
if (platform === "web" || (driverContext && fallbackAttempt > 1)) {
|
|
39
39
|
if (!updatedDoc) {
|
|
40
40
|
logData_js_1.logger.info("doc is undefined. Rebuilding document from domInfo.");
|
|
41
41
|
if (!updatedDomInfo || !updatedDomInfo.dom) {
|
|
@@ -57,6 +57,7 @@ async function resolveElementSelector(doc, domInfo, fireflinkIndex, platform, dr
|
|
|
57
57
|
selector = result.xpathPath;
|
|
58
58
|
}
|
|
59
59
|
selector = normalizeSvgSelector(selector);
|
|
60
|
+
selector = selector.replace(/\/div\[0\]\/div\[0\]/g, "/div[1]").replace(/\/div\[0\]/g, "/div[1]");
|
|
60
61
|
return { selector, doc: updatedDoc, domInfo: updatedDomInfo };
|
|
61
62
|
}
|
|
62
63
|
async function handleShadowAndIframe(driver, stepAction, selector, fireflinkIndex, domInfo, actionHandlers, elementName, elementType) {
|
|
@@ -4,6 +4,6 @@ import { DomCaptureResult, DomProcessResult } from "../interfaces/automationRunn
|
|
|
4
4
|
export { DomCaptureResult, DomProcessResult };
|
|
5
5
|
export declare class RunnerDomPipeline {
|
|
6
6
|
static parseDomDocument(domHtml: string, platform: PlatformType): any;
|
|
7
|
-
static captureAndParseDom(driver: any, platform: PlatformType, stepCount: number, action: string, isRetry?: boolean): Promise<DomCaptureResult>;
|
|
8
|
-
static processRelevantDom(domProcessor: DomProcessingEngine, domInfo: any, keywords: any, stepCount: number, action: string, currentStep: string, platform: PlatformType, driver: any, isRetry?: boolean): Promise<DomProcessResult>;
|
|
7
|
+
static captureAndParseDom(driver: any, platform: PlatformType, stepCount: number, action: string, isRetry?: boolean, driverContext?: any): Promise<DomCaptureResult>;
|
|
8
|
+
static processRelevantDom(domProcessor: DomProcessingEngine, domInfo: any, keywords: any, stepCount: number, action: string, currentStep: string, platform: PlatformType, driver: any, isRetry?: boolean, driverContext?: any): Promise<DomProcessResult>;
|
|
9
9
|
}
|
|
@@ -19,7 +19,7 @@ class RunnerDomPipeline {
|
|
|
19
19
|
return parser.window.document;
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
|
-
static async captureAndParseDom(driver, platform, stepCount, action, isRetry = false) {
|
|
22
|
+
static async captureAndParseDom(driver, platform, stepCount, action, isRetry = false, driverContext) {
|
|
23
23
|
try {
|
|
24
24
|
let domInfo;
|
|
25
25
|
let doc;
|
|
@@ -32,12 +32,23 @@ class RunnerDomPipeline {
|
|
|
32
32
|
doc = RunnerDomPipeline.parseDomDocument(domInfo.dom, "web");
|
|
33
33
|
}
|
|
34
34
|
else {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
if (driverContext) {
|
|
36
|
+
await driver.switchContext(driverContext);
|
|
37
|
+
domInfo = await (0, jsForAttributeInjection_js_1.getAnnotatedDOM)(driver);
|
|
38
|
+
const fileName = isRetry
|
|
39
|
+
? `annotated-dom-retry-webview-${stepCount}`
|
|
40
|
+
: `annotated-dom-${stepCount}`;
|
|
41
|
+
logData_js_1.logger.saveDOM(domInfo.dom, fileName);
|
|
42
|
+
doc = RunnerDomPipeline.parseDomDocument(domInfo.dom, "web");
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
domInfo = await (0, jsForAttributeInjection_js_1.getAnnotatedDOMForMobile)(driver);
|
|
46
|
+
const fileName = isRetry
|
|
47
|
+
? `annotated-dom-Mob-retry-${stepCount}`
|
|
48
|
+
: `annotated-dom-Mob-${stepCount}`;
|
|
49
|
+
logData_js_1.logger.saveDOM(domInfo.dom, fileName);
|
|
50
|
+
doc = RunnerDomPipeline.parseDomDocument(domInfo.dom, platform);
|
|
51
|
+
}
|
|
41
52
|
}
|
|
42
53
|
return { domInfo, doc };
|
|
43
54
|
}
|
|
@@ -48,11 +59,11 @@ class RunnerDomPipeline {
|
|
|
48
59
|
throw error;
|
|
49
60
|
}
|
|
50
61
|
}
|
|
51
|
-
static async processRelevantDom(domProcessor, domInfo, keywords, stepCount, action, currentStep, platform, driver, isRetry = false) {
|
|
62
|
+
static async processRelevantDom(domProcessor, domInfo, keywords, stepCount, action, currentStep, platform, driver, isRetry = false, driverContext) {
|
|
52
63
|
try {
|
|
53
64
|
let extractedRelevantDom;
|
|
54
65
|
let toonDom;
|
|
55
|
-
if (platform === "web") {
|
|
66
|
+
if (platform === "web" || driverContext) {
|
|
56
67
|
extractedRelevantDom = await domProcessor.webProcess({
|
|
57
68
|
keywords: keywords,
|
|
58
69
|
rawDom: domInfo?.dom,
|
|
@@ -10,7 +10,7 @@ const runnerPostActionHelper_js_1 = require("./runnerPostActionHelper.js");
|
|
|
10
10
|
const runnerDomPipeline_js_1 = require("./runnerDomPipeline.js");
|
|
11
11
|
const logData_js_1 = require("../../utils/logger/logData.js");
|
|
12
12
|
async function executeVisionFallback(params) {
|
|
13
|
-
const { driver, platform, action, step, stepAction, inputValue, stepCount, fallbackAttempt, priorAndNextSteps, listOfSteps, currentElementName, extractorElementName, stepElementType, iframeDomHtml, beforeHandles, context, stepProcessor, actionHandlers, reporter, causeError, } = params;
|
|
13
|
+
const { driver, platform, action, step, stepAction, inputValue, stepCount, fallbackAttempt, priorAndNextSteps, listOfSteps, currentElementName, extractorElementName, stepElementType, iframeDomHtml, beforeHandles, context, stepProcessor, actionHandlers, reporter, causeError, driverContext } = params;
|
|
14
14
|
let domInfo = params.domInfo;
|
|
15
15
|
let doc = params.doc;
|
|
16
16
|
logData_js_1.logger.error(`execution failed for step "${step}": ${causeError} and going for vision`);
|
|
@@ -20,7 +20,7 @@ async function executeVisionFallback(params) {
|
|
|
20
20
|
if (action === "scroll") {
|
|
21
21
|
throw new Error("Scroll action failed element not found", { cause: causeError });
|
|
22
22
|
}
|
|
23
|
-
if (platform === "web") {
|
|
23
|
+
if (platform === "web" || (driverContext && fallbackAttempt > 1)) {
|
|
24
24
|
annotatedScreenshot = await (0, generateAnnotatedScreenshots_js_1.generateAnnotatedScreenshots)(driver);
|
|
25
25
|
}
|
|
26
26
|
else {
|
|
@@ -36,7 +36,7 @@ async function executeVisionFallback(params) {
|
|
|
36
36
|
bounds = visionResult.boxes;
|
|
37
37
|
}
|
|
38
38
|
let stepResultVision;
|
|
39
|
-
if (platform === "web") {
|
|
39
|
+
if (platform === "web" || (driverContext && fallbackAttempt > 1)) {
|
|
40
40
|
stepResultVision = await stepProcessor.getLLMResponse({
|
|
41
41
|
platform,
|
|
42
42
|
type: promptType_js_1.PromptType.VISION_PROMPT,
|
|
@@ -73,7 +73,7 @@ async function executeVisionFallback(params) {
|
|
|
73
73
|
throw new Error(`vision unable to find element for step "${step}"`, { cause: causeError });
|
|
74
74
|
}
|
|
75
75
|
let xpath;
|
|
76
|
-
if (platform === "android" || platform === "ios") {
|
|
76
|
+
if ((platform === "android" || platform === "ios") && (driverContext && fallbackAttempt < 2)) {
|
|
77
77
|
const index = Number(fireflinkIndex.split("-").pop());
|
|
78
78
|
const box = bounds[index - 1];
|
|
79
79
|
if (!box) {
|
|
@@ -205,6 +205,7 @@ class AutomationRunner {
|
|
|
205
205
|
currentPlatform = context.activePlatform;
|
|
206
206
|
(0, platformContext_js_1.setPlatform)(currentPlatform);
|
|
207
207
|
let stepResult;
|
|
208
|
+
let driverContext = null;
|
|
208
209
|
let stepAction;
|
|
209
210
|
let fireflinkIndex;
|
|
210
211
|
let selector;
|
|
@@ -383,6 +384,8 @@ class AutomationRunner {
|
|
|
383
384
|
}
|
|
384
385
|
else {
|
|
385
386
|
driver = await context.mobileSession.getCurrentDriver();
|
|
387
|
+
const contexts = await driver.getContexts();
|
|
388
|
+
driverContext = contexts.find((context) => String(context).startsWith("WEBVIEW")) || null;
|
|
386
389
|
}
|
|
387
390
|
}
|
|
388
391
|
catch (error) {
|
|
@@ -518,10 +521,10 @@ class AutomationRunner {
|
|
|
518
521
|
selector = "";
|
|
519
522
|
iframeDomHtml = null;
|
|
520
523
|
beforeHandles = null;
|
|
521
|
-
const retryDomResult = await runnerDomPipeline_js_1.RunnerDomPipeline.captureAndParseDom(driver, currentPlatform, stepCount, action, true);
|
|
524
|
+
const retryDomResult = await runnerDomPipeline_js_1.RunnerDomPipeline.captureAndParseDom(driver, currentPlatform, stepCount, action, true, driverContext);
|
|
522
525
|
domInfo = retryDomResult.domInfo;
|
|
523
526
|
doc = retryDomResult.doc;
|
|
524
|
-
const retryRelevantResult = await runnerDomPipeline_js_1.RunnerDomPipeline.processRelevantDom(domProcessor, domInfo, keywords, stepCount, action, currentStep, currentPlatform, driver, true);
|
|
527
|
+
const retryRelevantResult = await runnerDomPipeline_js_1.RunnerDomPipeline.processRelevantDom(domProcessor, domInfo, keywords, stepCount, action, currentStep, currentPlatform, driver, true, driverContext);
|
|
525
528
|
extractedRelevantDom = retryRelevantResult.extractedRelevantDom;
|
|
526
529
|
const retryToonDom = retryRelevantResult.toonDom;
|
|
527
530
|
const retryActionResolution = await (0, stepActionInspector_js_1.resolveStepActionFromDom)(stepProcessor, currentPlatform, action, retryToonDom, priorAndNextSteps, currentStep, null);
|
|
@@ -560,7 +563,7 @@ class AutomationRunner {
|
|
|
560
563
|
logData_js_1.logger.error(`element not found for step "${step}"`);
|
|
561
564
|
throw new Error(`element not found for step "${step}"`);
|
|
562
565
|
}
|
|
563
|
-
const selectorResult = await (0, elementSelectorResolver_js_1.resolveElementSelector)(doc, domInfo, fireflinkIndex, currentPlatform, driver);
|
|
566
|
+
const selectorResult = await (0, elementSelectorResolver_js_1.resolveElementSelector)(doc, domInfo, fireflinkIndex, currentPlatform, driver, fallbackAttempt, driverContext);
|
|
564
567
|
selector = selectorResult.selector;
|
|
565
568
|
doc = selectorResult.doc;
|
|
566
569
|
domInfo = selectorResult.domInfo;
|
|
@@ -598,6 +601,9 @@ class AutomationRunner {
|
|
|
598
601
|
});
|
|
599
602
|
preCapturedDomInfo = await (0, runnerPostActionHelper_js_1.handlePostActionWeb)(currentPlatform, driver, action, beforeHandles, context.scriptAppender, stepCount);
|
|
600
603
|
if (fallbackAttempt === 2) {
|
|
604
|
+
if (driverContext) {
|
|
605
|
+
await driver.switchContext("NATIVE_APP");
|
|
606
|
+
}
|
|
601
607
|
await reporter.sendRetry("Retry successful. Moving on to the next step.");
|
|
602
608
|
}
|
|
603
609
|
break;
|
|
@@ -632,11 +638,15 @@ class AutomationRunner {
|
|
|
632
638
|
actionHandlers,
|
|
633
639
|
reporter,
|
|
634
640
|
causeError: error,
|
|
641
|
+
driverContext,
|
|
635
642
|
});
|
|
636
643
|
if (visionResult.retryNeeded) {
|
|
637
644
|
continue;
|
|
638
645
|
}
|
|
639
646
|
preCapturedDomInfo = visionResult.preCapturedDomInfo;
|
|
647
|
+
if (driverContext) {
|
|
648
|
+
await driver.switchContext("NATIVE_APP");
|
|
649
|
+
}
|
|
640
650
|
break;
|
|
641
651
|
}
|
|
642
652
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { platform } from "../../core/types/scriptTypesType.js";
|
|
1
2
|
import { LocatorWorkerPool } from "../../utils/workers/LocatorWorkerPool.js";
|
|
2
3
|
export declare class ElementGetter {
|
|
3
4
|
private readonly workerPool;
|
|
4
5
|
constructor(workerPool: LocatorWorkerPool);
|
|
5
|
-
getFireFlinkElement(html: string, attribute: string): Promise<any>;
|
|
6
|
+
getFireFlinkElement(html: string, attribute: string, targetPlatform?: platform): Promise<any>;
|
|
6
7
|
}
|
|
@@ -8,8 +8,8 @@ class ElementGetter {
|
|
|
8
8
|
constructor(workerPool) {
|
|
9
9
|
this.workerPool = workerPool;
|
|
10
10
|
}
|
|
11
|
-
async getFireFlinkElement(html, attribute) {
|
|
12
|
-
const platform = (0, platformContext_js_1.getPlatform)();
|
|
11
|
+
async getFireFlinkElement(html, attribute, targetPlatform) {
|
|
12
|
+
const platform = targetPlatform || (html?.includes("ff-inspect") ? "web" : (0, platformContext_js_1.getPlatform)());
|
|
13
13
|
// const formattedAttribute = attribute.replace('[', '//*[@');
|
|
14
14
|
try {
|
|
15
15
|
const result = await this.workerPool.getFireFlinkElement(html, attribute, platform);
|
|
@@ -3,10 +3,37 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.appendScriptData = appendScriptData;
|
|
4
4
|
const logData_js_1 = require("../../utils/logger/logData.js");
|
|
5
5
|
async function appendScriptData(args) {
|
|
6
|
+
const isMobile = args.platform === "android" || args.platform === "ios";
|
|
7
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
8
|
+
if (isMobile) {
|
|
9
|
+
if (args.scriptDataAppender.currentMobileView === undefined) {
|
|
10
|
+
args.scriptDataAppender.currentMobileView = "NATIVE";
|
|
11
|
+
}
|
|
12
|
+
if (isWebView && args.scriptDataAppender.currentMobileView !== "WEBVIEW") {
|
|
13
|
+
args.scriptDataAppender.add(async () => ({
|
|
14
|
+
nlpName: "MOB_SwitchToView",
|
|
15
|
+
stepInputs: ["WEBVIEW"],
|
|
16
|
+
elementsData: []
|
|
17
|
+
}));
|
|
18
|
+
args.scriptDataAppender.currentMobileView = "WEBVIEW";
|
|
19
|
+
}
|
|
20
|
+
else if (args.pageDOM && !isWebView && args.scriptDataAppender.currentMobileView === "WEBVIEW") {
|
|
21
|
+
args.scriptDataAppender.add(async () => ({
|
|
22
|
+
nlpName: "MOB_SwitchToNativeView",
|
|
23
|
+
stepInputs: [],
|
|
24
|
+
elementsData: []
|
|
25
|
+
}));
|
|
26
|
+
args.scriptDataAppender.currentMobileView = "NATIVE";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
6
29
|
args.scriptDataAppender.add(async () => {
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
30
|
+
const isWebView = args.pageDOM?.includes("ff-inspect");
|
|
31
|
+
const effectivePlatform = isWebView ? "web" : args.platform;
|
|
32
|
+
const searchAttribute = args.ffNumber
|
|
33
|
+
? (effectivePlatform === "web"
|
|
34
|
+
? `[ff-inspect="${args.ffNumber}"]`
|
|
35
|
+
: `[FF-inspecter="${args.ffNumber}"]`)
|
|
36
|
+
: args.selector;
|
|
10
37
|
const data = {
|
|
11
38
|
nlpName: args.nlpName
|
|
12
39
|
};
|
|
@@ -19,9 +46,12 @@ async function appendScriptData(args) {
|
|
|
19
46
|
if (args.elementName) {
|
|
20
47
|
let locators;
|
|
21
48
|
try {
|
|
22
|
-
|
|
49
|
+
if (!searchAttribute) {
|
|
50
|
+
throw new Error("searchAttribute is not found");
|
|
51
|
+
}
|
|
52
|
+
const ffElement = await args.elementGetter.getFireFlinkElement(args.pageDOM, searchAttribute, effectivePlatform);
|
|
23
53
|
// locators = [{ name: "xpath", value: args.selector }];
|
|
24
|
-
if (ffElement?.locators
|
|
54
|
+
if (!ffElement?.locators || ffElement.locators.length === 0) {
|
|
25
55
|
throw new Error("locators is not found");
|
|
26
56
|
}
|
|
27
57
|
else {
|
|
@@ -30,7 +60,13 @@ async function appendScriptData(args) {
|
|
|
30
60
|
}
|
|
31
61
|
catch (error) {
|
|
32
62
|
logData_js_1.logger.info(`Element is not found ${error.message}`);
|
|
33
|
-
locators = [{
|
|
63
|
+
locators = [{
|
|
64
|
+
name: "xpath",
|
|
65
|
+
type: "static",
|
|
66
|
+
value: args.selector,
|
|
67
|
+
isRecorded: "N",
|
|
68
|
+
isAiRecorded: "Y"
|
|
69
|
+
}];
|
|
34
70
|
}
|
|
35
71
|
data.elementsData = [{
|
|
36
72
|
name: args.elementName,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { NodeItem } from "./screenshotInterface.js";
|
|
2
|
-
export declare function annotateScreenshot(screenshotBase64: string, nodes: NodeItem[]): Promise<Buffer>;
|
|
2
|
+
export declare function annotateScreenshot(screenshotBase64: string, deviceWidth: number, deviceHeight: number, nodes: NodeItem[]): Promise<Buffer>;
|
|
@@ -59,10 +59,16 @@ function findFreeTagPosition(box, tagW, tagH, elementBoxes, usedTagRects, imgWid
|
|
|
59
59
|
usedTagRects.push([fallbackX, fallbackY, fallbackX + tagW, fallbackY + tagH]);
|
|
60
60
|
return { x: fallbackX, y: fallbackY };
|
|
61
61
|
}
|
|
62
|
-
async function annotateScreenshot(screenshotBase64, nodes) {
|
|
62
|
+
async function annotateScreenshot(screenshotBase64, deviceWidth, deviceHeight, nodes) {
|
|
63
63
|
const buffer = Buffer.from(screenshotBase64, "base64");
|
|
64
64
|
const image = await (0, canvas_1.loadImage)(buffer);
|
|
65
|
-
|
|
65
|
+
// Actual screenshot resolution
|
|
66
|
+
const screenshotWidth = image.width;
|
|
67
|
+
const screenshotHeight = image.height;
|
|
68
|
+
// Device / WebView resolution from which bbox coordinates are coming
|
|
69
|
+
const scaleX = screenshotWidth / deviceWidth;
|
|
70
|
+
const scaleY = screenshotHeight / deviceHeight;
|
|
71
|
+
const scale = Math.min(scaleX, scaleY);
|
|
66
72
|
const fontSize = Math.max(16, Math.floor(20 * scale));
|
|
67
73
|
const canvas = (0, canvas_1.createCanvas)(image.width, image.height);
|
|
68
74
|
const ctx = canvas.getContext("2d");
|
|
@@ -71,20 +77,28 @@ async function annotateScreenshot(screenshotBase64, nodes) {
|
|
|
71
77
|
ctx.textBaseline = "middle";
|
|
72
78
|
ctx.textAlign = "center";
|
|
73
79
|
const usedTagRects = [];
|
|
80
|
+
/*
|
|
81
|
+
* Convert all bounding boxes from device/WebView
|
|
82
|
+
* coordinates to screenshot coordinates.
|
|
83
|
+
*/
|
|
74
84
|
const elementBoxes = nodes.map((n) => [
|
|
75
|
-
Math.floor(n.bbox.x),
|
|
76
|
-
Math.floor(n.bbox.y),
|
|
77
|
-
Math.floor(n.bbox.x + n.bbox.w),
|
|
78
|
-
Math.floor(n.bbox.y + n.bbox.h),
|
|
85
|
+
Math.floor(n.bbox.x * scaleX),
|
|
86
|
+
Math.floor(n.bbox.y * scaleY),
|
|
87
|
+
Math.floor((n.bbox.x + n.bbox.w) * scaleX),
|
|
88
|
+
Math.floor((n.bbox.y + n.bbox.h) * scaleY),
|
|
79
89
|
]);
|
|
80
90
|
nodes.forEach((item, i) => {
|
|
81
91
|
const color = COLORS[i % COLORS.length];
|
|
82
92
|
const colorStr = `rgb(${color[0]},${color[1]},${color[2]})`;
|
|
83
93
|
const label = item.ff.split("-").pop() || "";
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
94
|
+
/*
|
|
95
|
+
* Convert current node bbox to actual
|
|
96
|
+
* screenshot coordinates.
|
|
97
|
+
*/
|
|
98
|
+
const x1 = Math.floor(item.bbox.x * scaleX);
|
|
99
|
+
const y1 = Math.floor(item.bbox.y * scaleY);
|
|
100
|
+
const x2 = Math.floor((item.bbox.x + item.bbox.w) * scaleX);
|
|
101
|
+
const y2 = Math.floor((item.bbox.y + item.bbox.h) * scaleY);
|
|
88
102
|
ctx.strokeStyle = colorStr;
|
|
89
103
|
ctx.lineWidth = Math.max(2, Math.floor(2 * scale));
|
|
90
104
|
ctx.strokeRect(x1, y1, x2 - x1, y2 - y1);
|
|
@@ -14,13 +14,16 @@ async function generateAnnotatedScreenshots(driver) {
|
|
|
14
14
|
// driver.execute(JS_PRIMARY),
|
|
15
15
|
// // driver.execute(JS_SECONDARY),
|
|
16
16
|
// ]);
|
|
17
|
+
const windowSize = await driver.getWindowSize();
|
|
18
|
+
const deviceWidth = windowSize.width;
|
|
19
|
+
const deviceHeight = windowSize.height;
|
|
17
20
|
const primary = primaryRaw ?? [];
|
|
18
21
|
let secondary = secondaryRaw ?? [];
|
|
19
22
|
const primaryIds = new Set(primary.map((item) => item.ff));
|
|
20
23
|
secondary = secondary.filter((item) => !primaryIds.has(item.ff));
|
|
21
24
|
const [primaryAnnotated, secondaryAnnotated] = await Promise.all([
|
|
22
|
-
(0, annotateScreenshot_js_1.annotateScreenshot)(screenshot, primary),
|
|
23
|
-
(0, annotateScreenshot_js_1.annotateScreenshot)(screenshot, secondary)
|
|
25
|
+
(0, annotateScreenshot_js_1.annotateScreenshot)(screenshot, deviceWidth, deviceHeight, primary),
|
|
26
|
+
(0, annotateScreenshot_js_1.annotateScreenshot)(screenshot, deviceWidth, deviceHeight, secondary)
|
|
24
27
|
]);
|
|
25
28
|
// Uncomment only when debugging
|
|
26
29
|
logData_js_1.logger.saveImage(primaryAnnotated, "annotated_primary.png");
|
|
File without changes
|