from time import sleep from selenium.common.exceptions import TimeoutException from selenium.webdriver.common.by import By from helpers.platform.android.locators import ( content_desc_xpath, resource_id_xpath, text_view_xpath, xpath_literal) from helpers.test_data import localization from ..base_screen import BaseScreen class MyFavoritesScreen(BaseScreen): songs_tab = content_desc_xpath("starredSoundRecordings") products_tab = content_desc_xpath("starredProducts") artists_tab = content_desc_xpath("starredParticipants") labels_tab = content_desc_xpath("starredLabels") artists_notification_settings = resource_id_xpath("categoryTextTestId") recommended_songs_title = "//*[@text='Recommended Songs']" recommended_songs = \ "//android.view.ViewGroup[contains(@content-desc, 'topTracksItem')]" recommended_products_title = "//*[@text='Recommended Products']" recommended_products = \ "//android.view.ViewGroup[contains(@content-desc, 'product')]" recommended_labels_title = "//*[@text='Recommended Labels']" first_recommended_label = ( '//*[@text="Recommended Labels"]' '/following-sibling::android.view.ViewGroup' '[.//android.widget.TextView][1]' ) label_name = './/android.widget.TextView[1]' label_star = './/*[@class="com.horcrux.svg.SvgView"]' recommended_label_stars = '(//*[@class="com.horcrux.svg.SvgView"])' recommended_labels = '' no_items_msg = { 'songs': text_view_xpath('You Have No Favorite Songs Yet'), 'products': text_view_xpath('You Have No Favorite Products Yet'), 'artists': text_view_xpath('Star Your First Artist'), 'labels': text_view_xpath('You have No Favorite Labels') } starred_star_icon = ( f'//com.horcrux.svg.PathView[@resource-id="' f'{BaseScreen.active_item_resource_id}"]' ) undo_button = '//*[@text="Undo"]' def assert_artists_tab_is_selected(self): assert self.waiter(20, By.XPATH, self.artists_notification_settings) def select_tab(self, tab_name): tab_element = getattr(self, tab_name) self.driver.find_element(By.XPATH, tab_element).click() def assert_recommended_songs_are_shown(self): assert self.waiter(40, By.XPATH, self.recommended_songs_title) self.driver.find_elements(By.XPATH, self.recommended_songs) def assert_recommended_products_are_shown(self): assert self.waiter(20, By.XPATH, self.recommended_products_title) self.driver.find_elements(By.XPATH, self.recommended_products) def assert_recommended_labels_are_shown(self): try: assert self.waiter(20, By.XPATH, self.recommended_labels_title) except TimeoutException: # Manage hidden labels when all are marked as favorites self.waiter(10, By.XPATH, self.recommended_label_stars, multiple=True)[1].click() assert self.waiter(5, By.XPATH, self.recommended_labels_title) def verify_my_favorites_screen_is_localized(self, context): localization_data = ( localization.MY_FAVORITES_LOCALIZATION)[context.current_language] self.waiter(10, By.XPATH, content_desc_xpath( localization_data['songs_notification_settings'])) recommended = localization_data["recommended_songs"] self.waiter(10, By.XPATH, text_view_xpath(recommended)) # TODO: Implementation for finding recommended labels def _zero_state_is_displayed(self, category): try: self.assert_element(By.XPATH, self.no_items_msg[category.lower()]) except TimeoutException: return False return True def clear_favorites(self, category): for i in range(50): if self._zero_state_is_displayed(category): break self.click_with_retry(5, By.XPATH, self.starred_star_icon) sleep(3) def assert_starred_products_are_shown(self, context): starred_product_names_loc = ( '//android.view.ViewGroup[' 'starts-with(@resource-id, "product") ' f'and .//*[@resource-id="{self.active_item_resource_id}"]' ']//*[@resource-id="productName"]' ) product_name_elements = self.waiter( 10, By.XPATH, starred_product_names_loc, multiple=True, ) actual_product_names = { element.text.strip() for element in product_name_elements if element.text.strip() } expected_product_names = set(context.starred_product_names) assert actual_product_names == expected_product_names, ( "Displayed products do not match starred products.\n" f"Expected: {sorted(expected_product_names)}\n" f"Displayed: {sorted(actual_product_names)}" ) def assert_no_favorite_products_are_shown(self): self.assert_element(By.XPATH, self.no_items_msg['products']) def assert_starred_artists_are_shown(self, context): starred_artist_names_loc = ( '//android.view.ViewGroup[' f'.//*[@resource-id="{self.active_item_resource_id}"]' ']//*[@resource-id="participantName"]' ) artist_name_elements = self.waiter( 10, By.XPATH, starred_artist_names_loc, multiple=True, ) actual_artist_names = { element.text.strip() for element in artist_name_elements if element.text.strip() } expected_artist_names = set(context.starred_artist_names) assert actual_artist_names == expected_artist_names, ( "Displayed artists do not match starred artists in My Favorites.\n" f"Expected: {sorted(expected_artist_names)}\n" f"Displayed: {sorted(actual_artist_names)}" ) def assert_no_favorite_artists_are_shown(self): self.assert_element(By.XPATH, self.no_items_msg['artists']) def assert_starred_labels_are_shown(self, context): starred_label_names_loc = ( f'//*[@resource-id="{self.active_item_resource_id}"]' '/ancestor::android.view.ViewGroup[.//android.widget.TextView][1]' '//android.widget.TextView[1]' ) label_name_elements = self.waiter( 10, By.XPATH, starred_label_names_loc, multiple=True, ) actual_label_names = { self._normalize_label_name(element.text) for element in label_name_elements if self._normalize_label_name(element.text) } expected_label_names = set(context.starred_label_names) missing_label_names = expected_label_names - actual_label_names assert not missing_label_names, ( "Starred label is not displayed in My Favorites.\n" f"Expected: {sorted(expected_label_names)}\n" f"Displayed: {sorted(actual_label_names)}" ) def assert_no_favorite_labels_are_shown(self): self.assert_element(By.XPATH, self.no_items_msg['labels']) def star_product_from_recommended_products(self, context, num=1): recommended_product_loc = ( By.XPATH, '//*[@text="Recommended Products"]' '/following-sibling::android.view.ViewGroup' '[starts-with(@resource-id, "product") ' f'and .//*[@resource-id="productName"]][{num}]' ) product_name_loc = (By.XPATH, resource_id_xpath('productName')) favorite_button_loc = (By.XPATH, resource_id_xpath('starIcon')) product = self.waiter(10, *recommended_product_loc) name_element = product.find_element(*product_name_loc) favorite_button = product.find_element(*favorite_button_loc) product_name = name_element.text.strip() assert product_name not in context.starred_product_names, ( f'Expected a unique product, ' f'but "{product_name}" was already selected. ' f'Products selected so far: {context.starred_product_names}') self.click_with_retry(5, element=favorite_button) context.starred_product_names.append(product_name) context.recommended_starred_product_name = product_name sleep(3) def star_first_label_from_recommended_labels(self, context): self.assert_recommended_labels_are_shown() label = self.waiter(10, By.XPATH, self.first_recommended_label) label_name = self._normalize_label_name( label.find_element(By.XPATH, self.label_name).text ) favorite_button = label.find_element(By.XPATH, self.label_star) assert label_name not in context.starred_label_names, ( f'Expected a unique label, ' f'but "{label_name}" was already selected. ' f'Labels selected so far: {context.starred_label_names}') self.click_with_retry(5, element=favorite_button) context.starred_label_names.append(label_name) sleep(3) def unstar_all_labels(self, context): self.clear_favorites('Labels') context.starred_label_names = [] def unstar_first_product(self, context, undo=False): product_name = context.starred_product_names[0] self.unstar_product(product_name) if undo: self.waiter(10, By.XPATH, self.undo_button).click() else: context.starred_product_names.remove(product_name) sleep(3) def unstar_product(self, product_name): product_name_xpath = xpath_literal(product_name) favorite_button_loc = ( '//android.view.ViewGroup[' 'starts-with(@resource-id, "product") ' f'and .//*[@resource-id="productName" ' f'and @text={product_name_xpath}]' f']//*[@resource-id="{self.active_item_resource_id}"]' ) self.click_with_retry(5, By.XPATH, favorite_button_loc) def unstar_first_artist(self, context, undo=False): artist_name = context.starred_artist_names[0] self.unstar_artist(artist_name) if undo: self.waiter(10, By.XPATH, self.undo_button).click() else: context.starred_artist_names.remove(artist_name) sleep(3) def unstar_artist(self, artist_name): artist_name_xpath = xpath_literal(artist_name) favorite_button_loc = ( '//android.view.ViewGroup[' f'.//*[@resource-id="participantName" ' f'and @text={artist_name_xpath}]' f']//*[@resource-id="{self.active_item_resource_id}"]' ) self.click_with_retry(5, By.XPATH, favorite_button_loc) @staticmethod def _normalize_label_name(label_name): return label_name.strip()