Add visual harmonization tests for editor UI components and ground finish chooser
- Implemented `test_editor_visual_harmonization.gd` to validate the Stickman editor and prop editor UI layouts, ensuring proper functionality and visual consistency. - Created `test_editor_visual_theme.gd` to verify the loading and application of visual themes in the editor, including semantic overrides and default values. - Introduced `test_phase19_ground_finish_chooser.gd` to test the ground finish chooser's functionality, including layout responsiveness, selection behavior, and interaction with the stage. - Added new scripts for ground finish preview and chooser UI components to enhance the editor's visual representation of ground finishes.
This commit is contained in:
@@ -0,0 +1,235 @@
|
||||
extends SceneTree
|
||||
|
||||
const SANDBOX_STAGE: PackedScene = preload("res://scenes/stage/sandbox_stage.tscn")
|
||||
const DIALOG_STYLER: Script = preload("res://scripts/shared/stage_ui/stage_dialog_styler.gd")
|
||||
|
||||
var _checks: int = 0
|
||||
var _failures: int = 0
|
||||
var _stage: SandboxStage
|
||||
var _chooser: StageGroundFinishChooser
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
call_deferred("_run")
|
||||
|
||||
|
||||
func _run() -> void:
|
||||
root.size = Vector2i(1280, 720)
|
||||
_stage = SANDBOX_STAGE.instantiate() as SandboxStage
|
||||
root.add_child(_stage)
|
||||
await _wait_frames(3)
|
||||
_open_through_palette_signal()
|
||||
await _wait_frames(3)
|
||||
_test_initial_gallery()
|
||||
await _save_capture("phase19_visual_ground_finish_chooser_1280x720.png")
|
||||
await _test_narrow_viewport_fit()
|
||||
await _save_capture("phase19_visual_ground_finish_chooser_470x417.png")
|
||||
_test_close_keeps_placement_unchanged()
|
||||
root.size = Vector2i(1280, 720)
|
||||
await _wait_frames(2)
|
||||
_open_through_palette_signal()
|
||||
await process_frame
|
||||
DIALOG_STYLER.dismiss_on_escape(_chooser)
|
||||
_test_escape_keeps_placement_unchanged()
|
||||
_open_through_palette_signal()
|
||||
await _wait_frames(2)
|
||||
_test_finish_selection_is_a_draft()
|
||||
await _save_capture("phase19_visual_ground_finish_chooser_dirt_selected.png")
|
||||
_test_draw_commits_selected_finish()
|
||||
if DisplayServer.get_name() != "headless":
|
||||
Input.warp_mouse(Vector2i(900, 420))
|
||||
await _wait_frames(3)
|
||||
_test_ground_ghost_is_armed()
|
||||
await _save_capture("phase19_visual_ground_finish_placement.png")
|
||||
await _test_ground_click_commits_selected_finish()
|
||||
_stage.queue_free()
|
||||
await _wait_frames(3)
|
||||
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 _open_through_palette_signal() -> void:
|
||||
var buttons: Dictionary = _stage.get("_palette_buttons") as Dictionary
|
||||
var ground_button: AssetCard = buttons.get("ground") as AssetCard
|
||||
_expect(ground_button != null, "Ground palette card exists")
|
||||
if ground_button == null:
|
||||
return
|
||||
ground_button.set_pressed_no_signal(true)
|
||||
ground_button.activated.emit(ground_button.payload())
|
||||
_chooser = _stage.get("_ground_finish_chooser") as StageGroundFinishChooser
|
||||
|
||||
|
||||
func _test_initial_gallery() -> void:
|
||||
_expect(_chooser != null, "Ground finish chooser is built")
|
||||
if _chooser == null:
|
||||
return
|
||||
_expect(DIALOG_STYLER.is_open(_chooser), "Ground card opens the modal chooser through the palette signal")
|
||||
_expect(_chooser.title == "Choose Ground", "Chooser title matches the approved gallery")
|
||||
_expect(_chooser.dialog_text == "Stickmen walk here", "Chooser explains the Ground role")
|
||||
_expect(_chooser.cancel_button_text == "Close", "Footer dismissal action is Close")
|
||||
_expect(_chooser.get_ok_button().text == "Draw Grass Ground", "Primary action names the selected finish")
|
||||
var buttons: Dictionary = _chooser.choice_buttons()
|
||||
_expect(buttons.size() == 7, "Gallery contains seven exclusive Ground finish choices")
|
||||
var grass_button: Button = buttons.get(&"finish_ground_grass") as Button
|
||||
_expect(grass_button != null and grass_button.button_pressed, "Current Grass finish is visibly selected")
|
||||
_expect(_chooser.selected_preview_finish() == &"finish_ground_grass", "Large sample previews the selected finish")
|
||||
var grid: GridContainer = _chooser.get("_choice_grid") as GridContainer
|
||||
var wide_row: HBoxContainer = _chooser.get("_layout_row") as HBoxContainer
|
||||
var preview_panel: Control = _chooser.get("_preview_panel") as Control
|
||||
_expect(bool(_chooser.get("_is_wide_layout")) and grid != null and grid.columns == 3, "1280×720 opens the side-by-side three-column gallery")
|
||||
_expect(grid != null and grid.get_parent() == wide_row and preview_panel != null and preview_panel.visible, "Wide gallery keeps choices beside the selected preview")
|
||||
var card: Control = _chooser.get_meta(DIALOG_STYLER.CARD_META) as Control
|
||||
var viewport_size: Vector2 = root.get_visible_rect().size
|
||||
var card_rect: Rect2 = card.get_global_rect() if card != null else Rect2()
|
||||
_expect(card != null and card_rect.size.x < viewport_size.x * 0.9, "Roomy chooser card leaves visible horizontal Stage margins")
|
||||
_expect(card != null and card_rect.size.y < viewport_size.y * 0.82, "Roomy chooser card stays comfortably below the Stage height")
|
||||
_expect(card != null and card_rect.position.x >= 20.0 and card_rect.end.x <= viewport_size.x - 20.0, "Roomy chooser remains inside the viewport width")
|
||||
_expect(card != null and card_rect.position.y >= 20.0 and card_rect.end.y <= viewport_size.y - 20.0, "Roomy chooser remains inside the viewport height")
|
||||
|
||||
|
||||
func _test_narrow_viewport_fit() -> void:
|
||||
if _chooser == null:
|
||||
return
|
||||
root.size = Vector2i(470, 417)
|
||||
await _wait_frames(2)
|
||||
_chooser.call("_apply_responsive_layout")
|
||||
DIALOG_STYLER.refit(_chooser)
|
||||
await _wait_frames(2)
|
||||
var card: Control = _chooser.get_meta(DIALOG_STYLER.CARD_META) as Control
|
||||
var viewport_size: Vector2 = root.get_visible_rect().size
|
||||
var card_rect: Rect2 = card.get_global_rect() if card != null else Rect2()
|
||||
var grid: GridContainer = _chooser.get("_choice_grid") as GridContainer
|
||||
var preview_panel: Control = _chooser.get("_preview_panel") as Control
|
||||
_expect(card != null and card_rect.position.x >= 12.0 and card_rect.end.x <= viewport_size.x - 12.0, "Narrow chooser clamps within horizontal safe margins")
|
||||
_expect(card != null and card_rect.position.y >= 12.0 and card_rect.end.y <= viewport_size.y - 12.0, "Narrow chooser clamps within vertical safe margins")
|
||||
_expect(card != null and card_rect.size.x < viewport_size.x and card_rect.size.y < viewport_size.y, "Narrow chooser remains a card instead of a full-scene sheet")
|
||||
_expect(bool(_chooser.get("_is_compact_layout")) and grid != null and grid.columns == 3, "470×417 uses the compact three-column gallery")
|
||||
_expect(preview_panel != null and not preview_panel.visible, "Compact gallery hides the redundant large preview")
|
||||
var body_scroll: ScrollContainer = _chooser.get_meta(DIALOG_STYLER.BODY_SCROLL_META) as ScrollContainer
|
||||
var body_rect: Rect2 = body_scroll.get_global_rect() if body_scroll != null else Rect2()
|
||||
var all_choices_visible: bool = grid != null and grid.get_child_count() == 7 and body_scroll != null
|
||||
if all_choices_visible:
|
||||
for child: Node in grid.get_children():
|
||||
var choice: Control = child as Control
|
||||
var choice_rect: Rect2 = choice.get_global_rect()
|
||||
if not choice.visible or choice_rect.position.x < body_rect.position.x - 1.0 or choice_rect.end.x > body_rect.end.x + 1.0 or choice_rect.position.y < body_rect.position.y - 1.0 or choice_rect.end.y > body_rect.end.y + 1.0:
|
||||
all_choices_visible = false
|
||||
break
|
||||
_expect(all_choices_visible, "Compact gallery exposes all seven finish cards without scrolling")
|
||||
var actions: HBoxContainer = _chooser.get_meta(DIALOG_STYLER.ACTIONS_META) as HBoxContainer
|
||||
var close_button: Button = DIALOG_STYLER.visual_button(_chooser.get_cancel_button())
|
||||
var draw_button: Button = DIALOG_STYLER.visual_button(_chooser.get_ok_button())
|
||||
_expect(actions != null and close_button != null and draw_button != null, "Narrow chooser keeps both footer actions in its fixed footer")
|
||||
_expect(close_button != null and close_button.get_global_rect().intersects(card_rect), "Narrow chooser keeps Close reachable inside the card")
|
||||
_expect(draw_button != null and draw_button.get_global_rect().intersects(card_rect), "Narrow chooser keeps Draw Ground reachable inside the card")
|
||||
|
||||
|
||||
func _test_close_keeps_placement_unchanged() -> void:
|
||||
if _chooser == null:
|
||||
return
|
||||
var close_button: Button = DIALOG_STYLER.visual_button(_chooser.get_cancel_button())
|
||||
if close_button != null:
|
||||
close_button.pressed.emit()
|
||||
var placement_id: String = String(_stage.get("_placement_id"))
|
||||
var new_finish: StringName = StringName(_stage.get("_new_ground_finish"))
|
||||
var ground_button: Button = (_stage.get("_palette_buttons") as Dictionary).get("ground") as Button
|
||||
_expect(not DIALOG_STYLER.is_open(_chooser), "Close dismisses the chooser")
|
||||
_expect(placement_id.is_empty(), "Close does not arm Ground placement")
|
||||
_expect(new_finish == &"finish_ground_grass", "Close discards the unconfirmed finish choice")
|
||||
_expect(ground_button != null and not ground_button.button_pressed, "Close restores the Ground card to its inactive state")
|
||||
|
||||
|
||||
func _test_escape_keeps_placement_unchanged() -> void:
|
||||
if _chooser == null:
|
||||
return
|
||||
var placement_id: String = String(_stage.get("_placement_id"))
|
||||
var ground_button: Button = (_stage.get("_palette_buttons") as Dictionary).get("ground") as Button
|
||||
_expect(not DIALOG_STYLER.is_open(_chooser), "Escape dismisses the chooser")
|
||||
_expect(placement_id.is_empty(), "Escape does not arm Ground placement")
|
||||
_expect(ground_button != null and not ground_button.button_pressed, "Escape restores the Ground card to its inactive state")
|
||||
|
||||
|
||||
func _test_finish_selection_is_a_draft() -> void:
|
||||
if _chooser == null:
|
||||
return
|
||||
var dirt_button: Button = _chooser.choice_buttons().get(&"finish_ground_dirt") as Button
|
||||
if dirt_button != null:
|
||||
dirt_button.set_pressed_no_signal(true)
|
||||
dirt_button.pressed.emit()
|
||||
_expect(_chooser.selected_finish() == &"finish_ground_dirt", "Choosing Dirt updates the draft finish")
|
||||
_expect(_chooser.selected_preview_finish() == &"finish_ground_dirt", "Choosing Dirt updates the large preview")
|
||||
_expect(_chooser.get_ok_button().text == "Draw Dirt Ground", "Choosing Dirt updates the primary action")
|
||||
_expect(StringName(_stage.get("_new_ground_finish")) == &"finish_ground_grass", "Draft selection does not mutate the active Ground finish")
|
||||
_expect(String(_stage.get("_placement_id")).is_empty(), "Draft selection does not start placement")
|
||||
|
||||
|
||||
func _test_draw_commits_selected_finish() -> void:
|
||||
if _chooser == null:
|
||||
return
|
||||
var draw_button: Button = DIALOG_STYLER.visual_button(_chooser.get_ok_button())
|
||||
if draw_button != null:
|
||||
draw_button.pressed.emit()
|
||||
var painter: StageTerrainPainter = _stage.get("_terrain_painter") as StageTerrainPainter
|
||||
_expect(not DIALOG_STYLER.is_open(_chooser), "Draw Ground closes the chooser")
|
||||
_expect(StringName(_stage.get("_new_ground_finish")) == &"finish_ground_dirt", "Draw Ground commits the selected finish")
|
||||
_expect(String(_stage.get("_placement_id")) == "ground", "Draw Ground enters the existing Ground placement tool")
|
||||
_expect(painter != null and StringName(painter.get("_ground_finish")) == &"finish_ground_dirt", "Ground painter receives the selected finish")
|
||||
|
||||
|
||||
func _test_ground_ghost_is_armed() -> void:
|
||||
var ghost: Node2D = _stage.get("_ghost") as Node2D
|
||||
_expect(ghost != null and is_instance_valid(ghost), "Draw Ground shows the cursor-following Ground ghost")
|
||||
|
||||
|
||||
func _test_ground_click_commits_selected_finish() -> void:
|
||||
var stage_position := Vector2(900.0, 420.0)
|
||||
var press := InputEventMouseButton.new()
|
||||
press.position = stage_position
|
||||
press.global_position = stage_position
|
||||
press.button_index = MOUSE_BUTTON_LEFT
|
||||
press.pressed = true
|
||||
_stage._unhandled_input(press)
|
||||
var release := InputEventMouseButton.new()
|
||||
release.position = stage_position
|
||||
release.global_position = stage_position
|
||||
release.button_index = MOUSE_BUTTON_LEFT
|
||||
release.pressed = false
|
||||
_stage._unhandled_input(release)
|
||||
await _wait_frames(3)
|
||||
var placed_dirt_ground: bool = false
|
||||
var world: Node2D = _stage.get_node("World") as Node2D
|
||||
if world != null:
|
||||
for child: Node in world.get_children():
|
||||
if child is TerrainBlock and (child as TerrainBlock).spawn_id == "ground" and (child as TerrainBlock).finish_id == &"finish_ground_dirt":
|
||||
placed_dirt_ground = true
|
||||
break
|
||||
_expect(placed_dirt_ground, "Production Ground input places the selected Dirt finish")
|
||||
|
||||
|
||||
func _wait_frames(count: int) -> void:
|
||||
for _frame_index: int in count:
|
||||
await process_frame
|
||||
|
||||
|
||||
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 path: String = ProjectSettings.globalize_path("res://tests/" + file_name)
|
||||
var error: Error = capture.save_png(path)
|
||||
_expect(error == OK, "render capture saved for visual inspection: %s" % path)
|
||||
print("CAPTURE: %s size=%s result=%s" % [path, str(capture.get_size()), str(error)])
|
||||
|
||||
|
||||
func _expect(condition: bool, description: String) -> void:
|
||||
_checks += 1
|
||||
if condition:
|
||||
print("PASS: %s" % description)
|
||||
else:
|
||||
_failures += 1
|
||||
push_error("FAIL: %s" % description)
|
||||
Reference in New Issue
Block a user