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

+176
View File
@@ -0,0 +1,176 @@
r"""
`visible()` and `appears()` — the instant check and the real wait.
python tests\test_page_utils.py
Review item P8 (2026-10-01): Playwright 1.62's `is_visible(timeout=...)`
ignores its timeout, so `visible(page, sel, timeout=10_000)` was always an
instant check, and every caller that meant "give it ten seconds" got none.
And `.first` answered for whichever match came first in the DOM — a hidden
leftover copy made the live element read as absent.
Local headless chromium, `page.set_content()` only. No eCW.
"""
import sys
import time
import pathlib
import inspect
ROOT = pathlib.Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
from ecw_bot.core import page_utils # noqa: E402
CASES = []
def case(fn):
CASES.append(fn)
return fn
# Two matches for one selector: a hidden leftover first, the live one second —
# the shape eCW leaves behind when it renders an id twice.
_HIDDEN_THEN_SHOWN = (
'<button class="b" style="display:none">old</button>'
'<button class="b">live</button>'
)
@case
def visible_is_true_when_the_second_of_two_matches_is_the_visible_one(page):
page.set_content(_HIDDEN_THEN_SHOWN)
# The old `.first.is_visible()` answered for the hidden copy.
assert page.locator(".b").first.is_visible() is False, \
"fixture no longer puts the hidden copy first"
assert page_utils.visible(page, ".b") is True
@case
def visible_is_false_when_every_match_is_hidden(page):
page.set_content('<p class="x" style="display:none">a</p>'
'<p class="x" style="visibility:hidden">b</p>')
assert page_utils.visible(page, ".x") is False
@case
def visible_is_instant_and_ignores_its_timeout(page):
"""The documented contract: `timeout` is accepted and ignored."""
page.set_content("<div></div>")
started = time.monotonic()
assert page_utils.visible(page, "#never", timeout=5_000) is False
assert time.monotonic() - started < 1.0, "visible() waited"
@case
def visible_never_raises_on_a_bad_selector(page):
page.set_content("<div></div>")
assert page_utils.visible(page, "[[[not a selector") is False
@case
def appears_waits_for_an_element_shown_after_a_delay(page):
page.set_content(
'<div id="late" style="display:none">x</div>'
'<script>setTimeout(function () {'
' document.getElementById("late").style.display = "block";'
'}, 800);</script>')
# The instant check misses it — the bug P8 is about.
assert page_utils.visible(page, "#late", timeout=5_000) is False
started = time.monotonic()
assert page_utils.appears(page, "#late", timeout=5_000) is True
took = time.monotonic() - started
assert 0.5 < took < 4.0, f"took {took:.1f}s"
@case
def appears_returns_false_at_its_timeout(page):
page.set_content('<div id="gone" style="display:none">x</div>')
started = time.monotonic()
assert page_utils.appears(page, "#gone", timeout=600) is False
took = time.monotonic() - started
assert 0.5 < took < 3.0, f"took {took:.1f}s"
@case
def appears_skips_a_hidden_leftover_ahead_of_the_live_element(page):
"""`.first` alone would wait on the hidden copy and time out."""
page.set_content(_HIDDEN_THEN_SHOWN)
started = time.monotonic()
assert page_utils.appears(page, ".b", timeout=2_000) is True
assert time.monotonic() - started < 1.0
# ── The callers that mean "wait" (final review, 2026-10-05) ─────────────────
# Each of these was `visible(timeout=...)` before P8 and answered instantly.
# A test per caller that fails if it is turned back into visible().
_LATE_SUBMENU = (
'<button id="claimLookupBtn71">View Claims Report</button>'
'<ul id="claimLookupUl1000" style="display:none">'
' <li><button>EXCEL</button></li></ul>'
'<script>'
'window.clicked = false;'
'const b = document.getElementById("claimLookupBtn71");'
'b.addEventListener("mouseenter", () => setTimeout(() => {'
' document.getElementById("claimLookupUl1000").style.display = "block";'
'}, 1000));'
'b.addEventListener("click", () => { window.clicked = true; });'
'</script>'
)
@case
def a_report_submenu_slower_than_the_hover_pause_is_not_clicked(page):
"""
`_open_report_submenu` hovers, pauses 600 ms, then gives the submenu
1.5s to appear before falling back to a click — the click its docstring
warns can close the parent dropdown instead. Here the submenu takes
1s: with an instant visible() the 1.5s was never waited and the click
fired.
"""
import io
import contextlib
from ecw_bot.pages import claim_report
page.set_content(_LATE_SUBMENU)
out = io.StringIO()
with contextlib.redirect_stdout(out):
claim_report._open_report_submenu(page)
assert "Report submenu opened (hover)" in out.getvalue(), out.getvalue()
assert page.evaluate("window.clicked") is False, "fell back to the click"
def main() -> int:
try:
from playwright.sync_api import sync_playwright
except ImportError:
print(" SKIP playwright is not installed")
return 0
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("_", " ")
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())