import logging import math import time from datetime import datetime, timedelta import allure from assertpy import assert_that from selenium.webdriver import ActionChains from ui_automation_framework.utils import AppConfig, CoreConfig from ui_automation_framework.utils import logger as cl from app.src.pages.track_page import TrackPage class PositionPlaylistPopup(TrackPage): log = cl.Logger(logging.DEBUG) export_button_id = "//*[contains(@id, 'window-export-btn')]" playlist_link_id = "//*[contains(@id, 'window-playlist-link')]" track_link_id = "//*[contains(@id, 'window-track-link')]" artists_link_id = "//*[contains(@id, 'window-artist-link')]" elements_selectors = ["title", "track-image-cover", "track-link", "account-link", "playlist-link", "artist-link", "day-picker", "export-btn", "day-count-label", "chart"] def __init__(self, driver): super().__init__(driver) self.driver = driver self.position_playlist_popup = self.get_page_locators("PositionPlaylistPopup", "position_in_playlist_popup_elements.json") self.trackPage = TrackPage(self.driver) @allure.step("Playlist in popup is opened") def is_popup_opened(self): self.is_popup_graph_displayed() header = self.wait_for_element_visible(*self.locator(self.position_playlist_popup, "header")) assert_that(self.is_element_displayed(element=header)).is_true() @allure.step("Playlist in popup graph is displayed") def is_popup_graph_displayed(self): self.wait_for_element_visible(*self.locator(self.position_playlist_popup, "graph_period_string")) @allure.step("Close Playlist in popup") def close_popup(self): self.click_element(*self.locator(self.position_playlist_popup, "close_icon")) self.page_has_loaded() time.sleep(3) @allure.step("Get playlist id from chart link") def get_id_from_playlist_link(self): self.page_has_loaded() self.wait_for_element_visible(*self.locator(self.position_playlist_popup, "playlist_link")) playlist = self.wait_for_element_clickable(*self.locator(self.position_playlist_popup, "playlist_link")) href_value = str(self.get_attribute_value(element=playlist, attribute="href")) self.log.info("href is: " + href_value) playlist_id = href_value.split("/")[-1].split("?")[0] self.log.info("playlist id from playlist link: " + playlist_id) return playlist_id @allure.step("Get track id from chart link") def get_id_from_track_link(self): self.page_has_loaded() self.wait_for_element_visible(self.track_link_id, "xpath") playlist = self.wait_for_element_clickable(self.track_link_id, "xpath") href_value = str(self.get_attribute_value(element=playlist, attribute="href")) self.log.info("href is: " + href_value) playlist_id = href_value.split("/")[-1].split("?")[0] self.log.info("track id from playlist link: " + playlist_id) return playlist_id @allure.step("PIP - open") def open_popup(self, type="spotify", index=0): detail_link_el = "//*[contains(@id, 'position')]/..//*[contains(@class, 'position')]" if "/playlist/" in self.get_url() and "/amazon/" not in self.get_url(): detail_link_el = "//*[contains(@id, 'details')]/span[text()='Details']" self.wait_for_element_clickable(detail_link_el, "xpath") link = self.get_elements(detail_link_el, "xpath")[index] self.log.info("Position pop-up: " + str(link)) self.click_element(element=link) self.is_popup_opened() @allure.step("PIP - get legend kpis") def get_legend_kpis(self): kpis = [0, 0] if self.is_element_displayed(*self.locator(self.position_playlist_popup, "legend_kpis")): self.wait_for_element_visible(*self.locator(self.position_playlist_popup, "legend_kpis")) kpis_el = self.get_elements(*self.locator(self.position_playlist_popup, "legend_kpis")) kpis = [int(self.get_text(element=k).replace(",", "").replace("N/A", "-1")) for k in kpis_el] return kpis @allure.step("PIP - get graph days count") def get_graph_period_displayed(self): selector = "//*[contains(@id, 'window-day-count-label')]" period_el = self.wait_for_element_visible(selector, "xpath") days = self.get_text(element=period_el) return int(days.split(" ")[0]) @allure.step("PIP - verify export") def verify_export(self, param): kpi_ui = self.get_legend_kpis() self.wait_for_element_visible(self.export_button_id, "xpath") export_button = self.wait_for_element_clickable(self.export_button_id, "xpath") self.click_on_element_js(element=export_button) track_name_ui = self.get_text(self.track_link_id, "xpath") playlist_name_ui = self.get_text(self.playlist_link_id, "xpath") file_path = f"""http://{CoreConfig.SELENOID_HOST}:4444/download/{self.driver.session_id}/Playlist Position - {track_name_ui} - {playlist_name_ui.replace(':', '_').replace("'", ' ')}.csv""" self.log.info(f"file path: {file_path}") data = self.read_from_csv(file_path) columns = [date, artists, track_name, track_uri, playlist_name, playlist_uri, position, tracks_in_playlist, ww_streams, m_streams] = ["Date", "Artists", "Track Name", "Track URI", "Playlist Name", "Playlist URI", "Position", "Tracks in Playlist", "Worldwide Streams", "Market Streams"] data_columns = list(data.keys()) assert_that(columns).is_equal_to(data_columns) file = {} for c in data_columns: file[c] = data.get(c).to_list() self.log.info(f"file: {file}") start_date, end_date = datetime.strptime(self.get_calendar_start_date(), "%Y-%m-%d").date(), datetime.strptime(self.get_calendar_end_date(), "%Y-%m-%d").date() dd = [str(start_date + timedelta(days=x)) for x in range((end_date - start_date).days + 1)] dd = [datetime.strptime(d, "%Y-%m-%d").strftime(self.trackPage.get_date_format_from_locale()) for d in dd] file_ww_streams = sum(f for f in file[ww_streams] if not math.isnan(f)) file_m_streams = sum(f for f in file[m_streams] if not math.isnan(f)) file_track_uri = [] for i in file[track_uri]: if isinstance(i, int): file_track_uri.append(str(i)) else: file_track_uri.append(str(i.split(":")[-1].split("/")[-1])) file_track_uri = set(file_track_uri) ui_track_uri = self.get_id_from_track_link() if not isinstance(ui_track_uri, int): ui_track_uri = str(ui_track_uri.split(":")[-1].split("/")[-1]) ui_track_uri = {ui_track_uri} assert_that(set(file[date])).is_subset_of(set(dd)) assert_that(set(a.replace(", ", ",") for a in file[artists])).is_equal_to({self.get_text(self.artists_link_id, "xpath").replace(", ", ",")}) assert_that(set(file[track_name])).is_equal_to({track_name_ui}) assert_that(file_track_uri).is_equal_to(ui_track_uri) assert_that(set(file[playlist_name])).is_equal_to({playlist_name_ui}) assert_that(set(str(p).split(":")[-1].split("?")[0].split("/")[-1] for p in file[playlist_uri])).is_equal_to({self.get_id_from_playlist_link()}) if "amazon_" in file[playlist_uri][0]: assert_that(file[playlist_uri][0].split("?")[-1]).described_as(file[playlist_uri][0]).starts_with("hd_filter_market=") assert_that(kpi_ui[0]).is_in(file_ww_streams, -1) if param != AppConfig.get("market")["global"]["api"]: assert_that(kpi_ui[1]).is_in(file_m_streams, -1) else: assert_that(file_m_streams).is_equal_to(0) @allure.step("PIP - verify export hover") def verify_export_hover(self): export = self.wait_for_element_clickable(self.export_button_id, "xpath") color_before = export.value_of_css_property("background-color") ActionChains(self.driver).move_to_element(to_element=export).perform() color_after = export.value_of_css_property("background-color") self.ts.markFinal(color_after != color_before, f"color: {color_before}\nafter: {color_after}") @allure.step("PIP - verify export button active") def verify_export_button_active(self, active=True): inactive = self.get_element(self.export_button_id, "xpath").get_attribute("disabled") if active: self.ts.markFinal(inactive is None, f"export button is active: {inactive}") else: self.ts.markFinal(inactive == "true", f"export button is inactive: {inactive}") @allure.step("PIP - verify elements") def verify_elements(self, amazon=False): for e in self.elements_selectors: if e == "account-link" and amazon: pass else: el = self.wait_for_element_visible(f"//*[contains(@id, 'window-{e}')]", "xpath") assert_that(self.is_element_displayed(element=el)).described_as(e).is_true() @allure.step("PIP - verify owner category") def verify_owner_category(self, owner_exp): el = self.wait_for_element_visible("//*[contains(@id, 'window-account-link')]", "xpath") owner = self.get_text(element=el) assert_that(owner).is_equal_to(owner_exp) @allure.step("PIP - verify chart elements") def verify_chart_elements(self, market): el_selectors = ["//*[text()='Position in Playlist']", "//*[contains(@class, 'subtitle') and text()='Stream count']", "//*[text()='Global streams in playlist']", "//*[contains(@id, 'peak-icon')]", "//*[contains(@id, 'min-icon')]"] if market != "_gl": el_selectors.append(f"//*[text()='{market} streams in playlist']") for el in el_selectors: e = self.wait_for_element_visible(el, "xpath") assert_that(self.is_element_displayed(element=e)).described_as(el).is_true() @allure.step("PIP - verify links") def verify_links(self, type=""): sels = ["track-link", "account-link", "playlist-link"] if type == "amazon": sels = ["track-link", "playlist-link"] elements = [self.wait_for_element_visible(f"//*[contains(@id, 'modal-window-{sel}')]", "xpath") for sel in sels] + [self.wait_for_element_visible("(//*[contains(@id, 'window-artist-link')]//../a)[1]", "xpath")] links = [self.get_attribute_value(element=el, attribute="href") for el in elements] self.log.info(f"links: {links}") if type == "amazon": assert_that(links[0]).described_as(links[0]).starts_with(f"{CoreConfig.ENV_BASE_URL}/spotify/") assert_that(links[1]).described_as(links[1]).starts_with(f"{CoreConfig.ENV_BASE_URL}/") assert_that(links[-1]).described_as(links[-1]).starts_with(f"{CoreConfig.ENV_BASE_URL}/spotify/") links_pure = [l.replace(f"{CoreConfig.ENV_BASE_URL}/", "").replace("spotify/", "").replace(f"{type}/", "").split("/")[0] for l in links] assert_that(links_pure).described_as(links_pure).is_equal_to(["track", "playlist", "artist"]) else: assert_that(all(str(l).startswith(f"{CoreConfig.ENV_BASE_URL}/spotify/") for l in links)).described_as(links).is_true() links_pure = [l.replace(f"{CoreConfig.ENV_BASE_URL}/spotify/", "").split("/")[0] for l in links] assert_that(links_pure).described_as(links_pure).is_equal_to(["track", "user", "playlist", "artist"]) @allure.step("PIP - verify no data") def verify_no_data(self): selector = """//*[text()="No data available."]""" no_data = self.wait_for_element_visible(selector, "xpath") assert_that(self.is_element_displayed(element=no_data)).is_true() @allure.step("PIP - get playlist id") def get_playlist_id(self): id_el = self.wait_for_element_clickable(self.playlist_link_id, "xpath") id = self.get_attribute_value(element=id_el, attribute="href").split("/")[-1] return id @allure.step("PIP - get metadata") def get_metadata(self): track_link = self.get_attribute_value(locator="//*[contains(@id, 'window-track-link')]", locator_type="xpath", attribute="href").split("/")[-1] playlist_link = self.get_attribute_value(locator="//*[contains(@id, 'playlist-link')]", locator_type="xpath", attribute="href").split("/")[-1].split("?")[0] return {"track_id": track_link, "playlist_id": playlist_link} @allure.step("Select period - today") def select_today_period(self): today_selector = "//*[contains(@class, 'today')][string-length(text()) > 0]" first = self.wait_for_element_visible(today_selector, "xpath") time.sleep(1) self.click_on_element_js(element=first) time.sleep(1) self.click_on_element_js(element=first) self.is_graph_period_displayed() @allure.step("Verify axes") def verify_axes(self): loc = "//*[contains(@class, 'highcharts-') and contains(@class, 'title') and not(contains(text(), '%'))][string-length(text()) > 0]" self.wait_for_element_visible(loc, "xpath") els = self.get_elements(loc, "xpath") axes_text = [self.get_text(element=e) for e in els] text_exp = set(list(["POSITION IN PLAYLIST", "STREAM COUNT", "DATE"])) assert_that(set(axes_text)).is_equal_to(text_exp) @allure.step("PIP - get amazon tooltip") def get_amazon_tooltip(self): param = self.graph_get_count_days_from_calendar() selector = "//*[contains(@class, 'highcharts-plot-background')]" self.is_graph_period_displayed() e = self.wait_for_element_visible(selector, "xpath") height = int(e.size["height"]) width = int(e.size["width"]) + 1 self.log.info("height: {height}, width: {width}".format(height=height, width=width)) step = int(width / param) self.log.info("step: {step}".format(step=step)) tooltip = "" for t in range(0, width, step): if len(tooltip) < 1: e = self.wait_for_element_visible(selector, "xpath") ActionChains(self.driver).move_to_element_with_offset(to_element=e, xoffset=t, yoffset=height).perform() t_el = self.wait_for_element_visible(self.hint_locator, "xpath") if t_el is not None: tel_text = self.get_text(element=t_el) if "DSP" in tel_text: self.log.info(f"tooltip text: {tel_text}") text = tel_text.replace("\nMinimum", "").replace("\nPeak", "") assert_that("DSP: Amazon" in text).described_as(text).is_true() assert_that("Position" in text).described_as(text).is_true() assert_that("Global Streams in Playlist" in text).described_as(text).is_true() assert_that(" tracks" in text).described_as(text).is_true() assert_that(" of " in text).described_as(text).is_true() if text.strip() not in tooltip: tooltip = { "date": datetime.strptime(text.split(", ")[1].split("\n")[0], self.get_date_format_from_locale()).date(), "position": int(text.split(" of")[0].split("\n")[-1].lower().replace("\n", "").replace("peak", "").replace("minimum", "")), "global_streams": int(text.split("\n")[-3].replace(",", "")), "local_streams": int(text.split("\n")[-2].replace(",", "")), "percentage": int(text.split("(")[-1].split("%")[0]), } self.log.info("tooltip text: {}".format(tooltip)) return tooltip @allure.step("PIP - get playlist and track names") def get_playlist_track_names(self): playlist_loc = "//*[contains(@id, 'playlist-link')]" track_link = "//*[contains(@id, 'track-link')]" playlist = self.get_text(element=self.wait_for_element_visible(playlist_loc, "xpath")) track = self.get_text(element=self.wait_for_element_visible(track_link, "xpath")) return {"playlist": playlist, "track": track} @allure.step("PIP - verify non clickable elements") def verify_non_clickable_elements(self): locs = ["//*[contains(@id, 'track-image-cover')]", "//*[contains(@id, 'playlist-image-cover')]", "(//span[text()=' • Spotify'])[last()]"] url_before = self.get_url() for loc in locs: self.click_element(loc, "xpath") assert_that(url_before).described_as(loc).is_equal_to(self.get_url()) @allure.step("PIP - get spotify tooltip") def get_spotify_tooltip(self, market="global"): param = self.graph_get_count_days_from_calendar() selector = "//*[contains(@class, 'highcharts-plot-background')]" self.is_graph_period_displayed() e = self.wait_for_element_visible(selector, "xpath") height = int(e.size["height"]) width = int(e.size["width"]) + 1 self.log.info("height: {height}, width: {width}".format(height=height, width=width)) step = int(width / param) self.log.info("step: {step}".format(step=step)) tooltip = "" for t in range(0, width, step): if len(tooltip) < 1: e = self.wait_for_element_visible(selector, "xpath") ActionChains(self.driver).move_to_element_with_offset(to_element=e, xoffset=t, yoffset=height).perform() t_el = self.wait_for_element_visible(self.hint_locator, "xpath") if t_el is not None: tel_text = self.get_text(element=t_el) if "DSP" in tel_text: self.log.info(f"tooltip text: {tel_text}") text = tel_text.replace("\nMinimum", "").replace("\nPeak", "") assert_that("DSP: Spotify" in text).described_as(text).is_true() assert_that("Position" in text).described_as(text).is_true() assert_that("Global Streams in Playlist" in text).described_as(text).is_true() assert_that(" tracks" in text).described_as(text).is_true() assert_that(" of " in text).described_as(text).is_true() if text.strip() not in tooltip: if market == AppConfig.get("market")["global"]["market"]: tooltip = { "date": datetime.strptime(text.split(", ")[1].split("\n")[0], self.get_date_format_from_locale()).date(), "position": int(text.split(" of")[0].split("\n")[-1].lower().replace("\n", "").replace("peak", "").replace("minimum", "")), "global_streams": int(text.split("\n")[-1].replace(",", "")), "local_streams": -1, "percentage": -1, } else: tooltip = { "date": datetime.strptime(text.split(", ")[1].split("\n")[0], self.get_date_format_from_locale()).date(), "position": int(text.split(" of")[0].split("\n")[-1].lower().replace("\n", "").replace("peak", "").replace("minimum", "")), "global_streams": int(text.split("\n")[-3].replace(",", "")), "local_streams": int(text.split("\n")[-2].replace(",", "")), "percentage": int(text.split("(")[-1].split("%")[0]), } self.log.info("tooltip text: {}".format(tooltip)) return tooltip @allure.step("PIP - verify popup personalized") def is_popup_personalized(self): locs = ["//*[contains(@class, 'icon-warning')]", "//*[text()='The personalized playlist data is not accurate.']"] for loc in locs: assert_that(self.is_element_displayed(loc, "xpath")).described_as(loc).is_true()