extends SceneTree ## Headless smoke test for the world list screen. ## ## Run with: ## godot --headless --path . --script res://scripts/smoke_test_world_list.gd ## ## Requires the backend to be running (default base URL ## `application/agp/api_base_url` -> http://127.0.0.1:8000, override with ## AGP_API_BASE_URL). ## ## Unlike a pure API-level test, this actually instantiates scenes/ ## world_list.tscn as a real scene and adds it to the tree -- catches ## @onready node-path bugs (a node reference pointing at the wrong path ## errors right here, not just "looks right" from reading the script) that ## an API-only test would miss entirely. ## ## Tests: ## 1. Register + log in a fresh user (so get_worlds() has a valid token). ## 2. Instantiate world_list.tscn, add to tree -- @onready resolution ## must not error. ## 3. Wait for _refresh_worlds()'s awaited request to complete. ## 4. Confirm the screen reached a real end state (world cards, or the ## empty-list message -- either is fine, a crash/stuck-loading isn't). ## ## Exits 0 on full pass, 1 on any failure. const WORLD_LIST_SCENE := "res://scenes/world_list.tscn" func _initialize() -> void: _run() func _run() -> void: await process_frame var api := root.get_node("ApiClient") assert(api != null, "ApiClient autoload not found") var passed: int = 0 var failed: int = 0 # --- Setup: register + log in so the world list has a valid token --- print("\n=== Setup: register + login ===") randomize() var unique_id: int = int(Time.get_unix_time_from_system() * 1000) + randi() % 100000 var username := "worldlist_smoke_%d" % unique_id var email := "worldlist_smoke_%d@test.example.com" % unique_id var reg_result: Dictionary = await api.register(username, email, "SmokeTest123!", "2000-01-01") if not _check("setup: register succeeded", reg_result.ok): print(" detail: ", reg_result.detail) quit(1) return # --- Test 1: Instantiate the real scene --- print("\n=== Test 1: Instantiate world_list.tscn ===") var packed_scene: PackedScene = load(WORLD_LIST_SCENE) if not _check("scene loads", packed_scene != null): quit(1) return var instance: Control = packed_scene.instantiate() if not _check("scene instantiates", instance != null): quit(1) return root.add_child(instance) # One frame for _ready() (and its @onready resolution) to run. A wrong # node path in an @onready var errors during this, not silently. await process_frame if _check("scene added to tree without error", is_instance_valid(instance)): passed += 1 else: failed += 1 # --- Test 2: Wait for the world fetch to resolve, check a real end state --- print("\n=== Test 2: World fetch reaches a real end state ===") # _refresh_worlds() runs from _ready(); give its awaited HTTP call time # to complete rather than a fixed sleep -- poll for either end state. var world_list_node: VBoxContainer = instance.get_node("VBoxContainer/ScrollContainer/WorldList") var empty_label: Label = instance.get_node("VBoxContainer/EmptyLabel") var error_label: Label = instance.get_node("VBoxContainer/ErrorLabel") var settled := false for i in range(50): # up to ~5s at 100ms/frame-ish via process_frame waits await process_frame if world_list_node.visible or empty_label.visible or error_label.visible: settled = true break if _check("reached a real end state (cards, empty, or error) not stuck loading", settled): passed += 1 else: failed += 1 print(" world_list.visible=%s empty_label.visible=%s error_label.visible=%s" % [ world_list_node.visible, empty_label.visible, error_label.visible ]) if error_label.visible: printerr(" Note: ended in error state: ", error_label.text) elif world_list_node.visible: print(" Note: rendered %d world card(s)" % world_list_node.get_child_count()) elif empty_label.visible: print(" Note: ended in empty-list state") # --- Summary --- print("\n=== SMOKE RESULT: %d/%d passed ===" % [passed, passed + failed]) if failed > 0: quit(1) else: print("SMOKE RESULT: ALL PASS") quit(0) func _check(description: String, condition: bool) -> bool: if condition: print(" PASS: ", description) return true else: printerr(" FAIL: ", description) return false