243 lines
11 KiB
GDScript
243 lines
11 KiB
GDScript
extends SceneTree
|
|
|
|
## Normal build/purchase commands: UI drafts, money/stock reconciliation, queued
|
|
## purchases and an isolated save slot. No free inventory is inserted.
|
|
var _checks := 0
|
|
var _failures := 0
|
|
var _main: Control
|
|
var _panel: SupplyManagementPanel
|
|
var _runtime: Variant
|
|
var _clock: Variant
|
|
var _warehouse: StringName = &""
|
|
var _kitchens: Array[StringName] = []
|
|
var _item: StringName = &""
|
|
var _save_path := "user://tests/supply_ui_%d/queued_purchase" % OS.get_process_id()
|
|
|
|
|
|
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(true)
|
|
root.size = Vector2i(1280, 720)
|
|
_main = load("res://scenes/main/main.tscn").instantiate() as Control
|
|
_main.set("manual_save_path", "user://tests/supply_ui_%d/manual" % OS.get_process_id())
|
|
root.add_child(_main)
|
|
_runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
|
|
await process_frame
|
|
await process_frame
|
|
_panel = _main.find_child("SupplyManagementPanel", true, false) as SupplyManagementPanel
|
|
_expect(_panel != null, "normal scene includes supply management")
|
|
if _panel == null:
|
|
_finish()
|
|
return
|
|
(_main.get("navigation_buttons")[&"product"] as Button).pressed.emit()
|
|
_expect(_panel.visible and _panel.purchase_button.disabled, "product opens a safe empty state before any warehouse exists")
|
|
_expect("倉庫" in _panel.warehouse_stock_label.text, "empty purchase page explains the warehouse prerequisite")
|
|
for request: Array in [
|
|
[&"warehouse", Vector3i(-8, 0, 1)],
|
|
[&"kitchen", Vector3i(0, 0, 1)],
|
|
[&"kitchen", Vector3i(6, 0, 1)],
|
|
]:
|
|
var before: int = _runtime.construction_service.get_jobs().size()
|
|
_main.call("_select_build_definition", request[0])
|
|
_main.call("_on_map_cell_clicked", request[1])
|
|
_expect(_runtime.construction_service.get_jobs().size() == before + 1, "normal UI builds %s" % request[0])
|
|
_clock.advance_minutes(180)
|
|
for facility: FacilityInstance in _runtime.facility_service.get_instances():
|
|
if facility.definition_id == &"warehouse":
|
|
_warehouse = facility.instance_id
|
|
elif facility.definition_id == &"kitchen":
|
|
_kitchens.append(facility.instance_id)
|
|
_expect(not _warehouse.is_empty() and _kitchens.size() == 2, "paid construction produces a warehouse and two kitchens")
|
|
if _warehouse.is_empty() or _kitchens.size() != 2:
|
|
_finish()
|
|
return
|
|
_main.call("_activate_management_mode", &"product")
|
|
_panel.refresh()
|
|
_item = _panel.selected_item_id
|
|
_expect(not _item.is_empty(), "Runtime purchase catalog supplies a real item")
|
|
_test_purchase_and_failure()
|
|
_test_kitchen_drafts()
|
|
_test_saved_pending_purchase()
|
|
await _test_navigation_and_layout()
|
|
_finish()
|
|
|
|
|
|
func _test_purchase_and_failure() -> void:
|
|
_choose(_panel.warehouse_option, _warehouse)
|
|
_panel.quantity_spin.value = 4
|
|
var quote: Dictionary = _runtime.preview_supply_purchase(_warehouse, _item, 4)
|
|
_expect(bool(quote.get(&"ok", false)), "Runtime quotes a paid purchase for the completed warehouse")
|
|
var balance_before: int = _runtime.economy_service.get_balance(&"PLAYER_CASH")
|
|
var stock_before := _stock()
|
|
_panel.purchase_button.pressed.emit()
|
|
_expect(_stock() == stock_before + 4, "purchase button credits the warehouse's actual inventory")
|
|
_expect(_runtime.economy_service.get_balance(&"PLAYER_CASH") == balance_before - int(quote.get(&"total_cost", -1)),
|
|
"purchase pays exactly the authoritative quoted cost")
|
|
_expect("仕入れました" in _panel.result_label.text, "successful purchase is visible in Japanese")
|
|
# A deliberately oversized draft must fail without reducing cash or stock.
|
|
_panel.quantity_spin.allow_greater = true
|
|
_panel.quantity_spin.value = int(quote.get(&"max_quantity", 100)) + 100
|
|
var invalid_quantity := int(_panel.quantity_spin.value)
|
|
var balance_after: int = _runtime.economy_service.get_balance(&"PLAYER_CASH")
|
|
var stock_after := _stock()
|
|
_expect(_panel.purchase_button.disabled, "invalid quoted quantity disables purchase before submission")
|
|
_panel.submit_purchase()
|
|
_expect(_stock() == stock_after and _runtime.economy_service.get_balance(&"PLAYER_CASH") == balance_after,
|
|
"rejected purchase never partially spends money or inserts stock")
|
|
_expect(int(_panel.quantity_spin.value) == invalid_quantity and not _panel.result_label.text.is_empty(),
|
|
"rejected purchase preserves the quantity and an actionable result")
|
|
_panel.quantity_spin.allow_greater = false
|
|
_panel.quantity_spin.value = 2
|
|
|
|
|
|
func _test_kitchen_drafts() -> void:
|
|
_panel.select_tab(&"kitchen")
|
|
_choose(_panel.kitchen_option, _kitchens[0])
|
|
_panel.production_enabled.button_pressed = true
|
|
_panel.target_spin.value = 4
|
|
_panel.select_tab(&"purchase")
|
|
_clock.advance_minutes(1)
|
|
_panel.select_tab(&"kitchen")
|
|
_expect(_panel.production_enabled.button_pressed and int(_panel.target_spin.value) == 4,
|
|
"time updates and tab changes preserve an unsaved kitchen draft")
|
|
_clock.set_paused(false)
|
|
_panel.production_button.pressed.emit()
|
|
_expect(_panel.production_button.disabled, "queued kitchen setting disables another submission for that kitchen")
|
|
_choose(_panel.kitchen_option, _kitchens[1])
|
|
_panel.production_enabled.button_pressed = true
|
|
_panel.target_spin.value = 8
|
|
_clock.advance_minutes(1)
|
|
_clock.set_paused(true)
|
|
_expect(_panel.selected_kitchen_id == _kitchens[1] and int(_panel.target_spin.value) == 8
|
|
and _panel.production_enabled.button_pressed, "another kitchen's completion preserves the selected kitchen's draft")
|
|
var configured := _kitchen_row(_kitchens[0])
|
|
_expect(bool(configured.get(&"enabled", false)) and int(configured.get(&"target_stock", -1)) == 4,
|
|
"saved kitchen settings reach the authoritative Runtime model")
|
|
_panel.production_button.pressed.emit()
|
|
var second := _kitchen_row(_kitchens[1])
|
|
_expect(bool(second.get(&"enabled", false)) and int(second.get(&"target_stock", -1)) == 8,
|
|
"second kitchen applies its own saved target")
|
|
_expect("職員" in _panel.kitchen_status_label.text or "経路" in _panel.kitchen_status_label.text,
|
|
"unready kitchen explains its authoritative staffing or route blocker in Japanese")
|
|
_panel.production_enabled.button_pressed = false
|
|
_panel.production_button.pressed.emit()
|
|
_expect(not bool(_kitchen_row(_kitchens[1]).get(&"enabled", true)), "automatic production can be stopped through the same controls")
|
|
|
|
|
|
func _test_saved_pending_purchase() -> void:
|
|
_panel.select_tab(&"purchase")
|
|
_panel.quantity_spin.value = 2
|
|
var stock_before := _stock()
|
|
var balance_before: int = _runtime.economy_service.get_balance(&"PLAYER_CASH")
|
|
var quote: Dictionary = _runtime.preview_supply_purchase(_warehouse, _item, 2)
|
|
_clock.set_paused(false)
|
|
_panel.purchase_button.pressed.emit()
|
|
var pending_before: int = _runtime.command_queue.create_snapshot().get("pending", []).size()
|
|
_panel.submit_purchase()
|
|
_expect(pending_before == 1 and _runtime.command_queue.create_snapshot().get("pending", []).size() == 1,
|
|
"repeated purchase cannot enqueue the same draft twice")
|
|
var saved: Dictionary = _runtime.save_to_path(_save_path)
|
|
_expect(bool(saved.get(&"ok", false)), "queued purchase saves to an isolated test slot")
|
|
_clock.advance_minutes(1)
|
|
var loaded: Dictionary = _runtime.load_from_path(_save_path)
|
|
_expect(bool(loaded.get(&"ok", false)), "pending purchase reloads successfully")
|
|
_panel.refresh()
|
|
_expect(_panel.purchase_button.disabled, "restored pending purchase cannot be resubmitted")
|
|
_clock.advance_minutes(1)
|
|
_clock.set_paused(true)
|
|
_panel.refresh()
|
|
_expect(_stock() == stock_before + 2 and _runtime.economy_service.get_balance(&"PLAYER_CASH") == balance_before - int(quote.get(&"total_cost", -1)),
|
|
"restored purchase credits and charges exactly once")
|
|
_expect(not _panel.purchase_button.disabled, "completed restored command releases purchase controls")
|
|
var digest: String = _runtime.latest_digest
|
|
_panel.refresh()
|
|
_panel.select_tab(&"delivery")
|
|
_expect(_runtime.latest_digest == digest, "displaying stock and deliveries never mutates simulation")
|
|
_expect(not (_panel.get_report().get(&"deliveries", []) as Array).is_empty(),
|
|
"real warehouse stock creates visible kitchen replenishment requests")
|
|
_expect("倉庫" in _panel.delivery_label.text and "厨房" in _panel.delivery_label.text,
|
|
"delivery page resolves source and destination to identifiable facilities")
|
|
_expect("運搬員" in _panel.delivery_label.text and "経路" in _panel.delivery_label.text,
|
|
"waiting delivery explains the actual porter or route shortage in Japanese")
|
|
|
|
|
|
func _test_navigation_and_layout() -> void:
|
|
_main.call("_activate_management_mode", &"ledger")
|
|
_expect(not _panel.visible and (_main.get("operating_ledger_panel") as Control).visible, "ledger replaces supply controls")
|
|
_main.call("_unhandled_key_input", _key(KEY_H))
|
|
_expect(not _panel.visible and (_main.get("staff_candidate_panel") as Control).visible, "H returns to the hiring candidates")
|
|
_main.call("_activate_management_mode", &"product")
|
|
await process_frame
|
|
await process_frame
|
|
var side := _main.find_child("SidePanel", true, false) as Control
|
|
var guide := _main.find_child("InnOpeningGuide", true, false) as Control
|
|
_expect(not side.get_global_rect().intersects(guide.get_global_rect()), "1280x720 keeps supply inspector clear of opening guidance")
|
|
_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,
|
|
"supply tabs and inputs fit inside the existing sidebar width")
|
|
if OS.get_environment("THE_TREE_SUPPLY_UI_CAPTURE") == "1":
|
|
for tab: StringName in [&"purchase", &"kitchen", &"delivery"]:
|
|
_panel.select_tab(tab)
|
|
if tab == &"kitchen":
|
|
_choose(_panel.kitchen_option, _kitchens[0])
|
|
await process_frame
|
|
await RenderingServer.frame_post_draw
|
|
var captured := root.get_texture().get_image()
|
|
captured.save_png("/tmp/the-tree-supply-%s.png" % tab)
|
|
if tab == &"purchase":
|
|
captured.save_png("/tmp/the-tree-supply-management.png")
|
|
_main.call("_unhandled_key_input", _key(KEY_ESCAPE))
|
|
_expect(not _panel.visible and not bool(_main.get("_management_panel_open")), "Escape closes supply management and restores the map")
|
|
|
|
|
|
func _stock() -> int:
|
|
var facility: FacilityInstance = _runtime.facility_service.get_instance(_warehouse)
|
|
return _runtime.inventory_service.total_quantity(facility.local_inventory_id, _item)
|
|
|
|
|
|
func _kitchen_row(id: StringName) -> Dictionary:
|
|
for row: Dictionary in _runtime.get_supply_management_report().get(&"kitchens", []):
|
|
if StringName(row.get(&"facility_id", &"")) == id:
|
|
return row
|
|
return {}
|
|
|
|
|
|
func _choose(picker: OptionButton, id: StringName) -> void:
|
|
for index: int in picker.item_count:
|
|
if StringName(picker.get_item_metadata(index)) == id:
|
|
picker.select(index)
|
|
picker.item_selected.emit(index)
|
|
return
|
|
_expect(false, "requested facility appears in the relevant supply dropdown: %s" % id)
|
|
|
|
|
|
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:
|
|
SaveService.delete_save(_save_path)
|
|
if is_instance_valid(_main):
|
|
_main.queue_free()
|
|
print("Supply management UI: %d checks, %d failures" % [_checks, _failures])
|
|
quit(1 if _failures > 0 else 0)
|