"""Headless render test for the Streamlit UI. Uses streamlit.testing.v1.AppTest to actually EXECUTE ui/app.py (not just boot the server), including the satellite panel and the weather block, and asserts no exception is raised. This catches version-sensitive widget signatures (e.g. st.image keyword args) that an HTTP-200 check on the static shell would miss. Requires the network (NASA/Open-Meteo) for the satellite/weather paths; run when online. The API server need not be running — the UI falls back to the in-process engine. Run: python tests/test_ui_render.py """ from __future__ import annotations import os import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) os.environ.setdefault("AGROSENSE_EMBEDDING_BACKEND", "hashing") from streamlit.testing.v1 import AppTest APP = str(Path(__file__).resolve().parent.parent / "ui" / "app.py") def main() -> int: at = AppTest.from_file(APP, default_timeout=120).run() assert not at.exception, f"initial render raised: {at.exception}" # Top-bar tickers: news + the slow-scroll commodities ticker (Gold/Silver/etc.). md0 = " ".join((m.value or "") for m in at.markdown) assert "Commodities" in md0 and "Gold" in md0, "commodities ticker did not render" print("PASS initial render + commodities ticker") # Setting a location enables the satellite checkbox AND drives the top-bar # local-weather strip (rendered below the news ticker). at.text_input[0].set_value("Belagavi").run() assert not at.exception, f"after location: {at.exception}" assert at.checkbox, "expected a satellite checkbox" md = " ".join((m.value or "") for m in at.markdown) assert "🌤️" in md and "°C" in md, "local weather strip did not render" print("PASS location set + weather strip") # Ticking it executes get_satellite() + render_satellite() (st.image/metric/columns). at.checkbox[0].set_value(True).run() assert not at.exception, f"satellite panel raised: {at.exception}" print("PASS satellite panel rendered") # The decision-advisories checkbox executes get_advisories() + render_advisories() # (st.error/warning/info + st.image) over the fused weather+satellite signals. at.checkbox[1].set_value(True).run() assert not at.exception, f"advisories panel raised: {at.exception}" print("PASS advisories panel rendered") # The environment checkbox executes get_environment() + render_environment() # (st.metric, st.columns) over the live Open-Meteo profile. at.checkbox[2].set_value(True).run() assert not at.exception, f"environment panel raised: {at.exception}" print("PASS environment panel rendered") # The planetary checkbox executes get_planetary() + render_planetary() # (st.markdown + st.dataframe) over the locally-computed ephemeris. at.checkbox[3].set_value(True).run() assert not at.exception, f"planetary panel raised: {at.exception}" print("PASS planetary panel rendered") # The hazards checkbox executes get_hazards() + render_hazards() (NASA EONET/FIRMS). at.checkbox[4].set_value(True).run() assert not at.exception, f"hazards panel raised: {at.exception}" print("PASS hazards panel rendered") # A query exercises the answer + weather block render path. at.chat_input[0].set_value("paddy disease control").run() assert not at.exception, f"query render raised: {at.exception}" print("PASS query + weather render") # The NDVI-populated branch (st.line_chart) is credential-gated, so the run # above only hits the "not configured" caption. Verify that exact widget # signature works in this Streamlit version, with no creds needed. chart = AppTest.from_string( 'import streamlit as st\n' 'st.line_chart({"date": ["2026-05-01", "2026-05-20"], "NDVI": [0.52, 0.71]}, ' 'x="date", y="NDVI")\n', default_timeout=60, ).run() assert not chart.exception, f"st.line_chart signature failed: {chart.exception}" print("PASS NDVI line_chart signature") # The market-prices render path (st.dataframe) is key-gated; verify its # signature in this Streamlit version without a data.gov.in key. table = AppTest.from_string( 'import streamlit as st\n' 'st.dataframe([{"Market": "Hubli", "Modal": 1500}], hide_index=True)\n', default_timeout=60, ).run() assert not table.exception, f"st.dataframe signature failed: {table.exception}" print("PASS prices dataframe signature") print("\nUI RENDER OK") return 0 if __name__ == "__main__": raise SystemExit(main())