extends SceneTree const MainScene = preload("res://main.tscn") const SETTINGS_PATH := "user://aetherbound_guild_settings.json" var failures: Array[String] = [] func _initialize() -> void: call_deferred("run_test") func run_test() -> void: root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS # Single target size (owner policy): every screen is authored at 1280x720. root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_KEEP root.content_scale_size = Vector2i(1280, 720) root.size = Vector2i(1280, 720) _cleanup() var game = MainScene.instantiate() game.audio_enabled = false root.add_child(game) await _frames(50) var shell = game.entry_shell expect(shell != null and shell.current_screen == "boot", "normal launch creates the entry shell") # The boot screen dwells on its verification pass; wait for the handoff # instead of assuming a fixed frame budget. var apply: Button = await _wait_for_node(shell, "SetupApply") await _touch(apply) expect(shell.current_screen == "title", "parsed ScreenTouch applies first launch and reaches Title") var settings_button: Button = shell.find_child("TitleSettings", true, false) await _touch(settings_button) expect(shell.current_screen == "settings", "parsed ScreenTouch opens Settings") var language_category: Button = shell.find_child("SettingsCategory_language", true, false) await _touch(language_category) var locale_button: Button = shell.find_child("SettingLocale", true, false) await _touch(locale_button) var apply_settings: Button = shell.find_child("SettingsApply", true, false) await _touch(apply_settings) var back: Button = shell.find_child("SettingsBack", true, false) await _touch(back) expect(shell.current_screen == "title" and shell.settings.locale == "en", "locale applies and Back returns to Title") await _release_scene(game) var restarted = MainScene.instantiate() restarted.audio_enabled = false root.add_child(restarted) await _frames(50) var restarted_title := await _wait_for_node(restarted.entry_shell, "TitleSafeArea") expect(restarted_title != null and restarted.entry_shell.current_screen == "title", "restart skips completed first launch") await _release_scene(restarted) _cleanup() var reset_game = MainScene.instantiate() reset_game.audio_enabled = false root.add_child(reset_game) await _frames(50) var reset_setup := await _wait_for_node(reset_game.entry_shell, "FirstLaunchResponsiveLayout") expect(reset_setup != null, "settings reset restores first-launch setup") await _release_scene(reset_game) _cleanup() if failures.is_empty(): print("ABG_DEV1_ENTRY_TOUCH_OK parsed_screen_touch=true boot_setup_title_settings_apply_back=true restart=title reset=first_launch") quit(0) else: for failure in failures: push_error(failure) quit(1) func _release_scene(scene: Node) -> void: # Release the synthesized SFX stream while the tree is still alive; freeing # it during teardown races the audio server and leaks the generator playback. var facade = scene.get("facade") if facade != null: var adapter = facade.get("audio_adapter") if adapter != null: adapter.cleanup() scene.queue_free() await _frames(20) func _touch(control: Control) -> void: var center := root.get_final_transform() * control.get_global_rect().get_center() var press := InputEventScreenTouch.new() press.position = center press.pressed = true press.index = 0 Input.parse_input_event(press) await _frames(2) var release := InputEventScreenTouch.new() release.position = center release.pressed = false release.index = 0 Input.parse_input_event(release) await _frames(4) func _wait_for_node(shell: Node, node_name: String, max_frames := 600) -> Node: for _i in range(max_frames): var node := shell.find_child(node_name, true, false) if node != null: return node await process_frame return null func _frames(count: int) -> void: for frame in range(count): await process_frame func _cleanup() -> void: for path in [SETTINGS_PATH, "%s.bak" % SETTINGS_PATH, "%s.candidate" % SETTINGS_PATH]: if FileAccess.file_exists(path): DirAccess.remove_absolute(path) func expect(condition: bool, message: String) -> void: if not condition: failures.append("DEV-1 entry touch: %s" % message)