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

122 lines
7.5 KiB
GDScript

extends SceneTree
const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd")
var _checks := 0
var _failures := 0
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()
_expect(bool(runtime.initialize(clock, state, 230225).get(&"valid", false)), "normal Runtime initialization")
clock.set_paused(true)
runtime.configure_save_policy({&"enabled": false, &"daily_checkpoint_enabled": false})
root.size = Vector2i(1280, 720)
var main: Control = load("res://scenes/main/main.tscn").instantiate()
main.set("manual_save_path", "user://tests/event_log_%d/manual" % OS.get_process_id())
root.add_child(main)
await process_frame
await process_frame
var dialog: AcceptDialog = main.get("event_log_dialog")
var button := main.find_child("EventLogButton", true, false) as Button
_expect(button != null and button.is_visible_in_tree() and dialog != null, "event history has a normal UI entry")
if button == null or dialog == null:
var early_cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main)
_expect(bool(early_cleanup.get(&"passed", false)), "early ordinary Main release preserves whole typed Runtime and controls")
_finish()
return
var baseline: Dictionary = runtime.create_snapshot()
var baseline_bytes: PackedByteArray = var_to_bytes(baseline)
button.pressed.emit()
_expect(dialog.visible, "existing UI signal opens the log dialog")
var expected_rows: Array[Dictionary] = _recent_rows(runtime)
var rows: Array[Dictionary] = dialog.call("get_event_rows")
_expect(var_to_bytes(rows) == var_to_bytes(expected_rows), "every retained row and full typed dictionary is returned in publication order")
_expect(var_to_bytes(runtime.create_snapshot()) == baseline_bytes, "opening and reading preserve the whole Runtime snapshot")
var changed: CommandResult = runtime.submit_command(&"change_resources", {&"deltas": {&"gold": 1}},
&"player", &"", 50, -1, &"event_log.fixture.paid_fact")
_expect(changed.is_success(), "disclosed fixture uses a genuine Runtime command")
expected_rows = _recent_rows(runtime)
_expect(not expected_rows.is_empty(), "public command leaves actual published facts")
dialog.call("refresh")
var list := dialog.find_child("EventLogList", true, false) as ItemList
_expect(list != null and list.item_count == expected_rows.size(), "visible list covers all retained records without an added cap")
var saved: Dictionary = runtime.create_snapshot()
var saved_bytes: PackedByteArray = var_to_bytes(saved)
if list != null:
for index: int in range(expected_rows.size()):
list.select(index)
list.item_selected.emit(index)
_expect(var_to_bytes(dialog.call("get_selected_event")) == var_to_bytes(expected_rows[index]), "selected row owns every typed field of the exact existing record")
_expect(var_to_bytes(runtime.create_snapshot()) == saved_bytes, "selection preserves IDs/RNG/all systems/clock/events/queue")
# Formatter-only synthetic rows exercise known/unknown labels without
# publishing facts or claiming natural employment/arrival acceptance.
var known_row := SimulationDomainEvent.new(&"fixture.naming", &"STAFF_REGISTERED", 360,
&"staff_service", [&"staff_eri"], {&"nested": [{&"preserve_type": &"original"}]}, 0).to_dictionary()
var row_bytes := var_to_bytes(known_row)
var named: Dictionary = dialog.call("get_event_presentation", known_row)
var entry: Dictionary = runtime.mvp_content_catalog.get_entry(&"staff_candidates", &"staff_eri")
_expect(named[&"title"] == "従業員を登録" and named[&"targets"] == entry[&"display_name"], "known fact uses Japanese type and existing target display name")
_expect(not String(named[&"title"]).contains("STAFF_REGISTERED") and not String(named[&"targets"]).contains("staff_eri"), "raw type and entity ID are not primary labels")
_expect(String(named[&"supplement"]).contains("STAFF_REGISTERED") and String(named[&"supplement"]).contains("staff_eri"), "original raw identifiers remain available as supplemental metadata")
var unknown_row := SimulationDomainEvent.new(&"fixture.unknown", &"FUTURE_NOTIFICATION", 361,
&"future_service", [&"unknown_target"], {}, 0).to_dictionary()
var unknown: Dictionary = dialog.call("get_event_presentation", unknown_row)
_expect(unknown[&"title"] == "未識別の記録" and unknown[&"targets"] == "名称を確認できない対象(1件)", "unknown kind and target use safe human-readable fallback")
_expect(var_to_bytes(known_row) == row_bytes and var_to_bytes(runtime.create_snapshot()) == saved_bytes, "name formatting preserves full input types and whole Runtime")
for facility: FacilityInstance in runtime.facility_service.get_instances():
var facility_row := SimulationDomainEvent.new(&"fixture.facility", &"FACILITY_STATE_CHANGED", 360,
&"facility_service", [facility.instance_id], {}, 0).to_dictionary()
var presentation: Dictionary = dialog.call("get_event_presentation", facility_row)
var definition: FacilityDefinition = runtime.definition_registry.get_facility(facility.definition_id)
_expect(definition != null and String(presentation[&"targets"]).contains(definition.display_name), "existing facility definition name resolves without new catalog membership")
if list != null:
for index: int in range(expected_rows.size()):
var presentation: Dictionary = dialog.call("get_event_presentation", expected_rows[index])
_expect(list.get_item_text(index) == "%s %s — %s" % [presentation[&"time"], presentation[&"title"], presentation[&"targets"]], "each actual retained row uses its human-readable presentation")
_expect(var_to_bytes(runtime.create_snapshot()) == saved_bytes, "all actual and synthetic formatting leaves whole Runtime unchanged")
rows = dialog.call("get_event_rows")
if not rows.is_empty():
rows[0]["payload"][&"outside_mutation_probe"] = true
_expect(var_to_bytes(dialog.call("get_event_rows")) == var_to_bytes(expected_rows), "nested returned payload mutation cannot affect source or next query")
_expect(var_to_bytes(runtime.create_snapshot()) == saved_bytes, "defensive copies preserve the entire Runtime")
dialog.call("apply_layer_skin", 1)
dialog.call("apply_layer_skin", 0)
dialog.call("_select_row", -1)
dialog.call("_select_row", expected_rows.size())
_expect(var_to_bytes(runtime.create_snapshot()) == saved_bytes, "skin changes and invalid selection create no commands or state changes")
_expect(runtime.restore_snapshot(saved), "unchanged Runtime14 snapshot restores through existing validation")
dialog.call("refresh")
_expect(var_to_bytes(dialog.call("get_event_rows")) == var_to_bytes(expected_rows), "view queries the replaced live stream after restore")
_expect(var_to_bytes(runtime.create_snapshot()) == saved_bytes, "same-process restore retains every typed snapshot byte")
dialog.hide()
var cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main)
_expect(bool(cleanup.get(&"passed", false)), "ordinary Main release preserves whole typed Runtime and controls")
_finish()
func _recent_rows(runtime: Variant) -> Array[Dictionary]:
var result: Array[Dictionary] = []
for event: SimulationDomainEvent in runtime.domain_events.recent_events():
result.append(event.to_dictionary())
return result
func _expect(condition: bool, message: String) -> void:
_checks += 1
if not condition:
_failures += 1
push_error(message)
func _finish() -> void:
print("Event log UI: %d checks, %d failures" % [_checks, _failures])
quit(_failures)