First real upload lands: three browser-flow bugs, found by doing it
A Gentle Rain is on BGG (collid 148198034, version 701315 — the
English Bloom edition matched from a shelf photo), verified by
re-fetching the collection. The failures on the way, all in the
login gate the docs called "verified" and none in the version-picker
code they called "unverified":
1. BGG's Sign In is an <a class="btn"> with NO href, so it has no
implicit link role: get_by_role("link", name="Sign In") matched
zero elements in EVERY state, and "no Sign In link" was read as
"already signed in". Every run browsed anonymously.
2. The header hydrates after domcontentloaded, so for a moment
neither control exists — a check resting on one absence guesses.
_signed_out() now polls until the page proves one state or the
other (Sign In vs Sign Out) and raises after 30s rather than
assume; login is verified by the transition, and the session file
saves only after that proof.
3. get_by_label("Own") also matched "Prev. Owned" — strict-mode
violation; the Own checkbox is now matched exactly.
Also: Playwright's multi-line call logs no longer break upload_log.csv
into ragged rows (errors flatten to one line). Three unit tests cover
the hydration window, positive detection, and the undeterminable case.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
co-authored by
Claude Fable 5
parent
481fa63488
commit
6ae2667c58
@@ -491,3 +491,57 @@ def test_drift_warning_suppressed_for_multi_edition_partial_run(tmp_path, capsys
|
||||
)
|
||||
run_upload(cfg, uploader=FakeUploader(), sleep=lambda s: None, now=_now)
|
||||
assert "manual correction" not in capsys.readouterr().out
|
||||
|
||||
|
||||
# -- sign-in detection: never guess from a single absence ----------------
|
||||
|
||||
|
||||
class _FakePage:
|
||||
"""Counts per selector, replayed one poll at a time — the header
|
||||
hydrates late, so the first reads legitimately see nothing."""
|
||||
|
||||
def __init__(self, script):
|
||||
self.script = list(script)
|
||||
self.polls = 0
|
||||
|
||||
def locator(self, selector):
|
||||
counts = self.script[min(self.polls, len(self.script) - 1)]
|
||||
|
||||
class _Loc:
|
||||
def count(self):
|
||||
return counts["out" if "Sign In" in selector else "in"]
|
||||
|
||||
return _Loc()
|
||||
|
||||
def wait_for_timeout(self, ms):
|
||||
self.polls += 1
|
||||
|
||||
|
||||
def _uploader_with(script):
|
||||
from bggpipe.upload import PlaywrightUploader
|
||||
|
||||
up = PlaywrightUploader("someone")
|
||||
up._page = _FakePage(script)
|
||||
return up
|
||||
|
||||
|
||||
def test_signed_out_waits_through_hydration():
|
||||
# neither control for two polls (the window the old check misread as
|
||||
# "already signed in"), then the Sign In control appears
|
||||
up = _uploader_with([{"out": 0, "in": 0}, {"out": 0, "in": 0}, {"out": 1, "in": 0}])
|
||||
assert up._signed_out() is True
|
||||
|
||||
|
||||
def test_signed_in_is_detected_positively():
|
||||
up = _uploader_with([{"out": 0, "in": 0}, {"out": 0, "in": 1}])
|
||||
assert up._signed_out() is False
|
||||
|
||||
|
||||
def test_undeterminable_state_raises_instead_of_guessing(monkeypatch):
|
||||
from bggpipe.upload import LoginError
|
||||
|
||||
ticks = iter([0, 1, 2, 31]) # force the deadline to pass
|
||||
monkeypatch.setattr("time.monotonic", lambda: next(ticks))
|
||||
up = _uploader_with([{"out": 0, "in": 0}])
|
||||
with pytest.raises(LoginError, match="could not tell"):
|
||||
up._signed_out()
|
||||
|
||||
Reference in New Issue
Block a user