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:
ManikandanandClaude Opus 5.5 committed 2026-10-23 18:38:17 +05:30
commit ba15a574ab
205 files changed
+56772

No files matched your search

+258
View File
@@ -0,0 +1,258 @@
r"""
Stacked dialogs (live run 2026-10-12, Logs\run_20261012_160331.log): one
dialog left on top of another blocked every click after it, and five of
seven claims went to Review.
* Claim 1: Prog. Notes was clicked a second time while the first viewer
was still loading its document — two viewers. Close was clicked on the
LOWER one (`.first`), which the upper, blank one covered: 15 s timeout,
the viewer stayed, and the CPT tab, the form's Cancel and the next two
claims' searches were all intercepted by its iframe.
* Claim 6: eCW opened the same HCFA claim twice (two claim windows 648 ms
apart). The bot worked on the lower one; its Prog. Notes click was
intercepted by the upper one, and the next claim's search too.
python tests\test_stacked_dialogs.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 page_utils # noqa: E402
from ecw_bot.pages import claim_form, progress_note # noqa: E402
from ecw_bot.selectors import claim_form as sel # 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()
# ── the helper ───────────────────────────────────────────────────────────────
@case
def click_topmost_clicks_the_button_on_top_not_the_first_in_the_dom(page):
page.set_content("""
<div id="low" style="position:fixed;left:0;top:0;width:400px;
height:200px;background:#eee;z-index:1050">
<button class="b" id="b1" style="margin:50px">Close</button></div>
<div id="high" style="position:fixed;left:0;top:0;width:400px;
height:200px;background:#ddd;z-index:1060">
<button class="b" id="b2" style="margin:50px">Close</button></div>
<script>window.hits = [];
for (const b of document.querySelectorAll('.b'))
b.addEventListener('click', () => window.hits.push(b.id));</script>
""")
assert page_utils.click_topmost(page, ".b", timeout=3_000) is True
assert page.evaluate("window.hits") == ["b2"]
@case
def click_topmost_says_false_when_nothing_matches(page):
page.set_content("<div>nothing here</div>")
assert page_utils.click_topmost(page, ".b", timeout=1_000) is False
# ── claim 1: two progress note viewers ───────────────────────────────────────
@case
def close_shuts_every_stacked_viewer_top_first(page):
page.goto(HCFA.as_uri())
btn = "#claimProgressNoteBtn8888888888888"
page.click(btn)
page.click(btn)
page.evaluate("""() => {
window.shut = [];
document.querySelectorAll('[ng-click="closeProgressNoteViwer()"]')
.forEach((b, i) => b.addEventListener('click',
() => window.shut.push(i)));
}""")
assert page.locator(sel.SEL_PN_CLOSE).filter(visible=True).count() == 2
ok, out = _quiet(progress_note.close, page, wait_ms=2_000)
assert ok is True, out
assert page.evaluate("window.shut") == [1, 0], "top (second) first"
assert page.locator(sel.SEL_PN_CLOSE).filter(visible=True).count() == 0
assert "2 progress note viewers" in out, out
@case
def one_viewer_with_two_close_buttons_is_one_viewer(page):
"""Live 2026-10-12: the viewer draws an X and a Close — one viewer."""
page.set_content("""
<div class="modal in" id="v" style="display:block">
<button ng-click="closeProgressNoteViwer()">x</button>
<button ng-click="closeProgressNoteViwer()">Close</button></div>
<script>for (const b of document.querySelectorAll('button'))
b.addEventListener('click',
() => document.getElementById('v').remove());</script>
""")
ok, out = _quiet(progress_note.close, page, wait_ms=1_000)
assert ok is True, out
assert "viewers were open" not in out, out
assert page.locator(sel.SEL_PN_CLOSE).count() == 0
@case
def no_second_click_while_a_viewer_is_still_loading_its_note(page):
"""The re-click must not fire while eCW's viewer frame is open."""
page.set_content("""
<button ng-click="viewProgNote()" id="pn">Prog. Notes</button>
<div class="modal in" style="display:block">
<iframe id="ProgNoteViwerFrame" name="ProgNoteViwerFrame"
style="width:300px;height:100px"></iframe></div>
<script>window.clicks = 0;
document.getElementById('pn').addEventListener('click',
() => window.clicks++);</script>
""")
old = cfg.PROGRESS_NOTE_WAIT_MS
cfg.PROGRESS_NOTE_WAIT_MS = 2_000
try:
(reader, where, _), out = _quiet(
claim_form._wait_for_note, page, len(page.context.pages))
finally:
cfg.PROGRESS_NOTE_WAIT_MS = old
assert reader is None and where == "nothing"
assert page.evaluate("window.clicks") == 0, out
assert "once more" not in out, out
@case
def the_second_click_still_fires_when_nothing_at_all_opened(page):
"""The control: the click that did not take is still retried."""
page.set_content("""
<button ng-click="viewProgNote()" id="pn">Prog. Notes</button>
<script>window.clicks = 0;
document.getElementById('pn').addEventListener('click',
() => window.clicks++);</script>
""")
old = cfg.PROGRESS_NOTE_WAIT_MS
cfg.PROGRESS_NOTE_WAIT_MS = 2_000
try:
_, out = _quiet(claim_form._wait_for_note, page,
len(page.context.pages))
finally:
cfg.PROGRESS_NOTE_WAIT_MS = old
assert page.evaluate("window.clicks") == 1, out
# ── claim 6: the same HCFA claim opened twice ────────────────────────────────
def _duplicate_hcfa_window(page):
"""A second copy of the claim window on top, as eCW opened it."""
page.evaluate("""() => {
const w = document.querySelector('.claimModalBackdrop');
const copy = w.cloneNode(true);
// Over the original, as angular-ui draws it: same place, higher.
const r = w.getBoundingClientRect();
Object.assign(copy.style, {position: 'absolute', zIndex: '1060',
left: (r.left + scrollX) + 'px', top: (r.top + scrollY) + 'px',
width: r.width + 'px', background: '#fff'});
copy.dataset.copy = '1';
document.body.appendChild(copy);
window.cancelled = [];
for (const win of document.querySelectorAll('.claimModalBackdrop')) {
const c = win.querySelector('[ng-click="closeClaimScreen()"]');
const clone = c.cloneNode(true); // drop the fixture's handler
c.replaceWith(clone);
clone.addEventListener('click', () => {
window.cancelled.push(win.dataset.copy ? 'copy' : 'original');
win.remove();
});
}
}""")
@case
def a_claim_opened_twice_loses_its_top_copy_by_cancel(page):
page.goto(HCFA.as_uri())
_duplicate_hcfa_window(page)
n, out = _quiet(claim_form.close_duplicate_forms, page, "9103000")
assert n == 1, out
assert page.evaluate("window.cancelled") == ["copy"]
assert page.locator(".claimModalBackdrop").count() == 1
assert "opened the claim twice" in out, out
@case
def one_claim_window_is_left_alone(page):
page.goto(HCFA.as_uri())
n, out = _quiet(claim_form.close_duplicate_forms, page, "9103000")
assert n == 0, out
assert page.locator(".claimModalBackdrop").count() == 1
@case
def close_claim_cancels_the_window_on_top(page):
"""With two windows up, Cancel goes to the one that can be clicked."""
page.goto(HCFA.as_uri())
_duplicate_hcfa_window(page)
page.evaluate("""() => { // the original's Cancel is covered
window.cancelled = window.cancelled || [];
}""")
ok, out = _quiet(claim_form.close_claim, page)
assert page.evaluate("window.cancelled")[0] == "copy", out
@case
def a_viewer_left_from_the_last_claim_is_closed_before_the_next_search(page):
"""
Defence in depth: claims 2 and 3 were lost to claim 1's viewer. Before
a claim is searched, any viewer still up is closed.
"""
page.goto(HCFA.as_uri())
page.click("#claimProgressNoteBtn8888888888888")
assert page.locator(sel.SEL_PN_CLOSE).filter(visible=True).count() == 1
done, out = _quiet(claim_form.clear_screen, page, "9103000",
page.context.pages)
assert page.locator(sel.SEL_PN_CLOSE).filter(visible=True).count() == 0
assert "a progress note viewer" in done, out
assert "Left over from the last claim" in out, out
def main() -> 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(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
browser.close()
print(f"\n{len(CASES) - failures}/{len(CASES)} passed")
return 1 if failures else 0
if __name__ == "__main__":
sys.exit(main())