test: fix v3 branding drift, add reconcile CLI + registry coverage

GUI/lang-pack tests were asserting against pre-v3 strings ("Data
Cleaning Mastery", "Maestría en limpieza…") that the brand refresh
replaced with "UNALOGIX DataTools" + "Clean. Normalize. Transform."
Updated assertions to the current copy and switched the findings
panel tests to the redesigned flat-list layout (per-finding "Open
Tool →" buttons instead of per-tool expanders).

New coverage:
- tests/test_cli_reconcile.py (13) — preview/apply, tolerance flags,
  sign inversion, key flags, error paths, Excel input.
- tests/test_tools_registry.py (27) — unique tool_ids, page_slug →
  real file, valid sections/tiers, localized accessor fallbacks,
  explicit pins for PDF Extractor + Reconciler entries.
- tests/test_reconcile.py — one-side-empty, key-pass tagging,
  additional validation cases, input-DataFrame immutability.
- tests/gui/test_smoke.py — PAGE_SLUGS now includes 10_PDF_Extractor
  and 11_Reconciler in both en/es.
- tests/gui/test_workflows.py — TestPdfExtractorWorkflow and
  TestReconcilerWorkflow render checks.

Net: 2317 passed → 2418 passed, 0 failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-22 19:30:02 +00:00
parent ea99e292d2
commit 6627895a10
9 changed files with 737 additions and 80 deletions

View File

@@ -98,11 +98,19 @@ class TestHeader:
# ---------------------------------------------------------------------------
# Per-tool grouping → one expander per tool id
# Per-finding row → one "Open Tool" button per targeted finding
# ---------------------------------------------------------------------------
#
# The findings panel was redesigned (mockup-v2): it now renders ONE
# severity-sorted flat list rather than per-tool expanders. Each finding
# with a known tool id gets a tertiary button labelled
# ``"{Tool display name} →"`` that switches pages on click. Findings
# with no tool id (file-level CSV-shape warnings, encoding flags, etc.)
# render without a button — the description still shows so the user
# isn't blind to them.
class TestGrouping:
def test_findings_grouped_into_per_tool_expanders(self):
class TestRowsRenderForFindings:
def test_one_button_per_targeted_finding(self):
findings = [
_make_finding(tool="02_text_cleaner", id="whitespace_padding"),
_make_finding(tool="02_text_cleaner", id="nbsp_padding"),
@@ -110,96 +118,96 @@ class TestGrouping:
]
app = _harness(findings)
app.run()
labels = [e.label for e in app.expander]
# Two unique tools → two expanders. Each label carries the
# tool's display name + finding count.
text_cleaner_expanders = [lbl for lbl in labels if "Clean Text" in lbl]
format_expanders = [lbl for lbl in labels if "Standardize Formats" in lbl]
assert len(text_cleaner_expanders) == 1, (
f"expected one Clean Text expander; got: {labels}"
labels = [b.label for b in app.button]
# Each targeted finding gets its own "Open Tool" button — three
# findings → three buttons (two pointing at Clean Text, one at
# Standardize Formats).
clean_text_buttons = [l for l in labels if l == "Clean Text →"]
format_buttons = [l for l in labels if l == "Standardize Formats →"]
assert len(clean_text_buttons) == 2, (
f"expected 2 Clean Text buttons; got: {labels}"
)
assert len(format_expanders) == 1, (
f"expected one Standardize Formats expander; got: {labels}"
assert len(format_buttons) == 1, (
f"expected 1 Standardize Formats button; got: {labels}"
)
def test_tool_names_localize_in_spanish(self):
findings = [_make_finding(tool="02_text_cleaner")]
app = _harness(findings, lang="es")
app.run()
labels = [e.label for e in app.expander]
labels = [b.label for b in app.button]
assert any("Limpiar texto" in lbl for lbl in labels), (
f"Spanish tool name missing; expanders: {labels}"
)
def test_finding_count_in_expander_label(self):
findings = [
_make_finding(tool="02_text_cleaner", id=f"f{i}")
for i in range(3)
]
app = _harness(findings)
app.run()
labels = [e.label for e in app.expander]
# Pack template: "{tool} — {n} finding(s)"
text_cleaner_label = next(l for l in labels if "Clean Text" in l)
assert "3" in text_cleaner_label, (
f"expected count '3' in expander label; got {text_cleaner_label!r}"
f"Spanish tool name missing; buttons: {labels}"
)
# ---------------------------------------------------------------------------
# Open-tool button localizes
# Open-tool button labels — confirm the arrow + name format
# ---------------------------------------------------------------------------
class TestOpenToolButton:
"""Each tool section has an ``st.page_link`` to jump to that tool's
page. AppTest exposes page_links as ``app.button`` entries with
label ``"Open {tool}"`` (English) / ``"Abrir {tool}"`` (Spanish)."""
"""Each finding with a known tool gets a tertiary button labelled
``"{Tool name} →"``. The arrow + spacing is the affordance that
distinguishes the row's primary action from the title text."""
def test_open_tool_label_english(self):
findings = [_make_finding(tool="02_text_cleaner")]
app = _harness(findings)
app.run()
# ``st.page_link`` may show up under ``app.button`` or in the
# raw markdown. We probe both.
text = collected_text(app)
# Pack template: "Open {tool} →"
assert "Open Clean Text" in text
labels = [b.label for b in app.button]
assert "Clean Text →" in labels, (
f"expected 'Clean Text →' button; got: {labels}"
)
def test_open_tool_label_spanish(self):
findings = [_make_finding(tool="02_text_cleaner")]
app = _harness(findings, lang="es")
app.run()
text = collected_text(app)
# Pack template: "Abrir {tool} →"
assert "Abrir Limpiar texto" in text
labels = [b.label for b in app.button]
assert "Limpiar texto →" in labels, (
f"expected 'Limpiar texto →' button; got: {labels}"
)
# ---------------------------------------------------------------------------
# Untargeted findings (file-level) go in the "Other" expander
# Untargeted findings (file-level) render without an action button
# ---------------------------------------------------------------------------
class TestUntargetedFindings:
def test_untargeted_goes_to_other_expander_en(self):
"""A finding with ``tool=""`` (e.g., CSV BOM stripped at read time)
is file-level — no tool page to jump to — and the redesigned panel
renders the description without a button. We assert that the row
contributes nothing to ``app.button`` while still appearing in the
rendered markdown."""
def test_untargeted_renders_no_button_en(self):
findings = [
_make_finding(tool="", id="csv_bom_stripped"),
_make_finding(tool="", id="csv_bom_stripped", description="BOM stripped"),
_make_finding(tool="02_text_cleaner", id="nbsp_padding"),
]
app = _harness(findings)
app.run()
labels = [e.label for e in app.expander]
# Pack template: "Other / file-level — {n} finding(s)"
assert any("Other / file-level" in lbl for lbl in labels), (
f"untargeted expander missing; got: {labels}"
labels = [b.label for b in app.button]
# Only the targeted finding contributed a button.
assert "Clean Text →" in labels
# The BOM finding's description must still be visible somewhere.
all_md = "\n".join(
m.body for m in app.markdown if hasattr(m, "body")
)
assert "BOM stripped" in all_md, (
"untargeted finding's description should still render"
)
def test_untargeted_label_spanish(self):
findings = [_make_finding(tool="", id="csv_bom_stripped")]
def test_untargeted_renders_no_button_es(self):
findings = [_make_finding(
tool="", id="csv_bom_stripped", description="BOM eliminado",
)]
app = _harness(findings, lang="es")
app.run()
labels = [e.label for e in app.expander]
# Spanish pack: "Otros / a nivel de archivo — {n} hallazgo(s)"
assert any("Otros / a nivel de archivo" in lbl for lbl in labels), (
f"Spanish 'Other' expander missing; got: {labels}"
labels = [b.label for b in app.button]
# No tool id → no tool-jump button at all.
assert not any("" in lbl for lbl in labels), (
f"untargeted finding should not render a tool button; got: {labels}"
)