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
|
@@ -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) {
|
|
@@ -23,7 +23,7 @@ export interface AutomationRequest {
|
|
|
23
23
|
userName: string;
|
|
24
24
|
serverHost: string;
|
|
25
25
|
browserName: BrowserName;
|
|
26
|
-
platform
|
|
26
|
+
platform?: platform;
|
|
27
27
|
token: string;
|
|
28
28
|
appiumPort: number;
|
|
29
29
|
realDeviceId: string;
|
|
@@ -38,4 +38,5 @@ export interface AutomationRequest {
|
|
|
38
38
|
adbPath?: string;
|
|
39
39
|
topic: string;
|
|
40
40
|
aiExecutionAction: string;
|
|
41
|
+
hasMoreSteps: boolean;
|
|
41
42
|
}
|
|
@@ -9,8 +9,7 @@ export interface IStep {
|
|
|
9
9
|
}
|
|
10
10
|
export interface IPayload {
|
|
11
11
|
scriptName: string;
|
|
12
|
-
|
|
13
|
-
type?: string;
|
|
12
|
+
type: string;
|
|
14
13
|
projectId?: string;
|
|
15
14
|
testCaseId?: string;
|
|
16
15
|
pageDetails?: any;
|
|
@@ -20,7 +19,6 @@ export interface IPayload {
|
|
|
20
19
|
projectType?: string;
|
|
21
20
|
tokensConsumed?: number;
|
|
22
21
|
error?: IErrorInfo;
|
|
23
|
-
platform?: string;
|
|
24
22
|
serviceProvider?: string;
|
|
25
23
|
model?: string;
|
|
26
24
|
}
|
|
@@ -4,6 +4,11 @@ export interface PromptArgsMap {
|
|
|
4
4
|
[PromptType.KEYWORD_EXTRACTOR]: {
|
|
5
5
|
priorAndNextSteps: string[];
|
|
6
6
|
};
|
|
7
|
+
[PromptType.MULTICHANNEL_KEYWORD_EXTRACTOR]: {
|
|
8
|
+
priorAndNextSteps: string[];
|
|
9
|
+
allSteps: string[];
|
|
10
|
+
previousPlatform?: string | null;
|
|
11
|
+
};
|
|
7
12
|
[PromptType.ERROR_DESCRIPTION]: Record<string, any>;
|
|
8
13
|
[PromptType.FF_INSPECTOR]: {
|
|
9
14
|
stepAction: string;
|
|
@@ -265,7 +265,7 @@ function createActionHandlers(context) {
|
|
|
265
265
|
},
|
|
266
266
|
openApp: async (result) => {
|
|
267
267
|
try {
|
|
268
|
-
const isIOS = context.
|
|
268
|
+
const isIOS = context.activePlatform === "ios";
|
|
269
269
|
const appPackage = result.keywords
|
|
270
270
|
.find((item) => item.startsWith("appPackage:"))
|
|
271
271
|
?.substring("appPackage:".length);
|
|
@@ -340,7 +340,7 @@ function createActionHandlers(context) {
|
|
|
340
340
|
closeApp: async () => {
|
|
341
341
|
try {
|
|
342
342
|
await context.mobileSession.close();
|
|
343
|
-
const nlpName = context.
|
|
343
|
+
const nlpName = context.activePlatform === "ios" ? "IOS_CloseApp" : "MOB_CloseApp";
|
|
344
344
|
context.scriptAppender.add(async () => ({
|
|
345
345
|
nlpName: nlpName,
|
|
346
346
|
elementsData: [],
|
|
@@ -357,7 +357,7 @@ function createActionHandlers(context) {
|
|
|
357
357
|
if (!context.browserOpened) {
|
|
358
358
|
logData_js_1.logger.info("Browser not initialized, auto-opening before navigate...");
|
|
359
359
|
await context.session.open();
|
|
360
|
-
context.executor = new executor_js_1.ActionExecutor(await context.session.getCurrentDriver(), context.scriptAppender, context.locator, context.
|
|
360
|
+
context.executor = new executor_js_1.ActionExecutor(await context.session.getCurrentDriver(), context.scriptAppender, context.locator, context.activePlatform, context.adbPath);
|
|
361
361
|
context.browserOpened = true;
|
|
362
362
|
context.scriptAppender.add(async () => ({
|
|
363
363
|
nlpName: "Open Browser",
|
|
@@ -18,6 +18,8 @@ export declare class ExecutionContext {
|
|
|
18
18
|
activePlatform: platform;
|
|
19
19
|
browserOpened: boolean;
|
|
20
20
|
stepsInputData: any[];
|
|
21
|
+
allManualSteps: string[];
|
|
21
22
|
constructor(request: AutomationRequest, workerPool: LocatorWorkerPool);
|
|
22
23
|
initializeMobileDriver(caps: any): Promise<void>;
|
|
24
|
+
syncExecutorToActivePlatform(): Promise<void>;
|
|
23
25
|
}
|
|
@@ -15,15 +15,16 @@ class ExecutionContext {
|
|
|
15
15
|
this.executor = null;
|
|
16
16
|
this.browserOpened = false;
|
|
17
17
|
this.stepsInputData = [];
|
|
18
|
+
this.allManualSteps = [];
|
|
18
19
|
this.session = new initiateBrowserSession_js_1.BrowserSession(browserType_js_1.browserName[request.browserName], request.isCloud, request.capabilities, defaultValues_js_1.DEFAULT_PAGE_LOAD, request.cloudConfig);
|
|
19
20
|
this.locator = new getListOfLocators_js_1.ElementGetter(workerPool);
|
|
20
21
|
this.scriptAppender = new scriptGenrationData_js_1.ScriptDataAppender();
|
|
21
22
|
this.mobileSession = new initiateMobileSession_js_1.MobileSession(request.appiumPort, request.isCloud, request.capabilities, request.cloudConfig, request.adbPath);
|
|
22
23
|
this.adbPath = request.adbPath || "";
|
|
23
|
-
this.activePlatform = request.platform
|
|
24
|
+
this.activePlatform = (request.platform?.toLowerCase() || "web");
|
|
24
25
|
}
|
|
25
26
|
async initializeMobileDriver(caps) {
|
|
26
|
-
const isIOS = this.
|
|
27
|
+
const isIOS = this.activePlatform === "ios";
|
|
27
28
|
const appiumCaps = {};
|
|
28
29
|
const allowedCapabilities = [
|
|
29
30
|
"app",
|
|
@@ -107,7 +108,21 @@ class ExecutionContext {
|
|
|
107
108
|
...appiumCaps
|
|
108
109
|
};
|
|
109
110
|
await this.mobileSession.createDriver(capabilities);
|
|
110
|
-
this.executor = new executor_js_1.ActionExecutor(await this.mobileSession.getCurrentDriver(), this.scriptAppender, this.locator, this.
|
|
111
|
+
this.executor = new executor_js_1.ActionExecutor(await this.mobileSession.getCurrentDriver(), this.scriptAppender, this.locator, this.activePlatform, this.adbPath, this.stepsInputData);
|
|
112
|
+
}
|
|
113
|
+
async syncExecutorToActivePlatform() {
|
|
114
|
+
try {
|
|
115
|
+
if (this.activePlatform === "web") {
|
|
116
|
+
const webDriver = await this.session.getCurrentDriver();
|
|
117
|
+
this.executor = new executor_js_1.ActionExecutor(webDriver, this.scriptAppender, this.locator, "web", this.adbPath, this.stepsInputData);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const mobileDriver = await this.mobileSession.getCurrentDriver();
|
|
121
|
+
this.executor = new executor_js_1.ActionExecutor(mobileDriver, this.scriptAppender, this.locator, this.activePlatform, this.adbPath, this.stepsInputData);
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
// Driver for the detected platform may not exist yet (open/openApp comes later).
|
|
125
|
+
}
|
|
111
126
|
}
|
|
112
127
|
}
|
|
113
128
|
exports.ExecutionContext = ExecutionContext;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import "../../utils/sslConfig/apiCallPatch.js";
|
|
2
2
|
import { IAutomationRunner } from "../interfaces/automationRunnerInterface.js";
|
|
3
3
|
import { AutomationRequest } from "../../core/interfaces/executionDetails.js";
|
|
4
|
+
import { platform } from "../types/scriptTypesType.js";
|
|
4
5
|
export declare class AutomationRunner implements IAutomationRunner {
|
|
5
6
|
static sessionTerminationDetails: Record<string, boolean>;
|
|
6
7
|
static sessionNames: Record<string, string>;
|
|
@@ -9,6 +10,7 @@ export declare class AutomationRunner implements IAutomationRunner {
|
|
|
9
10
|
static updateSessionTerminationInfo(testCaseId: string): void;
|
|
10
11
|
static sessionName(testCaseId: string, sessionName: string): void;
|
|
11
12
|
static getSessionName(testCaseId: string): string;
|
|
13
|
+
static resolveDetectedPlatform(detectedPlatform?: string, previousPlatform?: platform): platform;
|
|
12
14
|
static normalizeScriptType(scriptType?: string): string;
|
|
13
15
|
private cleanup;
|
|
14
16
|
private saveSessionStepData;
|
|
@@ -24,7 +24,6 @@ const isAlertPresent_js_1 = require("../../utils/alertPopup/isAlertPresent.js");
|
|
|
24
24
|
const logData_js_1 = require("../../utils/logger/logData.js");
|
|
25
25
|
const sameActionsHelper_js_1 = require("../../utils/helpers/sameActionsHelper.js");
|
|
26
26
|
const messageType_js_1 = require("../types/messageType.js");
|
|
27
|
-
const platformSyntax_js_1 = require("../../utils/helpers/platformSyntax.js");
|
|
28
27
|
const driverState_js_1 = require("../../automation/driverState.js");
|
|
29
28
|
const SessionManager_js_1 = require("./SessionManager.js");
|
|
30
29
|
const runnerKafkaReporter_js_1 = require("../helpers/runnerKafkaReporter.js");
|
|
@@ -49,6 +48,18 @@ class AutomationRunner {
|
|
|
49
48
|
static getSessionName(testCaseId) {
|
|
50
49
|
return AutomationRunner.sessionNames[testCaseId];
|
|
51
50
|
}
|
|
51
|
+
static resolveDetectedPlatform(detectedPlatform, previousPlatform) {
|
|
52
|
+
const value = (detectedPlatform ?? "").trim().toLowerCase();
|
|
53
|
+
if (value === "ios")
|
|
54
|
+
return "ios";
|
|
55
|
+
if (value === "android")
|
|
56
|
+
return "android";
|
|
57
|
+
if (value === "web")
|
|
58
|
+
return "web";
|
|
59
|
+
if (previousPlatform)
|
|
60
|
+
return previousPlatform;
|
|
61
|
+
return "web";
|
|
62
|
+
}
|
|
52
63
|
static normalizeScriptType(scriptType) {
|
|
53
64
|
const rawValue = (scriptType ?? "Web").trim();
|
|
54
65
|
if (!rawValue) {
|
|
@@ -150,15 +161,16 @@ class AutomationRunner {
|
|
|
150
161
|
const usedElementNames = [];
|
|
151
162
|
const usedVariableNames = [];
|
|
152
163
|
(0, logData_js_1.initLogger)(request.sessionPath);
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
164
|
+
const isMultichannel = request.type?.toLowerCase().includes("Multi-Channel");
|
|
165
|
+
if (!isMultichannel) {
|
|
166
|
+
request.platform = AutomationRunner.normalizeScriptType(request.platform);
|
|
167
|
+
}
|
|
168
|
+
const isWebMobile = isMultichannel ||
|
|
169
|
+
(request.type?.toLowerCase().includes("web") &&
|
|
170
|
+
request.type?.toLowerCase().includes("android")) ||
|
|
157
171
|
(request.type?.toLowerCase() === "android");
|
|
158
172
|
logData_js_1.logger.info("User Given Step", request.userStory);
|
|
159
|
-
const
|
|
160
|
-
const hasMoreSteps = data?.hasMoreSteps ?? false;
|
|
161
|
-
request.scriptName = data?.automationScriptName ?? "automationScriptName";
|
|
173
|
+
const hasMoreSteps = request.hasMoreSteps;
|
|
162
174
|
if (!request.isTerminate)
|
|
163
175
|
AutomationRunner.sessionName(request.testCaseId, request.scriptName);
|
|
164
176
|
if (request.isTerminate) {
|
|
@@ -170,6 +182,7 @@ class AutomationRunner {
|
|
|
170
182
|
const instanceDetails = await fireflinkServiceInstance.runGetInstancesDetails(request.projectId, request.token, request.serverHost, request.licenseId);
|
|
171
183
|
const aiInfo = (0, getInstancesInfo_js_1.getAiInstanceInfo)(instanceDetails.responseObject);
|
|
172
184
|
let context = SessionManager_js_1.SessionManager.get(request);
|
|
185
|
+
const hadExistingSession = Boolean(context);
|
|
173
186
|
if (!context) {
|
|
174
187
|
context = SessionManager_js_1.SessionManager.create(request);
|
|
175
188
|
}
|
|
@@ -183,11 +196,13 @@ class AutomationRunner {
|
|
|
183
196
|
const stepProcessor = new stepProcessor_js_1.StepProcessor(llm);
|
|
184
197
|
let currentPlatform = context.activePlatform;
|
|
185
198
|
(0, platformContext_js_1.setPlatform)(currentPlatform);
|
|
199
|
+
let previousDetectedPlatform = isMultichannel && hadExistingSession ? context.activePlatform : undefined;
|
|
186
200
|
const actionHandlers = (0, actionHandlerFactory_js_1.createActionHandlers)(context);
|
|
187
201
|
const domProcessor = new getRelaventElements_js_1.DomProcessingEngine();
|
|
188
202
|
let stepCount = 1;
|
|
189
203
|
const stepsInputData = context.stepsInputData || (context.stepsInputData = []);
|
|
190
204
|
const listOfSteps = request.userStory.map((step) => String(step));
|
|
205
|
+
context.allManualSteps.push(...listOfSteps);
|
|
191
206
|
let driver;
|
|
192
207
|
if (listOfSteps.length === 0 && request?.aiExecutionAction !== "stopExecution") {
|
|
193
208
|
logData_js_1.logger.info(listOfSteps);
|
|
@@ -205,6 +220,7 @@ class AutomationRunner {
|
|
|
205
220
|
currentPlatform = context.activePlatform;
|
|
206
221
|
(0, platformContext_js_1.setPlatform)(currentPlatform);
|
|
207
222
|
let stepResult;
|
|
223
|
+
let driverContext = null;
|
|
208
224
|
let stepAction;
|
|
209
225
|
let fireflinkIndex;
|
|
210
226
|
let selector;
|
|
@@ -226,14 +242,40 @@ class AutomationRunner {
|
|
|
226
242
|
const start = Math.max(0, index - 3);
|
|
227
243
|
const end = Math.min(listOfSteps.length, index + 4);
|
|
228
244
|
const priorAndNextSteps = listOfSteps.slice(start, end);
|
|
229
|
-
const result =
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
245
|
+
const result = isMultichannel
|
|
246
|
+
? await stepProcessor.getLLMResponse({
|
|
247
|
+
platform: currentPlatform || "web",
|
|
248
|
+
type: promptType_js_1.PromptType.MULTICHANNEL_KEYWORD_EXTRACTOR,
|
|
249
|
+
args: {
|
|
250
|
+
priorAndNextSteps,
|
|
251
|
+
allSteps: context.allManualSteps,
|
|
252
|
+
previousPlatform: previousDetectedPlatform ?? null,
|
|
253
|
+
},
|
|
254
|
+
input: { currentStep: step },
|
|
255
|
+
})
|
|
256
|
+
: await stepProcessor.getLLMResponse({
|
|
257
|
+
platform: currentPlatform,
|
|
258
|
+
type: promptType_js_1.PromptType.KEYWORD_EXTRACTOR,
|
|
259
|
+
args: { priorAndNextSteps },
|
|
260
|
+
input: { currentStep: step },
|
|
261
|
+
});
|
|
262
|
+
if (isMultichannel) {
|
|
263
|
+
const firstStepPlatform = AutomationRunner.resolveDetectedPlatform(result.response?.steps?.[0]?.platform ?? result.response?.platform, previousDetectedPlatform);
|
|
264
|
+
currentPlatform = firstStepPlatform;
|
|
265
|
+
previousDetectedPlatform = firstStepPlatform;
|
|
266
|
+
context.activePlatform = firstStepPlatform;
|
|
267
|
+
context.request.platform = firstStepPlatform;
|
|
268
|
+
(0, platformContext_js_1.setPlatform)(firstStepPlatform);
|
|
269
|
+
}
|
|
235
270
|
if (currentPlatform === "android" || currentPlatform === "ios") {
|
|
236
|
-
|
|
271
|
+
let mobileDriver = null;
|
|
272
|
+
try {
|
|
273
|
+
mobileDriver = await context.mobileSession.getCurrentDriver();
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
mobileDriver = null;
|
|
277
|
+
}
|
|
278
|
+
if (!mobileDriver) {
|
|
237
279
|
const initialKeywords = result.response?.keywords ||
|
|
238
280
|
(Array.isArray(result.response?.steps) ? result.response.steps[0]?.keywords : undefined);
|
|
239
281
|
await context.initializeMobileDriver(initialKeywords);
|
|
@@ -252,6 +294,15 @@ class AutomationRunner {
|
|
|
252
294
|
const stepObj = stepsArray[i];
|
|
253
295
|
const action = stepObj.action;
|
|
254
296
|
const keywords = stepObj.keywords;
|
|
297
|
+
if (isMultichannel) {
|
|
298
|
+
const stepPlatform = AutomationRunner.resolveDetectedPlatform(stepObj.platform, previousDetectedPlatform);
|
|
299
|
+
currentPlatform = stepPlatform;
|
|
300
|
+
previousDetectedPlatform = stepPlatform;
|
|
301
|
+
context.activePlatform = stepPlatform;
|
|
302
|
+
context.request.platform = stepPlatform;
|
|
303
|
+
(0, platformContext_js_1.setPlatform)(stepPlatform);
|
|
304
|
+
await context.syncExecutorToActivePlatform();
|
|
305
|
+
}
|
|
255
306
|
const currentStep = action?.toLowerCase() === "getotp" ? step : stepObj.step || step;
|
|
256
307
|
let currentElementName = stepObj.elementName;
|
|
257
308
|
let currentVariableName;
|
|
@@ -383,6 +434,8 @@ class AutomationRunner {
|
|
|
383
434
|
}
|
|
384
435
|
else {
|
|
385
436
|
driver = await context.mobileSession.getCurrentDriver();
|
|
437
|
+
const contexts = await driver.getContexts();
|
|
438
|
+
driverContext = contexts.find((context) => String(context).startsWith("WEBVIEW")) || null;
|
|
386
439
|
}
|
|
387
440
|
}
|
|
388
441
|
catch (error) {
|
|
@@ -518,10 +571,10 @@ class AutomationRunner {
|
|
|
518
571
|
selector = "";
|
|
519
572
|
iframeDomHtml = null;
|
|
520
573
|
beforeHandles = null;
|
|
521
|
-
const retryDomResult = await runnerDomPipeline_js_1.RunnerDomPipeline.captureAndParseDom(driver, currentPlatform, stepCount, action, true);
|
|
574
|
+
const retryDomResult = await runnerDomPipeline_js_1.RunnerDomPipeline.captureAndParseDom(driver, currentPlatform, stepCount, action, true, driverContext);
|
|
522
575
|
domInfo = retryDomResult.domInfo;
|
|
523
576
|
doc = retryDomResult.doc;
|
|
524
|
-
const retryRelevantResult = await runnerDomPipeline_js_1.RunnerDomPipeline.processRelevantDom(domProcessor, domInfo, keywords, stepCount, action, currentStep, currentPlatform, driver, true);
|
|
577
|
+
const retryRelevantResult = await runnerDomPipeline_js_1.RunnerDomPipeline.processRelevantDom(domProcessor, domInfo, keywords, stepCount, action, currentStep, currentPlatform, driver, true, driverContext);
|
|
525
578
|
extractedRelevantDom = retryRelevantResult.extractedRelevantDom;
|
|
526
579
|
const retryToonDom = retryRelevantResult.toonDom;
|
|
527
580
|
const retryActionResolution = await (0, stepActionInspector_js_1.resolveStepActionFromDom)(stepProcessor, currentPlatform, action, retryToonDom, priorAndNextSteps, currentStep, null);
|
|
@@ -560,7 +613,7 @@ class AutomationRunner {
|
|
|
560
613
|
logData_js_1.logger.error(`element not found for step "${step}"`);
|
|
561
614
|
throw new Error(`element not found for step "${step}"`);
|
|
562
615
|
}
|
|
563
|
-
const selectorResult = await (0, elementSelectorResolver_js_1.resolveElementSelector)(doc, domInfo, fireflinkIndex, currentPlatform, driver);
|
|
616
|
+
const selectorResult = await (0, elementSelectorResolver_js_1.resolveElementSelector)(doc, domInfo, fireflinkIndex, currentPlatform, driver, fallbackAttempt, driverContext);
|
|
564
617
|
selector = selectorResult.selector;
|
|
565
618
|
doc = selectorResult.doc;
|
|
566
619
|
domInfo = selectorResult.domInfo;
|
|
@@ -598,6 +651,9 @@ class AutomationRunner {
|
|
|
598
651
|
});
|
|
599
652
|
preCapturedDomInfo = await (0, runnerPostActionHelper_js_1.handlePostActionWeb)(currentPlatform, driver, action, beforeHandles, context.scriptAppender, stepCount);
|
|
600
653
|
if (fallbackAttempt === 2) {
|
|
654
|
+
if (driverContext) {
|
|
655
|
+
await driver.switchContext("NATIVE_APP");
|
|
656
|
+
}
|
|
601
657
|
await reporter.sendRetry("Retry successful. Moving on to the next step.");
|
|
602
658
|
}
|
|
603
659
|
break;
|
|
@@ -632,11 +688,15 @@ class AutomationRunner {
|
|
|
632
688
|
actionHandlers,
|
|
633
689
|
reporter,
|
|
634
690
|
causeError: error,
|
|
691
|
+
driverContext,
|
|
635
692
|
});
|
|
636
693
|
if (visionResult.retryNeeded) {
|
|
637
694
|
continue;
|
|
638
695
|
}
|
|
639
696
|
preCapturedDomInfo = visionResult.preCapturedDomInfo;
|
|
697
|
+
if (driverContext) {
|
|
698
|
+
await driver.switchContext("NATIVE_APP");
|
|
699
|
+
}
|
|
640
700
|
break;
|
|
641
701
|
}
|
|
642
702
|
}
|
|
@@ -676,7 +736,6 @@ class AutomationRunner {
|
|
|
676
736
|
const payload = {
|
|
677
737
|
scriptName: AutomationRunner.getSessionName(request.testCaseId) ?? "SCRIPT_NAME",
|
|
678
738
|
type: request.type,
|
|
679
|
-
scriptType: request.type,
|
|
680
739
|
projectId: request.projectId,
|
|
681
740
|
testCaseId: request.testCaseId,
|
|
682
741
|
pageDetails: request.pageDetails,
|
|
@@ -684,7 +743,6 @@ class AutomationRunner {
|
|
|
684
743
|
licenseId: request.licenseId,
|
|
685
744
|
userId: request.userId,
|
|
686
745
|
projectType: request.projectType,
|
|
687
|
-
platform: (0, platformSyntax_js_1.getPlatformSyntax)(currentPlatform),
|
|
688
746
|
tokensConsumed: (await stepProcessor.getResultTokenUsage()).totalTokens,
|
|
689
747
|
};
|
|
690
748
|
this.KafkaPayload.topic = request.topic;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export declare enum PromptType {
|
|
2
2
|
USER_STORY_TO_LIST = "userStoryToList",
|
|
3
3
|
KEYWORD_EXTRACTOR = "keywordExtractorPrompt",
|
|
4
|
+
MULTICHANNEL_KEYWORD_EXTRACTOR = "multichannelKeywordExtractorPrompt",
|
|
4
5
|
ERROR_DESCRIPTION = "errorDescriptionPrompt",
|
|
5
6
|
FF_INSPECTOR = "ffInspectorNumExtractor",
|
|
6
7
|
VISION_PROMPT = "visionPrompt",
|
|
@@ -5,6 +5,7 @@ var PromptType;
|
|
|
5
5
|
(function (PromptType) {
|
|
6
6
|
PromptType["USER_STORY_TO_LIST"] = "userStoryToList";
|
|
7
7
|
PromptType["KEYWORD_EXTRACTOR"] = "keywordExtractorPrompt";
|
|
8
|
+
PromptType["MULTICHANNEL_KEYWORD_EXTRACTOR"] = "multichannelKeywordExtractorPrompt";
|
|
8
9
|
PromptType["ERROR_DESCRIPTION"] = "errorDescriptionPrompt";
|
|
9
10
|
PromptType["FF_INSPECTOR"] = "ffInspectorNumExtractor";
|
|
10
11
|
PromptType["VISION_PROMPT"] = "visionPrompt";
|
|
@@ -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
|
};
|
|
@@ -18,20 +45,40 @@ async function appendScriptData(args) {
|
|
|
18
45
|
}
|
|
19
46
|
if (args.elementName) {
|
|
20
47
|
let locators;
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
48
|
+
const isJsPath = args.selector?.includes(".shadowRoot") || args.selector?.startsWith("document.");
|
|
49
|
+
if (isJsPath) {
|
|
50
|
+
locators = [{
|
|
51
|
+
name: "JsPath",
|
|
52
|
+
type: "Static",
|
|
53
|
+
value: args.selector,
|
|
54
|
+
isRecorded: "N",
|
|
55
|
+
isAiRecorded: "Y"
|
|
56
|
+
}];
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
try {
|
|
60
|
+
if (!searchAttribute) {
|
|
61
|
+
throw new Error("searchAttribute is not found");
|
|
62
|
+
}
|
|
63
|
+
const ffElement = await args.elementGetter.getFireFlinkElement(args.pageDOM, searchAttribute, effectivePlatform);
|
|
64
|
+
if (!ffElement?.locators || ffElement.locators.length === 0) {
|
|
65
|
+
throw new Error("locators is not found");
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
locators = ffElement.locators;
|
|
69
|
+
}
|
|
26
70
|
}
|
|
27
|
-
|
|
28
|
-
|
|
71
|
+
catch (error) {
|
|
72
|
+
logData_js_1.logger.info(`Element is not found ${error.message}`);
|
|
73
|
+
locators = [{
|
|
74
|
+
name: "xpath",
|
|
75
|
+
type: "static",
|
|
76
|
+
value: args.selector,
|
|
77
|
+
isRecorded: "N",
|
|
78
|
+
isAiRecorded: "Y"
|
|
79
|
+
}];
|
|
29
80
|
}
|
|
30
81
|
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
logData_js_1.logger.info(`Element is not found ${error.message}`);
|
|
33
|
-
locators = [{ name: "xpath", value: args.selector }];
|
|
34
|
-
}
|
|
35
82
|
data.elementsData = [{
|
|
36
83
|
name: args.elementName,
|
|
37
84
|
type: args.elementType,
|
|
@@ -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
|