def getDataFromFile(file_name): """ :param file_name: a file with data for test case. Usually a file name consists a name of test_function :return: data(list) with rows from a file except the first row """ data = [] log = logTestExecution() with open(file_name) as f: reader = csv.reader(f, delimiter=';') next(f) # skip the first row try: for row in reader: data.append(row) except csv.Error as e: log.error('File {}, line {}: {}'.format(file_name, reader.line_num, e)) sys.exit('File {}, line {}: {}'.format(file_name, reader.line_num, e)) log.info('Got data <{}> from file <{}>'.format(data, file_name)) return data
class TestLoginPage(unittest.TestCase): log = logTestExecution() # The function defines LoginPage-instance and get the url. Ran before each test case @pytest.fixture(autouse=True) def classSetUp(self, setUp_and_tearDown): self.login_page = LoginPage(self.driver) self.driver.get(self.login_page.url) self.log.info('Testing the method of class: TestLoginPage') # Got and unpacked values (username, password, expected_message) to feed to the test from a file. @data(*getDataFromFile('GUI/test_login_failure_func.csv')) @unpack # Test different combinations of invalid credentials and error messages def test_login_failure(self, username, password, expected): self.login_page.login_button.click() result = self.login_page.loginIsNotValid(username, password, expected) assert result == True self.driver.refresh() def test_title(self): result = self.login_page.titleIsValid() assert result == True
class MainPage: log = logTestExecution() def __init__(self, driver): self.driver = driver @property def url(self): return 'https://www.southwest.com/' @property def get_depart_input(self): waits.wait_visibility(self.driver, MainPageLocators.DEPART_INPUT) return self.driver.find_element(*MainPageLocators.DEPART_INPUT) @property def get_destination_input(self): waits.wait_visibility(self.driver, MainPageLocators.DESTINATION_INPUT) return self.driver.find_element(*MainPageLocators.DESTINATION_INPUT) @property def get_book_element(self): waits.wait_visibility(self.driver, MainPageLocators.BOOK_ELEMENT) return self.driver.find_element(*MainPageLocators.BOOK_ELEMENT) @property def get_language_switcher(self): waits.wait_click(self.driver, MainPageLocators.LANGUAGE_SWITCHER) return self.driver.find_element(*MainPageLocators.LANGUAGE_SWITCHER) @property def get_hint_depart(self): waits.wait_visibility(self.driver, MainPageLocators.HINT_DEPART) return self.driver.find_element(*MainPageLocators.HINT_DEPART) @property def get_hint_destination(self): waits.wait_visibility(self.driver, MainPageLocators.HINT_DESTINATION) return self.driver.find_element(*MainPageLocators.HINT_DESTINATION) @property def get_hint_depart_options(self): menu = self.get_hint_depart waits.wait_visibility(self.driver, MainPageLocators.LI_TAGS) return menu.find_elements(*MainPageLocators.LI_TAGS) @property def get_menu_destination_options(self): menu = self.get_hint_destination waits.wait_visibility(self.driver, MainPageLocators.LI_TAGS) return menu.find_elements(*MainPageLocators.LI_TAGS) def get_control_messages(self): waits.wait_visibility(self.driver, MainPageLocators.CONTROL_MESSAGE) messages_list = self.driver.find_elements( *MainPageLocators.CONTROL_MESSAGE) self.log.info([message.text for message in messages_list]) return messages_list def clear_inputs(self): # Clear inputs for departure and destination airport depart = self.get_depart_input depart.send_keys(Keys.CONTROL + "a") depart.send_keys(Keys.DELETE) destination = self.get_destination_input destination.send_keys(Keys.CONTROL + "a") destination.send_keys(Keys.DELETE) def size_element(self, element): # DIV_LIST = [HEADER_DIV, NAVIGATION_DIV, CONTACT_DIV, FOOTER_DIV, COPYRIGHT_DIV if element == 'HEADER_DIV': waits.wait_visibility(self.driver, MainPageLocators.HEADER_DIV) return self.driver.find_element( *MainPageLocators.HEADER_DIV).size['width'] > 0 elif element == 'NAVIGATION_DIV': waits.wait_visibility(self.driver, MainPageLocators.NAVIGATION_DIV) return self.driver.find_element( *MainPageLocators.NAVIGATION_DIV).size['width'] > 0 elif element == 'CONTACT_DIV': waits.wait_visibility(self.driver, MainPageLocators.CONTACT_DIV) return self.driver.find_element( *MainPageLocators.CONTACT_DIV).size['width'] > 0 elif element == 'FOOTER_DIV': waits.wait_visibility(self.driver, MainPageLocators.FOOTER_DIV) return self.driver.find_element( *MainPageLocators.FOOTER_DIV).size['width'] > 0 elif element == 'COPYRIGHT_DIV': waits.wait_visibility(self.driver, MainPageLocators.COPYRIGHT_DIV) return self.driver.find_element( *MainPageLocators.COPYRIGHT_DIV).size['width'] > 0 def allElementsPresence(self): """ Check that the main blocks (header, footer, contacts, etc.) on the website are present, visible and with width > 0 :return: True if all the elements are found """ # DIV_LIST = [HEADER_DIV, NAVIGATION_DIV, CONTACT_DIV, FOOTER_DIV, COPYRIGHT_DIV elements = MainPageLocators.DIV_LIST for element in elements: try: result = self.size_element(element) if result is True: self.log.info( 'DIV element <{}> found successfully with width > 0'. format(element)) return True else: self.log.error( 'DIV element <{}> NOT found'.format(element)) return False except: self.log.error('DIV element <{}> NOT found'.format(element)) return False def languageSwitch(self): """ :return: True if button to switch a language are clicked """ try: switcher = self.get_language_switcher self.log.info('<language_switcher> found') switcher.click() self.log.info('Clicked SUCCESSFULLY') return True except: self.log.error('<language_switcher> NOT clickable') return False def getValue_LangAttribute(self, expected_value): """ :param expected_value: EN or ES :return: True if expected_value == value of the lang-attribute of the <html>-tag """ if expected_value.lower() == 'es': try: waits.wait_title(self.driver, MainPageLocators.TITLE_ES) waits.wait_visibility(self.driver, MainPageLocators.HTML_TAG) html_tag = self.driver.find_element(*MainPageLocators.HTML_TAG) lang_value = html_tag.get_attribute('lang') self.log.info( 'Value of the attribute @lang = {}'.format(lang_value)) if lang_value.lower() == expected_value.lower(): self.log.info( 'Expected value <{}> for property @lang for tag <html> found' .format(expected_value)) return True except: self.log.error( 'Expected value <{}> for property @lang for tag <html> NOT found' .format(expected_value)) return False elif expected_value.lower() == 'en': try: waits.wait_title(self.driver, MainPageLocators.TITLE_EN) waits.wait_visibility(self.driver, MainPageLocators.HTML_TAG) html_tag = self.driver.find_element(*MainPageLocators.HTML_TAG) lang_value = html_tag.get_attribute('lang') self.log.info( 'Value of the attribute @lang = {}'.format(lang_value)) if lang_value.lower() == expected_value.lower(): self.log.info( 'Expected value <{}> for property @lang for tag <html> found' .format(expected_value)) return True except: self.log.error( 'Expected value <{}> for property @lang for tag <html> NOT found' .format(expected_value)) return False else: self.log.error( 'Expected value is not expected'.format(expected_value)) return False def get_attributes_depart(self): ''' Check that the departure-input has attributes "aria-activedescendant" and "aria-expanded". Then check their expected values. The attributes are responsible for appearing the menu with hints. :return: True if the expected attributes and their values have been found ''' # ATTRIBUTES = {"aria-activedescendant": "LandingAirBookingSearchForm", "aria-expanded": "true"} attributes = MainPageLocators.ATTRIBUTES self.log.info('Attribute: value to check {}'.format(attributes)) try: for k in attributes.keys(): if attributes[k] in self.get_depart_input.get_attribute(k): self.log.info('Value <{}> in attribute <{}> found'.format( attributes[k], k)) else: self.log.error( 'Value <{}> in attribute <{}> NOT found'.format( attributes[k], k)) return False return True except: self.log.error('Wrong attributes or their values') return False def get_attributes_destination(self): ''' Check that the destination-input has attributes "aria-activedescendant" and "aria-expanded". Then check their expected values. The attributes are responsible for appearing the menu with hints. :return: True if the expected attributes and their values have been found ''' # ATTRIBUTES = {"aria-activedescendant": "LandingAirBookingSearchForm", "aria-expanded": "true"} attributes1 = MainPageLocators.ATTRIBUTES self.log.info('Attribute: value to check {}'.format(attributes1)) try: for k in attributes1.keys(): if attributes1[k] in self.get_destination_input.get_attribute( k): self.log.info('Value <{}> in attribute <{}> found'.format( attributes1[k], k)) else: self.log.error( 'Value <{}> in attribute <{}> NOT found'.format( attributes1[k], k)) attributes1.clear() return False return True except: self.log.error('Wrong attributes or their values') return False def listingDepartIsEnable(self): """ Check that the menu with hints for the departure-input has an arrow to move up and down if hints are more than 10. :return: True if hints are more than 10 and there are arrows to move up and down """ try: list = self.get_hint_depart_options if len(list) > 10: self.driver.find_element( *MainPageLocators.MOVE_DOWN).is_enabled() self.log.info('Element <{}> found'.format( MainPageLocators.MOVE_DOWN)) self.driver.find_element(*MainPageLocators.MOVE_UP) self.log.info('Element <{}> found'.format( MainPageLocators.MOVE_UP)) return True else: self.log.warning( 'Listing is unable: not enough options (< 10)') return False except: self.log.error('Listing NOT found') return False def listingDestinationIsEnable(self): """ Check that the menu with hints for the destination-input has an arrow to move up and down if hints are more than 10. :return: True if hints are more than 10 and there are arrows to move up and down """ try: list1 = self.get_menu_destination_options if len(list1) > 10: self.driver.find_element( *MainPageLocators.MOVE_DOWN).is_enabled() self.log.info('Element <{}> found'.format( MainPageLocators.MOVE_DOWN)) self.driver.find_element(*MainPageLocators.MOVE_UP) self.log.info('Element <{}> found'.format( MainPageLocators.MOVE_UP)) return True else: self.log.warning( 'Listing is unable: not enough options (< 10)') return False except: self.log.error('Listing NOT found') return False @property def listEnableButtonsDepart(self): """ Find the menu with hints for the departure-input. Then find the list of enable hints (buttons) to click :return: the list of the buttons which are enabled to click """ menu = self.get_hint_depart buttons = menu.find_elements(*MainPageLocators.OPTIONS) enabled_buttons = [button for button in buttons if button.is_enabled()] self.log.info('{} buttons to click found'.format(len(enabled_buttons))) return enabled_buttons # Before click def getValue_AriaLabelAttr(self, position): """ Find enable buttons to click in a menu for the departure-input and get the value of the attribute 'aria-label' for the button on the pointed <position>. :param: position to get an attribute value :return: value of the attribute 'aria-label' (text of the button on the pointed <position>). If <position> is out of range or smt goes wrong the function return value = '$$$' but not None to avoid an error in a test case """ position = int(position) button_list = self.listEnableButtonsDepart if len(button_list) > position: try: value = button_list[position].get_attribute( 'aria-label') # 'aria-label' contains the button's text self.log.info( 'Attribute value (text) found: <{}>'.format(value)) except: value = '$$$' self.log.error("Attribute NOT found.") else: value = '$$$' self.log.error( "Available buttons NOT found on the {}-th position.".format( position)) return value def getEnableButtonClick(self, position): """ Find enable buttons to click in a menu for the departure-input and click on the button on the pointed <position> :param: position to click :return: True if clicked successfully """ position = int(position) button_list = self.listEnableButtonsDepart if len(button_list) > int(position): try: button = button_list[position] button.click() self.log.info( "Click on {}-th button successfully".format(position)) return True except: self.log.error("Click failed.") return False else: self.log.error( "Available buttons NOT found on the {}-th position.".format( position)) return False
from selenium.common.exceptions import TimeoutException from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions from GUI.configfiles.log_execution import logTestExecution log = logTestExecution() # Explicit wait (<=20 sec with polling every 1 sec) with expected_conditions: clickability of the element def wait_click(driver, locator_name): try: wait = WebDriverWait(driver, 20) wait.until(expected_conditions.element_to_be_clickable(locator_name)) log.info('Element {} is clickable'.format(locator_name)) except TimeoutException: log.error('Element {} is not clickable'.format(locator_name)) # Explicit wait (<=20 sec with polling every 1 sec) with expected_conditions: visibility of the element def wait_visibility(driver, locator_name): try: wait = WebDriverWait(driver, 20) wait.until( expected_conditions.visibility_of_element_located(locator_name)) log.info('Element {} is visible'.format(locator_name)) except TimeoutException: log.error('Element {} is not visible'.format(locator_name)) # Explicit wait (<=20 sec with polling every 1 sec) with expected_conditions: text in title def wait_title(driver, text):
class LoginPage: log = logTestExecution() def __init__(self, driver): self.driver = driver @property def url(self): return 'https://www.southwest.com/' @property def login_button(self): waits.wait_click(self.driver, LoginPageLocators.LOGIN_BUTTON) return self.driver.find_element(*LoginPageLocators.LOGIN_BUTTON) @property def username_field(self): waits.wait_visibility(self.driver, LoginPageLocators.USERNAME_FIELD) return self.driver.find_element(*LoginPageLocators.USERNAME_FIELD) @property def password_field(self): waits.wait_visibility(self.driver, LoginPageLocators.PASSWORD_FIELD) return self.driver.find_element(*LoginPageLocators.PASSWORD_FIELD) @property def submit_button(self): waits.wait_visibility(self.driver, LoginPageLocators.SUBMIT) return self.driver.find_element(*LoginPageLocators.SUBMIT) @property def get_title(self): return self.driver.title @property def error_messages(self): waits.wait_visibility(self.driver, LoginPageLocators.ERROR_MESSAGE_LOCATOR) return self.driver.find_elements( *LoginPageLocators.ERROR_MESSAGE_LOCATOR) def titleIsValid(self): try: if LoginPageLocators.TITLE in self.get_title: self.log.info('The title <{}> found'.format(self.get_title)) return True else: self.log.error('The WRONG title <{}> found'.format( self.get_title)) return False except: self.log.error('The title NOT found') return False def login(self, username, password): self.username_field.send_keys(username) self.log.info('Use username = {}'.format(username)) self.password_field.send_keys(password) self.log.info('Use password = {}'.format(password)) self.submit_button.click() def loginIsNotValid(self, username, password, expected): """ :param username, password :param expected: message that is expected to see after entering and submitting login and password :return: True if inputs for username/password are found, data are submitted and expected messages are found """ try: self.login(username, password) for message in self.error_messages: if (expected[0] in message.text) or (expected[1] in message.text): self.log.info('The expected message <{}> found'.format( message.text)) return True else: self.log.error( 'The expected message <{}> NOT found'.format( message.text)) return False except: self.log.error( 'Something is wrong during attempting to fail the login with invalid username <{}> and ' 'password <{}>'.format(username, password)) return False
class TestMainPage(unittest.TestCase): log = logTestExecution() # The function defines MainPage-instance and get the url. Ran before each test case @pytest.fixture(autouse=True) def classSetUp(self, setUp_and_tearDown): self.page = MainPage(self.driver) # Used to make a few assertions in one test case w/o failure in the middle one. self.soft_assert = SoftAssertion(self.driver) self.driver.get(self.page.url) self.log.info('Testing the method of class: TestMainPage') # The presence of the main blocks: ['HEADER_DIV', 'NAVIGATION_DIV', 'CONTACT_DIV', 'FOOTER_DIV', 'COPYRIGHT_DIV'] def test_element_presence(self): result = self.page.allElementsPresence() assert result == True # Used softAssertion to test switching a language # click on "Español", check the attribute in <html>-tag, the current url, key-words on the page def test_EN_to_ES(self): result = self.page.languageSwitch() self.soft_assert.assert_it(result, 'Language switched (EN to ES)') result2 = self.page.getValue_LangAttribute('ES') self.soft_assert.assert_it(result2, 'Language property in DOM found') result3 = 'reservar' in self.page.get_book_element.text.lower() self.soft_assert.assert_it(result3, 'Book-element found') result4 = 'english' in self.page.get_language_switcher.text.lower() self.soft_assert.assert_it(result4, "Button's text switched") result5 = 'espanol' in self.driver.current_url self.soft_assert.assert_last(result5, 'Current URL verified') # Used softAssertion to test switching a language # click on "Español" and back to "English", check the <html>-tag's attribute, the current url, key-words def test_ES_to_EN(self): result = self.page.languageSwitch() self.soft_assert.assert_it(result, 'Language switched (EN to ES)') # Test doesn't work w/o it (although there is explicit wait). # I don't know why, but it's easier leave it than spend 2 more hours to investigate. time.sleep(2) result2 = self.page.languageSwitch() self.soft_assert.assert_it(result2, 'Language switched (ES to EN)') result3 = self.page.getValue_LangAttribute('EN') self.soft_assert.assert_it(result3, 'Language property in DOM found') result4 = 'book' in self.page.get_book_element.text.lower() self.soft_assert.assert_it(result4, 'Book-element found') result5 = 'español' in self.page.get_language_switcher.text.lower() self.soft_assert.assert_it(result5, "Button's text switched") result6 = 'espanol' not in self.driver.current_url self.soft_assert.assert_last(result6, 'Current URL verified') def test_hint_appearance_depart(self): depart_port = 'new' # Find the departure input and enter data depart = self.page.get_depart_input depart.send_keys(depart_port) # The <input>-tag get the attributes that are responsible for the menu with hints result = self.page.get_attributes_depart() self.soft_assert.assert_it(result, 'Attributes for departure input found') # Find the menu-element result2 = self.page.get_hint_depart is not None self.soft_assert.assert_last(result2, 'Hint for departure form found') self.page.clear_inputs() def test_hint_appearance_destination(self): destin_port = 'wash' # Find the destination input and enter data destination = self.page.get_destination_input destination.send_keys(destin_port) # The <input>-tag get the attributes that are responsible for the menu with hints result = self.page.get_attributes_destination() self.soft_assert.assert_it(result, 'Attributes for destination input found') # Find the menu-element result2 = self.page.get_hint_destination is not None self.soft_assert.assert_last(result2, 'Hint for destination form found') self.page.clear_inputs() # This one is unstable: sometimes a destination menu covers very quick (mouse hover and time.sleep don't help) def test_hint_listing(self): depart_port = 'new' destin_port = 'san' # Find the departure input and enter data depart = self.page.get_depart_input depart.send_keys(depart_port) # Menu appears with > 10 options and buttons to move up and down result = self.page.listingDepartIsEnable() self.soft_assert.assert_it(result, 'Listing elements in departure hint found') # Find the destination input and enter data destin = self.page.get_destination_input destin.send_keys(destin_port) # Menu appears with > 10 options and buttons to move up and down result2 = self.page.listingDestinationIsEnable() self.soft_assert.assert_last( result2, 'Listing elements in destination hint found') self.page.clear_inputs() # Got and unpacked values (depart_text, position, expected_depart_text) to feed to the test from a file. @data(*getDataFromFile('GUI/test_choose_hint_option_depart_func.csv')) @unpack def test_choose_hint_option_depart(self, depart_text, position, expected_depart_text): # Find the departure input and enter data depart = self.page.get_depart_input depart.send_keys(depart_text) # Before click # Get the value of the attribute 'aria-label' for the button(button's text) we are going to click further valueAriaLabelAttr = self.page.getValue_AriaLabelAttr(position) # Then click result = self.page.getEnableButtonClick(position) self.soft_assert.assert_it(result, 'Click on the button occurred') # After click # Get the texts of control messages (under an input) control_messages = self.page.get_control_messages() # Apply a soft assertion to check that texts in input and under it are the same result2 = control_messages[ 0].text in valueAriaLabelAttr # 0-th element for departure, 1-th for destination self.soft_assert.assert_it( result2, 'Control message <{}> found in button attribute value <{}>'.format( control_messages[0].text, valueAriaLabelAttr)) # Apply a soft assertion to check that control messages are equal to expected one. result3 = expected_depart_text in control_messages[0].text self.soft_assert.assert_last( result3, 'Expected text <{}> found in control massage <{}>'.format( expected_depart_text, control_messages[0].text)) self.page.clear_inputs()