ff-automationv2 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +112 -0
- package/FireFlink_Architecture.drawio +68 -0
- package/ONBOARDING.md +29 -0
- package/README.md +28 -0
- package/TECHNICAL_DEEP_DIVE.md +26 -0
- package/eslint.config.ts +29 -0
- package/package.json +51 -0
- package/src/ai/llmcalls/decodeApiKey.ts +14 -0
- package/src/ai/llmcalls/llmAction.ts +89 -0
- package/src/ai/llmcalls/parseLlmOputput.ts +69 -0
- package/src/ai/llmprompts/promptRegistry.ts +16 -0
- package/src/ai/llmprompts/systemPrompts/actionExtractorPrompt.ts +70 -0
- package/src/ai/llmprompts/systemPrompts/errorDescriptionPrompt.ts +23 -0
- package/src/ai/llmprompts/systemPrompts/fireflinkElementIndexExtactors.ts +198 -0
- package/src/ai/llmprompts/systemPrompts/userStoryToListPrompt.ts +24 -0
- package/src/ai/llmprompts/systemPrompts/visionPrompt.ts +28 -0
- package/src/ai/llmprompts/userPrompts/userPrompt.ts +41 -0
- package/src/automation/actions/executor.ts +75 -0
- package/src/automation/actions/interaction/click.ts +25 -0
- package/src/automation/actions/interaction/enterInput.ts +27 -0
- package/src/automation/actions/interface/interactionActionInterface.ts +27 -0
- package/src/automation/actions/interface/navigationActionInterface.ts +22 -0
- package/src/automation/actions/interface/waitActionInterface.ts +6 -0
- package/src/automation/actions/navigation/getTitle.ts +9 -0
- package/src/automation/actions/navigation/goBack.ts +9 -0
- package/src/automation/actions/navigation/navigate.ts +10 -0
- package/src/automation/actions/navigation/refresh.ts +9 -0
- package/src/automation/actions/wait/wait.ts +10 -0
- package/src/automation/browserSession/initiateBrowserSession.ts +81 -0
- package/src/core/constants/supportedActions.ts +8 -0
- package/src/core/interfaces/StableDomInterface.ts +6 -0
- package/src/core/interfaces/actionInterface.ts +13 -0
- package/src/core/interfaces/automationRunnerInterface.ts +3 -0
- package/src/core/interfaces/browserCapabilitiesInterface.ts +5 -0
- package/src/core/interfaces/browserConfigurationInterface.ts +3 -0
- package/src/core/interfaces/domAnalysisInterface.ts +34 -0
- package/src/core/interfaces/executionDetails.ts +29 -0
- package/src/core/interfaces/fireflinkScriptPayloadInterface.ts +39 -0
- package/src/core/interfaces/llmConfigurationInterface.ts +3 -0
- package/src/core/interfaces/llmResponseInterface.ts +38 -0
- package/src/core/interfaces/promptInterface.ts +21 -0
- package/src/core/interfaces/scriptGenrationDataInterface.ts +16 -0
- package/src/core/interfaces/toolsInterface.ts +5 -0
- package/src/core/main/actionHandlerFactory.ts +86 -0
- package/src/core/main/executionContext.ts +18 -0
- package/src/core/main/runAutomationScript.ts +177 -0
- package/src/core/main/stepProcessor.ts +28 -0
- package/src/core/types/llmResponseType.ts +11 -0
- package/src/core/types/promptMap.ts +7 -0
- package/src/core/types/promptType.ts +7 -0
- package/src/core/types/visionllmInputType.ts +4 -0
- package/src/domAnalysis/getRelaventElements.ts +24 -0
- package/src/domAnalysis/relativeElementsFromDom.ts +94 -0
- package/src/domAnalysis/searchBest.ts +159 -0
- package/src/domAnalysis/simplifyAndFlatten.ts +118 -0
- package/src/fireflinkData/fireflinkLocators/elementsFromHTML.ts +656 -0
- package/src/fireflinkData/fireflinkLocators/getListOfLocators.ts +31 -0
- package/src/fireflinkData/fireflinkLocators/typeList.ts +36 -0
- package/src/fireflinkData/fireflinkScript/scriptGenrationData.ts +30 -0
- package/src/index.ts +5 -0
- package/src/llmConfig/llmConfiguration.ts +26 -0
- package/src/service/fireflinkApi.service.ts +46 -0
- package/src/service/scriptRunner.service.ts +83 -0
- package/src/utils/DomExtraction/jsForAttributeInjection.ts +254 -0
- package/src/utils/javascript/jsFindElement.ts +161 -0
- package/src/utils/javascript/jsForShadowRoot.ts +216 -0
- package/src/utils/javascript/jsForToaster.ts +60 -0
- package/src/utils/logger/logData.ts +36 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
type FFInspectorArgs = {
|
|
2
|
+
stepAction: string;
|
|
3
|
+
extractedDomJson: string;
|
|
4
|
+
priorAndNextSteps: string[];
|
|
5
|
+
isAlert?: boolean;
|
|
6
|
+
isDrag?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export async function ffInspectorNumExtractor({
|
|
9
|
+
stepAction,
|
|
10
|
+
extractedDomJson,
|
|
11
|
+
priorAndNextSteps,
|
|
12
|
+
isAlert = false,
|
|
13
|
+
isDrag = false,
|
|
14
|
+
}: FFInspectorArgs
|
|
15
|
+
): Promise<
|
|
16
|
+
string
|
|
17
|
+
> {
|
|
18
|
+
const clickActions = ["click", "doubleclick", "rightclick"];
|
|
19
|
+
const alertActions = [
|
|
20
|
+
"ClickOkOnAlertPopup",
|
|
21
|
+
"ClickCancelOnAlertPopup",
|
|
22
|
+
"EnterDataToAlert",
|
|
23
|
+
"GetTextPresentOnAlertPopup",
|
|
24
|
+
"VerifyAlertPopupIsDisplayed",
|
|
25
|
+
"VerifyAlertPopUpMessageContainsString",
|
|
26
|
+
"WaitTillAlertIsPresent",
|
|
27
|
+
"VerifyTextPresentOnAlertPopup"
|
|
28
|
+
];
|
|
29
|
+
const elementType = ["button", "link"]
|
|
30
|
+
|
|
31
|
+
let prompt;
|
|
32
|
+
|
|
33
|
+
// ---------------- ALERT ----------------
|
|
34
|
+
if (isAlert) {
|
|
35
|
+
prompt = `
|
|
36
|
+
You are given a Selenium test step that requires interaction with a browser alert popup.
|
|
37
|
+
|
|
38
|
+
Rules:
|
|
39
|
+
- if the alert requires entering text or verification comparison, extract that text.
|
|
40
|
+
- If no input is needed, set "input_text" to "None".
|
|
41
|
+
- If step verifies text presence → VerifyTextPresentOnAlertPopup.
|
|
42
|
+
- If step verifies text contains → VerifyAlertPopUpMessageContainsString.
|
|
43
|
+
- Do not add explanations or extra text.
|
|
44
|
+
- Return ONE action from this list only: ${JSON.stringify(alertActions)}.
|
|
45
|
+
- Output must be valid JSON.
|
|
46
|
+
Respond only with JSON using this format:
|
|
47
|
+
|
|
48
|
+
{
|
|
49
|
+
"action": x,
|
|
50
|
+
"input_text": "text to enter" | "None"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ---------------- DRAG & DROP ----------------
|
|
57
|
+
else if (isDrag) {
|
|
58
|
+
prompt = `
|
|
59
|
+
You are an intelligent assistant that extracts structured UI action data.
|
|
60
|
+
|
|
61
|
+
Given a structured UI JSON representation with uniquely identified elements
|
|
62
|
+
(ff-inspect values like Fire-Flink-1, Fire-Flink-2... in DOM order),
|
|
63
|
+
locate the most appropriate elements for a drag-and-drop action.
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
Rules:
|
|
68
|
+
- Step: for the given step
|
|
69
|
+
- Use the simplified JSON to find elements.
|
|
70
|
+
- Identify source (drag) and destination (drop).
|
|
71
|
+
- If not found, return Fire-Flink-0 for both.
|
|
72
|
+
- Extract element names from step if possible.
|
|
73
|
+
- Respond with JSON only.
|
|
74
|
+
Simplified JSON:
|
|
75
|
+
${extractedDomJson}
|
|
76
|
+
|
|
77
|
+
Respond only with JSON using this format:
|
|
78
|
+
{
|
|
79
|
+
"drag": "Fire-Flink-x",
|
|
80
|
+
"drop": "Fire-Flink-x",
|
|
81
|
+
"action": "Builtin_DragAndDropToElement",
|
|
82
|
+
"drag_element_name": "x",
|
|
83
|
+
"drop_element_name": "x"
|
|
84
|
+
}
|
|
85
|
+
`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ---------------- WAIT / SCROLL ----------------
|
|
89
|
+
else if (stepAction === "wait" || stepAction === "scroll") {
|
|
90
|
+
prompt = `
|
|
91
|
+
You are an intelligent assistant that extracts structured UI action data.
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
Rules:
|
|
96
|
+
- Step: for the given step
|
|
97
|
+
- Use context from test steps: ${JSON.stringify(priorAndNextSteps)}
|
|
98
|
+
- Use simplified JSON to find best FF-inspect match.
|
|
99
|
+
- Choose closest semantic + DOM-distance match.
|
|
100
|
+
- Default direction is down.
|
|
101
|
+
- Action:
|
|
102
|
+
- scroll → extract num_of_scrolls & direction
|
|
103
|
+
- wait → extract wait time (number only)
|
|
104
|
+
- No nulls; use empty strings if needed.
|
|
105
|
+
- The element type should be from this list ${elementType}
|
|
106
|
+
|
|
107
|
+
Simplified JSON:
|
|
108
|
+
${extractedDomJson}
|
|
109
|
+
|
|
110
|
+
Return ONLY valid JSON:
|
|
111
|
+
{
|
|
112
|
+
"attribute_value": "Fire-Flink-x",
|
|
113
|
+
"action": "x",
|
|
114
|
+
"input_text": "seconds in Interger | input text",
|
|
115
|
+
"keyword": "x",
|
|
116
|
+
"num_of_scrolls": "0",
|
|
117
|
+
"direction": "down",
|
|
118
|
+
"elementType":"x"
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ---------------- DEFAULT (CLICK / ENTER / UPLOAD etc.) ----------------
|
|
124
|
+
else {
|
|
125
|
+
prompt = `
|
|
126
|
+
You are a deterministic UI action extraction engine.
|
|
127
|
+
|
|
128
|
+
OBJECTIVE:
|
|
129
|
+
Identify the SINGLE best matching element from the Simplified JSON
|
|
130
|
+
for the given step and return structured automation data.
|
|
131
|
+
|
|
132
|
+
-----------------------------------------
|
|
133
|
+
|
|
134
|
+
CONTEXT STEPS:
|
|
135
|
+
${JSON.stringify(priorAndNextSteps)}
|
|
136
|
+
|
|
137
|
+
CLICK ACTION WORDS:
|
|
138
|
+
${JSON.stringify(clickActions)}
|
|
139
|
+
|
|
140
|
+
ALLOWED ELEMENT TYPES:
|
|
141
|
+
${elementType}
|
|
142
|
+
-----------------------------------------
|
|
143
|
+
|
|
144
|
+
CRITICAL MATCHING LOGIC:
|
|
145
|
+
|
|
146
|
+
1. First understand intent:
|
|
147
|
+
- click words → click
|
|
148
|
+
- enter/type/write → enter_text
|
|
149
|
+
- upload → upload
|
|
150
|
+
- drag_and_drop → drag_and_drop
|
|
151
|
+
|
|
152
|
+
2. For ENTER actions:
|
|
153
|
+
DO NOT pick first input blindly.
|
|
154
|
+
Score candidates using priority:
|
|
155
|
+
|
|
156
|
+
Priority Order:
|
|
157
|
+
(1) Exact label text match
|
|
158
|
+
(2) Placeholder match
|
|
159
|
+
(3) Name attribute semantic match
|
|
160
|
+
(4) ID semantic match
|
|
161
|
+
(5) type="email" if step contains "email"
|
|
162
|
+
(6) DOM sibling label relationship
|
|
163
|
+
|
|
164
|
+
Choose highest scoring match.
|
|
165
|
+
|
|
166
|
+
If NO strong semantic match → return:
|
|
167
|
+
attribute_value = "Fire-Flink-0"
|
|
168
|
+
|
|
169
|
+
3. For CLICK actions:
|
|
170
|
+
- Prefer exact text match
|
|
171
|
+
- If multiple, choose closest contextual match
|
|
172
|
+
- If icon + svg exists, prefer svg element
|
|
173
|
+
|
|
174
|
+
4. Never hallucinate.
|
|
175
|
+
5. Only use FF values from provided JSON.
|
|
176
|
+
6. No null values. Use empty string "".
|
|
177
|
+
7. Output must be valid JSON only.
|
|
178
|
+
|
|
179
|
+
-----------------------------------------
|
|
180
|
+
SIMPLIFIED JSON:
|
|
181
|
+
${extractedDomJson}
|
|
182
|
+
-----------------------------------------
|
|
183
|
+
|
|
184
|
+
Respond ONLY with JSON:
|
|
185
|
+
|
|
186
|
+
{
|
|
187
|
+
"attribute_value": "Fire-Flink-x",
|
|
188
|
+
"action": "click | enter | upload | drag_and_drop",
|
|
189
|
+
"input_text": "",
|
|
190
|
+
"keyword": "",
|
|
191
|
+
"num_of_scrolls": "0",
|
|
192
|
+
"direction": "down",
|
|
193
|
+
"elementType": ""
|
|
194
|
+
}
|
|
195
|
+
`;
|
|
196
|
+
}
|
|
197
|
+
return prompt;
|
|
198
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
export async function buildStepExtractionPrompt(
|
|
3
|
+
): Promise<string> {
|
|
4
|
+
const prompt = `
|
|
5
|
+
You are a structured automation assistant. Your task is to extract step data from a provided user story for web automation.
|
|
6
|
+
|
|
7
|
+
Requirements
|
|
8
|
+
Each step must be a single string (not a tuple or any other data type).
|
|
9
|
+
Preserve the original wording of each step.
|
|
10
|
+
You may rewrite for clarity or formatting consistency only.
|
|
11
|
+
Do not add new steps.
|
|
12
|
+
Do not remove any existing steps.
|
|
13
|
+
Do not introduce additional actions.
|
|
14
|
+
Do not include the example list in the response.
|
|
15
|
+
Do not include any extra text, commentary, markdown, or code fences.
|
|
16
|
+
Output Format
|
|
17
|
+
|
|
18
|
+
Respond strictly in valid JSON using the following structure:
|
|
19
|
+
{
|
|
20
|
+
"manaulSteps": ["step 1", "step 2", "step 3"]
|
|
21
|
+
}
|
|
22
|
+
`;
|
|
23
|
+
return prompt;
|
|
24
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
|
|
2
|
+
type visionPrompt = {
|
|
3
|
+
currentStep: string,
|
|
4
|
+
priorAndNextSteps: string[]
|
|
5
|
+
}
|
|
6
|
+
export async function visionPrompt(
|
|
7
|
+
{ currentStep, priorAndNextSteps }: visionPrompt
|
|
8
|
+
): Promise<string> {
|
|
9
|
+
|
|
10
|
+
const customPromptText = `
|
|
11
|
+
You are a precise automation assistant.
|
|
12
|
+
Context: ${priorAndNextSteps}
|
|
13
|
+
- Use the context to understand the step and select the closest semantic match to ${currentStep} and return only its index, else Fire-Flink-0.
|
|
14
|
+
- **If u can't find the element, return Fire-Flink-0. dont return any other elements index.**
|
|
15
|
+
- Using the provided annotated screenshot and ${currentStep}, identify the correct element index from the image.
|
|
16
|
+
- Match the index to the text or action described in ${currentStep} as accurately as possible.
|
|
17
|
+
- Return only the Fire-Flink-xx for the target element.
|
|
18
|
+
- Do not provide any additional text or explanation. The output must be strictly Fire-Flink-xx.
|
|
19
|
+
Now, Analyze the step:
|
|
20
|
+
Step: "${currentStep}"
|
|
21
|
+
|
|
22
|
+
Respond only with JSON using this format:
|
|
23
|
+
{fireflinkIndex: Fire-Flink-xx.}
|
|
24
|
+
|
|
25
|
+
`.trim();
|
|
26
|
+
|
|
27
|
+
return customPromptText;
|
|
28
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { PromptType } from "../../../core/types/promptType.js";
|
|
2
|
+
import { visionPromptMessage } from "../../../core/types/visionllmInputType.js";
|
|
3
|
+
|
|
4
|
+
export const userInputFormatters: {
|
|
5
|
+
[K in PromptType]: (
|
|
6
|
+
userInput: Record<string, any>
|
|
7
|
+
) => string | visionPromptMessage;
|
|
8
|
+
} = {
|
|
9
|
+
[PromptType.USER_STORY_TO_LIST]: (userInput) =>
|
|
10
|
+
`Convert the following user story into steps:\n\n${JSON.stringify(userInput.userStory, null, 2)}`,
|
|
11
|
+
|
|
12
|
+
[PromptType.KEYWORD_EXTRACTOR]: (userInput) =>
|
|
13
|
+
`Extract keywords from the following input:\n\n${JSON.stringify(userInput.currentStep, null, 2)}`,
|
|
14
|
+
|
|
15
|
+
[PromptType.ERROR_DESCRIPTION]: (userInput) =>
|
|
16
|
+
`Analyze the following error context:\n\n${JSON.stringify(userInput.error, null, 2)}`,
|
|
17
|
+
|
|
18
|
+
[PromptType.FF_INSPECTOR]: (userInput) =>
|
|
19
|
+
`Inspect the DOM for the following action context:\n\n${JSON.stringify(userInput.currentStep, null, 2)}`,
|
|
20
|
+
|
|
21
|
+
[PromptType.VISION_PROMPT]: (userInput) => {
|
|
22
|
+
const content: visionPromptMessage = [
|
|
23
|
+
{
|
|
24
|
+
type: "text",
|
|
25
|
+
text: `Analyze the following step:\n\n${JSON.stringify(
|
|
26
|
+
userInput.currentStep,
|
|
27
|
+
null,
|
|
28
|
+
2
|
|
29
|
+
)}`
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: "image_url",
|
|
33
|
+
image_url: {
|
|
34
|
+
url: String(userInput.imageBase64 ?? "")
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
];
|
|
38
|
+
return content;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { IActionExecutor } from "../../core/interfaces/actionInterface.js";
|
|
2
|
+
import { navigate } from './navigation/navigate.js';
|
|
3
|
+
import { goBack } from './navigation/goBack.js';
|
|
4
|
+
import { pageRefresh } from './navigation/refresh.js';
|
|
5
|
+
import { click } from './interaction/click.js';
|
|
6
|
+
import { wait } from "./wait/wait.js"
|
|
7
|
+
import { enterInput } from "./interaction/enterInput.js";
|
|
8
|
+
import { ScriptDataAppender } from "../../fireflinkData/fireflinkScript/scriptGenrationData.js";
|
|
9
|
+
import { ElementGetter } from "../../fireflinkData/fireflinkLocators/getListOfLocators.js";
|
|
10
|
+
export class ActionExecutor implements IActionExecutor {
|
|
11
|
+
constructor(private readonly browser: WebdriverIO.Browser,
|
|
12
|
+
private scriptDataAppender: ScriptDataAppender,
|
|
13
|
+
private elementGetter: ElementGetter,
|
|
14
|
+
private platform: string) { }
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async navigate(url: string): Promise<void> {
|
|
19
|
+
await navigate({
|
|
20
|
+
browser: this.browser,
|
|
21
|
+
url,
|
|
22
|
+
scriptDataAppender: this.scriptDataAppender
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async goBack(): Promise<void> {
|
|
27
|
+
await goBack({
|
|
28
|
+
browser: this.browser,
|
|
29
|
+
scriptDataAppender: this.scriptDataAppender
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async refresh(): Promise<void> {
|
|
34
|
+
await pageRefresh({
|
|
35
|
+
browser: this.browser,
|
|
36
|
+
scriptDataAppender: this.scriptDataAppender
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
async wait(time: string): Promise<void> {
|
|
42
|
+
await wait({
|
|
43
|
+
browser: this.browser,
|
|
44
|
+
time,
|
|
45
|
+
scriptDataAppender: this.scriptDataAppender
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async click(pageDOM: string, selector: string, fireflinkIndex: string, elementName: string, elementType: string): Promise<void> {
|
|
49
|
+
await click({
|
|
50
|
+
browser: this.browser,
|
|
51
|
+
selector,
|
|
52
|
+
scriptDataAppender: this.scriptDataAppender,
|
|
53
|
+
elementGetter: this.elementGetter,
|
|
54
|
+
fireflinkIndex,
|
|
55
|
+
pageDOM,
|
|
56
|
+
elementName,
|
|
57
|
+
elementType,
|
|
58
|
+
platform: this.platform
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async enterInput(selector: string, value: string, fireflinkIndex: string, pageDOM: string, elementName: string, elementType: string): Promise<void> {
|
|
62
|
+
await enterInput({
|
|
63
|
+
browser: this.browser,
|
|
64
|
+
selector: selector,
|
|
65
|
+
value,
|
|
66
|
+
scriptDataAppender: this.scriptDataAppender,
|
|
67
|
+
elementGetter: this.elementGetter,
|
|
68
|
+
fireflinkIndex,
|
|
69
|
+
pageDOM,
|
|
70
|
+
elementName,
|
|
71
|
+
elementType,
|
|
72
|
+
platform: this.platform
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { ClickInterface } from "../interface/interactionActionInterface.js";
|
|
2
|
+
export async function click(args: ClickInterface): Promise<void> {
|
|
3
|
+
try {
|
|
4
|
+
const element = await args.browser.$(args.selector);
|
|
5
|
+
await element.scrollIntoView({ block: 'center', inline: 'center' });
|
|
6
|
+
await element.click();
|
|
7
|
+
const ffElement: any = await args.elementGetter.getFireFlinkElement(
|
|
8
|
+
args.pageDOM,
|
|
9
|
+
`[ff-inspect="${args.fireflinkIndex}"]`
|
|
10
|
+
);
|
|
11
|
+
args.scriptDataAppender.add(
|
|
12
|
+
{
|
|
13
|
+
nlpName: 'Click',
|
|
14
|
+
stepInputs: [],
|
|
15
|
+
elementsData: [{
|
|
16
|
+
elementName: args.elementName,
|
|
17
|
+
type: args.elementType,
|
|
18
|
+
locators: ffElement.locators,
|
|
19
|
+
platform: args.platform
|
|
20
|
+
}]
|
|
21
|
+
});
|
|
22
|
+
} catch (error: any) {
|
|
23
|
+
throw new Error("Click action failed", { cause: error });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { EnterInputInterface } from "../interface/interactionActionInterface.js";
|
|
2
|
+
export async function enterInput(args: EnterInputInterface): Promise<void> {
|
|
3
|
+
try {
|
|
4
|
+
const element = args.browser.$(args.selector);
|
|
5
|
+
await element.scrollIntoView({ block: 'center', inline: 'center' });
|
|
6
|
+
await element.setValue(args.value);
|
|
7
|
+
const ffElement: any = await args.elementGetter.getFireFlinkElement(
|
|
8
|
+
args.pageDOM,
|
|
9
|
+
`[ff-inspect="${args.fireflinkIndex}"]`
|
|
10
|
+
);
|
|
11
|
+
args.scriptDataAppender.add({
|
|
12
|
+
nlpName: 'SendKeys',
|
|
13
|
+
stepInputs: [args.value],
|
|
14
|
+
elementsData: [{
|
|
15
|
+
elementName: args.elementName,
|
|
16
|
+
type: args.elementType,
|
|
17
|
+
locators: ffElement.locators,
|
|
18
|
+
platform: args.platform
|
|
19
|
+
}]
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
catch (error: any) {
|
|
24
|
+
throw new Error("enter action failed", { cause: error });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { ScriptDataAppender } from "../../../fireflinkData/fireflinkScript/scriptGenrationData.js";
|
|
2
|
+
import { ElementGetter } from "../../../fireflinkData/fireflinkLocators/getListOfLocators.js";
|
|
3
|
+
export interface ClickInterface {
|
|
4
|
+
browser: WebdriverIO.Browser;
|
|
5
|
+
selector: string;
|
|
6
|
+
scriptDataAppender: ScriptDataAppender;
|
|
7
|
+
elementGetter: ElementGetter;
|
|
8
|
+
fireflinkIndex: string;
|
|
9
|
+
pageDOM: string;
|
|
10
|
+
elementName: string;
|
|
11
|
+
elementType: any;
|
|
12
|
+
platform: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
export interface EnterInputInterface {
|
|
17
|
+
browser: WebdriverIO.Browser;
|
|
18
|
+
selector: string;
|
|
19
|
+
value: string;
|
|
20
|
+
scriptDataAppender: ScriptDataAppender;
|
|
21
|
+
elementGetter: ElementGetter;
|
|
22
|
+
fireflinkIndex: string;
|
|
23
|
+
pageDOM: string;
|
|
24
|
+
elementName: string;
|
|
25
|
+
elementType: any;
|
|
26
|
+
platform: string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { ScriptDataAppender } from "../../../fireflinkData/fireflinkScript/scriptGenrationData.js";
|
|
2
|
+
|
|
3
|
+
export interface getTitleInterface {
|
|
4
|
+
browser: WebdriverIO.Browser;
|
|
5
|
+
scriptDataAppender: ScriptDataAppender;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface goBackInterface {
|
|
9
|
+
browser: WebdriverIO.Browser;
|
|
10
|
+
scriptDataAppender: ScriptDataAppender;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface navigateToUrlInterface {
|
|
14
|
+
browser: WebdriverIO.Browser;
|
|
15
|
+
scriptDataAppender: ScriptDataAppender;
|
|
16
|
+
url: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface refreshInterface {
|
|
20
|
+
browser: WebdriverIO.Browser;
|
|
21
|
+
scriptDataAppender: ScriptDataAppender;
|
|
22
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { getTitleInterface } from "../interface/navigationActionInterface.js";
|
|
2
|
+
export async function getTitle(args: getTitleInterface): Promise<void> {
|
|
3
|
+
try {
|
|
4
|
+
const title = await args.browser.getTitle();
|
|
5
|
+
args.scriptDataAppender.add({ nlpName: 'getTitle', elementsData: [], stepInputs: [title] });
|
|
6
|
+
} catch (error: any) {
|
|
7
|
+
throw new Error("Get title action failed", { cause: error });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { goBackInterface } from "../interface/navigationActionInterface.js";
|
|
2
|
+
export async function goBack(args: goBackInterface): Promise<void> {
|
|
3
|
+
try {
|
|
4
|
+
await args.browser.back();
|
|
5
|
+
args.scriptDataAppender.add({ nlpName: 'goBack', elementsData: [], stepInputs: [] });
|
|
6
|
+
} catch (error: any) {
|
|
7
|
+
throw new Error("Navigate action failed", { cause: error });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { navigateToUrlInterface } from "../interface/navigationActionInterface.js";
|
|
2
|
+
export async function navigate(args: navigateToUrlInterface): Promise<void> {
|
|
3
|
+
try {
|
|
4
|
+
await args.browser.url(args.url);
|
|
5
|
+
args.scriptDataAppender.add({ nlpName: 'NavigateToURL', elementsData: [], stepInputs: [args.url] });
|
|
6
|
+
|
|
7
|
+
} catch (error: any) {
|
|
8
|
+
throw new Error("Navigate action failed", { cause: error });
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { refreshInterface } from "../interface/navigationActionInterface.js";
|
|
2
|
+
export async function pageRefresh(args: refreshInterface): Promise<void> {
|
|
3
|
+
try {
|
|
4
|
+
await args.browser.refresh();
|
|
5
|
+
args.scriptDataAppender.add({ nlpName: 'refresh', elementsData: [], stepInputs: [] });
|
|
6
|
+
} catch (error: any) {
|
|
7
|
+
throw new Error("Refresh action failed", { cause: error });
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { waitInterface } from "../interface/waitActionInterface.js";
|
|
2
|
+
export async function wait(args: waitInterface): Promise<void> {
|
|
3
|
+
try {
|
|
4
|
+
await args.browser.pause(Number(args.time) * 1000);
|
|
5
|
+
args.scriptDataAppender.add({ nlpName: 'Sleep', elementsData: [], stepInputs: [args.time] });
|
|
6
|
+
|
|
7
|
+
} catch (error: any) {
|
|
8
|
+
throw new Error("Navigate action failed", { cause: error });
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { remote } from "webdriverio";
|
|
2
|
+
import { browserCapabilities } from "../../core/interfaces/browserCapabilitiesInterface.js";
|
|
3
|
+
|
|
4
|
+
export class BrowserSession {
|
|
5
|
+
private browser: WebdriverIO.Browser | null = null;
|
|
6
|
+
|
|
7
|
+
private async openBrowser(capabilities: browserCapabilities): Promise<WebdriverIO.Browser> {
|
|
8
|
+
try {
|
|
9
|
+
const browser = await remote(
|
|
10
|
+
{
|
|
11
|
+
capabilities: {
|
|
12
|
+
browserName: capabilities.browserName,
|
|
13
|
+
},
|
|
14
|
+
logLevel: "silent"
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
browser.setTimeout({ 'pageLoad': capabilities.pageLoad, 'implicit': capabilities.implicit });
|
|
18
|
+
await browser.waitUntil(async () => {
|
|
19
|
+
const state = await browser.execute(() => document.readyState);
|
|
20
|
+
return state === "complete";
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return browser;
|
|
24
|
+
} catch (error: any) {
|
|
25
|
+
throw new Error("Browser failed to open", { cause: error });
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async open(capabilities: browserCapabilities): Promise<void> {
|
|
31
|
+
this.browser = await this.openBrowser(capabilities);
|
|
32
|
+
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async close(): Promise<void> {
|
|
36
|
+
if (this.browser) {
|
|
37
|
+
await this.browser.deleteSession();
|
|
38
|
+
this.browser = null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async getCurrentBrowser(): Promise<WebdriverIO.Browser> {
|
|
43
|
+
if (!this.browser) {
|
|
44
|
+
throw new Error("Browser is not initialized");
|
|
45
|
+
}
|
|
46
|
+
return this.browser;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
async waitForStableDOM(timeout = 10000): Promise<void> {
|
|
51
|
+
if (!this.browser) throw new Error("Browser session not found");
|
|
52
|
+
|
|
53
|
+
let lastHTML = "";
|
|
54
|
+
let stableCount = 0;
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
await this.browser.waitUntil(async () => {
|
|
58
|
+
if (!this.browser) return;
|
|
59
|
+
const currentHTML = await this.browser.execute(() =>
|
|
60
|
+
document.documentElement.outerHTML
|
|
61
|
+
);
|
|
62
|
+
if (currentHTML === lastHTML) {
|
|
63
|
+
stableCount++;
|
|
64
|
+
} else {
|
|
65
|
+
stableCount = 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
lastHTML = currentHTML;
|
|
69
|
+
|
|
70
|
+
return stableCount >= 3;
|
|
71
|
+
}, {
|
|
72
|
+
timeout,
|
|
73
|
+
interval: 500
|
|
74
|
+
});
|
|
75
|
+
} catch {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
// Main action executor interface
|
|
4
|
+
export interface IActionExecutor {
|
|
5
|
+
navigate(url: string): Promise<void>;
|
|
6
|
+
goBack(): Promise<void>;
|
|
7
|
+
refresh(): Promise<void>;
|
|
8
|
+
wait(time: string): Promise<void>;
|
|
9
|
+
click(pageDOM: string, selector: string, fireflinkIndex: string, elementName: string, elementType: string): Promise<void>;
|
|
10
|
+
enterInput(selector: string, value: string, fireflinkIndex: string, pageDOM: string, elementName: string, elementType: string, platform: string): Promise<void>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|