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>
190 lines
6.4 KiB
Python
190 lines
6.4 KiB
Python
r"""
|
|
VERIFY mode writes nothing to eCW (operator, 2026-10-16 — option C).
|
|
|
|
Found 2026-10-15, confirmed in eCW's audit log: clicking a CPT line's
|
|
"Assign To Patient" box runs eCW's updateBillToIns(), which saves on the
|
|
server at once — closing the form with Cancel did not undo it. So with
|
|
SAVE_CORRECTIONS=0 (VERIFY) the box is set on SCREEN only (no click event,
|
|
so updateBillToIns never runs), and no CPT row is added (Add → picker OK
|
|
runs addClaimFSCPTCode('save')). LIVE-WRITE still clicks for real.
|
|
|
|
python tests\test_verify_no_live_writes.py
|
|
|
|
Uses tests/fixtures/vaccine_claim.html, reshaped per case with
|
|
seedCptRows; a capture listener counts real clicks on the bill-to boxes.
|
|
"""
|
|
|
|
import io
|
|
import sys
|
|
import pathlib
|
|
import inspect
|
|
import contextlib
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
|
sys.path.insert(0, str(ROOT / "src"))
|
|
|
|
FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "vaccine_claim.html"
|
|
|
|
from ecw_bot import settings as cfg # noqa: E402
|
|
from ecw_bot.conditions import vaccine_admin_codes as vac # 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 _fresh(page, rows):
|
|
page.goto(FIXTURE.as_uri())
|
|
page.evaluate("rows => window.seedCptRows(rows)", rows)
|
|
page.evaluate("""() => {
|
|
window.billClicks = 0; window.addClicks = 0;
|
|
document.addEventListener('click', e => {
|
|
if (e.target.closest('[ng-click*="updateBillToIns("]'))
|
|
window.billClicks++;
|
|
if (e.target.closest('button[ng-click*="addProcedureCode("]'))
|
|
window.addClicks++;
|
|
}, true);
|
|
}""")
|
|
|
|
|
|
def _billed(page):
|
|
return {r["code"]: r["billed"] for r in claim_form.extract(page)["cpt"]}
|
|
|
|
|
|
def _clicks(page):
|
|
return page.evaluate("window.billClicks")
|
|
|
|
|
|
ROWS = [
|
|
{"code": "99213", "title": "OFFICE VISIT EST LEVEL 3", "fee": "120.00"},
|
|
{"code": "G8510", "title": "SCREENING NEGATIVE", "fee": "0.00"},
|
|
{"code": "J2001", "title": "LIDOCAINE", "fee": "15.00"},
|
|
{"code": "90656", "title": "FLU VACC", "fee": "65.00", "checked": False},
|
|
]
|
|
|
|
|
|
# ── VERIFY: on screen, no click ──────────────────────────────────────────────
|
|
|
|
@case
|
|
def verify_unchecks_a_zero_fee_line_on_screen_without_clicking(page):
|
|
_fresh(page, ROWS)
|
|
changes, out = _quiet(claim_form.uncheck_zero_fee_cpt_rows, page)
|
|
assert [c["code"] for c in changes] == ["G8510"], changes
|
|
assert _billed(page)["G8510"] is False, "the screen shows it unchecked"
|
|
assert _clicks(page) == 0, "a real click would run updateBillToIns()"
|
|
|
|
|
|
@case
|
|
def verify_checks_a_priced_line_on_screen_without_clicking(page):
|
|
_fresh(page, ROWS)
|
|
changes, _ = _quiet(claim_form.check_priced_unchecked_cpt_rows, page)
|
|
assert [c["code"] for c in changes] == ["90656"], changes
|
|
assert _billed(page)["90656"] is True
|
|
assert _clicks(page) == 0
|
|
|
|
|
|
@case
|
|
def verify_unchecks_and_zeroes_a_target_code_without_clicking(page):
|
|
_fresh(page, ROWS)
|
|
changes, _ = _quiet(claim_form.zero_and_uncheck_cpt_rows, page, {"J2001"})
|
|
assert changes and changes[0]["unchecked"] is True, changes
|
|
row = [r for r in claim_form.extract(page)["cpt"] if r["code"] == "J2001"][0]
|
|
assert row["billed"] is False and row["billed_fee"] == "0.00", row
|
|
assert _clicks(page) == 0
|
|
|
|
|
|
@case
|
|
def verify_re_includes_a_kept_line_without_clicking(page):
|
|
_fresh(page, [
|
|
{"code": "99213", "title": "OFFICE VISIT", "fee": "120.00"},
|
|
{"code": "G8510", "title": "SCREENING", "fee": "65.00",
|
|
"checked": False}])
|
|
changes, _ = _quiet(claim_form.ensure_cpt_rows_checked, page, {"G8510"})
|
|
assert [c["code"] for c in changes] == ["G8510"], changes
|
|
assert _billed(page)["G8510"] is True
|
|
assert _clicks(page) == 0
|
|
|
|
|
|
@case
|
|
def verify_adds_no_cpt_row(page):
|
|
_fresh(page, ROWS)
|
|
before = len(claim_form.extract(page)["cpt"])
|
|
added, out = _quiet(claim_form.add_cpt_row, page, "96372")
|
|
assert added is False, out
|
|
assert len(claim_form.extract(page)["cpt"]) == before
|
|
assert page.evaluate("window.addClicks") == 0, "Add was clicked"
|
|
assert "VERIFY" in out, out
|
|
|
|
|
|
@case
|
|
def verify_admin_code_is_not_added_and_says_why(page):
|
|
_fresh(page, ROWS)
|
|
change, _ = _quiet(vac._add_admin_code, page, "C1", "96372", 1)
|
|
assert change["added"] is False and change["grid_changed"] is False, change
|
|
assert "VERIFY" in (change["error"] or ""), change
|
|
|
|
|
|
# ── LIVE-WRITE: the real click, as before ────────────────────────────────────
|
|
|
|
@case
|
|
def live_write_still_clicks_for_real(page):
|
|
cfg.SAVE_CORRECTIONS = True
|
|
_fresh(page, ROWS)
|
|
_quiet(claim_form.uncheck_zero_fee_cpt_rows, page)
|
|
assert _billed(page)["G8510"] is False
|
|
assert _clicks(page) == 1, "LIVE-WRITE keeps eCW's own click"
|
|
|
|
|
|
def _run_cases() -> int:
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
failures = 0
|
|
with sync_playwright() as pw:
|
|
browser = pw.chromium.launch(headless=True)
|
|
page = browser.new_page()
|
|
for fn in CASES:
|
|
name = fn.__name__.replace("_", " ")
|
|
cfg.SAVE_CORRECTIONS = False # VERIFY unless the case says
|
|
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
|
|
browser.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
|
|
orig = (cfg.SCREENSHOTS, cfg.SAVE_CORRECTIONS)
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
cfg.SCREENSHOTS = pathlib.Path(tmp)
|
|
try:
|
|
return _run_cases()
|
|
finally:
|
|
cfg.SCREENSHOTS, cfg.SAVE_CORRECTIONS = orig
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|