Skip to content

Commit 14981f3

Browse files
Merge pull request #505 from AutomationSolutionz/shadow-root
Shadow root implement
2 parents f9ac77e + 46f3fcf commit 14981f3

File tree

3 files changed

+127
-33
lines changed

3 files changed

+127
-33
lines changed

Framework/Built_In_Automation/Sequential_Actions/action_declarations/info.py

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -92,21 +92,10 @@
9292
"fail message",
9393
)
9494
patterns = [
95-
"^parent \d parameter$",
96-
"^sibling \d parameter$",
97-
"^child \d parameter$",
98-
"^following \d parameter$",
99-
"^next \d parameter$",
100-
"^preceding \d parameter$",
101-
"^previous \d parameter$",
102-
103-
"^src parent \d parameter$", "^src sibling \d parameter$", "^src child \d parameter$", "^src following \d parameter$", "^src next \d parameter$", "^src preceding \d parameter$", "^src previous \d parameter$",
104-
"^dst parent \d parameter$", "^dst sibling \d parameter$", "^dst child \d parameter$", "^dst following \d parameter$", "^dst next \d parameter$", "^dst preceding \d parameter$", "^dst previous \d parameter$",
105-
"^source parent \d parameter$", "^source sibling \d parameter$", "^source child \d parameter$", "^source following \d parameter$", "^source next \d parameter$", "^source preceding \d parameter$", "^source prevoius \d parameter$",
106-
"^destination parent \d parameter$", "^destination sibling \d parameter$", "^destination child \d parameter$", "^destination following \d parameter$", "^destination next \d parameter$", "^destination preceding \d parameter$","^destination previous \d parameter$",
107-
"^desired parent \d parameter$", "^desired sibling \d parameter$", "^desired child \d parameter$", "^desired following \d parameter$", "^desired next \d parameter$", "^desired preceding \d parameter$", "^desired previous \d parameter$",
108-
95+
r'^sr *(src |source |dst |destination |desired )?(parent|sibling|child|next|following|previous|preceding) (\d )*parameter$',
96+
r'^sr *(src |source |dst |destination |desired )?element parameter$',
10997
]
98+
11099
# List of supported mobile platforms - must be lower case
111100
supported_platforms = ("android", "ios")
112101

Framework/Built_In_Automation/Shared_Resources/LocateElement.py

Lines changed: 123 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import sys, time, re
88
import inspect
99
import traceback
10+
from typing import Literal
1011
from pathlib import Path
1112
from Framework.Utilities import CommonUtil
1213
from Framework.Utilities.CommonUtil import passed_tag_list, failed_tag_list
@@ -26,13 +27,119 @@
2627
global driver_type
2728
driver_type = None
2829

29-
3030
MODULE_NAME = inspect.getmodulename(__file__)
3131

32+
def build_css_selector_query(dataset:list[list[str]]) -> str:
33+
""" Builds css selector query from dataset """
34+
try:
35+
sModuleInfo = inspect.currentframe().f_code.co_name + " : " + MODULE_NAME
36+
query = ""
37+
element_parameter_list = []
38+
parent_parameter_list = []
39+
for left, mid, right in dataset:
40+
mid_ = mid.replace(" ", "").lower()
41+
if "elementparameter" == mid_:
42+
element_parameter_list.append((left, right))
43+
if left == "css selector":
44+
return right
45+
elif "parent" in mid_ and "parameter" in mid_:
46+
parent_parameter_list.append([left, right])
47+
elif "sibling" in mid_ and "parameter" in mid_:
48+
CommonUtil.ExecLog(sModuleInfo, "Sibling parameter is not supported in css selector", 2)
49+
elif "child" in mid_ and "parameter" in mid_:
50+
CommonUtil.ExecLog(sModuleInfo, "Child parameter is not supported in css selector", 2)
51+
elif "preceding" in mid_ and "parameter" in mid_:
52+
CommonUtil.ExecLog(sModuleInfo, "Preceding parameter is not supported in css selector", 2)
53+
elif "following" in mid_ and "parameter" in mid_:
54+
CommonUtil.ExecLog(sModuleInfo, "Following parameter is not supported in css selector", 2)
55+
56+
for left, right in parent_parameter_list:
57+
if left == "tag":
58+
query = right + query
59+
break
60+
else:
61+
if len(parent_parameter_list) > 0:
62+
query = "*" + query
63+
64+
for left, right in parent_parameter_list:
65+
if left == "tag":
66+
pass
67+
elif left == "text":
68+
CommonUtil.ExecLog(sModuleInfo, "Text parameter is not supported in css selector", 2)
69+
elif left == "xpath":
70+
CommonUtil.ExecLog(sModuleInfo, "xpath parameter is not supported in css selector", 2)
71+
elif left != "index":
72+
quote = "'" if '"' in right else '"'
73+
query += f"[{left}={quote}{right}{quote}]"
74+
75+
if len(parent_parameter_list) > 0:
76+
query += " "
77+
78+
for left, right in element_parameter_list:
79+
if left == "tag":
80+
query += right
81+
break
82+
else:
83+
query += "*"
84+
85+
for left, right in element_parameter_list:
86+
if left == "tag":
87+
pass
88+
elif left == "text":
89+
CommonUtil.ExecLog(sModuleInfo, "Text parameter is not supported in css selector", 2)
90+
elif left == "xpath":
91+
CommonUtil.ExecLog(sModuleInfo, "xpath parameter is not supported in css selector", 2)
92+
elif left != "index":
93+
quote = "'" if '"' in right else '"'
94+
query += f"[{left}={quote}{right}{quote}]"
3295

33-
def Get_Element(step_data_set, driver, query_debug=False, return_all_elements=False, element_wait=None):
96+
return query
97+
98+
except:
99+
CommonUtil.Exception_Handler(sys.exc_info())
100+
return ""
101+
102+
get_element_return_type = list[selenium.webdriver.remote.webelement.WebElement] | Literal["zeuz_failed"] | selenium.webdriver.remote.webelement.WebElement
103+
def shadow_root_elements(shadow_root_ds: list[list[str]], element_ds: list[list[str]], Filter: str, element_wait: float, return_all_elements: bool) -> get_element_return_type:
104+
""" Finds the shadow root container and the element inside there, both in css-selector method"""
105+
try:
106+
sModuleInfo = inspect.currentframe().f_code.co_name + " : " + MODULE_NAME
107+
child_shadow_root_ds = []
108+
for left, mid, right in shadow_root_ds:
109+
mid = mid.strip().lower()
110+
if mid.startswith("sr"):
111+
child_shadow_root_ds.append((left, mid, right))
112+
if len(child_shadow_root_ds) > 0:
113+
# handle nested roots later
114+
CommonUtil.ExecLog(sModuleInfo, "Nested shadow root is not supported yet", 2)
115+
return "zeuz_failed"
116+
else:
117+
element_query = build_css_selector_query(shadow_root_ds)
118+
index = _locate_index_number(shadow_root_ds)
119+
index = 0 if index is None else index
120+
elements = generic_driver.find_elements(By.CSS_SELECTOR, element_query)
121+
filtered_elements = filter_elements(elements, Filter)
122+
shadow_container_element = filtered_elements[index]
123+
shadow_root_element = generic_driver.execute_script('return arguments[0].shadowRoot', shadow_container_element)
124+
125+
element_query = build_css_selector_query(element_ds)
126+
index = _locate_index_number(element_ds)
127+
index = 0 if index is None else index
128+
elements = shadow_root_element.find_elements(By.CSS_SELECTOR, element_query)
129+
filtered_elements = filter_elements(elements, Filter)
130+
if return_all_elements:
131+
return filtered_elements
132+
elif len(filtered_elements) == 0:
133+
return []
134+
else:
135+
return filtered_elements[index]
136+
except:
137+
return CommonUtil.Exception_Handler(sys.exc_info())
138+
139+
140+
def Get_Element(step_data_set, driver, query_debug=False, return_all_elements=False, element_wait=None) -> get_element_return_type:
34141
"""
35-
This funciton will return "zeuz_failed" if something went wrong, else it will always return a single element
142+
This function will return "zeuz_failed" if something went wrong, else it will always return a single element
36143
if you are trying to produce a query from a step dataset, make sure you provide query_debug =True. This is
37144
good when you are just trying to see how your step data would be converted to a query for testing local runs
38145
"""
@@ -62,19 +169,8 @@ def Get_Element(step_data_set, driver, query_debug=False, return_all_elements=Fa
62169
# We need to switch to default content just in case previous action switched to something else
63170
try:
64171
if driver_type == "selenium":
65-
pass #generic_driver.switch_to.default_content()
66-
# we need to see if there are more than one handles. Since we cannot know if we had switch
67-
# windows before, we are going to assume that we can always safely switch to default handle 0
68-
"""
69-
try:
70-
all_windows = generic_driver.window_handles
71-
generic_driver.switch_to.window(all_windows[0])
72-
True
73-
except:
74-
True
75-
"""
172+
pass
76173
elif driver_type == "appium":
77-
78174
# If we find a '|' character in the left column, then try to check the platform
79175
# and filter the appropriate data for the left column by removing '|'
80176
device_platform = (
@@ -128,6 +224,8 @@ def Get_Element(step_data_set, driver, query_debug=False, return_all_elements=Fa
128224
get_parameter = ""
129225
Filter = ""
130226
text_filter_cond = False
227+
shadow_root_ds = []
228+
element_ds = []
131229
for row in step_data_set:
132230
if row[1] == "save parameter":
133231
if row[2] != "ignore":
@@ -147,7 +245,13 @@ def Get_Element(step_data_set, driver, query_debug=False, return_all_elements=Fa
147245
element_wait = float(right)
148246
elif left == "text filter":
149247
text_filter_cond = right in ("yes", "true", "ok", "enable")
248+
elif row[1].strip().lower().startswith("sr"):
249+
shadow_root_ds.append([row[0], row[1][2:].strip(), row[2]])
250+
else:
251+
element_ds.append([row[0], row[1], row[2]])
150252

253+
if len(shadow_root_ds) > 0:
254+
return shadow_root_elements(shadow_root_ds, element_ds, Filter, element_wait, return_all_elements)
151255

152256
if get_parameter != "":
153257

@@ -238,9 +342,9 @@ def text_filter(step_data_set, Filter, element_wait, return_all_elements):
238342
"""
239343
suppose dom has <div>Hello &nbsp;World</div>
240344
the text will be converted to "<something unknown>Hello world<something unknown>"
241-
Thats why (text, element parameter, Hello world) does not work
345+
That's why (text, element parameter, Hello world) does not work
242346
But (*text, element parameter, Hello world) works!
243-
So for now we don't need this python script for now as we have an existing solution
347+
So for now we don't need this python script as we have an existing solution
244348
"""
245349
try:
246350
sModuleInfo = inspect.currentframe().f_code.co_name + " : " + MODULE_NAME
@@ -1084,7 +1188,8 @@ def filter_elements(all_matching_elements_visible_invisible, Filter):
10841188
return all_matching_elements
10851189
else:
10861190
return all_matching_elements_visible_invisible
1087-
except:
1191+
except Exception as e:
1192+
CommonUtil.Exception_Handler(sys.exc_info())
10881193
all_matching_elements = []
10891194
return all_matching_elements
10901195

Framework/MainDriverApi.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1275,7 +1275,7 @@ def send_dom_variables():
12751275
verify=False
12761276
)
12771277
if res.status_code == 500:
1278-
CommonUtil.ExecLog(sModuleInfo, res.json()["info"], 3)
1278+
CommonUtil.ExecLog(sModuleInfo, res.json()["info"], 2)
12791279
elif res.status_code == 404:
12801280
CommonUtil.ExecLog(sModuleInfo, 'The chatbot API does not exist, server upgrade needed', 2)
12811281
return

0 commit comments

Comments
 (0)