Every failed task printed the same reason, because the blanket catch in
complete_all_tasks treated NoSuchElementException and TimeoutException as one
finding. A wait that expires says nothing about whether the section was there,
since wait_for_element swallowed whatever the getter raised and returned False.
A market that does not ship visual search and a points panel that was on screen
and slow both arrived as TimeoutException
wait_for_element now keeps the last reason the getter gave and raises
ElementNeverAppeared when nothing was in the DOM for the whole wait.
_container_by_id raises ElementNotReady when the id is present but no visible
copy has content yet, which is the hydrating case it used to report as missing.
Both subclass what they refine, so claim_bonus_points and
complete_bing_daily_set keep working untouched
A task that was reached and then ran out of time is now a [FAIL] rather than a
[SKIP], because it may have left points behind. Absence stays a [SKIP]
56 tests pass, 20 of them new. A mutation check backs them, forcing every
expired wait to count as absence fails 2, and collapsing the two branches back
into one [SKIP] fails 4
Refs #52
Matching on bing.com/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, so the filter skipped it and the run came up short without saying why.
Three shapes now count, bing.com/search, bing.com/rewards and rewards.bing.com. The bing app promo from #45 sits on bingapp.microsoft.com and stays out of all three, which is what keeps that fix intact. Five tests, one per shape plus a mixed panel.
#50 landed a tests directory using stdlib unittest, discovered with
python -m unittest discover -s tests. The multi-account checks were a
standalone script with their own runner, so discovery would have walked
straight past them.
Same cases, rewritten as TestCases. The layer that starts Edge twice is
behind REWARDS_BROWSER_TESTS rather than a --browser argument, since
discovery does not pass arguments through.
Claude-Session: https://claude.ai/code/session_019PWUtibJn81iDbxZ4hzeU3
Five layers, cheapest first: which accounts a configuration produces, the
flags each one hands Edge, the run loop's ordering and exit codes, one
account failing every way it can without ending the batch, and two real
Edge profiles holding two independent, persistent identities.
Layers 1 to 4 need nothing installed beyond selenium and run in a second.
Layer 5 starts Edge twice and reaches bing.com, so it is opt in behind
--browser. It keys on bing.com's own MUID rather than an injected cookie:
a cookie added through webdriver is not written to the profile the way a
Set-Cookie is, so it would prove nothing about a sign-in surviving.
Claude-Session: https://claude.ai/code/session_019PWUtibJn81iDbxZ4hzeU3
Four bugs that reached real runs are covered: a point value the parser could not read, the wrong row of the breakdown panel, a container that looks right but is empty, and a label that matches two different buttons.
Stdlib unittest with small fakes for the selenium calls the selectors make, so this adds no dependency and needs no browser. Run with python -m unittest discover -s tests.