pages/desktop/frontend/collections.py (483 lines of code) (raw):

import pytest from pypom import Region from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support.wait import WebDriverWait from pages.desktop.base import Base class Collections(Base): 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") _my_collections_list_header_locator = ( By.CSS_SELECTOR, ".CollectionList-list header", ) _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", ) 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): # wait needed to avoid a weird bug on the site where you land on the # homepage when clicking on a collection before the name is visible self.wait.until(lambda _: len(self.list[0].name.text) > 0) self.find_elements(*self._collection_item_locator)[count].click() return self.CollectionDetail(self).wait_for_details_to_load() @property def collection_detail(self): return self.CollectionDetail(self) @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" ), message="The collection save button was not displayed. The collection create form was not loaded properly", ) @property def create(self): return self.CollectionCreate(self) 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") @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", "") 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") _create_button_disabled_locator = ( By.CSS_SELECTOR, ".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") _edit_collection_addons_list_locator = ( By.CSS_SELECTOR, ".EditableCollectionAddon", ) _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): self.find_element(*self._slug_input_locator).send_keys(value) @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): self.find_element(*self._create_button_locator).click() return ( Collections(self.driver, self.page) .CollectionDetail(self) .wait_for_details_to_load() ) @property def warning_text(self): self.wait.until( EC.visibility_of_element_located(self._warning_text_locator) ) return self.find_element(*self._warning_text_locator).text @property def addon_search(self): 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") _search_list_locator = ( By.CSS_SELECTOR, ".AutoSearchInput-suggestions-list", ) _search_item_locator = ( By.CSS_SELECTOR, ".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): textbox = self.find_element(*self._search_field_locator) textbox.click() textbox.send_keys(term) return self.search_items() def search_items(self): self.wait.until( lambda _: self.is_element_displayed(*self._search_list_locator), 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", ) 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") @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", ) return self.find_element(*self._add_success_message_locator).text @property 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", ) return self.find_element(*self._add_error_message_locator).text @property 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", ) 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", ) _add_note_button_locator = ( By.CLASS_NAME, "EditableCollectionAddon-leaveNote-button", ) _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", ) _edit_addon_note_button_locator = ( By.CLASS_NAME, "EditableCollectionAddon-notes-edit-button", ) _delete_addon_note_button_locator = ( By.CLASS_NAME, "DismissibleTextForm-delete", ) _remove_addon_button_locator = ( By.CLASS_NAME, "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", ) def note_input_text(self, value): self.find_element(*self._add_note_textarea_locator).send_keys(value) def clear_collection_note_text_field(self): self.find_element(*self._add_note_textarea_locator).clear() @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", ) @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", ) 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: self.wait.until( EC.invisibility_of_element_located( self._add_note_textarea_locator ), 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" ).text pytest.fail(error) def remove_addon(self): self.find_element(*self._remove_addon_button_locator).click() self.wait.until( EC.visibility_of_element_located( (By.CSS_SELECTOR, ".Notice-generic") ), 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") _edit_details_button_locator = ( By.CLASS_NAME, "CollectionDetails-edit-details-button", ) _cancel_collection_edit_locator = ( By.CLASS_NAME, "CollectionDetails-back-to-collection-button", ) _cancel_meta_edit_button_locator = ( By.CSS_SELECTOR, ".CollectionManager-cancel", ) _delete_button_locator = (By.CLASS_NAME, "Collection-delete-button") _confirm_delete_dialog_locator = ( By.CSS_SELECTOR, ".ConfirmationDialog-message", ) _cancel_delete_button_locator = ( By.CSS_SELECTOR, ".ConfirmationDialog-cancel-button", ) _confirm_delete_button_locator = ( By.CSS_SELECTOR, ".ConfirmationDialog-confirm-button", ) _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", ) 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" ), message="The edit collection search component was not loaded", ) def click_back_to_collection(self): self.find_element(*self._cancel_collection_edit_locator).click() self.wait.until( EC.element_to_be_clickable(self._edit_button_locator), message='Could not interact with the "Back to collection" button', ) def click_edit_collection_meta(self): 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", ) 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", ) 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", ) @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", ) @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)