extends SceneTree const PageContext = preload("res://foundation/page_context.gd") const RiskCommitmentPageView = preload("res://entry/views/risk_commitment_page_view.gd") var failures: Array[String] = [] var intents: Array[Dictionary] = [] func _initialize() -> void: call_deferred("run_test") func run_test() -> void: root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS # Single target size (owner policy): every screen is authored at 1280x720. root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_KEEP root.content_scale_size = Vector2i(1280, 720) root.size = Vector2i(1280, 720) var view := RiskCommitmentPageView.new() view.name = "RiskCommitmentViewContractFixture" view.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) view.configure_presentation({"safe_area": 24, "text_scale": 1.0, "locale": "en"}) view.mount(PageContext.new({ "page_id": "risk_commitment", "caller_id": "risk_board", "return_page_id": "risk_board", "return_focus_path": "RiskCommitmentCancel", })) view.intent_emitted.connect(_record_intent) root.add_child(view) view.render(_fixture_view_data(false)) await process_frame var context = view.get("context") expect(context != null and String(context.get("page_id")) == "risk_commitment", "mount stores a copied PageContext") expect(view.find_child("RiskCommitmentMatte", true, false) != null, "View owns the commitment matte") expect(view.find_child("RiskCommitmentMasterCanvas", true, false) != null, "View owns the normalized 16:9 canvas") expect(view.find_child("RiskCommitmentStaticMaster", true, false) != null, "View owns the static master") expect(view.find_child("RiskFrozenMembers", true, false) != null and view.find_child("RiskFrozenMembers", true, false).get_child_count() == 4, "View renders four frozen Party members") expect(view.find_child("RiskConsequenceFact0", true, false) != null and view.find_child("RiskConsequenceFact5", true, false) != null, "View renders six consequence facts") var settings := view.find_child("RiskCommitmentSettings", true, false) as Button var details := view.find_child("RiskSelectedRiskDetails", true, false) as Button var consequences := view.find_child("RiskConsequenceBand", true, false) as Button var acknowledge := view.find_child("RiskCommitmentAcknowledge", true, false) as Button var begin := view.find_child("RiskBeginBattle", true, false) as Button var back := view.find_child("RiskCommitmentCancel", true, false) as Button expect(settings != null and not settings.disabled, "Settings intent is enabled") expect(details != null and not details.disabled, "risk-details intent is enabled") expect(consequences != null and not consequences.disabled, "consequence intent is enabled") expect(acknowledge != null and not acknowledge.disabled and begin != null and begin.disabled, "blocked command state is reflected") expect(back != null and not back.disabled and back.focus_mode != Control.FOCUS_NONE, "Back intent is focusable") for button in [settings, details, consequences, acknowledge]: if button != null: button.emit_signal("pressed") await process_frame expect(intents.size() == 4, "first four intents are emitted exactly once") expect(intents[0].get("intent_id") == "settings", "Settings intent is named") expect(intents[1].get("intent_id") == "review_risk_details", "risk-details intent is named") expect(intents[2].get("intent_id") == "review_consequences", "consequence intent is named") expect(intents[3].get("intent_id") == "acknowledge", "acknowledge intent is named") view.render(_fixture_view_data(true)) await process_frame var ready_begin := view.find_child("RiskBeginBattle", true, false) as Button var ready_acknowledge := view.find_child("RiskCommitmentAcknowledge", true, false) as Button expect(ready_begin != null and not ready_begin.disabled and ready_acknowledge != null and ready_acknowledge.disabled, "ready command state is reflected") if ready_begin != null: ready_begin.emit_signal("pressed") await process_frame expect(intents.size() == 5 and intents[4].get("intent_id") == "begin_battle", "Begin Battle intent is named") var ready_back := view.find_child("RiskCommitmentCancel", true, false) as Button if ready_back != null: ready_back.emit_signal("pressed") await process_frame expect(intents.size() == 6 and intents[5].get("intent_id") == "back", "Back intent is named") view.render({"ok": false, "locale": "en", "notice": "Risk unavailable"}) await process_frame expect(view.find_child("RiskCommitmentInvalidOverlay", true, false) != null, "View renders invalid state from supplied data") expect(view.find_child("RiskCommitmentMasterOverlay", true, false) == null, "invalid render removes the interactive overlay") var source := FileAccess.get_file_as_string("res://entry/views/risk_commitment_page_view.gd") expect(not source.contains("FormationRiskService"), "View does not import FormationRiskService") expect(not source.contains("BattleService"), "View does not import BattleService") expect(not source.contains("RuntimeContentRegistry"), "View does not resolve RuntimeContentRegistry") expect(not source.contains("current_screen"), "View does not own route state") expect(not source.contains("res://compat/"), "View does not import compatibility facade") view.queue_free() await process_frame if failures.is_empty(): print("ABG_M36_RISK_COMMITMENT_VIEW_CONTRACT_OK mount=true normalized_canvas=true static_master=true party=4 facts=6 intents=settings,review_risk_details,review_consequences,acknowledge,begin_battle,back invalid=true forbidden_imports=true") quit(0) else: for failure in failures: push_error(failure) quit(1) func _record_intent(intent_id: String, payload: Dictionary) -> void: intents.append({"intent_id": intent_id, "payload": payload}) func _fixture_view_data(acknowledged: bool) -> Dictionary: var facts: Array = [] for item in [ ["THREAT", "3 · 2 WAVES", "!", Color("#a74337")], ["UNCERTAINTY", "ONE INTENT OPEN", "?", Color("#5d3f91")], ["ENTRY COIN", "4 · 34 LEFT", "¢", Color("#a07816")], ["REWARD", "27 · ENHANCED", "◆", Color("#3d7449")], ["RECOVERY", "MODERATE", "!", Color("#a44a2e")], ["RETREAT", "NEXT STAGE", "↩", Color("#2c67a3")], ]: facts.append({"label": item[0], "value": item[1], "text": item[1], "glyph": item[2], "color": item[3]}) return { "ok": true, "page_id": "risk_commitment", "locale": "en", "contract": {"contract_id": "CTR-R1-01", "coin": 38}, "risk": { "risk_option_id": "risk-2", "option": { "risk_option_id": "risk-2", "tier_id": "pressured", "risk": 1, "option_hash": "option-hash-001", "known_threat": {"enemy_count": 3, "wave_count": 2}, "uncertainty_id": "one_intent_unresolved", "entry_cost": 4, "reward_coin": 27, "reward_class_id": "enhanced_choice", "recovery_exposure_id": "moderate", }, }, "consequences": {"facts": facts, "count": facts.size()}, "party": { "snapshot_id": "snapshot-001", "snapshot_hash": "snapshot-hash-001", "front_to_rear_ids": ["r-1", "r-2", "r-3", "r-4"], "ordered_recruits": [ {"position": 0, "recruit": _recruit("r-1", "PF-B01", "Ari", "Hearthguard"), "relation": {"exposure_id": "highest", "reach_id": "front_two"}}, {"position": 1, "recruit": _recruit("r-2", "PF-B05", "Bram", "Farstring"), "relation": {"exposure_id": "high", "reach_id": "front_three"}}, {"position": 2, "recruit": _recruit("r-3", "PF-B07", "Cera", "Kindhand"), "relation": {"exposure_id": "guarded", "reach_id": "full_line"}}, {"position": 3, "recruit": _recruit("r-4", "PF-B03", "Dain", "Spear"), "relation": {"exposure_id": "rear", "reach_id": "limited"}}, ], "coverage": {"protection": 2, "long_reach": 2, "sustain": 1, "control": 1}, }, "dependency": {"ok": true, "code": "READY", "facts": {"ok": true, "code": "READY", "coin": 38, "cost": 4}}, "confirmation": {"acknowledged": acknowledged, "state": "acknowledged" if acknowledged else "required"}, "commands": { "settings_enabled": true, "selected_details_enabled": true, "consequences_review_enabled": true, "cancel_enabled": true, "acknowledge_enabled": not acknowledged, "begin_enabled": acknowledged, }, "notice": "", } func _recruit(recruit_id: String, profession_id: String, name: String, profession: String) -> Dictionary: return { "recruit_id": recruit_id, "profession_id": profession_id, "rendered_name_snapshot": {"en": name}, "profession_name": {"en": profession}, } func expect(condition: bool, message: String) -> void: if not condition: failures.append("M36 Risk Commitment View: %s" % message)