Files
TheTree_mock/tests/ui/test_anri_recruitment.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

111 lines
7.6 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,42601); clock.set_paused(true)
root.size = Vector2i(1280,720)
var main: Control = load("res://scenes/main/main.tscn").instantiate()
var path := "user://tests/anri-recruitment-%d" % Time.get_ticks_usec()
main.manual_save_path = path
root.add_child(main)
runtime.configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false})
await process_frame; await process_frame
var before: Dictionary = runtime.create_snapshot()
var economy_before: Dictionary = runtime.economy_service.create_snapshot()
main.staff_candidate_toggle_button.pressed.emit()
var button: Button = main.staff_candidate_buttons.get(&"heroine_anri")
expect(button != null and button.text == "無料で加入" and button.is_visible_in_tree(), "normal staff screen offers Anri free recruitment")
clock.set_paused(false)
button.pressed.emit()
clock.set_paused(true)
expect(button.disabled and not runtime.staff_service.has_staff(&"heroine_anri"), "running game queues recruitment without premature joining")
button.pressed.emit()
expect(runtime.command_queue.create_snapshot()["pending"].size() == 1, "repeated UI action does not duplicate recruitment")
main.call("_save_manual")
expect(SaveService.load_snapshot(path)[&"ok"], "pending Anri recruitment saves through normal UI")
expect(runtime.restore_snapshot(before), "restore control before loading pending recruitment")
main.call("_load_manual")
clock.advance_one_minute()
var anri: StaffRuntime = runtime.staff_service.get_staff(&"heroine_anri")
expect(anri != null and anri.role_id == &"innkeeper" and anri.employment_kind == StaffRuntime.EmploymentKind.UNIQUE, "restored request creates canonical unique innkeeper")
expect(runtime.economy_service.create_snapshot() == economy_before and anri.daily_wage == 0, "joining costs no cash and creates no daily wage")
expect(button.disabled and button.text == "加入済み", "joined unique cannot be recruited twice in UI")
main.staff_management_toggle_button.pressed.emit()
var panel: StaffManagementPanel = main.find_child("StaffManagementPanel",true,false)
panel.select_staff(&"heroine_anri")
expect(panel.staff_detail_label.text.contains("アンリ") and panel.staff_detail_label.text.contains("女将") and panel.staff_detail_label.text.contains("日給 G0"), "staff screen uses unique name role and zero upkeep")
main.call("_save_manual")
var joined: Dictionary = runtime.create_snapshot()
expect(runtime.restore_snapshot(before), "restore pre-join control")
main.call("_load_manual")
expect(runtime.staff_service.has_staff(&"heroine_anri") and runtime.get_hired_staff_candidate_ids().has(&"heroine_anri"), "normal load restores unique identity and recruitment history")
expect(not runtime.staff_service.retire_staff(&"heroine_anri").is_success(), "recruited Anri cannot retire")
var invalid := joined.duplicate(true)
for row: Dictionary in invalid[&"systems"][&"runtime"][&"staff"][&"staff"]:
if row[&"staff_id"] == "heroine_anri": row[&"employment_kind"] = "FREE_HIRE"
var intact: Dictionary = runtime.create_snapshot()
expect(not runtime.restore_snapshot(invalid) and runtime.create_snapshot() == intact, "save cannot downgrade Anri to removable generic employment")
# Place the recruited manager through the normal staff controls.
for build: Array in [[&"reception",Vector3i(-6,0,0)],[&"public_bath",Vector3i(1,0,0)]]:
main.call("_select_build_definition", build[0])
main.call("_on_map_cell_clicked", build[1])
clock.advance_minutes(runtime.definition_registry.get_facility(build[0]).build_minutes)
var reception: StringName = &""
var bath: StringName = &""
for facility: FacilityInstance in runtime.facility_service.get_instances():
if facility.definition_id==&"reception": reception=facility.instance_id
if facility.definition_id==&"public_bath": bath=facility.instance_id
expect(not reception.is_empty() and not bath.is_empty(), "normal UI builds manager workplace and same-floor service")
panel.refresh(); panel.select_staff(&"heroine_anri")
for i in panel.facility_option.item_count:
if panel.facility_option.get_item_metadata(i)==reception:
panel.facility_option.select(i); panel.facility_option.item_selected.emit(i)
panel.apply_button.pressed.emit()
expect(runtime.staff_service.get_work_assignment(&"heroine_anri").get(&"facility_id")==reception, "normal staff controls place unique innkeeper")
var reception_instance: FacilityInstance=runtime.facility_service.get_instance(reception)
expect(reception_instance.active_staff_ids.is_empty(), "Anri does not meet the receptionist staffing requirement")
expect(not main.opening_guide._has_reception_assignment(reception_instance), "opening guide does not mistake innkeeper placement for receptionist coverage")
var manager_only: Dictionary=runtime.create_snapshot()
main.call("_hire_staff_candidate", &"staff_akane")
panel.refresh();panel.select_staff(&"staff_akane")
for i in panel.facility_option.item_count:
if panel.facility_option.get_item_metadata(i)==reception:
panel.facility_option.select(i);panel.facility_option.item_selected.emit(i)
panel.apply_button.pressed.emit()
expect(main.opening_guide._has_reception_assignment(reception_instance), "actual receptionist placement satisfies opening guide")
expect(runtime.restore_snapshot(manager_only), "restore manager-only workplace after receptionist comparison")
expect(not main.opening_guide._has_reception_assignment(runtime.facility_service.get_instance(reception)), "loading manager-only placement restores unsatisfied receptionist guidance")
main.navigation_buttons[&"business"].pressed.emit()
var business: BusinessManagementPanel = main.find_child("BusinessManagementPanel",true,false)
business.refresh()
for i in business.facility_option.item_count:
if business.facility_option.get_item_metadata(i)==bath:
business.facility_option.select(i); business.facility_option.item_selected.emit(i)
expect("24G" in business.quote_label.text and "アンリ配置フロア" in business.effect_label.text, "normal business screen explains current20percent floor price")
for i in business.price_option.item_count:
if business.price_option.get_item_metadata(i)==&"expensive":
business.price_option.select(i); business.price_option.item_selected.emit(i)
business.refresh()
expect("設定後 36G" in business.quote_label.text, "draft150percent policy combines with Anri120percent consistently")
if OS.get_environment("THE_TREE_ANRI_CAPTURE") == "1":
if not panel.visible: main.staff_management_toggle_button.pressed.emit()
panel.select_staff(&"heroine_anri")
expect(panel.is_visible_in_tree(), "recruited staff panel visible for rendered inspection")
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/anri-floor-assigned.png")) == OK, "capture recruited Anri")
SaveService.delete_save(path)
var cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main)
if not bool(cleanup.get(&"passed", false)):
push_error("Anri recruitment ordinary owner cleanup failed: %s" % str(cleanup))
quit(96)
return
print("Anri recruitment UI: %d checks, %d failures" % [checks,failures]); quit(failures)