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

166 lines
8.0 KiB
GDScript

extends SceneTree
const PageContext = preload("res://foundation/page_context.gd")
const ReadinessPageView = preload("res://entry/views/readiness_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 := ReadinessPageView.new()
view.name = "ReadinessViewContractFixture"
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": "readiness_review",
"caller_id": "party_line",
"return_page_id": "party_line",
"return_focus_path": "ReadinessBack",
}))
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")) == "readiness_review", "mount stores a copied PageContext")
expect(view.find_child("ReadinessMasterMatte", true, false) != null, "View owns the Readiness matte")
expect(view.find_child("ReadinessMasterCanvas", true, false) != null, "View owns the normalized 16:9 canvas")
expect(view.find_child("ReadinessStaticMaster", true, false) != null, "View owns the static master")
expect(view.find_child("ReadinessSummaryBand", true, false) != null, "View owns the frozen-facts summary band")
expect(view.find_child("ReadinessWarning", true, false) != null and view.find_child("ReadinessWarningRow2", true, false) != null, "View renders warning rows")
var cord := view.find_child("ReadinessFormationCord", true, false) as Control
var effects := view.find_child("ReadinessFormationEffects", true, false) as Control
expect(cord != null and effects != null and cord.z_index == 25 and effects.z_index == 35, "View preserves PartyLineTrack layer z-order")
for position in range(4):
var member := view.find_child("ReadinessPosition%d" % position, true, false) as Control
expect(member != null and member.z_index == 30, "View renders ReadinessPosition%d between track layers" % position)
var settings := view.find_child("ReadinessSettings", true, false) as Button
var member_button := view.find_child("ReadinessPosition1", true, false) as Button
var warning := view.find_child("ReadinessWarning", true, false) as Button
var revise := view.find_child("ReadinessRevise", true, false) as Button
var back := view.find_child("ReadinessBack", true, false) as Button
var accept := view.find_child("ReadinessAccept", true, false) as Button
var risk := view.find_child("ReadinessRisk", true, false) as Button
expect(settings != null and not settings.disabled, "Settings intent is enabled")
expect(member_button != null and not member_button.disabled, "member review intent is enabled")
expect(warning != null and not warning.disabled, "warning review intent is enabled")
expect(revise != null and back != null and accept != null and not accept.disabled and risk != null and risk.disabled, "ordered command state is reflected")
for button in [settings, member_button, warning, revise, back, accept]:
if button != null:
button.emit_signal("pressed")
await process_frame
expect(intents.size() == 6, "ordered page emits six enabled intents exactly once")
expect(intents[0].get("intent_id") == "settings", "Settings intent is named")
expect(intents[1].get("intent_id") == "review_member" and intents[1].get("payload", {}).get("recruit_id") == "r-2", "member intent carries Recruit identity")
expect(intents[2].get("intent_id") == "review_warnings", "warning intent is named")
expect(intents[3].get("intent_id") == "revise", "Revise intent is named")
expect(intents[4].get("intent_id") == "back", "Back intent is named")
expect(intents[5].get("intent_id") == "accept", "Accept intent is named")
view.render(_fixture_view_data(true))
await process_frame
var ready_accept := view.find_child("ReadinessAccept", true, false) as Button
var ready_risk := view.find_child("ReadinessRisk", true, false) as Button
expect(ready_accept != null and ready_accept.disabled and ready_risk != null and not ready_risk.disabled, "ready command state is reflected")
if ready_risk != null:
ready_risk.emit_signal("pressed")
await process_frame
expect(intents.size() == 7 and intents[6].get("intent_id") == "risk", "Risk intent is named")
view.render({"ok": false, "locale": "en", "notice": "Readiness unavailable"})
await process_frame
expect(view.find_child("ReadinessInvalidOverlay", true, false) != null, "View renders invalid state from supplied data")
expect(view.find_child("ReadinessMasterOverlay", true, false) == null, "invalid render removes the interactive overlay")
var source := FileAccess.get_file_as_string("res://entry/views/readiness_page_view.gd")
expect(not source.contains("FormationRiskService"), "View does not import FormationRiskService")
expect(not source.contains("Save"), "View does not own Save semantics")
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("entry_shell.gd"), "View does not import EntryShell")
expect(not source.contains("res://compat/"), "View does not import compatibility facade")
view.queue_free()
await process_frame
if failures.is_empty():
print("ABG_M37_READINESS_VIEW_CONTRACT_OK mount=true normalized_canvas=true static_master=true party=4 track_layers=25,30,35 warnings=3 intents=settings,review_member,review_warnings,revise,back,accept,risk 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(ready: bool) -> Dictionary:
return {
"ok": true,
"page_id": "readiness_review",
"locale": "en",
"company": {"company_id": "company-001", "owned_count": 4, "line_capacity": 4},
"contract": {"contract_id": "CTR-R1-01", "difficulty": "stormbound", "line_capacity": 4},
"formation": {
"status": "ready" if ready else "ordered",
"state": "ready" if ready else "ordered",
"orientation": "rear_left_front_right",
"front_position": 0,
"coverage": {"protection": 2, "long_reach": 2, "sustain": 1, "control": 1},
},
"order": {
"front_to_rear_ids": ["r-1", "r-2", "r-3", "r-4"],
"order_hash": "order-hash-001",
"front_recruit": _recruit("r-1", "PF-B01", "Ari"),
"ordered_recruits": [
{"position": 0, "recruit_id": "r-1", "recruit": _recruit("r-1", "PF-B01", "Ari"), "relation": {}},
{"position": 1, "recruit_id": "r-2", "recruit": _recruit("r-2", "PF-B05", "Bram"), "relation": {}},
{"position": 2, "recruit_id": "r-3", "recruit": _recruit("r-3", "PF-B07", "Cera"), "relation": {}},
{"position": 3, "recruit_id": "r-4", "recruit": _recruit("r-4", "PF-B03", "Dain"), "relation": {}},
],
},
"warnings": {"rows": [
{"text": "NO GUARD", "severity": "warning", "owner": "coverage"},
{"text": "Outcome not guaranteed", "severity": "notice", "owner": "risk"},
{"text": "Reserve: none", "severity": "info", "owner": "formation"},
]},
"receipt": {"present": ready, "latest": {"receipt_hash": "receipt-hash-001"}},
"commands": {
"settings_enabled": true,
"member_review_enabled": true,
"warning_review_enabled": true,
"revise_enabled": true,
"back_enabled": true,
"accept_enabled": not ready,
"risk_enabled": ready,
},
}
func _recruit(recruit_id: String, profession_id: String, name: String) -> Dictionary:
return {
"recruit_id": recruit_id,
"profession_id": profession_id,
"rendered_name_snapshot": {"en": name},
}
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("M37 Readiness View: %s" % message)