import logging from datetime import datetime, timedelta import allure from assertpy import assert_that from selenium.webdriver import ActionChains from ui_automation_framework.core import BasePage, TestStatus from ui_automation_framework.utils import AppConfig, CoreConfig from ui_automation_framework.utils import logger as cl from app.api_client import ApiClient from app.mysql_db_client_custom import MySqlClient from app.src.pages.apple_playlist_page import ApplePlaylistPage from app.src.pages.artist_page import ArtistPage from app.src.pages.track_page import TrackPage class GTPCPOTPopup(BasePage): log = cl.Logger(logging.DEBUG) TOOLTIP_POSITION = "Position" TOOLTIP_STREAMS = "Streams" TOOLTIP_GLOBAL = "Global Chart" TOOLTIP_MARKET = "{} Chart" TOOLTIP_DSP_SPOTIFY = "DSP: Spotify" TOOLTIP_MINIMUM = "Minimum" TOOLTIP_PEAK = "Peak" dots_colors = ["34,150,243,1", "75,175,80,1"] def __init__(self, driver): super().__init__(driver) self.driver = driver self.gtp_cpot_locators = self.get_page_locators("GTPCPOTpopup", "gtp_chart_position_over_time_popup.json") self.tp = TrackPage(self.driver) self.ap = ApplePlaylistPage(self.driver) self.artist_page = ArtistPage(self.driver) self.sql_client = MySqlClient() self.api_client = ApiClient() self.ts = TestStatus(self.driver) @allure.step("Verify GTP CPOT popup opened") def is_modal_opened(self, open=True): title = self.is_element_displayed(*self.locator(self.gtp_cpot_locators, "title")) == open self.ts.markFinal(title, "CPOT popup is opened") @allure.step("Close CPOT popup") def close_popup(self): self.click_element(*self.locator(self.gtp_cpot_locators, "gtp-chart-position-modal-window-close-icon")) @allure.step("Verify CPOT elements") def verify_elements(self): for e in [ "spotify-icon", "title_text", "track_art", "track_name", "artist_name", "datepicker", "export_button", "chart_position", "stream_count", "legend_kpi", ]: d = self.wait_for_element_visible(*self.locator(self.gtp_cpot_locators, e)) self.ts.markFinal(self.is_element_displayed(element=d), f"element in cpot: {e} is displayed") @allure.step("CPOT click track name and verify") def click_track_name_verify(self): name_el = self.wait_for_element_clickable(*self.locator(self.gtp_cpot_locators, "track_name")) text = self.get_text(element=name_el) self.click_element(element=name_el) self.tp.is_track_page_title_present(text) self.go_back() return text @allure.step("CPOT click artist name and verify") def click_artist_name_verify(self): self.wait_for_element_clickable(*self.locator(self.gtp_cpot_locators, "artist_name")) name_el = self.get_elements(*self.locator(self.gtp_cpot_locators, "artist_name"))[0] text = self.get_text(element=name_el) self.click_element(element=name_el) self.artist_page.verify_title(text) self.go_back() @allure.step("CPOT verify cp and sc yaxis and headers") def verify_cp_sc_areas(self): for e in ["cp", "sc"]: for s in ["yaxis", "scale"]: d1 = self.wait_for_element_visible(*self.locator(self.gtp_cpot_locators, f"{e}_{s}")) el_d1 = self.is_element_displayed(element=d1) self.ts.markFinal(el_d1, f"{e}_{s} is displayed") d2 = self.wait_for_element_visible(*self.locator(self.gtp_cpot_locators, "xaxis")) el_d2 = self.is_element_displayed(element=d2) self.ts.markFinal(el_d2, f"{e}_{s} is displayed") self.wait_for_element_visible(*self.locator(self.gtp_cpot_locators, "xaxis")) d2 = self.get_elements(*self.locator(self.gtp_cpot_locators, "xaxis")) d2 = [self.get_text(element=d) for d in d2 if self.is_element_displayed(element=d)] self.ts.markFinal("DATE" in d2, f"dates are in {d2}") @allure.step("CPOT - get days count from day picker") def get_days_count_from_day_picker(self): start_t = self.get_text(*self.locator(self.gtp_cpot_locators, "datepicker")).split("–")[0] end_t = self.get_text(*self.locator(self.gtp_cpot_locators, "datepicker")).split("–")[1] start = datetime.strptime(start_t, AppConfig.get("date_formats")["mm/dd/yy"]).date() end = datetime.strptime(end_t, AppConfig.get("date_formats")["mm/dd/yy"]).date() days_count_exp = (end - start).days + 1 return days_count_exp @allure.step("CPOT get tooltips") def get_tooltips(self, market): tooltips = [] tooltips_parsed = [] self.wait_for_element_visible(*self.locator(self.gtp_cpot_locators, "xaxis")) self.wait_for_element_clickable(*self.locator(self.gtp_cpot_locators, "xaxis")) dots = self.get_elements(*self.locator(self.gtp_cpot_locators, "xaxis")) dots = [d for d in dots if self.is_element_displayed(element=d)] for d in dots: ActionChains(self.driver).move_to_element(to_element=d).perform() tooltip_el = self.wait_for_element_visible(*self.locator(self.gtp_cpot_locators, "tooltip")) if tooltip_el not in [None, "None", ["None"], [None]]: date = self.get_text(*self.locator(self.gtp_cpot_locators, "tooltip_date")).split(" ")[-1] date = datetime.strptime(date, self.tp.get_date_format_from_locale()).date() positions = self.get_elements(*self.locator(self.gtp_cpot_locators, "tooltip_positions")) streams = self.get_elements(*self.locator(self.gtp_cpot_locators, "tooltip_streams")) g_position = -1 m_position = -1 m_streams = -1 if self.get_text(element=positions[0]) != "–": g_position = int(self.get_text(element=positions[0])) if market != AppConfig.get("market")["global"]["name"]: if self.get_text(element=positions[1]) != "–": m_position = int(self.get_text(element=positions[1])) m_streams = int(self.get_text(element=streams[1]).replace(",", "").replace("–", "0")) tool = { "date": date, "g_position": g_position, "m_position": m_position, "g_streams": int(self.get_text(element=streams[0]).replace(",", "").replace("–", "0")), "m_streams": m_streams, } if tool not in tooltips_parsed: tooltips_parsed.append(tool) tooltips.append(self.get_text(element=tooltip_el)) self.log.info("tooltip text: {}".format(self.get_text(element=tooltip_el))) self.log.info("CPOT tooltips content: {}".format(tooltips)) self.ts.markFinal( any(self.TOOLTIP_STREAMS and self.TOOLTIP_POSITION and self.TOOLTIP_GLOBAL and self.TOOLTIP_DSP_SPOTIFY in tool for tool in tooltips), f"CPOT tooltip contains all elements: {tooltips}", ) if market != AppConfig.get("market")["global"]["name"]: self.ts.markFinal( any(self.TOOLTIP_MARKET.format(market.upper()) in tool for tool in tooltips), f"CPOT tooltip contains local market: {tooltips}", ) return tooltips_parsed @allure.step("CPOT get track spotify id") def get_track_id(self): track_name = self.wait_for_element_clickable(*self.locator(self.gtp_cpot_locators, "track_name")) id = self.get_attribute_value(element=track_name, attribute="href").split("/")[-1] return id @allure.step("CPOT - popup verify chart tooltips min/max") def verify_chart_min_max(self, tooltips, market): for c, v in zip(self.dots_colors, [self.TOOLTIP_POSITION, self.TOOLTIP_STREAMS]): dots = self.get_elements(locator=f"//*[@fill='rgba({c})'][position() < last()]", locator_type="xpath") self.ts.markFinal(len(dots) > 0, f"count of min/peak dots is: {len(dots)}") if len(dots) > 0: for d in dots: ActionChains(self.driver).move_to_element(to_element=d).perform() tooltip_el = self.wait_for_element_visible(*self.locator(self.gtp_cpot_locators, "tooltip")) text = self.get_text(element=tooltip_el) if len(text) > 0: positions = self.get_elements(*self.locator(self.gtp_cpot_locators, "tooltip_positions")) streams = self.get_elements(*self.locator(self.gtp_cpot_locators, "tooltip_streams")) g_position = -1 m_position = -1 if self.get_text(element=positions[0]) != "–": g_position = int(self.get_text(element=positions[0])) if self.get_text(element=positions[1]) != "–": m_position = int(self.get_text(element=positions[1])) g_streams = int(self.get_text(element=streams[0]).replace(",", "").replace("–", "0")) m_streams = int(self.get_text(element=streams[1]).replace(",", "").replace("–", "0")) self.ts.markFinal( f"{v} ({self.TOOLTIP_MINIMUM})" or f"{v} ({self.TOOLTIP_PEAK})" in text, f"tooltip text min/max: \n{v}({self.TOOLTIP_MINIMUM}) or \n{v} ({self.TOOLTIP_PEAK}) in \n{text}", ) max_positions = [ (max(p["g_position"]) for p in tooltips if p["g_position"] >= 0), (max(p["m_position"]) for p in tooltips if p["m_position"] >= 0), ] max_streams = [ (max(p["g_streams"]) for p in tooltips if p["g_streams"] >= 0), (max(p["m_streams"]) for p in tooltips if p["m_streams"] >= 0), ] min_positions = [ (min(p["g_position"]) for p in tooltips if p["g_position"] >= 0), (min(p["m_position"]) for p in tooltips if p["m_position"] >= 0), ] min_streams = [ (min(p["g_streams"]) for p in tooltips if p["g_streams"] >= 0), (min(p["m_streams"]) for p in tooltips if p["m_streams"] >= 0), ] if f"{self.TOOLTIP_POSITION}\n ({self.TOOLTIP_PEAK})" in text: self.ts.markFinal( g_position or m_position in max_positions, f"peak position is: {g_position}, {m_position}\nexpected: {max_positions}", ) elif f"{self.TOOLTIP_POSITION}\n ({self.TOOLTIP_MINIMUM})" in text: self.ts.markFinal( g_position or m_position in min_positions, f"minimum position is: {g_position}, {m_position}\nexpected: {min_positions}", ) elif f"{self.TOOLTIP_STREAMS}\n ({self.TOOLTIP_PEAK})" in text: self.ts.markFinal( g_streams or m_streams in max_streams, f"peak streams is: {g_streams}, {m_streams}\nexpected: {max_streams}", ) elif f"{self.TOOLTIP_STREAMS}\n ({self.TOOLTIP_MINIMUM})" in text: self.ts.markFinal( g_streams or m_streams in min_streams, f"minimum streams is: {g_streams}, {m_streams}\nexpected: {min_streams}", ) @allure.step("CPOT - popup get markets legend") def get_markets(self): self.wait_for_element_visible(*self.locator(self.gtp_cpot_locators, "legend_markets")) markets_el = self.get_elements(*self.locator(self.gtp_cpot_locators, "legend_markets")) markets = [self.get_text(element=m) for m in markets_el] return markets @allure.step("CPOT - popup verify period label") def verify_period_label(self): period = self.is_element_displayed(*self.locator(self.gtp_cpot_locators, "period_label")) self.ts.markFinal(period, "period label is displayed") @allure.step("CPOT - popup verify default period") def verify_default_period(self): start_t = self.get_text(*self.locator(self.gtp_cpot_locators, "datepicker")).split("–")[0] end_t = self.get_text(*self.locator(self.gtp_cpot_locators, "datepicker")).split("–")[1] start = datetime.strptime(start_t, self.tp.get_date_format_from_locale()).strftime("%Y-%m-%d") end = datetime.strptime(end_t, self.tp.get_date_format_from_locale()).strftime("%Y-%m-%d") market = self.tp.parameter_from_url(self.ap.hd_filter_market) id = self.get_track_id() isrc = self.api_client.get_isrc_by_spotify_id(market=market, id=id)["external_ids"]["isrc"] start_date_api_local = self.api_client.get_gtp_cpot_charts(market=market, isrc=isrc)["data"][0]["start_date"] end_date_api_local = self.api_client.get_gtp_cpot_charts(market=market, isrc=isrc)["data"][0]["end_date"] start_date_api_global = self.api_client.get_gtp_cpot_charts(market=AppConfig.get("market")["global"]["api3"], isrc=isrc)["data"][0]["start_date"] end_date_api_global = self.api_client.get_gtp_cpot_charts(market=AppConfig.get("market")["global"]["api3"], isrc=isrc)["data"][0]["end_date"] if start_date_api_local < start_date_api_global: assert_that(start).is_equal_to(start_date_api_local) else: assert_that(start).is_equal_to(start_date_api_global) if end_date_api_local > end_date_api_global: assert_that(end).is_equal_to(end_date_api_local) else: assert_that(end).is_equal_to(end_date_api_global) @allure.step("CPOT - popup verify predefined periods") def popup_verify_predefined_periods(self): for p, d in zip(self.ap.dp_predefined_periods, self.ap.dp_predefined_count_days): xaxis_el = self.get_elements(*self.locator(self.gtp_cpot_locators, "xaxis")) xaxis_before = [self.get_text(element=e) for e in xaxis_el] kpis_before = self.get_elements(*self.locator(self.gtp_cpot_locators, "legend_kpi")) kpis_before = [self.get_text(element=k) for k in kpis_before] dp = self.wait_for_element_clickable(*self.locator(self.gtp_cpot_locators, "datepicker")) self.click_element(element=dp) dr = self.wait_for_element_clickable(locator=f"//*[contains(@id, '-modal-window-day-picker-{p}')]", locator_type="xpath") self.click_element(element=dr) count_days = self.get_days_count_from_day_picker() xaxis_el = self.get_elements(*self.locator(self.gtp_cpot_locators, "xaxis")) xaxis_after = [self.get_text(element=e) for e in xaxis_el] kpis_after = self.get_elements(*self.locator(self.gtp_cpot_locators, "legend_kpi")) kpis_after = [self.get_text(element=k) for k in kpis_after] if d != self.get_days_count_from_day_picker(): self.ts.markFinal(xaxis_before != xaxis_after, f"xaxis: {xaxis_before} is changed for {p}: {xaxis_after}") if d != 365: self.ts.markFinal(kpis_before != kpis_after, f"{kpis_before} is changed to {kpis_after}") self.ts.markFinal(count_days >= d, f"count: {count_days} match expected: {d}") @allure.step("CPOT - select previous month period") def popup_select_previous_mounth_period(self): kpis_before = self.get_elements(*self.locator(self.gtp_cpot_locators, "legend_kpi")) kpis_before = [self.get_text(element=k) for k in kpis_before] self.click_element(*self.locator(self.gtp_cpot_locators, "datepicker")) previous_month_picker = "DayPicker-NavButton--prev" self.driver.execute_script("document.getElementsByClassName('" + previous_month_picker + "')[0].click();") self.click_element(*self.locator(self.gtp_cpot_locators, "firstDayOfPreviousMonth")) self.click_element(*self.locator(self.gtp_cpot_locators, "lastDayOfPreviousMonth")) kpis_after = self.get_elements(*self.locator(self.gtp_cpot_locators, "legend_kpi")) kpis_after = [self.get_text(element=k) for k in kpis_after] graph = self.is_element_displayed(*self.locator(self.gtp_cpot_locators, "xaxis")) self.ts.markFinal(kpis_before != kpis_after, f"{kpis_before} is changed to {kpis_after}") self.ts.markFinal(graph, "graph is displayed") @allure.step("CPOT - verify export functionality") def verify_export(self, name_add=""): export = self.wait_for_element_clickable(*self.locator(self.gtp_cpot_locators, "export_button")) self.click_element(element=export) start_t = self.get_text(*self.locator(self.gtp_cpot_locators, "datepicker")).split("–")[0] end_t = self.get_text(*self.locator(self.gtp_cpot_locators, "datepicker")).split("–")[1] start = datetime.strptime(start_t, self.tp.get_date_format_from_locale()).date() end = datetime.strptime(end_t, self.tp.get_date_format_from_locale()).date() date_range = [] span = end - start days_count = self.get_days_count_from_day_picker() for i in range(span.days + 1): d = start + timedelta(days=i) date_range.append(d) artist_name = self.get_text("gtp-chart-position-modal-window-artist-name") track_name = self.get_text(*self.locator(self.gtp_cpot_locators, "track_name")) track_uri = self.get_track_id() file_path = f"""http://{CoreConfig.SELENOID_HOST}:4444/download/{self.driver.session_id}/Chart Position over Time - {track_name}{name_add}.csv""" self.log.info(f"file path: {file_path}") columns = ["Date", "Artist", "Track Name", "Track URI", "Position in Global Chart"] market_from_url = self.tp.parameter_from_url(self.ap.hd_filter_market) if market_from_url == AppConfig.get("market")["global"]["api3"]: columns = columns + ["Global Streams"] else: columns = columns + [ f"Position in {market_from_url.upper()} Chart", "Global Streams", f"{market_from_url.upper()} Streams", ] data = self.tp.read_from_csv(file_path) file = {} for c in columns: file[c] = data.get(c).to_list() self.log.info(f"file: {file}") dates_file = [datetime.strptime(d, "%m/%d/%y").date() for d in file["Date"]] artis_names = [artist_name] track_names_ui = [track_name] track_uri_ui = [f"spotify:track:{track_uri}"] isrc_global = self.api_client.get_isrc_by_spotify_id(market=AppConfig.get("market")["global"]["api3"], id=track_uri)["external_ids"]["isrc"] global_positions_api = self.api_client.get_gtp_cpot_charts_with_date_range(isrc=isrc_global, market=AppConfig.get("market")["global"]["api3"], start_date=start, date_shift=days_count)["data"][0]["positions"] global_positions_api = [ p["position"] for p in global_positions_api # https://data-analytics.atlassian.net/browse/AP-6018 # if (datetime.strptime(p['date'], "%Y-%m-%d")).date() in date_range ] global_streams_api = self.get_api_streams_count(id=track_uri, start_date=start, end_date=end, market=AppConfig.get("market")["global"]["api3"]) file_position_global = set([f for f in file["Position in Global Chart"] if f > 0]) file_streams_global = set([f for f in file["Global Streams"] if f > 0]) self.ts.markFinal(dates_file == date_range, f"Dates: {dates_file} \nand {date_range} match") self.ts.markFinal(list(set(file["Artist"])) == artis_names, f"Artist: {list(set(file['Artist']))}\n and {artis_names} match") self.ts.markFinal( list(set(file["Track Name"])) == track_names_ui, f"Track Name: {list(set(file['Track Name']))}\n and {track_names_ui} match", ) self.ts.markFinal( list(set(file["Track URI"])) == track_uri_ui, f"Track URI: {list(set(file['Track URI']))}\n and {track_uri_ui} match", ) assert_that(set(file_position_global)).is_equal_to(set(global_positions_api)) assert_that(set(file_streams_global)).is_equal_to(set(global_streams_api)) if market_from_url != AppConfig.get("market")["global"]["api3"]: isrc_local = self.api_client.get_isrc_by_spotify_id(market=market_from_url, id=track_uri)["external_ids"]["isrc"] local_positions_api = self.api_client.get_gtp_cpot_charts_with_date_range(isrc=isrc_local, market=market_from_url, start_date=start, date_shift=days_count)["data"][0]["positions"] local_positions_api = [p["position"] for p in local_positions_api] local_streams_api = self.get_api_streams_count(id=track_uri, start_date=start, end_date=end, market=market_from_url) position_local_name = f"Position in {market_from_url.upper()} Chart" streams_local_name = f"{market_from_url.upper()} Streams" file_position_local = [f for f in file[position_local_name] if f > 0] file_streams_local = [f for f in file[streams_local_name] if f > 0] self.ts.markFinal( file_position_local == local_positions_api, f"{position_local_name} Chart: {file_position_local}\n and {local_positions_api} match", ) self.ts.markFinal( file_streams_local == local_streams_api, f"{streams_local_name}: {file_streams_local}\n and {local_streams_api} match", ) @allure.step("CPOT - verify export hover") def verify_export_hover(self): export = self.wait_for_element_clickable(*self.locator(self.gtp_cpot_locators, "export_button")) 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("CPOT - get streams count for date and market") def get_api_streams_count(self, market, start_date, end_date, id): isrc = self.api_client.get_isrc_by_spotify_id(market=market, id=id)["external_ids"]["isrc"] streams_tp_api_resp = self.api_client.get_track_per_country(isrc=isrc, start_date=start_date, end_date=end_date, vendor="spotify") streams_tp_api = 0 for r in streams_tp_api_resp: if r["countryCode"] == market: streams_tp_api = [int(str(s["streams"]).replace("null", "0")) for s in r["data"]] return streams_tp_api @allure.step("CPOT - verify KPI elements") def verify_elements_kpi(self, local_market): text_ui = [] if local_market != AppConfig.get("market")["global"]["name"]: text_exp = ["GLOBAL", local_market.upper()] + ["Peak"] * 2 + ["Peak Chart Position"] * 2 + ["Streams in Period"] * 2 count = 2 else: text_exp = ["GLOBAL", "Peak", "Peak Chart Position", "Streams in Period"] count = 1 for e in ["market-name", "peak-icon", "min-icon", "peak-label", "peak-chart-title", "streams-title"]: selector = f"//*[contains(@id, '{e}')]" if e == "streams-title": selector = f"//*[contains(@id, '{e}') and contains(@id, 'legend')]" self.wait_for_element_visible(selector, "xpath") elements = self.get_elements(selector, "xpath") assert_that(len(elements)).described_as(e).is_equal_to(count) text_el = [self.get_text(element=e) for e in elements if self.get_text(element=e) != ""] text_ui = text_ui + text_el assert_that(text_ui).is_equal_to(text_exp) @allure.step("CPOT - get KPI values UI") def get_kpi_values_ui(self): chart_positions_selector = "//*[contains(@id, 'chart-value')]" streams_selector = "//*[contains(@id, 'streams-value') and contains(@id, 'legend')]" values_ui = [] for s in [chart_positions_selector, streams_selector]: self.wait_for_element_visible(s, "xpath") elements = self.get_elements(s, "xpath") self.log.info("Print elements: " + str(elements)) values_el = [int(self.get_text(element=e).replace(",", "").replace("N/A", "-1")) for e in elements] values_ui = values_ui + values_el return values_ui @allure.step("CPOT - verify chart position no data") def verify_chart_position_no_data(self): no_data = self.wait_for_element_visible("(//*[@id='container']/*/*)[1]", "xpath") no_data_text = self.get_text(element=no_data) assert_that(no_data_text).is_equal_to("There's no data available for Chart Position graph.")