extends SceneTree # Headless gameplay test: run with # godot --headless -s res://tests/test_main_menu.gd # Verifies: # 1. the menu loads with Play/Map Editor/Quit; Map Editor is enabled and # reaches PlanetSetupEditor.tscn (#48) # 2. there is no Quit entry in any build - a web build has nothing to quit # to, and Android's back gesture already does it # 3. Play resets GameState to a fresh game AND reaches Main.tscn var menu: Node var game_state: Node var elapsed: float = 0.0 var stage: int = 0 func _initialize() -> void: game_state = root.get_node("GameState") menu = load("res://ui/MainMenu.tscn").instantiate() root.add_child(menu) current_scene = menu func _process(delta: float) -> bool: elapsed += delta match stage: 0: # A scene added to the tree inside _initialize() has not had its # _ready() flushed yet - see test_planet_setups.gd for the same # hazard - so the menu's @onready buttons aren't valid until a # frame has actually processed. if elapsed < 0.1: return false if not menu.editor_button.enabled: return _fail("map editor button is not enabled (#48 shipped)") # Both development entries follow the same rule, and the rule is # what is under test rather than the concrete boolean - this run # is from Godot, so it exercises the "shown" branch. var developing: bool = menu.is_developer_build() if menu.editor_button.visible != developing or menu.editor_label.visible != developing: return _fail("map editor entry visibility did not follow is_developer_build()") if menu.dev_button.visible != developing or menu.dev_label.visible != developing: return _fail("dev mode entry visibility did not follow is_developer_build()") if menu.has_node("UI/QuitButton") or menu.has_node("UI/QuitLabel"): return _fail("the menu still carries a Quit entry") _press(menu.editor_button) elapsed = 0.0 stage = 1 return false 1: # _start awaits a frame so its status text paints before the # blocking load, so the change takes a couple of frames. if elapsed < 1.0: return false var on_editor: bool = current_scene != null and current_scene.name == "PlanetSetupEditor" if not on_editor: return _fail("pressing Map Editor did not reach PlanetSetupEditor.tscn") # Return to the menu the same way the player would, then test Play. menu = load("res://ui/MainMenu.tscn").instantiate() root.add_child(menu) current_scene = menu elapsed = 0.0 stage = 2 return false 2: if elapsed < 0.1: return false # Dirty the game state exactly like an in-progress run would, # so a genuine reset is distinguishable from a coincidental match. game_state.bank_inventory.set_amount(GameState.RESOURCE_ORE, 999) game_state.add_building(GameState.BUILDING_MOTHERSHIP, Vector2.ZERO) game_state.surviving_ship_count = 1 game_state.planet_capture["chunk:1:1"] = {"captured": [true], "conquered": true} _press(menu.play_button) elapsed = 0.0 stage = 3 return false 3: if elapsed < 1.0: return false var on_main: bool = current_scene != null and current_scene.name == "Main" # A normal game now starts with NOTHING banked - the opening # mining run is the tutorial, and 500 free ore skipped it. var reset_ok: bool = game_state.rocks == 0 and game_state.buildings.is_empty() \ and game_state.surviving_ship_count == GameState.STARTING_SHIP_COUNT \ and game_state.planet_capture.is_empty() print("on_main=%s reset_ok=%s rocks=%d buildings=%d ship_count=%d" % [ on_main, reset_ok, game_state.rocks, game_state.buildings.size(), game_state.surviving_ship_count ]) if not on_main or not reset_ok: return _fail("Play did not reset to a fresh game before reaching Main.tscn") print("TEST_MAIN_MENU: PASS") quit(0) return true return false func _press(button: IconButton) -> void: var down: InputEventScreenTouch = InputEventScreenTouch.new() down.pressed = true button._gui_input(down) var up: InputEventScreenTouch = InputEventScreenTouch.new() up.pressed = false button._gui_input(up) func _fail(message: String) -> bool: print("TEST_MAIN_MENU: FAIL (%s)" % message) quit(1) return true