2026-08-19 14:03:22 -04:00
|
|
|
from selenium.webdriver.common.by import By
|
|
|
|
|
from selenium.webdriver.remote.webelement import WebElement
|
|
|
|
|
from selenium.common.exceptions import NoSuchElementException
|
|
|
|
|
from selenium import webdriver
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ElementSelectionUtils:
|
|
|
|
|
def __init__(self, driver: webdriver.Edge):
|
|
|
|
|
self.driver = driver
|
|
|
|
|
|
|
|
|
|
def resolve(self, xpath: str):
|
|
|
|
|
return self.driver.find_element(By.XPATH, xpath)
|
|
|
|
|
|
|
|
|
|
def get_earn_tab(self):
|
|
|
|
|
return self.resolve('//*[@id="react-aria-_R_18mbslbH1_-tab-/earn"]')
|
|
|
|
|
|
|
|
|
|
def get_dashboard_tab(self):
|
|
|
|
|
return self.resolve('//*[@id="react-aria-_R_18mbslbH1_-tab-/dashboard"]')
|
|
|
|
|
|
|
|
|
|
def get_open_daily_set_button(self):
|
|
|
|
|
return self.resolve("/html/body/div[2]/div[2]/div/main/section[1]/div/div[2]/div/div/button[3]")
|
|
|
|
|
|
|
|
|
|
def get_open_visual_search_sidebar(self):
|
|
|
|
|
return self.resolve("/html/body/div[2]/div[2]/div/main/section[1]/div/div[2]/div/div/button[5]")
|
|
|
|
|
|
|
|
|
|
def get_sidebar_section(self):
|
|
|
|
|
sections = self.driver.find_elements(By.TAG_NAME, "section")
|
|
|
|
|
|
|
|
|
|
for section in sections:
|
|
|
|
|
if section.get_dom_attribute("id").startswith("react-aria"):
|
|
|
|
|
return section
|
|
|
|
|
|
|
|
|
|
raise Exception("Sidebar section not found")
|
|
|
|
|
|
|
|
|
|
def get_daily_set_elements(self):
|
|
|
|
|
daily_set_sidebar = self.get_sidebar_section()
|
|
|
|
|
|
|
|
|
|
daily_set_elems = daily_set_sidebar.find_elements(By.TAG_NAME, "a")[1:]
|
|
|
|
|
|
|
|
|
|
return daily_set_elems
|
|
|
|
|
|
|
|
|
|
def get_explore_on_bing_elements(self):
|
|
|
|
|
return [
|
|
|
|
|
self.resolve("/html/body/div[2]/div[2]/div/main/section[2]/div/div[2]/div/div/a[1]"),
|
|
|
|
|
self.resolve("/html/body/div[2]/div[2]/div/main/section[2]/div/div[2]/div/div/a[2]"),
|
|
|
|
|
self.resolve("/html/body/div[2]/div[2]/div/main/section[2]/div/div[2]/div/div/a[3]"),
|
|
|
|
|
self.resolve("/html/body/div[2]/div[2]/div/main/section[2]/div/div[2]/div/div/a[4]")
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
def get_search_now_link_from_visual_search_sidebar(self):
|
|
|
|
|
visual_search_sidebar = self.get_sidebar_section()
|
|
|
|
|
|
|
|
|
|
return visual_search_sidebar.find_elements(By.TAG_NAME, "a")[1]
|
|
|
|
|
|
|
|
|
|
def extract_card_descriptions(self, card: WebElement):
|
|
|
|
|
return card.find_element(By.CSS_SELECTOR, "p:nth-child(2)").text
|
|
|
|
|
|
|
|
|
|
def card_is_complete(self, card: WebElement):
|
|
|
|
|
return "completed" in card.find_element(By.CSS_SELECTOR, "div.flex.w-full.items-center.gap-2").text.lower()
|
|
|
|
|
|
|
|
|
|
def get_bing_search_bar(self):
|
|
|
|
|
return self.driver.find_element(By.TAG_NAME, "textarea")
|
|
|
|
|
|
2026-08-20 10:29:34 -04:00
|
|
|
def get_clear_bing_search_query_button(self):
|
|
|
|
|
return self.driver.find_element(By.ID, "sw_clx")
|
|
|
|
|
|
2026-08-19 14:03:22 -04:00
|
|
|
def get_visual_search_button(self):
|
|
|
|
|
return self.driver.find_element(By.CSS_SELECTOR, "#sb_form > div.camera.icon")
|
|
|
|
|
|
|
|
|
|
def get_visual_search_file_input(self):
|
|
|
|
|
return self.driver.find_element(By.CSS_SELECTOR, "#sb_fileinput")
|
|
|
|
|
|
|
|
|
|
def get_all_misc_cards(self):
|
|
|
|
|
misc_cards_container = self.driver.find_element(By.ID, "moreactivities")
|
|
|
|
|
|
|
|
|
|
return misc_cards_container.find_elements(By.TAG_NAME, "a")
|
|
|
|
|
|
|
|
|
|
def get_card_point_value(self, card: WebElement):
|
|
|
|
|
# querySelector("div.flex.w-full.items-center.gap-2").querySelector('p')
|
|
|
|
|
|
|
|
|
|
try: elem = card.find_element(By.CSS_SELECTOR, "div.flex.w-full.items-center.gap-2").find_element(By.TAG_NAME, "p")
|
|
|
|
|
except NoSuchElementException:
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
return int(elem.text)
|
|
|
|
|
|
|
|
|
|
def element_is_fully_in_viewport(self, elem: WebElement) -> bool:
|
|
|
|
|
js_viewport_check = """
|
|
|
|
|
var elem = arguments[0];
|
|
|
|
|
var box = elem.getBoundingClientRect();
|
|
|
|
|
|
|
|
|
|
// Check if the element is at least partially in the viewport
|
|
|
|
|
return (
|
|
|
|
|
box.top >= 0 &&
|
|
|
|
|
box.left >= 0 &&
|
|
|
|
|
box.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
|
|
|
|
|
box.right <= (window.innerWidth || document.documentElement.clientWidth)
|
|
|
|
|
);
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
return self.driver.execute_script(js_viewport_check, elem)
|
|
|
|
|
|
|
|
|
|
def get_points_breakdown_button(self):
|
|
|
|
|
elem = self.driver.find_element(By.XPATH, "/html/body/div[2]/div[2]/div/main/div/button[1]")
|
|
|
|
|
|
|
|
|
|
if "points breakdown" not in elem.text.lower():
|
|
|
|
|
raise Exception("Points Breakdown button not found")
|
|
|
|
|
|
|
|
|
|
return elem
|
|
|
|
|
|
|
|
|
|
def get_close_button_on_points_breakdown(self):
|
|
|
|
|
breakdown_sidebar = self.get_sidebar_section()
|
|
|
|
|
|
|
|
|
|
return breakdown_sidebar.find_elements(By.TAG_NAME, "button")[2]
|
|
|
|
|
|
|
|
|
|
def get_points_earned_from_searches_on_points_breakdown(self) -> int:
|
|
|
|
|
breakdown_sidebar = self.get_sidebar_section()
|
|
|
|
|
|
|
|
|
|
fraction = breakdown_sidebar.find_element(By.CSS_SELECTOR, "div.py-3.wrap-anywhere.justify-self-end").text
|
|
|
|
|
|
|
|
|
|
earned_str, max_str = fraction.split('/')
|
|
|
|
|
|
|
|
|
|
return int(earned_str.strip()), int(max_str.strip())
|
|
|
|
|
|
|
|
|
|
def get_bonus_button_on_dashboard(self):
|
|
|
|
|
button = self.driver.find_element(By.XPATH, "/html/body/div[2]/div[2]/div/main/div/button[2]")
|
|
|
|
|
|
|
|
|
|
if "ready to claim" not in button.text.lower():
|
|
|
|
|
raise Exception("Bonus button not found")
|
|
|
|
|
|
|
|
|
|
return button
|
|
|
|
|
|
|
|
|
|
def get_claim_bonus_points_button(self):
|
|
|
|
|
bonus_sidebar = self.get_sidebar_section()
|
|
|
|
|
|
|
|
|
|
return bonus_sidebar.find_elements(By.TAG_NAME, "button")[2]
|
|
|
|
|
|
|
|
|
|
def get_generic_sidebar_close_button(self):
|
|
|
|
|
sidebar = self.get_sidebar_section()
|
|
|
|
|
|
|
|
|
|
return sidebar.find_elements(By.TAG_NAME, "button")[0]
|