ZainabEman's picture
Upload 152 files
546c207 verified
Raw History Blame Contribute Delete
5.77 kB
"""End-to-end checks of the Streamlit app, run headlessly via AppTest.
These drive the real `app.py` script: type into the box, submit, inspect what
was rendered. They are deliberately few -- enough to prove the search path,
the multi-occurrence path and the error paths work.
"""
import pytest
from spareparts import config
pytest.importorskip("streamlit")
from streamlit.testing.v1 import AppTest # noqa: E402
pytestmark = pytest.mark.skipif(
not config.DB_PATH.exists(), reason="run `python -m spareparts.build_index` first"
)
APP = str(config.PROJECT_ROOT / "app.py")
def _search(term: str, timeout: int = 60) -> AppTest:
app = AppTest.from_file(APP, default_timeout=timeout).run()
app.text_input[0].set_value(term)
app.button[0].click().run()
return app
def _text(app: AppTest) -> str:
parts = [el.value for el in app.markdown] + [el.value for el in app.caption]
parts += [el.value for el in app.subheader] + [el.value for el in app.warning]
parts += [el.value for el in app.error] + [el.value for el in app.info]
return "\n".join(str(p) for p in parts)
def test_app_starts_without_a_search():
app = AppTest.from_file(APP, default_timeout=60).run()
assert not app.exception
assert app.title[0].value == "Machine Part Finder"
assert app.text_input[0].label == "Part number"
def test_valid_part_shows_metadata_and_an_image():
app = _search("30642")
assert not app.exception
body = _text(app)
assert "30642" in body
assert "worm,cpl." in body # canonical English name
assert "Motor-drive" in body # catalogue section title
assert "Located" in body # localization method, stated plainly
# PDF page and section number are shown as distinct facts.
metrics = {m.label: m.value for m in app.metric}
assert metrics["PDF page"] == "34"
assert metrics["Catalogue section"] == "07"
assert len(app.image) == 1, "the annotated diagram should be displayed"
def test_unknown_part_is_reported_not_crashed():
app = _search("99999")
assert not app.exception
assert app.error, "an unknown part must produce an error message"
assert "not found in the catalogue" in app.error[0].value
assert not app.image
@pytest.mark.parametrize("term", ["", " ", "abc", "!!!"])
def test_empty_and_non_numeric_input_is_handled(term):
app = _search(term)
assert not app.exception
assert not app.image
if term.strip():
assert app.warning or app.error
def test_messy_input_is_normalized_then_found():
app = _search(" part no. 30642 ")
assert not app.exception
assert not app.error
assert "worm,cpl." in _text(app)
def test_multiple_occurrences_offer_a_selector():
app = _search("73290") # drawn on ten catalogue pages
assert not app.exception
assert app.radio, "a part on several pages must offer a choice"
options = app.radio[0].options
assert len(options) > 1
assert all(o.startswith("Occurrence ") for o in options)
assert "PDF page" in options[0]
# One page is shown at a time, and it is one the user chose.
assert len(app.image) == 1
def test_selecting_another_occurrence_switches_page():
app = _search("73290")
first = {m.label: m.value for m in app.metric}["PDF page"]
app.radio[0].set_value(app.radio[0].options[2]).run()
assert not app.exception
second = {m.label: m.value for m in app.metric}["PDF page"]
assert first != second
def test_low_confidence_result_is_shown_as_approximate():
"""A conservative fallback must be visible as such, not dressed up."""
from spareparts import index_db
conn = index_db.connect()
try:
row = conn.execute(
"SELECT l.part_no FROM labels l JOIN components c ON c.label_id = l.id"
" WHERE c.method = 'capped' AND l.part_no IN"
" (SELECT part_no FROM labels GROUP BY part_no HAVING COUNT(*) = 1)"
" LIMIT 1"
).fetchone()
finally:
conn.close()
app = _search(row[0])
assert not app.exception
body = _text(app)
assert "Approximate" in body
assert "Confidence" in body
def test_listed_but_not_drawn_part_shows_the_page_and_says_so():
app = _search("70248") # listed on page 39, not drawn there
assert not app.exception
assert not app.error
assert app.warning, "the user must be told why there is no highlight"
assert "no callout" in app.warning[0].value
assert len(app.image) == 1, "the page itself should still be shown"
def test_no_ocr_engine_is_loaded_during_a_search():
"""Definition of done: searching must not run OCR or localization.
Checked in a subprocess so that nothing another test imported can mask it.
"""
import subprocess
import sys
import textwrap
script = textwrap.dedent(
"""
import sys
from streamlit.testing.v1 import AppTest
app = AppTest.from_file(sys.argv[1], default_timeout=60).run()
app.text_input[0].set_value("30642")
app.button[0].click().run()
assert not app.exception, app.exception
banned = [m for m in sys.modules
if "rapidocr" in m or m.startswith("spareparts.ocr")
or m.startswith("spareparts.localizer")]
print("LOADED:" + ",".join(sorted(banned)))
"""
)
result = subprocess.run(
[sys.executable, "-c", script, APP],
capture_output=True, text=True, cwd=str(config.PROJECT_ROOT), timeout=300,
)
assert result.returncode == 0, result.stderr[-2000:]
line = next(l for l in result.stdout.splitlines() if l.startswith("LOADED:"))
assert line == "LOADED:", f"search pulled in offline-only modules: {line}"