diff --git a/scripts/main_menu.gd b/scripts/main_menu.gd index a38ae4e..605feee 100644 --- a/scripts/main_menu.gd +++ b/scripts/main_menu.gd @@ -15,6 +15,14 @@ extends Control ## back to the login form. +## Emitted once _ready()'s session check (has_token() + the awaited +## get_me() call) has actually resolved, whichever way -- lets a test (or +## anything else) know the UI state is trustworthy now, rather than +## guessing from a frame count or from UI properties that are ambiguous +## mid-await (e.g. AuthButtonsRow.visible is true both before the check +## resolves AND in a genuine logged-out end state). +signal auth_state_ready + # ─── UI references ──────────────────────────────────────────────────────────── var _auth_buttons_row: HBoxContainer @@ -66,10 +74,12 @@ func _ready() -> void: var username: String = str((me_result.data as Dictionary).get("username", "")) if username != "": _show_authenticated(username) + auth_state_ready.emit() return # Token missing or invalid — fall through to logged-out state. _reset_to_logged_out() + auth_state_ready.emit() ## Restore the UI to the authenticated state (Play enabled, welcome label, diff --git a/scripts/smoke_test_main_menu_back.gd b/scripts/smoke_test_main_menu_back.gd index c8c6758..621a31e 100644 --- a/scripts/smoke_test_main_menu_back.gd +++ b/scripts/smoke_test_main_menu_back.gd @@ -45,7 +45,8 @@ func _run() -> void: return # 1. Establish a real, valid session exactly the way a user would. - var unique_id := Time.get_ticks_msec() + randomize() + var unique_id: int = int(Time.get_unix_time_from_system() * 1000) + randi() % 100000 var username := USERNAME % unique_id var email := EMAIL % unique_id print("\n=== Setup: register ", username, " ===") @@ -59,13 +60,21 @@ func _run() -> void: # 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() + # change_scene_to_file defers the actual swap -- a couple frames is + # plenty for the scene object itself to exist (unlike the real + # get_me() network round-trip afterward, which is what auth_state_ready + # actually waits for below). + for i in 5: + await process_frame + if current_scene is Control: + break 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 + await menu.auth_state_ready _check("first main menu visit restores authenticated state", _is_authenticated(menu)) # 3. Play -> world_list.tscn (real button press). @@ -90,6 +99,12 @@ func _run() -> void: _finish() return + # The new main_menu instance's own _ready() is *also* awaiting a real + # get_me() call -- _wait_for_scene_change only confirms the scene + # object exists, not that its authenticated-state fields have been + # set yet. Wait for the real signal, not a guess. + await menu.auth_state_ready + # 5. THE regression assertion: still logged in after returning. _check("main menu stays authenticated after Play -> Back", _is_authenticated(menu)) @@ -113,18 +128,6 @@ func _is_authenticated(menu: Control) -> bool: 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