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>
973 lines
42 KiB
Python
973 lines
42 KiB
Python
r"""
|
|
conditions/priced_line_check.py — the reverse half of the operator's common
|
|
rule for every claim (2026-09-30): a CPT line that carries a billed fee but
|
|
is unchecked gets checked. Only that direction — a checked line is never
|
|
touched, and the fee is never written.
|
|
|
|
python tests\test_conditions_priced_line_check.py
|
|
|
|
Pure-data cases first (the shared priced-fee predicate, then detect()), then
|
|
page cases against BOTH claim screens, with the same injection helpers and
|
|
fixtures as test_conditions_zero_fee_uncheck.py: hcfa_claim.html, whose own
|
|
rows carry the live-verified "Assign To Patient" checkbox, and ub_claim.html,
|
|
whose own rows carry a plain checkbox with no ng-click at all — so on the UB
|
|
form a UB-style row is injected with the checkbox vaccine_claim.html's rows
|
|
use, and the fixture's own rows stand in for a row whose checkbox state is
|
|
unknown.
|
|
|
|
hcfa_claim.html's own G8510 (CPTROW_2) sits UNCHECKED at 65.00 — the 9103000
|
|
shape depression_dx_pointer re-includes, and exactly the line this rule
|
|
checks. Cases about some other line call `_neutralise_fixture_g8510()`
|
|
first, so that line cannot answer for them.
|
|
|
|
The run_all() cases are the point of registering this LAST. Each exclusion
|
|
condition — j2930_j2001_update (J2001), dummy_codes (DCONS),
|
|
medicare_telehealth ("other primary CPT codes") and depression_dx_pointer's
|
|
duplicate step — starts from an original grid where the line it excludes is
|
|
UNCHECKED but still PRICED. That line must end unchecked at 0.00, its
|
|
checkbox never clicked, and no result may report a check on it: the
|
|
check/uncheck toggle claim 9131000 showed between two steps of one
|
|
condition, here between two conditions. One contrast case runs this
|
|
condition FIRST instead, to prove that order is what prevents it.
|
|
|
|
Live evidence: 0 of the 39 saved claim snapshots had an unchecked priced
|
|
line — this is rare — and exactly one had a checked zero-fee line (85018 at
|
|
0.00, zero_fee_uncheck's half).
|
|
"""
|
|
|
|
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" / "hcfa_claim.html"
|
|
UB_FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "ub_claim.html"
|
|
VACCINE_FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "vaccine_claim.html"
|
|
|
|
from ecw_bot import conditions # noqa: E402
|
|
from ecw_bot.conditions import priced_line_check as plc # noqa: E402
|
|
from ecw_bot.conditions import (bh_h_code_modifier, # noqa: E402
|
|
dental_dummy_codes,
|
|
dental_payer_check,
|
|
depression_dx_pointer, dummy_codes,
|
|
injection_admin_code,
|
|
medicare_pps_code,
|
|
j2930_j2001_update, medicaid_uncheck_cpts,
|
|
medicare_billing_screening,
|
|
medicare_telehealth,
|
|
medicare_vaccine_a6, medicare_wellness_history,
|
|
medicaid_pregnancy_th,
|
|
telehealth, vaccine_admin_codes,
|
|
zero_fee_uncheck)
|
|
from ecw_bot.conditions.base import ( # noqa: E402
|
|
CPT_TAB_NOT_CONFIRMED, ConditionResult, run_all)
|
|
from ecw_bot.pages import claim_form # noqa: E402
|
|
from ecw_bot.selectors import claim_form as sel # noqa: E402
|
|
|
|
|
|
CASES = []
|
|
|
|
|
|
def case(fn):
|
|
CASES.append(fn)
|
|
return fn
|
|
|
|
|
|
def _cpt(code, billed=False, fee="150.00"):
|
|
"""One `extract()["cpt"]` row, cut down to what detect() reads."""
|
|
return {"code": code, "billed": billed, "billed_fee": fee}
|
|
|
|
|
|
def _inject_row(page, code, row_id, *, screen="hcfa", checked=False,
|
|
fee="150.00", has_checkbox=True, has_fee=True):
|
|
"""
|
|
Adds one real CPT row (a `CPTROW_` id) above the grid's trailing "*"
|
|
entry row — the same helper as test_conditions_zero_fee_uncheck.py's,
|
|
defaulting to this rule's shape (unchecked, priced). `screen` picks the
|
|
row's own markup:
|
|
|
|
* "hcfa" — hcfa_claim.html's shape: `data-fieldname` fields.
|
|
* "ub" — the UB form's `uBClaim*<n>` id prefixes, with the
|
|
"Assign To Patient" checkbox vaccine_claim.html's rows carry.
|
|
|
|
`has_checkbox` / `has_fee` drop the checkbox or the Billed Fee input
|
|
entirely — the rows whose state is unknown, which must be left alone.
|
|
"""
|
|
page.evaluate(
|
|
"""(a) => {
|
|
const n = a.rowId.replace('CPTROW_', '');
|
|
const tbody = document.querySelector(a.screen === 'ub'
|
|
? '#claimCPTCodesTable9999999999999 tbody'
|
|
: '#claimCPTCodesTable8888888888888 tbody');
|
|
const entryRow = tbody.querySelector('tr:last-child');
|
|
const esc = s => String(s).replace(/&/g, '&').replace(/"/g, '"');
|
|
const box = !a.hasCheckbox ? '' : (a.screen === 'ub'
|
|
? '<input type="checkbox" id="claimCPTBillToInsChk9999999999999' + n + '"'
|
|
+ ' ng-click="updateBillToIns(' + n + ')"'
|
|
: '<input type="checkbox" ng-click="updateBillToIns(' + n + ')"')
|
|
+ (a.checked ? ' checked' : '') + '>';
|
|
const codeInput = a.screen === 'ub'
|
|
? '<input id="uBClaimCPTCode' + n + '" type="text" value="' + esc(a.code) + '" readonly>'
|
|
: '<input data-fieldname="claimCPTCode" type="text" value="' + esc(a.code) + '" readonly>';
|
|
const feeInput = !a.hasFee ? '' : (a.screen === 'ub'
|
|
? '<input type="text" id="uBClaimBillFee' + n + '" value="' + esc(a.fee) + '">'
|
|
: '<input data-fieldname="ClaimCPTBilledFee" type="text" value="' + esc(a.fee) + '">');
|
|
const tr = document.createElement('tr');
|
|
tr.id = a.rowId;
|
|
tr.setAttribute('ng-repeat', 'cpt in CPTCodes');
|
|
tr.innerHTML = '<td>' + box + '</td><td>' + codeInput + '</td>'
|
|
+ '<td>' + feeInput + '</td>';
|
|
tbody.insertBefore(tr, entryRow);
|
|
}""", {"code": code, "rowId": row_id, "screen": screen,
|
|
"checked": checked, "fee": fee,
|
|
"hasCheckbox": has_checkbox, "hasFee": has_fee})
|
|
|
|
|
|
def _inject_full_g8510_row(page, row_id, *, checked, fee, icd1="1"):
|
|
"""
|
|
A second G8510 on hcfa_claim.html carrying all four ICD pointer fields
|
|
— depression_dx_pointer's retag step reads every one — the same helper
|
|
as test_conditions_zero_fee_uncheck.py's, plus `icd1` for a copy that
|
|
already carries the Z13.31 pointer ("2") alone.
|
|
"""
|
|
page.evaluate(
|
|
"""(a) => {
|
|
const tbody = document.querySelector(
|
|
'#claimCPTCodesTable8888888888888 tbody');
|
|
const entryRow = tbody.querySelector('tr:last-child');
|
|
const n = a.rowId.replace('CPTROW_', '');
|
|
const tr = document.createElement('tr');
|
|
tr.id = a.rowId;
|
|
tr.innerHTML =
|
|
'<td><input data-fieldname="claimCPTCode" value="G8510" readonly></td>'
|
|
+ '<td><input type="checkbox" ng-click="updateBillToIns(' + n + ')"'
|
|
+ (a.checked ? ' checked' : '') + '></td>'
|
|
+ '<td><input data-fieldname="ClaimCPTICD1" value="' + a.icd1 + '"></td>'
|
|
+ '<td><input data-fieldname="ClaimCPTICD2" value=""></td>'
|
|
+ '<td><input data-fieldname="ClaimCPTICD3" value=""></td>'
|
|
+ '<td><input data-fieldname="ClaimCPTICD4" value=""></td>'
|
|
+ '<td><input data-fieldname="ClaimCPTBilledFee" value="' + a.fee + '"></td>';
|
|
tbody.insertBefore(tr, entryRow);
|
|
}""", {"rowId": row_id, "checked": checked, "fee": fee, "icd1": icd1})
|
|
|
|
|
|
def _set_row(page, row_id, *, checked=None, fee=None):
|
|
"""Sets one existing row's checkbox and/or Billed Fee in place."""
|
|
page.evaluate(
|
|
"""(a) => {
|
|
const tr = document.getElementById(a.rowId);
|
|
if (a.checked !== null)
|
|
tr.querySelector('[ng-click*="updateBillToIns("]').checked = a.checked;
|
|
if (a.fee !== null)
|
|
tr.querySelector('[id^="uBClaimBillFee"], [data-fieldname="ClaimCPTBilledFee"]')
|
|
.value = a.fee;
|
|
}""", {"rowId": row_id, "checked": checked, "fee": fee})
|
|
|
|
|
|
def _neutralise_fixture_g8510(page):
|
|
"""
|
|
hcfa_claim.html's own G8510 (CPTROW_2) is unchecked at 65.00 — itself
|
|
an unchecked priced line. Checked here, so a case about some other line
|
|
is not answered by that one.
|
|
"""
|
|
_set_row(page, "CPTROW_2", checked=True)
|
|
|
|
|
|
def _count_checkbox_clicks(page, row_id):
|
|
"""
|
|
Counts clicks on one row's "Assign To Patient" checkbox, read back as
|
|
`window.clicksOn[row_id]` — the same helper the depression and
|
|
zero_fee_uncheck tests use. On the live screen that click is what fires
|
|
eCW's own `updateBillToIns()`, so a line that must stay excluded should
|
|
never receive one at all — not merely end up unchecked again.
|
|
"""
|
|
page.evaluate(
|
|
"""(rowId) => {
|
|
window.clicksOn = window.clicksOn || {};
|
|
window.clicksOn[rowId] = 0;
|
|
document.querySelector('#' + rowId + ' [ng-click*="updateBillToIns("]')
|
|
.addEventListener('click', () => { window.clicksOn[rowId] += 1; });
|
|
}""", row_id)
|
|
|
|
|
|
def _watch_fee_writes(page):
|
|
"""
|
|
Counts `input`/`change` events on every Billed Fee input on the grid as
|
|
`window.feeWrites` — a zero proves the fee was never written, not
|
|
merely left with the same value. Call after every row has been injected.
|
|
"""
|
|
page.evaluate(
|
|
"""(s) => {
|
|
window.feeWrites = 0;
|
|
document.querySelectorAll('tr[id^="CPTROW_"]').forEach(tr => {
|
|
tr.querySelectorAll(s).forEach(el => {
|
|
el.addEventListener('input', () => { window.feeWrites += 1; });
|
|
el.addEventListener('change', () => { window.feeWrites += 1; });
|
|
});
|
|
});
|
|
}""", sel.SEL_CPT_BILLED_FEE)
|
|
|
|
|
|
def _row_state(page, index):
|
|
"""(checked or None, fee or None) for the index-th real CPT row."""
|
|
row = page.locator(sel.SEL_CPT_ROWS).nth(index)
|
|
box = row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first
|
|
fee = row.locator(sel.SEL_CPT_BILLED_FEE).first
|
|
return (box.is_checked() if box.count() else None,
|
|
fee.input_value() if fee.count() else None)
|
|
|
|
|
|
def _run_all_logged(page, claim_id, data, **kwargs):
|
|
"""run_all(), with everything it printed."""
|
|
out = io.StringIO()
|
|
with contextlib.redirect_stdout(out):
|
|
results = run_all(page, claim_id, data, **kwargs)
|
|
return results, out.getvalue()
|
|
|
|
|
|
def _checks_reported_on(results, row):
|
|
"""
|
|
Every change in `results` that reports CHECKING `row`: any change
|
|
priced_line_check makes (it only ever checks), and depression's
|
|
re-include step, whose change is `{"row", "code"}` alone.
|
|
"""
|
|
return [(r.condition_name, c) for r in results for c in r.changes
|
|
if c.get("row") == row
|
|
and (r.condition_name == plc.NAME
|
|
or (r.condition_name == depression_dx_pointer.NAME
|
|
and set(c) == {"row", "code"}))]
|
|
|
|
|
|
def _assert_priced_line_check_ran_and_found_nothing(log, claim_id):
|
|
"""
|
|
The common case this rule is registered last for: detect() saw the
|
|
excluded line priced on the claim as read, and by the time apply()
|
|
looked, another condition had zeroed it.
|
|
"""
|
|
assert f"[COND priced_line_check {claim_id}]" in log, log
|
|
assert "nothing to check" in log, log
|
|
|
|
|
|
# =============================================================================
|
|
# The shared priced-fee predicate
|
|
# =============================================================================
|
|
|
|
@case
|
|
def is_priced_fee_reads_a_positive_amount_as_priced():
|
|
for value in ("150.00", "65", "0.01", "1,200.00", "$45.00", "$1,200.00",
|
|
" 65.00 "):
|
|
assert claim_form.is_priced_fee(value) is True, repr(value)
|
|
|
|
|
|
@case
|
|
def is_priced_fee_reads_zero_blank_non_numeric_and_negative_as_not_priced():
|
|
for value in ("", " ", None, "0", "0.00", ".00", "$0.00", "0,000.00",
|
|
"N/A", "abc", "--", "0.00 USD", "-5.00", "nan", "inf"):
|
|
assert claim_form.is_priced_fee(value) is False, repr(value)
|
|
|
|
|
|
@case
|
|
def is_priced_fee_and_is_zero_fee_never_both_hold():
|
|
# The two halves of the common rule can never both claim one line.
|
|
for value in ("", None, "0", "0.00", ".00", "$0.00", "65.00", "0.01",
|
|
"1,200.00", "$45.00", "N/A", "-5.00", "nan", "inf"):
|
|
assert not (claim_form.is_priced_fee(value)
|
|
and claim_form.is_zero_fee(value)), repr(value)
|
|
|
|
|
|
# =============================================================================
|
|
# detect()
|
|
# =============================================================================
|
|
|
|
@case
|
|
def detect_true_for_an_unchecked_priced_line():
|
|
data = {"cpt": [_cpt("99213", billed=True, fee="210.00"),
|
|
_cpt("90791", billed=False, fee="150.00")]}
|
|
assert plc.detect("C1", data) is True
|
|
|
|
|
|
@case
|
|
def detect_true_for_a_thousands_separated_fee():
|
|
assert plc.detect("C1", {"cpt": [_cpt("J7298", fee="1,200.00")]}) is True
|
|
|
|
|
|
@case
|
|
def detect_true_for_a_dollar_fee():
|
|
assert plc.detect("C1", {"cpt": [_cpt("J2001", fee="$45.00")]}) is True
|
|
|
|
|
|
@case
|
|
def detect_false_for_an_unchecked_zero_fee_line():
|
|
# Already excluded, and correctly — the 9131000 duplicate's shape.
|
|
for fee in ("0.00", "", "0", ".00", "$0.00"):
|
|
assert plc.detect("C1", {"cpt": [_cpt("G8510", fee=fee)]}) is False, fee
|
|
|
|
|
|
@case
|
|
def detect_false_for_a_checked_priced_line():
|
|
data = {"cpt": [_cpt("99213", billed=True, fee="210.00"),
|
|
_cpt("J7298", billed=True, fee="1200.00")]}
|
|
assert plc.detect("C1", data) is False
|
|
|
|
|
|
@case
|
|
def detect_false_when_the_checkbox_state_is_unknown():
|
|
# billed None: extract() found no "Assign To Patient" checkbox on the
|
|
# row. Unknown must never read as "unchecked".
|
|
assert plc.detect("C1", {"cpt": [_cpt("90791", billed=None)]}) is False
|
|
assert plc.detect("C1", {"cpt": [{"code": "90791",
|
|
"billed_fee": "150.00"}]}) is False
|
|
|
|
|
|
@case
|
|
def detect_false_for_a_non_numeric_or_negative_fee():
|
|
for fee in ("N/A", "abc", "0.00 USD", "-5.00"):
|
|
assert plc.detect("C1", {"cpt": [_cpt("90791", fee=fee)]}) is False, fee
|
|
|
|
|
|
@case
|
|
def detect_false_with_no_cpt_lines_or_no_fee_read():
|
|
assert plc.detect("C1", {"cpt": []}) is False
|
|
assert plc.detect("C1", {}) is False
|
|
assert plc.detect("C1", None) is False
|
|
# No billed_fee key at all: nothing was read, so nothing is priced.
|
|
assert plc.detect("C1", {"cpt": [{"code": "90791", "billed": False}]}) is False
|
|
|
|
|
|
# =============================================================================
|
|
# apply() — the professional/HCFA screen
|
|
# =============================================================================
|
|
|
|
@case
|
|
def apply_checks_unchecked_priced_lines_on_the_hcfa_screen(page):
|
|
"""
|
|
The fixture's own G8510 (unchecked, 65.00) plus two injected lines
|
|
priced the ways eCW can print a fee — "$45.00" and "1,200.00". Each is
|
|
checked and reported with its fee exactly as read; the fee itself is
|
|
never written. The checked 99213 and J7298 are never clicked.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
_inject_row(page, "85025", "CPTROW_3", fee="$45.00")
|
|
_inject_row(page, "36415", "CPTROW_4", fee="1,200.00")
|
|
for row_id in ("CPTROW_0", "CPTROW_1", "CPTROW_2", "CPTROW_3", "CPTROW_4"):
|
|
_count_checkbox_clicks(page, row_id)
|
|
_watch_fee_writes(page)
|
|
|
|
result = plc.apply(page, "9103000", {})
|
|
assert isinstance(result, ConditionResult)
|
|
assert result.condition_name == "priced_line_check"
|
|
assert result.changed is True
|
|
assert result.changes == [
|
|
{"row": 2, "code": "G8510", "fee": "65.00"},
|
|
{"row": 3, "code": "85025", "fee": "$45.00"},
|
|
{"row": 4, "code": "36415", "fee": "1,200.00"},
|
|
], result.changes
|
|
|
|
assert [_row_state(page, i) for i in range(5)] == [
|
|
(True, "210.00"), (True, "1200.00"), (True, "65.00"),
|
|
(True, "$45.00"), (True, "1,200.00")]
|
|
clicks = page.evaluate("window.clicksOn")
|
|
assert clicks == {"CPTROW_0": 0, "CPTROW_1": 0, "CPTROW_2": 1,
|
|
"CPTROW_3": 1, "CPTROW_4": 1}, clicks
|
|
assert page.evaluate("window.feeWrites") == 0, "the Billed Fee was written"
|
|
|
|
# A second pass has nothing left to check.
|
|
assert plc.apply(page, "9103000", {}).changed is False
|
|
|
|
|
|
@case
|
|
def apply_leaves_zero_fee_non_numeric_and_negative_lines_alone(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_row(page, "85018", "CPTROW_3", fee="0.00")
|
|
_inject_row(page, "85025", "CPTROW_4", fee="")
|
|
_inject_row(page, "81002", "CPTROW_5", fee="N/A")
|
|
_inject_row(page, "36415", "CPTROW_6", fee="-5.00")
|
|
for row_id in ("CPTROW_3", "CPTROW_4", "CPTROW_5", "CPTROW_6"):
|
|
_count_checkbox_clicks(page, row_id)
|
|
_watch_fee_writes(page)
|
|
|
|
result = plc.apply(page, "9103000", {})
|
|
assert result.changed is False, result.changes
|
|
assert result.changes == [], result.changes
|
|
assert [_row_state(page, i) for i in (3, 4, 5, 6)] == [
|
|
(False, "0.00"), (False, ""), (False, "N/A"), (False, "-5.00")]
|
|
assert set(page.evaluate("Object.values(window.clicksOn)")) == {0}, \
|
|
page.evaluate("window.clicksOn")
|
|
assert page.evaluate("window.feeWrites") == 0
|
|
|
|
|
|
@case
|
|
def apply_never_touches_a_checked_line_zero_fee_or_priced(page):
|
|
# A checked 0.00 line is zero_fee_uncheck's, never this rule's: only
|
|
# ever checks, never unchecks.
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_row(page, "85018", "CPTROW_3", checked=True, fee="0.00")
|
|
for row_id in ("CPTROW_0", "CPTROW_1", "CPTROW_2", "CPTROW_3"):
|
|
_count_checkbox_clicks(page, row_id)
|
|
|
|
result = plc.apply(page, "9103000", {})
|
|
assert result.changed is False, result.changes
|
|
assert [_row_state(page, i) for i in range(4)] == [
|
|
(True, "210.00"), (True, "1200.00"), (True, "65.00"), (True, "0.00")]
|
|
assert set(page.evaluate("Object.values(window.clicksOn)")) == {0}
|
|
|
|
|
|
@case
|
|
def apply_leaves_an_unchecked_row_with_no_fee_input_alone(page):
|
|
# No Billed Fee input at all: the fee is unknown, not priced.
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_row(page, "90791", "CPTROW_3", has_fee=False)
|
|
_count_checkbox_clicks(page, "CPTROW_3")
|
|
|
|
result = plc.apply(page, "9103000", {})
|
|
assert result.changed is False, result.changes
|
|
assert _row_state(page, 3) == (False, None)
|
|
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0
|
|
|
|
|
|
@case
|
|
def apply_leaves_a_priced_row_with_no_checkbox_alone(page):
|
|
# No "Assign To Patient" checkbox: whether it is billed is unknown.
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_row(page, "90791", "CPTROW_3", has_checkbox=False)
|
|
_watch_fee_writes(page)
|
|
|
|
result = plc.apply(page, "9103000", {})
|
|
assert result.changed is False, result.changes
|
|
assert _row_state(page, 3) == (None, "150.00")
|
|
assert page.evaluate("window.feeWrites") == 0
|
|
|
|
|
|
# =============================================================================
|
|
# apply() — the UB Claim form
|
|
# =============================================================================
|
|
|
|
@case
|
|
def apply_checks_an_unchecked_priced_line_on_the_ub_form(page):
|
|
page.goto(UB_FIXTURE.as_uri())
|
|
_inject_row(page, "90791", "CPTROW_2", screen="ub", fee="150.00")
|
|
_inject_row(page, "G8510", "CPTROW_3", screen="ub", fee="0.00")
|
|
_inject_row(page, "99213", "CPTROW_4", screen="ub", checked=True, fee="210.00")
|
|
for row_id in ("CPTROW_2", "CPTROW_3", "CPTROW_4"):
|
|
_count_checkbox_clicks(page, row_id)
|
|
_watch_fee_writes(page)
|
|
|
|
result = plc.apply(page, "9104000", {})
|
|
assert result.changes == [{"row": 2, "code": "90791", "fee": "150.00"}], result.changes
|
|
|
|
assert _row_state(page, 2) == (True, "150.00")
|
|
assert _row_state(page, 3) == (False, "0.00")
|
|
assert _row_state(page, 4) == (True, "210.00")
|
|
assert page.evaluate("window.clicksOn") == {
|
|
"CPTROW_2": 1, "CPTROW_3": 0, "CPTROW_4": 0}
|
|
assert page.evaluate("window.feeWrites") == 0, "the Billed Fee was written"
|
|
|
|
|
|
@case
|
|
def apply_leaves_ub_rows_without_the_assign_to_patient_checkbox_alone(page):
|
|
"""
|
|
ub_claim.html's own rows carry a plain checkbox with no
|
|
`updateBillToIns(` ng-click — not the live-verified "Assign To Patient"
|
|
box — so their billed state is unknown: one sitting unchecked at 74.00
|
|
must not be acted on.
|
|
"""
|
|
page.goto(UB_FIXTURE.as_uri())
|
|
page.evaluate("() => { document.getElementById('claimCPTBillToInsChk0')"
|
|
".checked = false; }")
|
|
|
|
result = plc.apply(page, "9104000", {})
|
|
assert result.changed is False, result.changes
|
|
assert page.locator("#claimCPTBillToInsChk0").is_checked() is False
|
|
assert page.locator("#uBClaimBillFee0").input_value() == "74.00"
|
|
|
|
|
|
# =============================================================================
|
|
# apply() — no change, and a tab that cannot be confirmed
|
|
# =============================================================================
|
|
|
|
@case
|
|
def apply_logs_why_when_there_is_nothing_to_check(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
out = io.StringIO()
|
|
with contextlib.redirect_stdout(out):
|
|
result = plc.apply(page, "9103000", {})
|
|
assert result == ConditionResult.none("priced_line_check"), result
|
|
_assert_priced_line_check_ran_and_found_nothing(out.getvalue(), "9103000")
|
|
|
|
# Only on that path — not when a line was actually checked.
|
|
page.goto(FIXTURE.as_uri())
|
|
out = io.StringIO()
|
|
with contextlib.redirect_stdout(out):
|
|
result = plc.apply(page, "9103000", {})
|
|
assert result.changed is True, result.changes
|
|
assert "nothing to check" not in out.getvalue(), out.getvalue()
|
|
|
|
|
|
@case
|
|
def apply_leaves_the_claim_as_read_when_the_cpt_tab_cannot_be_confirmed(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
|
|
def tab_that_raises(page_):
|
|
raise RuntimeError("No CPT/HCPCS tab matched (simulated)")
|
|
|
|
real = claim_form.open_cpt_hcpcs_tab
|
|
claim_form.open_cpt_hcpcs_tab = tab_that_raises
|
|
out = io.StringIO()
|
|
try:
|
|
with contextlib.redirect_stdout(out):
|
|
result = plc.apply(page, "9103000", {})
|
|
finally:
|
|
claim_form.open_cpt_hcpcs_tab = real
|
|
assert result.changed is False, result.changes
|
|
assert _row_state(page, 2) == (False, "65.00"), "nothing may be touched"
|
|
assert "leaving the claim as read" in out.getvalue(), out.getvalue()
|
|
# Declined, not none(): never checked is not clean (review item D2).
|
|
assert result.review == CPT_TAB_NOT_CONFIRMED, result
|
|
|
|
|
|
# =============================================================================
|
|
# describe()
|
|
# =============================================================================
|
|
|
|
@case
|
|
def describe_names_each_checked_line_and_its_fee(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_row(page, "99213", "CPTROW_3", fee="150.00")
|
|
result = plc.apply(page, "9103000", {})
|
|
text = plc.describe(result)
|
|
lines = text.splitlines()
|
|
assert lines[0] == "Unchecked CPT line(s) with a billed fee checked:", text
|
|
assert lines[1:] == [" Row 4 (99213): unchecked with billed fee 150.00 -> checked"], text
|
|
|
|
|
|
@case
|
|
def describe_lists_every_line_in_grid_order():
|
|
result = ConditionResult(condition_name="priced_line_check", changed=True,
|
|
changes=[{"row": 0, "code": "99213", "fee": "150.00"},
|
|
{"row": 5, "code": "J7298", "fee": "1,200.00"}])
|
|
assert plc.describe(result).splitlines()[1:] == [
|
|
" Row 1 (99213): unchecked with billed fee 150.00 -> checked",
|
|
" Row 6 (J7298): unchecked with billed fee 1,200.00 -> checked",
|
|
], plc.describe(result)
|
|
|
|
|
|
# =============================================================================
|
|
# The registry
|
|
# =============================================================================
|
|
|
|
@case
|
|
def registered_last_with_zero_fee_uncheck_first():
|
|
"""
|
|
zero_fee_uncheck FIRST, this LAST, every specific condition between
|
|
them in its own order: an exclusion condition has always zeroed its
|
|
line before this looks at the grid.
|
|
"""
|
|
assert conditions.CONDITIONS[0] is zero_fee_uncheck, conditions.CONDITIONS
|
|
assert conditions.CONDITIONS[-1] is plc, conditions.CONDITIONS
|
|
assert conditions.CONDITIONS == [
|
|
zero_fee_uncheck,
|
|
telehealth, medicare_telehealth, depression_dx_pointer,
|
|
j2930_j2001_update, dummy_codes, dental_dummy_codes,
|
|
dental_payer_check, medicaid_uncheck_cpts,
|
|
medicare_billing_screening, medicare_vaccine_a6,
|
|
medicare_wellness_history, medicaid_pregnancy_th,
|
|
bh_h_code_modifier, vaccine_admin_codes, injection_admin_code,
|
|
medicare_pps_code,
|
|
plc,
|
|
], conditions.CONDITIONS
|
|
assert conditions.BY_NAME["priced_line_check"] is plc
|
|
assert plc.NAME == "priced_line_check"
|
|
|
|
|
|
# =============================================================================
|
|
# run_all() against the REAL registry — the positive cases
|
|
# =============================================================================
|
|
|
|
@case
|
|
def run_all_checks_an_unrelated_priced_line_and_reports_it(page):
|
|
"""
|
|
99213 unchecked at 150.00 on the claim as read, no other condition's
|
|
business: priced_line_check alone fires, and the line ends checked with
|
|
its fee untouched. `icd` is left out of the data on purpose: the
|
|
fixture carries Z13.31, which would fire depression_dx_pointer on its
|
|
own account.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_set_row(page, "CPTROW_0", checked=False, fee="150.00")
|
|
_count_checkbox_clicks(page, "CPTROW_0")
|
|
data = {"cpt": claim_form.extract(page)["cpt"]}
|
|
|
|
results = run_all(page, "9103000", data)
|
|
assert [r.condition_name for r in results] == ["priced_line_check"], results
|
|
assert results[0].changes == [{"row": 0, "code": "99213", "fee": "150.00"}], \
|
|
results[0].changes
|
|
assert _row_state(page, 0) == (True, "150.00")
|
|
assert page.evaluate("window.clicksOn['CPTROW_0']") == 1
|
|
|
|
|
|
@case
|
|
def run_all_fires_both_halves_of_the_common_rule_on_one_claim(page):
|
|
# A checked 0.00 line and an unchecked priced one: each half takes its
|
|
# own line, and neither touches the other's.
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_row(page, "85018", "CPTROW_3", checked=True, fee="0.00")
|
|
_inject_row(page, "90791", "CPTROW_4", checked=False, fee="150.00")
|
|
_count_checkbox_clicks(page, "CPTROW_3")
|
|
_count_checkbox_clicks(page, "CPTROW_4")
|
|
data = {"cpt": claim_form.extract(page)["cpt"]}
|
|
|
|
results = run_all(page, "9103000", data)
|
|
assert [r.condition_name for r in results] == \
|
|
["zero_fee_uncheck", "priced_line_check"], results
|
|
assert results[0].changes == [{"row": 3, "code": "85018", "fee": "0.00"}]
|
|
assert results[1].changes == [{"row": 4, "code": "90791", "fee": "150.00"}]
|
|
assert (_row_state(page, 3), _row_state(page, 4)) == \
|
|
((False, "0.00"), (True, "150.00"))
|
|
assert page.evaluate("window.clicksOn") == {"CPTROW_3": 1, "CPTROW_4": 1}
|
|
|
|
|
|
@case
|
|
def run_all_leaves_claim_9131000_alone(page):
|
|
"""
|
|
Live claim 9131000 (Logs\\run_20260929_161613.log), already correct:
|
|
the first G8510 checked at 65.00 and tagged Z13.31 alone, a later G8510
|
|
unchecked at 0.00. Nothing may change — not even with this rule in the
|
|
registry: the 0.00 copy is not priced, and nobody clicks it.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
page.evaluate("""() => {
|
|
const set = (rowId, field, value) => {
|
|
document.querySelector(
|
|
'#' + rowId + ' [data-fieldname="' + field + '"]').value = value;
|
|
};
|
|
set('CPTROW_0', 'ClaimCPTICD2', '3');
|
|
set('CPTROW_0', 'ClaimCPTICD3', '');
|
|
set('CPTROW_2', 'ClaimCPTICD1', '2');
|
|
}""")
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_full_g8510_row(page, "CPTROW_3", checked=False, fee="0.00", icd1="2")
|
|
_count_checkbox_clicks(page, "CPTROW_3")
|
|
|
|
results = run_all(page, "9131000", claim_form.extract(page))
|
|
assert results == [], results
|
|
assert _row_state(page, 3) == (False, "0.00")
|
|
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0
|
|
|
|
|
|
# =============================================================================
|
|
# run_all() against the REAL registry — NO TOGGLE on an excluded line
|
|
# =============================================================================
|
|
|
|
@case
|
|
def run_all_does_not_toggle_an_unchecked_priced_j2001(page):
|
|
"""
|
|
J2001 unchecked but still at 45.00 on the claim as read.
|
|
priced_line_check's detect() fires on it; j2930_j2001_update zeroes
|
|
its fee (no click — it is already unchecked); priced_line_check,
|
|
running last, finds it at 0.00 and checks nothing.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_row(page, "J2001", "CPTROW_3", checked=False, fee="45.00")
|
|
_count_checkbox_clicks(page, "CPTROW_3")
|
|
data = {"cpt": claim_form.extract(page)["cpt"]}
|
|
assert plc.detect("9103000", data) is True
|
|
assert j2930_j2001_update.detect("9103000", data) is True
|
|
|
|
results, log = _run_all_logged(page, "9103000", data)
|
|
assert [r.condition_name for r in results] == ["j2930_j2001_update"], results
|
|
assert results[0].changes == [{"row": 3, "code": "J2001",
|
|
"fee": {"before": "45.00", "after": "0.00"}}], \
|
|
results[0].changes
|
|
assert _checks_reported_on(results, 3) == [], results
|
|
assert _row_state(page, 3) == (False, "0.00")
|
|
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0, \
|
|
"J2001's checkbox was clicked — updateBillToIns() would fire"
|
|
_assert_priced_line_check_ran_and_found_nothing(log, "9103000")
|
|
|
|
|
|
@case
|
|
def run_all_does_not_toggle_an_unchecked_priced_dcons(page):
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_row(page, "DCONS", "CPTROW_3", checked=False, fee="45.00")
|
|
_count_checkbox_clicks(page, "CPTROW_3")
|
|
data = {"cpt": claim_form.extract(page)["cpt"]}
|
|
assert plc.detect("9121000", data) is True
|
|
assert dummy_codes.detect("9121000", data) is True
|
|
|
|
results, log = _run_all_logged(page, "9121000", data)
|
|
assert [r.condition_name for r in results] == ["dummy_codes"], results
|
|
assert results[0].changes == [{"row": 3, "code": "DCONS",
|
|
"fee": {"before": "45.00", "after": "0.00"}}], \
|
|
results[0].changes
|
|
assert _checks_reported_on(results, 3) == [], results
|
|
assert _row_state(page, 3) == (False, "0.00")
|
|
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0, \
|
|
"DCONS's checkbox was clicked — updateBillToIns() would fire"
|
|
_assert_priced_line_check_ran_and_found_nothing(log, "9121000")
|
|
|
|
|
|
_MEDICARE_DATA = {
|
|
"progress_notes": {"text": "Session Setting (PoS): TeleHealth"},
|
|
"insurance": [{"Name": "Humana Medicare"}],
|
|
}
|
|
|
|
|
|
@case
|
|
def run_all_does_not_toggle_a_medicare_telehealth_other_line(page):
|
|
"""
|
|
Document #22's "Other Primary CPT codes need to be unchecked and change
|
|
the fee amount as Zero", on the fixture's own G8510 — already unchecked
|
|
but still at 65.00, exactly an excluded-but-priced line. G2025 is added
|
|
through the fixture's Fee Schedule picker, every other line is zeroed,
|
|
and priced_line_check leaves G8510 excluded. Data shaped the way
|
|
test_conditions_medicare_telehealth.py's `_MEDICARE_DATA` is, plus the
|
|
CPT grid as read.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
_count_checkbox_clicks(page, "CPTROW_2")
|
|
data = dict(_MEDICARE_DATA, cpt=claim_form.extract(page)["cpt"])
|
|
assert plc.detect("9103000", data) is True
|
|
assert medicare_telehealth.detect("9103000", data) is True
|
|
|
|
results, log = _run_all_logged(page, "9103000", data)
|
|
assert [r.condition_name for r in results] == ["medicare_telehealth"], results
|
|
g8510 = [c for c in results[0].changes if c.get("row") == 2]
|
|
assert g8510 == [{"row": 2, "code": "G8510",
|
|
"fee": {"before": "65.00", "after": "0.00"},
|
|
# Unit E (2026-10-05): every change also carries the
|
|
# audio/video evidence describe() shows.
|
|
"signal": "telehealth", "mode": "video",
|
|
"mode_phrase": ""}], results[0].changes
|
|
assert _checks_reported_on(results, 2) == [], results
|
|
assert _row_state(page, 2) == (False, "0.00")
|
|
assert page.evaluate("window.clicksOn['CPTROW_2']") == 0, \
|
|
"G8510's checkbox was clicked — updateBillToIns() would fire"
|
|
_assert_priced_line_check_ran_and_found_nothing(log, "9103000")
|
|
# G2025, the one line #22 keeps billed, is still billed.
|
|
last = page.locator(sel.SEL_CPT_ROWS).last
|
|
assert last.locator(sel.SEL_CPT_CODE).first.input_value() == "G2025"
|
|
assert last.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() is True
|
|
|
|
|
|
@case
|
|
def pinned_needs_ruling_g2025_unconfirmed_other_line_gets_checked(page):
|
|
"""
|
|
PINNED, NEEDS A RULING (review finding, 2026-09-30) — the one normal,
|
|
non-raising path running last does NOT protect. When G2025 cannot be
|
|
confirmed (here: ub_claim.html, where the Fee Schedule picker flow has
|
|
no evidence), medicare_telehealth deliberately leaves every other CPT
|
|
row as read "for review". An "other" line sitting unchecked but priced
|
|
is then checked by this rule, last — billing a line document #22 says
|
|
must be excluded, against medicare_telehealth's own log line. Leaving
|
|
it for review instead is beyond rulings A/B/C.
|
|
"""
|
|
page.goto(UB_FIXTURE.as_uri())
|
|
_inject_row(page, "99214", "CPTROW_7", screen="ub", checked=False,
|
|
fee="65.00")
|
|
data = dict(_MEDICARE_DATA, cpt=claim_form.extract(page)["cpt"])
|
|
results, log = _run_all_logged(page, "9120000", data)
|
|
names = [r.condition_name for r in results]
|
|
# The fixture's H0023 is billed to Medicare, not a BH payer: #12
|
|
# unchecks and zeroes it (operator, 2026-10-12).
|
|
assert names == ["medicare_telehealth", "bh_h_code_modifier",
|
|
"priced_line_check"], results
|
|
assert results[1].changes == [{"row": 0, "code": "H0023",
|
|
"fee": {"before": "74.00",
|
|
"after": "0.00"}}], \
|
|
results[1].changes
|
|
assert "Could not confirm G2025" in log, log
|
|
assert [c for c in results[0].changes if "fee" in c] == [], results[0].changes
|
|
assert [(c["code"], c["fee"]) for c in results[2].changes] == \
|
|
[("99214", "65.00")], ("today: the other line is checked",
|
|
results[2].changes)
|
|
|
|
|
|
@case
|
|
def run_all_does_not_toggle_an_unchecked_priced_duplicate_g8510(page):
|
|
"""
|
|
depression_dx_pointer's duplicate step: the first G8510 checked at
|
|
65.00 is the one kept, and a later G8510 sits unchecked but still at
|
|
75.00. The duplicate step zeroes its fee (no click — already
|
|
unchecked); priced_line_check, last, finds it at 0.00.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_full_g8510_row(page, "CPTROW_3", checked=False, fee="75.00")
|
|
_count_checkbox_clicks(page, "CPTROW_3")
|
|
data = claim_form.extract(page)
|
|
assert plc.detect("9103000", data) is True
|
|
assert depression_dx_pointer.detect("9103000", data) is True
|
|
|
|
results, log = _run_all_logged(page, "9103000", data)
|
|
assert [r.condition_name for r in results] == ["depression_dx_pointer"], results
|
|
duplicate = [c for c in results[0].changes if "before" not in c]
|
|
assert duplicate == [{"row": 3, "code": "G8510",
|
|
"fee": {"before": "75.00", "after": "0.00"}}], \
|
|
results[0].changes
|
|
assert _checks_reported_on(results, 3) == [], results
|
|
assert _row_state(page, 2) == (True, "65.00"), "the kept G8510 stays billed"
|
|
assert _row_state(page, 3) == (False, "0.00")
|
|
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0, \
|
|
"the duplicate's checkbox was clicked — updateBillToIns() would fire"
|
|
_assert_priced_line_check_ran_and_found_nothing(log, "9103000")
|
|
|
|
|
|
@case
|
|
def run_all_checks_the_unrelated_line_but_never_the_excluded_one(page):
|
|
# Both on one claim: 99213 unchecked at 150.00 (checked) and J2001
|
|
# unchecked at 45.00 (excluded) — the rule tells them apart only by
|
|
# running after j2930_j2001_update has zeroed J2001.
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_set_row(page, "CPTROW_0", checked=False, fee="150.00")
|
|
_inject_row(page, "J2001", "CPTROW_3", checked=False, fee="45.00")
|
|
_count_checkbox_clicks(page, "CPTROW_0")
|
|
_count_checkbox_clicks(page, "CPTROW_3")
|
|
data = {"cpt": claim_form.extract(page)["cpt"]}
|
|
|
|
results = run_all(page, "9103000", data)
|
|
assert [r.condition_name for r in results] == \
|
|
["j2930_j2001_update", "priced_line_check"], results
|
|
assert results[1].changes == [{"row": 0, "code": "99213", "fee": "150.00"}], \
|
|
results[1].changes
|
|
assert _checks_reported_on(results, 3) == [], results
|
|
assert (_row_state(page, 0), _row_state(page, 3)) == \
|
|
((True, "150.00"), (False, "0.00"))
|
|
assert page.evaluate("window.clicksOn") == {"CPTROW_0": 1, "CPTROW_3": 0}
|
|
|
|
|
|
@case
|
|
def run_all_with_this_rule_first_would_toggle_j2001(page):
|
|
"""
|
|
The contrast that is WHY this is registered last: the same claim as
|
|
`run_all_does_not_toggle_an_unchecked_priced_j2001`, with this rule
|
|
moved ahead of j2930_j2001_update. It checks J2001, and J2001's own
|
|
condition unchecks it straight after — the same end state, reached by
|
|
two clicks on a line that must stay out and two contradictory changes
|
|
in the review box.
|
|
"""
|
|
page.goto(FIXTURE.as_uri())
|
|
_neutralise_fixture_g8510(page)
|
|
_inject_row(page, "J2001", "CPTROW_3", checked=False, fee="45.00")
|
|
_count_checkbox_clicks(page, "CPTROW_3")
|
|
data = {"cpt": claim_form.extract(page)["cpt"]}
|
|
|
|
results = run_all(page, "9103000", data,
|
|
conditions=[plc, j2930_j2001_update])
|
|
assert [r.condition_name for r in results] == \
|
|
["priced_line_check", "j2930_j2001_update"], results
|
|
assert results[0].changes == [{"row": 3, "code": "J2001", "fee": "45.00"}]
|
|
assert results[1].changes == [{"row": 3, "code": "J2001", "unchecked": True,
|
|
"fee": {"before": "45.00", "after": "0.00"}}]
|
|
assert _row_state(page, 3) == (False, "0.00")
|
|
assert page.evaluate("window.clicksOn['CPTROW_3']") == 2, "the toggle"
|
|
|
|
|
|
# =============================================================================
|
|
# run_all() — a KNOWN GAP where this rule meets vaccine_admin_codes' own
|
|
# pre-read decision, PINNED pending an operator ruling. Not the settled end
|
|
# state; see priced_line_check.py's module docstring.
|
|
# =============================================================================
|
|
|
|
_TDAP = "TETANUS DIPHTHERIA TOXOIDS AND ACELLULAR PERTUSSIS VACC"
|
|
_ADMIN_1ST = "IMMUNIZATION ADMIN SINGLE OR COMBINATION"
|
|
|
|
|
|
@case
|
|
def run_all_pins_the_vaccine_gap_for_an_unchecked_priced_admin_line(page):
|
|
"""
|
|
PINNED KNOWN GAP, pending an operator ruling — the mirror image of the
|
|
checked-zero-fee admin line zero_fee_uncheck used to open (fixed
|
|
2026-09-30). vaccine_admin_codes decides on the claim as read, where
|
|
the 90471 is UNCHECKED — so "no admin billed", and it adds a new 90471.
|
|
This rule then runs last and checks the old 90471, still at 30.00: two
|
|
admin lines billed for one vaccine. None of the 39 snapshots has this
|
|
shape (none has an unchecked priced line at all). Fixing it means
|
|
vaccine_admin_codes counting an unchecked priced admin line as billed —
|
|
another condition's behaviour, not ruled on.
|
|
"""
|
|
page.goto(VACCINE_FIXTURE.as_uri())
|
|
page.evaluate("rows => window.seedCptRows(rows)", [
|
|
{"code": "99396", "title": "PREVENTIVE VISIT EST AGE 40-64",
|
|
"fee": "250.00"},
|
|
{"code": "90734", "title": _TDAP, "fee": "60.00", "icd1": "2"},
|
|
{"code": "90471", "title": _ADMIN_1ST, "fee": "30.00",
|
|
"checked": False},
|
|
])
|
|
results = run_all(page, "9113000", claim_form.extract(page))
|
|
assert [r.condition_name for r in results] == \
|
|
["vaccine_admin_codes", "priced_line_check"], results
|
|
assert results[1].changes == [{"row": 2, "code": "90471", "fee": "30.00"}]
|
|
grid = [(r["code"], r["billed"], r["billed_fee"])
|
|
for r in claim_form.extract(page)["cpt"]]
|
|
assert grid == [("99396", True, "250.00"), ("90734", True, "60.00"),
|
|
("90471", True, "30.00"), ("90471", True, "30.00")], \
|
|
("today: two 90471 lines billed", grid)
|
|
|
|
|
|
def _run_cases() -> int:
|
|
for fixture in (FIXTURE, UB_FIXTURE, VACCINE_FIXTURE):
|
|
if not fixture.exists():
|
|
print(f" FAIL fixture missing: {fixture}")
|
|
return 1
|
|
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
|
|
|
|
|
|
def main() -> int:
|
|
from ecw_bot import settings as _cfg
|
|
# These cases exercise eCW's own click and Add flow, as a LIVE-WRITE run
|
|
# does. VERIFY clicks no box and adds no row (eCW saves both at once,
|
|
# 2026-10-16): that is tests/test_verify_no_live_writes.py.
|
|
orig = _cfg.SAVE_CORRECTIONS
|
|
_cfg.SAVE_CORRECTIONS = True
|
|
try:
|
|
return _run_cases()
|
|
finally:
|
|
_cfg.SAVE_CORRECTIONS = orig
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|