extends SceneTree const EntryShell = preload("res://entry/entry_shell.gd") const SettingsStore = preload("res://entry/settings_store.gd") const DraftService = preload("res://entry/initial_company_draft_service.gd") const ContractService = preload("res://entry/guild_contract_service.gd") const FormationService = preload("res://entry/formation_risk_service.gd") const DRAFT_PATH := "user://dev4_risk_draft.json" const COMPANY_PATH := "user://dev4_risk_company.json" const SEED := "02468ace13579bdffdb97531eca86420" const CONTRACT_OP := "aaaaaaaa22222222bbbbbbbb33333333" const HIRE_OP_1 := "12121212343434345656565678787878" const HIRE_OP_2 := "90909090ababababcdcdcdcdefefefef" const ORDER_OP := "13571357246824681357135724682468" const READY_OP := "deadbeefdeadbeefdeadbeefdeadbeef" var failures: Array[String] = [] var parsed_touches := 0 func _initialize() -> void: call_deferred("run_test") func run_test() -> void: _cleanup() root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND root.size = Vector2i(844, 390) await verify_locale("en") _cleanup() await verify_locale("zh_CN") _cleanup() if failures.is_empty(): print("ABG_DEV4_RISK_OK options=3 auto_selected=false recommended=false compare=threat,uncertainty,cost,reward,recovery,coverage compact_copy=true selection=local cancel=true back=guild settings_return=risk_board restart=readiness commitment=frozen_party begin_battle=disabled_dev5 no_battle_receipt=true locales=zh_CN,en text_100=true text_130=non_blocking parsed_touch=%d" % parsed_touches) quit(0) else: for failure in failures: push_error(failure) quit(1) func verify_locale(locale: String) -> void: var prepared := _create_prepared_company() if prepared.is_empty(): return var settings := SettingsStore.new().defaults() settings.setup_completed = true settings.locale = locale settings.text_scale = 1.0 var shell := EntryShell.new() shell.auto_start = false shell.initial_company_service.path = DRAFT_PATH shell.initial_company_service.company_path = COMPANY_PATH root.add_child(shell) await _frames(3) expect(shell.app_services.has_service(&"risk_board_page_controller"), "%s AppServices registers Risk Board controller" % locale) shell.settings = settings shell.setup_draft = settings.duplicate(true) shell.show_readiness_review(prepared) await _frames(3) await _touch(shell.find_child("ReadinessRisk", true, false)) expect(shell.current_screen == "risk_board", "%s accepted readiness reaches Risk Board" % locale) expect(shell.risk_view_data.ok and shell.risk_view_data.page_id == "risk_board", "%s Risk Board consumes controller snapshot" % locale) expect(shell.risk_view_data.selection.state == "no_selection" and not shell.risk_view_data.commands.review_enabled, "%s controller preserves no-auto-selection boundary" % locale) expect(shell.risk_selected_id.is_empty() and shell.find_child("RiskNoSelection", true, false) != null, "%s Risk Board begins with no automatic selection" % locale) var options: Array = prepared.active_contract.formation.risk_options for option in options: var control := shell.find_child("RiskOption%s" % String(option.risk_option_id), true, false) as Button expect(control != null and not bool(control.get_meta("selected", false)), "%s stable risk option begins unselected" % locale) _verify_compact_fact_copy(shell, locale, options) var before_hash := String(_draft_service().load_company().company.company_hash) var selected_id := String(options[1].risk_option_id) await _touch(shell.find_child("RiskOption%s" % selected_id, true, false)) expect(shell.risk_selected_id == selected_id and shell.find_child("RiskSelectedDetail", true, false) != null, "%s selecting one option expands its complete comparison" % locale) expect(shell.risk_view_data.selection.selected_risk_id == selected_id and shell.risk_view_data.commands.review_enabled, "%s controller refreshes selected option and Review eligibility" % locale) expect(String(_draft_service().load_company().company.company_hash) == before_hash, "%s risk selection changes no persisted Company state" % locale) var review := shell.find_child("RiskReview", true, false) as Button expect(review != null and not review.disabled, "%s selected risk enables review" % locale) await _touch(review) expect(shell.current_screen == "risk_commitment", "%s review opens attached commitment preview" % locale) expect(shell.find_child("RiskCommitmentDetail", true, false) != null and shell.find_child("RiskFrozenMembers", true, false).get_child_count() == 4, "%s commitment retains exact risk facts and frozen four-member party" % locale) var begin := shell.find_child("RiskBeginBattleUnavailable", true, false) as Button expect(begin != null and begin.disabled and shell.find_child("RiskBeginBattleReason", true, false) != null, "%s Begin Battle is visibly disabled with DEV-5 reason" % locale) var unchanged: Dictionary = _draft_service().load_company() expect(String(unchanged.company.company_hash) == before_hash and not unchanged.company.active_contract.has("battle_receipts"), "%s commitment preview creates no battle receipt or mutation" % locale) await _touch(shell.find_child("RiskCommitmentCancel", true, false)) expect(shell.current_screen == "risk_board" and shell.risk_selected_id == selected_id, "%s Cancel returns to the same selected risk" % locale) await _touch(shell.find_child("RiskSettings", true, false)) expect(shell.current_screen == "settings", "%s Risk Board opens Settings" % locale) await _touch(shell.find_child("SettingsBack", true, false)) expect(shell.current_screen == "risk_board" and shell.risk_selected_id == selected_id, "%s Settings returns to the same selected Risk Board" % locale) await _touch(shell.find_child("RiskBackReadiness", true, false)) expect(shell.current_screen == "guild_handoff", "%s Back returns to Guild" % locale) shell.queue_free() await _frames(3) var resumed := EntryShell.new() resumed.auto_start = false resumed.initial_company_service.path = DRAFT_PATH resumed.initial_company_service.company_path = COMPANY_PATH root.add_child(resumed) await _frames(2) resumed.settings = settings resumed.setup_draft = settings.duplicate(true) resumed._open_company_destination(unchanged.company) await _frames(3) expect(resumed.current_screen == "readiness_review" and resumed.risk_selected_id.is_empty(), "%s restart resumes readiness without persisting local risk selection" % locale) resumed.queue_free() await _frames(3) func _create_prepared_company() -> Dictionary: var draft: Dictionary = _draft_service().begin_or_load(SEED) if not draft.ok: return {} _draft_service().set_selection([String(draft.draft.offers[0].offer_id), String(draft.draft.offers[2].offer_id)]) var committed: Dictionary = _draft_service().commit_company() if not committed.ok: return {} var created: Dictionary = _contract_service().create_contract(CONTRACT_OP, "CTR-R1-01", "standard") if not created.ok: return {} var offers: Array = created.company.active_contract.market_board.recruit_offers _contract_service().hire_recruit(HIRE_OP_1, String(offers[0].source_offer_id), 14) var hired: Dictionary = _contract_service().hire_recruit(HIRE_OP_2, String(offers[1].source_offer_id), 14) if not hired.ok: return {} var begun: Dictionary = _formation_service().begin_formation() if not begun.ok: return {} var saved: Dictionary = _formation_service().save_order(ORDER_OP, begun.formation.base_front_to_rear_ids) if not saved.ok: return {} var accepted: Dictionary = _formation_service().accept_readiness(READY_OP, String(saved.receipt.order_hash)) return accepted.company if accepted.ok else {} func _draft_service() -> RefCounted: var service := DraftService.new() service.path = DRAFT_PATH service.company_path = COMPANY_PATH return service func _contract_service() -> RefCounted: var service := ContractService.new() service.company_path = COMPANY_PATH return service func _formation_service() -> RefCounted: var service := FormationService.new() service.company_path = COMPANY_PATH return service func _touch(control: Control) -> void: if control == null: expect(false, "touch target exists") return var center := control.get_global_rect().get_center() var press := InputEventScreenTouch.new() press.position = center press.pressed = true press.index = 0 Input.parse_input_event(press) await _frames(2) var release := InputEventScreenTouch.new() release.position = center release.pressed = false release.index = 0 Input.parse_input_event(release) parsed_touches += 1 await _frames(5) func _frames(count: int) -> void: for _index in range(count): await process_frame func _verify_compact_fact_copy(shell: Control, locale: String, options: Array) -> void: var prefixes := ["威胁", "信息", "成本", "奖励", "恢复", "覆盖"] if locale == "zh_CN" else ["THREAT", "INFO", "COST", "REWARD", "RECOVERY", "COVERAGE"] for option in options: var button := shell.find_child("RiskOption%s" % String(option.risk_option_id), true, false) as Button expect(button != null, "%s compact option exists" % locale) if button == null: continue for index in range(prefixes.size()): var label := button.find_child("RiskOptionFact%d" % index, true, false) as Label expect(label != null, "%s compact fact row %d exists" % [locale, index]) if label == null: continue expect(label.text.begins_with(String(prefixes[index])), "%s compact fact row %d uses short semantic label" % [locale, index]) expect(label.text.length() <= (18 if locale == "zh_CN" else 28), "%s compact fact row %d stays concise: %s" % [locale, index, label.text]) func _cleanup() -> void: for path in [DRAFT_PATH, "%s.bak" % DRAFT_PATH, "%s.candidate" % DRAFT_PATH, "%s.bak.candidate" % DRAFT_PATH, COMPANY_PATH, "%s.bak" % COMPANY_PATH, "%s.candidate" % COMPANY_PATH, "%s.bak.candidate" % COMPANY_PATH, "%s.corrupt" % COMPANY_PATH]: if FileAccess.file_exists(path): DirAccess.remove_absolute(path) func expect(condition: bool, message: String) -> void: if not condition: failures.append("DEV-4 Risk: %s" % message)