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>
163 lines
6.6 KiB
Python
163 lines
6.6 KiB
Python
"""Chrome tests — language selector, hide_streamlit_chrome, quit flow.
|
|
|
|
These verify the GUI plumbing that every page depends on. Failures here
|
|
cascade into every other page, so they run cheap and run first
|
|
(alphabetical name ordering after smoke).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from .conftest import collected_text, with_language
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# hide_streamlit_chrome mounts the selector
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestHideChromeMountsSelector:
|
|
"""``hide_streamlit_chrome()`` is the one place the language selector
|
|
is mounted. Every page that hides chrome (= every page) must get
|
|
exactly one sidebar selectbox with the i18n label."""
|
|
|
|
def test_home_has_one_sidebar_selectbox(self, home_app):
|
|
home_app.run()
|
|
# Only one selectbox in the sidebar today; if a page adds
|
|
# another, this becomes a weaker bound.
|
|
assert len(home_app.sidebar.selectbox) == 1, (
|
|
"expected exactly one sidebar selectbox (the language picker); "
|
|
f"got {len(home_app.sidebar.selectbox)}"
|
|
)
|
|
|
|
def test_selector_label_is_localized(self, home_app):
|
|
with_language(home_app, "es")
|
|
home_app.run()
|
|
labels = [sb.label for sb in home_app.sidebar.selectbox]
|
|
assert "Idioma" in labels, (
|
|
f"Spanish selector should be labelled 'Idioma'; got {labels}"
|
|
)
|
|
|
|
def test_selector_label_english_default(self, home_app):
|
|
home_app.run() # no with_language → default = en
|
|
labels = [sb.label for sb in home_app.sidebar.selectbox]
|
|
assert "Language" in labels
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Language selector switches session state
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestLanguageSwitch:
|
|
"""Picking 'es' in the selector flips ``st.session_state['ui_lang']``
|
|
and re-renders the page with Spanish strings on the next run."""
|
|
|
|
def test_default_language_is_english(self, home_app):
|
|
home_app.run()
|
|
# AppTest's session_state proxy doesn't implement .get(); use
|
|
# membership check + attribute access. Absence == default ("en").
|
|
lang = home_app.session_state["ui_lang"] if "ui_lang" in home_app.session_state else "en"
|
|
assert lang == "en"
|
|
text = collected_text(home_app)
|
|
assert "Clean. Normalize. Transform." in text
|
|
|
|
def test_selecting_spanish_persists_in_session(self, home_app):
|
|
home_app.run()
|
|
selector = home_app.sidebar.selectbox[0]
|
|
selector.select("es").run()
|
|
assert home_app.session_state["ui_lang"] == "es"
|
|
|
|
def test_selecting_spanish_re_renders_in_spanish(self, home_app):
|
|
home_app.run()
|
|
selector = home_app.sidebar.selectbox[0]
|
|
selector.select("es").run()
|
|
text = collected_text(home_app)
|
|
assert "Limpia. Normaliza. Transforma." in text, (
|
|
"after selecting Spanish, the home tagline should switch to "
|
|
f"'Limpia. Normaliza. Transforma.'; got:\n{text[:300]}"
|
|
)
|
|
|
|
def test_selecting_back_to_english_reverts(self, home_app):
|
|
# Start in Spanish, then flip back.
|
|
with_language(home_app, "es")
|
|
home_app.run()
|
|
assert "Limpia. Normaliza. Transforma." in collected_text(home_app)
|
|
|
|
selector = home_app.sidebar.selectbox[0]
|
|
selector.select("en").run()
|
|
text = collected_text(home_app)
|
|
assert "Clean. Normalize. Transform." in text
|
|
assert "Limpia. Normaliza. Transforma." not in text
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Footer + page_title localization
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestLocalizedChrome:
|
|
"""A spot-check on the parts of the chrome that aren't the selector:
|
|
the home-page privacy pill (visible to AppTest) and the upload
|
|
section heading. The sticky footer caption is rendered via a
|
|
component-iframe and isn't visible through ``collected_text``."""
|
|
|
|
def test_privacy_pill_english(self, home_app):
|
|
home_app.run()
|
|
text = collected_text(home_app)
|
|
assert "Runs 100% locally" in text
|
|
|
|
def test_privacy_pill_spanish(self, home_app):
|
|
with_language(home_app, "es")
|
|
home_app.run()
|
|
text = collected_text(home_app)
|
|
assert "Se ejecuta 100% en local" in text
|
|
|
|
def test_upload_section_heading_localizes(self, home_app):
|
|
with_language(home_app, "es")
|
|
home_app.run()
|
|
# The visible "Files" section heading is hard-coded English
|
|
# in the redesigned home page; what's still localized is the
|
|
# file_uploader widget's label (``upload.uploader_label_multi``).
|
|
# AppTest exposes uploaders separately from the text-bearing
|
|
# widget collections, so we check the uploader's label
|
|
# attribute directly.
|
|
labels = [u.label for u in home_app.file_uploader]
|
|
assert any("Importa archivos" in lbl for lbl in labels), (
|
|
f"Spanish uploader label missing; got: {labels}"
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Quit / Close page
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestClosePageShutsDownImmediately:
|
|
"""The Close page no longer renders a confirm button — visiting the
|
|
page IS the close action. Under pytest the ``os._exit`` thread is
|
|
skipped (so the test runner doesn't suicide), but the farewell
|
|
success caption still renders and we assert against it."""
|
|
|
|
def test_english_close_renders_shutdown_message(self, app_factory):
|
|
app = app_factory("99_Close")
|
|
app.run()
|
|
text = collected_text(app)
|
|
assert "Shutting down" in text or "Goodbye" in text, (
|
|
f"Close page missing shutdown caption; got:\n{text[:300]}"
|
|
)
|
|
# No confirm button — the page is the action.
|
|
labels = [b.label for b in app.button]
|
|
assert not any("Close the app" in lbl for lbl in labels), (
|
|
f"Close page should not render a confirm button; got: {labels}"
|
|
)
|
|
|
|
def test_spanish_close_renders_localized_shutdown_message(self, app_factory):
|
|
app = app_factory("99_Close")
|
|
with_language(app, "es")
|
|
app.run()
|
|
text = collected_text(app)
|
|
# ``quit.shutting_down`` in the es pack.
|
|
assert "Cerrando" in text or "Apagando" in text or "Adiós" in text, (
|
|
f"Spanish Close page missing localized shutdown caption; got:\n{text[:300]}"
|
|
)
|
|
|
|
|