Files
test/framework/web/web_action_executor.py
jpike bfd6bbc293 Adding ability to hover
Added hover functionality for ui.

Change-Id: I41c9fb5465ef1c6b4189a2681650f2e00bcd88dc
Signed-off-by: jpike <jason.pike@windriver.com>
2025-08-13 14:03:33 -04:00

256 lines
9.5 KiB
Python

import time
from typing import List
from framework.logging.automation_logger import get_logger
from framework.threading.thread_manager import ThreadManager
from framework.web.action.web_action import WebAction
from framework.web.web_locator import WebLocator
from selenium.webdriver.remote.webelement import WebElement
class WebActionExecutor:
"""
This class executes WebDriver actions in a retry structure that makes use of stability conditions.
"""
def __init__(self, web_action: WebAction):
"""
Constructor which will instantiate the driver object.
Args:
web_action: Action that we want to perform.
"""
self.web_action = web_action
self.progressive_sleep = 1
self.progressive_sleep_increment = 1
def _find_element(self, locator: WebLocator) -> WebElement:
"""
This function will attempt to find this element in the DOM.
Args:
locator: Element that we are trying to find in the DOM.
Returns: A Selenium WebElement.
"""
webdriver = self.web_action.get_webdriver()
web_element = webdriver.find_element(locator.get_by(), locator.get_locator())
return web_element
def _find_all_elements(self, locator: WebLocator) -> List[WebElement]:
"""
This function will attempt to find all the elements in the DOM matching the locator.
Args:
locator: Locator matching the elements that we want to find.
Returns: A list of Selenium WebElement.
"""
webdriver = self.web_action.get_webdriver()
web_elements = webdriver.find_elements(locator.get_by(), locator.get_locator())
return web_elements
def _is_a_condition_satisfied(self):
"""
This function will check if a condition is satisfied.
Returns:
True if there is no condition associated with the action.
True if at least one condition associated with the action is satisfied.
False otherwise.
"""
is_a_condition_satisfied = False
# Assume that the action was a success if there is no condition.
if len(self.web_action.get_conditions()) == 0:
get_logger().log_debug("No Condition Found - Validation is Successful")
is_a_condition_satisfied = True
# Check if at least one condition is satisfied.
for condition in self.web_action.get_conditions():
get_logger().log_debug(f"Checking Condition - {condition}")
if condition.is_condition_satisfied(self.web_action.get_webdriver()):
is_a_condition_satisfied = True
get_logger().log_debug(f"Condition Satisfied! - {condition}")
break
return is_a_condition_satisfied
def _execute_action(self, *args):
"""
This function will execute the action with the arguments specified
Args:
*args: Parameters to pass in to the action function.
Returns:
The output of the action if it is successful.
"""
state = "NOT_FOUND"
is_action_done = False
value = None
timeout = time.time() + self.web_action.get_timeout()
while state != "COMPLETE" and time.time() < timeout:
try:
if state == "NOT_FOUND":
# Find the element.
web_locator = self.web_action.get_web_locator()
web_element = self._find_element(web_locator)
get_logger().log_debug(f"Found Element - {self.web_action.get_web_locator()}")
state = "FOUND"
if state == "FOUND":
# Execute the Specified Action
get_logger().log_debug(f"Attempting to perform {self.web_action} on - {self.web_action.get_web_locator()}")
value = self.web_action.perform_action(web_element, *args)
is_action_done = True
state = "VALIDATING"
if state == "VALIDATING":
if self._is_a_condition_satisfied():
state = "COMPLETE"
else:
get_logger().log_debug("Failed to satisfy a condition, moving to UNKNOWN state")
state = "UNKNOWN"
if state == "UNKNOWN":
get_logger().log_debug(f"Sleep for {self.progressive_sleep}s")
time.sleep(self.progressive_sleep)
self.progressive_sleep += self.progressive_sleep_increment
if is_action_done and self._is_a_condition_satisfied():
state = "COMPLETE"
else:
# Try the action again.
if is_action_done:
get_logger().log_debug("Failed to satisfy the conditions again, moving to NOT_FOUND state")
state = "NOT_FOUND"
except Exception as e:
get_logger().log_debug("Exception occurred during action, moving to UNKNOWN state")
state = "UNKNOWN"
if state != "COMPLETE":
raise Exception(f"Failed to perform Action {self.web_action}")
return value
def execute_action(self, *args):
"""
This function will execute the action with the arguments specified
This is the threaded version that calls _execute_action.
Args:
*args: Parameters to pass in to the action function.
Returns:
The output of the action if it is successful.
"""
# Thread the Selenium operation to ensure that we maintain control if it hangs.
thread_manager_send = ThreadManager(timeout=1.5 * self.web_action.get_timeout())
thread_manager_send.start_thread("Selenium", self._execute_action, *args)
thread_manager_send.join_all_threads()
value = thread_manager_send.get_thread_object("Selenium").get_result()
return value
def _execute_mass_action(self, *args):
"""
This function will execute the action specified on all the WebElements that match the WebLocator.
Args:
*args: Parameters to pass in to the action function.
Returns:
A List of the outputs of the actions.
"""
state = "NOT_FOUND"
is_action_done = False
values = []
timeout = time.time() + self.web_action.get_timeout()
while state != "COMPLETE" and time.time() < timeout:
try:
if state == "NOT_FOUND":
# Find the element.
web_locator = self.web_action.get_web_locator()
web_elements = self._find_all_elements(web_locator)
get_logger().log_debug(f"Found {len(web_elements)} Elements Matching - {self.web_action.get_web_locator()}")
state = "FOUND"
if state == "FOUND":
# Execute the Specified Action
values = []
get_logger().log_debug(f"Attempting to perform {self.web_action} on every element matching - {self.web_action.get_web_locator()}")
for element in web_elements:
value = self.web_action.perform_action(element, *args)
values.append(value)
is_action_done = True
state = "VALIDATING"
if state == "VALIDATING":
if self._is_a_condition_satisfied():
state = "COMPLETE"
else:
get_logger().log_debug("Failed to satisfy a condition, moving to UNKNOWN state")
state = "UNKNOWN"
if state == "UNKNOWN":
get_logger().log_debug(f"Sleep for {self.progressive_sleep}s")
time.sleep(self.progressive_sleep)
self.progressive_sleep += self.progressive_sleep_increment
if is_action_done and self._is_a_condition_satisfied():
state = "COMPLETE"
else:
# Try the action again.
if is_action_done:
get_logger().log_debug("Failed to satisfy the conditions again, moving to NOT_FOUND state")
state = "NOT_FOUND"
except Exception as e:
get_logger().log_debug(e)
get_logger().log_debug("Exception occurred during action, moving to UNKNOWN state")
state = "UNKNOWN"
if state != "COMPLETE":
raise Exception(f"Failed to perform Mass Action {self.web_action}")
return values
def execute_mass_action(self, *args):
"""
This function will execute the action specified on all the WebElements that match the WebLocator.
This is the threaded version that calls _execute_mass_action.
Args:
*args: Parameters to pass in to the action function.
Returns:
A List of the outputs of the actions.
"""
# Thread the Selenium operation to ensure that we maintain control if it hangs.
thread_manager_send = ThreadManager(timeout=1.5 * self.web_action.get_timeout())
thread_manager_send.start_thread("Selenium", self._execute_mass_action, *args)
thread_manager_send.join_all_threads()
values = thread_manager_send.get_thread_object("Selenium").get_result()
return values