Initial import: eCW Charge Validation bot and results Portal
Snapshot of the working project as of 2026-10-23 (local 2d4d4fc), one commit with no earlier history: the tracked files were de-identified first (synthetic claim ids and patient data only; see Context.md section 7). Credentials, run logs, screenshots, claim reports and client workbooks are gitignored and not included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
ba15a574ab
205 files changed
+56772
No files matched your search
@@ -0,0 +1,225 @@
|
||||
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("""
|
||||
<button class="b" id="b1">Close</button>
|
||||
<button class="b" id="b2" style="pointer-events:none">Close</button>
|
||||
<script>window.hits = [];
|
||||
for (const b of document.querySelectorAll('.b'))
|
||||
b.addEventListener('click', () => window.hits.push(b.id));</script>
|
||||
""")
|
||||
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('<button class="b" style="pointer-events:none">x</button>')
|
||||
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())
|
||||
Reference in new issue
Block a user