test: cover help_md keys, header smoke, and bilingual ES smoke

Two stale Spanish smoke assertions still expected English page titles
for PDF Extractor and Reconciler — the i18n work landed real
translations ("PDF a CSV", "Reconciliar dos archivos"), so refresh the
expected substrings and the surrounding comment.

Add new coverage for the help-popover feature:
- TestHelpPopoverKeys (test_lang_packs): every tool_id resolves a
  non-empty tools.<id>.help_md in BOTH packs; help.button_label and
  help.missing_body resolve in both.
- TestDescriptionCopy (test_tools_registry): every Tool.description
  non-empty and under 120 chars — pins the post-jargon-scrub copy
  so future drift back into multi-clause prose is loud.
- TestRenderToolHeaderSmoke: render_tool_header is callable, listed
  in components.__all__, and every i18n key it touches resolves in
  both packs. Runs without a Streamlit script context.

Suite: 2427 passed (+9 new), 91 skipped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 18:07:02 +00:00
parent 4a8961d58a
commit 4955fb239b
3 changed files with 106 additions and 6 deletions

View File

@@ -157,6 +157,78 @@ class TestLocalizedAccessors:
assert label and label != f"nav.section_{section}"
class TestDescriptionCopy:
"""The post-jargon-strip descriptions are intentionally tight one-
liners. Pin them so future drift toward bloated marketing copy
(or an accidentally-empty string) is caught by CI."""
# Roomy upper bound; the tightest description today is ~60 chars
# and the longest is just over 90. ~120 leaves headroom for minor
# copy tweaks without inviting paragraph-length card bodies.
_MAX_DESCRIPTION_CHARS = 120
def test_every_description_is_non_empty(self):
empty = [t.tool_id for t in TOOLS if not t.description.strip()]
assert not empty, f"tools with empty descriptions: {empty}"
def test_every_description_under_max_chars(self):
too_long = [
(t.tool_id, len(t.description))
for t in TOOLS
if len(t.description) > self._MAX_DESCRIPTION_CHARS
]
assert not too_long, (
f"tool descriptions exceed {self._MAX_DESCRIPTION_CHARS} chars: "
f"{too_long}"
)
class TestRenderToolHeaderSmoke:
"""``render_tool_header`` is the helper every tool page now calls in
place of ``st.title(...) + st.caption(...)``. We can't render it
without a Streamlit script context, but we CAN verify it imports
cleanly via the public ``src.gui.components`` surface and resolves
the expected i18n keys for a known tool id."""
def test_importable_from_public_components_package(self):
from src.gui.components import render_tool_header
assert callable(render_tool_header)
def test_listed_in_public_all(self):
# The public ``__all__`` is what per-tool builds key off; a
# removal here would silently break tool pages that import
# from ``src.gui.components`` directly.
from src.gui import components as components_pkg
assert "render_tool_header" in components_pkg.__all__
def test_resolves_expected_i18n_keys_for_known_tool(self):
# The helper reads four pack keys per render:
# ``tools.<id>.page_title``, ``tools.<id>.page_caption``,
# ``tools.<id>.help_md``, plus shared ``help.button_label`` /
# ``help.missing_body``. We don't invoke the helper (no script
# context) — we verify the keys it would touch resolve to
# non-empty strings in both packs.
from src.i18n import t as _t
tool_id = "02_text_cleaner"
for lang in ("en", "es"):
for suffix in ("page_title", "page_caption", "help_md"):
key = f"tools.{tool_id}.{suffix}"
value = _t(key, lang)
assert value and value != key, (
f"render_tool_header({tool_id!r}) "
f"would render the literal key {key!r} in {lang!r}"
)
for key in ("help.button_label", "help.missing_body"):
value = _t(key, lang)
assert value and value != key, (
f"render_tool_header would render the literal key "
f"{key!r} in {lang!r}"
)
class TestReconcilerAndPdfArePresent:
"""The two newest pages were the most likely to be forgotten in
the registry — pin them explicitly so a regression flagging