extends SceneTree const SELECTOR_SCENE: PackedScene = preload("res://scenes/stage/asset_selector.tscn") const SANDBOX_THEME_SCRIPT: Script = preload("res://scripts/stage/sandbox_theme.gd") var _checks: int = 0 var _failures: int = 0 var _selector: AssetSelector func _initialize() -> void: call_deferred("_run") func _run() -> void: root.size = Vector2i(1280, 720) _selector = SELECTOR_SCENE.instantiate() as AssetSelector root.add_child(_selector) await process_frame var stage_theme: SandboxTheme = SANDBOX_THEME_SCRIPT.new() as SandboxTheme stage_theme.load_from("res://sandbox_theme.json") _selector.apply_theme(stage_theme) _selector.open("starters", _starter_entries()) await process_frame await process_frame _test_roomy_layout() await _save_capture("phase19_starter_chooser_wide.png") await _test_hover_style_and_capture() await _test_narrow_layout() _selector.hide() _selector.queue_free() await process_frame if _failures == 0: print("RESULT: ALL PASSED (%d checks)" % _checks) quit(0) else: print("RESULT: %d FAILURE(S) out of %d checks" % [_failures, _checks]) quit(1) func _starter_entries() -> Array[Dictionary]: return [ { "id": "starter_plain_background", "name": "Plain Background", "description": "An empty stage with one color. Choose a new color whenever you like.", "path": "res://content/stages/starters/plain_background.scene", }, { "id": "starter_blank_scene", "name": "Open Sky", "description": "An empty stage with a soft sky. Sun and clouds, ready for your story.", "path": "res://content/stages/starters/blank.scene", }, { "id": "starter_town_street", "name": "Town Street", "description": "Cloudy sky, shops, trees, sidewalk, and road.", "path": "res://content/stages/starters/town_street.scene", }, { "id": "starter_living_room", "name": "Living Room", "description": "A cozy room with a window, rug, and furniture.", "path": "res://content/stages/starters/living_room.scene", }, ] func _test_roomy_layout() -> void: var grid: GridContainer = _grid() var close_button: Button = _selector.find_child("CloseButton", true, false) as Button _expect(_selector.size.x == 1160, "roomy starter chooser uses the preferred 1160-pixel width") _expect(int(_selector.get("_active_columns")) == 2 and grid != null and grid.columns == 2, "roomy chooser presents two card columns") _expect(close_button != null and close_button.visible and close_button.custom_minimum_size.y >= 44.0, "roomy chooser keeps its visible Close action reachable") var first_card: Control = grid.get_child(0) as Control if grid != null and grid.get_child_count() > 0 else null var preview: Control = first_card.find_child("StarterScenePreview", true, false) as Control if first_card != null else null var viewport: SubViewport = preview.get_child(0) as SubViewport if preview != null and preview.get_child_count() > 0 else null var preview_aspect: float = float(viewport.size.x) / float(viewport.size.y) if viewport != null and viewport.size.y > 0 else 0.0 _expect(first_card != null and first_card.custom_minimum_size.x >= 500.0, "roomy starter cards expand to at least 500 pixels") _expect(preview != null and preview.custom_minimum_size.x >= 500.0 and preview.custom_minimum_size.y >= 240.0, "roomy previews meet the approved dimensions") _expect(viewport != null and viewport.size.x >= 500 and viewport.size.y >= 240 and preview_aspect >= 1.9 and preview_aspect <= 2.2, "preview render target preserves the wide scene shape") var title: Label = _selector.find_child("TitleLabel", true, false) as Label _expect(title != null and title.visible and title.text == "Choose a starter scene", "roomy chooser keeps its readable title") func _test_narrow_layout() -> void: root.size = Vector2i(550, 690) await process_frame _selector.call("_update_popup_layout") _selector.call("_rebuild") await process_frame await process_frame var grid: GridContainer = _grid() var close_button: Button = _selector.find_child("CloseButton", true, false) as Button _expect(_selector.size.x <= root.size.x - 24, "narrow chooser stays within the safe screen margins") _expect(int(_selector.get("_active_columns")) == 1 and grid != null and grid.columns == 1, "narrow chooser switches to a single card column") _expect(int(_selector.get("_page_capacity")) == 4 and grid != null and grid.get_child_count() == 4, "narrow chooser keeps all four cards in the scrollable body") _expect(close_button != null and close_button.visible, "narrow chooser keeps Close visible outside the scrolling body") var first_card: Control = grid.get_child(0) as Control if grid != null and grid.get_child_count() > 0 else null var preview: Control = first_card.find_child("StarterScenePreview", true, false) as Control if first_card != null else null _expect(preview != null and preview.custom_minimum_size.x >= 400.0 and preview.custom_minimum_size.y >= 200.0, "narrow single-column layout retains large previews") await _save_capture("phase19_starter_chooser_narrow.png") func _test_hover_style_and_capture() -> void: var grid: GridContainer = _grid() var hover_card: Button = grid.get_child(1) as Button if grid != null and grid.get_child_count() > 1 else null if hover_card == null: _expect(false, "starter card is available for hover preview") return var hover_style: StyleBoxFlat = hover_card.get_theme_stylebox("hover") as StyleBoxFlat _expect(hover_style != null and hover_style.border_color.is_equal_approx(Color("#22C6FF")), "starter hover style uses the EDIT blue border") if hover_style == null: return var normal_style: StyleBox = hover_card.get_theme_stylebox("normal") hover_card.add_theme_stylebox_override("normal", hover_style) await process_frame await _save_capture("phase19_starter_chooser_hover.png") hover_card.add_theme_stylebox_override("normal", normal_style) func _grid() -> GridContainer: return _selector.find_child("GridContainer", true, false) as GridContainer func _expect(condition: bool, message: String) -> void: _checks += 1 if condition: print("PASS: " + message) else: _failures += 1 push_error("Starter chooser layout check failed: " + message) func _save_capture(file_name: String) -> void: if DisplayServer.get_name() == "headless": return await RenderingServer.frame_post_draw var capture: Image = root.get_texture().get_image() var capture_dir: String = ProjectSettings.globalize_path("res://.phase19_captures") DirAccess.make_dir_recursive_absolute(capture_dir) var error: Error = capture.save_png(capture_dir.path_join(file_name)) _expect(error == OK, "capture saved for visual inspection: %s" % file_name)