pages/desktop/base.py (462 lines of code) (raw):

import requests from pypom import Page, Region from selenium.common.exceptions import StaleElementReferenceException from selenium.webdriver.common.by import By from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.select import Select from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC class Base(Page): _url = "{base_url}" _amo_header = (By.CLASS_NAME, "Header") def __init__(self, selenium, base_url, **kwargs): super(Base, self).__init__(selenium, base_url, timeout=30, **kwargs) def wait_for_page_to_load(self): self.wait.until( lambda _: self.find_element(*self._amo_header).is_displayed(), message="AMO header was not loaded", ) return self def wait_for_title_update(self, term): self.wait.until( EC.title_contains(term), message=f"Page title was {self.driver.title}, expected {term}", ) return self def wait_for_current_url(self, term): self.wait.until( EC.url_contains(term), message=f"The url was {self.driver.current_url}" ) return self def wait_for_element_to_be_displayed(self, element): self.wait.until(EC.visibility_of_element_located(element)) return self def wait_for_element_to_be_clickable(self, element): self.wait.until(EC.element_to_be_clickable(element)) return self @property def header(self): return Header(self) @property def footer(self): return Footer(self) @property def logged_in(self): """Returns True if a user is logged in. Since the user element can become stale sometimes and causes the login test to fail, a StaleElementReferenceException was added to catch this error and wait for the element to be located again""" count = 0 while count < 5: try: self.wait.until( EC.visibility_of_element_located( ( By.CSS_SELECTOR, ".Header-user-and-external-links .DropdownMenu-button-text", ) ), message="LOGIN FAILED: The user name was not displayed in the header after login.", ) break except StaleElementReferenceException as exception: print(f"{exception}: Try to find the element again") count += 1 return self @property def search(self): return self.header.SearchBox(self) def login(self, user): fxa = self.header.click_login() # wait for the FxA login page to load self.wait.until( EC.visibility_of_element_located((By.NAME, "email")), message=f"FxA email input field was not displayed in {self.driver.current_url}", ) fxa.account(user) # verifies that the AMO page was fully loaded after login WebDriverWait(self.driver, 30).until( EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText")), message="AMO was not loaded properly after login.", ) # assess that the user has been logged in self.wait.until( lambda _: self.logged_in, message=f"Log in flow was not successful. URL at fail time was {self.driver.current_url}", ) def register(self): fxa_register_page = self.header.click_login() self.wait.until(EC.visibility_of_element_located((By.NAME, "email"))) fxa_register_page.fxa_register() # wait for transition between FxA page and AMO self.wait.until(EC.url_contains("addons")) self.wait.until(lambda _: self.logged_in) def logout(self): self.header.click_logout() class Header(Region): _root_locator = (By.CLASS_NAME, "Header") _header_title_locator = (By.CLASS_NAME, "Header-title") _firefox_logo_locator = (By.CLASS_NAME, "Header-title") _extensions_locator = ( By.CSS_SELECTOR, ".SectionLinks \ > li:nth-child(1) > a:nth-child(1)", ) _extensions_active_button_locator = ( By.CLASS_NAME, "SectionLinks-link-extension.SectionLinks-link--active", ) _login_locator = (By.CLASS_NAME, "Header-authenticate-button") _user_locator = ( By.CSS_SELECTOR, ".Header-user-and-external-links .DropdownMenu-button-text", ) _account_dropdown_locator = ( By.CSS_SELECTOR, ".DropdownMenu.Header-authenticate-button .DropdownMenu-items", ) _user_menu_links_locator = ( By.CSS_SELECTOR, ".Header-user-and-external-links .DropdownMenuItem-link a", ) _logout_locator = ( By.CSS_SELECTOR, ".DropdownMenu-items .Header-logout-button button", ) _more_menu_locator = ( By.CSS_SELECTOR, ".Header-SectionLinks .SectionLinks-dropdown", ) _more_dropdown_locator = ( By.CSS_SELECTOR, ".SectionLinks-dropdown .DropdownMenu-items", ) _more_dropdown_sections_locator = (By.CSS_SELECTOR, ".DropdownMenuItem-section") _more_dropdown_links_locator = (By.CSS_SELECTOR, ".DropdownMenuItem a") _themes_locator = ( By.CSS_SELECTOR, ".SectionLinks > li:nth-child(2) > \ a:nth-child(1)", ) _themes_active_button_locator = ( By.CLASS_NAME, "SectionLinks-link-theme.SectionLinks-link--active", ) _devhub_locator = (By.CLASS_NAME, "Header-developer-hub-link") _extension_workshop_locator = (By.CLASS_NAME, "Header-extension-workshop-link") _blog_link_locator = (By.CLASS_NAME, "Header-blog-link") _active_link_locator = (By.CLASS_NAME, "SectionLinks-link--active") def click_extensions(self): self.wait.until(EC.element_to_be_clickable(self._extensions_locator)) self.find_element(*self._extensions_locator).click() from pages.desktop.frontend.extensions import Extensions return Extensions(self.driver, self.page.base_url).wait_for_page_to_load() @property def extensions_text(self): self.wait.until(EC.visibility_of_element_located(self._extensions_locator)) return self.find_element(*self._extensions_locator).text @property def extensions_button_active(self): """Checks that the 'Extensions' menu button is highlighted when selected""" self.wait.until( EC.visibility_of_element_located(self._extensions_active_button_locator) ) return self.find_element(*self._extensions_active_button_locator) @property def themes_link(self): self.wait.until(EC.visibility_of_element_located(self._themes_locator)) return self.find_element(*self._themes_locator) def click_themes(self): self.wait.until(EC.element_to_be_clickable(self._themes_locator)) self.find_element(*self._themes_locator).click() from pages.desktop.frontend.themes import Themes return Themes(self.driver, self.page.base_url).wait_for_page_to_load() @property def themes_button_active(self): """Checks that the 'Themes' menu button is highlighted when selected""" self.wait.until( EC.visibility_of_element_located(self._themes_active_button_locator) ) return self.find_element(*self._themes_active_button_locator) def click_title(self): self.find_element(*self._header_title_locator).click() from pages.desktop.frontend.home import Home return Home(self.driver, self.page.base_url).wait_for_page_to_load() @property def login_button(self): return self.find_element(*self._login_locator) def click_login(self): self.find_element(*self._login_locator).click() from pages.desktop.frontend.login import Login return Login(self.driver, self.page.base_url) def user_header_display_name(self, value): WebDriverWait( self.driver, 30, ignored_exceptions=StaleElementReferenceException ).until( EC.text_to_be_present_in_element(self._user_locator, value), message="The expected displayed name was not visible", ) def click_logout(self): user = WebDriverWait( self.driver, 30, ignored_exceptions=StaleElementReferenceException ).until( EC.element_to_be_clickable( ( By.CSS_SELECTOR, ".Header-authenticate-button", ) ) ) action = ActionChains(self.driver) action.move_to_element(user) action.pause(3) action.perform() # assigning the webelement to a variable before initializing the action chains can lead # to stale element errors since the dropdown state changes when we hover over it logout = WebDriverWait( self.driver, 20, ignored_exceptions=StaleElementReferenceException ).until(EC.element_to_be_clickable(self._logout_locator)) action.move_to_element(logout) action.pause(3) action.click() action.pause(3) action.perform() self.wait.until( lambda s: self.is_element_displayed(*self._login_locator), message="The login button was not displayed after logout", ) def user_menu_link(self, count): self.wait.until(EC.visibility_of_element_located(self._user_menu_links_locator)) return self.find_elements(*self._user_menu_links_locator)[count] def click_user_menu_links(self, count, landing_page): user = WebDriverWait( self.driver, 30, ignored_exceptions=StaleElementReferenceException ).until( EC.element_to_be_clickable( ( By.CSS_SELECTOR, ".Header-authenticate-button", ) ) ) action = ActionChains(self.driver) action.move_to_element(user) action.pause(2) action.perform() link = WebDriverWait( self.driver, 20, ignored_exceptions=StaleElementReferenceException ).until(EC.element_to_be_clickable(self.user_menu_link(count))) action.move_to_element(link) action.pause(2) action.click() action.perform() # waits for the landing page to open self.wait.until( EC.visibility_of_element_located((By.CSS_SELECTOR, landing_page)), message=f"Expected page not loaded; page was {self.driver.current_url}", ) @property def more_menu_link(self): self.wait.until(EC.visibility_of_element_located(self._more_menu_locator)) return self.find_element(*self._more_menu_locator) @property def more_menu_dropdown_sections(self): return self.find_elements(*self._more_dropdown_sections_locator) @property def more_menu_dropdown_links(self): return self.find_elements(*self._more_dropdown_links_locator) def more_menu(self, item=None): menu = self.find_element(*self._more_menu_locator) dropdown = self.find_element(*self._more_dropdown_locator) link = menu.find_elements(*self._more_dropdown_links_locator) # Create an action chain clicking on the elements of the dropdown more menu action = ActionChains(self.driver) action.move_to_element(menu) action.pause(2) action.move_to_element(dropdown) action.move_to_element(link[item]) action.click(link[item]) action.pause(2) action.perform() @property def developer_hub_link(self): self.wait.until(EC.visibility_of_element_located(self._devhub_locator)) return self.find_element(*self._devhub_locator) def click_developer_hub(self): self.wait.until(EC.element_to_be_clickable(self._devhub_locator)) self.find_element(*self._devhub_locator).click() self.wait.until( EC.number_of_windows_to_be(2), message=f"Number of windows was {len(self.driver.window_handles)}, expected 2", ) new_tab = self.driver.window_handles[1] self.driver.switch_to.window(new_tab) self.wait.until( EC.visibility_of_element_located((By.CLASS_NAME, "DevHub-Navigation-Logo")), message=f"DevHub homepage not loaded; page was {self.driver.current_url}", ) @property def extension_workshop_link(self): self.wait.until( EC.visibility_of_element_located(self._extension_workshop_locator) ) return self.find_element(*self._extension_workshop_locator) def click_extension_workshop(self): self.wait.until(EC.element_to_be_clickable(self._extension_workshop_locator)) self.find_element(*self._extension_workshop_locator).click() self.wait.until( EC.number_of_windows_to_be(2), message=f"Number of windows was {len(self.driver.window_handles)}, expected 2", ) new_tab = self.driver.window_handles[1] self.driver.switch_to.window(new_tab) self.wait.until( EC.visibility_of_element_located((By.CLASS_NAME, "logo")), message=f"Extension Workshop not loaded; page was {self.driver.current_url}", ) @property def firefox_addons_blog_link(self): self.wait.until(EC.visibility_of_element_located(self._blog_link_locator)) return self.find_element(*self._blog_link_locator) def click_firefox_addons_blog(self): self.wait.until(EC.element_to_be_clickable(self._blog_link_locator)) self.find_element(*self._blog_link_locator).click() @property def is_active_link(self): return self.find_element(*self._active_link_locator).text class SearchBox(Region): _root_locator = (By.CLASS_NAME, "AutoSearchInput") _query_field_locator = (By.ID, "AutoSearchInput-q") _search_suggestions_list_locator = ( By.CLASS_NAME, "AutoSearchInput-suggestions-list", ) _search_suggestions_item_locator = ( By.CLASS_NAME, "AutoSearchInput-suggestions-item", ) _search_textbox_locator = (By.CLASS_NAME, "AutoSearchInput-query") _search_item_name = (By.CSS_SELECTOR, ".SearchSuggestion-name") _highlighted_selected_locator = ( By.CSS_SELECTOR, ".AutoSearchInput-suggestions-item--highlighted", ) @property def search_field(self): self.wait.until(EC.visibility_of_element_located(self._query_field_locator)) return self.find_element(*self._query_field_locator) def search_for(self, term, execute=True): self.wait.until( EC.visibility_of_element_located(self._search_textbox_locator) ) textbox = self.find_element(*self._search_textbox_locator) textbox.click() textbox.send_keys(term) # Send 'enter' since the mobile page does not have a submit button if execute: textbox.send_keys(Keys.ENTER) from pages.desktop.frontend.search import Search return Search(self.driver, self.page).wait_for_page_to_load() WebDriverWait(self.driver, 30).until( EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText")) ) return self.search_suggestions @property def search_suggestions(self): self.wait.until( lambda _: self.is_element_displayed( *self._search_suggestions_list_locator ), message="Search suggestions list did not open", ) WebDriverWait(self.driver, 30).until( EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText")) ) el_list = self.find_element(*self._search_suggestions_list_locator) items = el_list.find_elements(*self._search_suggestions_item_locator) return [self.SearchSuggestionItem(self.page, el) for el in items] @property def highlighted_suggestion(self): self.wait.until( EC.visibility_of_element_located(self._highlighted_selected_locator) ) return self.find_element(*self._highlighted_selected_locator) class SearchSuggestionItem(Region): _item_name_locator = (By.CLASS_NAME, "SearchSuggestion-name") _item_icon_locator = (By.CLASS_NAME, "SearchSuggestion-icon") _promoted_icon_locator = (By.CSS_SELECTOR, ".IconPromotedBadge > span") @property def name(self): self.wait.until( EC.visibility_of_element_located(self._item_name_locator) ) return self.find_element(*self._item_name_locator).text @property def addon_icon(self): self.wait.until( EC.visibility_of_element_located(self._item_icon_locator) ) return self.find_element(*self._item_icon_locator) @property def promoted_icon(self): WebDriverWait(self.driver, 10).until( EC.visibility_of_element_located(self._promoted_icon_locator), message="Promoted icon was not found for these search suggestions", ) return self.find_element(*self._promoted_icon_locator).text @property def select(self): self.root.click() from pages.desktop.frontend.details import Detail return Detail(self.driver, self.page).wait_for_page_to_load() class Footer(Region): _root_locator = (By.CSS_SELECTOR, ".Footer-wrapper") _footer_amo_links_locator = (By.CSS_SELECTOR, ".Footer-amo-links") _footer_browsers_links_locator = (By.CSS_SELECTOR, ".Footer-browsers-links") _footer_products_links_locator = (By.CSS_SELECTOR, ".Footer-product-links") _footer_mozilla_link_locator = (By.CSS_SELECTOR, ".Footer-mozilla-link") _footer_social_locator = (By.CSS_SELECTOR, ".Footer-links-social") _footer_links_locator = (By.CSS_SELECTOR, ".Footer-links li a") _footer_legal_locator = (By.CSS_SELECTOR, ".Footer-legal-links ") _footer_copyright_links_locator = (By.CSS_SELECTOR, ".Footer-copyright a") _copyright_message_locator = (By.CSS_SELECTOR, ".Footer-copyright") _language_picker_locator = (By.ID, "lang-picker") @property def addon_links(self): self.wait.until( EC.visibility_of_element_located(self._footer_amo_links_locator) ) element = self.find_element(*self._footer_amo_links_locator) return element.find_elements(*self._footer_links_locator) @property def browsers_links(self): self.wait.until( EC.visibility_of_element_located(self._footer_browsers_links_locator) ) element = self.find_element(*self._footer_browsers_links_locator) return element.find_elements(*self._footer_links_locator) @property def products_links(self): self.wait.until( EC.visibility_of_element_located(self._footer_products_links_locator) ) element = self.find_element(*self._footer_products_links_locator) return element.find_elements(*self._footer_links_locator) @property def mozilla_link(self): self.wait.until( EC.visibility_of_element_located(self._footer_mozilla_link_locator) ) return self.find_element(*self._footer_mozilla_link_locator) @property def social_links(self): self.wait.until(EC.visibility_of_element_located(self._footer_social_locator)) element = self.find_element(*self._footer_social_locator) return element.find_elements(By.CSS_SELECTOR, "li a") @property def legal_links(self): self.wait.until(EC.visibility_of_element_located(self._footer_legal_locator)) element = self.find_element(*self._footer_legal_locator) return element.find_elements(By.CSS_SELECTOR, "li a") @property def copyright_links(self): self.wait.until( EC.visibility_of_element_located(self._footer_copyright_links_locator) ) return self.find_elements(*self._footer_copyright_links_locator) @property def copyright_message(self): self.wait.until( EC.visibility_of_element_located(self._copyright_message_locator) ) return self.find_element(*self._copyright_message_locator) def language_picker(self, value): self.wait.until(EC.visibility_of_element_located(self._language_picker_locator)) select = Select(self.find_element(*self._language_picker_locator)) select.select_by_visible_text(value)