87 lines
3.8 KiB
GDScript
87 lines
3.8 KiB
GDScript
extends SceneTree
|
|
|
|
const AppServices = preload("res://foundation/app_services.gd")
|
|
const ScreenContext = preload("res://foundation/screen_context.gd")
|
|
const ScreenStack = preload("res://foundation/screen_stack.gd")
|
|
const Tokens = preload("res://foundation/ui_tokens.gd")
|
|
const SharedButton = preload("res://ui/shared/shared_button.gd")
|
|
|
|
var failures: Array[String] = []
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("run_test")
|
|
|
|
|
|
func run_test() -> void:
|
|
verify_context_round_trip()
|
|
verify_screen_stack()
|
|
verify_service_bundle()
|
|
await verify_shared_ui_contract()
|
|
if failures.is_empty():
|
|
print("ABG_M15_FOUNDATION_OK context=true stack=true services=true ui_tokens=true shared_button=true")
|
|
quit(0)
|
|
else:
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func verify_context_round_trip() -> void:
|
|
var context := ScreenContext.new({
|
|
"screen_id": "settings",
|
|
"caller_id": "battle_pause_controls",
|
|
"return_screen_id": "battle",
|
|
"return_focus_path": "BattleLive/Settings",
|
|
"presentation_mode": "overlay",
|
|
"metadata": {"locale": "zh_CN", "operation_id": "op-1"},
|
|
})
|
|
expect(context.is_overlay(), "overlay context reports its presentation mode")
|
|
var restored := ScreenContext.new(context.to_dictionary())
|
|
expect(restored.screen_id == "settings" and restored.return_screen_id == "battle", "context preserves route identity")
|
|
expect(restored.return_focus_path == "BattleLive/Settings" and restored.metadata.locale == "zh_CN", "context preserves focus and metadata")
|
|
|
|
|
|
func verify_screen_stack() -> void:
|
|
var stack = ScreenStack.new()
|
|
stack.push(ScreenContext.new({"screen_id": "title"}))
|
|
stack.push(ScreenContext.new({"screen_id": "settings", "caller_id": "title"}))
|
|
expect(stack.depth() == 2 and stack.contains("settings"), "stack pushes independent screen contexts")
|
|
var current = stack.current()
|
|
expect(current != null and current.screen_id == "settings", "stack returns the top context")
|
|
stack.replace(ScreenContext.new({"screen_id": "credits", "caller_id": "settings"}))
|
|
expect(stack.depth() == 2 and stack.current().screen_id == "credits", "stack replaces only the top context")
|
|
var popped = stack.pop()
|
|
expect(popped != null and popped.screen_id == "credits" and stack.current().screen_id == "title", "stack pops to the previous route")
|
|
stack.clear()
|
|
expect(stack.depth() == 0 and stack.current() == null, "stack clears without leaving stale context")
|
|
|
|
|
|
func verify_service_bundle() -> void:
|
|
var sentinel := RefCounted.new()
|
|
var services := AppServices.new({"settings": sentinel})
|
|
expect(services.has_service(&"settings") and services.resolve_service(&"settings") == sentinel, "service bundle registers explicit dependencies")
|
|
services.register_service(&"audio", sentinel)
|
|
expect(services.has_service(&"audio") and services.service_snapshot().screen_stack, "service bundle exposes screen stack and services")
|
|
services.register_service(&"audio", null)
|
|
expect(not services.has_service(&"audio"), "service bundle removes a cleared dependency")
|
|
|
|
|
|
func verify_shared_ui_contract() -> void:
|
|
expect(Tokens.touch_size(Vector2(10, 60)) == Vector2(48, 60), "shared touch token enforces the minimum target")
|
|
expect(Tokens.font_size("body", 1.0) == 16 and Tokens.font_size("body", 1.3) >= 20, "shared typography token scales predictably")
|
|
var button := SharedButton.new()
|
|
button.configure("Apply", "primary")
|
|
button.set_view_state(true, true, false)
|
|
root.add_child(button)
|
|
await process_frame
|
|
expect(button.semantic_variant == "primary" and button.selected_state and not button.disabled, "shared button exposes semantic view state")
|
|
expect(button.custom_minimum_size.x >= 48.0 and button.custom_minimum_size.y >= 48.0, "shared button keeps the touch target")
|
|
button.queue_free()
|
|
await process_frame
|
|
|
|
|
|
func expect(condition: bool, message: String) -> void:
|
|
if not condition:
|
|
failures.append("M15 Foundation: %s" % message)
|