Files
TheTree_mock/tests/ui/test_world_tree_rank.gd
Takeshi b84bade045 Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
2026-10-11 15:58:00 +09:00

96 lines
6.9 KiB
GDScript

extends SceneTree
const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd")
var checks:=0
var failures:=0
func expect(ok: bool, message: String) -> void:
checks+=1
if not ok: failures+=1;push_error(message)
func _initialize() -> void:call_deferred("run")
func run() -> void:
var runtime: Variant=root.get_node("SimulationRuntime")
var clock: Variant=root.get_node("GameClock")
var state: Variant=root.get_node("GameState")
clock.reset(1,6,0);state.reset();runtime.initialize(clock,state,42510);clock.set_paused(true)
root.size=Vector2i(1280,720)
var main: Control=load("res://scenes/main/main.tscn").instantiate()
var save_path:="user://tests/rank_ui_%d" % Time.get_ticks_usec()
main.set("manual_save_path",save_path);root.add_child(main)
runtime.configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false})
await process_frame;await process_frame
var button: Button=main.get("rank_button")
var dialog: ConfirmationDialog=main.get("rank_dialog")
expect(button.text=="世界樹ランク 1" and button.is_visible_in_tree(),"rank appears in normal HUD")
var command_results: Array[CommandResult]=[]
runtime.command_completed.connect(func(result: CommandResult) -> void: command_results.append(result))
var original: Dictionary=runtime.create_snapshot()
button.pressed.emit()
expect(dialog.visible and dialog.get_ok_button().disabled,"unmet rank conditions cannot be confirmed")
expect("5000 / 10000" in dialog.dialog_text and "評判" in dialog.dialog_text,"rank conditions show current facts and deficits")
expect(runtime.create_snapshot()==original,"viewing live eligibility does not mutate progression or simulation")
var front_text := dialog.get_label().get_theme_color("font_color")
var front_panel: Color = dialog.theme.get_stylebox("panel", "AcceptDialog").bg_color
var conditions := dialog.dialog_text
main.call("_set_layer", 1)
dialog.refresh()
expect(dialog.get_label().get_theme_color("font_color") == Color("d8eeea") and dialog.theme.get_stylebox("panel", "AcceptDialog").bg_color == Color("202e34"), "rank dialog uses readable BACK palette")
expect(dialog.get_ok_button().get_theme_color("font_disabled_color") == Color("8da4a4"), "disabled promotion remains legible in BACK")
expect(dialog.dialog_text == conditions and dialog.get_ok_button().disabled, "skin change preserves conditions and disabled gate")
if OS.get_environment("THE_TREE_RANK_SKIN_CAPTURE") == "1":
await process_frame; await RenderingServer.frame_post_draw
expect(root.get_texture().get_image().save_png(ProjectSettings.globalize_path("res://artifacts/facility-dimensions-2026-09-29/rank-skin-back.png")) == OK, "capture BACK rank conditions")
main.call("_set_layer", 0)
expect(dialog.get_label().get_theme_color("font_color") == front_text and dialog.theme.get_stylebox("panel", "AcceptDialog").bg_color == front_panel, "rank dialog restores FRONT palette")
expect(runtime.create_snapshot() == original, "round trip skin change preserves complete simulation snapshot")
if OS.get_environment("THE_TREE_RANK_CAPTURE")=="1":
await process_frame;await RenderingServer.frame_post_draw
expect(root.get_texture().get_image().save_png(ProjectSettings.globalize_path("res://artifacts/facility-dimensions-2026-09-29/rank-conditions.png"))==OK,"capture rank HUD and unmet conditions")
dialog.hide()
# Controlled boundary inputs verify UI wiring, not natural campaign completion.
expect(runtime.submit_command(&"update_progression_metrics",{&"metrics":{&"reputation":25}}).is_success(),"fund reputation boundary fixture")
expect(runtime.submit_command(&"change_resources",{&"deltas":{&"gold":5000}}).is_success(),"fund current cash boundary through ledger command")
button.pressed.emit()
expect(not dialog.get_ok_button().disabled and "10000 / 10000" in dialog.dialog_text,"eligibility uses live cash before another tick")
var eligible_snapshot: Dictionary=runtime.create_snapshot()
# Explicit recovery-failure injection tests the pre-existing operations gate.
runtime.operations_coordinator._enter_external_quarantine(&"tick",clock.absolute_minute,&"test_phase",&"TEST_FAILURE")
dialog.refresh()
expect(dialog.get_ok_button().disabled and "営業状態の復旧" in dialog.dialog_text,"otherwise eligible rank is blocked and explained during operations quarantine")
expect(runtime.restore_snapshot(eligible_snapshot),"recover valid pre-fault game")
dialog.refresh()
runtime.submit_command(&"change_resources",{&"deltas":{&"gold":-1}})
dialog.confirmed.emit();dialog.hide()
expect(runtime.progression_service.current_rank==1 and "昇格できません" in main.get("build_status_label").text,"confirmation revalidates changed conditions instead of trusting prior preview")
expect(not command_results.is_empty() and command_results.back().details.get(&"reason",&"")==&"REQUIREMENTS_NOT_MET","changed-cash rejection is eligibility failure, not a malformed UI request")
runtime.submit_command(&"change_resources",{&"deltas":{&"gold":1}})
button.pressed.emit()
clock.set_paused(false)
dialog.confirmed.emit();dialog.hide()
clock.set_paused(true)
expect(runtime.progression_service.current_rank==1 and int(dialog.get("_pending_id"))!=0,"running-game confirmation queues promotion without pretending it already happened")
main.call("_save_manual")
expect(SaveService.load_snapshot(save_path)[&"ok"],"pending rank request saves before execution")
expect(runtime.restore_snapshot(original),"restore control game while promotion is pending")
main.call("_load_manual")
button.pressed.emit()
expect(int(dialog.get("_pending_id"))!=0 and dialog.get_ok_button().disabled,"loaded pending promotion stays visible and cannot be submitted twice")
dialog.hide()
clock.advance_one_minute()
expect(runtime.progression_service.current_rank==2 and state.world_tree_rank==2,"queued UI promotion commits real rank")
expect(runtime.world_service.get_bounds()==ProgressionService.RANK_BOUNDS[2],"promotion expands actual construction bounds")
expect(button.text=="世界樹ランク 2" and int(dialog.get("_pending_id"))==0,"completed command updates HUD and clears pending UI state")
main.call("_save_manual")
expect(SaveService.load_snapshot(save_path)[&"ok"],"promoted game saves through normal UI")
expect(runtime.restore_snapshot(original),"restore pre-promotion control state")
main.call("_refresh_dashboard")
expect(button.text=="世界樹ランク 1","HUD follows restored rank")
main.call("_load_manual")
expect(button.text=="世界樹ランク 2" and runtime.world_service.get_bounds()==ProgressionService.RANK_BOUNDS[2],"normal load restores promoted HUD and map bounds")
SaveService.delete_save(save_path)
var _audio_cleanup_final: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main)
if not bool(_audio_cleanup_final.get(&"passed", false)):
push_error("Ordinary fixture audio cleanup failed: %s" % str(_audio_cleanup_final))
quit(96)
return
await process_frame
print("World tree rank UI: %d checks, %d failures" % [checks,failures]);quit(failures)