import logging import time from contextlib import suppress 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 waiting import TimeoutExpired from app.src.helpers.waiting_wrapper import wait from app.src.pages.navigation_page import Navigation from app.src.pages.track_page import TrackPage from app.src.ui_elements.button_ui_element import ButtonUIElement from app.src.ui_elements.label_ui_element import LabelUIElement from app.src.ui_elements.list_ui_element import ListUIElement class StarredPage(Navigation): log = cl.Logger(logging.DEBUG) STAR_LOCATOR = '(//XCUIElementTypeOther[@name="swipeable-star"])[{}]' STARRED_TRACKS = "**/XCUIElementTypeOther[`name=='swipeable-list'`]/XCUIElementTypeOther" STARRED_PLAYLISTS = "**/XCUIElementTypeOther[`name=='swipeable-playlist'`]/XCUIElementTypeOther" def __init__(self, driver, api_client): super().__init__(driver) self.api = api_client self.driver = driver self.starred_locators = self.get_page_locators("StarredPage", "starred_elements.json") @property def bn_tracks(self): return ButtonUIElement(self.driver, self.locator(self.starred_locators, "tracks_tab")) @property def bn_playlists(self): return ButtonUIElement(self.driver, self.locator(self.starred_locators, "playlists_tab")) @property def lt_tracks(self): return ListUIElement( self.driver, ( MobileBy.IOS_CLASS_CHAIN, '**/XCUIElementTypeOther[`name == "swipeable-list"`]/XCUIElementTypeOther/XCUIElementTypeOther[{}]', ), all_elements_locator=(MobileBy.IOS_CLASS_CHAIN, self.STARRED_TRACKS), ) @property def lt_playlists(self): return ListUIElement( self.driver, ( MobileBy.IOS_CLASS_CHAIN, '**/XCUIElementTypeOther[`name == "swipeable-playlist"`]/XCUIElementTypeOther/XCUIElementTypeOther[{}]', ), all_elements_locator=(MobileBy.IOS_CLASS_CHAIN, self.STARRED_PLAYLISTS), ) @property def lt_stars(self): return ListUIElement(self.driver, (MobileBy.XPATH, self.STAR_LOCATOR)) @property def bn_search_for_tracks(self): return ButtonUIElement(self.driver, self.locator(self.starred_locators, "search_for_tracks_btn")) @property def bn_header(self): return ButtonUIElement(self.driver, self.locator(self.starred_locators, "header")) @property def lb_unstarred(self): return LabelUIElement(self.driver, self.locator(self.starred_locators, "unstarred_label")) @property def lb_7_days_period_title(self): return LabelUIElement(self.driver, self.locator(self.starred_locators, "7-days_period_title")) @allure.step("Clicking on the tracks tab") def click_on_tracks_tab(self): self.bn_tracks.click() return self @allure.step("Clicking on the playlists tab") def click_on_playlists_tab(self): self.bn_playlists.click() return self @allure.step("Clicking on a track from starred tracks by name") def click_on_starred_track(self, track_name): locator = f"**/XCUIElementTypeOther[`name=='swipeable-list'`]/" f"XCUIElementTypeOther[`label CONTAINS '{track_name}'`]" self.wait_for_element_visible(locator, "class-chain") self.click_element(locator, "class-chain") return TrackPage(self.driver, self.api) @allure.step("Clicking on a track from starred tracks by position") def click_on_starred_track_by_position(self, position): locator = f"**/XCUIElementTypeOther[`name=='swipeable-list'`]/" f"XCUIElementTypeOther[{position}]" size = self.driver.get_window_size() TouchAction(self.driver).long_press(x=size["width"] / 2, y=size["height"] / 2).move_to(x=size["width"] / 2, y=size["height"] / 2 - 65).release().perform() self.click_element(locator, "class-chain") return TrackPage(self.driver, self.api) @allure.step("Opening markets popup") def click_market_icon(self): # Workaround for weird behavior when screen is blinking and search page is opened on locator usage time.sleep(2) size = self.driver.get_window_size() x = size["width"] * 0.42 y = size["height"] * 0.08 TouchAction(self.driver).tap(x=x, y=y).perform() return self @allure.step("Clicking 'search for tracks' button") def click_search_for_tracks_btn(self): self.bn_search_for_tracks.click() return self @allure.step("Clicking on starred tracks screen header") def click_on_header(self): self.bn_header.click() return self @allure.step("Getting track title on starred tracks screen") def scroll_on_the_starred_tracks(self): 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("Getting track title on starred playlists screen") def scroll_on_the_starred_playlists(self): 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("Closing popup by swipe") def close_market_popup_by_swipe(self): action = TouchAction(self.driver) element = self.get_element(*self.locator(self.starred_locators, "market_value")) size = element.location action.long_press(x=size["x"] + 10, y=size["y"] * 0.1).move_to(x=size["x"] + 10, y=size["y"] * 0.02).release().perform() @allure.step("Unstarring track on starred page") def unstar_track(self, track_name): locator = f"**/XCUIElementTypeOther[`name=='swipeable-list'`]/XCUIElementTypeOther[`label CONTAINS '{track_name}'`]" star_locator = f"**/XCUIElementTypeOther[`name=='swipeable-list'`]/XCUIElementTypeOther[`label CONTAINS '{track_name}'`]/**/XCUIElementTypeOther[`name=='swipeable-star'`]" size = self.driver.get_window_size() self.close_keyboard_by_swipe() first_track = self.get_element(locator, "class-chain") location = first_track.location TouchAction(self.driver).long_press(x=size["width"] * 0.9, y=location["y"] + 30).move_to(x=size["width"] * 0.07, y=location["y"] + 30).release().perform() self.click_element(star_locator, "class-chain") self.lb_unstarred.wait(to_assert=False) @allure.step("Getting streams dates for starred track") def get_dates_range(self, track_name): locator = f"**/XCUIElementTypeOther[`name == 'swipeable-list'`]/XCUIElementTypeOther[`label CONTAINS '–' AND label CONTAINS \"{track_name}\"`]" self.wait_for_element_visible(locator, "class-chain") text = self.get_text(locator, "class-chain").split() self.log.info(text) return text[-1] @allure.step("Getting count of starred tracks") def get_starred_tracks_count(self): starred_tracks = self.lt_tracks.elements self.log.info(len(starred_tracks)) return len(starred_tracks) @allure.step("Getting count of starred playlists") def get_starred_playlists_count(self): starred_playlists = self.lt_playlists.elements self.log.info(len(starred_playlists)) return len(starred_playlists) @allure.step("Getting starred tracks data") def get_starred_tracks_data(self): return [track.text for track in self.lt_tracks.elements] @allure.step("Getting track title on starred tracks screen") def get_starred_track_title(self, position): locator = f"**/XCUIElementTypeOther[`name=='swipeable-list'`]/XCUIElementTypeOther[{position}]" self.wait_for_element_visible(locator, "class-chain") title = self.get_attribute_value(locator, "class-chain", None, "label").replace("\ue923", "")[1:] self.log.info(title) return title @allure.step("Getting playlist title on starred playlists screen") def get_starred_playlist_title(self, position): locator = f"**/XCUIElementTypeOther[`name=='swipeable-playlist'`]/XCUIElementTypeOther[{position}]" self.wait_for_element_visible(locator, "class-chain") title = self.get_attribute_value(locator, "class-chain", None, "label").replace("\ue923", "")[1:] self.log.info(title) return title @allure.step("Getting market value from popup") def get_market(self): self.wait_for_element_present(*self.locator(self.starred_locators, "market_value")) value = self.get_text(*self.locator(self.starred_locators, "market_value")) self.log.info(value) return value @allure.step("Getting streams data for starred track") def get_streams_number(self, position): el = self.lt_tracks.get_item_by_position(position) wait(el.is_visible) return el.text.partition("STREAMS")[0].split()[-1] @allure.step("Verifying track is presented on starred tracks page") def is_track_starred(self, track_name): locator = f"**/XCUIElementTypeOther[`name=='swipeable-list'`]/XCUIElementTypeOther/XCUIElementTypeOther[`label CONTAINS[c] \"{track_name}\"`]" element = self.wait_for_element_visible(locator, "class-chain") return self.is_element_displayed(element=element) @allure.step("Verifying track is not presented on starred tracks page") def is_track_unstarred(self, track_name): locator = f"**/XCUIElementTypeOther[`name=='swipeable-list'`]/XCUIElementTypeOther/XCUIElementTypeOther[`label CONTAINS \"{track_name}\"`]" element = self.wait_for_element_visible(locator, "class-chain") return not self.is_element_displayed(element=element) @allure.step("Checking if numbers on global and local markets matching") def are_number_of_tracks_match(self, global_tracks, market_tracks): self.log.info("Number on global: " + str(global_tracks)) self.log.info("Number on market: " + str(market_tracks)) return global_tracks == market_tracks @allure.step("Verifying screen is in empty state") def is_search_for_tracks_button_displayed(self): return self.bn_search_for_tracks.is_visible() @allure.step("Verifying 7-days title is displayed") def is_7_days_title_displayed(self): self.lb_7_days_period_title.wait(to_assert=False) return self.lb_7_days_period_title.is_visible() @allure.step("Clear starred tracks page") def unstar_all(self): with suppress(TimeoutExpired): wait(lambda: self.lt_tracks.elements) tracks_count = self.get_starred_tracks_count() for i in range(tracks_count): self.lt_tracks.get_item_by_position(1).swipe_left_long() self.lt_stars.get_item_by_position(1).click() self.lb_unstarred.wait(to_assert=False) return self @allure.step("Verifying playlist is presented on starred page") def is_playlist_starred(self, playlist_name): locator = f"**/XCUIElementTypeOther[`name=='swipeable-playlist'`]/XCUIElementTypeOther/XCUIElementTypeOther[`name CONTAINS[c] \"{playlist_name}\"`]" element = self.wait_for_element_visible(locator, "class-chain") return self.is_element_displayed(element=element)