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)