From c01f9de586a8fae4d45e18f2ec0fc366d3255a6d Mon Sep 17 00:00:00 2001 From: coder-bot Date: Thu, 20 Aug 2026 15:04:19 +0000 Subject: [PATCH] WIP checkpoint: Fix: navigating Play -> Back logs the user out (should stay logged in) --- scripts/smoke_test_main_menu_back.gd | 147 +++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 scripts/smoke_test_main_menu_back.gd diff --git a/scripts/smoke_test_main_menu_back.gd b/scripts/smoke_test_main_menu_back.gd new file mode 100644 index 0000000..c8c6758 --- /dev/null +++ b/scripts/smoke_test_main_menu_back.gd @@ -0,0 +1,147 @@ +extends SceneTree +## Regression test for the Play -> Back navigation bug. +## +## The reported bug: after logging in and clicking Play, the user lands on +## world_list.tscn; clicking Back returns to main_menu.tscn, but the menu +## reset to a fully logged-out state (Play disabled, login form showing) +## even though ApiClient still held a valid token. main_menu.gd's _ready() +## used to unconditionally reset the UI on every scene load. +## +## This test drives the real navigation flow (change_scene_to_file) against +## a live backend: +## 1. Register a unique user so ApiClient holds a valid token. +## 2. Load main_menu.tscn and assert it restores the authenticated state. +## 3. Click Play -> world_list.tscn. +## 4. Click Back -> main_menu.tscn. +## 5. Assert the menu is STILL authenticated (Play enabled, welcome label +## visible, login/register buttons hidden). +## +## Run with: +## godot --headless --path . --script res://scripts/smoke_test_main_menu_back.gd +## +## Requires the backend to be running (default base URL +## `application/agp/api_base_url` -> http://127.0.0.1:8000). + +const USERNAME := "backnav_%d" +const EMAIL := "backnav_%d@test.example.com" +const PASSWORD := "BackNav123!" +const DOB := "2000-01-01" + +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. Establish a real, valid session exactly the way a user would. + var unique_id := Time.get_ticks_msec() + var username := USERNAME % unique_id + var email := EMAIL % unique_id + print("\n=== Setup: register ", username, " ===") + var register_result: Dictionary = await api.register(username, email, PASSWORD, DOB) + if not _check("register succeeds", register_result.ok): + print(" detail: ", register_result.detail) + _finish() + return + _check("ApiClient stores token after register", api.has_token()) + + # 2. First visit to the main menu: should already show the authenticated + # state (no login form, Play enabled). + change_scene_to_file("res://scenes/main_menu.tscn") + await _wait_for_menu_ready() + + var menu: Control = current_scene + if menu == null or not menu is Control: + printerr("FAIL: main_menu.tscn did not become current_scene") + _finish() + return + _check("first main menu visit restores authenticated state", _is_authenticated(menu)) + + # 3. Play -> world_list.tscn (real button press). + var play_button: Button = menu.get_node("CenterContainer/VBoxContainer/PlayButton") + play_button.pressed.emit() + await _wait_for_scene_change(menu) + + var world_list_scene := current_scene + if world_list_scene == null or world_list_scene == menu: + printerr("FAIL: world_list.tscn did not become current_scene after Play") + _finish() + return + + # 4. Back -> main_menu.tscn (real button press). + var back_button: Button = world_list_scene.get_node("VBoxContainer/HeaderHBox/BackButton") + back_button.pressed.emit() + await _wait_for_scene_change(world_list_scene) + + menu = current_scene + if menu == null or not menu is Control: + printerr("FAIL: main_menu.tscn did not become current_scene after Back") + _finish() + return + + # 5. THE regression assertion: still logged in after returning. + _check("main menu stays authenticated after Play -> Back", _is_authenticated(menu)) + + _finish() + + +func _is_authenticated(menu: Control) -> bool: + var play_button: Button = menu.get_node("CenterContainer/VBoxContainer/PlayButton") + var welcome_label: Label = menu.get_node("CenterContainer/VBoxContainer/AuthPanel/MarginContainer/AuthVBox/WelcomeLabel") + var auth_buttons_row: HBoxContainer = menu.get_node("CenterContainer/VBoxContainer/AuthButtonsRow") + var auth_panel: PanelContainer = menu.get_node("CenterContainer/VBoxContainer/AuthPanel") + + _check("Play enabled", not play_button.disabled) + _check("Welcome label visible", welcome_label.visible) + _check("Auth buttons row hidden", not auth_buttons_row.visible) + _check("Auth panel hidden", not auth_panel.visible) + + return (not play_button.disabled) \ + and welcome_label.visible \ + and not auth_buttons_row.visible \ + and not auth_panel.visible + + +## Wait for the main menu's _ready() to finish, including its awaited +## ApiClient.get_me() call (which is why we wait several frames). +func _wait_for_menu_ready() -> void: + for i in 20: + await process_frame + if current_scene is Control: + # The menu's _ready() awaits get_me(); give it time to resolve. + if i >= 1: + return + await process_frame + + +func _wait_for_scene_change(previous_scene: Node) -> void: + for i in 20: + 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)