chonk
unknown
python
a year ago
14 kB
5
Indexable
from webdriver_manager.firefox import GeckoDriverManager from webdriver_manager.chrome import ChromeDriverManager import time from typing import Any, List, Optional from selenium import webdriver from selenium.webdriver import ActionChains from selenium.webdriver.chrome.options import Options as chrome_options from selenium.webdriver.firefox.options import Options as firefox_options from selenium.webdriver.common.by import By from selenium.webdriver.firefox.webdriver import WebDriver from selenium.webdriver.remote.webelement import WebElement from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait class Driver(object): """ Selenium WebDriver Cleanup Class/Functions """ def __init__(self, webdriver_type: str = "geckodriver", options: Optional[List] = None): """ :param webdriver_type: webdriver type (chromedriver, geckodriver) :param options: list of webdriver options to add """ if "chromedriver" in str(webdriver_type).lower(): chromedriver_options = chrome_options() if options: for arg in options: chromedriver_options.add_argument(arg) self.browser = webdriver.Chrome(executable_path=ChromeDriverManager().install(), options=chromedriver_options) elif "geckodriver" in str(webdriver_type).lower(): geckodriver_options = firefox_options() if options: for arg in options: geckodriver_options.add_argument(arg) self.browser = webdriver.Firefox(executable_path=GeckoDriverManager().install(), options=geckodriver_options) else: raise Exception(f"Provided Webdriver type was not recognised! {webdriver_type}") self.browser.set_page_load_timeout(60) # set page load timeout to 1min def get(self, url: str) -> None: """ Get certain webpage :param url: url """ self.browser.get(url) def browser(self) -> WebDriver: """ Returns webdriver obj :return: WebDriver """ return self.browser def save_screenshot(self, filename: str) -> None: """ Saves a screenshot :param filename: The full path you wish to save your screenshot to. This should end with a `.png` extension. """ self.browser.save_screenshot(filename) def get_elem_by_xpath(self, xpath: str, timeout: Optional[int] = 30) -> WebElement: """ Waits for element to appear for selected time and returns that element :param xpath: element XPATH :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ return WebDriverWait(self.browser, timeout).until(EC.presence_of_element_located((By.XPATH, xpath))) def get_clickable_elem_by_xpath(self, xpath: str, timeout: Optional[int] = 30) -> WebElement: """ Waits for element to become clickable and returns that element :param xpath: element XPATH :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ return WebDriverWait(self.browser, timeout).until(EC.element_to_be_clickable((By.XPATH, xpath))) def get_elem_by_id(self, id: str, timeout: Optional[int] = 30) -> WebElement: """ Waits for element (BY ID) to appear for selected time and returns that element :param id: element ID :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ return WebDriverWait(self.browser, timeout).until(EC.presence_of_element_located((By.ID, id))) def get_clickable_elem_by_id(self, id: str, timeout: Optional[int] = 30) -> WebElement: """ Waits for element (BY ID) to become clickable and returns that element :param id: element ID :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ return WebDriverWait(self.browser, timeout).until(EC.element_to_be_clickable((By.ID, id))) def get_elem_by_class_name(self, class_name: str, timeout: Optional[int] = 30) -> WebElement: """ Waits for element (BY CLASS NAME) and returns that element :param class_name: element Class name :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ return WebDriverWait(self.browser, timeout).until(EC.presence_of_element_located((By.CLASS_NAME, class_name))) def get_clickable_elem_by_class_name(self, class_name: str, timeout: Optional[int] = 30) -> WebElement: """ Waits for element (BY CLASS NAME) to become clickable and returns that element :param class_name: element Class name :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ return WebDriverWait(self.browser, timeout).until(EC.element_to_be_clickable((By.CLASS_NAME, class_name))) def get_clickable_elem_that_contains_text_by_xpath(self, text: str, timeout: Optional[int] = 30) -> WebElement: """ Search for web element that contains provided text :param text: element text :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ return WebDriverWait(self.browser, timeout).until( EC.element_to_be_clickable((By.XPATH, f"//*[contains(text(), '{text}')]"))) def get_clickable_elem_by_css_selector(self, css_selector: str, timeout: Optional[int] = 30) -> WebElement: """ Waits for element to become clickable and returns that element :param css_selector: element css_selector :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ return WebDriverWait(self.browser, timeout).until(EC.element_to_be_clickable((By.CSS_SELECTOR, css_selector))) def get_clickable_elem_by_attribute_value(self, attribute: str, attribute_value: str, tag_name: Optional[str] = None, following_sibling: Optional[str] = None, preceding_sibling: Optional[str] = None, parent: Optional[str] = None, timeout: Optional[int] = 30) -> WebElement: """ Looks for element that contains given attribute value, waits for the element to become clickable and returns that element :param attribute: attribute :param attribute_value: attribute value :param tag_name: tag under which the element is located (Optional), default = None (searches every tag name) :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :param following_sibling: following sibling's tag :param preceding_sibling: preceding sibling's tag :param parent: parent element tag :return: WebElement """ if tag_name is None: tag_name = "*" xpath = f"//{tag_name}[@{attribute}='{attribute_value}']" if following_sibling is not None: xpath = f"{xpath}//following-sibling::{following_sibling}" if preceding_sibling is not None: xpath = f"{xpath}//preceding-sibling::{preceding_sibling}" if parent is not None: xpath = f"{xpath}//parent::{parent}" return WebDriverWait(self.browser, timeout).until(EC.element_to_be_clickable((By.XPATH, xpath))) def hover_over_xpath(self, element_xpath, timeout: Optional[int] = 30) -> WebElement: """ Hovers over web element by XPath and returns the WebElement :param element_xpath: element xpath to hover over :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ elem = WebDriverWait(self.browser, timeout).until(EC.presence_of_element_located((By.XPATH, element_xpath))) ActionChains(self.browser).move_to_element(elem).perform() time.sleep(1) # sanity wait to avoid before-hover future actions return elem def hover_over_id(self, element_id: str, timeout: Optional[int] = 30) -> WebElement: """ Hovers over web element by ID and returns the WebElement :param element_id: element id to hover over :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 :return: WebElement """ elem = WebDriverWait(self.browser, timeout).until(EC.presence_of_element_located((By.ID, element_id))) ActionChains(self.browser).move_to_element(elem).perform() time.sleep(1) # sanity wait to avoid before-hover future actions return elem def drag_and_drop(self, source, target) -> None: """ Drag and drop source element onto target element :param source: WebElement to be dragged :param target: WebElement to drag onto """ ActionChains(self.browser).drag_and_drop(source, target).perform() def drag_and_drop_by_xpath(self, source_xpath: int, target_xpath: int, timeout: Optional[int] = 30) -> None: """ Find, drag and drop one element onto another :param source_xpath: xpath of element to be dragged :param target_xpath: xpath of element to drag onto :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 """ source = WebDriverWait(self.browser, timeout).until(EC.element_to_be_clickable((By.XPATH, source_xpath))) target = WebDriverWait(self.browser, timeout).until(EC.element_to_be_clickable((By.XPATH, target_xpath))) ActionChains(self.browser).drag_and_drop(source, target).perform() def hide_elem(self, elem: WebElement) -> None: """ Hides unwanted WebElement using js :param elem: WebElement """ self.browser.execute_script("arguments[0].style.visibility='hidden'", elem) def check_if_elem_exists_by_id(self, elem_id: str) -> bool: """ Checks if elem exists by id and returns a bool :param elem_id: WebElement id :return: bool if WebElement exists """ if len(self.browser.find_elements(By.ID, elem_id)) > 0: return True else: return False def check_if_elem_exists_by_class_name(self, elem_class_name: str) -> bool: """ Checks if elem exists by class name and returns a bool :param elem_class_name: WebElement id :return: bool if WebElement exists """ if len(self.browser.find_elements(By.CLASS_NAME, elem_class_name)) > 0: return True else: return False def wait_for_iframe_by_xpath(self, iframe_xpath: str, timeout: Optional[int] = 30) -> None: """ Waits for iframe to load and switches to it :param iframe_xpath: iframe element xpath :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 """ WebDriverWait(self.browser, timeout).until(EC.frame_to_be_available_and_switch_to_it((By.XPATH, iframe_xpath))) def wait_for_iframe_by_id(self, iframe_id: str, timeout: Optional[int] = 30) -> None: """ Waits for iframe to load and switches to it :param iframe_id: iframe element id :param timeout: max time to wait for the WebElement in seconds (Optional), default = 30 """ WebDriverWait(self.browser, timeout).until(EC.frame_to_be_available_and_switch_to_it((By.ID, iframe_id))) def switch_to_main_frame(self) -> None: """Switches back to main frame of the page in case you need to exit iframe or any other frame""" self.browser.switch_to.default_content() def change_page_scale(self, scale: float) -> None: """ Changes page scale :param scale: the desired scale i.e. 0.5 """ self.browser.execute_script(f'document.body.style.MozTransform = "scale({scale})";') self.browser.execute_script('document.body.style.MozTransformOrigin = "0 0";') def quit(self) -> None: """ Quit the webdriver """ self.browser.quit() ========================================= @pytest.fixture(scope='function') def driver(config: RetailConfig) -> Driver: """ Starts the webdriver before test and closes it afterwards """ driver = Driver(webdriver_type=f'{config.webdriver_type}', options=['--headless', '--width=1920', '--height=1080']) try: yield driver finally: driver.quit() @pytest.hookimpl(tryfirst=True, hookwrapper=True) def pytest_runtest_makereport(item, call): outcome = yield rep = outcome.get_result() if "driver" in item.funcargs and "config" in item.funcargs and rep.when == 'call' and rep.failed: logging.info("Saving test failure screenshot!") driver = item.funcargs['driver'] config = item.funcargs['config'] filename = f'debug_screenshot_{item.obj.__name__}_{datetime.today().strftime("%Y-%m-%d_%H:%M:%S")}.png' driver.save_screenshot(f'{os.path.join(config.files_dir, filename)}') ===================================== import os class SharedConfig: def __init__(self): self.webdriver_type = os.getenv('WEBDRIVER_TYPE', 'geckodriver') self.files_dir = os.getenv('FILES_DIR', 'files') self.results_file_path = os.getenv('RESULTS_FILE_PATH', 'results.xml')
Editor is loading...
Leave a Comment