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.
Files changed (55) hide show
  1. package/dist/ai/llmprompts/llmPromptTypes/systemPromptTypes.d.ts +2 -0
  2. package/dist/ai/llmprompts/promptRegistry.js +4 -0
  3. package/dist/ai/llmprompts/systemPrompts/multichannelKeywordExtractor.d.ts +6 -0
  4. package/dist/ai/llmprompts/systemPrompts/multichannelKeywordExtractor.js +86 -0
  5. package/dist/ai/llmprompts/userPrompts/userPrompt.js +1 -0
  6. package/dist/automation/actions/interaction/clear/clear.js +4 -1
  7. package/dist/automation/actions/interaction/clear/clearAndEnter.js +4 -1
  8. package/dist/automation/actions/interaction/elementlessActions/getOtp.js +2 -2
  9. package/dist/automation/actions/interaction/enterActions/EnterInputIntoElementFromClipBoardInterface_mob.js +4 -1
  10. package/dist/automation/actions/interaction/enterActions/enterInput.js +5 -2
  11. package/dist/automation/actions/interaction/enterActions/enterInputAndPress.js +4 -1
  12. package/dist/automation/actions/interaction/enterActions/waitAndEnter.js +4 -1
  13. package/dist/automation/actions/interaction/get/getText.js +9 -2
  14. package/dist/automation/actions/interaction/iframe/switchToDefaultContent.js +1 -1
  15. package/dist/automation/actions/interaction/shadowDom/shadowDomUpload.js +88 -10
  16. package/dist/automation/actions/interaction/verify/VerifyElementNotContainsText.js +4 -1
  17. package/dist/automation/actions/interaction/verify/verifypartialtext.js +9 -2
  18. package/dist/automation/actions/interaction/verify/verifytext.js +9 -2
  19. package/dist/automation/actions/interface/FireFlinkResponseSchema.d.ts +30 -0
  20. package/dist/automation/actions/interface/FireFlinkResponseSchema.js +8 -1
  21. package/dist/core/helpers/elementSelectorResolver.d.ts +1 -1
  22. package/dist/core/helpers/elementSelectorResolver.js +3 -2
  23. package/dist/core/helpers/runnerDomPipeline.d.ts +2 -2
  24. package/dist/core/helpers/runnerDomPipeline.js +20 -9
  25. package/dist/core/helpers/visionFallbackHandler.js +4 -4
  26. package/dist/core/interfaces/automationRunnerInterface.d.ts +1 -0
  27. package/dist/core/interfaces/executionDetails.d.ts +2 -1
  28. package/dist/core/interfaces/fireflinkScriptPayloadInterface.d.ts +1 -3
  29. package/dist/core/interfaces/promptInterface.d.ts +5 -0
  30. package/dist/core/main/actionHandlerFactory.js +3 -3
  31. package/dist/core/main/executionContext.d.ts +2 -0
  32. package/dist/core/main/executionContext.js +18 -3
  33. package/dist/core/main/runAutomationScript.d.ts +2 -0
  34. package/dist/core/main/runAutomationScript.js +78 -20
  35. package/dist/core/types/promptType.d.ts +1 -0
  36. package/dist/core/types/promptType.js +1 -0
  37. package/dist/fireflinkData/fireflinkLocators/getListOfLocators.d.ts +2 -1
  38. package/dist/fireflinkData/fireflinkLocators/getListOfLocators.js +2 -2
  39. package/dist/fireflinkData/fireflinkScript/appendScriptData.js +61 -14
  40. package/dist/fireflinkData/fireflinkScript/scriptGenrationData.d.ts +1 -0
  41. package/dist/fireflinkData/fireflinkScript/scriptGenrationData.js +1 -0
  42. package/dist/imageAnalysisWeb/annotateScreenshot.d.ts +1 -1
  43. package/dist/imageAnalysisWeb/annotateScreenshot.js +24 -10
  44. package/dist/imageAnalysisWeb/generateAnnotatedScreenshots.js +5 -2
  45. package/dist/tests/senario.d.ts +0 -0
  46. package/dist/tests/senario.js +1120 -0
  47. package/dist/tests/test12.js +30 -62
  48. package/dist/utils/DomExtraction/jsForAttributeInjection.js +23 -0
  49. package/dist/utils/JsPathGenerator/generateJsPath.d.ts +2 -0
  50. package/dist/utils/JsPathGenerator/generateJsPath.js +169 -0
  51. package/dist/utils/helpers/xpathcreation.js +3 -0
  52. package/dist/utils/iframesExtraction/findElementUsingFF.js +1 -2
  53. package/dist/utils/iframesExtraction/switchToIframeByMeta.js +1 -1
  54. package/dist/utils/workers/locatorWorker.js +3 -1
  55. 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) {
@@ -52,6 +52,7 @@ export interface VisionFallbackParams {
52
52
  actionHandlers: Record<string, any>;
53
53
  reporter: RunnerKafkaReporter;
54
54
  causeError: any;
55
+ driverContext?: any;
55
56
  }
56
57
  export interface VisionFallbackResult {
57
58
  preCapturedDomInfo: any | null;
@@ -23,7 +23,7 @@ export interface AutomationRequest {
23
23
  userName: string;
24
24
  serverHost: string;
25
25
  browserName: BrowserName;
26
- platform: 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
- scriptType: string;
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.request.platform?.toLowerCase() === "ios";
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.request.platform.toLowerCase() === "ios" ? "IOS_CloseApp" : "MOB_CloseApp";
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.request.platform, context.adbPath);
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.toLowerCase();
24
+ this.activePlatform = (request.platform?.toLowerCase() || "web");
24
25
  }
25
26
  async initializeMobileDriver(caps) {
26
- const isIOS = this.request.platform.toLowerCase() === "ios";
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.request.platform, this.adbPath, this.stepsInputData);
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
- request.type = request.platform;
154
- request.platform = AutomationRunner.normalizeScriptType(request.platform);
155
- const isWebMobile = (request.type?.toLowerCase().includes("web") &&
156
- request.type?.toLowerCase().includes("android")) ||
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 data = request.scriptName ? JSON.parse(request.scriptName) : {};
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 = await stepProcessor.getLLMResponse({
230
- platform: currentPlatform,
231
- type: promptType_js_1.PromptType.KEYWORD_EXTRACTOR,
232
- args: { priorAndNextSteps },
233
- input: { currentStep: step },
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
- if (!driver) {
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 searchAttribute = args.platform === "web"
8
- ? `[ff-inspect="${args.ffNumber}"]`
9
- : `[FF-inspecter="${args.ffNumber}"]`;
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
- try {
22
- const ffElement = await args.elementGetter.getFireFlinkElement(args.pageDOM, searchAttribute);
23
- // locators = [{ name: "xpath", value: args.selector }];
24
- if (ffElement?.locators?.length === 0) {
25
- throw new Error("locators is not found");
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
- else {
28
- locators = ffElement.locators;
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,5 +1,6 @@
1
1
  import { IScriptGenerationData } from "../../core/interfaces/scriptGenrationDataInterface.js";
2
2
  export declare class ScriptDataAppender {
3
+ currentMobileView: "NATIVE" | "WEBVIEW";
3
4
  private data;
4
5
  private pending;
5
6
  private errorNLP;
@@ -4,6 +4,7 @@ exports.ScriptDataAppender = void 0;
4
4
  const platformSyntax_js_1 = require("../../utils/helpers/platformSyntax.js");
5
5
  class ScriptDataAppender {
6
6
  constructor() {
7
+ this.currentMobileView = "NATIVE";
7
8
  this.data = [];
8
9
  this.pending = [];
9
10
  this.errorNLP = "";
@@ -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
- const scale = image.width / 1920;
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
- const x1 = Math.floor(item.bbox.x);
85
- const y1 = Math.floor(item.bbox.y);
86
- const x2 = Math.floor(item.bbox.x + item.bbox.w);
87
- const y2 = Math.floor(item.bbox.y + item.bbox.h);
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