Files
aetherbound-guild/runtime/tests/foundation/risk_commitment_page_view_test.gd
T

178 lines
8.5 KiB
GDScript

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)