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
|
@@ -13,6 +13,7 @@ const getActionExtractorPrompt_js_1 = require("./systemPrompts/getActionExtracto
|
|
|
13
13
|
const verifyActionExtractorPromptMob_js_1 = require("./systemPrompts/verifyActionExtractorPromptMob.js");
|
|
14
14
|
const getActionExtractorPromptMob_js_1 = require("./systemPrompts/getActionExtractorPromptMob.js");
|
|
15
15
|
const mobileKeywordExtractor_js_1 = require("./systemPrompts/mobileKeywordExtractor.js");
|
|
16
|
+
const multichannelKeywordExtractor_js_1 = require("./systemPrompts/multichannelKeywordExtractor.js");
|
|
16
17
|
const combinedActionExtractorPromptMob_js_1 = require("./systemPrompts/combinedActionExtractorPromptMob.js");
|
|
17
18
|
const waitActionExtractorPromptMob_js_1 = require("./systemPrompts/waitActionExtractorPromptMob.js");
|
|
18
19
|
const waitActionExtractorPrompt_js_1 = require("./systemPrompts/waitActionExtractorPrompt.js");
|
|
@@ -25,6 +26,7 @@ exports.prompts = {
|
|
|
25
26
|
web: {
|
|
26
27
|
userStoryToList: userStoryToListPrompt_js_1.buildStepExtractionPrompt,
|
|
27
28
|
keywordExtractorPrompt: actionExtractorPrompt_js_1.keywordExtractor,
|
|
29
|
+
multichannelKeywordExtractorPrompt: multichannelKeywordExtractor_js_1.keywordExtractorMultichannel,
|
|
28
30
|
errorDescriptionPrompt: errorDescriptionPrompt_js_1.buildErrorDescriptionPrompt,
|
|
29
31
|
ffInspectorNumExtractor: fireflinkElementIndexExtactors_js_1.ffInspectorNumExtractor,
|
|
30
32
|
verifyActionExtractorPrompt: verifyActionExtractorPrompt_js_1.verifyActionExtractorPrompt,
|
|
@@ -40,6 +42,7 @@ exports.prompts = {
|
|
|
40
42
|
android: {
|
|
41
43
|
userStoryToList: userStoryToListPrompt_js_1.androidbuildStepExtractionPrompt,
|
|
42
44
|
keywordExtractorPrompt: mobileKeywordExtractor_js_1.keywordExtractorMobile,
|
|
45
|
+
multichannelKeywordExtractorPrompt: multichannelKeywordExtractor_js_1.keywordExtractorMultichannel,
|
|
43
46
|
errorDescriptionPrompt: errorDescriptionPrompt_js_1.buildErrorDescriptionPrompt,
|
|
44
47
|
ffInspectorNumExtractor: fireflinkElementIndexExtractor_Mob_js_1.ffInspectorNumExtractorMob,
|
|
45
48
|
verifyActionExtractorPrompt: verifyActionExtractorPromptMob_js_1.verifyActionExtractorPromptMob,
|
|
@@ -55,6 +58,7 @@ exports.prompts = {
|
|
|
55
58
|
ios: {
|
|
56
59
|
userStoryToList: userStoryToListPrompt_js_1.androidbuildStepExtractionPrompt,
|
|
57
60
|
keywordExtractorPrompt: mobileKeywordExtractor_js_1.keywordExtractorMobile,
|
|
61
|
+
multichannelKeywordExtractorPrompt: multichannelKeywordExtractor_js_1.keywordExtractorMultichannel,
|
|
58
62
|
errorDescriptionPrompt: errorDescriptionPrompt_js_1.buildErrorDescriptionPrompt,
|
|
59
63
|
ffInspectorNumExtractor: fireflinkElementIndexExtractor_Mob_js_1.ffInspectorNumExtractorMob,
|
|
60
64
|
verifyActionExtractorPrompt: verifyActionExtractorPromptMob_js_1.verifyActionExtractorPromptMob,
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { keywordExtractor } from "../llmPromptTypes/systemPromptTypes";
|
|
2
|
+
export type multichannelKeywordExtractor = keywordExtractor & {
|
|
3
|
+
allSteps: string[];
|
|
4
|
+
previousPlatform?: string | null;
|
|
5
|
+
};
|
|
6
|
+
export declare function keywordExtractorMultichannel({ priorAndNextSteps, allSteps, previousPlatform, }: multichannelKeywordExtractor): Promise<string>;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.keywordExtractorMultichannel = keywordExtractorMultichannel;
|
|
4
|
+
const actionExtractorPrompt_js_1 = require("./actionExtractorPrompt.js");
|
|
5
|
+
const mobileKeywordExtractor_js_1 = require("./mobileKeywordExtractor.js");
|
|
6
|
+
async function keywordExtractorMultichannel({ priorAndNextSteps, allSteps, previousPlatform, }) {
|
|
7
|
+
const webPrompt = await (0, actionExtractorPrompt_js_1.keywordExtractor)({ priorAndNextSteps });
|
|
8
|
+
const mobilePrompt = await (0, mobileKeywordExtractor_js_1.keywordExtractorMobile)({ priorAndNextSteps });
|
|
9
|
+
const allowedActions = [
|
|
10
|
+
"enter",
|
|
11
|
+
"refresh",
|
|
12
|
+
"wait",
|
|
13
|
+
"verify",
|
|
14
|
+
"scroll",
|
|
15
|
+
"navigate",
|
|
16
|
+
"navigateBack",
|
|
17
|
+
"click",
|
|
18
|
+
"maximize",
|
|
19
|
+
"minimize",
|
|
20
|
+
"get",
|
|
21
|
+
"upload",
|
|
22
|
+
"close",
|
|
23
|
+
"open",
|
|
24
|
+
"openApp",
|
|
25
|
+
"drag_and_drop",
|
|
26
|
+
"closeApp",
|
|
27
|
+
"switch",
|
|
28
|
+
"cleartext",
|
|
29
|
+
"tap",
|
|
30
|
+
"combined",
|
|
31
|
+
"mouseAction",
|
|
32
|
+
"switchToAndroid",
|
|
33
|
+
"switchToWeb",
|
|
34
|
+
"getOtp",
|
|
35
|
+
"enterOtp",
|
|
36
|
+
"arithmetic",
|
|
37
|
+
];
|
|
38
|
+
return `
|
|
39
|
+
You are extracting keywords for a MULTICHANNEL test. Platform is not provided by the user.
|
|
40
|
+
You MUST decide the platform for the CURRENT step and return it so downstream execution stays unchanged.
|
|
41
|
+
|
|
42
|
+
previousPlatform: ${previousPlatform ?? "none"}
|
|
43
|
+
allSteps (full script, use as context only): ${JSON.stringify(allSteps)}
|
|
44
|
+
nearbySteps: ${JSON.stringify(priorAndNextSteps)}
|
|
45
|
+
|
|
46
|
+
PLATFORM DECISION RULES (mandatory):
|
|
47
|
+
- platform MUST be exactly one of: "web" | "android" | "ios"
|
|
48
|
+
- Decide using the CURRENT step first, then allSteps for context, then previousPlatform.
|
|
49
|
+
- If the current step is in the middle of a flow and does not clearly name a platform, browser, URL, app, package, bundle, or device, you MUST reuse previousPlatform.
|
|
50
|
+
- If previousPlatform is "none" (first step), infer from the current step and allSteps:
|
|
51
|
+
- web: browser, URL, http/https, navigate, click, open browser, web page, tab, window
|
|
52
|
+
- android: Android app, APK, appPackage, appActivity, UiAutomator, Android device
|
|
53
|
+
- ios: iOS app, iPhone, IPA, bundle id, XCUITest, Safari on iOS
|
|
54
|
+
- Do not switch platform on generic UI verbs (tap/click/enter/verify/scroll) unless the step or surrounding allSteps clearly change channel.
|
|
55
|
+
- After you choose platform, follow THAT platform's keyword/action rules below. Do not mix web actions onto mobile or mobile actions onto web.
|
|
56
|
+
- web actions include: click, open, close, navigate, navigateBack, maximize, minimize, refresh, mouseAction, drag_and_drop, switch, cleartext, upload
|
|
57
|
+
- android/ios actions include: tap, openApp, closeApp, combined, enterOtp
|
|
58
|
+
- shared: enter, wait, verify, scroll, get, getOtp, arithmetic, switchToAndroid, switchToWeb
|
|
59
|
+
- action must be one of: ${JSON.stringify(allowedActions)}
|
|
60
|
+
|
|
61
|
+
===== WEB KEYWORD RULES (use when platform is web) =====
|
|
62
|
+
${webPrompt}
|
|
63
|
+
|
|
64
|
+
===== ANDROID / iOS KEYWORD RULES (use when platform is android or ios) =====
|
|
65
|
+
${mobilePrompt}
|
|
66
|
+
|
|
67
|
+
Respond ONLY with valid JSON in this EXACT format (this overrides the JSON formats in the sections above):
|
|
68
|
+
{
|
|
69
|
+
"steps": [
|
|
70
|
+
{
|
|
71
|
+
"platform": "web" | "android" | "ios",
|
|
72
|
+
"keywords": ["key1", "key2"],
|
|
73
|
+
"elementName": "element name",
|
|
74
|
+
"action": ${allowedActions.join("|")},
|
|
75
|
+
"isStepValueDependable": boolean,
|
|
76
|
+
"liveLog": "Short human-readable description of what the AI is about to do.",
|
|
77
|
+
"fileType": "pdf" | other lowercase file extension | null,
|
|
78
|
+
"step": "Single atomic instruction for this step."
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
Every object in "steps" MUST include platform. If you split one user step into multiple objects, keep the same platform unless the wording clearly switches channel.
|
|
84
|
+
No other text.
|
|
85
|
+
`;
|
|
86
|
+
}
|
|
@@ -6,6 +6,7 @@ const imageUtils_js_1 = require("../../../utils/helpers/imageUtils.js");
|
|
|
6
6
|
exports.userInputFormatters = {
|
|
7
7
|
[promptType_js_1.PromptType.USER_STORY_TO_LIST]: (userInput) => `Convert the following user story into steps:\n\n${JSON.stringify(userInput.userStory, null, 2)}`,
|
|
8
8
|
[promptType_js_1.PromptType.KEYWORD_EXTRACTOR]: (userInput) => `Extract keywords from the following input:\n\n${JSON.stringify(userInput.currentStep, null, 2)}`,
|
|
9
|
+
[promptType_js_1.PromptType.MULTICHANNEL_KEYWORD_EXTRACTOR]: (userInput) => `Extract keywords and decide platform for the following current step:\n\n${JSON.stringify(userInput.currentStep, null, 2)}`,
|
|
9
10
|
[promptType_js_1.PromptType.ERROR_DESCRIPTION]: (error) => `Analyze the following error context:\n\n${JSON.stringify(error.error, null, 2)}`,
|
|
10
11
|
[promptType_js_1.PromptType.FF_INSPECTOR]: (userInput) => `Inspect the DOM for the following action context:\n\n${JSON.stringify(userInput.currentStep, null, 2)}`,
|
|
11
12
|
[promptType_js_1.PromptType.VERIFY_PROMPT]: (userInput) => `Inspect the DOM for the following verify context:\n\n${JSON.stringify(userInput.currentStep, null, 2)}`,
|
|
@@ -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"
|
|
@@ -1,26 +1,104 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.uploadFileShadowElement = uploadFileShadowElement;
|
|
4
|
-
const jsForShadowRoot_js_1 = require("../../../../utils/javascript/jsForShadowRoot.js");
|
|
5
4
|
const appendScriptData_js_1 = require("../../../../fireflinkData/fireflinkScript/appendScriptData.js");
|
|
5
|
+
const generateJsPath_js_1 = require("../../../../utils/JsPathGenerator/generateJsPath.js");
|
|
6
|
+
const logData_js_1 = require("../../../../utils/logger/logData.js");
|
|
6
7
|
async function uploadFileShadowElement(args) {
|
|
7
8
|
try {
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const parts = args.selector.split("/shadow/");
|
|
10
|
+
let fileInput = null;
|
|
11
|
+
// Build shadow host chain ONCE — shared across all tiers (avoids duplicate traversal)
|
|
12
|
+
const shadowHosts = [];
|
|
13
|
+
try {
|
|
14
|
+
let el = await args.driver.$(parts[0]);
|
|
15
|
+
shadowHosts.push(el);
|
|
16
|
+
for (let i = 1; i < parts.length - 1; i++) {
|
|
17
|
+
el = await el.shadow$((0, generateJsPath_js_1.convertToCssSelector)(parts[i]));
|
|
18
|
+
shadowHosts.push(el);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// partial shadowHosts[] is still usable for whatever was resolved
|
|
23
|
+
}
|
|
24
|
+
const lastKnownHost = shadowHosts.length > 0 ? shadowHosts[shadowHosts.length - 1] : null;
|
|
25
|
+
// Tier 1 — shadow$() on deepest known host (1 level below last host)
|
|
26
|
+
if (lastKnownHost && await lastKnownHost.isExisting()) {
|
|
27
|
+
try {
|
|
28
|
+
const candidate = await lastKnownHost.shadow$('input[type="file"]');
|
|
29
|
+
if (candidate && await candidate.isExisting()) {
|
|
30
|
+
fileInput = candidate;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
// fall through to Tier 2
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Tier 2 — Option A: element-scoped >>> deep-pierce from last known host
|
|
38
|
+
if (!fileInput && lastKnownHost && await lastKnownHost.isExisting()) {
|
|
39
|
+
try {
|
|
40
|
+
const candidate = await lastKnownHost.$('>>>input[type="file"]');
|
|
41
|
+
if (candidate && await candidate.isExisting()) {
|
|
42
|
+
fileInput = candidate;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// fall through to Tier 3
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Tier 3 — backtrack deepest → outermost across all recorded shadow hosts
|
|
50
|
+
if (!fileInput) {
|
|
51
|
+
for (let i = shadowHosts.length - 1; i >= 0; i--) {
|
|
52
|
+
try {
|
|
53
|
+
const candidate = await shadowHosts[i].shadow$('input[type="file"]');
|
|
54
|
+
if (candidate && await candidate.isExisting()) {
|
|
55
|
+
fileInput = candidate;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// continue up the chain
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (!fileInput) {
|
|
65
|
+
throw new Error(`Shadow DOM file input not found. Selector: "${args.selector}"`);
|
|
12
66
|
}
|
|
13
67
|
const remoteFilePath = await args.driver.uploadFile(args.value);
|
|
14
|
-
|
|
15
|
-
|
|
68
|
+
try {
|
|
69
|
+
await fileInput.setValue(remoteFilePath);
|
|
70
|
+
}
|
|
71
|
+
catch (err) {
|
|
72
|
+
logData_js_1.logger.info(`setValue failed on shadow file input, trying addValue: ${err.message}`);
|
|
73
|
+
await fileInput.addValue(remoteFilePath);
|
|
74
|
+
}
|
|
75
|
+
//Dispatch change and input events so custom UI components update their state
|
|
76
|
+
await args.driver.execute((el) => {
|
|
77
|
+
if (el) {
|
|
78
|
+
el.dispatchEvent(new Event("input", { bubbles: true, composed: true }));
|
|
79
|
+
el.dispatchEvent(new Event("change", { bubbles: true, composed: true }));
|
|
80
|
+
}
|
|
81
|
+
}, fileInput);
|
|
82
|
+
logData_js_1.logger.info(`Successfully uploaded file into shadow DOM element`);
|
|
83
|
+
// Generate JSPath
|
|
84
|
+
const jsPath = (0, generateJsPath_js_1.generateJsPath)(args.selector, 'input[type="file"]');
|
|
85
|
+
console.warn("RAW JSPath:\n" + jsPath);
|
|
86
|
+
const ffElementIndex = `Fire-Flink-${args.fireflinkIndex}`;
|
|
16
87
|
(0, appendScriptData_js_1.appendScriptData)({
|
|
17
88
|
scriptDataAppender: args.scriptDataAppender,
|
|
18
|
-
nlpName: "
|
|
19
|
-
stepInputs: [
|
|
89
|
+
nlpName: "SendKeys",
|
|
90
|
+
stepInputs: [args.value],
|
|
91
|
+
pageDOM: args.pageDOM,
|
|
92
|
+
elementGetter: args.elementGetter,
|
|
93
|
+
ffNumber: ffElementIndex,
|
|
94
|
+
elementName: args.elementName,
|
|
95
|
+
elementType: "button",
|
|
96
|
+
selector: jsPath,
|
|
97
|
+
platform: args.platform
|
|
20
98
|
});
|
|
21
99
|
}
|
|
22
100
|
catch (error) {
|
|
23
|
-
args.scriptDataAppender.setErrorNLP("
|
|
101
|
+
args.scriptDataAppender.setErrorNLP("SendKeys");
|
|
24
102
|
throw new Error("Shadow Upload action failed", {
|
|
25
103
|
cause: error
|
|
26
104
|
});
|
|
@@ -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 });
|
|
@@ -19,6 +19,36 @@ export declare const KeywordSchema: z.ZodObject<{
|
|
|
19
19
|
step: z.ZodString;
|
|
20
20
|
}, z.core.$strip>>;
|
|
21
21
|
}, z.core.$strip>;
|
|
22
|
+
export declare const MultichannelStepItemSchema: z.ZodObject<{
|
|
23
|
+
keywords: z.ZodArray<z.ZodString>;
|
|
24
|
+
elementName: z.ZodString;
|
|
25
|
+
action: z.ZodString;
|
|
26
|
+
isStepValueDependable: z.ZodBoolean;
|
|
27
|
+
liveLog: z.ZodString;
|
|
28
|
+
fileType: z.ZodNullable<z.ZodString>;
|
|
29
|
+
step: z.ZodString;
|
|
30
|
+
platform: z.ZodEnum<{
|
|
31
|
+
web: "web";
|
|
32
|
+
android: "android";
|
|
33
|
+
ios: "ios";
|
|
34
|
+
}>;
|
|
35
|
+
}, z.core.$strip>;
|
|
36
|
+
export declare const MultichannelKeywordSchema: z.ZodObject<{
|
|
37
|
+
steps: z.ZodArray<z.ZodObject<{
|
|
38
|
+
keywords: z.ZodArray<z.ZodString>;
|
|
39
|
+
elementName: z.ZodString;
|
|
40
|
+
action: z.ZodString;
|
|
41
|
+
isStepValueDependable: z.ZodBoolean;
|
|
42
|
+
liveLog: z.ZodString;
|
|
43
|
+
fileType: z.ZodNullable<z.ZodString>;
|
|
44
|
+
step: z.ZodString;
|
|
45
|
+
platform: z.ZodEnum<{
|
|
46
|
+
web: "web";
|
|
47
|
+
android: "android";
|
|
48
|
+
ios: "ios";
|
|
49
|
+
}>;
|
|
50
|
+
}, z.core.$strip>>;
|
|
51
|
+
}, z.core.$strip>;
|
|
22
52
|
export declare const AlertShema: z.ZodObject<{
|
|
23
53
|
action: z.ZodString;
|
|
24
54
|
inputText: z.ZodString;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ArithmeticSchema = exports.InputContextSchema = exports.OtpExtractorSchema = exports.ErrorDescriptionSchema = exports.UserStorySchema = exports.VisionSchema = exports.DefaultInteractionSchema = exports.MouseActionSchema = exports.UploadActionSchema = exports.GetOtpActionSchema = exports.ClearTextActionSchema = exports.EnterActionSchema = exports.TapActionSchema = exports.ClickActionSchema = exports.SwitchActionSchema = exports.MobileScrollSchema = exports.WebScrollSchema = exports.DragDropActionSchema = exports.MobileCombinedSchema = exports.CombinedActionSchema = exports.WaitActionSchema = exports.MobileGetSchema = exports.MobileFileVerifySchema = exports.MobileVerifySchema = exports.WebGetSchema = exports.WebFileVerifySchema = exports.WebVerifySchema = exports.AlertShema = exports.KeywordSchema = exports.StepItemSchema = void 0;
|
|
3
|
+
exports.ArithmeticSchema = exports.InputContextSchema = exports.OtpExtractorSchema = exports.ErrorDescriptionSchema = exports.UserStorySchema = exports.VisionSchema = exports.DefaultInteractionSchema = exports.MouseActionSchema = exports.UploadActionSchema = exports.GetOtpActionSchema = exports.ClearTextActionSchema = exports.EnterActionSchema = exports.TapActionSchema = exports.ClickActionSchema = exports.SwitchActionSchema = exports.MobileScrollSchema = exports.WebScrollSchema = exports.DragDropActionSchema = exports.MobileCombinedSchema = exports.CombinedActionSchema = exports.WaitActionSchema = exports.MobileGetSchema = exports.MobileFileVerifySchema = exports.MobileVerifySchema = exports.WebGetSchema = exports.WebFileVerifySchema = exports.WebVerifySchema = exports.AlertShema = exports.MultichannelKeywordSchema = exports.MultichannelStepItemSchema = exports.KeywordSchema = exports.StepItemSchema = void 0;
|
|
4
4
|
exports.getSchemaForPrompt = getSchemaForPrompt;
|
|
5
5
|
const zod_1 = require("zod");
|
|
6
6
|
const promptType_js_1 = require("../../../core/types/promptType.js");
|
|
@@ -16,6 +16,12 @@ exports.StepItemSchema = zod_1.z.object({
|
|
|
16
16
|
exports.KeywordSchema = zod_1.z.object({
|
|
17
17
|
steps: zod_1.z.array(exports.StepItemSchema)
|
|
18
18
|
});
|
|
19
|
+
exports.MultichannelStepItemSchema = exports.StepItemSchema.extend({
|
|
20
|
+
platform: zod_1.z.enum(["web", "android", "ios"]),
|
|
21
|
+
});
|
|
22
|
+
exports.MultichannelKeywordSchema = zod_1.z.object({
|
|
23
|
+
steps: zod_1.z.array(exports.MultichannelStepItemSchema)
|
|
24
|
+
});
|
|
19
25
|
exports.AlertShema = zod_1.z.object({
|
|
20
26
|
action: zod_1.z.string(),
|
|
21
27
|
inputText: zod_1.z.string()
|
|
@@ -202,6 +208,7 @@ exports.ArithmeticSchema = zod_1.z.object({
|
|
|
202
208
|
});
|
|
203
209
|
const schemaMap = {
|
|
204
210
|
[promptType_js_1.PromptType.KEYWORD_EXTRACTOR]: exports.KeywordSchema,
|
|
211
|
+
[promptType_js_1.PromptType.MULTICHANNEL_KEYWORD_EXTRACTOR]: exports.MultichannelKeywordSchema,
|
|
205
212
|
[promptType_js_1.PromptType.VISION_PROMPT]: exports.VisionSchema,
|
|
206
213
|
[promptType_js_1.PromptType.USER_STORY_TO_LIST]: exports.UserStorySchema,
|
|
207
214
|
[promptType_js_1.PromptType.ERROR_DESCRIPTION]: exports.ErrorDescriptionSchema,
|
|
@@ -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,
|