chonk
unknown
python
2 years ago
14 kB
9
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