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())