├── .gitignore ├── abstract ├── __init__.py └── selenium_listener.py ├── base ├── __init__.py ├── seleniumbase.py └── utils.py ├── pom ├── __init__.py └── homepage_nav.py ├── requirements.txt └── tests ├── __init__.py ├── conftest.py └── test_homepage.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | venv/ 4 | .pytest_cache/ 5 | .env 6 | pytest.ini 7 | *.pyc -------------------------------------------------------------------------------- /abstract/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vBrooklyn/Python-Selenium/7152d1c1f72de7b8d7df15cb9561335e46bd3b23/abstract/__init__.py -------------------------------------------------------------------------------- /abstract/selenium_listener.py: -------------------------------------------------------------------------------- 1 | from selenium.webdriver.support.events import AbstractEventListener 2 | 3 | from base.seleniumbase import SeleniumBase 4 | 5 | 6 | class MyListener(AbstractEventListener): 7 | 8 | def before_click(self, element, driver): 9 | '''Deleting a cookie ak_bmsc before a click''' 10 | SeleniumBase(driver).delete_cookie('ak_bmsc') 11 | 12 | def after_click(self, element, driver): 13 | '''Deleting a cookie ak_bmsc after a click''' 14 | SeleniumBase(driver).delete_cookie('ak_bmsc') 15 | -------------------------------------------------------------------------------- /base/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vBrooklyn/Python-Selenium/7152d1c1f72de7b8d7df15cb9561335e46bd3b23/base/__init__.py -------------------------------------------------------------------------------- /base/seleniumbase.py: -------------------------------------------------------------------------------- 1 | from selenium.common.exceptions import StaleElementReferenceException 2 | from selenium.webdriver.common.by import By 3 | from selenium.webdriver.support import expected_conditions as ec 4 | from selenium.webdriver.support.ui import WebDriverWait 5 | from selenium.webdriver.remote.webelement import WebElement 6 | from typing import List 7 | 8 | 9 | class SeleniumBase: 10 | def __init__(self, driver): 11 | self.driver = driver 12 | self.__wait = WebDriverWait(driver, 15, 0.3, ignored_exceptions=StaleElementReferenceException) 13 | 14 | def __get_selenium_by(self, find_by: str) -> dict: 15 | '''Return a dictionary, where Keys are Strings representing a search locator strategies and Values are related By class values''' 16 | find_by = find_by.lower() 17 | locating = {'css': By.CSS_SELECTOR, 18 | 'xpath': By.XPATH, 19 | 'class_name': By.CLASS_NAME, 20 | 'id': By.ID, 21 | 'link_text': By.LINK_TEXT, 22 | 'name': By.NAME, 23 | 'partial_link_text': By.PARTIAL_LINK_TEXT, 24 | 'tag_name': By.TAG_NAME} 25 | return locating[find_by] 26 | 27 | def is_visible(self, find_by: str, locator: str, locator_name: str = None) -> WebElement: 28 | '''Waiting on element and return WebElement if it is visible''' 29 | return self.__wait.until(ec.visibility_of_element_located((self.__get_selenium_by(find_by), locator)), locator_name) 30 | 31 | def is_present(self, find_by: str, locator: str, locator_name: str = None) -> WebElement: 32 | '''Waiting on element and return WebElement if it is present on DOM''' 33 | return self.__wait.until(ec.presence_of_element_located((self.__get_selenium_by(find_by), locator)), locator_name) 34 | 35 | def is_not_present(self, find_by: str, locator: str, locator_name: str = None) -> WebElement: 36 | '''Wait on element until it disappears ''' 37 | return self.__wait.until(ec.invisibility_of_element_located((self.__get_selenium_by(find_by), locator)), 38 | locator_name) 39 | 40 | def are_visible(self, find_by: str, locator: str, locator_name: str = None) -> List[WebElement]: 41 | '''Waiting on elements and return WebElements if they are visible''' 42 | return self.__wait.until(ec.visibility_of_all_elements_located((self.__get_selenium_by(find_by), locator)), 43 | locator_name) 44 | 45 | def are_present(self, find_by: str, locator: str, locator_name: str = None) -> List[WebElement]: 46 | '''Waiting on elements and return WebElements if they are present on DOM''' 47 | return self.__wait.until(ec.presence_of_all_elements_located((self.__get_selenium_by(find_by), locator)), 48 | locator_name) 49 | 50 | def get_text_from_webelements(self, elements: List[WebElement]) -> List[str]: 51 | '''The input should be a list of WebElements, where we read text from each element and Return a List[String]''' 52 | return [element.text for element in elements] 53 | 54 | def get_element_by_text(self, elements: List[WebElement], name: str) -> WebElement: 55 | '''The input should we a list of WebElements, from which we return a single WebElement found by it's name''' 56 | name = name.lower() 57 | return [element for element in elements if element.text.lower() == name][0] 58 | 59 | def delete_cookie(self, cookie_name: str) -> None: 60 | '''Delete a cookie by a name''' 61 | self.driver.delete_cookie(cookie_name) -------------------------------------------------------------------------------- /base/utils.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | 4 | class Utils: 5 | 6 | @staticmethod 7 | def join_strings(str_list: List[str]) -> str: 8 | return ",".join(str_list) 9 | -------------------------------------------------------------------------------- /pom/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vBrooklyn/Python-Selenium/7152d1c1f72de7b8d7df15cb9561335e46bd3b23/pom/__init__.py -------------------------------------------------------------------------------- /pom/homepage_nav.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | 3 | from selenium.webdriver.remote.webelement import WebElement 4 | 5 | from base.seleniumbase import SeleniumBase 6 | 7 | 8 | class HomepageNav(SeleniumBase): 9 | 10 | def __init__(self, driver): 11 | super().__init__(driver) 12 | self.driver = driver 13 | self.__nav_links: str = '#mainNavigationFobs>li' 14 | self.NAV_LINK_TEXT = 'Gifts,Women,Men,Kids & Baby,Beauty,Home,Furniture,Shoes,Jewelry,Handbags & Accessories,Now Trending,Sale' 15 | 16 | def get_nav_links(self) -> List[WebElement]: 17 | '''Return WebElements for nav links''' 18 | return self.are_visible('css', self.__nav_links, 'Header Navigation Links') 19 | 20 | def get_nav_links_text(self) -> str: 21 | '''Return all nav links text. Return format is a String with comma separated values''' 22 | nav_links = self.get_nav_links() 23 | nav_links_text = self.get_text_from_webelements(nav_links) 24 | return ','.join(nav_links_text) 25 | 26 | def get_nav_link_by_name(self, name: str) -> WebElement: 27 | '''Return a nav link WebElement, the input is a link's name''' 28 | elements = self.get_nav_links() 29 | return self.get_element_by_text(elements, name) 30 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pytest==6.2.5 2 | selenium==4.0.0 -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vBrooklyn/Python-Selenium/7152d1c1f72de7b8d7df15cb9561335e46bd3b23/tests/__init__.py -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from selenium import webdriver 3 | from selenium.webdriver.chrome.options import Options as chrome_options 4 | from selenium.webdriver.support.event_firing_webdriver import EventFiringWebDriver 5 | 6 | from abstract.selenium_listener import MyListener 7 | 8 | 9 | @pytest.fixture 10 | def get_chrome_options(): 11 | options = chrome_options() 12 | options.add_argument('chrome') # Use headless if you do not need a browser UI 13 | options.add_argument('--start-maximized') 14 | options.add_argument('--window-size=1650,900') 15 | return options 16 | 17 | 18 | @pytest.fixture 19 | def get_webdriver(get_chrome_options): 20 | options = get_chrome_options 21 | driver = webdriver.Chrome(options=options) 22 | return driver 23 | 24 | 25 | @pytest.fixture(scope='function') 26 | def setup(request, get_webdriver): 27 | driver = get_webdriver 28 | driver = EventFiringWebDriver(driver, MyListener()) 29 | url = 'https://www.macys.com/' 30 | if request.cls is not None: 31 | request.cls.driver = driver 32 | driver.get(url) 33 | yield driver 34 | driver.quit() 35 | -------------------------------------------------------------------------------- /tests/test_homepage.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from pom.homepage_nav import HomepageNav 4 | 5 | 6 | @pytest.mark.usefixtures('setup') 7 | class TestHomepage: 8 | 9 | def test_nav_links(self): 10 | homepage_nav = HomepageNav(self.driver) 11 | for indx in range(12): 12 | homepage_nav.get_nav_links()[indx].click() 13 | 14 | 15 | --------------------------------------------------------------------------------