pages/desktop/frontend/details.py (1,010 lines of code) (raw):

import time from pypom import Region from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys 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 pages.desktop.frontend.reviews import Reviews from pages.desktop.frontend.versions import Versions class Detail(Base): _root_locator = (By.CLASS_NAME, "Addon-extension") _addon_name_locator = (By.CLASS_NAME, "AddonTitle") _compatible_locator = (By.CSS_SELECTOR, ".AddonCompatibilityError") _new_compatibility_banner_locator = (By.CLASS_NAME, "GetFirefoxButton-callout-text") _get_firefox_button_locator = (By.CLASS_NAME, "GetFirefoxButton-button") _install_button_locator = (By.CLASS_NAME, "AMInstallButton-button") _install_button_state_locator = (By.CSS_SELECTOR, ".AMInstallButton a") _promoted_badge_locator = (By.CLASS_NAME, "PromotedBadge-large") _promoted_badge_label_locator = ( By.CSS_SELECTOR, ".PromotedBadge-large .PromotedBadge-label", ) _experimental_badge_locator = (By.CLASS_NAME, "Badge-experimental") _addon_icon_locator = (By.CLASS_NAME, "Addon-icon-image") _addon_author_locator = (By.CSS_SELECTOR, ".AddonTitle-author a") _summary_locator = (By.CLASS_NAME, "Addon-summary") _install_warning_locator = (By.CLASS_NAME, "InstallWarning") _install_warning_text_locator = (By.CSS_SELECTOR, ".InstallWarning p") _install_warning_button_locator = (By.CSS_SELECTOR, ".InstallWarning a") _non_public_addon_notice_locator = (By.CSS_SELECTOR, ".Addon-non-public-notice") _privacy_policy_locator = (By.CSS_SELECTOR, ".AddonMoreInfo-privacy-policy-link") _license_agreement_locator = (By.CSS_SELECTOR, ".AddonMoreInfo-eula-link") _addon_info_text = (By.CSS_SELECTOR, ".AddonInfo-info-html") _page_not_available_in_region = (By.XPATH, "//div[contains(text(), 'That page is not available in your region')]") _not_available_in_your_region_message = (By.XPATH, "//p[contains(text(), 'The page you tried to access is not available in your region.')]") _paragraphs_with_links_message = (By.CSS_SELECTOR, "p.Errors-paragraph-with-links") _card_header_text_message = (By.CSS_SELECTOR, ".Card-header-text") _why_was_it_blocked_message = (By.CSS_SELECTOR, ".Card-contents > h2") _block_metadata_message = (By.CSS_SELECTOR, ".Block-metadata") 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")) ) time.sleep(3) return self @property def name(self): """The only valid locator for the addon name on AMO will return the name as 'Addon name by author(s) name'. This format makes it difficult to determine the exact addon name, especially when we want to use it in comparisons. The following method makes sure that we only return the addon name without authors """ # get the name + authors el = self.find_element(*self._addon_name_locator).text # isolate the authors text authors = self.find_element(By.CSS_SELECTOR, ".AddonTitle-author").text # remove the authors part from the name element to get only the actual addon name name = el.replace(authors, "")[:-1] return name @property def is_compatible(self): return not self.is_element_displayed(*self._compatible_locator) @property def incompatibility_message(self): self.wait_for_element_to_be_displayed(self._compatible_locator) return self.find_element(*self._compatible_locator).text @property def compatibility_banner(self): self.wait_for_element_to_be_displayed(self._new_compatibility_banner_locator) return self.find_element(*self._new_compatibility_banner_locator) @property def get_firefox_button(self): self.wait_for_element_to_be_displayed(self._get_firefox_button_locator) return self.find_element(*self._get_firefox_button_locator) def install(self): self.find_element(*self._install_button_locator).click() @property def button_text(self): self.wait.until( expected.invisibility_of_element_located( (By.CLASS_NAME, "AMInstallButton-loading-button") ) ) return self.find_element(*self._install_button_locator).text @property def addon_xpi(self): """Gets the add-on .xpi mapped to the install button""" self.wait_for_element_to_be_displayed(self._install_button_locator) return self.find_element(*self._install_button_locator).get_attribute("href") @property def button_state_disabled(self): # checking that an inactive install button has a 'disabled' attribute return self.find_element(*self._install_button_state_locator).get_attribute( "disabled" ) @property def promoted_badge(self): self.wait_for_element_to_be_displayed(self._install_button_locator) return self.find_element(*self._promoted_badge_locator) @property def promoted_badge_category(self): self.wait_for_element_to_be_displayed(self._install_button_locator) return self.find_element(*self._promoted_badge_label_locator).text def click_promoted_badge(self): # clicks on the promoted badge and waits for the sumo page to load self.promoted_badge.click() self.wait.until(expected.number_of_windows_to_be(2)) new_tab = self.driver.window_handles[1] self.driver.switch_to.window(new_tab) self.wait.until( expected.visibility_of_element_located((By.CLASS_NAME, "sumo-page-heading")) ) @property def experimental_badge(self): self.wait_for_element_to_be_displayed(self._experimental_badge_locator) return self.find_element(*self._experimental_badge_locator) @property def addon_icon(self): self.wait_for_element_to_be_displayed(self._addon_icon_locator) return self.find_element(*self._addon_icon_locator) @property def authors(self): return self.find_element(*self._addon_author_locator) @property def summary(self): self.wait_for_element_to_be_displayed(self._summary_locator) return self.find_element(*self._summary_locator) @property def install_warning(self): self.wait_for_element_to_be_displayed(self._install_warning_locator) return self.find_element(*self._install_warning_locator) @property def install_warning_message(self): self.wait_for_element_to_be_displayed(self._install_warning_text_locator) return self.find_element(*self._install_warning_text_locator).text def click_install_warning_button(self): # clicks on the install warning and waits for the sumo page to load self.wait_for_element_to_be_clickable(self._install_warning_button_locator) self.find_element(*self._install_warning_button_locator).click() self.wait.until(expected.number_of_windows_to_be(2)) new_tab = self.driver.window_handles[1] self.driver.switch_to.window(new_tab) self.wait.until( expected.visibility_of_element_located((By.CLASS_NAME, "sumo-page-heading")) ) @property def non_public_addon_notice(self): self.wait_for_element_to_be_clickable(self._non_public_addon_notice_locator) return self.find_element(*self._non_public_addon_notice_locator) @property def stats(self): return self.Stats(self) @property def contribute(self): return self.Contribute(self) @property def permissions(self): return self.Permissions(self) @property def more_info(self): return self.MoreInfo(self) @property def screenshots(self): return self.Screenshots(self) @property def release_notes(self): return self.ReleaseNotes(self) @property def same_author_addons(self): return self.AddonsByAuthor(self) @property def add_to_collection(self): return self.AddToCollection(self) @property def description(self): return self.AddonDescription(self) @property def developer_comments(self): return self.DeveloperComments(self) @property def recommendations(self): return self.AddonRecommendations(self) @property def ratings(self): return self.Ratings(self) @property def themes(self): return self.Theme(self) @property def addon_info_text(self): """used for privacy policy and license agreement""" return self.find_element(*self._addon_info_text) @property def privacy_policy_locator(self): return self.find_element(*self._privacy_policy_locator) @property def license_agreement_locator(self): return self.find_element(*self._license_agreement_locator) @property def page_not_available_in_region(self): return self.find_element(*self._page_not_available_in_region) @property def not_available_in_your_region_message(self): return self.find_element(*self._not_available_in_your_region_message) @property def paragraphs_with_links_message(self): return self.find_element(*self._paragraphs_with_links_message) @property def card_header_text(self): return self.find_element(*self._card_header_text_message) @property def why_was_it_blocked(self): return self.find_element(*self._why_was_it_blocked_message) @property def block_metadata(self): return self.find_element(*self._block_metadata_message) class Stats(Region): _root_locator = (By.CLASS_NAME, "AddonMeta") _stats_users_locator = (By.CSS_SELECTOR, ".AddonMeta dl:nth-child(1)") _stats_reviews_locator = (By.CSS_SELECTOR, ".AddonMeta dl:nth-child(2)") _stats_ratings_locator = (By.CSS_SELECTOR, ".AddonMeta dl:nth-child(3)") _rating_score_title_locator = ( By.CSS_SELECTOR, ".AddonMeta-rating-content .Rating--small", ) _rating_title_locator = (By.CSS_SELECTOR, ".AddonMeta-rating-title") _grouped_ratings_locator = (By.CSS_SELECTOR, ".RatingsByStar-star") _rating_bar_locator = (By.CSS_SELECTOR, ".RatingsByStar-barContainer") _rating_bar_count_locator = (By.CSS_SELECTOR, ".RatingsByStar-count") @property def addon_user_stats(self): self.wait.until(EC.visibility_of_element_located(self._stats_users_locator)) return self.find_element(*self._stats_users_locator) @property def stats_users_count(self): count = self.addon_user_stats.find_element(By.CSS_SELECTOR, "dd").text return int(count.split()[0].replace(",", "")) @property def no_user_stats(self): return self.addon_user_stats.find_element(By.CSS_SELECTOR, "dt").text @property def addon_reviews_stats(self): self.wait.until( EC.visibility_of_element_located(self._stats_reviews_locator) ) return self.find_element(*self._stats_reviews_locator) @property def stats_reviews_count(self): count = self.addon_reviews_stats return int(count.find_element(By.CSS_SELECTOR, "dd").text.replace(",", "")) def stats_reviews_link(self): self.addon_reviews_stats.find_element(By.CSS_SELECTOR, "dt a").click() return Reviews(self.driver, self.page.base_url).wait_for_page_to_load() @property def no_reviews_stats(self): return self.addon_reviews_stats.find_element(By.CSS_SELECTOR, "dt").text @property def addon_star_rating_stats(self): self.wait.until( EC.visibility_of_element_located(self._stats_ratings_locator) ) return self.find_element(*self._stats_ratings_locator) @property def rating_score_tile(self): self.wait.until( EC.visibility_of_element_located(self._rating_score_title_locator) ) return self.find_element(*self._rating_score_title_locator).get_attribute( "title" ) @property def rating_title(self): self.wait.until( EC.visibility_of_element_located(self._rating_title_locator) ) return self.find_element(*self._rating_title_locator) @property def no_star_ratings(self): return self.addon_star_rating_stats.find_element(By.CSS_SELECTOR, "dt").text @property def bar_grouped_ratings(self): self.wait.until( EC.visibility_of_element_located(self._grouped_ratings_locator) ) return self.find_elements(*self._grouped_ratings_locator) @property def rating_bars(self): self.wait.until(EC.visibility_of_element_located(self._rating_bar_locator)) return self.find_elements(*self._rating_bar_locator) @property def bar_rating_counts(self): self.wait.until( EC.visibility_of_element_located(self._rating_bar_count_locator) ) return self.find_elements(*self._rating_bar_count_locator) class Contribute(Region): _contribute_header_locator = (By.CSS_SELECTOR, ".ContributeCard header") _contribute_content_locator = (By.CLASS_NAME, "ContributeCard-content") _contribute_button_locator = (By.CLASS_NAME, "ContributeCard-button") _contribute_button_icon_locator = (By.CLASS_NAME, "Icon-heart") @property def contribute_card_header(self): self.wait.until( EC.visibility_of_element_located(self._contribute_header_locator) ) return self.find_element(*self._contribute_header_locator).text @property def contribute_card_content(self): self.wait.until( EC.visibility_of_element_located(self._contribute_header_locator) ) return self.find_element(*self._contribute_content_locator).text @property def contribute_button_text(self): self.wait.until( EC.visibility_of_element_located(self._contribute_button_locator) ) return self.find_element(*self._contribute_button_locator).text @property def contribute_button_heart_icon(self): self.wait.until( EC.visibility_of_element_located(self._contribute_button_icon_locator) ) return self.find_element(*self._contribute_button_icon_locator) def click_contribute_button(self): self.find_element(*self._contribute_button_locator).click() self.wait.until(expected.number_of_windows_to_be(2)) new_tab = self.driver.window_handles[1] self.driver.switch_to.window(new_tab) class Permissions(Region): _permissions_header_locator = (By.CSS_SELECTOR, ".PermissionsCard header") _permissions_list_locator = ( By.CSS_SELECTOR, ".PermissionsCard-list--required li", ) _permissions_learn_more_locator = (By.CLASS_NAME, "PermissionsCard-learn-more") _learn_more_button_icon_locator = (By.CLASS_NAME, "Icon-external-dark") @property def permissions_card_header(self): self.wait.until( EC.visibility_of_element_located(self._permissions_header_locator) ) return self.find_element(*self._permissions_header_locator).text @property def permissions_list(self): self.wait.until( EC.visibility_of_element_located(self._permissions_list_locator) ) items = self.find_elements(*self._permissions_list_locator) return [self.PermissionDetails(self.page, el) for el in items] @property def permissions_learn_more_button(self): self.wait.until( EC.visibility_of_element_located(self._permissions_learn_more_locator) ) return self.find_element(*self._permissions_learn_more_locator).text @property def permissions_learn_more_button_icon(self): self.wait.until( EC.visibility_of_element_located(self._learn_more_button_icon_locator) ) return self.find_element(*self._learn_more_button_icon_locator) def click_permissions_button(self): self.wait.until( EC.element_to_be_clickable(self._permissions_learn_more_locator) ) self.find_element(*self._permissions_learn_more_locator).click() self.wait.until(expected.number_of_windows_to_be(2)) new_tab = self.driver.window_handles[1] self.driver.switch_to.window(new_tab) class PermissionDetails(Region): _permission_icon_locator = (By.CSS_SELECTOR, ".Permission .Icon") _permission_description_locator = (By.CLASS_NAME, "Permission-description") @property def permission_icon(self): self.wait.until( EC.visibility_of_element_located(self._permission_icon_locator) ) return self.find_element(*self._permission_icon_locator) @property def permission_description(self): self.wait.until( EC.visibility_of_element_located( self._permission_description_locator ) ) return self.find_element(*self._permission_description_locator) class MoreInfo(Region): _more_info_header_locator = (By.CSS_SELECTOR, ".AddonMoreInfo header") _support_links_locator = (By.CSS_SELECTOR, ".AddonMoreInfo-links a") _homepage_link_locator = (By.CSS_SELECTOR, ".AddonMoreInfo-homepage-link") _support_site_link_locator = (By.CSS_SELECTOR, ".AddonMoreInfo-support-link") _version_number_locator = (By.CLASS_NAME, "AddonMoreInfo-version") _addon_size_locator = (By.CLASS_NAME, "AddonMoreInfo-filesize") _last_updated_locator = (By.CLASS_NAME, "AddonMoreInfo-last-updated") _addon_categories_locator = ( By.CSS_SELECTOR, ".AddonMoreInfo-related-categories-list a", ) _addon_license_locator = (By.CLASS_NAME, "AddonMoreInfo-license-link") _all_rights_reserved_license_locator = ( By.CLASS_NAME, "AddonMoreInfo-license-name", ) _privacy_policy_locator = (By.CLASS_NAME, "AddonMoreInfo-privacy-policy-link") _eula_locator = (By.CLASS_NAME, "AddonMoreInfo-eula") _all_versions_locator = (By.CLASS_NAME, "AddonMoreInfo-version-history-link") _addon_tags_locator = (By.CSS_SELECTOR, ".AddonMoreInfo-tag-links-list a") @property def more_info_card_header(self): self.wait.until( EC.visibility_of_element_located(self._more_info_header_locator) ) return self.find_element(*self._more_info_header_locator).text @property def addon_support_links(self): self.wait.until( EC.visibility_of_element_located(self._support_links_locator) ) return self.find_elements(*self._support_links_locator) @property def addon_homepage_link(self): self.wait.until( EC.visibility_of_element_located(self._homepage_link_locator) ) return self.find_element(*self._homepage_link_locator) @property def addon_support_site_link(self): self.wait.until( EC.visibility_of_element_located(self._support_site_link_locator) ) return self.find_element(*self._support_site_link_locator) @property def addon_version_number(self): self.wait.until( EC.visibility_of_element_located(self._version_number_locator) ) return self.find_element(*self._version_number_locator) @property def addon_size(self): self.wait.until(EC.visibility_of_element_located(self._addon_size_locator)) return self.find_element(*self._addon_size_locator) @property def addon_last_update_date(self): self.wait.until( EC.visibility_of_element_located(self._last_updated_locator) ) return self.find_element(*self._last_updated_locator) @property def addon_categories(self): self.wait.until( EC.visibility_of_element_located(self._addon_categories_locator) ) return self.find_elements(*self._addon_categories_locator) @property def addon_external_license_text(self): self.wait.until( EC.visibility_of_element_located(self._addon_license_locator) ) return self.find_element(*self._addon_license_locator).text @property def addon_all_rights_reserved_license_text(self): self.wait.until( EC.visibility_of_element_located( self._all_rights_reserved_license_locator ) ) return self.find_element(*self._all_rights_reserved_license_locator).text def click_addon_external_license(self): self.wait.until(EC.element_to_be_clickable(self._addon_license_locator)) self.find_element(*self._addon_license_locator).click() # clicking on license should open a link outside of AMO self.wait.until( expected.invisibility_of_element_located(self._more_info_header_locator) ) def click_addon_custom_license(self): self.wait.until(EC.element_to_be_clickable(self._addon_license_locator)) self.find_element(*self._addon_license_locator).click() return self.License(self).wait_for_region_to_load() def click_addon_privacy_policy(self): self.wait.until(EC.element_to_be_clickable(self._privacy_policy_locator)) self.find_element(*self._privacy_policy_locator).click() return self.License(self).wait_for_region_to_load() def addon_eula(self): self.wait.until(EC.visibility_of_element_located(self._eula_locator)) self.find_element(*self._eula_locator).click() return self.License(self).wait_for_region_to_load() def addon_versions(self): self.wait.until( EC.visibility_of_element_located(self._all_versions_locator) ) self.find_element(*self._all_versions_locator).click() return Versions(self.driver, self.page.base_url).wait_for_page_to_load() @property def addon_tags(self): self.wait.until(EC.visibility_of_element_located(self._addon_tags_locator)) return self.find_elements(*self._addon_tags_locator) class License(Region): _license_and_privacy_header_locator = ( By.CSS_SELECTOR, ".AddonInfo-info header", ) _license_and_privacy_text_locator = (By.CSS_SELECTOR, ".AddonInfo-info p") _addon_summary_card_locator = (By.CSS_SELECTOR, ".AddonSummaryCard") def wait_for_region_to_load(self): """Waits for various page components to be loaded""" self.wait.until( expected.invisibility_of_element_located( (By.CLASS_NAME, "LoadingText") ) ) return self @property def custom_licence_and_privacy_header(self): self.wait.until( EC.visibility_of_element_located( self._license_and_privacy_header_locator ) ) return self.find_element(*self._license_and_privacy_header_locator).text @property def custom_licence_and_privacy_text(self): self.wait.until( EC.visibility_of_element_located( self._license_and_privacy_text_locator ) ) return self.find_element(*self._license_and_privacy_text_locator) @property def custom_licence_and_privacy_summary_card(self): self.wait.until( EC.visibility_of_element_located(self._addon_summary_card_locator) ) return self.find_element(*self._addon_summary_card_locator) class Screenshots(Region): _screenshot_section_header_locator = ( By.CSS_SELECTOR, ".Addon-screenshots .Card-header", ) _screenshot_thumbnail_locator = (By.CSS_SELECTOR, ".ScreenShots-list img") _screenshot_viewer_locator = (By.CSS_SELECTOR, ".pswp--open") _next_preview_locator = (By.CSS_SELECTOR, ".pswp__button--arrow--right") _previous_preview_locator = (By.CSS_SELECTOR, ".pswp__button--arrow--left") _image_view_close_button_locator = ( By.CSS_SELECTOR, ".pswp__button--close", ) _screenshot_counter_location = (By.CSS_SELECTOR, ".pswp__counter") @property def screenshot_section_header(self): self.wait.until( EC.visibility_of_element_located( self._screenshot_section_header_locator ) ) return self.find_element(*self._screenshot_section_header_locator) @property def screenshot_preview(self): self.wait.until( EC.visibility_of_element_located(self._screenshot_thumbnail_locator) ) return self.find_elements(*self._screenshot_thumbnail_locator) @property def screenshot_viewer(self): self.wait.until( EC.visibility_of_element_located(self._screenshot_viewer_locator) ) return self.find_element(*self._screenshot_viewer_locator) def screenshot_full_view_displayed(self): self.wait.until( lambda _: self.is_element_displayed(*self._screenshot_viewer_locator) ) return self def go_to_next_screenshot(self): self.wait.until(EC.element_to_be_clickable(self._next_preview_locator)) self.find_element(*self._next_preview_locator).click() def go_to_previous_screenshot(self): self.wait.until(EC.element_to_be_clickable(self._previous_preview_locator)) self.find_element(*self._previous_preview_locator).click() def right_key_for_next_screenshot(self): self.find_element(*self._screenshot_viewer_locator).send_keys( Keys.ARROW_RIGHT ) def left_key_for_previous_screenshot(self): self.find_element(*self._screenshot_viewer_locator).send_keys( Keys.ARROW_LEFT ) @property def screenshot_counter(self): self.wait.until( EC.visibility_of_element_located(self._screenshot_counter_location) ) return self.find_element(*self._screenshot_counter_location).text def close_screenshot_view(self): self.wait.until( EC.element_to_be_clickable(self._image_view_close_button_locator) ) self.find_element(*self._image_view_close_button_locator).click() self.wait.until( expected.invisibility_of_element_located(self.screenshot_viewer) ) def esc_to_close_screenshot_viewer(self): self.find_element(*self._screenshot_viewer_locator).send_keys(Keys.ESCAPE) self.wait.until( expected.invisibility_of_element_located(self.screenshot_viewer) ) class ReleaseNotes(Region): _release_notes_card_header_locator = ( By.CSS_SELECTOR, ".AddonDescription-version-notes header", ) _release_notes_content_locator = ( By.CSS_SELECTOR, ".AddonDescription-version-notes .ShowMoreCard-contents", ) @property def release_notes_header(self): self.wait.until( EC.visibility_of_element_located( self._release_notes_card_header_locator ) ) return self.find_element(*self._release_notes_card_header_locator).text @property def release_notes_text(self): self.wait.until( EC.visibility_of_element_located(self._release_notes_content_locator) ) return self.find_element(*self._release_notes_content_locator) class AddonsByAuthor(Region): _addons_by_author_header_locator = ( By.CSS_SELECTOR, ".AddonsByAuthorsCard header", ) _addons_by_author_results_locator = ( By.CSS_SELECTOR, ".AddonsByAuthorsCard .SearchResult", ) _addons_by_author_results_item_locator = ( By.CSS_SELECTOR, ".AddonsByAuthorsCard a", ) @property def addons_by_author_header(self): self.wait.until( EC.visibility_of_element_located(self._addons_by_author_header_locator) ) return self.find_element(*self._addons_by_author_header_locator).text @property def addons_by_author_results_list(self): self.wait.until( EC.visibility_of_element_located(self._addons_by_author_results_locator) ) return self.find_elements(*self._addons_by_author_results_locator) @property def addons_by_author_results_items(self): self.wait.until( EC.visibility_of_element_located( self._addons_by_author_results_item_locator ) ) return self.find_elements(*self._addons_by_author_results_item_locator) class AddToCollection(Region): _collection_card_header_locator = ( By.CSS_SELECTOR, ".AddAddonToCollection header", ) _collection_select_locator = (By.CLASS_NAME, "AddAddonToCollection-select") _select_collections_list_locator = ( By.CSS_SELECTOR, ".AddAddonToCollection optgroup option", ) _add_to_collection_success_notice_locator = ( By.CSS_SELECTOR, ".AddAddonToCollection-added .Notice-text", ) _add_to_collection_error_notice_locator = ( By.CSS_SELECTOR, ".Notice-error .Notice-text", ) @property def collections_card_header(self): self.wait.until( EC.visibility_of_element_located(self._collection_card_header_locator) ) return self.find_element(*self._collection_card_header_locator).text @property def collections_select_field(self): self.wait.until( EC.visibility_of_element_located(self._collection_select_locator) ) return self.find_element(*self._collection_select_locator) @property def add_to_collections_list(self): self.wait.until( EC.visibility_of_element_located(self._select_collections_list_locator) ) return self.find_elements(*self._select_collections_list_locator) @property def add_to_collection_success_notice(self): self.wait.until( expected.visibility_of_element_located( self._add_to_collection_success_notice_locator ), message="The Add to collection success message was not displayed", ) return self.find_element( *self._add_to_collection_success_notice_locator ).text @property def add_to_collection_error_notice(self): self.wait.until( expected.visibility_of_element_located( self._add_to_collection_error_notice_locator ), message="The Add to collection error message was not displayed", ) return self.find_element(*self._add_to_collection_error_notice_locator).text class AddonDescription(Region): _description_header_locator = (By.CSS_SELECTOR, ".AddonDescription header") _description_text_locator = (By.CLASS_NAME, "AddonDescription-contents") _read_more_button_locator = (By.CSS_SELECTOR, ".ShowMoreCard-expand-link") @property def addon_description_header(self): self.wait.until( EC.visibility_of_element_located(self._description_header_locator) ) return self.find_element(*self._description_header_locator).text @property def addon_description_text(self): self.wait.until( EC.visibility_of_element_located(self._description_text_locator) ) return self.find_element(*self._description_text_locator) @property def read_more_button(self): self.wait.until( EC.visibility_of_element_located(self._read_more_button_locator) ) return self.find_element(*self._read_more_button_locator) def click_read_more_button(self): self.wait.until(EC.element_to_be_clickable(self._read_more_button_locator)) return self.find_element(*self._read_more_button_locator).click() class AddonRecommendations(Region): _addon_recommendations_root_locator = (By.CSS_SELECTOR, ".AddonRecommendations") _recommendations_card_header_locator = ( By.CSS_SELECTOR, ".AddonRecommendations header", ) _recommendations_card_results_locator = ( By.CSS_SELECTOR, ".AddonRecommendations .SearchResult", ) _recommendations_name_locator = ( By.CSS_SELECTOR, ".AddonRecommendations .SearchResult-link", ) @property def addon_recommendations_header(self): return self.find_element(*self._recommendations_card_header_locator).text @property def addons_recommendations_results_list(self): self.wait.until( EC.visibility_of_element_located( self._recommendations_card_results_locator ) ) return self.find_elements(*self._recommendations_card_results_locator) @property def recommendations_results_item(self): self.wait.until( EC.visibility_of_element_located(self._recommendations_name_locator) ) return self.find_elements(*self._recommendations_name_locator) class DeveloperComments(Region): _header_locator = (By.CSS_SELECTOR, ".Addon-developer-comments-header") _content_locator = (By.CSS_SELECTOR, ".Addon-developer-comments-contents") @property def header(self): self.wait.until(EC.visibility_of_element_located(self._header_locator)) return self.find_element(*self._header_locator) @property def content(self): self.wait.until(EC.visibility_of_element_located(self._content_locator)) return self.find_element(*self._content_locator) class Theme(Region): _theme_preview_locator = (By.CSS_SELECTOR, ".ThemeImage-image") _same_author_theme_previews_locator = (By.CSS_SELECTOR, ".SearchResult-icon") @property def theme_preview(self): self.wait.until( EC.visibility_of_element_located(self._theme_preview_locator) ) return self.find_element(*self._theme_preview_locator) @property def more_themes_by_author_previews(self): # waiting for the element to be clickable to avoid 'element could not be scrolled into view' exception self.wait.until( expected.element_to_be_clickable( self._same_author_theme_previews_locator ) ) return self.find_elements(*self._same_author_theme_previews_locator) @property def preview_source(self): return [ item.get_attribute("src") for item in self.more_themes_by_author_previews ] class Ratings(Region): _ratings_card_header_locator = (By.CSS_SELECTOR, ".Addon-overall-rating header") _ratings_card_summary_locator = (By.CLASS_NAME, "RatingManager-legend") _login_to_rate_button_locator = ( By.CLASS_NAME, "RatingManager-log-in-to-rate-button", ) _rating_stars_locator = (By.CSS_SELECTOR, ".RatingManager-UserRating button") _loaded_rating_stars_locator = (By.CSS_SELECTOR, ".Rating--loading") _highlighted_star_locator = ( By.CSS_SELECTOR, ".RatingManager-UserRating .Rating-selected-star", ) _delete_rating_link_locator = (By.CSS_SELECTOR, ".AddonReviewCard-delete") _delete_confirm_button_locator = ( By.CSS_SELECTOR, ".ConfirmationDialog-confirm-button", ) _write_review_button_locator = ( By.CSS_SELECTOR, ".AddonReviewCard-writeReviewButton", ) _review_textarea_locator = (By.CSS_SELECTOR, ".AddonReviewManager textarea") _cancel_review_write_locator = ( By.CSS_SELECTOR, ".AddonReviewManager .DismissibleTextForm-dismiss", ) _submit_review_button_locator = ( By.CSS_SELECTOR, ".AddonReviewManager .DismissibleTextForm-submit", ) _review_text_locator = (By.CSS_SELECTOR, ".UserReview-body") _edit_review_link_locator = (By.CSS_SELECTOR, ".AddonReviewCard-allControls a") _delete_review_link_locator = (By.CLASS_NAME, "AddonReviewCard-delete") _keep_review_button_locator = ( By.CSS_SELECTOR, ".ConfirmationDialog-cancel-button", ) _review_permalink_locator = (By.CSS_SELECTOR, ".UserReview-byLine a") _report_abuse_button_locator = (By.CLASS_NAME, "AddonReportAbuseLink--preview") _all_reviews_link_locator = (By.CLASS_NAME, "Addon-all-reviews-link") _submit_review_error_locator = (By.CSS_SELECTOR, ".Notice-error") @property def submit_review_error(self): self.wait.until( EC.visibility_of_element_located(self._submit_review_error_locator) ) return self.find_element(*self._submit_review_error_locator) def submit_review_error_message(self, value): self.wait.until( expected.text_to_be_present_in_element( (By.CSS_SELECTOR, ".Notice-error"), value ), message=f'The expected error message "{value}" was not raised', ) @property def ratings_card_header(self): self.wait.until( EC.visibility_of_element_located(self._ratings_card_header_locator) ) return self.find_element(*self._ratings_card_header_locator).text @property def ratings_card_summary(self): self.wait.until( EC.visibility_of_element_located(self._ratings_card_summary_locator) ) return self.find_element(*self._ratings_card_summary_locator).text @property def rating_login_button(self): self.wait.until( EC.visibility_of_element_located(self._login_to_rate_button_locator) ) return self.find_element(*self._login_to_rate_button_locator) @property def rating_stars(self): # waits for the rating stars to be loaded self.wait.until( expected.invisibility_of_element_located( self._loaded_rating_stars_locator ) ) time.sleep(3) # waits for the rating stars to be editable self.wait.until( expected.element_to_be_clickable(self._rating_stars_locator) ) return self.find_elements(*self._rating_stars_locator) @property def selected_star_highlight(self): # waits for the ratings stars to be fully loaded and editable self.wait.until( expected.invisibility_of_element_located( self._loaded_rating_stars_locator ) ) return self.find_elements(*self._highlighted_star_locator) @property def delete_rating_link(self): # waits for the ratings stars to be fully loaded and editable self.wait.until( expected.invisibility_of_element_located( self._loaded_rating_stars_locator ) ) # waits for delete link to be displayed self.wait.until( expected.visibility_of_element_located(self._delete_rating_link_locator) ) return self.find_element(*self._delete_rating_link_locator) @property def delete_confirm_button(self): self.wait.until( EC.visibility_of_element_located(self._delete_confirm_button_locator) ) return self.find_element(*self._delete_confirm_button_locator) def click_delete_confirm_button(self): self.wait.until( EC.element_to_be_clickable(self._delete_confirm_button_locator) ) self.find_element(*self._delete_confirm_button_locator).click() self.wait.until( expected.invisibility_of_element_located(self._review_text_locator) ) @property def write_a_review(self): self.wait.until( EC.visibility_of_element_located(self._write_review_button_locator) ) return self.find_element(*self._write_review_button_locator) def wait_for_rating_form(self): self.wait.until( expected.element_to_be_clickable(self._write_review_button_locator) ) def review_text_input(self, value): self.wait.until( EC.visibility_of_element_located(self._review_textarea_locator) ) self.find_element(*self._review_textarea_locator).send_keys(value) def clear_review_text_field(self): self.find_element(*self._review_textarea_locator).clear() @property def submit_review_button(self): self.wait.until( EC.visibility_of_element_located(self._submit_review_button_locator) ) return self.find_element(*self._submit_review_button_locator) def submit_review(self): self.wait.until( EC.element_to_be_clickable(self._submit_review_button_locator) ) self.find_element(*self._submit_review_button_locator).click() self.wait.until( expected.visibility_of_element_located(self._edit_review_link_locator) ) @property def written_review(self): self.wait.until(EC.visibility_of_element_located(self._review_text_locator)) return self.find_element(*self._review_text_locator) @property def cancel_review(self): self.wait.until( EC.visibility_of_element_located(self._cancel_review_write_locator) ) return self.find_element(*self._cancel_review_write_locator) @property def edit_review(self): # waits for the ratings stars to be fully loaded and editable self.wait.until( expected.invisibility_of_element_located( self._loaded_rating_stars_locator ) ) return self.find_element(*self._edit_review_link_locator) @property def delete_review(self): # waits for the ratings stars to be fully loaded and editable self.wait.until( expected.invisibility_of_element_located( self._loaded_rating_stars_locator ) ) return self.find_element(*self._delete_review_link_locator) @property def keep_review_button(self): self.wait.until( EC.visibility_of_element_located(self._keep_review_button_locator) ) return self.find_element(*self._keep_review_button_locator) @property def review_permalink(self): # waits for the ratings stars to be fully loaded and editable self.wait.until( expected.invisibility_of_element_located( self._loaded_rating_stars_locator ) ) return self.find_element(*self._review_permalink_locator) @property def all_reviews_link(self): self.wait.until( EC.visibility_of_element_located(self._all_reviews_link_locator) ) return self.find_element(*self._all_reviews_link_locator) def click_all_reviews_link(self): self.wait.until(EC.element_to_be_clickable(self._all_reviews_link_locator)) self.find_element(*self._all_reviews_link_locator).click() return Reviews(self.driver, self.page.base_url).wait_for_page_to_load() @property def all_reviews_link_rating_count(self): self.wait.until( EC.visibility_of_element_located(self._all_reviews_link_locator) ) count = self.find_element(*self._all_reviews_link_locator).text return int(count.split()[2]) @property def report_abuse_button(self): self.wait.until( EC.visibility_of_element_located(self._report_abuse_button_locator) ) return self.find_element(*self._report_abuse_button_locator) def click_report_abuse(self): self.wait.until( EC.element_to_be_clickable(self._report_abuse_button_locator) ) self.find_element(*self._report_abuse_button_locator).click()