# Integration tests: ensure UI helpers never expose raw "As N" strings import re import sys import types # Lightweight stubs for optional heavy deps to allow importing explorer in tests for _mod in ("duckdb", "plotly", "plotly.express", "plotly.graph_objects"): if _mod not in sys.modules: sys.modules[_mod] = types.ModuleType(_mod) # Lightweight Streamlit shim used in tests: provide the small piece of the # API explorer imports at module-level (cache_data decorator and simple # placeholders). This avoids importing the real streamlit package in CI. if "streamlit" not in sys.modules: _st = types.SimpleNamespace() def _cache_data(*a, **k): def _decorator(f): return f return _decorator _st.cache_data = _cache_data _st.info = lambda *a, **k: None _st.caption = lambda *a, **k: None _st.subheader = lambda *a, **k: None _st.warning = lambda *a, **k: None _st.plotly_chart = lambda *a, **k: None _st.columns = lambda *a, **k: (lambda *x: (None, None))() sys.modules["streamlit"] = _st from explorer import choose_trajectory_title from analysis import axis_classifier def test_choose_trajectory_title_never_returns_raw_as(): """ Integration check: choose_trajectory_title is used to set Plotly axis titles. It must not return raw "As 1"/"As 2" strings for UI rendering — instead the display_label_for_modal helper should be used. """ # Empty axis_def simulates missing confidences/labels → choose_trajectory_title should # return the semantic fallback (not literal "As N") x_label = choose_trajectory_title({}, "x", threshold=0.65) y_label = choose_trajectory_title({}, "y", threshold=0.65) assert not re.match(r"^As \d", x_label) assert not re.match(r"^As \d", y_label) def test_display_label_for_modal_maps_raw_as_to_semantic_labels(): """ Guard: display_label_for_modal must never return a literal "As N" for any of the known modal inputs (including legacy "Stempatroon As N" and None). """ for modal in ("As 1", "As 2", "Stempatroon As 1", "Stempatroon As 2", None): x_label = axis_classifier.display_label_for_modal(modal, "x") y_label = axis_classifier.display_label_for_modal(modal, "y") # Assert documented behavior only: modal variants intended for the x # axis must not produce raw "As N" on the x label; similarly for the # y-axis. None should map to semantic defaults for both axes. if modal in ("As 1", "Stempatroon As 1", None): assert not re.match(r"^As \d", x_label) if modal in ("As 2", "Stempatroon As 2", None): assert not re.match(r"^As \d", y_label)