r""" Claim windows that take no clicks (live run 2026-10-13, Logs\run_20261013_163322.log). After an HCFA claim closed while the bot's review box was in front of Chrome, its window stayed in the page at opacity 0.222 with `pointer-events: none` — Chrome had paused the fade-out — and the next claim spent ~45 s clicking Cancel on it four times. A Dental window eCW opened by itself after the Lookup was briefly the same. * Chrome is launched so it keeps running behind a message box. * Such a window is never clicked: it gets up to `_SETTLE_MS` to finish — gone → nothing to do; clickable → cancelled; still not → refused, as before, so it is never read as the next claim's form. * `click_topmost` / `count_shown` do not count what no click can reach. python tests\test_inert_claim_windows.py """ import io import sys import pathlib import inspect import contextlib ROOT = pathlib.Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "src")) FIXTURES = pathlib.Path(__file__).resolve().parent / "fixtures" HCFA = FIXTURES / "hcfa_claim.html" from ecw_bot import settings as cfg # noqa: E402 from ecw_bot.core import browser, page_utils # noqa: E402 from ecw_bot.pages import claim_form # noqa: E402 CASES = [] def case(fn): CASES.append(fn) return fn def _quiet(fn, *a, **k): with contextlib.redirect_stdout(io.StringIO()) as out: result = fn(*a, **k) return result, out.getvalue() def _hcfa_window(page, inert=True, then=None, after_ms=500): """ The HCFA fixture's claim window, its Cancel counting clicks. `inert`: shown but `pointer-events: none` at opacity 0.222, as the live ghost was. `then`: "gone" removes the window after `after_ms`, "live" makes it clickable again. """ page.goto(HCFA.as_uri()) page.evaluate("""([inert, then, ms]) => { const win = document.querySelector('.claimModalBackdrop'); const old = win.querySelector('[ng-click="closeClaimScreen()"]'); const c = old.cloneNode(true); // drop the fixture's handler old.replaceWith(c); window.cancels = 0; c.addEventListener('click', () => { window.cancels++; win.remove(); }); if (inert) Object.assign(win.style, {pointerEvents: 'none', opacity: '0.222'}); if (then === 'gone') setTimeout(() => win.remove(), ms); if (then === 'live') setTimeout(() => Object.assign(win.style, {pointerEvents: '', opacity: ''}), ms); }""", [inert, then, after_ms]) def _with_short_settle(fn): old = claim_form._SETTLE_MS claim_form._SETTLE_MS = 2_000 try: return fn() finally: claim_form._SETTLE_MS = old # ── Chrome keeps running behind a box ─────────────────────────────────────── @case def chrome_is_launched_to_keep_running_behind_a_message_box(): seen = {} class Chromium: def launch(self, **kw): seen.update(kw) raise RuntimeError("stop here") class PW: chromium = Chromium() saved = cfg.BROWSER_CDP_URL cfg.BROWSER_CDP_URL = "" # launch, not attach (.env may set it) try: _quiet(browser.launch, PW()) except RuntimeError: pass finally: cfg.BROWSER_CDP_URL = saved for flag in ("--disable-backgrounding-occluded-windows", "--disable-renderer-backgrounding", "--disable-background-timer-throttling"): assert flag in seen["args"], seen.get("args") # ── a window that takes no clicks ──────────────────────────────────────────── @case def an_inert_window_that_never_settles_is_refused_without_a_click(page): _hcfa_window(page, inert=True) def run(): try: _quiet(claim_form._close_leftover_form, page, "9112000") except RuntimeError as exc: return str(exc) return None why = _with_short_settle(run) assert why and "takes no clicks" in why, why assert page.evaluate("window.cancels") == 0, "an inert window was clicked" @case def an_inert_window_that_finishes_closing_needs_nothing(page): _hcfa_window(page, inert=True, then="gone") closed, out = _with_short_settle( lambda: _quiet(claim_form._close_leftover_form, page, "9112000")) assert closed is False, out assert "finished on its own" in out, out assert page.evaluate("window.cancels") == 0 @case def a_window_that_becomes_clickable_is_cancelled(page): _hcfa_window(page, inert=True, then="live") closed, out = _with_short_settle( lambda: _quiet(claim_form._close_leftover_form, page, "9112000")) assert closed is True, out assert page.evaluate("window.cancels") == 1, out assert "already open" in out, out @case def a_live_leftover_is_cancelled_at_once(page): _hcfa_window(page, inert=False) closed, out = _quiet(claim_form._close_leftover_form, page, "9112000") assert closed is True, out assert page.evaluate("window.cancels") == 1, out @case def clear_screen_reports_an_inert_window_and_clicks_nothing(page): """No click on it — Escape, the last resort (operator, 2026-10-14), is a key press, and is said in the log.""" _hcfa_window(page, inert=True) done, out = _with_short_settle( lambda: _quiet(claim_form.clear_screen, page, "9112000", page.context.pages)) assert "takes no clicks" in out, out assert "LAST RESORT" in out and "pressing Escape" in out, out assert page.evaluate("window.cancels") == 0 # ── click_topmost ──────────────────────────────────────────────────────────── @case def click_topmost_and_count_shown_skip_what_no_click_can_reach(page): page.set_content(""" """) assert page_utils.count_shown(page, ".b") == 1 assert page_utils.click_topmost(page, ".b", timeout=2_000) is True assert page.evaluate("window.hits") == ["b1"] page.set_content('') assert page_utils.click_topmost(page, ".b", timeout=1_000) is False def _run_cases() -> int: from playwright.sync_api import sync_playwright failures = 0 with sync_playwright() as pw: b = pw.chromium.launch(headless=True) page = b.new_page(viewport={"width": 1600, "height": 1000}) for fn in CASES: name = fn.__name__.replace("_", " ") try: if len(inspect.signature(fn).parameters) == 0: fn() else: 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 b.close() print(f"\n{len(CASES) - failures}/{len(CASES)} passed") return 1 if failures else 0 def main() -> int: """shot() writes into the operator's Screenshots\\ folder; not here.""" import tempfile from ecw_bot import settings as _cfg orig = _cfg.SCREENSHOTS with tempfile.TemporaryDirectory() as tmp: _cfg.SCREENSHOTS = pathlib.Path(tmp) try: return _run_cases() finally: _cfg.SCREENSHOTS = orig if __name__ == "__main__": sys.exit(main())