167 lines
7.5 KiB
GDScript
167 lines
7.5 KiB
GDScript
extends SceneTree
|
|
|
|
var failures := 0
|
|
|
|
|
|
class ScreenHost extends Control:
|
|
var shown_screen := ""
|
|
|
|
func show_screen(screen_name: String, _payload: Variant = null) -> void:
|
|
shown_screen = screen_name
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
|
|
func _run() -> void:
|
|
var prior_environment: String = ProjectSettings.get_setting("step3/deployment_environment", "development")
|
|
ProjectSettings.set_setting("step3/deployment_environment", "uat")
|
|
var main_screen = load("res://scripts/ui/main.gd").new()
|
|
root.add_child(main_screen)
|
|
await process_frame
|
|
var nav_button := main_screen.sidebar.find_child("NavCcsCases2", true, false) as Button
|
|
_assert(nav_button != null and nav_button.visible, "UAT should expose a CCS Cases 2 navigation item")
|
|
_assert(main_screen.screens.has("ccs_cases_2"), "UAT should register the graphical CCS screen")
|
|
main_screen.queue_free()
|
|
await process_frame
|
|
ProjectSettings.set_setting("step3/deployment_environment", prior_environment)
|
|
|
|
var service = root.get_node("CCSService")
|
|
var prior_attempt: Dictionary = service.attempt.duplicate(true)
|
|
var prior_cases: Array = service.cases.duplicate(true)
|
|
var prior_origin: String = service.launch_origin
|
|
var host := ScreenHost.new()
|
|
host.size = Vector2(1200.0, 700.0)
|
|
root.add_child(host)
|
|
var screen = load("res://scripts/ui/ccs_cases_2_screen.gd").new()
|
|
screen.main = host
|
|
host.add_child(screen)
|
|
await process_frame
|
|
|
|
service.cases = [
|
|
{
|
|
"externalId": "interactive", "title": "Interactive case", "system": "Endocrine", "difficulty": 2,
|
|
"realTimeMinutes": 20, "interactiveInterviewAvailable": true, "graphicalPresentationAvailable": true,
|
|
"graphicalPresentation": {"sceneKey": "doctor_office", "patientAssetKey": "patient_44_white_male"}
|
|
},
|
|
{"externalId": "legacy", "title": "Legacy case", "interactiveInterviewAvailable": false}
|
|
]
|
|
service.attempt = {}
|
|
screen.cases_loaded = true
|
|
screen.active_loaded = true
|
|
screen._render_state()
|
|
await process_frame
|
|
_assert(screen.current_view == "case_selection", "CCS Cases 2 should begin with compatible case selection")
|
|
_assert(screen.case_list.get_child_count() == 1, "legacy cases should be excluded from graphical selection")
|
|
|
|
service.attempt = _sample_attempt(false)
|
|
screen._render_state()
|
|
await process_frame
|
|
_assert(screen.current_view == "doctor_office", "an active interactive attempt should open the doctor office")
|
|
_assert(not screen.active_scene.is_patient_dialogue_visible(), "the opening should wait for a clinician dialogue selection")
|
|
_assert(screen.active_scene.choice_container.get_child_count() == 1, "the scene should render server-provided dialogue choices")
|
|
_assert(is_equal_approx(screen.stage_root.scale.x, screen.stage_root.scale.y), "the graphical stage should preserve its aspect ratio")
|
|
var stale_graphical_attempt := _sample_attempt(false)
|
|
stale_graphical_attempt.experienceMode = "graphical_interview"
|
|
stale_graphical_attempt.openingCompleted = true
|
|
service.attempt = stale_graphical_attempt
|
|
screen._render_state()
|
|
await process_frame
|
|
_assert(screen.current_view == "doctor_office", "legacy completion flags should not block a graphical interview")
|
|
service.attempt = _sample_branching_attempt()
|
|
screen._render_state()
|
|
await process_frame
|
|
_assert(screen.active_scene.is_patient_dialogue_visible(), "a selected topic should display the patient's response")
|
|
_assert(not screen.active_scene.choice_container.visible, "the next choices should wait until the patient finishes speaking")
|
|
screen.active_scene.complete_dialogue_reveal()
|
|
_assert(screen.active_scene.choice_container.columns == 2, "branching interview choices should use two columns")
|
|
_assert(screen.active_scene.choice_container.get_child_count() == 8, "all available branching topics should render")
|
|
|
|
service.attempt = _sample_attempt(true)
|
|
screen._on_attempt_changed()
|
|
await process_frame
|
|
_assert(screen.active_scene.dialogue_text.text == "Okay, doctor.", "attempt updates should advance the rendered patient response")
|
|
_assert(not screen.active_scene.choice_container.visible, "the terminal action should wait for the closing response")
|
|
screen.active_scene.complete_dialogue_reveal()
|
|
_assert(screen.active_scene.choice_container.get_child_count() == 1, "terminal dialogue should expose the management handoff")
|
|
screen.active_scene.interview_continue_requested.emit()
|
|
_assert(host.shown_screen == "ccs_player", "terminal dialogue should hand off the same attempt to the CCS player")
|
|
|
|
service.attempt = {"id": "classic-attempt", "state": "active", "openingCompleted": false, "interview": {}}
|
|
host.shown_screen = ""
|
|
screen._on_attempt_changed()
|
|
await process_frame
|
|
_assert(screen.current_view == "case_selection", "unsupported active attempts should remain outside the classic player")
|
|
_assert(host.shown_screen.is_empty(), "CCS Cases 2 should not trigger classic opening overlays")
|
|
|
|
service.attempt = _sample_graphical_debrief()
|
|
screen._on_attempt_changed()
|
|
await process_frame
|
|
_assert(screen.current_view == "interview_debrief", "a completed graphical interview should remain in CCS Cases 2")
|
|
_assert(screen.active_scene.find_child("InterviewDebrief", true, false) != null, "the graphical case should render its debrief")
|
|
|
|
screen.on_hide()
|
|
await process_frame
|
|
_assert(screen.active_scene == null, "hiding CCS Cases 2 should release the doctor scene")
|
|
host.queue_free()
|
|
service.attempt = prior_attempt
|
|
service.cases = prior_cases
|
|
service.launch_origin = prior_origin
|
|
await process_frame
|
|
quit(1 if failures > 0 else 0)
|
|
|
|
|
|
func _sample_attempt(completed: bool) -> Dictionary:
|
|
var node := {
|
|
"id": "done" if completed else "greeting",
|
|
"patientText": "Okay, doctor." if completed else "Hello, doctor.",
|
|
"isTerminal": completed,
|
|
"suppressPatientText": not completed,
|
|
"choices": [] if completed else [{"id": "ask_reason", "text": "What brings you in?", "nextNodeId": "done"}]
|
|
}
|
|
return {
|
|
"id": "attempt-1", "state": "active", "revision": 1,
|
|
"simulatedStartUtc": "2026-08-05T09:00:00Z", "simulatedMinute": 0,
|
|
"graphicalPresentation": {"sceneKey": "doctor_office", "patientAssetKey": "patient_44_white_male"},
|
|
"openingCompleted": completed,
|
|
"interview": {"currentNodeId": node.id, "currentNode": node, "visitedChoiceIds": [], "completed": completed}
|
|
}
|
|
|
|
|
|
func _sample_graphical_debrief() -> Dictionary:
|
|
return {
|
|
"id": "attempt-2", "caseExternalId": "interactive", "state": "completed", "revision": 7,
|
|
"experienceMode": "graphical_interview", "simulatedStartUtc": "2026-08-05T09:00:00Z", "simulatedMinute": 0,
|
|
"graphicalPresentation": {"sceneKey": "doctor_office", "patientAssetKey": "patient_44_white_male"},
|
|
"interviewDebrief": {
|
|
"likelyDiagnosis": "Probable duodenal ulcer", "summary": "A focused history supports the assessment.",
|
|
"teachingPoints": ["Characterize dizziness.", "Screen for bleeding."]
|
|
},
|
|
"score": {"percent": 85.0}
|
|
}
|
|
|
|
|
|
func _sample_branching_attempt() -> Dictionary:
|
|
var choices: Array = []
|
|
for index in 8:
|
|
choices.append({"id": "topic_%d" % index, "text": "Ask about history topic %d" % (index + 1), "nextNodeId": "topic_%d" % index})
|
|
return {
|
|
"id": "attempt-1", "state": "active", "revision": 2,
|
|
"simulatedStartUtc": "2026-08-05T09:00:00Z", "simulatedMinute": 0,
|
|
"graphicalPresentation": {"sceneKey": "doctor_office", "patientAssetKey": "patient_44_white_male"},
|
|
"openingCompleted": false,
|
|
"interview": {
|
|
"currentNodeId": "chief_concern",
|
|
"currentNode": {"id": "chief_concern", "patientText": "I feel tired and lightheaded.", "isTerminal": false, "choices": choices},
|
|
"visitedChoiceIds": ["chief_concern"], "completed": false
|
|
}
|
|
}
|
|
|
|
|
|
func _assert(condition: bool, message: String) -> void:
|
|
if condition:
|
|
return
|
|
failures += 1
|
|
push_error(message)
|