Merge pull request #49 from mardausdennis/fix/daily-set-promo-links

match daily set activities by target instead of by position
This commit is contained in:
Carl Furtado
2026-08-31 09:08:40 -04:00
committed by GitHub
3 changed files with 124 additions and 4 deletions
+12 -1
View File
@@ -24,6 +24,9 @@ from constants import USER_DATA_DIR, PROFILE_NAME
RENDER_TIMEOUT = 60 RENDER_TIMEOUT = 60
# How many activities a fully rendered daily set panel holds.
DAILY_SET_ACTIVITIES = 3
def build_driver(): def build_driver():
options = webdriver.EdgeOptions() options = webdriver.EdgeOptions()
@@ -149,7 +152,15 @@ def main():
driver.execute_script("arguments[0].scrollIntoView({block:'center'});", opener) driver.execute_script("arguments[0].scrollIntoView({block:'center'});", opener)
time.sleep(1) time.sleep(1)
driver.execute_script("arguments[0].click();", opener) driver.execute_script("arguments[0].click();", opener)
wait_until(lambda: elements.get_sidebar_section() is not None, 30)
# Waiting for the section only tells you the panel opened, not that
# it filled. It hydrates progressively, so a check that runs on the
# first non-empty state reports whatever happened to be rendered at
# that moment, which is why this came out differently run to run.
# Same wait as complete_bing_daily_set: hold out for the full set,
# and report what is there if it never arrives.
wait_until(lambda: len(elements.get_daily_set_elements()) >= DAILY_SET_ACTIVITIES, 30)
report.check("get_daily_set_elements", elements.get_daily_set_elements) report.check("get_daily_set_elements", elements.get_daily_set_elements)
try: try:
+58 -3
View File
@@ -154,11 +154,66 @@ class ElementSelectionUtils:
try: try:
return self._button_containing(Labels.DAILY_SET_STREAK) return self._button_containing(Labels.DAILY_SET_STREAK)
except NoSuchElementException: except NoSuchElementException:
return self._streaks_button(3) pass
# The positional fallback only helps if what sits there really is the
# daily set entry. On a partially rendered streaks section it is not:
# observed returning the mobile app entry, and clicking that opens the
# app store page instead of the panel, which is what the reports in #45
# and #46 describe. Check before handing it back, and skip the task
# rather than click the wrong streak.
candidate = self._streaks_button(3)
label = (candidate.text or "").strip()
if "daily set" not in label.lower():
raise NoSuchElementException(
"daily set opener not found by label, and position 3 holds "
f"{label.splitlines()[0] if label else '<empty>'!r} instead"
)
return candidate
def get_daily_set_elements(self): def get_daily_set_elements(self):
# The first link in the opened panel is the progress row, not an activity. """The daily set activities in the opened panel.
return self.get_sidebar_section().find_elements(By.TAG_NAME, "a")[1:]
Everything after the first link is not reliably an activity. The panel
also carries promotional links, a referral card and a Bing app promo have
both been observed sitting between the progress row and the activities.
Handing one of those back gets it clicked, which navigates away from
rewards.bing.com, and every element captured beforehand then goes stale.
Matching on a Bing search alone was too narrow. "Turn referrals into
rewards" is a real daily set activity that awards points, and it points
at a rewards URL rather than a search. Three shapes have been observed:
1. `bing.com/search?q=...`, the classic search activity,
2. `bing.com/rewards/...`, seen on daily sets alongside the searches,
3. `rewards.bing.com/...`, the same activity written against the
rewards host.
The Bing app promo behind #45 is on `bingapp.microsoft.com`, so it stays
out of all three, and so does anything else off those hosts. If nothing
matches, return nothing: clicking a promo is worse than skipping the
task, and the caller already reports the shortfall.
"""
activities = []
for link in self.get_sidebar_section().find_elements(By.TAG_NAME, "a"):
try:
if self._is_daily_set_activity(link.get_dom_attribute("href") or ""):
activities.append(link)
except StaleElementReferenceException:
continue
return activities
@staticmethod
def _is_daily_set_activity(href: str) -> bool:
"""Whether an href in the daily set panel is an activity rather than a promo."""
return any(
marker in href
for marker in ("bing.com/search", "bing.com/rewards", "rewards.bing.com/")
)
# ------------------------------------------------------------------ # ------------------------------------------------------------------
# explore on bing (absent in en-US, present in some other markets) # explore on bing (absent in en-US, present in some other markets)
+54
View File
@@ -190,5 +190,59 @@ class DailySetOpener(unittest.TestCase):
selectors_for(driver).get_open_daily_set_button() selectors_for(driver).get_open_daily_set_button()
class DailySetActivityUrls(unittest.TestCase):
"""Which hrefs in the panel count as activities and which are promos."""
def _links(self, *hrefs):
links = [FakeElement(text="activity", attributes={"href": h}) for h in hrefs]
panel = FakeElement(
attributes={"id": "react-aria-42"},
children={(By.TAG_NAME, "a"): links},
)
return FakeDriver(children={(By.TAG_NAME, "section"): [panel]})
def test_matches_a_plain_search_activity(self):
found = selectors_for(
self._links("https://www.bing.com/search?q=weather")
).get_daily_set_elements()
self.assertEqual(len(found), 1)
def test_matches_a_rewards_path_activity(self):
# "Turn referrals into rewards" awards points and is not a search.
found = selectors_for(
self._links("https://www.bing.com/rewards/panelflyout")
).get_daily_set_elements()
self.assertEqual(len(found), 1)
def test_matches_an_activity_on_the_rewards_host(self):
found = selectors_for(
self._links("https://rewards.bing.com/redeem/12345")
).get_daily_set_elements()
self.assertEqual(len(found), 1)
def test_skips_the_bing_app_promo(self):
# The link behind #45. Clicking it leaves the rewards host and every
# element captured beforehand goes stale.
found = selectors_for(
self._links("https://bingapp.microsoft.com/bing?adjust=14u4j3kz")
).get_daily_set_elements()
self.assertEqual(found, [])
def test_keeps_activities_and_drops_promos_from_the_same_panel(self):
found = selectors_for(self._links(
"https://bingapp.microsoft.com/bing?adjust=14u4j3kz",
"https://www.bing.com/search?q=news",
"https://www.bing.com/rewards/panelflyout",
"https://play.google.com/store/apps/details?id=com.microsoft.bing",
)).get_daily_set_elements()
self.assertEqual(len(found), 2)
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()