166 lines
5.7 KiB
GDScript
166 lines
5.7 KiB
GDScript
extends SceneTree
|
|
## Regression/smoke test for the PC2 "Enter World" flow end to end.
|
|
##
|
|
## Real user path: register -> land on world_list.tscn -> click "Enter World"
|
|
## on a real card -> the playable level (scenes/game.tscn) actually loads
|
|
## with the player spawned in it -> pressing Escape returns to world_list.
|
|
##
|
|
## Run with:
|
|
## godot --headless --path . --script res://scripts/smoke_test_enter_world.gd
|
|
##
|
|
## Requires the backend to be running (default base URL
|
|
## `application/agp/api_base_url` -> http://127.0.0.1:8000, overridable via
|
|
## AGP_API_BASE_URL) and at least one real world to already exist.
|
|
|
|
var passed := 0
|
|
var failed := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
_run()
|
|
|
|
|
|
func _run() -> void:
|
|
await process_frame
|
|
|
|
var api := root.get_node("ApiClient")
|
|
if api == null:
|
|
printerr("FAIL: ApiClient autoload not found")
|
|
quit(1)
|
|
return
|
|
|
|
# 1. Register a real user and seed a world so the browse list has a
|
|
# real card to click, same pattern as smoke_test_world_list.gd.
|
|
randomize()
|
|
var unique_id: int = int(Time.get_unix_time_from_system() * 1000) + randi() % 100000
|
|
var username := "enterworld_%d" % unique_id
|
|
var email := "enterworld_%d@test.example.com" % unique_id
|
|
var reg_result: Dictionary = await api.register(username, email, "SmokeTest123!", "1990-01-01")
|
|
if not _check("setup: register succeeded", reg_result.ok):
|
|
print(" detail: ", reg_result.detail)
|
|
_finish()
|
|
return
|
|
|
|
var world_body := JSON.stringify({
|
|
"name": "EnterWorld Smoke Test World %d" % unique_id,
|
|
"description": "Seeded by smoke_test_enter_world.gd",
|
|
"visibility": "public",
|
|
})
|
|
var world_result: Dictionary = await api.request(
|
|
HTTPClient.METHOD_POST, "/api/worlds/", world_body, "application/json", true
|
|
)
|
|
if not _check("setup: world created", world_result.ok):
|
|
print(" detail: ", world_result.detail)
|
|
_finish()
|
|
return
|
|
|
|
# 2. Load world_list.tscn as a real scene and let it fetch worlds.
|
|
var packed: PackedScene = load("res://scenes/world_list.tscn")
|
|
var instance: Control = packed.instantiate()
|
|
root.add_child(instance)
|
|
# _ready() kicks off an async _refresh_worlds() -- wait real frames for
|
|
# the HTTP round trip rather than guessing a fixed count.
|
|
var world_list_node: VBoxContainer = instance.get_node("VBoxContainer/ScrollContainer/WorldList")
|
|
var settled := false
|
|
for i in 120:
|
|
await process_frame
|
|
if world_list_node.get_child_count() > 0:
|
|
settled = true
|
|
break
|
|
if not _check("world list populated with at least one card", settled):
|
|
_finish()
|
|
return
|
|
|
|
# 3. Click "Enter World" on the first real card -- find the button by
|
|
# walking the card's children rather than assuming an index, so this
|
|
# doesn't silently pass if _create_world_card's structure changes.
|
|
var card: Control = world_list_node.get_child(0)
|
|
var enter_button: Button = _find_button(card, "Enter World")
|
|
if not _check("Enter World button exists on the card", enter_button != null):
|
|
_finish()
|
|
return
|
|
|
|
enter_button.pressed.emit()
|
|
await process_frame
|
|
var briefing: Node = instance.find_child("WorldBriefingModal", true, false)
|
|
if briefing != null:
|
|
var modal_enter := _find_button(briefing, "Enter World")
|
|
if modal_enter != null:
|
|
modal_enter.pressed.emit()
|
|
|
|
await _wait_for_scene_change(instance, 600) # generous -- a real POST /join may spawn a dedicated server (slow)
|
|
|
|
var game_scene := current_scene
|
|
if not _check("game.tscn became current_scene after Enter World", game_scene != null and game_scene != instance):
|
|
_finish()
|
|
return
|
|
|
|
# 4. Verify the player actually exists and spawned at the level's
|
|
# PlayerSpawn marker -- not just that *some* scene loaded.
|
|
var player: Node = game_scene.get_node_or_null("Player")
|
|
_check("Player node exists in the loaded game scene", player is CharacterBody3D)
|
|
|
|
var spawn_marker: Node = game_scene.get_node_or_null("Level/PlayerSpawn")
|
|
_check("PlayerSpawn marker exists in the loaded level", spawn_marker is Marker3D)
|
|
|
|
if player is CharacterBody3D and spawn_marker is Marker3D:
|
|
var dist := (player as CharacterBody3D).global_position.distance_to((spawn_marker as Marker3D).global_position)
|
|
_check("player spawned at (or very near) the PlayerSpawn marker", dist < 0.5)
|
|
|
|
# 5. Escape opens the Pause Menu, and Confirm Leave safely returns to world_list.tscn
|
|
var esc_event := InputEventKey.new()
|
|
esc_event.keycode = KEY_ESCAPE
|
|
esc_event.pressed = true
|
|
Input.parse_input_event(esc_event)
|
|
await process_frame
|
|
await process_frame
|
|
var pause_menu: Node = game_scene.find_child("PauseMenu", true, false)
|
|
_check("Escape opened PauseMenu in-game", pause_menu != null)
|
|
if pause_menu != null:
|
|
var leave_btn := _find_button(pause_menu, "🚪 Leave World")
|
|
if leave_btn != null:
|
|
leave_btn.pressed.emit()
|
|
await process_frame
|
|
var confirm_btn := _find_button(pause_menu, "Confirm Leave")
|
|
if confirm_btn != null:
|
|
confirm_btn.pressed.emit()
|
|
|
|
await _wait_for_scene_change(game_scene, 120)
|
|
|
|
var back_to_menu := current_scene
|
|
_check("Confirmed Leave returned to world_list.tscn", back_to_menu != null and back_to_menu.scene_file_path == "res://scenes/world_list.tscn")
|
|
|
|
_finish()
|
|
|
|
|
|
func _find_button(node: Node, text: String) -> Button:
|
|
if node is Button and (node as Button).text == text:
|
|
return node
|
|
for child in node.get_children():
|
|
var found := _find_button(child, text)
|
|
if found:
|
|
return found
|
|
return null
|
|
|
|
|
|
func _wait_for_scene_change(previous_scene: Node, max_frames: int) -> void:
|
|
for i in max_frames:
|
|
await process_frame
|
|
if current_scene != previous_scene and current_scene != null:
|
|
return
|
|
|
|
|
|
func _check(description: String, condition: bool) -> bool:
|
|
if condition:
|
|
print(" PASS: ", description)
|
|
passed += 1
|
|
else:
|
|
printerr(" FAIL: ", description)
|
|
failed += 1
|
|
return condition
|
|
|
|
|
|
func _finish() -> void:
|
|
print("\n=== SMOKE RESULT: %d/%d passed ===" % [passed, passed + failed])
|
|
quit(0 if failed == 0 else 1)
|