Schek/add_explicit_waits_for_the_rest_of_the_elements (#852)
* commit with waits added to several pages * reverted changes for review and formatted files with black
This commit is contained in:
Родитель
5d9640b8ec
Коммит
eca60af776
Двоичный файл не отображается.
|
@ -1 +1 @@
|
|||
w0mmdr9zr2n0n1n3i3zgf7xisjwmu7el
|
||||
yqi0af1kceg0mb31low9l318o3qtm8xc
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -7,25 +7,24 @@ from selenium.webdriver.support import expected_conditions as EC
|
|||
|
||||
|
||||
class AboutAddons(Page):
|
||||
|
||||
_addon_cards_locator = (By.CLASS_NAME, 'card.addon')
|
||||
_search_box_locator = (By.CSS_SELECTOR, '.main-search search-textbox')
|
||||
_addon_cards_locator = (By.CLASS_NAME, "card.addon")
|
||||
_search_box_locator = (By.CSS_SELECTOR, ".main-search search-textbox")
|
||||
_extension_tab_button_locator = (By.CSS_SELECTOR, 'button[name = "extension"]')
|
||||
_theme_tab_button_locator = (By.CSS_SELECTOR, 'button[name = "theme"]')
|
||||
_dictionary_tab_button_locator = (By.CSS_SELECTOR, 'button[name = "dictionary"]')
|
||||
_langpack_tab_button_locator = (By.CSS_SELECTOR, 'button[name = "locale"]')
|
||||
_extension_disable_toggle_locator = (By.CLASS_NAME, 'extension-enable-button')
|
||||
_enabled_theme_status_locator = (By.CLASS_NAME, 'card.addon')
|
||||
_installed_addon_cards_locator = (By.CSS_SELECTOR, '.card.addon')
|
||||
_enabled_theme_image_locator = (By.CLASS_NAME, 'card-heading-image')
|
||||
_installed_addon_name_locator = (By.CSS_SELECTOR, '.addon-name a')
|
||||
_installed_addon_author_locator = (By.CSS_SELECTOR, '.addon-detail-row-author a')
|
||||
_find_more_addons_button_locator = (By.CLASS_NAME, 'primary')
|
||||
_extension_disable_toggle_locator = (By.CLASS_NAME, "extension-enable-button")
|
||||
_enabled_theme_status_locator = (By.CLASS_NAME, "card.addon")
|
||||
_installed_addon_cards_locator = (By.CSS_SELECTOR, ".card.addon")
|
||||
_enabled_theme_image_locator = (By.CLASS_NAME, "card-heading-image")
|
||||
_installed_addon_name_locator = (By.CSS_SELECTOR, ".addon-name a")
|
||||
_installed_addon_author_locator = (By.CSS_SELECTOR, ".addon-detail-row-author a")
|
||||
_find_more_addons_button_locator = (By.CLASS_NAME, "primary")
|
||||
_installed_extension_version_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-detail-row-version',
|
||||
".addon-detail-row-version",
|
||||
)
|
||||
_options_button_locator = (By.CSS_SELECTOR, '.more-options-button')
|
||||
_options_button_locator = (By.CSS_SELECTOR, ".more-options-button")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(
|
||||
|
@ -34,6 +33,7 @@ class AboutAddons(Page):
|
|||
return self
|
||||
|
||||
def search_box(self, value):
|
||||
self.wait.until(EC.visibility_of_element_located(self._search_box_locator))
|
||||
search_field = self.find_element(*self._search_box_locator)
|
||||
search_field.send_keys(value)
|
||||
# send Enter to initiate search redirection to AMO
|
||||
|
@ -41,7 +41,7 @@ class AboutAddons(Page):
|
|||
# AMO search results open in a new tab, so we need to switch windows
|
||||
self.wait.until(
|
||||
EC.number_of_windows_to_be(2),
|
||||
message=f'Number of windows was {len(self.driver.window_handles)}, expected 2',
|
||||
message=f"Number of windows was {len(self.driver.window_handles)}, expected 2",
|
||||
)
|
||||
self.driver.switch_to.window(self.driver.window_handles[1])
|
||||
from pages.desktop.frontend.search import Search
|
||||
|
@ -49,42 +49,52 @@ class AboutAddons(Page):
|
|||
return Search(self.driver, self.base_url).wait_for_page_to_load()
|
||||
|
||||
def click_extensions_side_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._extension_tab_button_locator))
|
||||
self.find_element(*self._extension_tab_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.text_to_be_present_in_element(
|
||||
(By.CLASS_NAME, 'list-section-heading'), 'Enabled'
|
||||
(By.CLASS_NAME, "list-section-heading"), "Enabled"
|
||||
)
|
||||
)
|
||||
|
||||
def click_themes_side_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._theme_tab_button_locator))
|
||||
self.find_element(*self._theme_tab_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.text_to_be_present_in_element(
|
||||
(By.CLASS_NAME, 'list-section-heading'), 'Enabled'
|
||||
(By.CLASS_NAME, "list-section-heading"), "Enabled"
|
||||
)
|
||||
)
|
||||
|
||||
def click_dictionaries_side_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._dictionary_tab_button_locator))
|
||||
self.find_element(*self._dictionary_tab_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.text_to_be_present_in_element(
|
||||
(By.CLASS_NAME, 'list-section-heading'), 'Enabled'
|
||||
(By.CLASS_NAME, "list-section-heading"), "Enabled"
|
||||
)
|
||||
)
|
||||
|
||||
def click_language_side_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._langpack_tab_button_locator))
|
||||
self.find_element(*self._langpack_tab_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.text_to_be_present_in_element(
|
||||
(By.CLASS_NAME, 'list-section-heading'), 'Enabled'
|
||||
(By.CLASS_NAME, "list-section-heading"), "Enabled"
|
||||
)
|
||||
)
|
||||
|
||||
def disable_extension(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._extension_disable_toggle_locator)
|
||||
)
|
||||
self.find_element(*self._extension_disable_toggle_locator).click()
|
||||
|
||||
@property
|
||||
def installed_addon_cards(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._installed_addon_cards_locator)
|
||||
)
|
||||
return self.find_elements(*self._installed_addon_cards_locator)
|
||||
|
||||
@property
|
||||
|
@ -93,31 +103,44 @@ class AboutAddons(Page):
|
|||
|
||||
@property
|
||||
def installed_addon_author_name(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._installed_addon_author_locator)
|
||||
)
|
||||
return self.find_element(*self._installed_addon_author_locator).text
|
||||
|
||||
@property
|
||||
def enabled_theme_active_status(self):
|
||||
"""Verifies if a theme is enabled"""
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._enabled_theme_status_locator)
|
||||
)
|
||||
el = self.find_elements(*self._enabled_theme_status_locator)
|
||||
return el[0].get_attribute('active')
|
||||
return el[0].get_attribute("active")
|
||||
|
||||
@property
|
||||
def enabled_theme_image(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._enabled_theme_image_locator)
|
||||
)
|
||||
return self.find_elements(*self._enabled_theme_image_locator)[0].get_attribute(
|
||||
'src'
|
||||
"src"
|
||||
)
|
||||
|
||||
@property
|
||||
def addon_cards_items(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_cards_locator))
|
||||
items = self.find_elements(*self._addon_cards_locator)
|
||||
return [self.AddonCards(self, el) for el in items]
|
||||
|
||||
def click_find_more_addons(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._find_more_addons_button_locator)
|
||||
)
|
||||
self.find_element(*self._find_more_addons_button_locator).click()
|
||||
# this button opens AMO homepage in a new tab
|
||||
self.wait.until(
|
||||
EC.number_of_windows_to_be(2),
|
||||
message=f'Number of windows was {len(self.driver.window_handles)}, expected 2',
|
||||
message=f"Number of windows was {len(self.driver.window_handles)}, expected 2",
|
||||
)
|
||||
self.driver.switch_to.window(self.driver.window_handles[1])
|
||||
from pages.desktop.frontend.home import Home
|
||||
|
@ -126,24 +149,28 @@ class AboutAddons(Page):
|
|||
|
||||
@property
|
||||
def installed_version_number(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._installed_extension_version_locator)
|
||||
)
|
||||
return self.find_element(
|
||||
*self._installed_extension_version_locator
|
||||
).text.replace('Version\n', '')
|
||||
).text.replace("Version\n", "")
|
||||
|
||||
def click_options_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._options_button_locator))
|
||||
self.find_element(*self._options_button_locator).click()
|
||||
|
||||
class AddonCards(Region):
|
||||
_theme_image_locator = (By.CLASS_NAME, 'card-heading-image')
|
||||
_extension_icon_locator = (By.CLASS_NAME, 'card-heading-icon')
|
||||
_disco_addon_name_locator = (By.CLASS_NAME, 'disco-addon-name')
|
||||
_disco_addon_author_locator = (By.CSS_SELECTOR, '.disco-addon-author a')
|
||||
_extension_summary_locator = (By.CLASS_NAME, 'disco-description-main')
|
||||
_theme_image_locator = (By.CLASS_NAME, "card-heading-image")
|
||||
_extension_icon_locator = (By.CLASS_NAME, "card-heading-icon")
|
||||
_disco_addon_name_locator = (By.CLASS_NAME, "disco-addon-name")
|
||||
_disco_addon_author_locator = (By.CSS_SELECTOR, ".disco-addon-author a")
|
||||
_extension_summary_locator = (By.CLASS_NAME, "disco-description-main")
|
||||
_extension_rating_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.disco-description-statistics moz-five-star',
|
||||
".disco-description-statistics moz-five-star",
|
||||
)
|
||||
_extension_users_count_locator = (By.CLASS_NAME, 'disco-user-count')
|
||||
_extension_users_count_locator = (By.CLASS_NAME, "disco-user-count")
|
||||
_addon_install_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'button[action="install-addon"]',
|
||||
|
@ -160,25 +187,35 @@ class AboutAddons(Page):
|
|||
|
||||
@property
|
||||
def theme_image(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._theme_image_locator))
|
||||
return self.find_element(*self._theme_image_locator)
|
||||
|
||||
@property
|
||||
def extension_image(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._extension_icon_locator)
|
||||
)
|
||||
return self.find_element(*self._extension_icon_locator)
|
||||
|
||||
@property
|
||||
def disco_addon_name(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._disco_addon_name_locator)
|
||||
)
|
||||
return self.find_element(*self._disco_addon_name_locator)
|
||||
|
||||
@property
|
||||
def disco_addon_author(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._disco_addon_author_locator)
|
||||
)
|
||||
return self.find_element(*self._disco_addon_author_locator)
|
||||
|
||||
def click_disco_addon_author(self):
|
||||
self.disco_addon_author.click()
|
||||
self.wait.until(
|
||||
EC.number_of_windows_to_be(2),
|
||||
message=f'Number of windows was {len(self.driver.window_handles)}, expected 2',
|
||||
message=f"Number of windows was {len(self.driver.window_handles)}, expected 2",
|
||||
)
|
||||
self.driver.switch_to.window(self.driver.window_handles[1])
|
||||
from pages.desktop.frontend.details import Detail
|
||||
|
@ -187,26 +224,38 @@ class AboutAddons(Page):
|
|||
|
||||
@property
|
||||
def disco_extension_summary(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._extension_summary_locator)
|
||||
)
|
||||
return self.find_element(*self._extension_summary_locator).text
|
||||
|
||||
@property
|
||||
def disco_extension_rating(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._extension_rating_locator)
|
||||
)
|
||||
return self.find_element(*self._extension_rating_locator)
|
||||
|
||||
@property
|
||||
def rating_score(self):
|
||||
return self.disco_extension_rating.get_attribute('rating')
|
||||
return self.disco_extension_rating.get_attribute("rating")
|
||||
|
||||
@property
|
||||
def disco_extension_users(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._extension_users_count_locator)
|
||||
)
|
||||
return self.find_element(*self._extension_users_count_locator)
|
||||
|
||||
@property
|
||||
def user_count(self):
|
||||
return int(
|
||||
self.disco_extension_users.text.replace('Users: ', '').replace(',', '')
|
||||
self.disco_extension_users.text.replace("Users: ", "").replace(",", "")
|
||||
)
|
||||
|
||||
@property
|
||||
def install_button(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._addon_install_button_locator)
|
||||
)
|
||||
return self.find_element(*self._addon_install_button_locator)
|
||||
|
|
|
@ -12,8 +12,8 @@ from selenium.webdriver.support import expected_conditions as EC
|
|||
|
||||
|
||||
class Base(Page):
|
||||
_url = '{base_url}'
|
||||
_amo_header = (By.CLASS_NAME, 'Header')
|
||||
_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)
|
||||
|
@ -21,33 +21,29 @@ class Base(Page):
|
|||
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',
|
||||
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}',
|
||||
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}'
|
||||
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)
|
||||
)
|
||||
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)
|
||||
)
|
||||
self.wait.until(EC.element_to_be_clickable(element))
|
||||
return self
|
||||
|
||||
@property
|
||||
|
@ -70,14 +66,14 @@ class Base(Page):
|
|||
EC.visibility_of_element_located(
|
||||
(
|
||||
By.CSS_SELECTOR,
|
||||
'.Header-user-and-external-links .DropdownMenu-button-text',
|
||||
".Header-user-and-external-links .DropdownMenu-button-text",
|
||||
)
|
||||
),
|
||||
message='LOGIN FAILED: The user name was not displayed in the header after login.',
|
||||
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')
|
||||
print(f"{exception}: Try to find the element again")
|
||||
count += 1
|
||||
return self
|
||||
|
||||
|
@ -89,27 +85,27 @@ class Base(Page):
|
|||
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}',
|
||||
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.',
|
||||
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}',
|
||||
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')))
|
||||
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(EC.url_contains("addons"))
|
||||
self.wait.until(lambda _: self.logged_in)
|
||||
|
||||
def logout(self):
|
||||
|
@ -117,60 +113,61 @@ class Base(Page):
|
|||
|
||||
|
||||
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')
|
||||
_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)',
|
||||
".SectionLinks \
|
||||
> li:nth-child(1) > a:nth-child(1)",
|
||||
)
|
||||
_extensions_active_button_locator = (
|
||||
By.CLASS_NAME,
|
||||
'SectionLinks-link-extension.SectionLinks-link--active',
|
||||
"SectionLinks-link-extension.SectionLinks-link--active",
|
||||
)
|
||||
_login_locator = (By.CLASS_NAME, 'Header-authenticate-button')
|
||||
_login_locator = (By.CLASS_NAME, "Header-authenticate-button")
|
||||
_user_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Header-user-and-external-links .DropdownMenu-button-text',
|
||||
".Header-user-and-external-links .DropdownMenu-button-text",
|
||||
)
|
||||
_account_dropdown_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DropdownMenu.Header-authenticate-button .DropdownMenu-items',
|
||||
".DropdownMenu.Header-authenticate-button .DropdownMenu-items",
|
||||
)
|
||||
_user_menu_links_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Header-user-and-external-links .DropdownMenuItem-link a',
|
||||
".Header-user-and-external-links .DropdownMenuItem-link a",
|
||||
)
|
||||
_logout_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DropdownMenu-items .Header-logout-button button',
|
||||
".DropdownMenu-items .Header-logout-button button",
|
||||
)
|
||||
_more_menu_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Header-SectionLinks .SectionLinks-dropdown',
|
||||
".Header-SectionLinks .SectionLinks-dropdown",
|
||||
)
|
||||
_more_dropdown_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.SectionLinks-dropdown .DropdownMenu-items',
|
||||
".SectionLinks-dropdown .DropdownMenu-items",
|
||||
)
|
||||
_more_dropdown_sections_locator = (By.CSS_SELECTOR, '.DropdownMenuItem-section')
|
||||
_more_dropdown_links_locator = (By.CSS_SELECTOR, '.DropdownMenuItem a')
|
||||
_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)',
|
||||
".SectionLinks > li:nth-child(2) > \
|
||||
a:nth-child(1)",
|
||||
)
|
||||
_themes_active_button_locator = (
|
||||
By.CLASS_NAME,
|
||||
'SectionLinks-link-theme.SectionLinks-link--active',
|
||||
"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')
|
||||
_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
|
||||
|
||||
|
@ -178,18 +175,24 @@ class Header(Region):
|
|||
|
||||
@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
|
||||
|
||||
|
@ -198,6 +201,9 @@ class Header(Region):
|
|||
@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):
|
||||
|
@ -221,7 +227,7 @@ class Header(Region):
|
|||
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',
|
||||
message="The expected displayed name was not visible",
|
||||
)
|
||||
|
||||
def click_logout(self):
|
||||
|
@ -231,7 +237,7 @@ class Header(Region):
|
|||
EC.element_to_be_clickable(
|
||||
(
|
||||
By.CSS_SELECTOR,
|
||||
'.Header-authenticate-button',
|
||||
".Header-authenticate-button",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -251,10 +257,11 @@ class Header(Region):
|
|||
action.perform()
|
||||
self.wait.until(
|
||||
lambda s: self.is_element_displayed(*self._login_locator),
|
||||
message='The login button was not displayed after logout',
|
||||
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):
|
||||
|
@ -264,7 +271,7 @@ class Header(Region):
|
|||
EC.element_to_be_clickable(
|
||||
(
|
||||
By.CSS_SELECTOR,
|
||||
'.Header-authenticate-button',
|
||||
".Header-authenticate-button",
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -282,11 +289,12 @@ class Header(Region):
|
|||
# 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}',
|
||||
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
|
||||
|
@ -313,43 +321,51 @@ class Header(Region):
|
|||
|
||||
@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',
|
||||
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}',
|
||||
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',
|
||||
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}',
|
||||
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
|
||||
|
@ -357,28 +373,32 @@ class Header(Region):
|
|||
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')
|
||||
_root_locator = (By.CLASS_NAME, "AutoSearchInput")
|
||||
_query_field_locator = (By.ID, "AutoSearchInput-q")
|
||||
_search_suggestions_list_locator = (
|
||||
By.CLASS_NAME,
|
||||
'AutoSearchInput-suggestions-list',
|
||||
"AutoSearchInput-suggestions-list",
|
||||
)
|
||||
_search_suggestions_item_locator = (
|
||||
By.CLASS_NAME,
|
||||
'AutoSearchInput-suggestions-item',
|
||||
"AutoSearchInput-suggestions-item",
|
||||
)
|
||||
_search_textbox_locator = (By.CLASS_NAME, 'AutoSearchInput-query')
|
||||
_search_item_name = (By.CSS_SELECTOR, '.SearchSuggestion-name')
|
||||
_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',
|
||||
".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)
|
||||
|
@ -389,7 +409,7 @@ class Header(Region):
|
|||
|
||||
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'))
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText"))
|
||||
)
|
||||
return self.search_suggestions
|
||||
|
||||
|
@ -399,10 +419,10 @@ class Header(Region):
|
|||
lambda _: self.is_element_displayed(
|
||||
*self._search_suggestions_list_locator
|
||||
),
|
||||
message='Search suggestions list did not open',
|
||||
message="Search suggestions list did not open",
|
||||
)
|
||||
WebDriverWait(self.driver, 30).until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText'))
|
||||
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)
|
||||
|
@ -410,26 +430,35 @@ class Header(Region):
|
|||
|
||||
@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')
|
||||
_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',
|
||||
message="Promoted icon was not found for these search suggestions",
|
||||
)
|
||||
return self.find_element(*self._promoted_icon_locator).text
|
||||
|
||||
|
@ -442,55 +471,76 @@ class Header(Region):
|
|||
|
||||
|
||||
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')
|
||||
_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')
|
||||
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')
|
||||
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)
|
||||
|
|
|
@ -8,16 +8,15 @@ from pages.desktop.developers.edit_addon import EditAddon
|
|||
|
||||
|
||||
class ManageAddons(Base):
|
||||
|
||||
_my_addons_page_logo = (By.CSS_SELECTOR, '.site-titles')
|
||||
_page_title_locator = (By.CSS_SELECTOR, '.hero > h1')
|
||||
_my_addons_page_logo = (By.CSS_SELECTOR, ".site-titles")
|
||||
_page_title_locator = (By.CSS_SELECTOR, ".hero > h1")
|
||||
_my_themes_section_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.submission-type-tabs > a:nth-child(2)',
|
||||
".submission-type-tabs > a:nth-child(2)",
|
||||
)
|
||||
_sort_by_created_locator = (By.LINK_TEXT, 'Created')
|
||||
_addon_items_locator = (By.CLASS_NAME, 'item.addon')
|
||||
_submit_addon_button_locator = (By.CSS_SELECTOR, '#submit-addon > a')
|
||||
_sort_by_created_locator = (By.LINK_TEXT, "Created")
|
||||
_addon_items_locator = (By.CLASS_NAME, "item.addon")
|
||||
_submit_addon_button_locator = (By.CSS_SELECTOR, "#submit-addon > a")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(
|
||||
|
@ -27,10 +26,12 @@ class ManageAddons(Base):
|
|||
|
||||
@property
|
||||
def my_addons_page_logo(self):
|
||||
self.wait_for_element_to_be_displayed(self._my_addons_page_logo)
|
||||
return self.find_element(*self._my_addons_page_logo)
|
||||
|
||||
@property
|
||||
def my_addons_page_title(self):
|
||||
self.wait_for_element_to_be_displayed(self._page_title_locator)
|
||||
return self.find_element(*self._page_title_locator)
|
||||
|
||||
def click_on_my_themes(self):
|
||||
|
@ -38,8 +39,8 @@ class ManageAddons(Base):
|
|||
self.wait.until(
|
||||
lambda _: self.find_element(
|
||||
*self._my_themes_section_button_locator
|
||||
).get_attribute('class')
|
||||
== 'active'
|
||||
).get_attribute("class")
|
||||
== "active"
|
||||
)
|
||||
|
||||
def sort_by_created(self):
|
||||
|
@ -51,13 +52,15 @@ class ManageAddons(Base):
|
|||
return [self.AddonDetails(self, el) for el in items]
|
||||
|
||||
class AddonDetails(Region):
|
||||
_addon_name_locator = (By.CSS_SELECTOR, '.item.addon .info > h3')
|
||||
_addon_edit_link_locator = (By.CSS_SELECTOR, '.item.addon .info > h3 > a')
|
||||
_addon_name_locator = (By.CSS_SELECTOR, ".item.addon .info > h3")
|
||||
_addon_edit_link_locator = (By.CSS_SELECTOR, ".item.addon .info > h3 > a")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_name_locator))
|
||||
return self.find_element(*self._addon_name_locator).text
|
||||
|
||||
def click_addon_name(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._addon_edit_link_locator))
|
||||
self.find_element(*self._addon_edit_link_locator).click()
|
||||
return EditAddon(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
|
|
@ -30,7 +30,10 @@ class DevhubAddonValidate(Base):
|
|||
_on_your_own_text_checkbox = (By.CSS_SELECTOR, "#id_channel_1")
|
||||
_upload_details_text = (By.CSS_SELECTOR, ".upload-details")
|
||||
_upload_status_results_succes = (By.ID, "upload-status-results")
|
||||
_upload_status_results_failed = (By.CSS_SELECTOR, "div.upload-status div.status-fail strong")
|
||||
_upload_status_results_failed = (
|
||||
By.CSS_SELECTOR,
|
||||
"div.upload-status div.status-fail strong",
|
||||
)
|
||||
_upload_status_bar_results_approved = (By.CSS_SELECTOR, "div.bar-success")
|
||||
_upload_status_bar_results_failed = (By.CSS_SELECTOR, "div.bar-fail")
|
||||
_upload_status = (By.ID, "uploadstatus")
|
||||
|
@ -44,11 +47,15 @@ class DevhubAddonValidate(Base):
|
|||
|
||||
def is_validation_approved(self):
|
||||
"""Wait for addon validation to complete; if not successful, the test will fail"""
|
||||
self.wait.until(EC.visibility_of_element_located(self._upload_status_bar_results_approved))
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._upload_status_bar_results_approved)
|
||||
)
|
||||
|
||||
def is_not_validated(self):
|
||||
"""Wait for addon validation to complete; if successful, the test will fail"""
|
||||
self.wait.until(EC.visibility_of_element_located(self._upload_status_bar_results_failed))
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._upload_status_bar_results_failed)
|
||||
)
|
||||
|
||||
@property
|
||||
def addon_on_your_site(self):
|
||||
|
|
|
@ -18,90 +18,90 @@ from pages.desktop.developers.submit_addon import SubmitAddon
|
|||
class DevHubHome(Base):
|
||||
"""AMO Developer Hub homepage"""
|
||||
|
||||
URL_TEMPLATE = 'developers/'
|
||||
URL_TEMPLATE = "developers/"
|
||||
|
||||
_logo_locator = (By.CLASS_NAME, 'DevHub-Navigation-Logo')
|
||||
_ext_workshop_link_locator = (By.LINK_TEXT, 'Extension Workshop')
|
||||
_documentation_link_locator = (By.LINK_TEXT, 'Documentation')
|
||||
_support_link_locator = (By.LINK_TEXT, 'Support')
|
||||
_blog_link_locator = (By.LINK_TEXT, 'Blog')
|
||||
_fxa_login_button_locator = (By.CLASS_NAME, 'DevHub-Navigation-Register')
|
||||
_page_overview_title_locator = (By.CSS_SELECTOR, '.DevHub-Overview h1')
|
||||
_page_overview_summary_locator = (By.CSS_SELECTOR, '.DevHub-Overview p')
|
||||
_learn_how_button_locator = (By.CSS_SELECTOR, '.DevHub-Overview a')
|
||||
_logo_locator = (By.CLASS_NAME, "DevHub-Navigation-Logo")
|
||||
_ext_workshop_link_locator = (By.LINK_TEXT, "Extension Workshop")
|
||||
_documentation_link_locator = (By.LINK_TEXT, "Documentation")
|
||||
_support_link_locator = (By.LINK_TEXT, "Support")
|
||||
_blog_link_locator = (By.LINK_TEXT, "Blog")
|
||||
_fxa_login_button_locator = (By.CLASS_NAME, "DevHub-Navigation-Register")
|
||||
_page_overview_title_locator = (By.CSS_SELECTOR, ".DevHub-Overview h1")
|
||||
_page_overview_summary_locator = (By.CSS_SELECTOR, ".DevHub-Overview p")
|
||||
_learn_how_button_locator = (By.CSS_SELECTOR, ".DevHub-Overview a")
|
||||
_page_content_title_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-content-header--submit-or-manage',
|
||||
".DevHub-content-header--submit-or-manage",
|
||||
)
|
||||
_page_content_summary_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-content-copy p:nth-child(3)',
|
||||
".DevHub-content-copy p:nth-child(3)",
|
||||
)
|
||||
_page_content_login_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-content-copy:nth-child(1) > a',
|
||||
".DevHub-content-copy:nth-child(1) > a",
|
||||
)
|
||||
_page_content_featured_image_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-content-image--submit-or-manage',
|
||||
".DevHub-content-image--submit-or-manage",
|
||||
)
|
||||
_get_involved_title_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-content-container--get-involved h3',
|
||||
".DevHub-content-container--get-involved h3",
|
||||
)
|
||||
_get_involved_summary_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-content-container--get-involved p',
|
||||
".DevHub-content-container--get-involved p",
|
||||
)
|
||||
_dev_community_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-content-container--get-involved a',
|
||||
".DevHub-content-container--get-involved a",
|
||||
)
|
||||
_get_involved_image_locator = (By.CLASS_NAME, 'DevHub-content-image--get-involved')
|
||||
_footer_language_picker_locator = (By.ID, 'language')
|
||||
_footer_products_section_locator = (By.CSS_SELECTOR, '.Footer-products-links')
|
||||
_footer_links_locator = (By.CSS_SELECTOR, '.Footer-links li a')
|
||||
_get_involved_image_locator = (By.CLASS_NAME, "DevHub-content-image--get-involved")
|
||||
_footer_language_picker_locator = (By.ID, "language")
|
||||
_footer_products_section_locator = (By.CSS_SELECTOR, ".Footer-products-links")
|
||||
_footer_links_locator = (By.CSS_SELECTOR, ".Footer-links li a")
|
||||
|
||||
# elements visible only to logged in users
|
||||
_sign_out_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Navigation-SignOut a:nth-child(1)',
|
||||
".DevHub-Navigation-SignOut a:nth-child(1)",
|
||||
)
|
||||
_user_logged_in_avatar_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Navigation-SignOut a:nth-child(2)',
|
||||
".DevHub-Navigation-SignOut a:nth-child(2)",
|
||||
)
|
||||
_my_addons_header_link_locator = (By.LINK_TEXT, 'My Add-ons')
|
||||
_my_addons_header_link_locator = (By.LINK_TEXT, "My Add-ons")
|
||||
_user_profile_picture_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Navigation-SignOut a img',
|
||||
".DevHub-Navigation-SignOut a img",
|
||||
)
|
||||
_logged_in_hero_banner_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-callout-box--banner h2',
|
||||
".DevHub-callout-box--banner h2",
|
||||
)
|
||||
_logged_in_hero_banner_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-callout-box--banner p',
|
||||
".DevHub-callout-box--banner p",
|
||||
)
|
||||
_logged_in_hero_banner_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-callout-box--banner a',
|
||||
".DevHub-callout-box--banner a",
|
||||
)
|
||||
_my_addons_section_header_locator = (By.CSS_SELECTOR, '.DevHub-MyAddons h2')
|
||||
_my_addons_section_paragraph_locator = (By.CSS_SELECTOR, '.DevHub-MyAddons-copy')
|
||||
_my_addons_section_list_locator = (By.CSS_SELECTOR, '.DevHub-MyAddons-item')
|
||||
_my_addons_section_header_locator = (By.CSS_SELECTOR, ".DevHub-MyAddons h2")
|
||||
_my_addons_section_paragraph_locator = (By.CSS_SELECTOR, ".DevHub-MyAddons-copy")
|
||||
_my_addons_section_list_locator = (By.CSS_SELECTOR, ".DevHub-MyAddons-item")
|
||||
_see_all_addons_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-MyAddons-item-buttons-all',
|
||||
".DevHub-MyAddons-item-buttons-all",
|
||||
)
|
||||
_submit_addon_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-MyAddons .Button:nth-of-type(1)',
|
||||
".DevHub-MyAddons .Button:nth-of-type(1)",
|
||||
)
|
||||
_submit_theme_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-MyAddons .Button:nth-of-type(2)',
|
||||
".DevHub-MyAddons .Button:nth-of-type(2)",
|
||||
)
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
|
@ -117,7 +117,7 @@ class DevHubHome(Base):
|
|||
# There are a few Devhub links that redirect to the Extension Workshop(EW) page.
|
||||
# The following method can be used by tests which verify that EW has loaded.
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located((By.CSS_SELECTOR, '.banner.intro h1'))
|
||||
EC.visibility_of_element_located((By.CSS_SELECTOR, ".banner.intro h1"))
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -130,7 +130,7 @@ class DevHubHome(Base):
|
|||
self.find_element(*self._documentation_link_locator).click()
|
||||
self.wait.until(
|
||||
EC.text_to_be_present_in_element(
|
||||
(By.CSS_SELECTOR, '.main-content h1'), 'Browser extensions'
|
||||
(By.CSS_SELECTOR, ".main-content h1"), "Browser extensions"
|
||||
),
|
||||
message=f'Expected "Browser Extensions" in the page title but got'
|
||||
f' "{self.find_element(By.CSS_SELECTOR, ".main-content h1").text}".',
|
||||
|
@ -143,7 +143,7 @@ class DevHubHome(Base):
|
|||
def click_blog(self):
|
||||
self.wait_for_element_to_be_clickable(self._blog_link_locator)
|
||||
self.find_element(*self._blog_link_locator).click()
|
||||
self.wait.until(EC.visibility_of_element_located((By.ID, 'site-title')))
|
||||
self.wait.until(EC.visibility_of_element_located((By.ID, "site-title")))
|
||||
|
||||
@property
|
||||
def header_login_button(self):
|
||||
|
@ -174,14 +174,14 @@ class DevHubHome(Base):
|
|||
fxa = self.click_header_login_button()
|
||||
# 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}',
|
||||
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)
|
||||
# assess that the user has been logged in
|
||||
self.wait.until(
|
||||
lambda _: self.sign_out_link.is_displayed(),
|
||||
message=f'Log in flow was not successful. URL at fail time was {self.driver.current_url}',
|
||||
message=f"Log in flow was not successful. URL at fail time was {self.driver.current_url}",
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -240,8 +240,9 @@ class DevHubHome(Base):
|
|||
@property
|
||||
def user_profile_icon(self):
|
||||
# get the 'alt' attribute to determine if img is uploaded by the user and is not the default avatar
|
||||
self.wait_for_element_to_be_displayed(self._user_profile_picture_locator)
|
||||
return self.find_element(*self._user_profile_picture_locator).get_attribute(
|
||||
'alt'
|
||||
"alt"
|
||||
)
|
||||
|
||||
def click_user_profile_picture(self):
|
||||
|
@ -260,7 +261,9 @@ class DevHubHome(Base):
|
|||
|
||||
@property
|
||||
def logged_in_hero_banner_header(self):
|
||||
self.wait_for_element_to_be_displayed(self._logged_in_hero_banner_header_locator)
|
||||
self.wait_for_element_to_be_displayed(
|
||||
self._logged_in_hero_banner_header_locator
|
||||
)
|
||||
return self.find_element(*self._logged_in_hero_banner_header_locator).text
|
||||
|
||||
@property
|
||||
|
@ -279,6 +282,7 @@ class DevHubHome(Base):
|
|||
return self.find_element(*self._logged_in_hero_banner_text_locator).text
|
||||
|
||||
def click_logged_in_hero_banner_extension_workshop_link(self):
|
||||
self.wait_for_element_to_be_clickable(self._logged_in_hero_banner_link_locator)
|
||||
self.find_element(*self._logged_in_hero_banner_link_locator).click()
|
||||
|
||||
def click_see_all_addons_link(self):
|
||||
|
@ -320,30 +324,36 @@ class DevHubHome(Base):
|
|||
return element.find_elements(*self._footer_links_locator)
|
||||
|
||||
class MyAddonsList(Region):
|
||||
_my_addon_icon_locator = (By.CSS_SELECTOR, '.DevHub-MyAddons-item-icon')
|
||||
_my_addon_name_locator = (By.CSS_SELECTOR, '.DevHub-MyAddons-item-name')
|
||||
_my_addon_edit_link_locator = (By.CSS_SELECTOR, '.DevHub-MyAddons-item-edit')
|
||||
_my_addon_icon_locator = (By.CSS_SELECTOR, ".DevHub-MyAddons-item-icon")
|
||||
_my_addon_name_locator = (By.CSS_SELECTOR, ".DevHub-MyAddons-item-name")
|
||||
_my_addon_edit_link_locator = (By.CSS_SELECTOR, ".DevHub-MyAddons-item-edit")
|
||||
_my_addon_version_number_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-MyAddons-item-versions',
|
||||
".DevHub-MyAddons-item-versions",
|
||||
)
|
||||
_my_addon_version_status_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-MyAddons-VersionStatus',
|
||||
".DevHub-MyAddons-VersionStatus",
|
||||
)
|
||||
_my_addon_rating_text_locator = (By.CSS_SELECTOR, '.addon-rating strong')
|
||||
_my_addon_rating_stars_locator = (By.CSS_SELECTOR, '.stars')
|
||||
_my_addon_rating_text_locator = (By.CSS_SELECTOR, ".addon-rating strong")
|
||||
_my_addon_rating_stars_locator = (By.CSS_SELECTOR, ".stars")
|
||||
_my_addon_last_modified_date_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-MyAddons-item-modified span:nth-of-type(2)',
|
||||
".DevHub-MyAddons-item-modified span:nth-of-type(2)",
|
||||
)
|
||||
|
||||
@property
|
||||
def my_addon_icon(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._my_addon_icon_locator)
|
||||
)
|
||||
return self.find_element(*self._my_addon_icon_locator)
|
||||
|
||||
@property
|
||||
def my_addon_name(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._my_addon_name_locator)
|
||||
)
|
||||
return self.find_element(*self._my_addon_name_locator)
|
||||
|
||||
def click_my_addon_edit_link(self):
|
||||
|
@ -352,22 +362,39 @@ class DevHubHome(Base):
|
|||
|
||||
@property
|
||||
def my_addon_version_number(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._my_addon_version_number_locator)
|
||||
)
|
||||
return self.find_element(*self._my_addon_version_number_locator)
|
||||
|
||||
@property
|
||||
def my_addon_version_status(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._my_addon_version_status_locator)
|
||||
)
|
||||
return self.find_element(*self._my_addon_version_status_locator)
|
||||
|
||||
@property
|
||||
def my_addon_rating_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._my_addon_rating_text_locator)
|
||||
)
|
||||
return self.find_element(*self._my_addon_rating_text_locator)
|
||||
|
||||
@property
|
||||
def my_addon_rating_stars(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._my_addon_rating_stars_locator)
|
||||
)
|
||||
return self.find_element(*self._my_addon_rating_stars_locator)
|
||||
|
||||
@property
|
||||
def my_addon_last_modified_date(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._my_addon_last_modified_date_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._my_addon_last_modified_date_locator)
|
||||
|
||||
@property
|
||||
|
@ -386,8 +413,8 @@ class DevHubHome(Base):
|
|||
try:
|
||||
# DevHub homepage will display only the Approved or Awaiting Review statuses, so we check these first
|
||||
if (
|
||||
'Approved' in status.listed_addon_status
|
||||
or 'Awaiting Review' in status.listed_addon_status
|
||||
"Approved" in status.listed_addon_status
|
||||
or "Awaiting Review" in status.listed_addon_status
|
||||
):
|
||||
return True
|
||||
else:
|
||||
|
@ -407,100 +434,138 @@ class DevHubHome(Base):
|
|||
class ConnectFooter(Region):
|
||||
_connect_footer_title_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-content-header--Connect h2',
|
||||
".DevHub-content-header--Connect h2",
|
||||
)
|
||||
_twitter_column_title_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Devhub-content-copy--Connect div:nth-child(1) h4:nth-child(1)',
|
||||
".Devhub-content-copy--Connect div:nth-child(1) h4:nth-child(1)",
|
||||
)
|
||||
_twitter_links_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-content-copy--Connect-twitter-list a',
|
||||
".DevHub-content-copy--Connect-twitter-list a",
|
||||
)
|
||||
_more_column_title_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Devhub-content-copy--Connect div:nth-child(2) h4:nth-child(1)',
|
||||
".Devhub-content-copy--Connect div:nth-child(2) h4:nth-child(1)",
|
||||
)
|
||||
_more_contact_links_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Connect-section:nth-child(2) > ul a',
|
||||
".DevHub-Connect-section:nth-child(2) > ul a",
|
||||
)
|
||||
_newsletter_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Devhub-content-copy--Connect div:nth-child(3) h4',
|
||||
".Devhub-content-copy--Connect div:nth-child(3) h4",
|
||||
)
|
||||
_newsletter_info_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Devhub-content-copy--Connect div:nth-child(3) p',
|
||||
".Devhub-content-copy--Connect div:nth-child(3) p",
|
||||
)
|
||||
_newsletter_email_input_field_locator = (By.ID, 'email')
|
||||
_newsletter_sign_up_button_locator = (By.CSS_SELECTOR, '.btn-success')
|
||||
_newsletter_privacy_checkbox_locator = (By.ID, 'privacy')
|
||||
_newsletter_privacy_notice_link_locator = (By.CSS_SELECTOR, '.form_group-agree a')
|
||||
_newsletter_email_input_field_locator = (By.ID, "email")
|
||||
_newsletter_sign_up_button_locator = (By.CSS_SELECTOR, ".btn-success")
|
||||
_newsletter_privacy_checkbox_locator = (By.ID, "privacy")
|
||||
_newsletter_privacy_notice_link_locator = (By.CSS_SELECTOR, ".form_group-agree a")
|
||||
_newsletter_sign_up_confirmation_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.newsletter_thanks h2',
|
||||
".newsletter_thanks h2",
|
||||
)
|
||||
_newsletter_sign_up_confirmation_message_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.newsletter_thanks p',
|
||||
".newsletter_thanks p",
|
||||
)
|
||||
|
||||
@property
|
||||
def connect_footer_title(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._connect_footer_title_locator)
|
||||
)
|
||||
return self.find_element(*self._connect_footer_title_locator).text
|
||||
|
||||
@property
|
||||
def connect_twitter_title(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._twitter_column_title_locator)
|
||||
)
|
||||
return self.find_element(*self._twitter_column_title_locator).text
|
||||
|
||||
@property
|
||||
def twitter_links(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._twitter_links_locator))
|
||||
return self.find_elements(*self._twitter_links_locator)
|
||||
|
||||
@property
|
||||
def connect_more_title(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._more_column_title_locator)
|
||||
)
|
||||
return self.find_element(*self._more_column_title_locator).text
|
||||
|
||||
@property
|
||||
def more_connect_links(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._more_contact_links_locator)
|
||||
)
|
||||
return self.find_elements(*self._more_contact_links_locator)
|
||||
|
||||
@property
|
||||
def newsletter_section_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._newsletter_header_locator)
|
||||
)
|
||||
return self.find_element(*self._newsletter_header_locator).text
|
||||
|
||||
@property
|
||||
def newsletter_info_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._newsletter_info_text_locator)
|
||||
)
|
||||
return self.find_element(*self._newsletter_info_text_locator).text
|
||||
|
||||
def newsletter_email_input_field(self, email):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._newsletter_email_input_field_locator)
|
||||
)
|
||||
self.find_element(*self._newsletter_email_input_field_locator).send_keys(email)
|
||||
|
||||
@property
|
||||
def newsletter_sign_up(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._newsletter_sign_up_button_locator)
|
||||
)
|
||||
return self.find_element(*self._newsletter_sign_up_button_locator)
|
||||
|
||||
def click_privacy_checkbox(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._newsletter_privacy_checkbox_locator)
|
||||
)
|
||||
self.find_element(*self._newsletter_privacy_checkbox_locator).click()
|
||||
|
||||
def click_newsletter_privacy_notice_link(self):
|
||||
self.find_element(*self._newsletter_privacy_notice_link_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',
|
||||
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)
|
||||
|
||||
@property
|
||||
def newsletter_signup_confirmation_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._newsletter_sign_up_confirmation_header_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(
|
||||
*self._newsletter_sign_up_confirmation_header_locator
|
||||
).text
|
||||
|
||||
@property
|
||||
def newsletter_signup_confirmation_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._newsletter_sign_up_confirmation_message_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(
|
||||
*self._newsletter_sign_up_confirmation_message_locator
|
||||
).text
|
||||
|
@ -509,16 +574,16 @@ class ConnectFooter(Region):
|
|||
retry = 0
|
||||
while retry < 10:
|
||||
# verify that a response is available and get the email subject
|
||||
request = requests.get(f'https://restmail.net/mail/{email}', timeout=10)
|
||||
request = requests.get(f"https://restmail.net/mail/{email}", timeout=10)
|
||||
response = request.json()
|
||||
if response:
|
||||
confirmation = [key['subject'] for key in response]
|
||||
confirmation = [key["subject"] for key in response]
|
||||
return confirmation
|
||||
elif not response:
|
||||
print('Confirmation email not received yet')
|
||||
print("Confirmation email not received yet")
|
||||
# fail if we retired 10 times and there was no email received
|
||||
if retry == 9:
|
||||
pytest.fail('Newsletter confirmation email was not sent')
|
||||
pytest.fail("Newsletter confirmation email was not sent")
|
||||
# pause between subsequent requests to give more time to the email to be sent
|
||||
time.sleep(2)
|
||||
retry += 1
|
||||
|
@ -528,95 +593,112 @@ class ConnectFooter(Region):
|
|||
class ResourcesFooter(Region):
|
||||
_documentation_section_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(1) h4',
|
||||
".DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(1) h4",
|
||||
)
|
||||
_documentation_section_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(1)',
|
||||
".DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(1)",
|
||||
)
|
||||
_tools_section_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(2) h4',
|
||||
".DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(2) h4",
|
||||
)
|
||||
_tools_section_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(2)',
|
||||
".DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(2)",
|
||||
)
|
||||
_promote_section_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(3) h4',
|
||||
".DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(3) h4",
|
||||
)
|
||||
_promote_section_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(3)',
|
||||
".DevHub-Footer-sections:nth-of-type(1) .DevHub-Footer-section:nth-of-type(3)",
|
||||
)
|
||||
_resources_footer_section_links = (By.CSS_SELECTOR, '.DevHub-Footer-sections li a')
|
||||
_resources_footer_section_links = (By.CSS_SELECTOR, ".DevHub-Footer-sections li a")
|
||||
_addon_review_section_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(1) h4',
|
||||
".DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(1) h4",
|
||||
)
|
||||
_addon_review_info_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(1) p',
|
||||
".DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(1) p",
|
||||
)
|
||||
_addon_review_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(1) a',
|
||||
".DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(1) a",
|
||||
)
|
||||
_write_code_section_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(2) h4',
|
||||
".DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(2) h4",
|
||||
)
|
||||
_write_code_info_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(2) p',
|
||||
".DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(2) p",
|
||||
)
|
||||
_write_code_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(2) a',
|
||||
".DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(2) a",
|
||||
)
|
||||
_participate_section_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(3) h4',
|
||||
".DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(3) h4",
|
||||
)
|
||||
_participate_info_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(3) p',
|
||||
".DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(3) p",
|
||||
)
|
||||
_participate_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(3) a',
|
||||
".DevHub-Footer-sections:nth-of-type(2) .DevHub-Footer-section:nth-of-type(3) a",
|
||||
)
|
||||
|
||||
@property
|
||||
def documentation_section_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._documentation_section_header_locator)
|
||||
)
|
||||
return self.find_element(*self._documentation_section_header_locator).text
|
||||
|
||||
@property
|
||||
def documentation_section_links(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._documentation_section_locator)
|
||||
)
|
||||
el = self.find_element(*self._documentation_section_locator)
|
||||
return el.find_elements(*self._resources_footer_section_links)
|
||||
|
||||
@property
|
||||
def tools_section_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._tools_section_header_locator)
|
||||
)
|
||||
return self.find_element(*self._tools_section_header_locator).text
|
||||
|
||||
@property
|
||||
def tools_section_links(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._tools_section_locator))
|
||||
el = self.find_element(*self._tools_section_locator)
|
||||
return el.find_elements(*self._resources_footer_section_links)
|
||||
|
||||
@property
|
||||
def promote_section_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._promote_section_header_locator)
|
||||
)
|
||||
return self.find_element(*self._promote_section_header_locator).text
|
||||
|
||||
@property
|
||||
def promote_section_links(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._promote_section_locator))
|
||||
el = self.find_element(*self._promote_section_locator)
|
||||
return el.find_elements(*self._resources_footer_section_links)
|
||||
|
||||
@property
|
||||
def review_addons_section_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._addon_review_section_header_locator)
|
||||
)
|
||||
return self.find_element(*self._addon_review_section_header_locator).text
|
||||
|
||||
@property
|
||||
|
@ -661,4 +743,5 @@ class ResourcesFooter(Region):
|
|||
return self.find_element(*self._participate_info_text_locator).text
|
||||
|
||||
def click_participate_section_link(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._participate_link_locator))
|
||||
self.find_element(*self._participate_link_locator).click()
|
||||
|
|
|
@ -8,19 +8,19 @@ from pages.desktop.developers.submit_addon import SubmitAddon
|
|||
class EditAddon(Base):
|
||||
"""Edit page for a specific addon."""
|
||||
|
||||
_root_locator = (By.CLASS_NAME, 'section')
|
||||
_edit_addon_navbar_locator = (By.CLASS_NAME, 'edit-addon-nav')
|
||||
_addon_name_locator = (By.CSS_SELECTOR, '.section header h2')
|
||||
_listed_addon_status_locator = (By.CSS_SELECTOR, '.addon-listed-status a')
|
||||
_last_modified_date_locator = (By.CLASS_NAME, 'date-updated')
|
||||
_root_locator = (By.CLASS_NAME, "section")
|
||||
_edit_addon_navbar_locator = (By.CLASS_NAME, "edit-addon-nav")
|
||||
_addon_name_locator = (By.CSS_SELECTOR, ".section header h2")
|
||||
_listed_addon_status_locator = (By.CSS_SELECTOR, ".addon-listed-status a")
|
||||
_last_modified_date_locator = (By.CLASS_NAME, "date-updated")
|
||||
_unlisted_version_tooltip_locator = (
|
||||
By.CLASS_NAME,
|
||||
'distribution-tag-unlisted.tooltip',
|
||||
"distribution-tag-unlisted.tooltip",
|
||||
)
|
||||
_submit_new_version_link_locator = (By.CLASS_NAME, 'version-upload')
|
||||
_submit_new_version_link_locator = (By.CLASS_NAME, "version-upload")
|
||||
_manage_versions_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#edit-addon-nav ul:nth-child(1) li:nth-child(3)',
|
||||
"#edit-addon-nav ul:nth-child(1) li:nth-child(3)",
|
||||
)
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
|
@ -29,6 +29,7 @@ class EditAddon(Base):
|
|||
|
||||
@property
|
||||
def name(self):
|
||||
self.wait_for_element_to_be_displayed(self._addon_name_locator)
|
||||
return self.find_element(*self._addon_name_locator).text
|
||||
|
||||
@property
|
||||
|
@ -43,6 +44,7 @@ class EditAddon(Base):
|
|||
|
||||
@property
|
||||
def unlisted_version_tooltip(self):
|
||||
self.wait_for_element_to_be_displayed(self._unlisted_version_tooltip_locator)
|
||||
return self.find_element(*self._unlisted_version_tooltip_locator)
|
||||
|
||||
@property
|
||||
|
@ -50,8 +52,8 @@ class EditAddon(Base):
|
|||
"""Get the date string from the Last Update date section and format it"""
|
||||
site_date = (
|
||||
self.find_element(*self._last_modified_date_locator)
|
||||
.text.split('Last Updated: ')[1]
|
||||
.replace('.', '')
|
||||
.text.split("Last Updated: ")[1]
|
||||
.replace(".", "")
|
||||
)
|
||||
month = site_date.split()[0]
|
||||
# get only the first three letters in the month to have a uniform date structure
|
||||
|
@ -59,6 +61,7 @@ class EditAddon(Base):
|
|||
return final_date
|
||||
|
||||
def click_manage_versions_link(self):
|
||||
self.wait_for_element_to_be_clickable(self._manage_versions_link_locator)
|
||||
self.find_element(*self._manage_versions_link_locator).click()
|
||||
from pages.desktop.developers.manage_versions import ManageVersions
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ class EditTheme(Base):
|
|||
been approved.
|
||||
"""
|
||||
|
||||
_root_locator = (By.CLASS_NAME, 'section')
|
||||
_edit_addon_navbar_locator = (By.CLASS_NAME, 'edit-addon-nav')
|
||||
_root_locator = (By.CLASS_NAME, "section")
|
||||
_edit_addon_navbar_locator = (By.CLASS_NAME, "edit-addon-nav")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(
|
||||
|
|
|
@ -11,49 +11,49 @@ class ManageVersions(Page):
|
|||
_addon_name_title_locator = (By.CSS_SELECTOR, 'div[class="section"] header h2')
|
||||
_listing_visibility_section_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#edit-addon h3:nth-child(1)',
|
||||
"#edit-addon h3:nth-child(1)",
|
||||
)
|
||||
_visible_listing_radio_locator = (By.CSS_SELECTOR, 'input[value="listed"]')
|
||||
_visible_explainer_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#addon-current-state label:nth-child(1)',
|
||||
"#addon-current-state label:nth-child(1)",
|
||||
)
|
||||
_invisible_listing_radio_locator = (By.CSS_SELECTOR, 'input[value="hidden"]')
|
||||
_invisible_explainer_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#addon-current-state label:nth-of-type(2)',
|
||||
"#addon-current-state label:nth-of-type(2)",
|
||||
)
|
||||
_hide_addon_confirmation_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#modal-disable p:nth-child(1)',
|
||||
"#modal-disable p:nth-child(1)",
|
||||
)
|
||||
_hide_addon_button_locator = (By.CSS_SELECTOR, '#modal-disable p button')
|
||||
_hide_addon_cancel_link_locator = (By.CSS_SELECTOR, '#modal-disable p a')
|
||||
_hide_addon_button_locator = (By.CSS_SELECTOR, "#modal-disable p button")
|
||||
_hide_addon_cancel_link_locator = (By.CSS_SELECTOR, "#modal-disable p a")
|
||||
|
||||
_incomplete_status_locator = (By.CSS_SELECTOR, '.status-incomplete b')
|
||||
_addon_listed_status_locator = (By.CSS_SELECTOR, '.addon-listed-status b')
|
||||
_delete_addon_button_locator = (By.CLASS_NAME, 'delete-button.delete-addon')
|
||||
_incomplete_status_locator = (By.CSS_SELECTOR, ".status-incomplete b")
|
||||
_addon_listed_status_locator = (By.CSS_SELECTOR, ".addon-listed-status b")
|
||||
_delete_addon_button_locator = (By.CLASS_NAME, "delete-button.delete-addon")
|
||||
|
||||
# Version List Section
|
||||
_version_list_locator = (By.ID, 'version-list')
|
||||
_version_list_locator = (By.ID, "version-list")
|
||||
_current_version_status_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#current-version-status .file-status > div:nth-child(1)',
|
||||
"#current-version-status .file-status > div:nth-child(1)",
|
||||
)
|
||||
_version_approval_status_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#version-list .file-status div:nth-child(1)',
|
||||
"#version-list .file-status div:nth-child(1)",
|
||||
)
|
||||
_disable_delete_version_button_locator = (By.CSS_SELECTOR, '.version-delete a')
|
||||
_delete_version_help_text_locator = (By.CSS_SELECTOR, '.current-version-warning')
|
||||
_delete_version_warning_locator = (By.CSS_SELECTOR, '.highlight.warning')
|
||||
_delete_version_button_locator = (By.CSS_SELECTOR, '.modal-actions .delete-button')
|
||||
_disable_delete_version_button_locator = (By.CSS_SELECTOR, ".version-delete a")
|
||||
_delete_version_help_text_locator = (By.CSS_SELECTOR, ".current-version-warning")
|
||||
_delete_version_warning_locator = (By.CSS_SELECTOR, ".highlight.warning")
|
||||
_delete_version_button_locator = (By.CSS_SELECTOR, ".modal-actions .delete-button")
|
||||
_disable_version_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.modal-actions .disable-button',
|
||||
".modal-actions .disable-button",
|
||||
)
|
||||
_cancel_disable_version_link_locator = (By.CSS_SELECTOR, '.modal-actions .close')
|
||||
_enable_version_button_locator = (By.CSS_SELECTOR, '.file-status button')
|
||||
_cancel_disable_version_link_locator = (By.CSS_SELECTOR, ".modal-actions .close")
|
||||
_enable_version_button_locator = (By.CSS_SELECTOR, ".file-status button")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._version_list_locator))
|
||||
|
@ -61,10 +61,16 @@ class ManageVersions(Page):
|
|||
|
||||
@property
|
||||
def version_page_title(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._addon_name_title_locator)
|
||||
)
|
||||
return self.find_element(*self._addon_name_title_locator).text
|
||||
|
||||
@property
|
||||
def listing_visibility_section(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._listing_visibility_section_locator)
|
||||
)
|
||||
return self.find_element(*self._listing_visibility_section_locator).text
|
||||
|
||||
def set_addon_visible(self):
|
||||
|
@ -75,10 +81,16 @@ class ManageVersions(Page):
|
|||
|
||||
@property
|
||||
def visible_status_explainer(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._visible_explainer_text_locator)
|
||||
)
|
||||
return self.find_element(*self._visible_explainer_text_locator).text
|
||||
|
||||
@property
|
||||
def invisible_status_explainer(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._invisible_explainer_text_locator)
|
||||
)
|
||||
return self.find_element(*self._invisible_explainer_text_locator).text
|
||||
|
||||
def set_addon_invisible(self):
|
||||
|
@ -87,7 +99,7 @@ class ManageVersions(Page):
|
|||
el.click()
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, '#modal-disable p:nth-child(1)')
|
||||
(By.CSS_SELECTOR, "#modal-disable p:nth-child(1)")
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -96,6 +108,9 @@ class ManageVersions(Page):
|
|||
|
||||
@property
|
||||
def hide_addon_confirmation_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._hide_addon_confirmation_text_locator)
|
||||
)
|
||||
return self.find_element(*self._hide_addon_confirmation_text_locator).text
|
||||
|
||||
def cancel_hide_addon_process(self):
|
||||
|
@ -105,48 +120,74 @@ class ManageVersions(Page):
|
|||
|
||||
@property
|
||||
def addon_listed_status(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._addon_listed_status_locator)
|
||||
)
|
||||
return self.find_element(*self._addon_listed_status_locator).text
|
||||
|
||||
@property
|
||||
def incomplete_status(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._incomplete_status_locator)
|
||||
)
|
||||
return self.find_element(*self._incomplete_status_locator)
|
||||
|
||||
@property
|
||||
def current_version_status(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._current_version_status_locator)
|
||||
)
|
||||
return self.find_element(*self._current_version_status_locator).text
|
||||
|
||||
@property
|
||||
def version_approval_status(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._version_approval_status_locator)
|
||||
)
|
||||
return self.find_elements(*self._version_approval_status_locator)
|
||||
|
||||
def click_delete_disable_version(self):
|
||||
self.find_element(*self._disable_delete_version_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, '.modal-actions .delete-button')
|
||||
(By.CSS_SELECTOR, ".modal-actions .delete-button")
|
||||
),
|
||||
message='The Delete/Disable modal was not opened',
|
||||
message="The Delete/Disable modal was not opened",
|
||||
)
|
||||
|
||||
@property
|
||||
def delete_disable_version_helptext(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._delete_version_help_text_locator)
|
||||
)
|
||||
return self.find_element(*self._delete_version_help_text_locator).text
|
||||
|
||||
@property
|
||||
def delete_disable_version_warning(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._delete_version_warning_locator)
|
||||
)
|
||||
return self.find_element(*self._delete_version_warning_locator).text
|
||||
|
||||
def click_delete_version_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._delete_version_button_locator))
|
||||
self.find_element(*self._delete_version_button_locator).click()
|
||||
|
||||
def click_disable_version_button(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._disable_version_button_locator)
|
||||
)
|
||||
self.find_element(*self._disable_version_button_locator).click()
|
||||
|
||||
def click_cancel_version_delete_link(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._cancel_disable_version_link_locator)
|
||||
)
|
||||
return self.find_element(*self._cancel_disable_version_link_locator).click()
|
||||
|
||||
def click_enable_version(self):
|
||||
"""Allows developers to re-enable a version that was previously disabled"""
|
||||
self.wait.until(EC.element_to_be_clickable(self._enable_version_button_locator))
|
||||
self.find_element(*self._enable_version_button_locator).click()
|
||||
|
||||
def wait_for_version_autoapproval(self, value):
|
||||
|
@ -166,7 +207,7 @@ class ManageVersions(Page):
|
|||
# if auto-approval took longer than normal, we want to fail the test and capture the final status
|
||||
else:
|
||||
pytest.fail(
|
||||
f'Autoapproval took longer than normal; '
|
||||
f"Autoapproval took longer than normal; "
|
||||
f'Addon final status was "{self.version_approval_status[0].text}" instead of "{value}"'
|
||||
)
|
||||
|
||||
|
@ -175,16 +216,16 @@ class ManageVersions(Page):
|
|||
return self.DeleteAddonModal(self).wait_for_region_to_load()
|
||||
|
||||
class DeleteAddonModal(Region):
|
||||
_root_locator = (By.ID, 'modal-delete')
|
||||
_root_locator = (By.ID, "modal-delete")
|
||||
_delete_confirmation_string_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'p:nth-of-type(2) > label',
|
||||
"p:nth-of-type(2) > label",
|
||||
)
|
||||
_delete_confirmation_text_input_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'input[name="slug"]',
|
||||
)
|
||||
_delete_button_locator = (By.CSS_SELECTOR, '.delete-button')
|
||||
_delete_button_locator = (By.CSS_SELECTOR, ".delete-button")
|
||||
|
||||
def wait_for_region_to_load(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._root_locator))
|
||||
|
|
|
@ -12,40 +12,40 @@ from pages.desktop.developers.manage_versions import ManageVersions
|
|||
class SubmitAddon(Page):
|
||||
"""A class holding all the components used for addon submissions in DevHub"""
|
||||
|
||||
_my_addons_page_logo_locator = (By.CSS_SELECTOR, '.site-titles')
|
||||
_submission_form_header_locator = (By.CSS_SELECTOR, '.is_addon')
|
||||
_my_addons_page_logo_locator = (By.CSS_SELECTOR, ".site-titles")
|
||||
_submission_form_header_locator = (By.CSS_SELECTOR, ".is_addon")
|
||||
_addon_distribution_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submission-process h3',
|
||||
".addon-submission-process h3",
|
||||
)
|
||||
_developer_notification_box_locator = (By.CSS_SELECTOR, '.notification-box')
|
||||
_developer_notification_box_locator = (By.CSS_SELECTOR, ".notification-box")
|
||||
_distribution_page_explainer_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submission-process p:nth-of-type(1)',
|
||||
".addon-submission-process p:nth-of-type(1)",
|
||||
)
|
||||
_distribution_agreement_checkbox_locator = (By.ID, 'id_distribution_agreement')
|
||||
_distribution_agreement_checkbox_locator = (By.ID, "id_distribution_agreement")
|
||||
_distribution_agreement_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submission-process li:nth-of-type(1) a',
|
||||
".addon-submission-process li:nth-of-type(1) a",
|
||||
)
|
||||
_review_policies_checkbox_locator = (By.ID, 'id_review_policy')
|
||||
_review_policies_checkbox_locator = (By.ID, "id_review_policy")
|
||||
_review_policies_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submission-process li:nth-of-type(2) a',
|
||||
".addon-submission-process li:nth-of-type(2) a",
|
||||
)
|
||||
_user_consent_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submission-process p:nth-of-type(2)',
|
||||
".addon-submission-process p:nth-of-type(2)",
|
||||
)
|
||||
_recaptcha_locator = (By.ID, 'id_recaptcha')
|
||||
_recaptcha_checkbox_locator = (By.ID, 'recaptcha-anchor')
|
||||
_recaptcha_locator = (By.ID, "id_recaptcha")
|
||||
_recaptcha_checkbox_locator = (By.ID, "recaptcha-anchor")
|
||||
_recaptcha_checkbox_is_selected_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'span[aria-checked="true"]',
|
||||
)
|
||||
_accept_agreement_button = (By.ID, 'accept-agreement')
|
||||
_cancel_agreement_button = (By.CSS_SELECTOR, '.submit-buttons a')
|
||||
_dev_accounts_info_link_locator = (By.CSS_SELECTOR, '.addon-submission-process p a')
|
||||
_accept_agreement_button = (By.ID, "accept-agreement")
|
||||
_cancel_agreement_button = (By.CSS_SELECTOR, ".submit-buttons a")
|
||||
_dev_accounts_info_link_locator = (By.CSS_SELECTOR, ".addon-submission-process p a")
|
||||
|
||||
_listed_option_locator = (By.CSS_SELECTOR, 'input[value="listed"]')
|
||||
_listed_option_helptext_locator = (
|
||||
|
@ -59,77 +59,103 @@ class SubmitAddon(Page):
|
|||
)
|
||||
_distribution_and_signing_helptext_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submit-distribute p:nth-of-type(3)',
|
||||
".addon-submit-distribute p:nth-of-type(3)",
|
||||
)
|
||||
_addon_policies_helptext_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submit-distribute p:nth-of-type(4)',
|
||||
".addon-submit-distribute p:nth-of-type(4)",
|
||||
)
|
||||
_change_distribution_link_locator = (By.CSS_SELECTOR, '.addon-submit-distribute a')
|
||||
_continue_button_locator = (By.CSS_SELECTOR, '.addon-submission-field button')
|
||||
_file_upload_process_helptext_locator = (By.CSS_SELECTOR, '.new-addon-file p')
|
||||
_upload_file_button_locator = (By.CSS_SELECTOR, '.invisible-upload input')
|
||||
_accepted_file_types_locator = (By.CLASS_NAME, 'upload-details')
|
||||
_compatibility_helptext_locator = (By.CSS_SELECTOR, '.compatible-apps label')
|
||||
_compatibility_error_message_locator = (By.CSS_SELECTOR, '.errorlist li')
|
||||
_firefox_compat_checkbox_locator = (By.CSS_SELECTOR, '.app.firefox input')
|
||||
_android_compat_checkbox_locator = (By.CSS_SELECTOR, '.app.android input')
|
||||
_change_distribution_link_locator = (By.CSS_SELECTOR, ".addon-submit-distribute a")
|
||||
_continue_button_locator = (By.CSS_SELECTOR, ".addon-submission-field button")
|
||||
_file_upload_process_helptext_locator = (By.CSS_SELECTOR, ".new-addon-file p")
|
||||
_upload_file_button_locator = (By.CSS_SELECTOR, ".invisible-upload input")
|
||||
_accepted_file_types_locator = (By.CLASS_NAME, "upload-details")
|
||||
_compatibility_helptext_locator = (By.CSS_SELECTOR, ".compatible-apps label")
|
||||
_compatibility_error_message_locator = (By.CSS_SELECTOR, ".errorlist li")
|
||||
_firefox_compat_checkbox_locator = (By.CSS_SELECTOR, ".app.firefox input")
|
||||
_android_compat_checkbox_locator = (By.CSS_SELECTOR, ".app.android input")
|
||||
_create_theme_subheader_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-create-theme-section h3',
|
||||
".addon-create-theme-section h3",
|
||||
)
|
||||
_create_theme_button_locator = (By.ID, 'wizardlink')
|
||||
_submit_file_button_locator = (By.ID, 'submit-upload-file-finish')
|
||||
_addon_validation_success_locator = (By.CLASS_NAME, 'bar-success')
|
||||
_validation_fail_bar_locator = (By.CLASS_NAME, 'bar-fail')
|
||||
_validation_support_link_locator = (By.CSS_SELECTOR, '#upload-status-results a')
|
||||
_create_theme_button_locator = (By.ID, "wizardlink")
|
||||
_submit_file_button_locator = (By.ID, "submit-upload-file-finish")
|
||||
_addon_validation_success_locator = (By.CLASS_NAME, "bar-success")
|
||||
_validation_fail_bar_locator = (By.CLASS_NAME, "bar-fail")
|
||||
_validation_support_link_locator = (By.CSS_SELECTOR, "#upload-status-results a")
|
||||
_validation_failed_message_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#upload-status-results strong',
|
||||
"#upload-status-results strong",
|
||||
)
|
||||
_validation_warning_message_locator = (By.CSS_SELECTOR, '.submission-warning p')
|
||||
_validation_summary_link_locator = (By.CSS_SELECTOR, '.submission-warning a')
|
||||
_validation_fail_reason_locator = (By.CSS_SELECTOR, '#upload_errors li')
|
||||
_validation_status_text_locator = (By.ID, 'upload-status-text')
|
||||
_validation_success_message_locator = (By.ID, 'upload-status-results')
|
||||
_validation_warning_message_locator = (By.CSS_SELECTOR, ".submission-warning p")
|
||||
_validation_summary_link_locator = (By.CSS_SELECTOR, ".submission-warning a")
|
||||
_validation_fail_reason_locator = (By.CSS_SELECTOR, "#upload_errors li")
|
||||
_validation_status_text_locator = (By.ID, "upload-status-text")
|
||||
_validation_success_message_locator = (By.ID, "upload-status-results")
|
||||
|
||||
@property
|
||||
def my_addons_page_logo(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._my_addons_page_logo_locator)
|
||||
)
|
||||
return self.find_element(*self._my_addons_page_logo_locator)
|
||||
|
||||
@property
|
||||
def submission_form_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._submission_form_header_locator)
|
||||
)
|
||||
return self.find_element(*self._submission_form_header_locator)
|
||||
|
||||
@property
|
||||
def submission_form_subheader(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._addon_distribution_header_locator)
|
||||
)
|
||||
return self.find_element(*self._addon_distribution_header_locator)
|
||||
|
||||
@property
|
||||
def developer_notification_box(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._developer_notification_box_locator)
|
||||
)
|
||||
return self.find_element(*self._developer_notification_box_locator)
|
||||
|
||||
@property
|
||||
def distribution_page_explainer(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._distribution_page_explainer_locator)
|
||||
)
|
||||
return self.find_element(*self._distribution_page_explainer_locator).text
|
||||
|
||||
@property
|
||||
def distribution_agreement_checkbox(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._distribution_agreement_checkbox_locator)
|
||||
EC.visibility_of_element_located(
|
||||
self._distribution_agreement_checkbox_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._distribution_agreement_checkbox_locator)
|
||||
|
||||
@property
|
||||
def distribution_agreement_article_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._distribution_agreement_link_locator)
|
||||
)
|
||||
return self.find_element(*self._distribution_agreement_link_locator)
|
||||
|
||||
@property
|
||||
def review_policies_checkbox(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._review_policies_checkbox_locator)
|
||||
)
|
||||
return self.find_element(*self._review_policies_checkbox_locator)
|
||||
|
||||
@property
|
||||
def review_policies_article_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._review_policies_link_locator)
|
||||
)
|
||||
return self.find_element(*self._review_policies_link_locator)
|
||||
|
||||
def click_extension_workshop_article_link(self, link, text):
|
||||
|
@ -138,12 +164,12 @@ class SubmitAddon(Page):
|
|||
link.click()
|
||||
self.wait.until(
|
||||
EC.number_of_windows_to_be(2),
|
||||
message=f'Number of windows was {len(self.driver.window_handles)}, expected 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.text_to_be_present_in_element((By.CSS_SELECTOR, '.page-hero h1'), text)
|
||||
EC.text_to_be_present_in_element((By.CSS_SELECTOR, ".page-hero h1"), text)
|
||||
)
|
||||
self.driver.close()
|
||||
# return to the main tab
|
||||
|
@ -151,10 +177,14 @@ class SubmitAddon(Page):
|
|||
|
||||
@property
|
||||
def user_consent_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_consent_text_locator)
|
||||
)
|
||||
return self.find_element(*self._user_consent_text_locator).text
|
||||
|
||||
@property
|
||||
def recaptcha(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._recaptcha_locator))
|
||||
return self.find_element(*self._recaptcha_locator)
|
||||
|
||||
def click_recaptcha_checkbox(self):
|
||||
|
@ -165,10 +195,12 @@ class SubmitAddon(Page):
|
|||
|
||||
@property
|
||||
def accept_agreement(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._accept_agreement_button))
|
||||
return self.find_element(*self._accept_agreement_button)
|
||||
|
||||
@property
|
||||
def cancel_agreement(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._cancel_agreement_button))
|
||||
return self.find_element(*self._cancel_agreement_button)
|
||||
|
||||
def click_dev_accounts_info_link(self):
|
||||
|
@ -177,56 +209,64 @@ class SubmitAddon(Page):
|
|||
self.find_element(*self._dev_accounts_info_link_locator).click()
|
||||
self.wait.until(
|
||||
EC.text_to_be_present_in_element(
|
||||
(By.CSS_SELECTOR, '.page-hero h1'), 'Developer accounts'
|
||||
(By.CSS_SELECTOR, ".page-hero h1"), "Developer accounts"
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
def listed_option_helptext(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._listed_option_helptext_locator)
|
||||
)
|
||||
return self.find_element(*self._listed_option_helptext_locator).text
|
||||
|
||||
@property
|
||||
def unlisted_option_helptext(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._unlisted_option_helptext_locator)
|
||||
)
|
||||
return self.find_element(*self._unlisted_option_helptext_locator)
|
||||
|
||||
@property
|
||||
def update_url_link(self):
|
||||
return self.unlisted_option_helptext.find_element(By.CSS_SELECTOR, 'a')
|
||||
return self.unlisted_option_helptext.find_element(By.CSS_SELECTOR, "a")
|
||||
|
||||
@property
|
||||
def listed_option_radiobutton(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._listed_option_locator)
|
||||
)
|
||||
self.wait.until(EC.visibility_of_element_located(self._listed_option_locator))
|
||||
return self.find_element(*self._listed_option_locator)
|
||||
|
||||
def select_listed_option(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._listed_option_locator)
|
||||
)
|
||||
self.wait.until(EC.element_to_be_clickable(self._listed_option_locator))
|
||||
self.find_element(*self._listed_option_locator).click()
|
||||
|
||||
def select_unlisted_option(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._unlisted_option_locator)
|
||||
)
|
||||
self.wait.until(EC.element_to_be_clickable(self._unlisted_option_locator))
|
||||
self.find_element(*self._unlisted_option_locator).click()
|
||||
|
||||
@property
|
||||
def distribution_and_signing_helptext(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._distribution_and_signing_helptext_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._distribution_and_signing_helptext_locator)
|
||||
|
||||
@property
|
||||
def distribution_and_signing_link(self):
|
||||
return self.distribution_and_signing_helptext.find_element(By.CSS_SELECTOR, 'a')
|
||||
return self.distribution_and_signing_helptext.find_element(By.CSS_SELECTOR, "a")
|
||||
|
||||
@property
|
||||
def addon_policies_helptext(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._addon_policies_helptext_locator)
|
||||
)
|
||||
return self.find_element(*self._addon_policies_helptext_locator)
|
||||
|
||||
@property
|
||||
def addon_policies_link(self):
|
||||
return self.addon_policies_helptext.find_element(By.CSS_SELECTOR, 'a')
|
||||
return self.addon_policies_helptext.find_element(By.CSS_SELECTOR, "a")
|
||||
|
||||
def change_version_distribution(self):
|
||||
"""Changes the distribution (listed/unlisted) when submitting a new version"""
|
||||
|
@ -238,24 +278,36 @@ class SubmitAddon(Page):
|
|||
|
||||
@property
|
||||
def file_upload_helptext(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._file_upload_process_helptext_locator)
|
||||
)
|
||||
return self.find_elements(*self._file_upload_process_helptext_locator)
|
||||
|
||||
def upload_addon(self, addon):
|
||||
"""Selects an addon from the 'sample-addons' folder and uploads it"""
|
||||
button = self.find_element(*self._upload_file_button_locator)
|
||||
archive = Path(f'{os.getcwd()}/sample-addons/{addon}')
|
||||
archive = Path(f"{os.getcwd()}/sample-addons/{addon}")
|
||||
button.send_keys(str(archive))
|
||||
|
||||
@property
|
||||
def accepted_file_types(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._accepted_file_types_locator)
|
||||
)
|
||||
return self.find_element(*self._accepted_file_types_locator).text
|
||||
|
||||
@property
|
||||
def compatibility_helptext(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._compatibility_helptext_locator)
|
||||
)
|
||||
return self.find_elements(*self._compatibility_helptext_locator)[0].text
|
||||
|
||||
@property
|
||||
def compatibility_error_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._compatibility_error_message_locator)
|
||||
)
|
||||
return self.find_element(*self._compatibility_error_message_locator).text
|
||||
|
||||
@property
|
||||
|
@ -264,10 +316,16 @@ class SubmitAddon(Page):
|
|||
|
||||
@property
|
||||
def android_compat_checkbox(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._android_compat_checkbox_locator)
|
||||
)
|
||||
return self.find_element(*self._android_compat_checkbox_locator)
|
||||
|
||||
@property
|
||||
def create_theme_subheader(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._create_theme_subheader_locator)
|
||||
)
|
||||
return self.find_element(*self._create_theme_subheader_locator).text
|
||||
|
||||
def click_create_theme_button(self):
|
||||
|
@ -282,50 +340,68 @@ class SubmitAddon(Page):
|
|||
|
||||
@property
|
||||
def failed_validation_bar(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_fail_bar_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_fail_bar_locator)
|
||||
|
||||
@property
|
||||
def validation_status_title(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_status_text_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_status_text_locator).text
|
||||
|
||||
def click_validation_support_link(self):
|
||||
self.find_element(*self._validation_support_link_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',
|
||||
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.url_contains('/mozilla/addons-linter/'))
|
||||
self.wait.until(EC.url_contains("/mozilla/addons-linter/"))
|
||||
self.driver.close()
|
||||
# return to the main tab
|
||||
self.driver.switch_to.window(self.driver.window_handles[0])
|
||||
|
||||
@property
|
||||
def validation_failed_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_failed_message_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_failed_message_locator).text
|
||||
|
||||
@property
|
||||
def validation_failed_reason(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_fail_reason_locator)
|
||||
)
|
||||
return self.find_elements(*self._validation_fail_reason_locator)
|
||||
|
||||
@property
|
||||
def validation_warning_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_warning_message_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_warning_message_locator).text
|
||||
|
||||
def click_validation_summary(self):
|
||||
self.find_element(*self._validation_summary_link_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',
|
||||
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, 'results')))
|
||||
self.wait.until(EC.visibility_of_element_located((By.CLASS_NAME, "results")))
|
||||
return ValidationResults(self.driver, self.base_url)
|
||||
|
||||
@property
|
||||
def success_validation_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_success_message_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_success_message_locator)
|
||||
|
||||
def click_continue_upload_button(self):
|
||||
|
@ -333,7 +409,7 @@ class SubmitAddon(Page):
|
|||
return UploadSource(self.driver, self.base_url)
|
||||
|
||||
def submit_button_disabled(self):
|
||||
self.find_element(*self._submit_file_button_locator).get_attribute('disabled')
|
||||
self.find_element(*self._submit_file_button_locator).get_attribute("disabled")
|
||||
|
||||
|
||||
class ValidationResults(Page):
|
||||
|
@ -341,74 +417,109 @@ class ValidationResults(Page):
|
|||
By.CSS_SELECTOR,
|
||||
"div[class='section'] header h2",
|
||||
)
|
||||
_validation_summary_shelf_locator = (By.CLASS_NAME, 'tiers')
|
||||
_validation_general_results_locator = (By.ID, 'suite-results-tier-1')
|
||||
_validation_security_results_locator = (By.ID, 'suite-results-tier-2')
|
||||
_validation_extension_results_locator = (By.ID, 'suite-results-tier-3')
|
||||
_validation_localization_results_locator = (By.ID, 'suite-results-tier-4')
|
||||
_validation_compatibility_results_locator = (By.ID, 'suite-results-tier-5')
|
||||
_validation_summary_shelf_locator = (By.CLASS_NAME, "tiers")
|
||||
_validation_general_results_locator = (By.ID, "suite-results-tier-1")
|
||||
_validation_security_results_locator = (By.ID, "suite-results-tier-2")
|
||||
_validation_extension_results_locator = (By.ID, "suite-results-tier-3")
|
||||
_validation_localization_results_locator = (By.ID, "suite-results-tier-4")
|
||||
_validation_compatibility_results_locator = (By.ID, "suite-results-tier-5")
|
||||
|
||||
@property
|
||||
def validation_results_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_results_header_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_results_header_locator).text
|
||||
|
||||
@property
|
||||
def validation_summary_shelf(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_results_header_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_summary_shelf_locator)
|
||||
|
||||
@property
|
||||
def validation_general_results(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_general_results_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_general_results_locator)
|
||||
|
||||
@property
|
||||
def validation_security_results(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_security_results_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_security_results_locator)
|
||||
|
||||
@property
|
||||
def validation_extension_results(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._validation_extension_results_locator)
|
||||
)
|
||||
return self.find_element(*self._validation_extension_results_locator)
|
||||
|
||||
@property
|
||||
def validation_localization_results(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._validation_localization_results_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._validation_localization_results_locator)
|
||||
|
||||
@property
|
||||
def validation_compatibility_results(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._validation_compatibility_results_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._validation_compatibility_results_locator)
|
||||
|
||||
|
||||
class UploadSource(Page):
|
||||
_submit_source_code_page_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submission-process h3',
|
||||
".addon-submission-process h3",
|
||||
)
|
||||
_yes_submit_source_radio_button_locator = (By.ID, 'id_has_source_0')
|
||||
_no_submit_source_radio_button_locator = (By.ID, 'id_has_source_1')
|
||||
_choose_source_file_button_locator = (By.ID, 'id_source')
|
||||
_yes_submit_source_radio_button_locator = (By.ID, "id_has_source_0")
|
||||
_no_submit_source_radio_button_locator = (By.ID, "id_has_source_1")
|
||||
_choose_source_file_button_locator = (By.ID, "id_source")
|
||||
_continue_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.submission-buttons button:nth-child(1)',
|
||||
".submission-buttons button:nth-child(1)",
|
||||
)
|
||||
_upload_source_error_message_locator = (By.CSS_SELECTOR, '.errorlist li')
|
||||
_upload_source_error_message_locator = (By.CSS_SELECTOR, ".errorlist li")
|
||||
_cancel_and_disable_version_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.confirm-submission-cancel',
|
||||
".confirm-submission-cancel",
|
||||
)
|
||||
_cancel_and_disable_explainer_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#modal-confirm-submission-cancel p',
|
||||
"#modal-confirm-submission-cancel p",
|
||||
)
|
||||
_cancel_version_confirm_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.modal-actions .delete-button',
|
||||
".modal-actions .delete-button",
|
||||
)
|
||||
_do_not_cancel_version_link_locator = (By.CSS_SELECTOR, '.modal-actions a')
|
||||
_do_not_cancel_version_link_locator = (By.CSS_SELECTOR, ".modal-actions a")
|
||||
|
||||
@property
|
||||
def submit_source_page_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._submit_source_code_page_header_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._submit_source_code_page_header_locator).text
|
||||
|
||||
def select_yes_to_submit_source(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._yes_submit_source_radio_button_locator
|
||||
)
|
||||
)
|
||||
self.find_element(*self._yes_submit_source_radio_button_locator).click()
|
||||
|
||||
def select_no_to_omit_source(self):
|
||||
|
@ -416,7 +527,7 @@ class UploadSource(Page):
|
|||
|
||||
def choose_source(self, file):
|
||||
button = self.find_element(*self._choose_source_file_button_locator)
|
||||
archive = Path(f'{os.getcwd()}/sample-addons/{file}')
|
||||
archive = Path(f"{os.getcwd()}/sample-addons/{file}")
|
||||
button.send_keys(str(archive))
|
||||
|
||||
def continue_unlisted_submission(self):
|
||||
|
@ -433,6 +544,9 @@ class UploadSource(Page):
|
|||
|
||||
@property
|
||||
def source_upload_fail_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._upload_source_error_message_locator)
|
||||
)
|
||||
return self.find_element(*self._upload_source_error_message_locator).text
|
||||
|
||||
def click_cancel_and_disable_version(self):
|
||||
|
@ -440,6 +554,11 @@ class UploadSource(Page):
|
|||
|
||||
@property
|
||||
def cancel_and_disable_explainer_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._cancel_and_disable_explainer_text_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._cancel_and_disable_explainer_text_locator).text
|
||||
|
||||
def click_do_not_cancel_version(self):
|
||||
|
@ -451,61 +570,61 @@ class UploadSource(Page):
|
|||
|
||||
|
||||
class ListedAddonSubmissionForm(Page):
|
||||
_addon_name_field_locator = (By.CSS_SELECTOR, '#trans-name input:nth-child(1)')
|
||||
_edit_addon_slug_link_locator = (By.ID, 'edit_slug')
|
||||
_edit_addon_slug_field_locator = (By.ID, 'id_slug')
|
||||
_addon_summary_field_locator = (By.ID, 'id_summary_0')
|
||||
_addon_detail_fields_info_text_locator = (By.CSS_SELECTOR, '.edit-addon-details')
|
||||
_addon_name_field_locator = (By.CSS_SELECTOR, "#trans-name input:nth-child(1)")
|
||||
_edit_addon_slug_link_locator = (By.ID, "edit_slug")
|
||||
_edit_addon_slug_field_locator = (By.ID, "id_slug")
|
||||
_addon_summary_field_locator = (By.ID, "id_summary_0")
|
||||
_addon_detail_fields_info_text_locator = (By.CSS_SELECTOR, ".edit-addon-details")
|
||||
_summary_character_count_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
".char-count[data-for-startswith='id_summary_'] > b",
|
||||
)
|
||||
_addon_description_field_locator = (By.ID, 'id_description_0')
|
||||
_is_experimental_checkbox_locator = (By.ID, 'id_is_experimental')
|
||||
_requires_payment_checkbox_locator = (By.ID, 'id_requires_payment')
|
||||
_categories_section_locator = (By.ID, 'addon-categories-edit')
|
||||
_addon_description_field_locator = (By.ID, "id_description_0")
|
||||
_is_experimental_checkbox_locator = (By.ID, "id_is_experimental")
|
||||
_requires_payment_checkbox_locator = (By.ID, "id_requires_payment")
|
||||
_categories_section_locator = (By.ID, "addon-categories-edit")
|
||||
_firefox_categories_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-app-cats:nth-of-type(1) > ul input',
|
||||
".addon-app-cats:nth-of-type(1) > ul input",
|
||||
)
|
||||
_android_categories_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-app-cats:nth-of-type(2) > ul input',
|
||||
".addon-app-cats:nth-of-type(2) > ul input",
|
||||
)
|
||||
_email_input_field_locator = (By.ID, 'id_support_email_0')
|
||||
_support_site_input_field_locator = (By.ID, 'id_support_url_0')
|
||||
_license_options_locator = (By.CLASS_NAME, 'license')
|
||||
_license_details_link_locator = (By.CSS_SELECTOR, '.xx.extra')
|
||||
_custom_license_name_locator = (By.ID, 'id_license-name')
|
||||
_custom_license_text_locator = (By.ID, 'id_license-text')
|
||||
_privacy_policy_checkbox_locator = (By.ID, 'id_has_priv')
|
||||
_privacy_policy_textarea_locator = (By.ID, 'id_privacy_policy_0')
|
||||
_reviewer_notes_textarea_locator = (By.ID, 'id_approval_notes')
|
||||
_email_input_field_locator = (By.ID, "id_support_email_0")
|
||||
_support_site_input_field_locator = (By.ID, "id_support_url_0")
|
||||
_license_options_locator = (By.CLASS_NAME, "license")
|
||||
_license_details_link_locator = (By.CSS_SELECTOR, ".xx.extra")
|
||||
_custom_license_name_locator = (By.ID, "id_license-name")
|
||||
_custom_license_text_locator = (By.ID, "id_license-text")
|
||||
_privacy_policy_checkbox_locator = (By.ID, "id_has_priv")
|
||||
_privacy_policy_textarea_locator = (By.ID, "id_privacy_policy_0")
|
||||
_reviewer_notes_textarea_locator = (By.ID, "id_approval_notes")
|
||||
_submit_addon_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.submission-buttons button:nth-child(1)',
|
||||
".submission-buttons button:nth-child(1)",
|
||||
)
|
||||
_cancel_addon_submission_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.submission-buttons button:nth-child(2)',
|
||||
".submission-buttons button:nth-child(2)",
|
||||
)
|
||||
# _theme_categories_locator = (By.CSS_SELECTOR, '#addon-categories-edit > ul input') - temporary not available
|
||||
_theme_categories_locator = (By.CSS_SELECTOR, '#id_category input')
|
||||
_theme_categories_locator = (By.CSS_SELECTOR, "#id_category input")
|
||||
_theme_licence_sharing_rights_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#cc-chooser ul:nth-of-type(1) input',
|
||||
"#cc-chooser ul:nth-of-type(1) input",
|
||||
)
|
||||
_theme_license_commercial_use_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#cc-chooser ul:nth-of-type(2) input',
|
||||
"#cc-chooser ul:nth-of-type(2) input",
|
||||
)
|
||||
_theme_license_creation_rights_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'#cc-chooser ul:nth-of-type(3) input',
|
||||
"#cc-chooser ul:nth-of-type(3) input",
|
||||
)
|
||||
_selected_theme_license_locator = (By.CSS_SELECTOR, '#cc-license')
|
||||
_open_theme_licenses_list_locator = (By.CLASS_NAME, 'select-license')
|
||||
_theme_licenses_list_locator = (By.CSS_SELECTOR, '#id_license-builtin input')
|
||||
_selected_theme_license_locator = (By.CSS_SELECTOR, "#cc-license")
|
||||
_open_theme_licenses_list_locator = (By.CLASS_NAME, "select-license")
|
||||
_theme_licenses_list_locator = (By.CSS_SELECTOR, "#id_license-builtin input")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(
|
||||
|
@ -518,6 +637,9 @@ class ListedAddonSubmissionForm(Page):
|
|||
|
||||
@property
|
||||
def addon_name_field(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._addon_name_field_locator)
|
||||
)
|
||||
return self.find_element(*self._addon_name_field_locator)
|
||||
|
||||
def edit_addon_slug(self, value):
|
||||
|
@ -535,6 +657,9 @@ class ListedAddonSubmissionForm(Page):
|
|||
|
||||
@property
|
||||
def summary_character_count(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._summary_character_count_locator)
|
||||
)
|
||||
return self.find_element(*self._summary_character_count_locator).text
|
||||
|
||||
def set_addon_description(self, value):
|
||||
|
@ -542,14 +667,23 @@ class ListedAddonSubmissionForm(Page):
|
|||
|
||||
@property
|
||||
def is_experimental(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._is_experimental_checkbox_locator)
|
||||
)
|
||||
return self.find_element(*self._is_experimental_checkbox_locator)
|
||||
|
||||
@property
|
||||
def requires_payment(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._requires_payment_checkbox_locator)
|
||||
)
|
||||
return self.find_element(*self._requires_payment_checkbox_locator)
|
||||
|
||||
@property
|
||||
def categories_section(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._categories_section_locator)
|
||||
)
|
||||
return self.find_element(*self._categories_section_locator)
|
||||
|
||||
def select_firefox_categories(self, count):
|
||||
|
@ -569,24 +703,40 @@ class ListedAddonSubmissionForm(Page):
|
|||
|
||||
@property
|
||||
def select_license_options(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._license_options_locator))
|
||||
return self.find_elements(*self._license_options_locator)
|
||||
|
||||
def license_option_names(self, count, value):
|
||||
return self.select_license_options[count].get_attribute(value)
|
||||
|
||||
def license_details_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._license_details_link_locator)
|
||||
)
|
||||
self.find_element(*self._license_details_link_locator).click()
|
||||
|
||||
def set_custom_license_name(self, value):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._custom_license_name_locator)
|
||||
)
|
||||
self.find_element(*self._custom_license_name_locator).send_keys(value)
|
||||
|
||||
def set_custom_license_text(self, value):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._custom_license_text_locator)
|
||||
)
|
||||
self.find_element(*self._custom_license_text_locator).send_keys(value)
|
||||
|
||||
def select_theme_licence_sharing_rights(self, count):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._theme_licence_sharing_rights_locator)
|
||||
)
|
||||
self.find_elements(*self._theme_licence_sharing_rights_locator)[count].click()
|
||||
|
||||
def select_theme_license_commercial_use(self, count):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._theme_license_commercial_use_locator)
|
||||
)
|
||||
self.find_elements(*self._theme_license_commercial_use_locator)[count].click()
|
||||
|
||||
def select_theme_license_creation_rights(self, count):
|
||||
|
@ -594,15 +744,27 @@ class ListedAddonSubmissionForm(Page):
|
|||
|
||||
@property
|
||||
def generated_theme_license(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._selected_theme_license_locator)
|
||||
)
|
||||
return self.find_element(*self._selected_theme_license_locator)
|
||||
|
||||
def open_theme_licenses_list(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._open_theme_licenses_list_locator)
|
||||
)
|
||||
self.find_element(*self._open_theme_licenses_list_locator).click()
|
||||
|
||||
def select_theme_license_from_list(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._theme_licenses_list_locator)
|
||||
)
|
||||
self.find_elements(*self._theme_licenses_list_locator)
|
||||
|
||||
def set_privacy_policy(self, value):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._privacy_policy_checkbox_locator)
|
||||
)
|
||||
self.find_element(*self._privacy_policy_checkbox_locator).click()
|
||||
self.find_element(*self._privacy_policy_textarea_locator).send_keys(value)
|
||||
|
||||
|
@ -623,25 +785,26 @@ class ListedAddonSubmissionForm(Page):
|
|||
|
||||
|
||||
class ThemeWizard(Page):
|
||||
_wizard_header_locator = (By.CSS_SELECTOR, '.addon-submission-process > h3')
|
||||
_theme_name_input_field = (By.ID, 'theme-name')
|
||||
_upload_theme_image_button_locator = (By.ID, 'header-img')
|
||||
_uploaded_image_preview_locator = (By.CLASS_NAME, 'preview.loaded')
|
||||
_change_image_button_locator = (By.CLASS_NAME, 'reset')
|
||||
_browser_preview_locator = (By.ID, 'preview-svg-root')
|
||||
_browser_preview_header_image_locator = (By.ID, 'svg-header-img')
|
||||
_submit_theme_button_locator = (By.CLASS_NAME, 'button.upload')
|
||||
_wizard_header_locator = (By.CSS_SELECTOR, ".addon-submission-process > h3")
|
||||
_theme_name_input_field = (By.ID, "theme-name")
|
||||
_upload_theme_image_button_locator = (By.ID, "header-img")
|
||||
_uploaded_image_preview_locator = (By.CLASS_NAME, "preview.loaded")
|
||||
_change_image_button_locator = (By.CLASS_NAME, "reset")
|
||||
_browser_preview_locator = (By.ID, "preview-svg-root")
|
||||
_browser_preview_header_image_locator = (By.ID, "svg-header-img")
|
||||
_submit_theme_button_locator = (By.CLASS_NAME, "button.upload")
|
||||
_cancel_submission_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.submission-buttons .delete-button',
|
||||
".submission-buttons .delete-button",
|
||||
)
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(EC.visibility_of_element_located((By.ID, 'theme-header')))
|
||||
self.wait.until(EC.visibility_of_element_located((By.ID, "theme-header")))
|
||||
return self
|
||||
|
||||
@property
|
||||
def wizard_header(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._wizard_header_locator))
|
||||
return self.find_element(*self._wizard_header_locator).text
|
||||
|
||||
def set_theme_name(self, value):
|
||||
|
@ -649,20 +812,24 @@ class ThemeWizard(Page):
|
|||
|
||||
def upload_theme_header(self, img):
|
||||
button = self.find_element(*self._upload_theme_image_button_locator)
|
||||
header_img = Path(f'{os.getcwd()}/img/{img}')
|
||||
header_img = Path(f"{os.getcwd()}/img/{img}")
|
||||
button.send_keys(str(header_img))
|
||||
|
||||
@property
|
||||
def uploaded_image_preview(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._uploaded_image_preview_locator)
|
||||
)
|
||||
return self.find_element(*self._uploaded_image_preview_locator)
|
||||
|
||||
@property
|
||||
def uploaded_image_source(self):
|
||||
"""Fetch the source of the uploaded theme image"""
|
||||
return self.uploaded_image_preview.get_attribute('src')
|
||||
return self.uploaded_image_preview.get_attribute("src")
|
||||
|
||||
@property
|
||||
def browser_preview(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._browser_preview_locator))
|
||||
return self.find_element(*self._browser_preview_locator)
|
||||
|
||||
@property
|
||||
|
@ -670,7 +837,7 @@ class ThemeWizard(Page):
|
|||
"""Fetch the source of tha generated theme preview image"""
|
||||
return self.find_element(
|
||||
*self._browser_preview_header_image_locator
|
||||
).get_attribute('href')
|
||||
).get_attribute("href")
|
||||
|
||||
def submit_theme(self):
|
||||
self.find_element(*self._submit_theme_button_locator).click()
|
||||
|
@ -686,16 +853,16 @@ class ThemeWizard(Page):
|
|||
class SubmissionConfirmationPage(Page):
|
||||
_confirmation_page_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submission-process h3',
|
||||
".addon-submission-process h3",
|
||||
)
|
||||
_confirmation_messages_locator = (By.CSS_SELECTOR, '.addon-submission-process p')
|
||||
_manage_listing_button_locator = (By.LINK_TEXT, 'Go to My Submissions')
|
||||
_confirmation_messages_locator = (By.CSS_SELECTOR, ".addon-submission-process p")
|
||||
_manage_listing_button_locator = (By.LINK_TEXT, "Go to My Submissions")
|
||||
_edit_version_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.addon-submission-process p:nth-child(6) > a',
|
||||
".addon-submission-process p:nth-child(6) > a",
|
||||
)
|
||||
_edit_listing_button_locator = (By.LINK_TEXT, 'Manage Listing')
|
||||
_theme_preview_locator = (By.CSS_SELECTOR, '.addon-submission-process img')
|
||||
_edit_listing_button_locator = (By.LINK_TEXT, "Manage Listing")
|
||||
_theme_preview_locator = (By.CSS_SELECTOR, ".addon-submission-process img")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(
|
||||
|
@ -705,19 +872,25 @@ class SubmissionConfirmationPage(Page):
|
|||
|
||||
@property
|
||||
def submission_confirmation_messages(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._confirmation_messages_locator)
|
||||
)
|
||||
return self.find_elements(*self._confirmation_messages_locator)
|
||||
|
||||
def click_manage_listing_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._manage_listing_button_locator))
|
||||
self.find_element(*self._manage_listing_button_locator).click()
|
||||
from pages.desktop.developers.addons_manage import ManageAddons
|
||||
|
||||
return ManageAddons(self.driver, self.base_url).wait_for_page_to_load()
|
||||
|
||||
def click_edit_version_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._edit_version_button_locator))
|
||||
self.find_element(*self._edit_version_button_locator).click()
|
||||
return ManageVersions(self.driver, self.base_url)
|
||||
|
||||
def click_edit_listing_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._edit_listing_button_locator))
|
||||
self.find_element(*self._edit_listing_button_locator).click()
|
||||
from pages.desktop.developers.edit_addon import EditAddon
|
||||
|
||||
|
@ -725,4 +898,5 @@ class SubmissionConfirmationPage(Page):
|
|||
|
||||
@property
|
||||
def generated_theme_preview(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._theme_preview_locator))
|
||||
return self.find_element(*self._theme_preview_locator)
|
||||
|
|
|
@ -9,9 +9,9 @@ from pages.desktop.base import Base
|
|||
|
||||
|
||||
class BlogHomepage(Base):
|
||||
URL_TEMPLATE = 'blog/'
|
||||
URL_TEMPLATE = "blog/"
|
||||
|
||||
_articles_locator = (By.CSS_SELECTOR, '.blog-entry')
|
||||
_articles_locator = (By.CSS_SELECTOR, ".blog-entry")
|
||||
|
||||
@property
|
||||
def articles(self):
|
||||
|
@ -19,49 +19,54 @@ class BlogHomepage(Base):
|
|||
return [self.ArticlesList(self, el) for el in items]
|
||||
|
||||
class ArticlesList(Region):
|
||||
|
||||
_image_locator = (By.CSS_SELECTOR, '.blog-entry-featured-image > img')
|
||||
_image_link_locator = (By.CSS_SELECTOR, '.blog-entry-featured-image')
|
||||
_title_locator = (By.CLASS_NAME, 'blog-entry-title')
|
||||
_date_locator = (By.CLASS_NAME, 'blog-entry-date')
|
||||
_intro_text_locator = (By.CSS_SELECTOR, '.blog-entry-excerpt > p:nth-child(1)')
|
||||
_read_more_link_locator = (By.CSS_SELECTOR, '.blog-entry-read-more > a')
|
||||
_image_locator = (By.CSS_SELECTOR, ".blog-entry-featured-image > img")
|
||||
_image_link_locator = (By.CSS_SELECTOR, ".blog-entry-featured-image")
|
||||
_title_locator = (By.CLASS_NAME, "blog-entry-title")
|
||||
_date_locator = (By.CLASS_NAME, "blog-entry-date")
|
||||
_intro_text_locator = (By.CSS_SELECTOR, ".blog-entry-excerpt > p:nth-child(1)")
|
||||
_read_more_link_locator = (By.CSS_SELECTOR, ".blog-entry-read-more > a")
|
||||
|
||||
@property
|
||||
def image(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._image_locator))
|
||||
return self.find_element(*self._image_locator)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._image_locator))
|
||||
return self.find_element(*self._title_locator)
|
||||
|
||||
@property
|
||||
def date(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._image_locator))
|
||||
return self.find_element(*self._date_locator)
|
||||
|
||||
@property
|
||||
def intro_text(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._image_locator))
|
||||
return self.find_element(*self._intro_text_locator)
|
||||
|
||||
@property
|
||||
def read_more_link(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._image_locator))
|
||||
return self.find_element(*self._read_more_link_locator)
|
||||
|
||||
def click_read_more_link(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._image_locator))
|
||||
self.read_more_link.click()
|
||||
return ArticlePage(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
||||
|
||||
class ArticlePage(Base):
|
||||
_header_logo_locator = (By.CLASS_NAME, 'header-logo')
|
||||
_article_title = (By.CLASS_NAME, 'header-title')
|
||||
_navigation_bar_locator = (By.CSS_SELECTOR, '.blogpost-breadcrumb ol li')
|
||||
_content_paragraphs_locator = (By.CSS_SELECTOR, '.blogpost-content-wrapper > p')
|
||||
_last_updated_date_locator = (By.CSS_SELECTOR, 'dd.updated')
|
||||
_previous_article_link_locator = (By.CSS_SELECTOR, '.blogpost-nav-prev a p')
|
||||
_next_article_link_locator = (By.CSS_SELECTOR, '.blogpost-nav-next a p')
|
||||
_author_info_section_locator = (By.CLASS_NAME, 'blogpost-meta')
|
||||
_static_addon_card_locator = (By.CLASS_NAME, 'StaticAddonCard')
|
||||
_header_logo_locator = (By.CLASS_NAME, "header-logo")
|
||||
_article_title = (By.CLASS_NAME, "header-title")
|
||||
_navigation_bar_locator = (By.CSS_SELECTOR, ".blogpost-breadcrumb ol li")
|
||||
_content_paragraphs_locator = (By.CSS_SELECTOR, ".blogpost-content-wrapper > p")
|
||||
_last_updated_date_locator = (By.CSS_SELECTOR, "dd.updated")
|
||||
_previous_article_link_locator = (By.CSS_SELECTOR, ".blogpost-nav-prev a p")
|
||||
_next_article_link_locator = (By.CSS_SELECTOR, ".blogpost-nav-next a p")
|
||||
_author_info_section_locator = (By.CLASS_NAME, "blogpost-meta")
|
||||
_static_addon_card_locator = (By.CLASS_NAME, "StaticAddonCard")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(
|
||||
|
@ -71,30 +76,37 @@ class ArticlePage(Base):
|
|||
|
||||
@property
|
||||
def header_logo(self):
|
||||
self.wait_for_element_to_be_displayed(self._header_logo_locator)
|
||||
return self.find_element(*self._header_logo_locator)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
self.wait_for_element_to_be_displayed(self._article_title)
|
||||
return self.find_element(*self._article_title)
|
||||
|
||||
@property
|
||||
def nav_bar_links(self):
|
||||
self.wait_for_element_to_be_displayed(self._navigation_bar_locator)
|
||||
return self.find_elements(*self._navigation_bar_locator)
|
||||
|
||||
@property
|
||||
def content_paragraphs(self):
|
||||
self.wait_for_element_to_be_displayed(self._content_paragraphs_locator)
|
||||
return self.find_elements(*self._content_paragraphs_locator)
|
||||
|
||||
@property
|
||||
def last_updated_date(self):
|
||||
self.wait_for_element_to_be_displayed(self._last_updated_date_locator)
|
||||
return self.find_element(*self._last_updated_date_locator)
|
||||
|
||||
@property
|
||||
def next_article(self):
|
||||
self.wait_for_element_to_be_displayed(self._next_article_link_locator)
|
||||
return self.find_element(*self._next_article_link_locator)
|
||||
|
||||
@property
|
||||
def previous_article(self):
|
||||
self.wait_for_element_to_be_displayed(self._previous_article_link_locator)
|
||||
return self.find_element(*self._previous_article_link_locator)
|
||||
|
||||
@property
|
||||
|
@ -102,25 +114,31 @@ class ArticlePage(Base):
|
|||
return self.Author(self, self.find_element(*self._author_info_section_locator))
|
||||
|
||||
class Author(Region):
|
||||
_name_locator = (By.CSS_SELECTOR, 'dd.author')
|
||||
_picture_locator = (By.CSS_SELECTOR, '.author-avatar > img')
|
||||
_twitter_link_locator = (By.CLASS_NAME, 'share-twitter-link')
|
||||
_pocket_link_locator = (By.CLASS_NAME, 'share-pocket-link')
|
||||
_name_locator = (By.CSS_SELECTOR, "dd.author")
|
||||
_picture_locator = (By.CSS_SELECTOR, ".author-avatar > img")
|
||||
_twitter_link_locator = (By.CLASS_NAME, "share-twitter-link")
|
||||
_pocket_link_locator = (By.CLASS_NAME, "share-pocket-link")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._name_locator))
|
||||
return self.find_element(*self._name_locator)
|
||||
|
||||
@property
|
||||
def picture(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._picture_locator))
|
||||
return self.find_element(*self._picture_locator)
|
||||
|
||||
@property
|
||||
def twitter_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._twitter_link_locator)
|
||||
)
|
||||
return self.find_element(*self._twitter_link_locator)
|
||||
|
||||
@property
|
||||
def pocket_link(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._pocket_link_locator))
|
||||
return self.find_element(*self._pocket_link_locator)
|
||||
|
||||
@property
|
||||
|
@ -131,46 +149,56 @@ class ArticlePage(Base):
|
|||
]
|
||||
|
||||
class AddonCard(Region):
|
||||
_title_locator = (By.CSS_SELECTOR, '.AddonTitle > a')
|
||||
_author_locator = (By.CSS_SELECTOR, '.AddonTitle-author > a')
|
||||
_summary_locator = (By.CLASS_NAME, 'StaticAddonCard-summary')
|
||||
_rating_locator = (By.CLASS_NAME, 'Rating')
|
||||
_users_number_locator = (By.CLASS_NAME, 'StaticAddonCard-metadata-adu')
|
||||
_add_to_firefox_button_locator = (By.CLASS_NAME, 'GetFirefoxButton-button')
|
||||
_title_locator = (By.CSS_SELECTOR, ".AddonTitle > a")
|
||||
_author_locator = (By.CSS_SELECTOR, ".AddonTitle-author > a")
|
||||
_summary_locator = (By.CLASS_NAME, "StaticAddonCard-summary")
|
||||
_rating_locator = (By.CLASS_NAME, "Rating")
|
||||
_users_number_locator = (By.CLASS_NAME, "StaticAddonCard-metadata-adu")
|
||||
_add_to_firefox_button_locator = (By.CLASS_NAME, "GetFirefoxButton-button")
|
||||
_recommended_badge_link_locator = (
|
||||
By.CLASS_NAME,
|
||||
'PromotedBadge-link--recommended',
|
||||
"PromotedBadge-link--recommended",
|
||||
)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._title_locator))
|
||||
return self.find_element(*self._title_locator)
|
||||
|
||||
@property
|
||||
def author(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._author_locator))
|
||||
return self.find_element(*self._author_locator)
|
||||
|
||||
@property
|
||||
def summary(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._summary_locator))
|
||||
return self.find_element(*self._summary_locator).text
|
||||
|
||||
@property
|
||||
def rating(self):
|
||||
rating = self.find_element(*self._rating_locator).get_attribute('title')
|
||||
if 'There are no ratings yet' in rating:
|
||||
self.wait.until(EC.visibility_of_element_located(self._rating_locator))
|
||||
rating = self.find_element(*self._rating_locator).get_attribute("title")
|
||||
if "There are no ratings yet" in rating:
|
||||
return 0
|
||||
return float(rating.split()[1])
|
||||
|
||||
@property
|
||||
def users_number(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._users_number_locator)
|
||||
)
|
||||
return int(
|
||||
self.find_element(*self._users_number_locator)
|
||||
.text.split('Users: ')[1]
|
||||
.text.split("Users: ")[1]
|
||||
.replace(",", "")
|
||||
)
|
||||
|
||||
@property
|
||||
def add_to_firefox_button(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._add_to_firefox_button_locator)
|
||||
)
|
||||
return self.find_element(*self._add_to_firefox_button_locator)
|
||||
|
||||
@property
|
||||
|
@ -185,4 +213,7 @@ class ArticlePage(Base):
|
|||
|
||||
@property
|
||||
def recommended_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._recommended_badge_link_locator)
|
||||
)
|
||||
return self.find_element(*self._recommended_badge_link_locator)
|
||||
|
|
|
@ -10,41 +10,51 @@ from pages.desktop.base import Base
|
|||
|
||||
|
||||
class Collections(Base):
|
||||
URL_TEMPLATE = 'collections/'
|
||||
URL_TEMPLATE = "collections/"
|
||||
|
||||
_collections_card_header_locator = (By.CSS_SELECTOR, '.CollectionList-info header')
|
||||
_collections_card_summary_locator = (By.CLASS_NAME, 'CollectionList-info-text')
|
||||
_collections_create_button_locator = (By.CLASS_NAME, 'CollectionList-create')
|
||||
_collections_card_header_locator = (By.CSS_SELECTOR, ".CollectionList-info header")
|
||||
_collections_card_summary_locator = (By.CLASS_NAME, "CollectionList-info-text")
|
||||
_collections_create_button_locator = (By.CLASS_NAME, "CollectionList-create")
|
||||
_my_collections_list_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.CollectionList-list header',
|
||||
".CollectionList-list header",
|
||||
)
|
||||
_collection_item_locator = (By.CLASS_NAME, 'UserCollection')
|
||||
_collection_item_locator = (By.CLASS_NAME, "UserCollection")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
"""Waits for various page components to be loaded"""
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText')),
|
||||
message='The collections page was not loaded',
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText")),
|
||||
message="The collections page was not loaded",
|
||||
)
|
||||
return self
|
||||
|
||||
@property
|
||||
def list(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._collection_item_locator))
|
||||
"""Represents the list of collections form My Collections page"""
|
||||
items = self.find_elements(*self._collection_item_locator)
|
||||
return [self.Collection(self, el) for el in items]
|
||||
|
||||
@property
|
||||
def collections_list_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._my_collections_list_header_locator)
|
||||
)
|
||||
return self.find_element(*self._my_collections_list_header_locator).text
|
||||
|
||||
@property
|
||||
def collections_summary_card_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._collections_card_header_locator)
|
||||
)
|
||||
return self.find_element(*self._collections_card_header_locator).text
|
||||
|
||||
@property
|
||||
def collections_card_summary(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._collections_card_summary_locator)
|
||||
)
|
||||
return self.find_element(*self._collections_card_summary_locator).text
|
||||
|
||||
def select_collection(self, count):
|
||||
|
@ -60,15 +70,18 @@ class Collections(Base):
|
|||
|
||||
@property
|
||||
def create_collection_button(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._collections_create_button_locator)
|
||||
)
|
||||
return self.find_element(*self._collections_create_button_locator)
|
||||
|
||||
def click_create_collection(self):
|
||||
self.find_element(*self._collections_create_button_locator).click()
|
||||
self.wait.until(
|
||||
lambda _: self.is_element_displayed(
|
||||
By.CSS_SELECTOR, '.CollectionManager-submit.Button--disabled'
|
||||
By.CSS_SELECTOR, ".CollectionManager-submit.Button--disabled"
|
||||
),
|
||||
message='The collection save button was not displayed. The collection create form was not loaded properly',
|
||||
message="The collection save button was not displayed. The collection create form was not loaded properly",
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -78,65 +91,80 @@ class Collections(Base):
|
|||
class Collection(Region):
|
||||
"""Represents an individual collection in My collections list."""
|
||||
|
||||
_name_locator = (By.CLASS_NAME, 'UserCollection-name')
|
||||
_link_locator = (By.CLASS_NAME, 'UserCollection-link')
|
||||
_addon_number_locator = (By.CLASS_NAME, 'UserCollection-number')
|
||||
_name_locator = (By.CLASS_NAME, "UserCollection-name")
|
||||
_link_locator = (By.CLASS_NAME, "UserCollection-link")
|
||||
_addon_number_locator = (By.CLASS_NAME, "UserCollection-number")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._name_locator))
|
||||
return self.find_element(*self._name_locator)
|
||||
|
||||
@property
|
||||
def link(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._link_locator))
|
||||
return self.find_element(*self._link_locator)
|
||||
|
||||
@property
|
||||
def number_of_addons(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._addon_number_locator)
|
||||
)
|
||||
return self.find_element(*self._addon_number_locator)
|
||||
|
||||
@property
|
||||
def list_addons_count(self):
|
||||
count = self.number_of_addons.text
|
||||
return count.split()[0].replace(' add-ons', '')
|
||||
return count.split()[0].replace(" add-ons", "")
|
||||
|
||||
class CollectionCreate(Region):
|
||||
"""Represents the collections create form."""
|
||||
|
||||
_name_input_locator = (By.ID, 'collectionName')
|
||||
_description_input_locator = (By.ID, 'collectionDescription')
|
||||
_slug_input_locator = (By.ID, 'collectionSlug')
|
||||
_cancel_button_locator = (By.CLASS_NAME, 'CollectionManager-cancel')
|
||||
_name_input_locator = (By.ID, "collectionName")
|
||||
_description_input_locator = (By.ID, "collectionDescription")
|
||||
_slug_input_locator = (By.ID, "collectionSlug")
|
||||
_cancel_button_locator = (By.CLASS_NAME, "CollectionManager-cancel")
|
||||
_create_button_disabled_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.CollectionManager-submit.Button--disabled',
|
||||
".CollectionManager-submit.Button--disabled",
|
||||
)
|
||||
_create_button_locator = (By.CSS_SELECTOR, '.CollectionManager-submit')
|
||||
_placeholder_locator = (By.CLASS_NAME, 'Collection-placeholder')
|
||||
_add_success_message_locator = (By.CSS_SELECTOR, '.Notice-success p')
|
||||
_add_error_message_locator = (By.CSS_SELECTOR, '.Notice-error p')
|
||||
_removed_addon_notice_locator = (By.CSS_SELECTOR, '.Notice-generic p')
|
||||
_create_button_locator = (By.CSS_SELECTOR, ".CollectionManager-submit")
|
||||
_placeholder_locator = (By.CLASS_NAME, "Collection-placeholder")
|
||||
_add_success_message_locator = (By.CSS_SELECTOR, ".Notice-success p")
|
||||
_add_error_message_locator = (By.CSS_SELECTOR, ".Notice-error p")
|
||||
_removed_addon_notice_locator = (By.CSS_SELECTOR, ".Notice-generic p")
|
||||
_edit_collection_addons_list_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.EditableCollectionAddon',
|
||||
".EditableCollectionAddon",
|
||||
)
|
||||
|
||||
_warning_text_locator = (By.CSS_SELECTOR, '.Notice-error .Notice-text')
|
||||
_warning_text_locator = (By.CSS_SELECTOR, ".Notice-error .Notice-text")
|
||||
|
||||
def set_name(self, value):
|
||||
self.find_element(*self._name_input_locator).send_keys(value)
|
||||
|
||||
@property
|
||||
def name_value(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._name_input_locator))
|
||||
return self.find_element(*self._name_input_locator)
|
||||
|
||||
def set_description(self, value):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._description_input_locator)
|
||||
)
|
||||
self.find_element(*self._description_input_locator).send_keys(value)
|
||||
|
||||
def clear_description(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._description_input_locator)
|
||||
)
|
||||
self.find_element(*self._description_input_locator).clear()
|
||||
|
||||
@property
|
||||
def description_value(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._description_input_locator)
|
||||
)
|
||||
return self.find_element(*self._description_input_locator).text
|
||||
|
||||
def set_slug(self, value):
|
||||
|
@ -144,18 +172,28 @@ class Collections(Base):
|
|||
|
||||
@property
|
||||
def slug_value(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._description_input_locator)
|
||||
)
|
||||
return self.find_element(*self._description_input_locator).text
|
||||
|
||||
@property
|
||||
def slug_label_element(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._slug_input_locator))
|
||||
return self.find_element(*self._slug_input_locator)
|
||||
|
||||
@property
|
||||
def cancel_creation(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._cancel_button_locator)
|
||||
)
|
||||
return self.find_element(*self._cancel_button_locator)
|
||||
|
||||
@property
|
||||
def create_button_disabled(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._create_button_disabled_locator)
|
||||
)
|
||||
return self.find_element(*self._create_button_disabled_locator)
|
||||
|
||||
def save_collection(self):
|
||||
|
@ -178,20 +216,21 @@ class Collections(Base):
|
|||
return self.AddonSearch(self)
|
||||
|
||||
class AddonSearch(Region):
|
||||
_header_locator = (By.CSS_SELECTOR, '.AutoSearchInput-label')
|
||||
_root_locator = (By.CSS_SELECTOR, '.CollectionAddAddon')
|
||||
_search_field_locator = (By.ID, 'AutoSearchInput-collection-addon-query')
|
||||
_header_locator = (By.CSS_SELECTOR, ".AutoSearchInput-label")
|
||||
_root_locator = (By.CSS_SELECTOR, ".CollectionAddAddon")
|
||||
_search_field_locator = (By.ID, "AutoSearchInput-collection-addon-query")
|
||||
_search_list_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AutoSearchInput-suggestions-list',
|
||||
".AutoSearchInput-suggestions-list",
|
||||
)
|
||||
_search_item_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AutoSearchInput-suggestions-item',
|
||||
".AutoSearchInput-suggestions-item",
|
||||
)
|
||||
|
||||
@property
|
||||
def header(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._header_locator))
|
||||
return self.find_element(*self._header_locator)
|
||||
|
||||
def search(self, term):
|
||||
|
@ -203,28 +242,31 @@ class Collections(Base):
|
|||
def search_items(self):
|
||||
self.wait.until(
|
||||
lambda _: self.is_element_displayed(*self._search_list_locator),
|
||||
message='Search suggestions list was not loaded',
|
||||
message="Search suggestions list was not loaded",
|
||||
)
|
||||
WebDriverWait(self.driver, 30).until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText')),
|
||||
message='There were no search suggestions loaded for the used query',
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText")),
|
||||
message="There were no search suggestions loaded for the used query",
|
||||
)
|
||||
search_list = self.find_element(*self._search_list_locator)
|
||||
items = search_list.find_elements(*self._search_item_locator)
|
||||
return [self.SearchItems(self, el) for el in items]
|
||||
|
||||
class SearchItems(Region):
|
||||
_item_name_locator = (By.CSS_SELECTOR, '.SearchSuggestion-name')
|
||||
_item_name_locator = (By.CSS_SELECTOR, ".SearchSuggestion-name")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._item_name_locator)
|
||||
)
|
||||
return self.find_element(*self._item_name_locator)
|
||||
|
||||
@property
|
||||
def addon_add_confirmation(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._add_success_message_locator),
|
||||
message='There was no success message displayed after the addon was added to the collection',
|
||||
message="There was no success message displayed after the addon was added to the collection",
|
||||
)
|
||||
return self.find_element(*self._add_success_message_locator).text
|
||||
|
||||
|
@ -232,7 +274,7 @@ class Collections(Base):
|
|||
def addon_add_failure(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._add_error_message_locator),
|
||||
message='There was no error message displayed when the addon failed to be added to the collection',
|
||||
message="There was no error message displayed when the addon failed to be added to the collection",
|
||||
)
|
||||
return self.find_element(*self._add_error_message_locator).text
|
||||
|
||||
|
@ -240,52 +282,60 @@ class Collections(Base):
|
|||
def removed_addon_confirmation(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._removed_addon_notice_locator),
|
||||
message='There was no success message displayed after the addon was removed from the collection',
|
||||
message="There was no success message displayed after the addon was removed from the collection",
|
||||
)
|
||||
return self.find_element(*self._removed_addon_notice_locator).text
|
||||
|
||||
@property
|
||||
def edit_addons_list(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._edit_collection_addons_list_locator
|
||||
)
|
||||
)
|
||||
items = self.find_elements(*self._edit_collection_addons_list_locator)
|
||||
return [self.EditAddonsList(self, el) for el in items]
|
||||
|
||||
class EditAddonsList(Region):
|
||||
_edit_list_addon_name_locator = (
|
||||
By.CLASS_NAME,
|
||||
'EditableCollectionAddon-name',
|
||||
"EditableCollectionAddon-name",
|
||||
)
|
||||
_add_note_button_locator = (
|
||||
By.CLASS_NAME,
|
||||
'EditableCollectionAddon-leaveNote-button',
|
||||
"EditableCollectionAddon-leaveNote-button",
|
||||
)
|
||||
_add_note_textarea_locator = (By.CLASS_NAME, 'DismissibleTextForm-textarea')
|
||||
_save_note_button_locator = (By.CLASS_NAME, 'DismissibleTextForm-submit')
|
||||
_add_note_textarea_locator = (By.CLASS_NAME, "DismissibleTextForm-textarea")
|
||||
_save_note_button_locator = (By.CLASS_NAME, "DismissibleTextForm-submit")
|
||||
_note_text_locator = (
|
||||
By.CLASS_NAME,
|
||||
'EditableCollectionAddon-notes-content',
|
||||
"EditableCollectionAddon-notes-content",
|
||||
)
|
||||
_edit_addon_note_button_locator = (
|
||||
By.CLASS_NAME,
|
||||
'EditableCollectionAddon-notes-edit-button',
|
||||
"EditableCollectionAddon-notes-edit-button",
|
||||
)
|
||||
_delete_addon_note_button_locator = (
|
||||
By.CLASS_NAME,
|
||||
'DismissibleTextForm-delete',
|
||||
"DismissibleTextForm-delete",
|
||||
)
|
||||
_remove_addon_button_locator = (
|
||||
By.CLASS_NAME,
|
||||
'EditableCollectionAddon-remove-button',
|
||||
"EditableCollectionAddon-remove-button",
|
||||
)
|
||||
|
||||
@property
|
||||
def edit_list_addon_name(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._edit_list_addon_name_locator)
|
||||
)
|
||||
return self.find_element(*self._edit_list_addon_name_locator).text
|
||||
|
||||
def click_add_note(self):
|
||||
self.find_element(*self._add_note_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._add_note_textarea_locator),
|
||||
message='Collection addon note text input area was not displayed',
|
||||
message="Collection addon note text input area was not displayed",
|
||||
)
|
||||
|
||||
def note_input_text(self, value):
|
||||
|
@ -296,27 +346,36 @@ class Collections(Base):
|
|||
|
||||
@property
|
||||
def note_input_value(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._add_note_textarea_locator)
|
||||
)
|
||||
return self.find_element(*self._add_note_textarea_locator).text
|
||||
|
||||
def click_save_note(self):
|
||||
self.find_element(*self._save_note_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._note_text_locator),
|
||||
message='The collection addon note was not visible after saving',
|
||||
message="The collection addon note was not visible after saving",
|
||||
)
|
||||
|
||||
@property
|
||||
def note_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._note_text_locator)
|
||||
)
|
||||
return self.find_element(*self._note_text_locator).text
|
||||
|
||||
def click_edit_note(self):
|
||||
self.find_element(*self._edit_addon_note_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._add_note_textarea_locator),
|
||||
message='Collection addon note text input area was not displayed',
|
||||
message="Collection addon note text input area was not displayed",
|
||||
)
|
||||
|
||||
def click_delete_note(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._delete_addon_note_button_locator)
|
||||
)
|
||||
self.find_element(*self._delete_addon_note_button_locator).click()
|
||||
# waiting for the comment textarea to be closed after the note is deleted
|
||||
try:
|
||||
|
@ -324,13 +383,13 @@ class Collections(Base):
|
|||
EC.invisibility_of_element_located(
|
||||
self._add_note_textarea_locator
|
||||
),
|
||||
message='The collection note could not be deleted',
|
||||
message="The collection note could not be deleted",
|
||||
)
|
||||
# if the note could not be deleted because of a field error,
|
||||
# we need to catch that error and force the test to fail
|
||||
except TimeoutException:
|
||||
error = self.driver.find_element(
|
||||
By.CSS_SELECTOR, '.ErrorList p'
|
||||
By.CSS_SELECTOR, ".ErrorList p"
|
||||
).text
|
||||
pytest.fail(error)
|
||||
|
||||
|
@ -338,92 +397,106 @@ class Collections(Base):
|
|||
self.find_element(*self._remove_addon_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR, '.Notice-generic')
|
||||
(By.CSS_SELECTOR, ".Notice-generic")
|
||||
),
|
||||
message='Success message was not displayed after the addon has been removed from the collection',
|
||||
message="Success message was not displayed after the addon has been removed from the collection",
|
||||
)
|
||||
|
||||
class CollectionDetail(Region):
|
||||
"""Represents the detail page of a collection."""
|
||||
|
||||
_name_locator = (By.CLASS_NAME, 'CollectionDetails-title')
|
||||
_summary_locator = (By.CLASS_NAME, 'CollectionDetails-description')
|
||||
_addon_count_locator = (By.CSS_SELECTOR, '.MetadataCard dl:nth-child(1)')
|
||||
_collection_creator_locator = (By.CSS_SELECTOR, '.MetadataCard dl:nth-child(2)')
|
||||
_last_modified_date_locator = (By.CSS_SELECTOR, '.MetadataCard dl:nth-child(3)')
|
||||
_stats_data_locator = (By.CSS_SELECTOR, '.MetadataCard dd')
|
||||
_collection_addons_list_locator = (By.CSS_SELECTOR, '.SearchResult-result')
|
||||
_edit_button_locator = (By.CLASS_NAME, 'CollectionDetails-edit-button')
|
||||
_name_locator = (By.CLASS_NAME, "CollectionDetails-title")
|
||||
_summary_locator = (By.CLASS_NAME, "CollectionDetails-description")
|
||||
_addon_count_locator = (By.CSS_SELECTOR, ".MetadataCard dl:nth-child(1)")
|
||||
_collection_creator_locator = (By.CSS_SELECTOR, ".MetadataCard dl:nth-child(2)")
|
||||
_last_modified_date_locator = (By.CSS_SELECTOR, ".MetadataCard dl:nth-child(3)")
|
||||
_stats_data_locator = (By.CSS_SELECTOR, ".MetadataCard dd")
|
||||
_collection_addons_list_locator = (By.CSS_SELECTOR, ".SearchResult-result")
|
||||
_edit_button_locator = (By.CLASS_NAME, "CollectionDetails-edit-button")
|
||||
_edit_details_button_locator = (
|
||||
By.CLASS_NAME,
|
||||
'CollectionDetails-edit-details-button',
|
||||
"CollectionDetails-edit-details-button",
|
||||
)
|
||||
_cancel_collection_edit_locator = (
|
||||
By.CLASS_NAME,
|
||||
'CollectionDetails-back-to-collection-button',
|
||||
"CollectionDetails-back-to-collection-button",
|
||||
)
|
||||
_cancel_meta_edit_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.CollectionManager-cancel',
|
||||
".CollectionManager-cancel",
|
||||
)
|
||||
_delete_button_locator = (By.CLASS_NAME, 'Collection-delete-button')
|
||||
_delete_button_locator = (By.CLASS_NAME, "Collection-delete-button")
|
||||
_confirm_delete_dialog_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ConfirmationDialog-message',
|
||||
".ConfirmationDialog-message",
|
||||
)
|
||||
_cancel_delete_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ConfirmationDialog-cancel-button',
|
||||
".ConfirmationDialog-cancel-button",
|
||||
)
|
||||
_confirm_delete_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ConfirmationDialog-confirm-button',
|
||||
".ConfirmationDialog-confirm-button",
|
||||
)
|
||||
_collection_addons_sort_locator = (By.ID, 'CollectionSort-select')
|
||||
_collection_addons_sort_locator = (By.ID, "CollectionSort-select")
|
||||
|
||||
def wait_for_details_to_load(self):
|
||||
"""Waits for various page components to be loaded"""
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText')),
|
||||
message='The collections detail page was not loaded',
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText")),
|
||||
message="The collections detail page was not loaded",
|
||||
)
|
||||
return self
|
||||
|
||||
@property
|
||||
def collection_name(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._name_locator))
|
||||
return self.find_element(*self._name_locator).text
|
||||
|
||||
@property
|
||||
def collection_description(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._summary_locator))
|
||||
return self.find_element(*self._summary_locator)
|
||||
|
||||
@property
|
||||
def collection_addons_number(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_count_locator))
|
||||
return self.find_element(*self._addon_count_locator)
|
||||
|
||||
@property
|
||||
def collection_creator(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._collection_creator_locator)
|
||||
)
|
||||
return self.find_element(*self._collection_creator_locator)
|
||||
|
||||
@property
|
||||
def collection_last_update_date(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._last_modified_date_locator)
|
||||
)
|
||||
return self.find_element(*self._last_modified_date_locator)
|
||||
|
||||
@property
|
||||
def collection_stats(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._stats_data_locator))
|
||||
return self.find_elements(*self._stats_data_locator)
|
||||
|
||||
@property
|
||||
def collection_addons_list(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._collection_addons_list_locator)
|
||||
)
|
||||
return self.find_elements(*self._collection_addons_list_locator)
|
||||
|
||||
def click_edit_collection_button(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._edit_button_locator))
|
||||
self.find_element(*self._edit_button_locator).click()
|
||||
self.wait.until(
|
||||
lambda _: self.is_element_displayed(
|
||||
By.ID, 'AutoSearchInput-collection-addon-query'
|
||||
By.ID, "AutoSearchInput-collection-addon-query"
|
||||
),
|
||||
message='The edit collection search component was not loaded',
|
||||
message="The edit collection search component was not loaded",
|
||||
)
|
||||
|
||||
def click_back_to_collection(self):
|
||||
|
@ -437,46 +510,64 @@ class Collections(Base):
|
|||
self.find_element(*self._edit_details_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._cancel_meta_edit_button_locator),
|
||||
message='The edit collection meta form was not displayed',
|
||||
message="The edit collection meta form was not displayed",
|
||||
)
|
||||
|
||||
def cancel_edit_collection_meta(self):
|
||||
self.find_element(*self._cancel_meta_edit_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._edit_details_button_locator),
|
||||
message='The edit collection meta form could not be closed',
|
||||
message="The edit collection meta form could not be closed",
|
||||
)
|
||||
|
||||
def delete_collection(self):
|
||||
self.find_element(*self._delete_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._confirm_delete_button_locator),
|
||||
message='The delete collection confirmation section was not displayed',
|
||||
message="The delete collection confirmation section was not displayed",
|
||||
)
|
||||
|
||||
@property
|
||||
def confirm_delete_dialog_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._confirm_delete_dialog_locator)
|
||||
)
|
||||
return self.find_element(*self._confirm_delete_dialog_locator)
|
||||
|
||||
@property
|
||||
def cancel_delete_collection_button(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._cancel_delete_button_locator)
|
||||
)
|
||||
return self.find_element(*self._cancel_delete_button_locator)
|
||||
|
||||
def cancel_delete_collection(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._cancel_delete_button_locator)
|
||||
)
|
||||
self.find_element(*self._cancel_delete_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._delete_button_locator),
|
||||
message='The delete collection confirmation section could not be closed',
|
||||
message="The delete collection confirmation section could not be closed",
|
||||
)
|
||||
|
||||
@property
|
||||
def confirm_delete_collection_button(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._confirm_delete_button_locator)
|
||||
)
|
||||
return self.find_element(*self._confirm_delete_button_locator)
|
||||
|
||||
def confirm_delete_collection(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._confirm_delete_button_locator)
|
||||
)
|
||||
self.find_element(*self._confirm_delete_button_locator).click()
|
||||
return Collections(self.driver, self.page).wait_for_page_to_load()
|
||||
|
||||
@property
|
||||
def sort_addons(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._collection_addons_sort_locator)
|
||||
)
|
||||
return self.find_element(*self._collection_addons_sort_locator)
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -4,12 +4,14 @@ from pages.desktop.base import Base
|
|||
from regions.desktop.categories import Categories
|
||||
from regions.desktop.shelves import Shelves
|
||||
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
|
||||
class Extensions(Base):
|
||||
URL_TEMPLATE = 'extensions/'
|
||||
URL_TEMPLATE = "extensions/"
|
||||
|
||||
_title_locator = (By.CLASS_NAME, 'LandingPage-addonType-name')
|
||||
_header_summary_locator = (By.CSS_SELECTOR, '.LandingPage-header p')
|
||||
_title_locator = (By.CLASS_NAME, "LandingPage-addonType-name")
|
||||
_header_summary_locator = (By.CSS_SELECTOR, ".LandingPage-header p")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(lambda _: self.is_element_displayed(*self._title_locator))
|
||||
|
@ -17,10 +19,12 @@ class Extensions(Base):
|
|||
|
||||
@property
|
||||
def title(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._title_locator))
|
||||
return self.find_element(*self._title_locator).text
|
||||
|
||||
@property
|
||||
def header_summary(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._header_summary_locator))
|
||||
return self.find_element(*self._header_summary_locator).text
|
||||
|
||||
@property
|
||||
|
|
|
@ -12,92 +12,114 @@ from pages.desktop.frontend.details import Detail
|
|||
class Home(Base):
|
||||
"""Addons Home page"""
|
||||
|
||||
_recommended_extensions_locator = (By.CLASS_NAME, 'Home-Recommended-extensions')
|
||||
_recommended_themes_locator = (By.CLASS_NAME, 'Home-Recommended-themes')
|
||||
_hero_locator = (By.CLASS_NAME, 'HeroRecommendation')
|
||||
_secondary_hero_locator = (By.CLASS_NAME, 'SecondaryHero')
|
||||
_popular_extensions_locator = (By.CLASS_NAME, 'Home-PopularExtensions')
|
||||
_popular_themes_locator = (By.CLASS_NAME, 'Home-Popular-themes')
|
||||
_themes_category_locator = (By.CLASS_NAME, 'Home-CuratedThemes')
|
||||
_toprated_themes_locator = (By.CLASS_NAME, 'Home-TopRatedThemes')
|
||||
_featured_collections_locator = (By.CLASS_NAME, 'Home-FeaturedCollection')
|
||||
_shelves_titles_locator = (By.CSS_SELECTOR, '.CardList .Card-header-text')
|
||||
_recommended_extensions_locator = (By.CLASS_NAME, "Home-Recommended-extensions")
|
||||
_recommended_themes_locator = (By.CLASS_NAME, "Home-Recommended-themes")
|
||||
_hero_locator = (By.CLASS_NAME, "HeroRecommendation")
|
||||
_secondary_hero_locator = (By.CLASS_NAME, "SecondaryHero")
|
||||
_popular_extensions_locator = (By.CLASS_NAME, "Home-PopularExtensions")
|
||||
_popular_themes_locator = (By.CLASS_NAME, "Home-Popular-themes")
|
||||
_themes_category_locator = (By.CLASS_NAME, "Home-CuratedThemes")
|
||||
_toprated_themes_locator = (By.CLASS_NAME, "Home-TopRatedThemes")
|
||||
_featured_collections_locator = (By.CLASS_NAME, "Home-FeaturedCollection")
|
||||
_shelves_titles_locator = (By.CSS_SELECTOR, ".CardList .Card-header-text")
|
||||
_shelves_see_more_links_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Card-shelf-footer-in-header a',
|
||||
".Card-shelf-footer-in-header a",
|
||||
)
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
"""Waits for various page components to be loaded"""
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText'))
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText"))
|
||||
)
|
||||
return self
|
||||
|
||||
@property
|
||||
def primary_hero(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._hero_locator))
|
||||
return self.find_element(*self._hero_locator)
|
||||
|
||||
@property
|
||||
def hero_banner(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._hero_locator))
|
||||
el = self.find_element(*self._hero_locator)
|
||||
return self.PrimaryHero(self, el)
|
||||
|
||||
@property
|
||||
def addon_shelf_titles(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._shelves_titles_locator))
|
||||
return [
|
||||
title.text for title in self.find_elements(*self._shelves_titles_locator)
|
||||
]
|
||||
|
||||
@property
|
||||
def popular_extensions(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._popular_extensions_locator)
|
||||
)
|
||||
el = self.find_element(*self._popular_extensions_locator)
|
||||
return self.Extensions(self, el)
|
||||
|
||||
@property
|
||||
def recommended_extensions(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._recommended_extensions_locator)
|
||||
)
|
||||
el = self.find_element(*self._recommended_extensions_locator)
|
||||
return self.Extensions(self, el)
|
||||
|
||||
@property
|
||||
def recommended_themes(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._recommended_themes_locator)
|
||||
)
|
||||
el = self.find_element(*self._recommended_themes_locator)
|
||||
return self.Themes(self, el)
|
||||
|
||||
@property
|
||||
def popular_themes(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._popular_themes_locator))
|
||||
el = self.find_element(*self._popular_themes_locator)
|
||||
return self.Themes(self, el)
|
||||
|
||||
@property
|
||||
def toprated_themes(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._toprated_themes_locator))
|
||||
el = self.find_element(*self._toprated_themes_locator)
|
||||
return self.Themes(self, el)
|
||||
|
||||
@property
|
||||
def theme_category(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._themes_category_locator))
|
||||
el = self.find_element(*self._themes_category_locator)
|
||||
return self.ThemeCategory(self, el)
|
||||
|
||||
@property
|
||||
def secondary_hero(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._secondary_hero_locator))
|
||||
el = self.find_element(*self._secondary_hero_locator)
|
||||
return self.SecondaryHero(self, el)
|
||||
|
||||
@property
|
||||
def featured_collections(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._featured_collections_locator)
|
||||
)
|
||||
el = self.find_element(*self._featured_collections_locator)
|
||||
return self.Extensions(self, el)
|
||||
|
||||
@property
|
||||
def see_more_links_in_shelves(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._shelves_see_more_links_locator)
|
||||
)
|
||||
return self.find_elements(*self._shelves_see_more_links_locator)
|
||||
|
||||
def click_see_more_links(self, count):
|
||||
link = [el for el in self.see_more_links_in_shelves]
|
||||
target = link[count].get_attribute('target')
|
||||
target = link[count].get_attribute("target")
|
||||
# external links are opened in new tabs, so we need to account for multiple windows
|
||||
if target == '_blank':
|
||||
if target == "_blank":
|
||||
home_tab = self.driver.current_window_handle
|
||||
link[count].click()
|
||||
self.wait.until(EC.number_of_windows_to_be(2))
|
||||
|
@ -105,11 +127,11 @@ class Home(Base):
|
|||
self.driver.switch_to.window(new_tab)
|
||||
# see more external links can contain variable content we might not know in advance (especially on prod)
|
||||
# the solution used here is to verify that the content we link to is available (i.e. page response = 200)
|
||||
self.wait.until(custom_waits.url_not_contains('about:blank'))
|
||||
self.wait.until(custom_waits.url_not_contains("about:blank"))
|
||||
page = requests.head(self.driver.current_url)
|
||||
assert (
|
||||
page.status_code == 200
|
||||
), f'The response status code was {page.status_code}'
|
||||
), f"The response status code was {page.status_code}"
|
||||
self.driver.close()
|
||||
self.driver.switch_to.window(home_tab)
|
||||
else:
|
||||
|
@ -117,46 +139,58 @@ class Home(Base):
|
|||
# in this case, we check that the content exists inside the AMO domain
|
||||
link[count].click()
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText'))
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText"))
|
||||
)
|
||||
assert 'addons' in self.driver.current_url
|
||||
assert "addons" in self.driver.current_url
|
||||
page = requests.head(self.driver.current_url)
|
||||
assert (
|
||||
page.status_code == 200
|
||||
), f'The response status code was {page.status_code}'
|
||||
), f"The response status code was {page.status_code}"
|
||||
self.driver.back()
|
||||
# waits for the homepage to reload
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText'))
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText"))
|
||||
)
|
||||
|
||||
class ThemeCategory(Region):
|
||||
_home_theme_category_locator = (By.CLASS_NAME, 'Home-SubjectShelf-list-item')
|
||||
_shelf_summary_locator = (By.CLASS_NAME, 'Home-SubjectShelf-subheading')
|
||||
_home_theme_category_locator = (By.CLASS_NAME, "Home-SubjectShelf-list-item")
|
||||
_shelf_summary_locator = (By.CLASS_NAME, "Home-SubjectShelf-subheading")
|
||||
|
||||
@property
|
||||
def list(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._home_theme_category_locator)
|
||||
)
|
||||
items = self.find_elements(*self._home_theme_category_locator)
|
||||
return [self.CategoryDetail(self.page, el) for el in items]
|
||||
|
||||
@property
|
||||
def shelf_summary(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._shelf_summary_locator)
|
||||
)
|
||||
return self.find_element(*self._shelf_summary_locator).text
|
||||
|
||||
class CategoryDetail(Region):
|
||||
_category_link_locator = (By.CLASS_NAME, 'Home-SubjectShelf-link')
|
||||
_category_link_locator = (By.CLASS_NAME, "Home-SubjectShelf-link")
|
||||
_category_name_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Home-SubjectShelf-link span:nth-child(2)',
|
||||
".Home-SubjectShelf-link span:nth-child(2)",
|
||||
)
|
||||
_category_icon_locator = (By.CLASS_NAME, 'CategoryIcon')
|
||||
_category_icon_locator = (By.CLASS_NAME, "CategoryIcon")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._category_name_locator)
|
||||
)
|
||||
return self.find_element(*self._category_name_locator).text
|
||||
|
||||
@property
|
||||
def category_icon(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._category_icon_locator)
|
||||
)
|
||||
return self.find_element(*self._category_icon_locator)
|
||||
|
||||
def click(self):
|
||||
|
@ -166,16 +200,18 @@ class Home(Base):
|
|||
return Search(self.driver, self.page.base_url)
|
||||
|
||||
class Extensions(Region):
|
||||
_browse_all_locator = (By.CSS_SELECTOR, '.Card-shelf-footer-in-header a')
|
||||
_extensions_locator = (By.CLASS_NAME, 'SearchResult')
|
||||
_promo_card_header_locator = (By.CLASS_NAME, 'Card-header-text')
|
||||
_browse_all_locator = (By.CSS_SELECTOR, ".Card-shelf-footer-in-header a")
|
||||
_extensions_locator = (By.CLASS_NAME, "SearchResult")
|
||||
_promo_card_header_locator = (By.CLASS_NAME, "Card-header-text")
|
||||
|
||||
@property
|
||||
def list(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._extensions_locator))
|
||||
items = self.find_elements(*self._extensions_locator)
|
||||
return [Home.PromoShelvesAddons(self.page, el) for el in items]
|
||||
|
||||
def browse_all(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._browse_all_locator))
|
||||
self.find_element(*self._browse_all_locator).click()
|
||||
from pages.desktop.frontend.search import Search
|
||||
|
||||
|
@ -184,23 +220,29 @@ class Home(Base):
|
|||
|
||||
@property
|
||||
def card_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._promo_card_header_locator)
|
||||
)
|
||||
return self.find_element(*self._promo_card_header_locator).text
|
||||
|
||||
def see_collection_details(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._browse_all_locator))
|
||||
self.find_element(*self._browse_all_locator).click()
|
||||
# TODO: add additional validations when I'm covering collections
|
||||
|
||||
class Themes(Region):
|
||||
_browse_all_locator = (By.CSS_SELECTOR, '.Card-shelf-footer-in-header a')
|
||||
_themes_locator = (By.CLASS_NAME, 'SearchResult--theme')
|
||||
_promo_card_header_locator = (By.CLASS_NAME, 'Card-header-text')
|
||||
_browse_all_locator = (By.CSS_SELECTOR, ".Card-shelf-footer-in-header a")
|
||||
_themes_locator = (By.CLASS_NAME, "SearchResult--theme")
|
||||
_promo_card_header_locator = (By.CLASS_NAME, "Card-header-text")
|
||||
|
||||
@property
|
||||
def list(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._themes_locator))
|
||||
items = self.find_elements(*self._themes_locator)
|
||||
return [Home.PromoShelvesAddons(self.page, el) for el in items]
|
||||
|
||||
def browse_all(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._browse_all_locator))
|
||||
self.find_element(*self._browse_all_locator).click()
|
||||
from pages.desktop.frontend.search import Search
|
||||
|
||||
|
@ -209,20 +251,25 @@ class Home(Base):
|
|||
|
||||
@property
|
||||
def card_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._promo_card_header_locator)
|
||||
)
|
||||
return self.find_element(*self._promo_card_header_locator).text
|
||||
|
||||
class PromoShelvesAddons(Region):
|
||||
_addon_link_locator = (By.CLASS_NAME, 'SearchResult-link')
|
||||
_addon_name_locator = (By.CLASS_NAME, 'SearchResult-name')
|
||||
_addon_icon_locator = (By.CLASS_NAME, 'SearchResult-icon')
|
||||
_addon_users_locator = (By.CLASS_NAME, 'SearchResult-users-text')
|
||||
_addon_rating_locator = (By.CLASS_NAME, 'SearchResult-rating')
|
||||
_addon_link_locator = (By.CLASS_NAME, "SearchResult-link")
|
||||
_addon_name_locator = (By.CLASS_NAME, "SearchResult-name")
|
||||
_addon_icon_locator = (By.CLASS_NAME, "SearchResult-icon")
|
||||
_addon_users_locator = (By.CLASS_NAME, "SearchResult-users-text")
|
||||
_addon_rating_locator = (By.CLASS_NAME, "SearchResult-rating")
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_name_locator))
|
||||
return self.find_element(*self._addon_name_locator)
|
||||
|
||||
def click(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._addon_link_locator))
|
||||
self.find_element(*self._addon_link_locator).click()
|
||||
from pages.desktop.frontend.extensions import Extensions
|
||||
|
||||
|
@ -230,14 +277,19 @@ class Home(Base):
|
|||
|
||||
@property
|
||||
def addon_icon_preview(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_icon_locator))
|
||||
return self.find_element(*self._addon_icon_locator)
|
||||
|
||||
@property
|
||||
def addon_users_preview(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_users_locator))
|
||||
return self.find_element(*self._addon_users_locator)
|
||||
|
||||
@property
|
||||
def addon_rating_preview(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._addon_rating_locator)
|
||||
)
|
||||
return self.find_element(*self._addon_rating_locator)
|
||||
|
||||
def shelf_item_elements(self, item):
|
||||
|
@ -246,56 +298,75 @@ class Home(Base):
|
|||
assert item.addon_users_preview.is_displayed()
|
||||
|
||||
class PrimaryHero(Region):
|
||||
_hero_locator = (By.CLASS_NAME, 'HeroRecommendation')
|
||||
_hero_image_locator = (By.CLASS_NAME, 'HeroRecommendation-image')
|
||||
_hero_title_locator = (By.CLASS_NAME, 'HeroRecommendation-title-text')
|
||||
_hero_extension_name_locator = (By.CLASS_NAME, 'HeroRecommendation-heading')
|
||||
_hero_extension_summary_locator = (By.CLASS_NAME, 'HeroRecommendation-body')
|
||||
_extension_button_locator = (By.CLASS_NAME, 'HeroRecommendation-link')
|
||||
_extension_link_locator = (By.CSS_SELECTOR, '.HeroRecommendation-info a')
|
||||
_hero_locator = (By.CLASS_NAME, "HeroRecommendation")
|
||||
_hero_image_locator = (By.CLASS_NAME, "HeroRecommendation-image")
|
||||
_hero_title_locator = (By.CLASS_NAME, "HeroRecommendation-title-text")
|
||||
_hero_extension_name_locator = (By.CLASS_NAME, "HeroRecommendation-heading")
|
||||
_hero_extension_summary_locator = (By.CLASS_NAME, "HeroRecommendation-body")
|
||||
_extension_button_locator = (By.CLASS_NAME, "HeroRecommendation-link")
|
||||
_extension_link_locator = (By.CSS_SELECTOR, ".HeroRecommendation-info a")
|
||||
|
||||
@property
|
||||
def primary_hero_banner(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._hero_locator))
|
||||
return self.find_element(*self._hero_locator)
|
||||
|
||||
@property
|
||||
def primary_hero_image(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._hero_image_locator))
|
||||
return self.find_element(*self._hero_image_locator)
|
||||
|
||||
@property
|
||||
def primary_hero_title(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._hero_title_locator))
|
||||
return self.find_element(*self._hero_title_locator).text
|
||||
|
||||
@property
|
||||
def primary_hero_extension_name(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._hero_extension_name_locator)
|
||||
)
|
||||
return self.find_element(*self._hero_extension_name_locator).text
|
||||
|
||||
@property
|
||||
def primary_hero_extension_summary(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._hero_extension_summary_locator)
|
||||
)
|
||||
return self.find_element(*self._hero_extension_summary_locator)
|
||||
|
||||
def click_hero_extension_link(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._extension_button_locator))
|
||||
self.find_element(*self._extension_button_locator).click()
|
||||
return Detail(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
||||
class SecondaryHero(Region):
|
||||
_secondary_headline_locator = (By.CLASS_NAME, 'SecondaryHero-message-headline')
|
||||
_secondary_headline_locator = (By.CLASS_NAME, "SecondaryHero-message-headline")
|
||||
_secondary_description_locator = (
|
||||
By.CLASS_NAME,
|
||||
'SecondaryHero-message-description',
|
||||
"SecondaryHero-message-description",
|
||||
)
|
||||
_see_all_extensions_locator = (By.CLASS_NAME, 'SecondaryHero-message-link')
|
||||
_modules_locator = (By.CLASS_NAME, 'SecondaryHero-module')
|
||||
_see_all_extensions_locator = (By.CLASS_NAME, "SecondaryHero-message-link")
|
||||
_modules_locator = (By.CLASS_NAME, "SecondaryHero-module")
|
||||
|
||||
@property
|
||||
def secondary_hero_headline(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._secondary_headline_locator)
|
||||
)
|
||||
return self.find_element(*self._secondary_headline_locator).text
|
||||
|
||||
@property
|
||||
def secondary_hero_description(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._secondary_description_locator)
|
||||
)
|
||||
return self.find_element(*self._secondary_description_locator).text
|
||||
|
||||
def see_all_extensions(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._see_all_extensions_locator)
|
||||
)
|
||||
self.find_element(*self._see_all_extensions_locator).click()
|
||||
from pages.desktop.frontend.extensions import Extensions
|
||||
|
||||
|
@ -303,31 +374,38 @@ class Home(Base):
|
|||
|
||||
@property
|
||||
def secondary_hero_modules(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._modules_locator))
|
||||
element = self.find_elements(*self._modules_locator)
|
||||
return [self.SecondaryHeroModules(self.page, el) for el in element]
|
||||
|
||||
class SecondaryHeroModules(Region):
|
||||
_module_icon_locator = (By.CLASS_NAME, 'SecondaryHero-module-icon')
|
||||
_module_icon_locator = (By.CLASS_NAME, "SecondaryHero-module-icon")
|
||||
_module_description_locator = (
|
||||
By.CLASS_NAME,
|
||||
'SecondaryHero-module-description',
|
||||
"SecondaryHero-module-description",
|
||||
)
|
||||
_module_link_locator = (By.CSS_SELECTOR, '.SecondaryHero-module a')
|
||||
_module_link_text_locator = (By.CLASS_NAME, 'SecondaryHero-module-linkText')
|
||||
_module_link_locator = (By.CSS_SELECTOR, ".SecondaryHero-module a")
|
||||
_module_link_text_locator = (By.CLASS_NAME, "SecondaryHero-module-linkText")
|
||||
|
||||
@property
|
||||
def module_icon(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._module_icon_locator)
|
||||
)
|
||||
return self.find_element(*self._module_icon_locator)
|
||||
|
||||
@property
|
||||
def module_description(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._module_description_locator)
|
||||
)
|
||||
return self.find_element(*self._module_description_locator)
|
||||
|
||||
def click_secondary_module_link(self):
|
||||
link = self.find_element(*self._module_link_locator)
|
||||
target = link.get_attribute('target')
|
||||
target = link.get_attribute("target")
|
||||
# external links are opened in new tabs, so we need to account for multiple windows
|
||||
if target == '_blank':
|
||||
if target == "_blank":
|
||||
link.click()
|
||||
self.wait.until(EC.number_of_windows_to_be(2))
|
||||
new_tab = self.driver.window_handles[1]
|
||||
|
@ -336,11 +414,11 @@ class Home(Base):
|
|||
# in advance what that content might be; also, we want to avoid frequent maintenance for
|
||||
# these tests; the solution used is to verify that the content we link to is available
|
||||
# (i.e. we check that the page response status is 200)
|
||||
self.wait.until(custom_waits.url_not_contains('about:blank'))
|
||||
self.wait.until(custom_waits.url_not_contains("about:blank"))
|
||||
page = requests.head(self.driver.current_url)
|
||||
assert (
|
||||
page.status_code == 200
|
||||
), f'The response status code was {page.status_code}'
|
||||
), f"The response status code was {page.status_code}"
|
||||
else:
|
||||
# this condition handles links that open on the amo domain; again, we might not know the
|
||||
# content in advance, so the best we can do is check that the page opens in AMO
|
||||
|
@ -348,11 +426,11 @@ class Home(Base):
|
|||
link.click()
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located(
|
||||
(By.CLASS_NAME, 'LoadingText')
|
||||
(By.CLASS_NAME, "LoadingText")
|
||||
)
|
||||
)
|
||||
assert 'addons' in self.driver.current_url
|
||||
assert "addons" in self.driver.current_url
|
||||
page = requests.head(self.driver.current_url)
|
||||
assert (
|
||||
page.status_code == 200
|
||||
), f'The response status code was {page.status_code}'
|
||||
), f"The response status code was {page.status_code}"
|
||||
|
|
|
@ -5,42 +5,60 @@ from pages.desktop.base import Base
|
|||
|
||||
|
||||
class LanguageTools(Base):
|
||||
URL_TEMPLATE = '/language-tools/'
|
||||
URL_TEMPLATE = "/language-tools/"
|
||||
|
||||
_language_tools_header_locator = (By.CLASS_NAME, 'Card-header-text')
|
||||
_langpacks_info_text_locator = (By.CSS_SELECTOR, '.Card-contents p:nth-child(2)')
|
||||
_dictionaries_info_text_locator = (By.CSS_SELECTOR, '.Card-contents p:nth-child(1)')
|
||||
_language_list_column_locator = (By.CSS_SELECTOR, '.pivoted:nth-child(1) strong')
|
||||
_langpacks_list_column_locator = (By.CSS_SELECTOR, '.pivoted:nth-child(2) a')
|
||||
_dictionaries_list_column_locator = (By.CSS_SELECTOR, '.pivoted:nth-child(3) a')
|
||||
_language_tools_header_locator = (By.CLASS_NAME, "Card-header-text")
|
||||
_langpacks_info_text_locator = (By.CSS_SELECTOR, ".Card-contents p:nth-child(2)")
|
||||
_dictionaries_info_text_locator = (By.CSS_SELECTOR, ".Card-contents p:nth-child(1)")
|
||||
_language_list_column_locator = (By.CSS_SELECTOR, ".pivoted:nth-child(1) strong")
|
||||
_langpacks_list_column_locator = (By.CSS_SELECTOR, ".pivoted:nth-child(2) a")
|
||||
_dictionaries_list_column_locator = (By.CSS_SELECTOR, ".pivoted:nth-child(3) a")
|
||||
|
||||
def loaded(self):
|
||||
"""Waits for various page components to be loaded"""
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText'))
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText"))
|
||||
)
|
||||
return self
|
||||
|
||||
@property
|
||||
def language_tools_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._language_tools_header_locator)
|
||||
)
|
||||
return self.find_element(*self._language_tools_header_locator).text
|
||||
|
||||
@property
|
||||
def language_packs_info_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._langpacks_info_text_locator)
|
||||
)
|
||||
return self.find_element(*self._langpacks_info_text_locator).text
|
||||
|
||||
@property
|
||||
def dictionaries_info_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._dictionaries_info_text_locator)
|
||||
)
|
||||
return self.find_element(*self._dictionaries_info_text_locator).text
|
||||
|
||||
@property
|
||||
def supported_languages_list(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._language_list_column_locator)
|
||||
)
|
||||
return self.find_elements(*self._language_list_column_locator)
|
||||
|
||||
@property
|
||||
def available_language_packs(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._langpacks_list_column_locator)
|
||||
)
|
||||
return self.find_elements(*self._langpacks_list_column_locator)
|
||||
|
||||
@property
|
||||
def available_dictionaries(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._dictionaries_list_column_locator)
|
||||
)
|
||||
return self.find_elements(*self._dictionaries_list_column_locator)
|
||||
|
|
|
@ -17,97 +17,137 @@ class Login(Base):
|
|||
Project level Environment Variables and are picked up at runtime"""
|
||||
|
||||
# 1. user that performs normal operations on the site, like writing add-on reviews
|
||||
REGULAR_USER_EMAIL = os.environ.get('REGULAR_USER_EMAIL')
|
||||
REGULAR_USER_PASSWORD = os.environ.get('REGULAR_USER_PASSWORD')
|
||||
REGULAR_USER_EMAIL = os.environ.get("REGULAR_USER_EMAIL")
|
||||
REGULAR_USER_PASSWORD = os.environ.get("REGULAR_USER_PASSWORD")
|
||||
# 2. user with elevated permissions that can perform special actions on the site
|
||||
ADMIN_USER_EMAIL = os.environ.get('ADMIN_USER_EMAIL')
|
||||
ADMIN_USER_PASSWORD = os.environ.get('ADMIN_USER_PASSWORD')
|
||||
ADMIN_USER_EMAIL = os.environ.get("ADMIN_USER_EMAIL")
|
||||
ADMIN_USER_PASSWORD = os.environ.get("ADMIN_USER_PASSWORD")
|
||||
# 3. user who has published add-ons on AMO
|
||||
DEVELOPER_EMAIL = os.environ.get('DEVELOPER_EMAIL')
|
||||
DEVELOPER_PASSWORD = os.environ.get('DEVELOPER_PASSWORD')
|
||||
DEVELOPER_EMAIL = os.environ.get("DEVELOPER_EMAIL")
|
||||
DEVELOPER_PASSWORD = os.environ.get("DEVELOPER_PASSWORD")
|
||||
# 4. user who re-creates accounts on AMO after having deleted them previously
|
||||
REUSABLE_USER_EMAIL = os.environ.get('REUSABLE_USER_EMAIL')
|
||||
REUSABLE_USER_PASSWORD = os.environ.get('REUSABLE_USER_PASSWORD')
|
||||
REUSABLE_USER_EMAIL = os.environ.get("REUSABLE_USER_EMAIL")
|
||||
REUSABLE_USER_PASSWORD = os.environ.get("REUSABLE_USER_PASSWORD")
|
||||
# 5. user used for the ratings tests
|
||||
RATING_USER_EMAIL = os.environ.get('RATING_USER_EMAIL')
|
||||
RATING_USER_PASSWORD = os.environ.get('RATING_USER_PASSWORD')
|
||||
RATING_USER_EMAIL = os.environ.get("RATING_USER_EMAIL")
|
||||
RATING_USER_PASSWORD = os.environ.get("RATING_USER_PASSWORD")
|
||||
# 6. user used for collections tests
|
||||
COLLECTION_USER_EMAIL = os.environ.get('COLLECTION_USER_EMAIL')
|
||||
COLLECTION_USER_PASSWORD = os.environ.get('COLLECTION_USER_PASSWORD')
|
||||
COLLECTION_USER_EMAIL = os.environ.get("COLLECTION_USER_EMAIL")
|
||||
COLLECTION_USER_PASSWORD = os.environ.get("COLLECTION_USER_PASSWORD")
|
||||
# 7. user used for add-on submissions
|
||||
SUBMISSIONS_USER_EMAIL = os.environ.get('SUBMISSIONS_USER_EMAIL')
|
||||
SUBMISSIONS_USER_PASSWORD = os.environ.get('SUBMISSIONS_USER_PASSWORD')
|
||||
SUBMISSIONS_USER_EMAIL = os.environ.get("SUBMISSIONS_USER_EMAIL")
|
||||
SUBMISSIONS_USER_PASSWORD = os.environ.get("SUBMISSIONS_USER_PASSWORD")
|
||||
# 8. user used in API tests
|
||||
API_USER_EMAIL = os.environ.get('API_USER_EMAIL')
|
||||
API_USER_PASSWORD = os.environ.get('API_USER_PASSWORD')
|
||||
API_USER_EMAIL = os.environ.get("API_USER_EMAIL")
|
||||
API_USER_PASSWORD = os.environ.get("API_USER_PASSWORD")
|
||||
# 9. user with a mozilla account that has specific submission permissions
|
||||
STAFF_USER_EMAIL = os.environ.get('STAFF_USER_EMAIL')
|
||||
STAFF_USER_PASSWORD = os.environ.get('STAFF_USER_PASSWORD')
|
||||
STAFF_USER_EMAIL = os.environ.get("STAFF_USER_EMAIL")
|
||||
STAFF_USER_PASSWORD = os.environ.get("STAFF_USER_PASSWORD")
|
||||
# 10. account added to the list of banned user emails for rating and addon submissions
|
||||
RESTRICTED_USER_EMAIL = os.environ.get('RESTRICTED_USER_EMAIL')
|
||||
RESTRICTED_USER_PASSWORD = os.environ.get('RESTRICTED_USER_PASSWORD')
|
||||
RESTRICTED_USER_EMAIL = os.environ.get("RESTRICTED_USER_EMAIL")
|
||||
RESTRICTED_USER_PASSWORD = os.environ.get("RESTRICTED_USER_PASSWORD")
|
||||
|
||||
# # KEYS FOR AUTHENTICATOR DEV
|
||||
DEVELOPER_USER_KEY_DEV = os.environ.get('DEVELOPER_USER_KEY_DEV')
|
||||
RATING_USER_KEY_DEV = os.environ.get('RATING_USER_KEY_DEV')
|
||||
SUBMISSIONS_USER_KEY_DEV = os.environ.get('SUBMISSIONS_USER_KEY_DEV')
|
||||
API_USER_KEY_DEV = os.environ.get('API_USER_KEY_DEV')
|
||||
STAFF_USER_KEY_DEV = os.environ.get('STAFF_USER_KEY_DEV')
|
||||
DEVELOPER_USER_KEY_DEV = os.environ.get("DEVELOPER_USER_KEY_DEV")
|
||||
RATING_USER_KEY_DEV = os.environ.get("RATING_USER_KEY_DEV")
|
||||
SUBMISSIONS_USER_KEY_DEV = os.environ.get("SUBMISSIONS_USER_KEY_DEV")
|
||||
API_USER_KEY_DEV = os.environ.get("API_USER_KEY_DEV")
|
||||
STAFF_USER_KEY_DEV = os.environ.get("STAFF_USER_KEY_DEV")
|
||||
|
||||
DEVELOPER_USER_KEY_STAGE = os.environ.get('DEVELOPER_USER_KEY_STAGE')
|
||||
RATING_USER_KEY_STAGE = os.environ.get('RATING_USER_KEY_STAGE')
|
||||
SUBMISSIONS_USER_KEY_STAGE = os.environ.get('SUBMISSIONS_USER_KEY_STAGE')
|
||||
API_USER_KEY_STAGE = os.environ.get('API_USER_KEY_STAGE')
|
||||
STAFF_USER_KEY_STAGE = os.environ.get('STAFF_USER_KEY_STAGE')
|
||||
DEVELOPER_USER_KEY_STAGE = os.environ.get("DEVELOPER_USER_KEY_STAGE")
|
||||
RATING_USER_KEY_STAGE = os.environ.get("RATING_USER_KEY_STAGE")
|
||||
SUBMISSIONS_USER_KEY_STAGE = os.environ.get("SUBMISSIONS_USER_KEY_STAGE")
|
||||
API_USER_KEY_STAGE = os.environ.get("API_USER_KEY_STAGE")
|
||||
STAFF_USER_KEY_STAGE = os.environ.get("STAFF_USER_KEY_STAGE")
|
||||
|
||||
_email_locator = (By.NAME, 'email')
|
||||
_continue_locator = (By.CSS_SELECTOR, '.button-row button')
|
||||
_password_locator = (By.ID, 'password')
|
||||
_login_btn_locator = (By.ID, 'submit-btn')
|
||||
_repeat_password_locator = (By.ID, 'vpassword')
|
||||
_age_locator = (By.ID, 'age')
|
||||
_code_input_locator = (By.CSS_SELECTOR, '.tooltip-below')
|
||||
_login_card_header_locator = (By.CSS_SELECTOR, '.card header h1')
|
||||
_2fa_input_locator = (By.CSS_SELECTOR, '.tooltip-below')
|
||||
_confirm_2fa_button_locator = (By.ID, 'use-logged-in')
|
||||
_error_2fa_code_locator = (By.CSS_SELECTOR, '.tooltip-below.invalid')
|
||||
_email_locator = (By.NAME, "email")
|
||||
_continue_locator = (By.CSS_SELECTOR, ".button-row button")
|
||||
_password_locator = (By.ID, "password")
|
||||
_login_btn_locator = (By.ID, "submit-btn")
|
||||
_repeat_password_locator = (By.ID, "vpassword")
|
||||
_age_locator = (By.ID, "age")
|
||||
_code_input_locator = (By.CSS_SELECTOR, ".tooltip-below")
|
||||
_login_card_header_locator = (By.CSS_SELECTOR, ".card header h1")
|
||||
_2fa_input_locator = (By.CSS_SELECTOR, ".tooltip-below")
|
||||
_confirm_2fa_button_locator = (By.ID, "use-logged-in")
|
||||
_error_2fa_code_locator = (By.CSS_SELECTOR, ".tooltip-below.invalid")
|
||||
|
||||
def account(self, user):
|
||||
if user == 'reusable_user':
|
||||
self.fxa_login(self.REUSABLE_USER_EMAIL, self.REUSABLE_USER_PASSWORD, '')
|
||||
elif user == 'admin':
|
||||
self.fxa_login(self.ADMIN_USER_EMAIL, self.ADMIN_USER_PASSWORD, '')
|
||||
elif user == 'developer':
|
||||
if user == "reusable_user":
|
||||
self.fxa_login(self.REUSABLE_USER_EMAIL, self.REUSABLE_USER_PASSWORD, "")
|
||||
elif user == "admin":
|
||||
self.fxa_login(self.ADMIN_USER_EMAIL, self.ADMIN_USER_PASSWORD, "")
|
||||
elif user == "developer":
|
||||
if "dev.allizom" not in self.base_url:
|
||||
self.fxa_login(self.DEVELOPER_EMAIL, self.DEVELOPER_PASSWORD, self.DEVELOPER_USER_KEY_STAGE)
|
||||
self.fxa_login(
|
||||
self.DEVELOPER_EMAIL,
|
||||
self.DEVELOPER_PASSWORD,
|
||||
self.DEVELOPER_USER_KEY_STAGE,
|
||||
)
|
||||
else:
|
||||
self.fxa_login(self.DEVELOPER_EMAIL, self.DEVELOPER_PASSWORD, self.DEVELOPER_USER_KEY_DEV)
|
||||
elif user == 'rating_user':
|
||||
self.fxa_login(
|
||||
self.DEVELOPER_EMAIL,
|
||||
self.DEVELOPER_PASSWORD,
|
||||
self.DEVELOPER_USER_KEY_DEV,
|
||||
)
|
||||
elif user == "rating_user":
|
||||
if "dev.allizom" not in self.base_url:
|
||||
self.fxa_login(self.RATING_USER_EMAIL, self.RATING_USER_PASSWORD, self.RATING_USER_KEY_STAGE)
|
||||
self.fxa_login(
|
||||
self.RATING_USER_EMAIL,
|
||||
self.RATING_USER_PASSWORD,
|
||||
self.RATING_USER_KEY_STAGE,
|
||||
)
|
||||
else:
|
||||
self.fxa_login(self.RATING_USER_EMAIL, self.RATING_USER_PASSWORD, self.RATING_USER_KEY_DEV)
|
||||
elif user == 'collection_user':
|
||||
self.fxa_login(self.COLLECTION_USER_EMAIL, self.COLLECTION_USER_PASSWORD, '')
|
||||
elif user == 'submissions_user':
|
||||
self.fxa_login(
|
||||
self.RATING_USER_EMAIL,
|
||||
self.RATING_USER_PASSWORD,
|
||||
self.RATING_USER_KEY_DEV,
|
||||
)
|
||||
elif user == "collection_user":
|
||||
self.fxa_login(
|
||||
self.COLLECTION_USER_EMAIL, self.COLLECTION_USER_PASSWORD, ""
|
||||
)
|
||||
elif user == "submissions_user":
|
||||
if "dev.allizom" not in self.base_url:
|
||||
self.fxa_login(self.SUBMISSIONS_USER_EMAIL, self.SUBMISSIONS_USER_PASSWORD, self.SUBMISSIONS_USER_KEY_STAGE)
|
||||
self.fxa_login(
|
||||
self.SUBMISSIONS_USER_EMAIL,
|
||||
self.SUBMISSIONS_USER_PASSWORD,
|
||||
self.SUBMISSIONS_USER_KEY_STAGE,
|
||||
)
|
||||
else:
|
||||
self.fxa_login(self.SUBMISSIONS_USER_EMAIL, self.SUBMISSIONS_USER_PASSWORD, self.SUBMISSIONS_USER_KEY_DEV)
|
||||
elif user == 'api_user':
|
||||
self.fxa_login(
|
||||
self.SUBMISSIONS_USER_EMAIL,
|
||||
self.SUBMISSIONS_USER_PASSWORD,
|
||||
self.SUBMISSIONS_USER_KEY_DEV,
|
||||
)
|
||||
elif user == "api_user":
|
||||
if "dev.allizom" not in self.base_url:
|
||||
self.fxa_login(self.API_USER_EMAIL, self.API_USER_PASSWORD, self.API_USER_KEY_STAGE)
|
||||
self.fxa_login(
|
||||
self.API_USER_EMAIL, self.API_USER_PASSWORD, self.API_USER_KEY_STAGE
|
||||
)
|
||||
else:
|
||||
self.fxa_login(self.API_USER_EMAIL, self.API_USER_PASSWORD, self.API_USER_KEY_DEV)
|
||||
elif user == 'staff_user':
|
||||
self.fxa_login(
|
||||
self.API_USER_EMAIL, self.API_USER_PASSWORD, self.API_USER_KEY_DEV
|
||||
)
|
||||
elif user == "staff_user":
|
||||
if "dev.allizom" not in self.base_url:
|
||||
self.fxa_login(self.STAFF_USER_EMAIL, self.STAFF_USER_PASSWORD, self.STAFF_USER_KEY_STAGE)
|
||||
self.fxa_login(
|
||||
self.STAFF_USER_EMAIL,
|
||||
self.STAFF_USER_PASSWORD,
|
||||
self.STAFF_USER_KEY_STAGE,
|
||||
)
|
||||
else:
|
||||
self.fxa_login(self.STAFF_USER_EMAIL, self.STAFF_USER_PASSWORD, self.STAFF_USER_KEY_DEV)
|
||||
elif user == 'restricted_user':
|
||||
self.fxa_login(self.RESTRICTED_USER_EMAIL, self.RESTRICTED_USER_PASSWORD, '')
|
||||
self.fxa_login(
|
||||
self.STAFF_USER_EMAIL,
|
||||
self.STAFF_USER_PASSWORD,
|
||||
self.STAFF_USER_KEY_DEV,
|
||||
)
|
||||
elif user == "restricted_user":
|
||||
self.fxa_login(
|
||||
self.RESTRICTED_USER_EMAIL, self.RESTRICTED_USER_PASSWORD, ""
|
||||
)
|
||||
else:
|
||||
self.fxa_login(self.REGULAR_USER_EMAIL, self.REGULAR_USER_PASSWORD, '')
|
||||
self.fxa_login(self.REGULAR_USER_EMAIL, self.REGULAR_USER_PASSWORD, "")
|
||||
|
||||
def fxa_login(self, email, password, key):
|
||||
self.find_element(*self._email_locator).send_keys(email)
|
||||
|
@ -120,7 +160,7 @@ class Login(Base):
|
|||
# here, I'm capturing that TimeoutException and trying to push the script to continue to the next steps.
|
||||
try:
|
||||
continue_btn = self.wait.until(
|
||||
EC.element_to_be_clickable((By.ID, 'submit-btn'))
|
||||
EC.element_to_be_clickable((By.ID, "submit-btn"))
|
||||
)
|
||||
continue_btn.click()
|
||||
except TimeoutException as error:
|
||||
|
@ -129,8 +169,8 @@ class Login(Base):
|
|||
print('The "click continue button" event occurred.')
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._password_locator),
|
||||
message=f'Password input field not displayed; '
|
||||
f'FxA card header was {self.find_element(*self._login_card_header_locator).text}',
|
||||
message=f"Password input field not displayed; "
|
||||
f"FxA card header was {self.find_element(*self._login_card_header_locator).text}",
|
||||
)
|
||||
print(
|
||||
f'The script should be on the password input screen here. We should see "Sign in" in the header.'
|
||||
|
@ -139,18 +179,14 @@ class Login(Base):
|
|||
self.find_element(*self._password_locator).send_keys(password)
|
||||
# waits for the password to be filled in
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located((By.CSS_SELECTOR, '.password.empty')),
|
||||
message='There was no input added in the password field',
|
||||
EC.invisibility_of_element_located((By.CSS_SELECTOR, ".password.empty")),
|
||||
message="There was no input added in the password field",
|
||||
)
|
||||
self.find_element(*self._login_btn_locator).click()
|
||||
# logic for 2fa enabled accounts
|
||||
if key != '':
|
||||
self.wait.until(
|
||||
EC.url_contains('signin_totp_code')
|
||||
)
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._2fa_input_locator)
|
||||
)
|
||||
if key != "":
|
||||
self.wait.until(EC.url_contains("signin_totp_code"))
|
||||
self.wait.until(EC.visibility_of_element_located(self._2fa_input_locator))
|
||||
time.sleep(30)
|
||||
totp = pyotp.TOTP(key)
|
||||
self.find_element(*self._2fa_input_locator).send_keys(totp.now())
|
||||
|
@ -168,20 +204,20 @@ class Login(Base):
|
|||
|
||||
# wait for transition between FxA page and AMO
|
||||
self.wait.until(
|
||||
EC.url_contains('addons'),
|
||||
message=f'AMO could not be loaded in {self.driver.current_url}. '
|
||||
f'Response status code was {requests.head(self.driver.current_url).status_code}',
|
||||
EC.url_contains("addons"),
|
||||
message=f"AMO could not be loaded in {self.driver.current_url}. "
|
||||
f"Response status code was {requests.head(self.driver.current_url).status_code}",
|
||||
)
|
||||
|
||||
def fxa_register(self):
|
||||
email = f'{reusables.get_random_string(10)}@restmail.net'
|
||||
email = f"{reusables.get_random_string(10)}@restmail.net"
|
||||
password = reusables.get_random_string(10)
|
||||
self.find_element(*self._email_locator).send_keys(email)
|
||||
# catching the geckodriver click() issue, in cae it happens here
|
||||
# issue - https://github.com/mozilla/geckodriver/issues/1608
|
||||
try:
|
||||
continue_btn = self.wait.until(
|
||||
EC.element_to_be_clickable((By.ID, 'submit-btn'))
|
||||
EC.element_to_be_clickable((By.ID, "submit-btn"))
|
||||
)
|
||||
continue_btn.click()
|
||||
except TimeoutException as error:
|
||||
|
@ -190,8 +226,8 @@ class Login(Base):
|
|||
# verify that the fxa register form was opened
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._password_locator),
|
||||
message=f'Password input field not displayed; '
|
||||
f'FxA card header was {self.find_element(*self._login_card_header_locator).text}',
|
||||
message=f"Password input field not displayed; "
|
||||
f"FxA card header was {self.find_element(*self._login_card_header_locator).text}",
|
||||
)
|
||||
self.find_element(*self._password_locator).send_keys(password)
|
||||
self.find_element(*self._repeat_password_locator).send_keys(password)
|
||||
|
@ -204,7 +240,7 @@ class Login(Base):
|
|||
self.find_element(*self._login_btn_locator).click()
|
||||
|
||||
def get_verification_code(self, mail):
|
||||
request = requests.get(f'https://restmail.net/mail/{mail}', timeout=10)
|
||||
request = requests.get(f"https://restmail.net/mail/{mail}", timeout=10)
|
||||
response = request.json()
|
||||
# creating a timed loop to address a possible communication delay between
|
||||
# FxA and restmail; this loop polls the endpoint for 20s to await a response
|
||||
|
@ -213,10 +249,10 @@ class Login(Base):
|
|||
while time.time() < timeout_start + 20:
|
||||
if response:
|
||||
verification_code = [
|
||||
key['headers']['x-verify-short-code'] for key in response
|
||||
key["headers"]["x-verify-short-code"] for key in response
|
||||
]
|
||||
return verification_code
|
||||
elif not response:
|
||||
requests.get(f'https://restmail.net/mail/{mail}', timeout=10)
|
||||
print('Restmail did not receive an email from FxA')
|
||||
requests.get(f"https://restmail.net/mail/{mail}", timeout=10)
|
||||
print("Restmail did not receive an email from FxA")
|
||||
return self
|
||||
|
|
|
@ -7,25 +7,25 @@ from pages.desktop.base import Base
|
|||
|
||||
|
||||
class Reviews(Base):
|
||||
_review_count_title_locator = (By.CLASS_NAME, 'AddonReviewList-reviewCount')
|
||||
_filter_by_score_locator = (By.CLASS_NAME, 'AddonReviewList-filterByScoreSelector')
|
||||
_user_review_permalink_locator = (By.CSS_SELECTOR, '.FeaturedAddonReview header')
|
||||
_addon_summary_card_locator = (By.CLASS_NAME, 'AddonSummaryCard')
|
||||
_featured_review_card_locator = (By.CSS_SELECTOR, '.FeaturedAddonReview-card')
|
||||
_reviews_list_locator = (By.CSS_SELECTOR, '.AddonReviewList-reviews-listing li')
|
||||
_editable_rating_stars_locator = (By.CSS_SELECTOR, '.Rating--editable button')
|
||||
_review_count_title_locator = (By.CLASS_NAME, "AddonReviewList-reviewCount")
|
||||
_filter_by_score_locator = (By.CLASS_NAME, "AddonReviewList-filterByScoreSelector")
|
||||
_user_review_permalink_locator = (By.CSS_SELECTOR, ".FeaturedAddonReview header")
|
||||
_addon_summary_card_locator = (By.CLASS_NAME, "AddonSummaryCard")
|
||||
_featured_review_card_locator = (By.CSS_SELECTOR, ".FeaturedAddonReview-card")
|
||||
_reviews_list_locator = (By.CSS_SELECTOR, ".AddonReviewList-reviews-listing li")
|
||||
_editable_rating_stars_locator = (By.CSS_SELECTOR, ".Rating--editable button")
|
||||
_score_star_highlight_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Rating--editable .Rating-selected-star',
|
||||
".Rating--editable .Rating-selected-star",
|
||||
)
|
||||
_rating_score_bars_locator = (By.CSS_SELECTOR, '.RatingsByStar-barContainer')
|
||||
_bar_rating_score_locator = (By.CSS_SELECTOR, '.RatingsByStar-star')
|
||||
_rating_score_bars_locator = (By.CSS_SELECTOR, ".RatingsByStar-barContainer")
|
||||
_bar_rating_score_locator = (By.CSS_SELECTOR, ".RatingsByStar-star")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
"""Waits for various page components to be loaded"""
|
||||
self.wait.until(
|
||||
expected.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText')),
|
||||
message='All reviews page could not be loaded',
|
||||
expected.invisibility_of_element_located((By.CLASS_NAME, "LoadingText")),
|
||||
message="All reviews page could not be loaded",
|
||||
)
|
||||
return self
|
||||
|
||||
|
@ -36,8 +36,8 @@ class Reviews(Base):
|
|||
@property
|
||||
def reviews_title_count(self):
|
||||
count = self.reviews_page_title
|
||||
review_count = count.split()[0].replace(' reviews', '')
|
||||
return int(review_count.replace(',', ''))
|
||||
review_count = count.split()[0].replace(" reviews", "")
|
||||
return int(review_count.replace(",", ""))
|
||||
|
||||
@property
|
||||
def filter_by_score(self):
|
||||
|
@ -77,9 +77,9 @@ class Reviews(Base):
|
|||
return self.FeaturedReview(self, el)
|
||||
|
||||
class FeaturedReview(Region):
|
||||
_author_locator = (By.CSS_SELECTOR, '.AddonReviewCard-authorByLine')
|
||||
_body_locator = (By.CSS_SELECTOR, '.ShowMoreCard-contents > div')
|
||||
_rating_stars_locator = (By.CSS_SELECTOR, '.Rating--small')
|
||||
_author_locator = (By.CSS_SELECTOR, ".AddonReviewCard-authorByLine")
|
||||
_body_locator = (By.CSS_SELECTOR, ".ShowMoreCard-contents > div")
|
||||
_rating_stars_locator = (By.CSS_SELECTOR, ".Rating--small")
|
||||
|
||||
@property
|
||||
def author(self):
|
||||
|
@ -99,36 +99,36 @@ class Reviews(Base):
|
|||
return [self.UserReview(self, el) for el in items]
|
||||
|
||||
class UserReview(Region):
|
||||
_rating_stars_locator = (By.CSS_SELECTOR, '.Rating--small')
|
||||
_rating_user_locator = (By.CSS_SELECTOR, '.AddonReviewCard-authorByLine')
|
||||
_rating_permalink_locator = (By.CSS_SELECTOR, '.AddonReviewCard-authorByLine a')
|
||||
_rating_stars_locator = (By.CSS_SELECTOR, ".Rating--small")
|
||||
_rating_user_locator = (By.CSS_SELECTOR, ".AddonReviewCard-authorByLine")
|
||||
_rating_permalink_locator = (By.CSS_SELECTOR, ".AddonReviewCard-authorByLine a")
|
||||
_selected_star_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserReview-byLine .Rating-selected-star',
|
||||
".UserReview-byLine .Rating-selected-star",
|
||||
)
|
||||
_review_body_locator = (By.CSS_SELECTOR, '.UserReview-body')
|
||||
_review_body_locator = (By.CSS_SELECTOR, ".UserReview-body")
|
||||
_delete_confirm_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ConfirmationDialog-confirm-button',
|
||||
".ConfirmationDialog-confirm-button",
|
||||
)
|
||||
_flag_review_button_locator = (By.CSS_SELECTOR, '.FlagReviewMenu-menu')
|
||||
_flag_review_menu_options = (By.CSS_SELECTOR, '.TooltipMenu-inner button')
|
||||
_flag_review_success_text = (By.CSS_SELECTOR, '.TooltipMenu-inner li')
|
||||
_flag_review_button_locator = (By.CSS_SELECTOR, ".FlagReviewMenu-menu")
|
||||
_flag_review_menu_options = (By.CSS_SELECTOR, ".TooltipMenu-inner button")
|
||||
_flag_review_success_text = (By.CSS_SELECTOR, ".TooltipMenu-inner li")
|
||||
_flag_review_login_button = (
|
||||
By.CSS_SELECTOR,
|
||||
'.TooltipMenu-list .Button--micro',
|
||||
".TooltipMenu-list .Button--micro",
|
||||
)
|
||||
_reply_to_review_locator = (By.CSS_SELECTOR, '.AddonReviewCard-allControls a')
|
||||
_reply_to_review_locator = (By.CSS_SELECTOR, ".AddonReviewCard-allControls a")
|
||||
_review_reply_textarea_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DismissibleTextForm-textarea',
|
||||
".DismissibleTextForm-textarea",
|
||||
)
|
||||
_publish_reply_button_locator = (By.CSS_SELECTOR, '.DismissibleTextForm-submit')
|
||||
_publish_reply_button_locator = (By.CSS_SELECTOR, ".DismissibleTextForm-submit")
|
||||
_reply_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonReviewCard-reply .ShowMoreCard-contents > div',
|
||||
".AddonReviewCard-reply .ShowMoreCard-contents > div",
|
||||
)
|
||||
_dev_reply_header_locator = (By.CSS_SELECTOR, '.UserReview-reply-header')
|
||||
_dev_reply_header_locator = (By.CSS_SELECTOR, ".UserReview-reply-header")
|
||||
|
||||
@property
|
||||
def rating_stars(self):
|
||||
|
@ -157,9 +157,9 @@ class Reviews(Base):
|
|||
self.find_element(*self._flag_review_button_locator).click()
|
||||
self.wait.until(
|
||||
expected.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR, '.TooltipMenu-list')
|
||||
(By.CSS_SELECTOR, ".TooltipMenu-list")
|
||||
),
|
||||
message='The flag review menu did not open',
|
||||
message="The flag review menu did not open",
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -169,9 +169,9 @@ class Reviews(Base):
|
|||
def select_flag_option(self, count):
|
||||
self.wait.until(
|
||||
expected.element_to_be_clickable(
|
||||
(By.CSS_SELECTOR, '.TooltipMenu-list li:nth-of-type(1)')
|
||||
(By.CSS_SELECTOR, ".TooltipMenu-list li:nth-of-type(1)")
|
||||
),
|
||||
message='Flag menu options were not loaded',
|
||||
message="Flag menu options were not loaded",
|
||||
)
|
||||
# using JavaScriptExecutor to avoid ElementClickInterceptedException
|
||||
self.driver.execute_script(
|
||||
|
@ -179,9 +179,9 @@ class Reviews(Base):
|
|||
)
|
||||
self.wait.until(
|
||||
expected.text_to_be_present_in_element(
|
||||
self._flag_review_button_locator, 'Flagged'
|
||||
self._flag_review_button_locator, "Flagged"
|
||||
),
|
||||
message='Flag review button state did not change',
|
||||
message="Flag review button state did not change",
|
||||
)
|
||||
|
||||
@property
|
||||
|
@ -192,19 +192,19 @@ class Reviews(Base):
|
|||
def flag_review_login_button(self):
|
||||
self.wait.until(
|
||||
expected.element_to_be_clickable(self._flag_review_login_button),
|
||||
message='Login button in Flag review menu was not loaded',
|
||||
message="Login button in Flag review menu was not loaded",
|
||||
)
|
||||
return self.find_element(*self._flag_review_login_button)
|
||||
|
||||
def click_reply_to_review(self):
|
||||
reply = self.wait.until(
|
||||
expected.element_to_be_clickable(self._reply_to_review_locator),
|
||||
message='Reply button was not loaded',
|
||||
message="Reply button was not loaded",
|
||||
)
|
||||
reply.click()
|
||||
self.wait.until(
|
||||
expected.element_to_be_clickable(self._review_reply_textarea_locator),
|
||||
message='Reply text area was not opened',
|
||||
message="Reply text area was not opened",
|
||||
)
|
||||
|
||||
def reply_text_input(self, value):
|
||||
|
@ -217,7 +217,7 @@ class Reviews(Base):
|
|||
self.find_element(*self._publish_reply_button_locator).click()
|
||||
self.wait.until(
|
||||
expected.visibility_of_element_located(self._dev_reply_header_locator),
|
||||
message='Developer reply section header was not displayed',
|
||||
message="Developer reply section header was not displayed",
|
||||
)
|
||||
|
||||
@property
|
||||
|
|
|
@ -7,29 +7,29 @@ from selenium.webdriver.support import expected_conditions as EC
|
|||
|
||||
|
||||
class Search(Page):
|
||||
_context_card_locator = (By.CLASS_NAME, 'SearchContextCard-header')
|
||||
_search_box_locator = (By.CLASS_NAME, 'AutoSearchInput-query')
|
||||
_submit_button_locator = (By.CLASS_NAME, 'AutoSearchInput-submit-button')
|
||||
_search_filters_sort_locator = (By.ID, 'SearchFilters-Sort')
|
||||
_search_filters_type_locator = (By.ID, 'SearchFilters-AddonType')
|
||||
_search_filters_os_locator = (By.ID, 'SearchFilters-OperatingSystem')
|
||||
_search_filters_badging_locator = (By.ID, 'SearchFilters-Badging')
|
||||
_recommended_checkbox_locator = (By.ID, 'SearchFilters-Recommended')
|
||||
_pagination_next_locator = (By.CSS_SELECTOR, '.Paginate-item--next')
|
||||
_pagination_previous_locator = (By.CLASS_NAME, 'Paginate-item--previous')
|
||||
_selected_page_locator = (By.CLASS_NAME, 'Paginate-page-number')
|
||||
_context_card_locator = (By.CLASS_NAME, "SearchContextCard-header")
|
||||
_search_box_locator = (By.CLASS_NAME, "AutoSearchInput-query")
|
||||
_submit_button_locator = (By.CLASS_NAME, "AutoSearchInput-submit-button")
|
||||
_search_filters_sort_locator = (By.ID, "SearchFilters-Sort")
|
||||
_search_filters_type_locator = (By.ID, "SearchFilters-AddonType")
|
||||
_search_filters_os_locator = (By.ID, "SearchFilters-OperatingSystem")
|
||||
_search_filters_badging_locator = (By.ID, "SearchFilters-Badging")
|
||||
_recommended_checkbox_locator = (By.ID, "SearchFilters-Recommended")
|
||||
_pagination_next_locator = (By.CSS_SELECTOR, ".Paginate-item--next")
|
||||
_pagination_previous_locator = (By.CLASS_NAME, "Paginate-item--previous")
|
||||
_selected_page_locator = (By.CLASS_NAME, "Paginate-page-number")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(
|
||||
expected.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText')),
|
||||
message='Search page could not be loaded',
|
||||
expected.invisibility_of_element_located((By.CLASS_NAME, "LoadingText")),
|
||||
message="Search page could not be loaded",
|
||||
)
|
||||
return self
|
||||
|
||||
def wait_for_contextcard_update(self, value):
|
||||
self.wait.until(
|
||||
expected.text_to_be_present_in_element(
|
||||
(By.CLASS_NAME, 'SearchContextCard-header'), value
|
||||
(By.CLASS_NAME, "SearchContextCard-header"), value
|
||||
),
|
||||
message=f'Expected search term "{value}" not found in "{self.find_element(*self._context_card_locator).text}"',
|
||||
)
|
||||
|
@ -39,11 +39,12 @@ class Search(Page):
|
|||
contains a certain number of items"""
|
||||
self.wait.until(
|
||||
lambda _: len(self.result_list.search_results) >= count,
|
||||
message=f'Expected search results to be {count} but the list returned {len(self.result_list.search_results)}',
|
||||
message=f"Expected search results to be {count} but the list returned {len(self.result_list.search_results)}",
|
||||
)
|
||||
|
||||
@property
|
||||
def results_info(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._context_card_locator))
|
||||
return self.find_element(*self._context_card_locator)
|
||||
|
||||
@property
|
||||
|
@ -52,46 +53,64 @@ class Search(Page):
|
|||
|
||||
@property
|
||||
def filter_by_sort(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._search_filters_sort_locator)
|
||||
)
|
||||
return self.find_element(*self._search_filters_sort_locator)
|
||||
|
||||
@property
|
||||
def filter_by_type(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._search_filters_type_locator)
|
||||
)
|
||||
return self.find_element(*self._search_filters_type_locator)
|
||||
|
||||
@property
|
||||
def filter_by_os(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._search_filters_os_locator)
|
||||
)
|
||||
return self.find_element(*self._search_filters_os_locator)
|
||||
|
||||
@property
|
||||
def filter_by_badging(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._search_filters_badging_locator)
|
||||
)
|
||||
return self.find_element(*self._search_filters_badging_locator)
|
||||
|
||||
@property
|
||||
def recommended_filter(self) -> object:
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._recommended_checkbox_locator)
|
||||
)
|
||||
return self.find_element(*self._recommended_checkbox_locator)
|
||||
|
||||
def next_page(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._pagination_next_locator))
|
||||
self.find_element(*self._pagination_next_locator).click()
|
||||
|
||||
def previous_page(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._pagination_previous_locator))
|
||||
self.find_element(*self._pagination_previous_locator).click()
|
||||
|
||||
@property
|
||||
def page_number(self):
|
||||
self.wait.until(
|
||||
lambda _: self.is_element_displayed(*self._selected_page_locator),
|
||||
message='Pagination items were not loaded',
|
||||
message="Pagination items were not loaded",
|
||||
)
|
||||
return self.find_element(*self._selected_page_locator).text
|
||||
|
||||
class SearchResultList(Region):
|
||||
_result_locator = (By.CLASS_NAME, 'SearchResult')
|
||||
_result_link_locator = (By.CLASS_NAME, 'SearchResult-link')
|
||||
_theme_locator = (By.CLASS_NAME, 'SearchResult--theme')
|
||||
_extension_locator = (By.CLASS_NAME, 'SearchResult-name')
|
||||
_result_locator = (By.CLASS_NAME, "SearchResult")
|
||||
_result_link_locator = (By.CLASS_NAME, "SearchResult-link")
|
||||
_theme_locator = (By.CLASS_NAME, "SearchResult--theme")
|
||||
_extension_locator = (By.CLASS_NAME, "SearchResult-name")
|
||||
|
||||
@property
|
||||
def search_results(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._result_locator))
|
||||
items = self.find_elements(*self._result_locator)
|
||||
return [self.ResultListItems(self, el) for el in items]
|
||||
|
||||
|
@ -102,33 +121,41 @@ class Search(Page):
|
|||
|
||||
@property
|
||||
def extension(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._extension_locator))
|
||||
items = self.find_elements(*self._extension_locator)
|
||||
return [self.ResultListItems(self, el) for el in items]
|
||||
|
||||
def click_search_result(self, count):
|
||||
self.wait.until(EC.element_to_be_clickable(self._result_link_locator))
|
||||
self.find_elements(*self._result_link_locator)[count].click()
|
||||
from pages.desktop.frontend.details import Detail
|
||||
|
||||
return Detail(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
||||
class ResultListItems(Region):
|
||||
_rating_locator = (By.CSS_SELECTOR, '.Rating--small')
|
||||
_search_item_name_locator = (By.CSS_SELECTOR, '.SearchResult-link')
|
||||
_promoted_badge_locator = (By.CSS_SELECTOR, '.PromotedBadge')
|
||||
_promoted_badge_label_locator = (By.CSS_SELECTOR, '.PromotedBadge-label')
|
||||
_users_locator = (By.CLASS_NAME, 'SearchResult-users')
|
||||
_users_number_locator = (By.CLASS_NAME, 'SearchResult-users-text')
|
||||
_icon_locator = (By.CLASS_NAME, 'SearchResult-icon')
|
||||
_rating_stars_locator = (By.CLASS_NAME, 'SearchResult-rating')
|
||||
_author_locator = (By.CLASS_NAME, 'SearchResult-author')
|
||||
_summary_locator = (By.CLASS_NAME, 'SearchResult-summary')
|
||||
_rating_locator = (By.CSS_SELECTOR, ".Rating--small")
|
||||
_search_item_name_locator = (By.CSS_SELECTOR, ".SearchResult-link")
|
||||
_promoted_badge_locator = (By.CSS_SELECTOR, ".PromotedBadge")
|
||||
_promoted_badge_label_locator = (By.CSS_SELECTOR, ".PromotedBadge-label")
|
||||
_users_locator = (By.CLASS_NAME, "SearchResult-users")
|
||||
_users_number_locator = (By.CLASS_NAME, "SearchResult-users-text")
|
||||
_icon_locator = (By.CLASS_NAME, "SearchResult-icon")
|
||||
_rating_stars_locator = (By.CLASS_NAME, "SearchResult-rating")
|
||||
_author_locator = (By.CLASS_NAME, "SearchResult-author")
|
||||
_summary_locator = (By.CLASS_NAME, "SearchResult-summary")
|
||||
|
||||
@property
|
||||
def search_name(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._search_item_name_locator)
|
||||
)
|
||||
return self.find_element(*self._search_item_name_locator)
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._search_item_name_locator)
|
||||
)
|
||||
return self.find_element(*self._search_item_name_locator).text
|
||||
|
||||
def link(self):
|
||||
|
@ -143,43 +170,57 @@ class Search(Page):
|
|||
|
||||
@property
|
||||
def users(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._users_number_locator)
|
||||
)
|
||||
users = self.find_element(*self._users_number_locator).text
|
||||
return int(users.split()[0].replace(',', '').replace('users', ''))
|
||||
return int(users.split()[0].replace(",", "").replace("users", ""))
|
||||
|
||||
@property
|
||||
def rating(self):
|
||||
"""Returns the rating"""
|
||||
rating = self.find_element(*self._rating_locator).get_property('title')
|
||||
self.wait.until(EC.visibility_of_element_located(self._rating_locator))
|
||||
rating = self.find_element(*self._rating_locator).get_property("title")
|
||||
return float(rating.split()[1])
|
||||
|
||||
@property
|
||||
def search_result_icon(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._icon_locator))
|
||||
return self.find_element(*self._icon_locator)
|
||||
|
||||
@property
|
||||
def search_result_rating_stars(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._rating_stars_locator)
|
||||
)
|
||||
return self.find_element(*self._rating_stars_locator)
|
||||
|
||||
@property
|
||||
def search_result_author(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._author_locator))
|
||||
return self.find_element(*self._author_locator)
|
||||
|
||||
@property
|
||||
def search_result_users(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._users_locator))
|
||||
return self.find_element(*self._users_locator)
|
||||
|
||||
@property
|
||||
def search_result_summary(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._summary_locator))
|
||||
return self.find_element(*self._summary_locator)
|
||||
|
||||
@property
|
||||
def promoted_badge(self):
|
||||
WebDriverWait(self.driver, 10).until(
|
||||
EC.visibility_of_element_located(self._promoted_badge_locator),
|
||||
message='Promoted badge was not found for these search results',
|
||||
message="Promoted badge was not found for these search results",
|
||||
)
|
||||
return self
|
||||
|
||||
@property
|
||||
def promoted_badge_label(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._promoted_badge_label_locator)
|
||||
)
|
||||
return self.find_element(*self._promoted_badge_label_locator).text
|
||||
|
|
|
@ -9,70 +9,89 @@ class StaticPages(Base):
|
|||
"""This class will store informative pages, such as 404 pages, server error pages
|
||||
and other pages that have only an informative scope"""
|
||||
|
||||
_not_found_page_locator = (By.CSS_SELECTOR, '.NotFound')
|
||||
_notice_text_locator = (By.CSS_SELECTOR, '.Notice-warning')
|
||||
_page_header_locator = (By.CSS_SELECTOR, '.Card-header-text')
|
||||
_content_locator = (By.CSS_SELECTOR, '.Card-contents')
|
||||
_content_card_links_locator = (By.CSS_SELECTOR, '.Card-contents a')
|
||||
_not_found_page_locator = (By.CSS_SELECTOR, ".NotFound")
|
||||
_notice_text_locator = (By.CSS_SELECTOR, ".Notice-warning")
|
||||
_page_header_locator = (By.CSS_SELECTOR, ".Card-header-text")
|
||||
_content_locator = (By.CSS_SELECTOR, ".Card-contents")
|
||||
_content_card_links_locator = (By.CSS_SELECTOR, ".Card-contents a")
|
||||
# ------- Review Guidelines page
|
||||
_review_guidelines_page_forum_link_locator = (By.CSS_SELECTOR, 'section > p > a')
|
||||
_review_guidelines_page_forum_link_locator = (By.CSS_SELECTOR, "section > p > a")
|
||||
# ------- About Firefox Add-ons page
|
||||
_thunderbird_link_locator = (By.CSS_SELECTOR, '#about > p > a:nth-child(1)')
|
||||
_seamonkey_link_locator = (By.CSS_SELECTOR, '#about > p > a:nth-child(2)')
|
||||
_thunderbird_link_locator = (By.CSS_SELECTOR, "#about > p > a:nth-child(1)")
|
||||
_seamonkey_link_locator = (By.CSS_SELECTOR, "#about > p > a:nth-child(2)")
|
||||
_get_involved_links_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Card-contents section > ul > li > a',
|
||||
".Card-contents section > ul > li > a",
|
||||
)
|
||||
# ------- Blocked Add-on page
|
||||
_blocked_addon_page_links_locator = (By.CSS_SELECTOR, '.Card-contents > p > a')
|
||||
_blocked_addon_page_links_locator = (By.CSS_SELECTOR, ".Card-contents > p > a")
|
||||
# ------- Login Expired page
|
||||
_reload_the_page_link_locator = (By.CSS_SELECTOR, '.ReloadPageLink')
|
||||
_reload_the_page_link_locator = (By.CSS_SELECTOR, ".ReloadPageLink")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
"""Waits for various page components to be loaded"""
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText')),
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText")),
|
||||
message="The requested page could not be loaded",
|
||||
)
|
||||
return self
|
||||
|
||||
@property
|
||||
def not_found_page(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._not_found_page_locator))
|
||||
return self.find_element(*self._not_found_page_locator)
|
||||
|
||||
@property
|
||||
def notice_messages(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._notice_text_locator))
|
||||
return self.find_elements(*self._notice_text_locator)
|
||||
|
||||
@property
|
||||
def page_header(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._page_header_locator))
|
||||
return self.find_element(*self._page_header_locator).text
|
||||
|
||||
@property
|
||||
def content(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._content_locator))
|
||||
return self.find_element(*self._content_locator)
|
||||
|
||||
# ------- Review Guidelines page
|
||||
@property
|
||||
def forum_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._review_guidelines_page_forum_link_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._review_guidelines_page_forum_link_locator)
|
||||
|
||||
# ------- About Firefox Add-ons page
|
||||
@property
|
||||
def page_links(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._content_card_links_locator)
|
||||
)
|
||||
return self.find_elements(*self._content_card_links_locator)
|
||||
|
||||
@property
|
||||
def thunderbird_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._thunderbird_link_locator)
|
||||
)
|
||||
return self.find_element(*self._thunderbird_link_locator)
|
||||
|
||||
@property
|
||||
def seamonkey_link(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._seamonkey_link_locator))
|
||||
return self.find_element(*self._seamonkey_link_locator)
|
||||
|
||||
@property
|
||||
def get_involved_links(self):
|
||||
# add all the links except 'wiki'
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._get_involved_links_locator)
|
||||
)
|
||||
links = self.find_elements(*self._get_involved_links_locator)
|
||||
# add the 'wiki' link
|
||||
links.append(self.find_elements(*self._content_card_links_locator)[10])
|
||||
|
@ -80,34 +99,53 @@ class StaticPages(Base):
|
|||
|
||||
@property
|
||||
def report_an_issue_links(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._content_card_links_locator)
|
||||
)
|
||||
return self.find_elements(*self._content_card_links_locator)[11:15]
|
||||
|
||||
@property
|
||||
def get_support_links(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._content_card_links_locator)
|
||||
)
|
||||
return self.find_elements(*self._content_card_links_locator)[15:]
|
||||
|
||||
# ------- Blocked Add-on page
|
||||
@property
|
||||
def addon_policies_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._blocked_addon_page_links_locator)
|
||||
)
|
||||
return self.find_elements(*self._blocked_addon_page_links_locator)[0]
|
||||
|
||||
@property
|
||||
def certain_criteria_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._blocked_addon_page_links_locator)
|
||||
)
|
||||
return self.find_elements(*self._blocked_addon_page_links_locator)[1]
|
||||
|
||||
@property
|
||||
def this_support_article_link(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._blocked_addon_page_links_locator)
|
||||
)
|
||||
return self.find_elements(*self._blocked_addon_page_links_locator)[2]
|
||||
|
||||
# ------- Login Expired page
|
||||
@property
|
||||
def logged_out_notice_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._logged_out_notice_locator)
|
||||
)
|
||||
return self.find_element(*self._logged_out_notice_locator)
|
||||
|
||||
@property
|
||||
def click_reload_page_link(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._reload_the_page_link_locator))
|
||||
self.find_element(*self._reload_the_page_link_locator).click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located((By.CSS_SELECTOR, '.AddonTitle'))
|
||||
EC.visibility_of_element_located((By.CSS_SELECTOR, ".AddonTitle"))
|
||||
)
|
||||
return Detail(self.driver, self.base_url)
|
||||
|
|
|
@ -3,13 +3,14 @@ from selenium.webdriver.common.by import By
|
|||
from pages.desktop.base import Base
|
||||
from regions.desktop.categories import Categories
|
||||
from regions.desktop.shelves import Shelves
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
|
||||
class Themes(Base):
|
||||
URL_TEMPLATE = 'themes/'
|
||||
URL_TEMPLATE = "themes/"
|
||||
|
||||
_title_locator = (By.CLASS_NAME, 'LandingPage-addonType-name')
|
||||
_header_summary_locator = (By.CSS_SELECTOR, '.LandingPage-header p')
|
||||
_title_locator = (By.CLASS_NAME, "LandingPage-addonType-name")
|
||||
_header_summary_locator = (By.CSS_SELECTOR, ".LandingPage-header p")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
self.wait.until(lambda _: self.is_element_displayed(*self._title_locator))
|
||||
|
@ -17,10 +18,12 @@ class Themes(Base):
|
|||
|
||||
@property
|
||||
def title(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._title_locator))
|
||||
return self.find_element(*self._title_locator).text
|
||||
|
||||
@property
|
||||
def header_summary(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._header_summary_locator))
|
||||
return self.find_element(*self._header_summary_locator).text
|
||||
|
||||
@property
|
||||
|
|
|
@ -14,14 +14,14 @@ from pages.desktop.frontend.search import Search
|
|||
|
||||
|
||||
class User(Base):
|
||||
URL_TEMPLATE = '/users/edit'
|
||||
URL_TEMPLATE = "/users/edit"
|
||||
|
||||
_display_name_locator = (By.CLASS_NAME, 'UserProfile-name')
|
||||
_display_name_locator = (By.CLASS_NAME, "UserProfile-name")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
"""Waits for various page components to be loaded"""
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText'))
|
||||
EC.invisibility_of_element_located((By.CLASS_NAME, "LoadingText"))
|
||||
)
|
||||
return self
|
||||
|
||||
|
@ -33,6 +33,7 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def user_display_name(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._display_name_locator))
|
||||
return self.find_element(*self._display_name_locator)
|
||||
|
||||
@property
|
||||
|
@ -44,100 +45,103 @@ class User(Base):
|
|||
return self.EditProfile(self)
|
||||
|
||||
class ViewProfile(Region):
|
||||
_user_icon_placeholder_locator = (By.CSS_SELECTOR, '.Icon-anonymous-user')
|
||||
_user_profile_image_locator = (By.CSS_SELECTOR, '.UserAvatar-image')
|
||||
_user_developer_role_locator = (By.CSS_SELECTOR, '.UserProfile-developer')
|
||||
_user_developer_role_icon_locator = (By.CSS_SELECTOR, '.Icon-developer')
|
||||
_user_artist_role_locator = (By.CSS_SELECTOR, '.UserProfile-artist')
|
||||
_user_artist_role_icon_locator = (By.CSS_SELECTOR, '.Icon-artist')
|
||||
_user_homepage_locator = (By.CSS_SELECTOR, '.UserProfile-homepage a')
|
||||
_user_location_locator = (By.CSS_SELECTOR, '.UserProfile-location')
|
||||
_user_occupation_locator = (By.CSS_SELECTOR, '.UserProfile-occupation')
|
||||
_user_creation_date_locator = (By.CSS_SELECTOR, '.UserProfile-user-since')
|
||||
_user_addons_count_locator = (By.CSS_SELECTOR, '.UserProfile-number-of-addons')
|
||||
_user_icon_placeholder_locator = (By.CSS_SELECTOR, ".Icon-anonymous-user")
|
||||
_user_profile_image_locator = (By.CSS_SELECTOR, ".UserAvatar-image")
|
||||
_user_developer_role_locator = (By.CSS_SELECTOR, ".UserProfile-developer")
|
||||
_user_developer_role_icon_locator = (By.CSS_SELECTOR, ".Icon-developer")
|
||||
_user_artist_role_locator = (By.CSS_SELECTOR, ".UserProfile-artist")
|
||||
_user_artist_role_icon_locator = (By.CSS_SELECTOR, ".Icon-artist")
|
||||
_user_homepage_locator = (By.CSS_SELECTOR, ".UserProfile-homepage a")
|
||||
_user_location_locator = (By.CSS_SELECTOR, ".UserProfile-location")
|
||||
_user_occupation_locator = (By.CSS_SELECTOR, ".UserProfile-occupation")
|
||||
_user_creation_date_locator = (By.CSS_SELECTOR, ".UserProfile-user-since")
|
||||
_user_addons_count_locator = (By.CSS_SELECTOR, ".UserProfile-number-of-addons")
|
||||
_user_addon_average_rating_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfile-rating-average',
|
||||
".UserProfile-rating-average",
|
||||
)
|
||||
_user_biography_locator = (By.CSS_SELECTOR, '.UserProfile-biography')
|
||||
_user_profile_edit_link_locator = (By.CSS_SELECTOR, '.UserProfile-edit-link')
|
||||
_user_extensions_card_locator = (By.CSS_SELECTOR, '.AddonsCard--vertical')
|
||||
_user_biography_locator = (By.CSS_SELECTOR, ".UserProfile-biography")
|
||||
_user_profile_edit_link_locator = (By.CSS_SELECTOR, ".UserProfile-edit-link")
|
||||
_user_extensions_card_locator = (By.CSS_SELECTOR, ".AddonsCard--vertical")
|
||||
_user_extensions_card_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonsCard--vertical .Card-header-text',
|
||||
".AddonsCard--vertical .Card-header-text",
|
||||
)
|
||||
_user_extensions_results_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonsCard--vertical .SearchResult',
|
||||
".AddonsCard--vertical .SearchResult",
|
||||
)
|
||||
_user_themes_card_locator = (By.CSS_SELECTOR, '.AddonsByAuthorsCard--theme')
|
||||
_user_themes_card_locator = (By.CSS_SELECTOR, ".AddonsByAuthorsCard--theme")
|
||||
_user_themes_card_header_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonsByAuthorsCard--theme .Card-header-text',
|
||||
".AddonsByAuthorsCard--theme .Card-header-text",
|
||||
)
|
||||
_user_themes_results_locator = (By.CLASS_NAME, 'SearchResult--theme')
|
||||
_user_reviews_card_locator = (By.CSS_SELECTOR, '.UserProfile-reviews')
|
||||
_user_themes_results_locator = (By.CLASS_NAME, "SearchResult--theme")
|
||||
_user_reviews_card_locator = (By.CSS_SELECTOR, ".UserProfile-reviews")
|
||||
_extensions_pagination_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonsCard--vertical .Paginate',
|
||||
".AddonsCard--vertical .Paginate",
|
||||
)
|
||||
_extensions_next_page_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonsCard--vertical .Paginate-item--next',
|
||||
".AddonsCard--vertical .Paginate-item--next",
|
||||
)
|
||||
_extensions_page_number_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonsCard--vertical .Paginate-page-number',
|
||||
".AddonsCard--vertical .Paginate-page-number",
|
||||
)
|
||||
_themes_pagination_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonsByAuthorsCard--theme .Paginate',
|
||||
".AddonsByAuthorsCard--theme .Paginate",
|
||||
)
|
||||
_themes_next_page_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonsByAuthorsCard--theme .Paginate-item--next',
|
||||
".AddonsByAuthorsCard--theme .Paginate-item--next",
|
||||
)
|
||||
_themes_page_number_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonsByAuthorsCard--theme .Paginate-page-number',
|
||||
".AddonsByAuthorsCard--theme .Paginate-page-number",
|
||||
)
|
||||
_user_review_list_locator = (By.CSS_SELECTOR, '.AddonReviewCard-viewOnly')
|
||||
_user_review_list_locator = (By.CSS_SELECTOR, ".AddonReviewCard-viewOnly")
|
||||
_user_abuse_report_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ReportUserAbuse-show-more',
|
||||
".ReportUserAbuse-show-more",
|
||||
)
|
||||
_abuse_report_form_header_locator = (By.CSS_SELECTOR, '.ReportUserAbuse-header')
|
||||
_abuse_report_form_header_locator = (By.CSS_SELECTOR, ".ReportUserAbuse-header")
|
||||
_abuse_report_form_help_text = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ReportUserAbuse-form p:nth-child(2)',
|
||||
".ReportUserAbuse-form p:nth-child(2)",
|
||||
)
|
||||
_abuse_report_form_additional_help_text = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ReportUserAbuse-form p:nth-child(3)',
|
||||
".ReportUserAbuse-form p:nth-child(3)",
|
||||
)
|
||||
_abuse_report_textarea_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DismissibleTextForm-textarea',
|
||||
".DismissibleTextForm-textarea",
|
||||
)
|
||||
_abuse_report_cancel_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DismissibleTextForm-dismiss',
|
||||
".DismissibleTextForm-dismiss",
|
||||
)
|
||||
_abuse_report_submit_disabled_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DismissibleTextForm-submit.Button--disabled',
|
||||
".DismissibleTextForm-submit.Button--disabled",
|
||||
)
|
||||
_abuse_report_submit_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.DismissibleTextForm-submit',
|
||||
".DismissibleTextForm-submit",
|
||||
)
|
||||
_abuse_report_confirm_message_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ReportUserAbuse--report-sent p:nth-child(2)',
|
||||
".ReportUserAbuse--report-sent p:nth-child(2)",
|
||||
)
|
||||
|
||||
@property
|
||||
def user_profile_icon_placeholder(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_icon_placeholder_locator)
|
||||
)
|
||||
return self.find_element(*self._user_icon_placeholder_locator)
|
||||
|
||||
@property
|
||||
|
@ -146,49 +150,78 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def icon_source(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_profile_image_locator)
|
||||
)
|
||||
return self.user_profile_icon.get_attribute('src')
|
||||
return self.user_profile_icon.get_attribute("src")
|
||||
|
||||
@property
|
||||
def developer_role(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_developer_role_locator)
|
||||
)
|
||||
return self.find_element(*self._user_developer_role_locator)
|
||||
|
||||
@property
|
||||
def developer_role_icon(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_developer_role_icon_locator)
|
||||
)
|
||||
return self.find_element(*self._user_developer_role_icon_locator)
|
||||
|
||||
@property
|
||||
def artist_role(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_artist_role_locator)
|
||||
)
|
||||
return self.find_element(*self._user_artist_role_locator)
|
||||
|
||||
@property
|
||||
def artist_role_icon(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_artist_role_icon_locator)
|
||||
)
|
||||
return self.find_element(*self._user_artist_role_icon_locator)
|
||||
|
||||
@property
|
||||
def user_homepage(self):
|
||||
return self.find_element(*self._user_homepage_locator).get_attribute('href')
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_homepage_locator)
|
||||
)
|
||||
return self.find_element(*self._user_homepage_locator).get_attribute("href")
|
||||
|
||||
@property
|
||||
def user_location(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_location_locator)
|
||||
)
|
||||
return self.find_element(*self._user_location_locator).text
|
||||
|
||||
@property
|
||||
def user_occupation(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_occupation_locator)
|
||||
)
|
||||
return self.find_element(*self._user_occupation_locator).text
|
||||
|
||||
@property
|
||||
def user_profile_creation_date(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_creation_date_locator)
|
||||
)
|
||||
return self.find_element(*self._user_creation_date_locator)
|
||||
|
||||
@property
|
||||
def user_addons_number(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_addons_count_locator)
|
||||
)
|
||||
return self.find_element(*self._user_addons_count_locator)
|
||||
|
||||
@property
|
||||
def user_addons_average_rating(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._user_addon_average_rating_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._user_addon_average_rating_locator)
|
||||
|
||||
@property
|
||||
|
@ -197,23 +230,40 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def edit_profile_button(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_profile_edit_link_locator)
|
||||
)
|
||||
return self.find_element(*self._user_profile_edit_link_locator)
|
||||
|
||||
def click_edit_profile_button(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._user_profile_edit_link_locator)
|
||||
)
|
||||
self.find_element(*self._user_profile_edit_link_locator).click()
|
||||
return User(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
||||
@property
|
||||
def user_extensions(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_extensions_card_locator)
|
||||
)
|
||||
self.find_element(*self._user_extensions_card_locator)
|
||||
return Search(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
||||
@property
|
||||
def user_extensions_card_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._user_extensions_card_header_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._user_extensions_card_header_locator).text
|
||||
|
||||
@property
|
||||
def user_extensions_results(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_extensions_results_locator)
|
||||
)
|
||||
items = self.find_elements(*self._user_extensions_results_locator)
|
||||
return [
|
||||
Search(
|
||||
|
@ -224,15 +274,24 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def user_themes(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_themes_card_locator)
|
||||
)
|
||||
self.find_element(*self._user_themes_card_locator)
|
||||
return Search(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
||||
@property
|
||||
def user_themes_card_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_themes_card_header_locator)
|
||||
)
|
||||
return self.find_element(*self._user_themes_card_header_locator).text
|
||||
|
||||
@property
|
||||
def user_themes_results(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_themes_results_locator)
|
||||
)
|
||||
items = self.find_elements(*self._user_themes_results_locator)
|
||||
return [
|
||||
Search(
|
||||
|
@ -243,14 +302,23 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def extensions_pagination(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._extensions_pagination_locator)
|
||||
)
|
||||
return self.find_element(*self._extensions_pagination_locator)
|
||||
|
||||
def extensions_next_page(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._extensions_next_page_locator)
|
||||
)
|
||||
self.find_element(*self._extensions_next_page_locator).click()
|
||||
return User(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
||||
@property
|
||||
def extensions_page_number(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._extensions_page_number_locator)
|
||||
)
|
||||
return self.find_element(*self._extensions_page_number_locator).text
|
||||
|
||||
@property
|
||||
|
@ -258,11 +326,15 @@ class User(Base):
|
|||
return self.find_element(*self._themes_pagination_locator)
|
||||
|
||||
def themes_next_page(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._themes_next_page_locator))
|
||||
self.find_element(*self._themes_next_page_locator).click()
|
||||
return User(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
||||
@property
|
||||
def themes_page_number(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._themes_page_number_locator)
|
||||
)
|
||||
return self.find_element(*self._themes_page_number_locator).text
|
||||
|
||||
def user_reviews_section_loaded(self):
|
||||
|
@ -277,6 +349,9 @@ class User(Base):
|
|||
return [reviews.UserReview(self, el) for el in items]
|
||||
|
||||
def click_user_abuse_report(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._user_abuse_report_button_locator)
|
||||
)
|
||||
self.find_element(*self._user_abuse_report_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._abuse_report_textarea_locator)
|
||||
|
@ -284,20 +359,37 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def abuse_report_form_header(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._abuse_report_form_header_locator)
|
||||
)
|
||||
return self.find_element(*self._abuse_report_form_header_locator).text
|
||||
|
||||
@property
|
||||
def abuse_report_form_help_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._abuse_report_form_help_text)
|
||||
)
|
||||
return self.find_element(*self._abuse_report_form_help_text).text
|
||||
|
||||
@property
|
||||
def abuse_report_form_additional_help_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._abuse_report_form_additional_help_text
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._abuse_report_form_additional_help_text).text
|
||||
|
||||
def user_abuse_report_input_text(self, value):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._abuse_report_textarea_locator)
|
||||
)
|
||||
self.find_element(*self._abuse_report_textarea_locator).send_keys(value)
|
||||
|
||||
def cancel_abuse_report_form(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._abuse_report_cancel_button_locator)
|
||||
)
|
||||
self.find_element(*self._abuse_report_cancel_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._user_abuse_report_button_locator)
|
||||
|
@ -305,9 +397,17 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def abuse_report_submit_disabled(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._abuse_report_submit_disabled_button_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._abuse_report_submit_disabled_button_locator)
|
||||
|
||||
def submit_user_abuse_report(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._abuse_report_submit_button_locator)
|
||||
)
|
||||
self.find_element(*self._abuse_report_submit_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located(
|
||||
|
@ -317,90 +417,95 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def user_abuse_confirmation_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._abuse_report_confirm_message_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._abuse_report_confirm_message_locator).text
|
||||
|
||||
class EditProfile(Region):
|
||||
_view_profile_link_locator = (By.CSS_SELECTOR, '.UserProfileEdit-user-links a')
|
||||
_user_email_locator = (By.CSS_SELECTOR, '.UserProfileEdit-email')
|
||||
_view_profile_link_locator = (By.CSS_SELECTOR, ".UserProfileEdit-user-links a")
|
||||
_user_email_locator = (By.CSS_SELECTOR, ".UserProfileEdit-email")
|
||||
_user_email_help_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-email--help',
|
||||
".UserProfileEdit-email--help",
|
||||
)
|
||||
_user_email_help_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-email--help a',
|
||||
".UserProfileEdit-email--help a",
|
||||
)
|
||||
_fxa_account_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-manage-account-link',
|
||||
".UserProfileEdit-manage-account-link",
|
||||
)
|
||||
_edit_display_name_locator = (By.CSS_SELECTOR, '.UserProfileEdit-displayName')
|
||||
_edit_homepage_locator = (By.CSS_SELECTOR, '.UserProfileEdit-homepage')
|
||||
_edit_location_locator = (By.CSS_SELECTOR, '.UserProfileEdit-location')
|
||||
_edit_occupation_locator = (By.CSS_SELECTOR, '.UserProfileEdit-occupation')
|
||||
_profile_picture_placeholder_locator = (By.CSS_SELECTOR, '.Icon-anonymous-user')
|
||||
_edit_display_name_locator = (By.CSS_SELECTOR, ".UserProfileEdit-displayName")
|
||||
_edit_homepage_locator = (By.CSS_SELECTOR, ".UserProfileEdit-homepage")
|
||||
_edit_location_locator = (By.CSS_SELECTOR, ".UserProfileEdit-location")
|
||||
_edit_occupation_locator = (By.CSS_SELECTOR, ".UserProfileEdit-occupation")
|
||||
_profile_picture_placeholder_locator = (By.CSS_SELECTOR, ".Icon-anonymous-user")
|
||||
_upload_picture_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEditPicture-file-input',
|
||||
".UserProfileEditPicture-file-input",
|
||||
)
|
||||
_uploaded_profile_picture_locator = (By.CSS_SELECTOR, '.UserAvatar-image')
|
||||
_uploaded_profile_picture_locator = (By.CSS_SELECTOR, ".UserAvatar-image")
|
||||
_delete_picture_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEditPicture-delete-button button',
|
||||
".UserProfileEditPicture-delete-button button",
|
||||
)
|
||||
_cancel_delete_picture_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ConfirmationDialog-cancel-button',
|
||||
".ConfirmationDialog-cancel-button",
|
||||
)
|
||||
_confirm_delete_picture_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.ConfirmationDialog-confirm-button',
|
||||
".ConfirmationDialog-confirm-button",
|
||||
)
|
||||
_picture_delete_success_text_locator = (By.CSS_SELECTOR, '.Notice-success p')
|
||||
_edit_biography_locator = (By.CSS_SELECTOR, '.UserProfileEdit-biography')
|
||||
_picture_delete_success_text_locator = (By.CSS_SELECTOR, ".Notice-success p")
|
||||
_edit_biography_locator = (By.CSS_SELECTOR, ".UserProfileEdit-biography")
|
||||
_notifications_info_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-notifications-aside',
|
||||
".UserProfileEdit-notifications-aside",
|
||||
)
|
||||
_notification_checkbox_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEditNotification-input',
|
||||
".UserProfileEditNotification-input",
|
||||
)
|
||||
_notification_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEditNotification label',
|
||||
".UserProfileEditNotification label",
|
||||
)
|
||||
_notifications_help_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-notifications--help',
|
||||
".UserProfileEdit-notifications--help",
|
||||
)
|
||||
_edit_profile_submit_disabled_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-submit-button.Button--disabled',
|
||||
".UserProfileEdit-submit-button.Button--disabled",
|
||||
)
|
||||
_edit_profile_submit_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-submit-button',
|
||||
".UserProfileEdit-submit-button",
|
||||
)
|
||||
_delete_profile_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-delete-button',
|
||||
".UserProfileEdit-delete-button",
|
||||
)
|
||||
_delete_profile_overlay_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-deletion-modal',
|
||||
".UserProfileEdit-deletion-modal",
|
||||
)
|
||||
_cancel_delete_profile_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-deletion-modal .UserProfileEdit-cancel-button',
|
||||
".UserProfileEdit-deletion-modal .UserProfileEdit-cancel-button",
|
||||
)
|
||||
_confirm_delete_profile_button_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.UserProfileEdit-deletion-modal .UserProfileEdit-confirm-button',
|
||||
".UserProfileEdit-deletion-modal .UserProfileEdit-confirm-button",
|
||||
)
|
||||
_invalid_url_error_text_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Notice-error .Notice-text',
|
||||
".Notice-error .Notice-text",
|
||||
)
|
||||
|
||||
def click_view_profile_link(self):
|
||||
|
@ -409,62 +514,99 @@ class User(Base):
|
|||
).until(EC.element_to_be_clickable(self._view_profile_link_locator))
|
||||
link.click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located((By.CSS_SELECTOR, '.UserProfile-name'))
|
||||
EC.visibility_of_element_located((By.CSS_SELECTOR, ".UserProfile-name"))
|
||||
)
|
||||
|
||||
@property
|
||||
def email_field(self):
|
||||
return self.find_element(*self._user_email_locator).get_attribute('value')
|
||||
self.wait.until(EC.visibility_of_element_located(self._user_email_locator))
|
||||
return self.find_element(*self._user_email_locator).get_attribute("value")
|
||||
|
||||
@property
|
||||
def email_field_help_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._user_email_help_text_locator)
|
||||
)
|
||||
return self.find_element(*self._user_email_help_text_locator).text
|
||||
|
||||
def email_field_help_link(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._user_email_help_link_locator)
|
||||
)
|
||||
self.find_element(*self._user_email_help_link_locator).click()
|
||||
# waits for the fxa support page to be opened
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
(By.CSS_SELECTOR, '.sumo-page-heading')
|
||||
(By.CSS_SELECTOR, ".sumo-page-heading")
|
||||
)
|
||||
)
|
||||
|
||||
def link_to_fxa_account(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._fxa_account_link_locator))
|
||||
self.find_element(*self._fxa_account_link_locator).click()
|
||||
# waits for the fxa account page to be opened - check logo visibility
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located((By.CSS_SELECTOR, '.flex h1 span'))
|
||||
EC.visibility_of_element_located((By.CSS_SELECTOR, ".flex h1 span"))
|
||||
)
|
||||
|
||||
def display_name(self, value):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._edit_display_name_locator)
|
||||
)
|
||||
self.find_element(*self._edit_display_name_locator).send_keys(value)
|
||||
|
||||
@property
|
||||
def display_name_field(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._edit_display_name_locator)
|
||||
)
|
||||
return self.find_element(*self._edit_display_name_locator)
|
||||
|
||||
def homepage_link(self, value):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._edit_homepage_locator)
|
||||
)
|
||||
self.find_element(*self._edit_homepage_locator).send_keys(value)
|
||||
|
||||
@property
|
||||
def homepage_link_field(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._edit_homepage_locator)
|
||||
)
|
||||
return self.find_element(*self._edit_homepage_locator)
|
||||
|
||||
def location(self, value):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._edit_location_locator)
|
||||
)
|
||||
self.find_element(*self._edit_location_locator).send_keys(value)
|
||||
|
||||
@property
|
||||
def location_field(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._edit_location_locator)
|
||||
)
|
||||
return self.find_element(*self._edit_location_locator)
|
||||
|
||||
def occupation(self, value):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._edit_occupation_locator)
|
||||
)
|
||||
self.find_element(*self._edit_occupation_locator).send_keys(value)
|
||||
|
||||
@property
|
||||
def profile_avatar_placeholder(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._profile_picture_placeholder_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._profile_picture_placeholder_locator)
|
||||
|
||||
def upload_picture(self, image):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._upload_picture_button_locator)
|
||||
)
|
||||
button = self.find_element(*self._upload_picture_button_locator)
|
||||
path = Path(os.getcwd())
|
||||
img = str(path / "img" / image)
|
||||
|
@ -480,9 +622,12 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def picture_source(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._uploaded_profile_picture_locator)
|
||||
)
|
||||
return self.find_element(
|
||||
*self._uploaded_profile_picture_locator
|
||||
).get_attribute('src')
|
||||
).get_attribute("src")
|
||||
|
||||
def delete_profile_picture(self):
|
||||
self.find_element(*self._delete_picture_button_locator).click()
|
||||
|
@ -491,12 +636,18 @@ class User(Base):
|
|||
)
|
||||
|
||||
def cancel_delete_picture(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._cancel_delete_picture_locator)
|
||||
)
|
||||
self.find_element(*self._cancel_delete_picture_locator).click()
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._delete_picture_button_locator)
|
||||
)
|
||||
|
||||
def confirm_delete_picture(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._confirm_delete_picture_locator)
|
||||
)
|
||||
self.find_element(*self._confirm_delete_picture_locator).click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
|
@ -506,13 +657,24 @@ class User(Base):
|
|||
|
||||
@property
|
||||
def picture_delete_success_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._picture_delete_success_text_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._picture_delete_success_text_locator).text
|
||||
|
||||
def biography(self, value):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._edit_biography_locator)
|
||||
)
|
||||
self.find_element(*self._edit_biography_locator).send_keys(value)
|
||||
|
||||
@property
|
||||
def notifications_info_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._notifications_info_text_locator)
|
||||
)
|
||||
return self.find_element(*self._notifications_info_text_locator).text
|
||||
|
||||
@property
|
||||
|
@ -520,22 +682,28 @@ class User(Base):
|
|||
"""function used for developer notifications"""
|
||||
self.wait.until(
|
||||
lambda _: len(self.notification_text) == 8,
|
||||
message=f'There were {len(self.notification_text)} notifications displayed, expected 8',
|
||||
message=f"There were {len(self.notification_text)} notifications displayed, expected 8",
|
||||
)
|
||||
return self.find_elements(*self._notification_checkbox_locator)
|
||||
|
||||
@property
|
||||
def notification_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._notification_text_locator)
|
||||
)
|
||||
items = self.find_elements(*self._notification_text_locator)
|
||||
# the notifications endpoint takes a bit longer to respond, so a wait is helpful here
|
||||
self.wait.until(
|
||||
lambda _: len(items) > 0,
|
||||
message=f'Expected notifications list to be loaded but the list contains {len(items)} items',
|
||||
message=f"Expected notifications list to be loaded but the list contains {len(items)} items",
|
||||
)
|
||||
return items
|
||||
|
||||
@property
|
||||
def notifications_help_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._notifications_help_text_locator)
|
||||
)
|
||||
return self.find_element(*self._notifications_help_text_locator).text
|
||||
|
||||
@property
|
||||
|
@ -544,22 +712,34 @@ class User(Base):
|
|||
|
||||
def update_profile(self):
|
||||
"""Updates a user profile and expects to remain on the Edit Profile page (likely due to an error)"""
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._edit_profile_submit_button_locator)
|
||||
)
|
||||
self.find_element(*self._edit_profile_submit_button_locator).click()
|
||||
|
||||
def submit_changes(self):
|
||||
"""Updates a user profile and expects to navigate to the View Profile page"""
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._edit_profile_submit_button_locator)
|
||||
)
|
||||
self.find_element(*self._edit_profile_submit_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located((By.CLASS_NAME, 'UserProfile-name'))
|
||||
EC.visibility_of_element_located((By.CLASS_NAME, "UserProfile-name"))
|
||||
)
|
||||
|
||||
def delete_account(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._delete_profile_button_locator)
|
||||
)
|
||||
self.find_element(*self._delete_profile_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._delete_profile_overlay_locator)
|
||||
)
|
||||
|
||||
def cancel_delete_account(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._cancel_delete_profile_button_locator)
|
||||
)
|
||||
self.find_element(*self._cancel_delete_profile_button_locator).click()
|
||||
self.wait.until(
|
||||
EC.invisibility_of_element_located(
|
||||
|
@ -568,9 +748,15 @@ class User(Base):
|
|||
)
|
||||
|
||||
def confirm_delete_account(self):
|
||||
self.wait.until(
|
||||
EC.element_to_be_clickable(self._confirm_delete_profile_button_locator)
|
||||
)
|
||||
self.find_element(*self._confirm_delete_profile_button_locator).click()
|
||||
return Home(self.driver, self.page.base_url).wait_for_page_to_load()
|
||||
|
||||
@property
|
||||
def invalid_url_error_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._invalid_url_error_text_locator)
|
||||
)
|
||||
return self.find_element(*self._invalid_url_error_text_locator).text
|
||||
|
|
|
@ -2,45 +2,49 @@ from pypom import Region
|
|||
from selenium.common.exceptions import NoSuchElementException
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as expected
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
from pages.desktop.base import Base
|
||||
from regions.desktop.rating_stats_card import RatingStats
|
||||
|
||||
|
||||
class Versions(Base):
|
||||
_versions_page_header_locator = (By.CSS_SELECTOR, '.AddonVersions-versions header')
|
||||
_latest_version_locator = (By.CSS_SELECTOR, '.Card-contents li:nth-child(2) h2')
|
||||
_versions_list_locator = (By.CSS_SELECTOR, '.AddonVersionCard')
|
||||
_versions_page_header_locator = (By.CSS_SELECTOR, ".AddonVersions-versions header")
|
||||
_latest_version_locator = (By.CSS_SELECTOR, ".Card-contents li:nth-child(2) h2")
|
||||
_versions_list_locator = (By.CSS_SELECTOR, ".AddonVersionCard")
|
||||
_notice_message_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.Card-contents .Notice-warning .Notice-text',
|
||||
".Card-contents .Notice-warning .Notice-text",
|
||||
)
|
||||
_notice_message_text_locator = (By.CSS_SELECTOR, '.AddonVersions-warning-text')
|
||||
_rating_card_locator = (By.CSS_SELECTOR, '.AddonSummaryCard')
|
||||
_notice_message_text_locator = (By.CSS_SELECTOR, ".AddonVersions-warning-text")
|
||||
_rating_card_locator = (By.CSS_SELECTOR, ".AddonSummaryCard")
|
||||
|
||||
def wait_for_page_to_load(self):
|
||||
"""Waits for various page components to be loaded"""
|
||||
self.wait.until(
|
||||
expected.invisibility_of_element_located((By.CLASS_NAME, 'LoadingText'))
|
||||
expected.invisibility_of_element_located((By.CLASS_NAME, "LoadingText"))
|
||||
)
|
||||
return self
|
||||
|
||||
@property
|
||||
def versions_page_header(self):
|
||||
self.wait_for_element_to_be_displayed(self._versions_page_header_locator)
|
||||
return self.find_element(*self._versions_page_header_locator)
|
||||
|
||||
@property
|
||||
def latest_version_number(self):
|
||||
self.wait_for_element_to_be_displayed(self._latest_version_locator)
|
||||
el = self.find_element(*self._latest_version_locator).text
|
||||
return el.split()[1].replace('Version ', '')
|
||||
return el.split()[1].replace("Version ", "")
|
||||
|
||||
@property
|
||||
def notice_message(self):
|
||||
self.wait_for_element_to_be_displayed(self._notice_message_locator)
|
||||
return self.find_element(*self._notice_message_locator)
|
||||
|
||||
@property
|
||||
def rating_card(self):
|
||||
el = self.find_element(By.CLASS_NAME, 'AddonSummaryCard')
|
||||
el = self.find_element(By.CLASS_NAME, "AddonSummaryCard")
|
||||
return RatingStats(self, el)
|
||||
|
||||
@property
|
||||
|
@ -49,41 +53,53 @@ class Versions(Base):
|
|||
return [self.VersionCard(self, el) for el in items]
|
||||
|
||||
class VersionCard(Region):
|
||||
_version_number_locator = (By.CSS_SELECTOR, '.AddonVersionCard-version')
|
||||
_released_date_locator = (By.CSS_SELECTOR, '.AddonVersionCard-fileInfo')
|
||||
_version_number_locator = (By.CSS_SELECTOR, ".AddonVersionCard-version")
|
||||
_released_date_locator = (By.CSS_SELECTOR, ".AddonVersionCard-fileInfo")
|
||||
_version_release_notes_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.AddonVersionCard-releaseNotes',
|
||||
".AddonVersionCard-releaseNotes",
|
||||
)
|
||||
_license_link_locator = (By.CSS_SELECTOR, '.AddonVersionCard-license > a')
|
||||
_license_text_locator = (By.CSS_SELECTOR, '.AddonVersionCard-license')
|
||||
_warning_message_locator = (By.CSS_SELECTOR, '.Notice-text')
|
||||
_warning_learn_more_button_locator = (By.CSS_SELECTOR, '.Notice-button')
|
||||
_add_to_firefox_button_locator = (By.CSS_SELECTOR, '.AMInstallButton-button')
|
||||
_license_link_locator = (By.CSS_SELECTOR, ".AddonVersionCard-license > a")
|
||||
_license_text_locator = (By.CSS_SELECTOR, ".AddonVersionCard-license")
|
||||
_warning_message_locator = (By.CSS_SELECTOR, ".Notice-text")
|
||||
_warning_learn_more_button_locator = (By.CSS_SELECTOR, ".Notice-button")
|
||||
_add_to_firefox_button_locator = (By.CSS_SELECTOR, ".AMInstallButton-button")
|
||||
_download_link_locator = (
|
||||
By.CSS_SELECTOR,
|
||||
'.InstallButtonWrapper-download-link',
|
||||
".InstallButtonWrapper-download-link",
|
||||
)
|
||||
|
||||
@property
|
||||
def version_number(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._version_number_locator)
|
||||
)
|
||||
return self.find_element(*self._version_number_locator).text.split()[1]
|
||||
|
||||
@property
|
||||
def released_date(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._released_date_locator)
|
||||
)
|
||||
text = self.find_element(*self._released_date_locator).text
|
||||
text = text.split('Released ')[1]
|
||||
text = text.split('-')[0][:-1]
|
||||
text = text.split("Released ")[1]
|
||||
text = text.split("-")[0][:-1]
|
||||
return text
|
||||
|
||||
@property
|
||||
def version_size(self): # memory size, ex: 7.35 KB
|
||||
return self.find_element(*self._released_date_locator).text.split('-')[1][
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._released_date_locator)
|
||||
)
|
||||
return self.find_element(*self._released_date_locator).text.split("-")[1][
|
||||
1:
|
||||
]
|
||||
|
||||
@property
|
||||
def version_release_notes(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._version_release_notes_locator)
|
||||
)
|
||||
return self.find_element(*self._version_release_notes_locator)
|
||||
|
||||
@property
|
||||
|
@ -97,22 +113,37 @@ class Versions(Base):
|
|||
|
||||
@property
|
||||
def license_text(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._license_text_locator)
|
||||
)
|
||||
text = self.find_element(*self._license_text_locator).text
|
||||
if self.license_link:
|
||||
text += self.license_link.get_attribute('href')
|
||||
text += self.license_link.get_attribute("href")
|
||||
return text
|
||||
|
||||
@property
|
||||
def warning_message(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._warning_message_locator)
|
||||
)
|
||||
return self.find_element(*self._warning_message_locator)
|
||||
|
||||
@property
|
||||
def warning_learn_more_button(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(
|
||||
self._warning_learn_more_button_locator
|
||||
)
|
||||
)
|
||||
return self.find_element(*self._warning_learn_more_button_locator)
|
||||
|
||||
@property
|
||||
def add_to_firefox_button(self):
|
||||
self.wait.until(
|
||||
EC.visibility_of_element_located(self._add_to_firefox_button_locator)
|
||||
)
|
||||
return self.find_element(*self._add_to_firefox_button_locator)
|
||||
|
||||
def click_download_link(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._download_link_locator))
|
||||
self.find_element(*self._download_link_locator).click()
|
||||
|
|
|
@ -1 +1 @@
|
|||
i2zodbyg6ucjgrmr5mimdsavz43g5jom
|
||||
g13jfwts2dnkrlu5foykzonz30fb4458
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1,6 +1,7 @@
|
|||
from pypom import Region
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
|
||||
class Categories(Region):
|
||||
|
@ -15,10 +16,12 @@ class Categories(Region):
|
|||
|
||||
@property
|
||||
def categories_list_header(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._categories_card_header_locator))
|
||||
return self.find_element(*self._categories_card_header_locator)
|
||||
|
||||
@property
|
||||
def category_list(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._categories_locator))
|
||||
items = self.find_elements(*self._categories_locator)
|
||||
return [self.CategoryList(self, el) for el in items]
|
||||
|
||||
|
@ -27,6 +30,7 @@ class Categories(Region):
|
|||
|
||||
@property
|
||||
def category_button_name(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._name_locator))
|
||||
return self.find_element(*self._name_locator).text
|
||||
|
||||
def click(self):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
from pypom import Region
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
|
||||
class RatingStats(Region):
|
||||
|
@ -20,9 +21,11 @@ class RatingStats(Region):
|
|||
|
||||
@property
|
||||
def addon_title(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_title_locator))
|
||||
return self.find_element(*self._addon_title_locator)
|
||||
|
||||
def click_addon_title(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._addon_title_locator))
|
||||
self.find_element(*self._addon_title_locator).click()
|
||||
from pages.desktop.frontend.details import Detail
|
||||
|
||||
|
@ -30,9 +33,11 @@ class RatingStats(Region):
|
|||
|
||||
@property
|
||||
def addon_image(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_image_locator))
|
||||
return self.find_element(*self._addon_image_locator)
|
||||
|
||||
def click_addon_image(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._addon_image_locator))
|
||||
self.find_element(*self._addon_image_locator).click()
|
||||
from pages.desktop.frontend.details import Detail
|
||||
|
||||
|
@ -43,6 +48,7 @@ class RatingStats(Region):
|
|||
return [i.text for i in self.find_elements(*self._addon_author_locator)]
|
||||
|
||||
def click_author_name(self, index=0):
|
||||
self.wait.until(EC.element_to_be_clickable(self._addon_author_locator))
|
||||
self.find_elements(*self._addon_author_locator)[index].click()
|
||||
from pages.desktop.frontend.users import User
|
||||
|
||||
|
@ -50,28 +56,34 @@ class RatingStats(Region):
|
|||
|
||||
@property
|
||||
def rating_stars(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._rating_stars_locator))
|
||||
return self.find_elements(*self._rating_stars_locator)
|
||||
|
||||
@property
|
||||
def rating(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_rating_locator))
|
||||
rating = self.find_element(*self._addon_rating_locator).text.split()[0]
|
||||
return float(rating)
|
||||
|
||||
@property
|
||||
def rating_bars(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._rating_bars_locator))
|
||||
return self.find_elements(*self._rating_bars_locator)
|
||||
|
||||
def click_see_all_reviews_with_specific_stars(self, count):
|
||||
self.wait.until(EC.element_to_be_clickable(self._rating_by_star_locator))
|
||||
self.find_elements(*self._rating_by_star_locator)[count].click()
|
||||
from pages.desktop.frontend.reviews import Reviews
|
||||
|
||||
return Reviews(self.driver, self.page)
|
||||
|
||||
def number_of_reviews_with_specific_stars(self, count):
|
||||
self.wait.until(EC.visibility_of_element_located(self._number_of_reviews_locator))
|
||||
return int(self.find_elements(*self._number_of_reviews_locator)[count].text)
|
||||
|
||||
@property
|
||||
def number_of_filled_stars(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._filled_stars_locator))
|
||||
return len(self.find_elements(*self._filled_stars_locator))
|
||||
|
||||
@property
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
from pypom import Region
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support import expected_conditions as EC
|
||||
|
||||
|
||||
class Shelves(Region):
|
||||
|
@ -11,16 +12,19 @@ class Shelves(Region):
|
|||
|
||||
@property
|
||||
def recommended_addons(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._recommended_addons_locator))
|
||||
el = self.find_element(*self._recommended_addons_locator)
|
||||
return self.ShelfList(self, el)
|
||||
|
||||
@property
|
||||
def top_rated_addons(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._top_rated_locator))
|
||||
el = self.find_element(*self._top_rated_locator)
|
||||
return self.ShelfList(self, el)
|
||||
|
||||
@property
|
||||
def trending_addons(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._trending_addons_locator))
|
||||
el = self.find_element(*self._trending_addons_locator)
|
||||
return self.ShelfList(self, el)
|
||||
|
||||
|
@ -31,14 +35,17 @@ class Shelves(Region):
|
|||
|
||||
@property
|
||||
def list(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_item_locator))
|
||||
items = self.find_elements(*self._addon_item_locator)
|
||||
return [self.ShelfDetail(self.page, el) for el in items]
|
||||
|
||||
@property
|
||||
def card_header(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._promo_card_header_locator))
|
||||
return self.find_element(*self._promo_card_header_locator).text
|
||||
|
||||
def browse_all(self):
|
||||
self.wait.until(EC.element_to_be_clickable(self._browse_all_locator))
|
||||
self.find_element(*self._browse_all_locator).click()
|
||||
from pages.desktop.frontend.search import Search
|
||||
|
||||
|
@ -52,12 +59,15 @@ class Shelves(Region):
|
|||
|
||||
@property
|
||||
def name(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_name_locator))
|
||||
return self.find_element(*self._addon_name_locator).text
|
||||
|
||||
@property
|
||||
def addon_icon_preview(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_icon_locator))
|
||||
return self.find_element(*self._addon_icon_locator)
|
||||
|
||||
@property
|
||||
def addon_users_preview(self):
|
||||
self.wait.until(EC.visibility_of_element_located(self._addon_users_locator))
|
||||
return self.find_element(*self._addon_users_locator)
|
||||
|
|
|
@ -1 +1 @@
|
|||
iqv9l8hycnspa1g8f1apo7g361rox60g
|
||||
vqxln2g4ouuxhftphcty1cyajq5x9zzx
|
Двоичные данные
sample-addons/make-addon.zip
Двоичные данные
sample-addons/make-addon.zip
Двоичный файл не отображается.
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -1 +1 @@
|
|||
zw1vzjh06mbms2i13tcr0iu0yov38qkp
|
||||
j42ho2lqc0i8mei9uoyz8i0fk1s97mic
|
|
@ -726,6 +726,7 @@ def test_edit_version_valid_compatibility_values(
|
|||
url=f"{base_url}{_addon_create}{addon}",
|
||||
headers={"Authorization": f"Session {session_auth}"},
|
||||
)
|
||||
print(request)
|
||||
# get the version id of the version we want to edit
|
||||
version = request.json()["current_version"]["id"]
|
||||
payload = {**payloads.edit_version_details, "compatibility": request_value}
|
||||
|
|
|
@ -266,6 +266,7 @@ def test_submit_listed_addon(selenium, base_url, variables, wait):
|
|||
edit_listing = confirmation_page.click_edit_listing_button()
|
||||
assert addon_name in edit_listing.name
|
||||
|
||||
|
||||
@pytest.mark.sanity
|
||||
@pytest.mark.serial
|
||||
@pytest.mark.create_session("submissions_user")
|
||||
|
@ -311,13 +312,14 @@ def test_submit_addon_3mb_size(selenium, base_url, wait, variables):
|
|||
# submit the add-on details
|
||||
confirmation_page = details_form.submit_addon()
|
||||
assert (
|
||||
variables["listed_submission_confirmation"]
|
||||
in confirmation_page.submission_confirmation_messages[0].text
|
||||
variables["listed_submission_confirmation"]
|
||||
in confirmation_page.submission_confirmation_messages[0].text
|
||||
)
|
||||
# go to the addon edit listing page and check that it was created
|
||||
edit_listing = confirmation_page.click_edit_listing_button()
|
||||
assert addon_name in edit_listing.name
|
||||
|
||||
|
||||
@pytest.mark.serial
|
||||
@pytest.mark.create_session("submissions_user")
|
||||
def test_addon_last_modified_date(selenium, base_url):
|
||||
|
|
|
@ -384,6 +384,7 @@ def test_flag_review_menu_options(selenium, base_url, variables):
|
|||
|
||||
@pytest.mark.serial
|
||||
@pytest.mark.nondestructive
|
||||
@pytest.mark.create_session("rating_user")
|
||||
def test_click_on_review_posting_time_link(selenium, base_url, variables):
|
||||
# this test checks that if we go to all reviews page and clik on a review's posting time link (ex: 2 months ago)
|
||||
# it displays the review in a different section from the others
|
||||
|
|
|
@ -0,0 +1,782 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Test Report</title>
|
||||
<style>body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
/* do not increase min-width as some may use split screens */
|
||||
min-width: 800px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 24px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
p {
|
||||
color: black;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* SUMMARY INFORMATION
|
||||
******************************/
|
||||
#environment td {
|
||||
padding: 5px;
|
||||
border: 1px solid #E6E6E6;
|
||||
}
|
||||
#environment tr:nth-child(odd) {
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* TEST RESULT COLORS
|
||||
******************************/
|
||||
span.passed,
|
||||
.passed .col-result {
|
||||
color: green;
|
||||
}
|
||||
|
||||
span.skipped,
|
||||
span.xfailed,
|
||||
span.rerun,
|
||||
.skipped .col-result,
|
||||
.xfailed .col-result,
|
||||
.rerun .col-result {
|
||||
color: orange;
|
||||
}
|
||||
|
||||
span.error,
|
||||
span.failed,
|
||||
span.xpassed,
|
||||
.error .col-result,
|
||||
.failed .col-result,
|
||||
.xpassed .col-result {
|
||||
color: red;
|
||||
}
|
||||
|
||||
/******************************
|
||||
* RESULTS TABLE
|
||||
*
|
||||
* 1. Table Layout
|
||||
* 2. Extra
|
||||
* 3. Sorting items
|
||||
*
|
||||
******************************/
|
||||
/*------------------
|
||||
* 1. Table Layout
|
||||
*------------------*/
|
||||
#results-table {
|
||||
border: 1px solid #e6e6e6;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
}
|
||||
#results-table th,
|
||||
#results-table td {
|
||||
padding: 5px;
|
||||
border: 1px solid #E6E6E6;
|
||||
text-align: left;
|
||||
}
|
||||
#results-table th {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*------------------
|
||||
* 2. Extra
|
||||
*------------------*/
|
||||
.log {
|
||||
background-color: #e6e6e6;
|
||||
border: 1px solid #e6e6e6;
|
||||
color: black;
|
||||
display: block;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
height: 230px;
|
||||
overflow-y: scroll;
|
||||
padding: 5px;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.log:only-child {
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
div.image {
|
||||
border: 1px solid #e6e6e6;
|
||||
float: right;
|
||||
height: 240px;
|
||||
margin-left: 5px;
|
||||
overflow: hidden;
|
||||
width: 320px;
|
||||
}
|
||||
div.image img {
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
div.video {
|
||||
border: 1px solid #e6e6e6;
|
||||
float: right;
|
||||
height: 240px;
|
||||
margin-left: 5px;
|
||||
overflow: hidden;
|
||||
width: 320px;
|
||||
}
|
||||
div.video video {
|
||||
overflow: hidden;
|
||||
width: 320px;
|
||||
height: 240px;
|
||||
}
|
||||
|
||||
.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.expander::after {
|
||||
content: " (show details)";
|
||||
color: #BBB;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.collapser::after {
|
||||
content: " (hide details)";
|
||||
color: #BBB;
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*------------------
|
||||
* 3. Sorting items
|
||||
*------------------*/
|
||||
.sortable {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.sort-icon {
|
||||
font-size: 0px;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
margin-top: 5px;
|
||||
/*triangle*/
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
}
|
||||
.inactive .sort-icon {
|
||||
/*finish triangle*/
|
||||
border-top: 8px solid #E6E6E6;
|
||||
}
|
||||
.asc.active .sort-icon {
|
||||
/*finish triangle*/
|
||||
border-bottom: 8px solid #999;
|
||||
}
|
||||
.desc.active .sort-icon {
|
||||
/*finish triangle*/
|
||||
border-top: 8px solid #999;
|
||||
}
|
||||
</style></head>
|
||||
<body onLoad="init()">
|
||||
<script>/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
|
||||
function toArray(iter) {
|
||||
if (iter === null) {
|
||||
return null;
|
||||
}
|
||||
return Array.prototype.slice.call(iter);
|
||||
}
|
||||
|
||||
function find(selector, elem) { // eslint-disable-line no-redeclare
|
||||
if (!elem) {
|
||||
elem = document;
|
||||
}
|
||||
return elem.querySelector(selector);
|
||||
}
|
||||
|
||||
function findAll(selector, elem) {
|
||||
if (!elem) {
|
||||
elem = document;
|
||||
}
|
||||
return toArray(elem.querySelectorAll(selector));
|
||||
}
|
||||
|
||||
function sortColumn(elem) {
|
||||
toggleSortStates(elem);
|
||||
const colIndex = toArray(elem.parentNode.childNodes).indexOf(elem);
|
||||
let key;
|
||||
if (elem.classList.contains('result')) {
|
||||
key = keyResult;
|
||||
} else if (elem.classList.contains('links')) {
|
||||
key = keyLink;
|
||||
} else {
|
||||
key = keyAlpha;
|
||||
}
|
||||
sortTable(elem, key(colIndex));
|
||||
}
|
||||
|
||||
function showAllExtras() { // eslint-disable-line no-unused-vars
|
||||
findAll('.col-result').forEach(showExtras);
|
||||
}
|
||||
|
||||
function hideAllExtras() { // eslint-disable-line no-unused-vars
|
||||
findAll('.col-result').forEach(hideExtras);
|
||||
}
|
||||
|
||||
function showExtras(colresultElem) {
|
||||
const extras = colresultElem.parentNode.nextElementSibling;
|
||||
const expandcollapse = colresultElem.firstElementChild;
|
||||
extras.classList.remove('collapsed');
|
||||
expandcollapse.classList.remove('expander');
|
||||
expandcollapse.classList.add('collapser');
|
||||
}
|
||||
|
||||
function hideExtras(colresultElem) {
|
||||
const extras = colresultElem.parentNode.nextElementSibling;
|
||||
const expandcollapse = colresultElem.firstElementChild;
|
||||
extras.classList.add('collapsed');
|
||||
expandcollapse.classList.remove('collapser');
|
||||
expandcollapse.classList.add('expander');
|
||||
}
|
||||
|
||||
function showFilters() {
|
||||
const filterItems = document.getElementsByClassName('filter');
|
||||
for (let i = 0; i < filterItems.length; i++)
|
||||
filterItems[i].hidden = false;
|
||||
}
|
||||
|
||||
function addCollapse() {
|
||||
// Add links for show/hide all
|
||||
const resulttable = find('table#results-table');
|
||||
const showhideall = document.createElement('p');
|
||||
showhideall.innerHTML = '<a href="javascript:showAllExtras()">Show all details</a> / ' +
|
||||
'<a href="javascript:hideAllExtras()">Hide all details</a>';
|
||||
resulttable.parentElement.insertBefore(showhideall, resulttable);
|
||||
|
||||
// Add show/hide link to each result
|
||||
findAll('.col-result').forEach(function(elem) {
|
||||
const collapsed = getQueryParameter('collapsed') || 'Passed';
|
||||
const extras = elem.parentNode.nextElementSibling;
|
||||
const expandcollapse = document.createElement('span');
|
||||
if (extras.classList.contains('collapsed')) {
|
||||
expandcollapse.classList.add('expander');
|
||||
} else if (collapsed.includes(elem.innerHTML)) {
|
||||
extras.classList.add('collapsed');
|
||||
expandcollapse.classList.add('expander');
|
||||
} else {
|
||||
expandcollapse.classList.add('collapser');
|
||||
}
|
||||
elem.appendChild(expandcollapse);
|
||||
|
||||
elem.addEventListener('click', function(event) {
|
||||
if (event.currentTarget.parentNode.nextElementSibling.classList.contains('collapsed')) {
|
||||
showExtras(event.currentTarget);
|
||||
} else {
|
||||
hideExtras(event.currentTarget);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getQueryParameter(name) {
|
||||
const match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
|
||||
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
|
||||
}
|
||||
|
||||
function init () { // eslint-disable-line no-unused-vars
|
||||
resetSortHeaders();
|
||||
|
||||
addCollapse();
|
||||
|
||||
showFilters();
|
||||
|
||||
sortColumn(find('.initial-sort'));
|
||||
|
||||
findAll('.sortable').forEach(function(elem) {
|
||||
elem.addEventListener('click',
|
||||
function() {
|
||||
sortColumn(elem);
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
function sortTable(clicked, keyFunc) {
|
||||
const rows = findAll('.results-table-row');
|
||||
const reversed = !clicked.classList.contains('asc');
|
||||
const sortedRows = sort(rows, keyFunc, reversed);
|
||||
/* Whole table is removed here because browsers acts much slower
|
||||
* when appending existing elements.
|
||||
*/
|
||||
const thead = document.getElementById('results-table-head');
|
||||
document.getElementById('results-table').remove();
|
||||
const parent = document.createElement('table');
|
||||
parent.id = 'results-table';
|
||||
parent.appendChild(thead);
|
||||
sortedRows.forEach(function(elem) {
|
||||
parent.appendChild(elem);
|
||||
});
|
||||
document.getElementsByTagName('BODY')[0].appendChild(parent);
|
||||
}
|
||||
|
||||
function sort(items, keyFunc, reversed) {
|
||||
const sortArray = items.map(function(item, i) {
|
||||
return [keyFunc(item), i];
|
||||
});
|
||||
|
||||
sortArray.sort(function(a, b) {
|
||||
const keyA = a[0];
|
||||
const keyB = b[0];
|
||||
|
||||
if (keyA == keyB) return 0;
|
||||
|
||||
if (reversed) {
|
||||
return keyA < keyB ? 1 : -1;
|
||||
} else {
|
||||
return keyA > keyB ? 1 : -1;
|
||||
}
|
||||
});
|
||||
|
||||
return sortArray.map(function(item) {
|
||||
const index = item[1];
|
||||
return items[index];
|
||||
});
|
||||
}
|
||||
|
||||
function keyAlpha(colIndex) {
|
||||
return function(elem) {
|
||||
return elem.childNodes[1].childNodes[colIndex].firstChild.data.toLowerCase();
|
||||
};
|
||||
}
|
||||
|
||||
function keyLink(colIndex) {
|
||||
return function(elem) {
|
||||
const dataCell = elem.childNodes[1].childNodes[colIndex].firstChild;
|
||||
return dataCell == null ? '' : dataCell.innerText.toLowerCase();
|
||||
};
|
||||
}
|
||||
|
||||
function keyResult(colIndex) {
|
||||
return function(elem) {
|
||||
const strings = ['Error', 'Failed', 'Rerun', 'XFailed', 'XPassed',
|
||||
'Skipped', 'Passed'];
|
||||
return strings.indexOf(elem.childNodes[1].childNodes[colIndex].firstChild.data);
|
||||
};
|
||||
}
|
||||
|
||||
function resetSortHeaders() {
|
||||
findAll('.sort-icon').forEach(function(elem) {
|
||||
elem.parentNode.removeChild(elem);
|
||||
});
|
||||
findAll('.sortable').forEach(function(elem) {
|
||||
const icon = document.createElement('div');
|
||||
icon.className = 'sort-icon';
|
||||
icon.textContent = 'vvv';
|
||||
elem.insertBefore(icon, elem.firstChild);
|
||||
elem.classList.remove('desc', 'active');
|
||||
elem.classList.add('asc', 'inactive');
|
||||
});
|
||||
}
|
||||
|
||||
function toggleSortStates(elem) {
|
||||
//if active, toggle between asc and desc
|
||||
if (elem.classList.contains('active')) {
|
||||
elem.classList.toggle('asc');
|
||||
elem.classList.toggle('desc');
|
||||
}
|
||||
|
||||
//if inactive, reset all other functions and add ascending active
|
||||
if (elem.classList.contains('inactive')) {
|
||||
resetSortHeaders();
|
||||
elem.classList.remove('inactive');
|
||||
elem.classList.add('active');
|
||||
}
|
||||
}
|
||||
|
||||
function isAllRowsHidden(value) {
|
||||
return value.hidden == false;
|
||||
}
|
||||
|
||||
function filterTable(elem) { // eslint-disable-line no-unused-vars
|
||||
const outcomeAtt = 'data-test-result';
|
||||
const outcome = elem.getAttribute(outcomeAtt);
|
||||
const classOutcome = outcome + ' results-table-row';
|
||||
const outcomeRows = document.getElementsByClassName(classOutcome);
|
||||
|
||||
for(let i = 0; i < outcomeRows.length; i++){
|
||||
outcomeRows[i].hidden = !elem.checked;
|
||||
}
|
||||
|
||||
const rows = findAll('.results-table-row').filter(isAllRowsHidden);
|
||||
const allRowsHidden = rows.length == 0 ? true : false;
|
||||
const notFoundMessage = document.getElementById('not-found-message');
|
||||
notFoundMessage.hidden = !allRowsHidden;
|
||||
}
|
||||
</script>
|
||||
<h1>user-test-results.html</h1>
|
||||
<p>Report generated on 20-Sep-2023 at 15:56:44 by <a href="https://pypi.python.org/pypi/pytest-html">pytest-html</a> v3.1.1</p>
|
||||
<h2>Summary</h2>
|
||||
<p>33 tests ran in 400.54 seconds. </p>
|
||||
<p class="filter" hidden="true">(Un)check the boxes to filter the results.</p><input checked="true" class="filter" data-test-result="passed" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="passed">33 passed</span>, <input checked="true" class="filter" data-test-result="skipped" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="skipped">1 skipped</span>, <input checked="true" class="filter" data-test-result="failed" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="failed">0 failed</span>, <input checked="true" class="filter" data-test-result="error" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="error">0 errors</span>, <input checked="true" class="filter" data-test-result="xfailed" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="xfailed">0 expected failures</span>, <input checked="true" class="filter" data-test-result="xpassed" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="xpassed">0 unexpected passes</span>, <input checked="true" class="filter" data-test-result="rerun" disabled="true" hidden="true" name="filter_checkbox" onChange="filterTable(this)" type="checkbox"/><span class="rerun">0 rerun</span>
|
||||
<h2>Results</h2>
|
||||
<table id="results-table">
|
||||
<thead id="results-table-head">
|
||||
<tr>
|
||||
<th class="sortable result initial-sort" col="result">Result</th>
|
||||
<th class="sortable" col="name">Test</th>
|
||||
<th class="sortable" col="duration">Duration</th>
|
||||
<th class="sortable links" col="links">Links</th></tr>
|
||||
<tr hidden="true" id="not-found-message">
|
||||
<th colspan="4">No results found. Try to check the filters</th></tr></thead>
|
||||
<tbody class="skipped results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Skipped</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_notifications_subscriptions[Desktop]</td>
|
||||
<td class="col-duration">0.00</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log">('/Users/alexandru.schek/addons-release-tests/tests/frontend/test_users.py', 365, 'Skipped: Intermittent issue, see https://github.com/mozilla/addons-server/issues/20965')<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_login[Desktop]</td>
|
||||
<td class="col-duration">11.88</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> ------------------------------Captured stdout call------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_logout[Desktop]</td>
|
||||
<td class="col-duration">20.69</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> ------------------------------Captured stdout call------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_menu_collections_link[Desktop]</td>
|
||||
<td class="col-duration">17.12</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> ------------------------------Captured stdout call------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_menu_view_profile[Desktop]</td>
|
||||
<td class="col-duration">16.19</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> ------------------------------Captured stdout call------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_menu_edit_profile[Desktop]</td>
|
||||
<td class="col-duration">16.15</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> ------------------------------Captured stdout call------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_register_new_account[Desktop]</td>
|
||||
<td class="col-duration">17.33</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_menu_click_user_menu_links[Desktop]</td>
|
||||
<td class="col-duration">64.59</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> -----------------------------Captured stdout setup------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_developer_notifications[Desktop]</td>
|
||||
<td class="col-duration">7.56</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_mandatory_notifications[Desktop]</td>
|
||||
<td class="col-duration">9.78</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_edit_profile[Desktop]</td>
|
||||
<td class="col-duration">15.21</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> -----------------------------Captured stdout setup------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_view_profile[Desktop]</td>
|
||||
<td class="col-duration">5.89</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_change_profile_picture[Desktop]</td>
|
||||
<td class="col-duration">6.73</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_delete_profile_picture[Desktop]</td>
|
||||
<td class="col-duration">7.15</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_update_profile[Desktop]</td>
|
||||
<td class="col-duration">6.40</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_update_url[Desktop]</td>
|
||||
<td class="col-duration">6.41</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_delete_profile[Desktop]</td>
|
||||
<td class="col-duration">6.73</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_account_manage_section[Desktop]</td>
|
||||
<td class="col-duration">19.46</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> -----------------------------Captured stdout setup------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_data_for_deleted_profile[Desktop]</td>
|
||||
<td class="col-duration">5.43</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_regular_has_no_role[Desktop]</td>
|
||||
<td class="col-duration">6.73</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_regular_notifications[Desktop]</td>
|
||||
<td class="col-duration">12.05</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> ------------------------------Captured stdout call------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_developer_role[Desktop]</td>
|
||||
<td class="col-duration">4.44</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_theme_artist_role[Desktop]</td>
|
||||
<td class="col-duration">5.08</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_artist_and_developer_role[Desktop]</td>
|
||||
<td class="col-duration">4.60</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_non_developer_user_profile_is_not_public[Desktop]</td>
|
||||
<td class="col-duration">4.64</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_addon_cards_for_users_with_multiple_roles[Desktop]</td>
|
||||
<td class="col-duration">5.55</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_profile_extensions_card[Desktop]</td>
|
||||
<td class="col-duration">5.63</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_profile_themes_card[Desktop]</td>
|
||||
<td class="col-duration">4.35</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> ------------------------------Captured stdout call------------------------------ <br/>The user had 1 themes, so pagination is not present
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_profile_open_extension_detail_page[Desktop]</td>
|
||||
<td class="col-duration">5.51</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_profile_open_theme_detail_page[Desktop]</td>
|
||||
<td class="col-duration">5.23</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_profile_write_review[Desktop]</td>
|
||||
<td class="col-duration">55.38</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="log"> -----------------------------Captured stdout setup------------------------------ <br/>The "click continue button" event occurred.
|
||||
The script should be on the password input screen here. We should see "Sign in" in the header. The card header title is "Enter your password
|
||||
for your Firefox account"
|
||||
<br/></div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_profile_edit_review[Desktop]</td>
|
||||
<td class="col-duration">7.66</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_profile_delete_review[Desktop]</td>
|
||||
<td class="col-duration">6.63</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody>
|
||||
<tbody class="passed results-table-row">
|
||||
<tr>
|
||||
<td class="col-result">Passed</td>
|
||||
<td class="col-name">tests/frontend/test_users.py::test_user_abuse_report[Desktop]</td>
|
||||
<td class="col-duration">6.07</td>
|
||||
<td class="col-links"></td></tr>
|
||||
<tr>
|
||||
<td class="extra" colspan="4">
|
||||
<div class="empty log">No log output captured.</div></td></tr></tbody></table></body></html>
|
Загрузка…
Ссылка в новой задаче