feat(home): multi-file upload + per-file analysis, drop tool grid
Home is now upload + analysis only. The page accepts multiple files in
one go, analyzes each independently, and renders findings grouped by
filename in bordered containers. The 3-section tool-card grid is gone —
discovery happens via the sidebar now.
Mechanics:
- file_uploader uses accept_multiple_files=True. Each file's findings
cache in session_state["home_findings_by_file"] keyed by filename so
removing a file via Streamlit's "x" button drops its findings too,
and re-clicking Run only re-analyzes pending files.
- The first uploaded file is mirrored into the singular
home_uploaded_{name,bytes,size} keys so tool pages continue to pick
up an "active" upload through pickup_or_upload — no tool-page changes.
- New i18n keys: upload.intro_multi, upload.uploader_label_multi,
upload.clear_results, upload.empty_state. upload.heading text is
updated to "Upload one or more files to start" (EN + ES).
Dropped tests pinning the tool grid:
- TestHomeToolGridLocalization (test_chrome.py)
- test_home_tool_card_uses_es_name (test_smoke.py)
- TestLiteHomeGridBadges (test_lite_tier.py — locked-card lock-badge
assertions; locking is still enforced per-tool-page via
require_feature_or_render_upgrade)
2009 tests pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -114,8 +114,8 @@ class TestLocalizedChrome:
|
||||
with_language(home_app, "es")
|
||||
home_app.run()
|
||||
text = collected_text(home_app)
|
||||
# ``📤 Sube un archivo para empezar`` from the es pack.
|
||||
assert "Sube un archivo" in text
|
||||
# ``📤 Sube uno o más archivos para empezar`` from the es pack.
|
||||
assert "Sube uno o más archivos" in text
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -157,25 +157,3 @@ class TestQuitButtonRenders:
|
||||
assert "trabajo sin guardar" in text
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Tool cards use localized names on the home grid
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestHomeToolGridLocalization:
|
||||
"""The home grid pulls tool display names through ``tool_name()`` in
|
||||
``tools_registry``. The Spanish pack provides translations for every
|
||||
tool id; a regression in that wiring would make Spanish users see
|
||||
English names. Pin a few representative ones."""
|
||||
|
||||
@pytest.mark.parametrize("needle", [
|
||||
"Buscar duplicados",
|
||||
"Limpiar texto",
|
||||
"Estandarizar formatos",
|
||||
"Corregir valores faltantes",
|
||||
"Mapear columnas",
|
||||
])
|
||||
def test_es_tool_name_on_home_grid(self, home_app, needle):
|
||||
with_language(home_app, "es")
|
||||
home_app.run()
|
||||
text = collected_text(home_app)
|
||||
assert needle in text, f"missing localized tool name {needle!r}"
|
||||
|
||||
@@ -94,36 +94,6 @@ class TestLiteLockedPages:
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Home grid shows lock badges
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
class TestLiteHomeGridBadges:
|
||||
def test_locked_tool_card_shows_lock_badge(
|
||||
self, lite_license, home_app,
|
||||
):
|
||||
home_app.run()
|
||||
text = collected_text(home_app)
|
||||
# Fix Missing Values is locked under Lite — its card should
|
||||
# have a 🔒 Locked badge.
|
||||
# We assert the lock glyph appears alongside the locked tool's
|
||||
# display name. Streamlit renders the markdown verbatim so the
|
||||
# ``🔒 Locked`` text appears in the page markdown stream.
|
||||
assert "🔒" in text or "Locked" in text, (
|
||||
"home grid missing lock badge for Lite-locked tool"
|
||||
)
|
||||
|
||||
def test_unlocked_tool_card_no_lock(self, lite_license, home_app):
|
||||
home_app.run()
|
||||
# Dedup is unlocked under Lite. Its card markdown should NOT
|
||||
# contain a lock glyph adjacent to its name. We can't easily
|
||||
# scope by card without parsing the markdown stream, but we
|
||||
# can confirm both ``Ready`` (unlocked) and ``Locked``
|
||||
# (locked) badges coexist on the page.
|
||||
text = collected_text(home_app)
|
||||
assert "Ready" in text or "Listo" in text
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Sidebar status shows Lite
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -87,15 +87,6 @@ class TestHomePageRenders:
|
||||
text = collected_text(home_app)
|
||||
assert "Tus datos nunca salen" in text or "Se ejecuta localmente" in text
|
||||
|
||||
def test_home_tool_card_uses_es_name(self, home_app):
|
||||
"""When the home grid renders in Spanish, the dedup card title
|
||||
must use the Spanish display name, not the English fallback."""
|
||||
with_language(home_app, "es")
|
||||
home_app.run()
|
||||
text = collected_text(home_app)
|
||||
assert "Buscar duplicados" in text
|
||||
|
||||
|
||||
class TestEveryPageRenders:
|
||||
"""Parametrize over (page, language). Failure tells you exactly which
|
||||
page + which language broke."""
|
||||
|
||||
Reference in New Issue
Block a user