fugee / tests /integration /test_interview_flow.py
helmo's picture
[fix] Choice controls never toggle visibility; non-signatories not asylum dests
d22000a
Raw
History Blame Contribute Delete
3.48 kB
"""tests/unit-style tests for the fully-scripted interview.
The interview is deterministic (no LLM): a fixed question bank, pre-translated
into every supported language, with fixed controls. These tests verify the
script integrity and the deterministic controls.
"""
import gradio as gr
from app.interview_script import (
QUESTIONS,
REVIEW_INDEX,
TR,
option_labels,
question_text,
t,
)
from app.phases.interview import build, control_updates
from app.phases.intake import LANGUAGES
from app.state.session import SessionState, State
def test_all_offered_languages_have_full_translations():
base_keys = set(TR["English"])
offered = {english for _native, english in LANGUAGES}
for lang in offered:
assert lang in TR, f"missing translations for {lang}"
missing = base_keys - set(TR[lang])
assert not missing, f"{lang} missing keys: {missing}"
def test_questions_cover_fields_and_phases_in_order():
fields = [q.field for q in QUESTIONS]
for required in ("current_country", "origin_country", "immediate_danger"):
assert required in fields
phases = [q.phase for q in QUESTIONS]
assert phases == sorted(phases) # non-decreasing
def test_question_text_templates_origin():
s = SessionState()
s.language = "English"
s.interview.origin_country = "Afghanistan"
reason_q = next(q for q in QUESTIONS if q.field == "free_text_history")
text = question_text("English", reason_q, s)
assert "Afghanistan" in text and "{origin}" not in text
def test_controls_are_deterministic_per_question():
s = SessionState(); s.language = "English"
# control order: (radio, grounds, docs, country, text). Choice controls
# (radio/grounds/docs) are driven by non-empty choices, not a visible flag;
# country/text are driven by visible=True.
def active(u):
radio, grounds, docs, country, text = u
if radio.get("choices"):
return "radio"
if grounds.get("choices"):
return "grounds"
if docs.get("choices"):
return "docs"
if country.get("visible"):
return "country"
if text.get("visible"):
return "text"
return None
by_field = {q.field: i for i, q in enumerate(QUESTIONS)}
assert active(control_updates(s, by_field["current_country"])) == "country"
assert active(control_updates(s, by_field["immediate_danger"])) == "radio" # yes/no
assert active(control_updates(s, by_field["persecution_types"])) == "grounds"
assert active(control_updates(s, by_field["documents_available"])) == "docs"
assert active(control_updates(s, by_field["free_text_history"])) == "text"
assert active(control_updates(s, REVIEW_INDEX)) == "radio" # confirm
# The inactive choice controls are emptied so CSS collapses them (never a
# stray visible CheckboxGroup leaking through).
u = control_updates(s, by_field["documents_available"])
assert u[1]["choices"] == [] and u[0]["choices"] == [] # grounds + radio cleared
def test_option_labels_localised():
danger_q = next(q for q in QUESTIONS if q.control == "yesno")
assert option_labels("Spanish", danger_q) == [t("Spanish", "opt_yes"), t("Spanish", "opt_no")]
assert option_labels("Spanish", danger_q) == ["Sí", "No"]
def test_build_starts_with_country_control():
with gr.Blocks():
ui = build()
assert ui.session is not None and ui.continue_event is not None