chonk

 avatar
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