r""" auth/login.py `wait_after_username()` — what comes after the username's Next (live 2026-10-28 17:59: the security image page came late, the old one-look check missed it, and the bot clicked a password box that stayed disabled until it timed out). It waits, up to its timeout, for whichever comes first: eCW's "VERIFY YOUR SECURITY IMAGE" page or a password box that is visible AND enabled. Neither → a screenshot (with the DOM) and an error that says so. python tests\test_login_username_step.py Local headless chromium, `page.set_content()` only. No eCW, no login. """ import sys import time import pathlib import tempfile ROOT = pathlib.Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "src")) from ecw_bot import settings as cfg # noqa: E402 from ecw_bot.auth import login # noqa: E402 CASES = [] def case(fn): CASES.append(fn) return fn # The password box is on the page from the start, disabled — the shape # seen live; eCW enables it once the username step is done. _PAGE = """
""" @case def a_late_security_image_page_is_seen(page): page.set_content(_PAGE % "setTimeout(() => document.getElementById(" "'img').style.display = '', 3000);") assert login.wait_after_username(page, 10_000) == "image" @case def a_password_box_enabled_late_is_waited_for(page): page.set_content(_PAGE % "setTimeout(() => document.getElementById(" "'passwordField').disabled = false, 2500);") t0 = time.monotonic() assert login.wait_after_username(page, 10_000) == "password" assert time.monotonic() - t0 >= 2, "returned before it was enabled" @case def after_the_image_only_the_password_box_counts(page): page.set_content(_PAGE % "document.getElementById('img').style.display" " = ''; setTimeout(() => document.getElementById(" "'passwordField').disabled = false, 1500);") assert login.wait_after_username(page, 10_000, image=False) == "password" @case def a_password_box_that_stays_disabled_is_an_error_with_a_screenshot(page): page.set_content(_PAGE % "") try: login.wait_after_username(page, 2_000) except RuntimeError as exc: assert "password box stayed disabled" in str(exc), exc else: raise AssertionError("no error for a box that never enabled") shots = list(pathlib.Path(cfg.SCREENSHOTS).glob("*_username_step_disabled.*")) assert {p.suffix for p in shots} == {".png", ".html"}, shots def main() -> int: from playwright.sync_api import sync_playwright failures = 0 with tempfile.TemporaryDirectory() as tmp: cfg.SCREENSHOTS = pathlib.Path(tmp) # never the real folder with sync_playwright() as pw: browser = pw.chromium.launch(headless=True) page = browser.new_page() for fn in CASES: name = fn.__name__.replace("_", " ") try: fn(page) print(f" ok {name}") except AssertionError as exc: print(f" FAIL {name}: {exc}") failures += 1 except Exception as exc: print(f" ERROR {name}: {type(exc).__name__}: {exc}") failures += 1 browser.close() print(f"\n{len(CASES) - failures}/{len(CASES)} passed") return 1 if failures else 0 if __name__ == "__main__": sys.exit(main())