import logging import time import allure from appium.webdriver.common.mobileby import MobileBy from ui_automation_framework.utils import logger as cl from app.mysql_db_client_custom import MySqlClient from app.src.helpers.waiting_wrapper import wait from app.src.pages.navigation_page import Navigation from app.src.pages.playlist_page import PlaylistPage 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 SearchPage(Navigation): log = cl.Logger(logging.DEBUG) # Dynamic XPATH locators not suitable for usage in Pages json object =============================================== SEARCH_RESULT = "**/XCUIElementTypeOther[`name == 'swipeable-list'`]/XCUIElementTypeOther/XCUIElementTypeOther" PLAYLIST_SEARCH_RESULT = "**/XCUIElementTypeOther[`name == 'swipeable-playlist'`]/XCUIElementTypeOther/XCUIElementTypeOther" TRACK_IN_RESULTS_BY_POSITION_LOCATOR = "**/XCUIElementTypeOther[`name == 'swipeable-list'`]/XCUIElementTypeOther/XCUIElementTypeOther[{}]" APPLE_PLAYLIST_IN_RESULTS_BY_NAME_LOCATOR = "**/XCUIElementTypeOther[`name == 'swipeable-playlist'`]/XCUIElementTypeOther/XCUIElementTypeOther[`label == \"\ue91F {}\"`]" PLAYLIST_IN_RESULTS_BY_POSITION_LOCATOR = '**/XCUIElementTypeOther[`name == "swipeable-playlist"`]/XCUIElementTypeOther/XCUIElementTypeOther[{}]' TRACK_IN_RESULTS_BY_NAME_LOCATOR = "**/XCUIElementTypeOther[`name == 'swipeable-list'`]/XCUIElementTypeOther/XCUIElementTypeOther[`label CONTAINS " '"{}"`]' STAR_LOCATOR_BY_NAME = "**/XCUIElementTypeOther[`name =='swipeable-list'`]/XCUIElementTypeOther/XCUIElementTypeOther[`label CONTAINS" ' "{}"`]/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther[`name == ' "'swipeable-star'`]" STAR_PLAYLIST_LOCATOR_BY_NAME = "**/XCUIElementTypeOther[`name =='swipeable-playlist'`]/XCUIElementTypeOther/XCUIElementTypeOther[`label CONTAINS" ' "{}"`]/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther[`name == ' "'swipeable-star'`]" STAR_LOCATOR_BY_POSITION = "**/XCUIElementTypeOther[`name == 'swipeable-star'`][{}]" STARRED_TRACK_LOCATOR = "**/XCUIElementTypeOther[`name =='swipeable-list'`]/XCUIElementTypeOther/XCUIElementTypeOther[`label BEGINSWITH" " ' {}'`]" PLAYLIST_IN_RESULTS_BY_TEXT_LOCATOR = '**/XCUIElementTypeOther[`name == "swipeable-playlist"`]/XCUIElementTypeOther/XCUIElementTypeOther/XCUIElementTypeOther[`name BEGINSWITH "{} By"`]' def __init__(self, driver, api_client): super().__init__(driver) self.api = api_client self.driver = driver self.search_locators = self.get_page_locators("SearchPage", "search_elements.json") self.sql_client = MySqlClient() @property def bn_playlists(self): return ButtonUIElement(self.driver, self.locator(self.search_locators, "playlists_tab"), "Playlists tab") @property def lt_playlist_search_results(self): return ListUIElement( self.driver, all_elements_locator=(MobileBy.IOS_CLASS_CHAIN, self.PLAYLIST_SEARCH_RESULT), by_position_locator=(MobileBy.IOS_CLASS_CHAIN, self.PLAYLIST_IN_RESULTS_BY_POSITION_LOCATOR), by_text_locator=(MobileBy.IOS_CLASS_CHAIN, self.PLAYLIST_IN_RESULTS_BY_TEXT_LOCATOR), ) @property def lt_track_search_results(self): return ListUIElement( self.driver, all_elements_locator=(MobileBy.IOS_CLASS_CHAIN, self.SEARCH_RESULT), by_position_locator=(MobileBy.IOS_CLASS_CHAIN, self.TRACK_IN_RESULTS_BY_POSITION_LOCATOR), by_text_locator=(MobileBy.IOS_CLASS_CHAIN, self.TRACK_IN_RESULTS_BY_NAME_LOCATOR), ) @property def lt_star_tracks(self): return ListUIElement( self.driver, (MobileBy.IOS_CLASS_CHAIN, self.STAR_LOCATOR_BY_POSITION), (MobileBy.IOS_CLASS_CHAIN, self.STAR_LOCATOR_BY_NAME), ) @property def lt_star_playlists(self): return ListUIElement( self.driver, (MobileBy.IOS_CLASS_CHAIN, self.STAR_LOCATOR_BY_POSITION), (MobileBy.IOS_CLASS_CHAIN, self.STAR_PLAYLIST_LOCATOR_BY_NAME), ) @property def in_search(self): return InputUIElement(self.driver, self.locator(self.search_locators, "search_field"), "Search") @property def bn_clear(self): return ButtonUIElement(self.driver, self.locator(self.search_locators, "clear_button")) @property def lb_starred(self): return LabelUIElement(self.driver, self.locator(self.search_locators, "starred_label")) @property def lb_unstarred(self): return LabelUIElement(self.driver, self.locator(self.search_locators, "unstarred_label")) @property def lb_no_results(self): return LabelUIElement(self.driver, self.locator(self.search_locators, "no_results")) @property def lb_recent_search(self): return LabelUIElement(self.driver, self.locator(self.search_locators, "recent_searches_label")) @property def lb_search_for_track_placeholder(self): return LabelUIElement(self.driver, self.locator(self.search_locators, "search_for_track_placeholder")) @property def bn_filter(self): return ButtonUIElement(self.driver, self.locator(self.search_locators, "filter_button")) @property def bn_spotify_filter_option(self): return ButtonUIElement(self.driver, self.locator(self.search_locators, "spotify_option_filter_button")) @property def bn_apple_filter_option(self): return ButtonUIElement(self.driver, self.locator(self.search_locators, "apple_option_filter_button")) @property def bn_amazon_filter_option(self): return ButtonUIElement(self.driver, self.locator(self.search_locators, "amazon_option_filter_button")) @property def bn_done_fiter_popup(self): return ButtonUIElement(self.driver, self.locator(self.search_locators, "fiter_popup_done_button")) @allure.step("Searching track via passed text") def search_for(self, text): self.in_search.send_text(text) # self.close_keyboard_by_swipe() return self @allure.step("Clicking on the playlists tab") def click_playlists_tab(self): self.bn_playlists.click() return self @allure.step("Clicking on the search field") def click_search_field(self): self.in_search.click() return self @allure.step("Clearing the search field") def clear_search_field(self): self.bn_clear.click() self.close_keyboard_by_swipe() return self @allure.step("Editing the search query") def update_search_query(self, query): self.in_search.clear_text() self.in_search.send_text(query) @allure.step("Opening a track page from results") def open_track_page_from_results(self, position): self.lt_track_search_results.select_by_position(position) # workaround for search tests to update search history time.sleep(2) return TrackPage(self.driver, self.api) @allure.step("Opening a track page from results") def open_track_page_from_results_by_text(self, text): self.lt_track_search_results.select_by_text(text) # workaround for search tests to update search history time.sleep(2) return TrackPage(self.driver, self.api) @allure.step("Opening a playlist page from results") def open_playlist_page_from_results(self, position): self.lt_playlist_search_results.select_by_position(position, False) # workaround for search tests to update search history time.sleep(2) return PlaylistPage(self.driver, self.api) @allure.step("Opening a playlist page from results") def open_playlist_page_from_results_by_text(self, text): self.lt_playlist_search_results.select_by_text(text) # workaround for search tests to update search history time.sleep(2) return PlaylistPage(self.driver, self.api) @allure.step("Scrolling down the search results") def scroll_down_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"}) self.driver.execute_script("mobile: scroll", {"direction": "down"}) self.driver.execute_script("mobile: scroll", {"direction": "down"}) @allure.step("Swiping on a track from search results") def swipe_track_on_search_results(self, position): self.close_keyboard_by_swipe() self.lt_track_search_results.get_item_by_position(position).swipe_left_long() return self @allure.step("Swiping on a track from search results") def swipe_track_on_search_results_by_name(self, name): self.close_keyboard_by_swipe() self.lt_track_search_results.get_item_by_text(name).swipe_left_long() return self @allure.step("Swiping on a track from search results right direction") def swipe_right_track_on_search_results(self, position): self.close_keyboard_by_swipe() self.lt_track_search_results.get_item_by_position(position).swipe_right_long() return self @allure.step("Click to star for the track in results") def star_on_search_results(self, position): self.lt_star_tracks.select_by_position(position) self.lb_starred.wait(to_assert=False) return self @allure.step("Click to star for the track in results") def star_track_on_search_results_by_name(self, name): self.lt_star_tracks.select_by_text(name) self.lb_starred.wait(to_assert=False) return self @allure.step("Click to star for the track in results") def star_playlist_on_search_results_by_name(self, name): self.lt_star_playlists.select_by_text(name) self.lb_starred.wait(to_assert=False) return self @allure.step("Click to unstar for the track in results") def unstar_track_on_search_results_by_name(self, name): self.lt_star_tracks.select_by_text(name) self.lb_unstarred.wait(to_assert=False) return self @allure.step("Click to unstar for the playlist in results") def unstar_playlist_on_search_results_by_name(self, name): self.lt_star_playlists.select_by_text(name) self.lb_unstarred.wait(to_assert=False) return self @allure.step("Click to unstar for the track in results") def unstar_on_search_results(self, position): self.lt_star_tracks.select_by_position(position) self.lb_unstarred.wait(to_assert=False) return self @allure.step("Swiping on a playlist from search results") def swipe_playlist_on_search_results(self, position): self.close_keyboard_by_swipe() self.lt_playlist_search_results.get_item_by_position(position).swipe_left_long() return self @allure.step("Swiping on a playlist from search results") def swipe_playlist_on_search_results_by_name(self, name): self.close_keyboard_by_swipe() self.lt_playlist_search_results.get_item_by_text(name).swipe_left_long() return self @allure.step("Swiping on a playlist from search results right direction") def swipe_right_playlist_on_search_results(self, position): self.close_keyboard_by_swipe() self.lt_playlist_search_results.get_item_by_position(position).swipe_right_long() return self @allure.step("Getting search results title by position") def get_result_track_title(self, position): track = self.lt_track_search_results.get_item_by_position(position) track.wait(to_assert=False) wait(track.is_enabled) title = track.label.replace("\ue923", "").replace("\uE90B ", "").replace("\uE905 ", "").replace("\uE91A ", "").split() if len(title) < 3: return title[0] + " " + title[1] else: return title[1] + " " + title[2] @allure.step("Getting search results title and name by position") def get_result_playlists_title(self, position): playlist = self.lt_playlist_search_results.get_item_by_position(position) playlist.wait(to_assert=False) wait(playlist.is_enabled) title = playlist.label.split() if len(title) < 3: return title[0] + " " + title[1] else: return title[1] + " " + title[2] @allure.step("Getting search results title and name by position") def get_result_track_title_full(self, position): track = self.lt_track_search_results.get_item_by_position(position) wait(track.is_enabled) title = track.label.replace("\ue923", "").replace("\uE90B ", "").replace("\uE905 ", "").replace("\uE91A ", "") return title @allure.step("Getting search results title and name by position") def get_result_playlists_title_full(self, position): playlist = self.lt_playlist_search_results.get_item_by_position(position) wait(playlist.is_enabled) return playlist.label.lower() @allure.step("Getting search results") def get_search_results(self): return [e.label for e in wait(lambda: self.lt_track_search_results.elements)] @allure.step("Getting playlist search results") def get_playlist_search_results(self): return [e.label for e in wait(lambda: self.lt_playlist_search_results.elements)] @allure.step("Getting playlist search results") def get_playlist_search_results_without_icons(self): return [element.label.replace("\ue91e ", "").replace("\ue91f ", "") for element in wait(lambda: self.lt_playlist_search_results.elements)] @allure.step("Getting value from search query") def get_search_query_text(self): return self.in_search.text @allure.step("Verifying 'no results' message is displayed") def is_no_results_shown(self): self.lb_no_results.wait(to_assert=False) return self.lb_no_results.is_visible() @allure.step("Verifying there are no duplicates in results") def are_no_duplicates_in_results(self, results_ui, results_api): # duplicates are hidden on ui, and no results from the next offset will be shown until using of pagination # so the length should match self.log.info(len(results_ui)) self.log.info(len(results_api)) return len(results_ui) == len(results_api) @allure.step("Verifying star icon is displayed for track in the results") def is_star_icon_displayed(self, track_name): track = self.lt_star_tracks.get_item_by_text(track_name) track.wait(to_assert=False) return track.is_visible() @allure.step("Verifying star icon is closed for track in the results") def is_star_icon_closed(self, track_name): return not self.lt_star_tracks.get_item_by_text(track_name).is_visible() @allure.step("Verifying the track is starred on the search results") def is_track_starred(self, track_name): track_locator = self.STARRED_TRACK_LOCATOR.format(track_name[:10]) self.wait_for_element_visible(track_locator, "class-chain") return self.get_element(track_locator, "class-chain").is_displayed() @allure.step("Verifying the track is starred on the search results") def is_track_unstarred(self, track_name): track_locator = self.STARRED_TRACK_LOCATOR.format(track_name[:10]) self.wait_for_element_visible(track_locator, "class-chain") return not self.is_element_displayed(track_locator, "class-chain") @allure.step("Verifying the presence of recent searches") def is_recent_search_displayed(self): return self.lb_recent_search.is_visible() @allure.step("Verifying firs track is scrolled") def is_track_scrolled(self): return not self.lt_playlist_search_results.get_item_by_position(1).is_visible() @allure.step("Verifying tracks tab is selected") def is_tracks_tab_selected(self): return self.lb_search_for_track_placeholder.is_visible() @allure.step("Filter playlists by specific DSP") def filter_playlists_by_dsp(self, desired_dsp: set): dsp_buttons = {"Spotify": self.bn_spotify_filter_option, "Apple": self.bn_apple_filter_option, "Amazon": self.bn_amazon_filter_option} self.bn_filter.click() initial_dsp_collection = {"Spotify", "Apple", "Amazon"} dsp_for_unchecking = initial_dsp_collection.difference(desired_dsp) for dsp in dsp_for_unchecking: dsp_buttons[dsp].click() self.bn_done_fiter_popup.click()