extends SceneTree ## Real Runtime commands through the staff panel controls, including queued ## assignment, draft preservation, a rejected role, and pending Save restoration. var _checks := 0 var _failures := 0 var _main: Control var _panel: StaffManagementPanel var _runtime: Variant var _clock: Variant var _receptions: Array[StringName] = [] var _kitchen: StringName = &"" var _save_path := "" func _initialize() -> void: call_deferred("_run") func _run() -> void: _clock = root.get_node("GameClock") _runtime = root.get_node("SimulationRuntime") var state: Variant = root.get_node("GameState") _clock.reset(1, 6, 0) state.reset() _runtime.initialize(_clock, state, 824242) _clock.set_paused(false) root.size = Vector2i(1280, 720) _main = load("res://scenes/main/main.tscn").instantiate() as Control root.add_child(_main) _runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) _expect(_clock.paused, "a truly new opening pauses before the first construction") await process_frame await process_frame _panel = _main.find_child("StaffManagementPanel", true, false) as StaffManagementPanel _expect(_panel != null, "normal UI includes a separate staff management panel") if _panel == null: _finish() return for request: Array in [ [&"reception", Vector3i(-8, 0, 0)], [&"reception", Vector3i(2, 0, 0)], [&"kitchen", Vector3i(2, 0, 1)], ]: _main.call("_select_build_definition", request[0]) _main.call("_on_map_cell_clicked", request[1]) _clock.advance_minutes(180) for facility: FacilityInstance in _runtime.facility_service.get_instances(): if facility.definition_id == &"reception": _receptions.append(facility.instance_id) elif facility.definition_id == &"kitchen": _kitchen = facility.instance_id _expect(_receptions.size() == 2 and not _kitchen.is_empty(), "three real facilities are ready for assignment tests") if _receptions.size() != 2 or _kitchen.is_empty(): _finish() return _main.call("_unhandled_key_input", _key(KEY_H)) _expect((_main.get("staff_candidate_panel") as Control).visible, "H opens the unchanged hiring candidates") for candidate: StringName in [&"staff_akane", &"staff_bardo"]: var hire := (_main.get("staff_candidate_buttons") as Dictionary).get(candidate) as Button hire.pressed.emit() (_main.get("staff_management_toggle_button") as Button).pressed.emit() _expect(_panel.visible and not (_main.get("staff_candidate_panel") as Control).visible, "hired-staff tab replaces the candidates without hiding the management panel") _expect(_panel.roster_buttons.size() == 2 and "未配属 2人" in _panel.summary_label.text, "roster shows both real employees and unassigned count") _panel.select_staff(&"staff_akane") _choose_facility(_receptions[0]) _panel.apply_button.pressed.emit() _expect(_assignment(&"staff_akane").get(&"facility_id") == _receptions[0], "paused Apply assigns the receptionist to the chosen completed facility") _expect(_runtime.staff_service.get_staff(&"staff_akane").state == StaffRuntime.State.COMMUTING and _runtime.facility_service.get_instance(_receptions[0]).active_staff_ids.is_empty(), "paused Apply visibly starts a commute and does not invent facility attendance") _expect("出勤中" in _panel.staff_detail_label.text, "staff panel distinguishes physical commuting from active duty") _expect("変更しました" in _panel.result_label.text and "毎日" in _panel.staff_detail_label.text, "assignment success and recurring schedule are visible in Japanese") _test_rejected_assignment() _test_queued_assignment_preserves_other_draft() _test_save_pending_assignment() _test_guide_respects_current_assignments() await _test_navigation_and_layout() await _test_restored_opening_does_not_pause() _finish() func _test_rejected_assignment() -> void: var before: Dictionary = _assignment(&"staff_akane").duplicate(true) _choose_facility(_kitchen) _panel.apply_button.pressed.emit() _expect(_assignment(&"staff_akane") == before, "wrong-role assignment never mutates the existing reception schedule") _expect("役割" in _panel.result_label.text and "役割" in (_main.get("build_status_label") as Label).text, "rejected assignment shows its actionable Japanese reason in both panel and status") _expect(_chosen_facility() == _kitchen, "a rejected assignment preserves the attempted facility for correction") func _test_queued_assignment_preserves_other_draft() -> void: _choose_facility(_receptions[1]) _clock.set_paused(false) _panel.apply_button.pressed.emit() var queued: Array = _runtime.command_queue.create_snapshot().get("pending", []) _expect(queued.size() == 1 and _panel.apply_button.disabled, "running-clock Apply queues one assignment and disables repeated submission") _panel.submit_assignment() _expect((_runtime.command_queue.create_snapshot().get("pending", []) as Array).size() == 1, "repeated submit cannot create a second assignment for the same employee") _panel.select_staff(&"staff_bardo") _choose_facility(_receptions[0]) _panel.shift_option.select(1) _panel.shift_option.item_selected.emit(1) _clock.advance_minutes(1) _clock.set_paused(true) _expect(_assignment(&"staff_akane").get(&"facility_id") == _receptions[1], "queued reassignment commits on the command tick") _expect(_panel.selected_staff_id == &"staff_bardo" and _chosen_facility() == _receptions[0] and _panel.shift_option.selected == 1, "another employee's pending completion preserves the active facility and night-shift draft") _panel.apply_button.pressed.emit() _expect(_assignment(&"staff_bardo").get(&"template_id") == &"NIGHT", "night selector creates an actual recurring night schedule") _expect("18:00" in _panel.result_label.text and "反映" in _panel.staff_detail_label.text, "future night shift clearly reports when it begins") _panel.select_staff(&"staff_akane") _choose_facility(&"") _panel.apply_button.pressed.emit() _expect(StringName(_assignment(&"staff_akane").get(&"facility_id", &"missing")).is_empty(), "unassigned option removes the facility duty through Runtime") func _test_save_pending_assignment() -> void: _choose_facility(_receptions[0]) _clock.set_paused(false) _panel.apply_button.pressed.emit() _save_path = "user://tests/staff-management-%d" % OS.get_process_id() var saved: Dictionary = _runtime.save_to_path(_save_path) _expect(bool(saved.get(&"ok", false)), "pending UI assignment saves to an isolated test slot") _clock.advance_minutes(1) var restored: Dictionary = _runtime.load_from_path(_save_path) _expect(bool(restored.get(&"ok", false)), "pending UI assignment can be restored") _panel.refresh() _expect(_panel.apply_button.disabled and not (_panel.get("_pending_commands") as Dictionary).is_empty(), "restored command metadata restores disabled pending controls") var sequence_before := int(_panel.get("_sequence")) _clock.advance_minutes(1) _clock.set_paused(true) _panel.refresh() _expect(_assignment(&"staff_akane").get(&"facility_id") == _receptions[0] and (_panel.get("_pending_commands") as Dictionary).is_empty(), "restored assignment completes once and releases its UI pending state") _choose_facility(_receptions[1]) _panel.apply_button.pressed.emit() _expect(int(_panel.get("_sequence")) > sequence_before and _assignment(&"staff_akane").get(&"facility_id") == _receptions[1], "post-load Apply uses a fresh command key and changes the real assignment") func _test_guide_respects_current_assignments() -> void: # Remove both saved plans so an unrelated receptionist cannot satisfy the # guide while this employee's assignment is being changed. for staff_id: StringName in [&"staff_akane", &"staff_bardo"]: _panel.select_staff(staff_id) _choose_facility(&"") _panel.apply_button.pressed.emit() _build_guide_part(&"standard_room", Vector3i(-8, 2, 0)) _build_guide_part(&"standard_room", Vector3i(-6, 2, 0)) _build_guide_part(&"guest_corridor", Vector3i(-4, 1, 0)) _clock.advance_minutes(60) _build_guide_part(&"guest_corridor", Vector3i(-4, 2, 0)) _clock.advance_minutes(10) for x: int in range(-8, -3): _build_guide_part(&"guest_corridor", Vector3i(x, 3, 0)) _clock.advance_minutes(10) var accessible_room := false for facility: FacilityInstance in _runtime.facility_service.get_instances(): accessible_room = accessible_room or (facility.definition_id == &"standard_room" and facility.access_available) var guide := _main.find_child("InnOpeningGuide", true, false) as InnOpeningGuide _expect(accessible_room and guide.get_progress().get(&"stage") == &"staff", "real accessible room still requires a reception assignment before first sale") _main.call("_activate_management_mode", &"staff") _panel.select_staff(&"staff_akane") _choose_facility(_receptions[0]) _panel.shift_option.select(0) _panel.shift_option.item_selected.emit(0) _panel.apply_button.pressed.emit() _expect(guide.get_progress().get(&"stage") == &"sale", "day assignment advances the accessible inn to waiting for its first sale") _choose_facility(&"") _panel.apply_button.pressed.emit() var historical_reception_shift := false for shift: ShiftInstance in _runtime.staff_service.get_shifts(): if shift.staff_id == &"staff_akane" and shift.assigned_facility_id == _receptions[0]: historical_reception_shift = historical_reception_shift or shift.cancelled or shift.end_at <= _clock.absolute_minute _expect(historical_reception_shift and guide.get_progress().get(&"stage") == &"staff", "unassigning reception returns guidance to staffing despite past or cancelled reception shifts") _choose_facility(_receptions[0]) _panel.shift_option.select(1) _panel.shift_option.item_selected.emit(1) _panel.apply_button.pressed.emit() var night_plan: Dictionary = _assignment(&"staff_akane") var reception: FacilityInstance = _runtime.facility_service.get_instance(_receptions[0]) _expect(night_plan.get(&"template_id") == &"NIGHT" and int(night_plan.get(&"effective_from", -1)) > _clock.absolute_minute and reception.active_staff_ids.is_empty(), "night assignment is genuinely future duty with nobody staffing reception during daytime") _expect(guide.get_progress().get(&"stage") == &"sale", "saved future night duty satisfies staffing guidance before its shift starts") func _build_guide_part(definition_id: StringName, origin: Vector3i) -> void: var before: int = _runtime.construction_service.get_jobs().size() _main.call("_select_build_definition", definition_id) _main.call("_on_map_cell_clicked", origin) _expect(_runtime.construction_service.get_jobs().size() == before + 1, "guide fixture builds %s through UI at %s" % [definition_id, origin]) func _test_navigation_and_layout() -> void: _main.call("_clear_active_tool") _expect(not _panel.visible, "Escape behavior closes the hired staff pane") _main.call("_activate_management_mode", &"ledger") var ledger := _main.find_child("OperatingLedgerPanel", true, false) as Control _expect(ledger.visible and not (_main.get("operations_controls") as Control).visible, "ledger navigation displays the actual ledger while hiding assignment controls") _main.call("_unhandled_key_input", _key(KEY_H)) _expect(not ledger.visible and (_main.get("staff_candidate_panel") as Control).visible, "H returns from the ledger to hiring candidates") _main.call("_activate_management_mode", &"staff") await process_frame await process_frame var guide := _main.find_child("InnOpeningGuide", true, false) as Control var side := _main.find_child("SidePanel", true, false) as Control _expect(not guide.get_global_rect().intersects(side.get_global_rect()), "1280x720 keeps opening guidance and staff inspector apart") _expect(_panel.get_global_rect().position.x >= side.get_global_rect().position.x and _panel.get_global_rect().end.x <= side.get_global_rect().end.x, "staff roster and assignment controls fit the compact sidebar width") if OS.get_environment("THE_TREE_STAFF_UI_CAPTURE") == "1": await RenderingServer.frame_post_draw root.get_texture().get_image().save_png("/tmp/the-tree-staff-management.png") func _test_restored_opening_does_not_pause() -> void: _main.queue_free() await process_frame _clock.reset(1, 6, 0) var state: Variant = root.get_node("GameState") state.reset() _runtime.initialize(_clock, state, 824242) _clock.set_paused(false) var snapshot: Dictionary = _runtime.create_snapshot() _expect(_runtime.restore_snapshot(snapshot), "restored opening fixture accepts its empty initial save") _main = load("res://scenes/main/main.tscn").instantiate() as Control root.add_child(_main) _runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false}) _expect(not _clock.paused and int(_clock.absolute_minute) == 360, "mounting a restored empty opening preserves its saved running state and time") _clock.set_paused(true) func _assignment(staff_id: StringName) -> Dictionary: return _runtime.staff_service.get_work_assignment(staff_id) func _choose_facility(facility_id: StringName) -> void: for index: int in _panel.facility_option.item_count: if StringName(_panel.facility_option.get_item_metadata(index)) == facility_id: _panel.facility_option.select(index) _panel.facility_option.item_selected.emit(index) return _expect(false, "requested facility is available in the UI: %s" % facility_id) func _chosen_facility() -> StringName: return StringName(_panel.facility_option.get_item_metadata(_panel.facility_option.selected)) func _key(code: Key) -> InputEventKey: var event := InputEventKey.new() event.keycode = code event.pressed = true return event func _expect(condition: bool, message: String) -> void: _checks += 1 if not condition: _failures += 1 push_error(message) func _finish() -> void: if not _save_path.is_empty(): SaveService.delete_save(_save_path) if is_instance_valid(_main): _main.queue_free() print("Staff management UI: %d checks, %d failures" % [_checks, _failures]) quit(1 if _failures > 0 else 0)