import pyautogui import psutil import threading from appium.webdriver import Remote, WebElement from appium.options.common.base import AppiumOptions from appium.webdriver.common.appiumby import AppiumBy as By from selenium.common.exceptions import WebDriverException, NoSuchElementException from helpers.ConfigHelper import get_config, get_app_env from helpers.ElementHelper import get_element_center_xy from helpers.keys.keys_map import get_key def native_click(self, **kwargs): x, y = get_element_center_xy(self) win_x, win_y = get_window_location() if x < win_x: x = x + win_x if y < win_y: y = y + win_y pyautogui.click(x, y, **kwargs) def native_double_click(self, **kwargs): x, y = get_element_center_xy(self) win_x, win_y = get_window_location() if x < win_x: x = x + win_x if y < win_y: y = y + win_y pyautogui.doubleClick(x, y, **kwargs) def native_send_keys(self, key): pyautogui.press(get_key(key)) def find_element(self, by, selector): """ Returns a visible element. Throws if no elements are found or if multiple visible elements are found. """ elements = self.find_elements(by, selector) elements_count = len(elements) if elements_count > 1: visible_elements = [el for el in elements if el.is_displayed()] if len(visible_elements) == 1: return visible_elements.pop() raise WebDriverException( f'Found {elements_count} elements using "{by}={selector}"' ) if elements_count == 0: raise NoSuchElementException(f'No element found for "{by}={selector}"') return elements[0] def pause(self): threading.Event().wait() # bind custom element methods Remote.find_element = find_element Remote.pause = pause WebElement.native_click = native_click WebElement.native_double_click = native_double_click WebElement.native_send_keys = native_send_keys WebElement.find_element = find_element app_driver = None def app(): return app_driver def create_app_session(): global app_driver logfile = get_config("currentAppLogFile") command_args = f' --logfile {logfile}' options = AppiumOptions() options.set_capability( 'app', f'{get_config("app_path")} -s {command_args} --logdebug', ) options.set_capability('appium:environ', get_app_env()) app_driver = Remote(command_executor='http://localhost:4723', options=options) app_driver.implicitly_wait = 10 def close_and_kill_app(): """ Close Appium session and kill the desktop client process. Use this for both mid-scenario and end-of-scenario cleanup. """ global app_driver # Quit Appium session if app_driver is not None: app_driver.quit() # Kill remaining process by exe path app_path = get_config("app_path") for process in psutil.process_iter(['pid', 'exe']): if process.info['exe'] == app_path: print("Closing desktop client...") psutil.Process(process.info['pid']).kill() break # Reset driver for reuse app_driver = None def get_window_location(): window = ( app() .find_element(By.XPATH, "//*[contains(@name,'КуСфера')]") .location ) return window['x'], window['y']