import logging import time import allure from appium.webdriver.common.mobileby import MobileBy from appium.webdriver.common.touch_action import TouchAction from ui_automation_framework.utils import logger as cl from app.src.helpers.waiting_wrapper import wait from app.src.pages.track_page import TrackPage from app.src.ui_elements.button_ui_element import ButtonUIElement from app.src.ui_elements.input_ui_element import InputUIElement from app.src.ui_elements.label_ui_element import LabelUIElement from app.src.ui_elements.list_ui_element import ListUIElement class PlaylistPlacementsPage(TrackPage): log = cl.Logger(logging.DEBUG) TRACK_CARD_LOCATOR = '(//XCUIElementTypeOther[@name="swipeable-star"]/../../../../XCUIElementTypeOther[contains(@label, "Added")])[{}]' STAR_LOCATOR = '(//XCUIElementTypeOther[@name="swipeable-star"])[{}]' def __init__(self, driver, api_client): super().__init__(driver, api_client) self.driver = driver self.playlist_placements_locators = self.get_page_locators("PlaylistPlacements", "playlist_placements_elements.json") @property def lt_track_cards(self): return ListUIElement(self.driver, (MobileBy.XPATH, self.TRACK_CARD_LOCATOR)) @property def bn_search_field(self): return ButtonUIElement(self.driver, self.locator(self.playlist_placements_locators, "search_field")) @property def bn_cancel(self): return ButtonUIElement(self.driver, self.locator(self.playlist_placements_locators, "cancel_button")) @property def in_search_text_area(self): return InputUIElement(self.driver, self.locator(self.playlist_placements_locators, "search_text_area")) @property def lb_starred(self): return LabelUIElement(self.driver, self.locator(self.playlist_placements_locators, "starred_label")) @property def lb_unstarred(self): return LabelUIElement(self.driver, self.locator(self.playlist_placements_locators, "unstarred_label")) @property def bn_personalized_playlist_i_icon(self): return ButtonUIElement(self.driver, self.locator(self.playlist_placements_locators, "personalized_playlist_i_icon")) @property def bn_sorting_popup_done_button(self): return ButtonUIElement(self.driver, self.locator(self.playlist_placements_locators, "sorting_popup_done_button")) @property def lb_main_track_personalized(self): return LabelUIElement(self.driver, self.locator(self.playlist_placements_locators, "main_track_personalized")) @property def lb_main_track(self): return LabelUIElement(self.driver, self.locator(self.playlist_placements_locators, "main_track")) @property def lb_title(self): return LabelUIElement(self.driver, self.locator(self.playlist_placements_locators, "title")) @property def lb_scrolled_header(self): return LabelUIElement(self.driver, self.locator(self.playlist_placements_locators, "scrolled_header")) @property def bn_sorting_icon(self): return ButtonUIElement(self.driver, self.locator(self.playlist_placements_locators, "sorting_icon")) @property def lt_search_results(self): return ListUIElement( self.driver, all_elements_locator=( MobileBy.IOS_CLASS_CHAIN, self.locator(self.playlist_placements_locators, "search_results")[0], ), ) @property def lb_heading(self): return LabelUIElement(self.driver, self.locator(self.playlist_placements_locators, "heading")) @property def lt_playlist_tracks(self): return ListUIElement( self.driver, all_elements_locator=( MobileBy.IOS_CLASS_CHAIN, self.locator(self.playlist_placements_locators, "playlist_tracks")[0], ), ) @allure.step("Clicking on a search field") def click_search_field(self): self.bn_search_field.click() return self @allure.step("Clicking cancel button on playlists search screen") def click_playlists_search_cancel_button(self): self.bn_cancel.click() return self @allure.step("Searching for track in playlists") def search_for_track_in_playlists(self, query): self.bn_search_field.click() self.in_search_text_area.send_text(query) self.close_keyboard_by_swipe() return self @allure.step("Clicking on a track card") def click_track_card(self, positon): self.lt_track_cards.select_by_position(positon) return TrackPage(self.driver, self.api) @allure.step("Clicking on track card for sony track") def click_track_card_for_sony_track(self, position): is_found = False locator = '//XCUIElementTypeOther[@name="swipeable-star"]/../../../../XCUIElementTypeOther[contains(@label,' ' "Added") and contains(@label, "SONY")]' self.scroll_down_to_tracks() element = None while not is_found: try: element = self.get_elements(locator, "xpath")[position] is_found = True except IndexError: self.scroll_down().scroll_down() element = self.get_elements(locator, "xpath")[position] self.driver.execute_script("mobile: scroll", {"element": element, "toVisible": True}) self.click_element(element=element) return TrackPage(self.driver, self.api) @allure.step("Swiping track in a track list left") def swipe_track_in_tracklist(self, position): self.lt_track_cards.get_item_by_position(position).swipe_left_long() return self @allure.step("Starring track in a track list") def star_track(self, position): self.click_element(self.STAR_LOCATOR.format(position), "xpath") self.lb_starred.wait(to_assert=False) return self @allure.step("Unstarring track in a track list") def unstar_track(self, position): self.click_element(self.STAR_LOCATOR.format(position), "xpath") self.lb_unstarred.wait(to_assert=False) return self @allure.step("Clicking 'x' to clear search field") def clear_playlists_search_field(self): self.bn_clear_search_icon.click() self.close_keyboard_by_swipe() return self @allure.step("Scrolling search results") def scroll_playlists_search_results(self): self.close_keyboard_by_swipe() self.driver.execute_script("mobile: scroll", {"direction": "down"}) self.driver.execute_script("mobile: scroll", {"direction": "down"}) self.driver.execute_script("mobile: scroll", {"direction": "down"}) return self @allure.step("Clicking i icon for personalized playlist") def click_personalized_playlist_i_icon(self): self.bn_personalized_playlist_i_icon.click() return self @allure.step("Clicking sorting icon") def click_sorting_icon(self): self.bn_sorting_icon.click() return self @allure.step("Selecting sorting") def select_sorting_by(self, sort_by): locator = '**/XCUIElementTypeOther[`label BEGINSWITH "{}"`][-1]'.format(sort_by) self.wait_for_element_clickable(locator, "class-chain") self.click_element(locator, "class-chain") self.bn_sorting_popup_done_button.click() return self @allure.step("Scroll to see stickable header") def scroll_down_to_tracks(self): size = self.driver.get_window_size() action = TouchAction(self.driver) action.long_press(x=size["width"] / 2, y=size["height"] * 0.55).move_to(x=size["width"] / 2, y=size["height"] * 0.3).release().perform() return self @allure.step("Collecting search results") def get_playlists_search_results(self): # hack due to gathering results before search query passed in case the issue is not with entering test itself time.sleep(2) return [ element.label for element in wait( lambda: self.lt_search_results.elements, ) ] @allure.step("Collecting main track data") def get_main_track_data(self): return self.lb_main_track_personalized.visible_text @allure.step("Collecting main track data to match with search item") def get_main_track_data_without_position_title_and_date(self): data = self.get_main_track_data().partition("STREAMS,") second_part_data = " ".join(data[2].split()[1:]) data_witout_date_position_title_and_icons = (data[0] + second_part_data).replace("POSITION", "").replace("\ue902", "\\ue902").replace("\ue905", "\\ue905").replace("\ue904", "\\ue904").replace("\ue90b", "") self.log.info(data_witout_date_position_title_and_icons) return data_witout_date_position_title_and_icons @allure.step("Collecting playlist headind data") def get_header_data(self): return self.lb_heading.text @allure.step("Getting name from the track card") def get_track_name(self, position): track = self.lt_track_cards.get_item_by_position(position) track.wait(to_assert=False) text = track.text start = 5 if text.startswith("SONY") else 0 end = text.find(" #") return text[start:end] @allure.step("Getting 'added days' values from tracks cards") def get_playlist_tracks_added_dates(self): tracks = wait(lambda: self.lt_playlist_tracks.elements) dates = [] for track in tracks: track_info = track.text.partition("#")[2] if "Added" in track_info and "Year" not in track_info: days_added_value = track.text.partition("Added")[2].split()[0] if days_added_value != "Today": dates.append(int(days_added_value)) elif "Added" in track_info and "Year" in track_info: days_added_value = track.text.partition("Added")[2].split()[0] if days_added_value != "Today": dates.append(int(days_added_value) * 365) return dates @allure.step("Getting data from the main track card") def get_main_track_date_added(self): track_data_started_from_date = self.lb_main_track.text.partition("Added")[2].split() return " ".join(track_data_started_from_date[:3]) @allure.step("Getting data from the main track card") def get_main_track_positions_and_trend(self): track_data_started_from_position = self.lb_main_track.text.partition("POSITION")[2].split() return " ".join(track_data_started_from_position[:3]) @allure.step("Getting data from the main track card") def get_main_track_positions(self): track_data_started_from_position = self.lb_main_track.text.partition("POSITION")[2].split() track_position = track_data_started_from_position[0] return track_position @allure.step("Getting 'added days' values from tracks cards") def get_positions_for_secondary_sorting(self): tracks = wait(lambda: self.lt_playlist_tracks.elements) track_positions_for_secondary_sorting_in_case_dates_not_available = [] for track in tracks: if track.text.partition("#")[2].split()[2] == "N/A": track_positions_for_secondary_sorting_in_case_dates_not_available.append(int(track.text.partition("#")[2].split()[0])) return track_positions_for_secondary_sorting_in_case_dates_not_available @allure.step("Getting position values from tracks cards") def get_playlist_tracks_positions(self): tracks = wait(lambda: self.lt_playlist_tracks.elements) positions = [int(track.text.partition("#")[2].split()[0]) for track in tracks] return positions @allure.step("Verifying playlists placements page is opened") def is_playlists_placements_page_opened(self): return self.lb_title.is_visible() @allure.step("Verifying playlists placements page is opened by name") def is_playlists_placements_page_opened_for_name(self, playlist_name): playlist_name_locator = f'**/XCUIElementTypeOther[`name == "card-playlist" and label CONTAINS "{playlist_name}"`]' self.wait_for_element_visible(playlist_name_locator, "class-chain") self.lb_title.is_visible() return self.is_element_displayed(playlist_name_locator, "class-chain") and self.lb_title.is_visible() @allure.step("Verifying search screen is opened") def is_playlists_search_screen_displayed(self): return self.bn_cancel.is_visible() @allure.step("Verifying personalized playlist info popup presence") def is_personalized_playlist_info_popup_shown(self): return self.is_element_displayed(*self.locator(self.playlist_placements_locators, "personalized_playlist_i_title")) and self.is_element_displayed(*self.locator(self.playlist_placements_locators, "personalized_playlist_i_content")) @allure.step("Verifying sorting popup presence") def is_sorting_popup_opened(self): self.bn_sorting_popup_done_button.wait(to_assert=False) return self.bn_sorting_popup_done_button.is_visible() @allure.step("Verifying sorting title represents current sorting") def is_selected_sorting_title_displayed(self, sorting_type): locator = '**/XCUIElementTypeStaticText[`label == "{}"`]'.format(sorting_type) return self.is_element_displayed(locator, "class-chain") @allure.step("Verifying scrolled header contains data from the main track") def is_scrolled_header_contains(self, main_track_data): return main_track_data in self.lb_scrolled_header.text