- Remove character_select.tscn and character_select.gd entirely (Smuggler/Detective archetypes removed per v0.2 pivot) - New Game goes straight to character creation - Rename Randomise→Randomize throughout (US English convention) - Add *.import and *.uid to client gitignore (import artifacts) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
633 lines
20 KiB
GDScript
633 lines
20 KiB
GDScript
## Sprint 28 — Character creation screen tests (#705, Task #9)
|
||
##
|
||
## Validates the CharacterCreation UI: scene instantiation, tab structure,
|
||
## signal emission (creation_confirmed / creation_cancelled), keyboard nav
|
||
## callbacks, randomize, color derivation helpers, and game flow wiring.
|
||
##
|
||
## These are UI-only tests (no compositor/server required).
|
||
## CharacterVisual asset paths fall back gracefully when GLBs are absent.
|
||
##
|
||
## Ticket: #705 | D-146, D-155, D-158, D-159, D-165
|
||
class_name TestCharacterCreationSprint28
|
||
extends GdUnitTestSuite
|
||
|
||
const SCENE_PATH := "res://scenes/character_creation.tscn"
|
||
|
||
var _scene: CharacterCreation = null
|
||
|
||
|
||
func before_each() -> void:
|
||
var packed := load(SCENE_PATH) as PackedScene
|
||
if packed == null:
|
||
# Skip all tests gracefully if scene not loadable in headless env
|
||
return
|
||
_scene = packed.instantiate() as CharacterCreation
|
||
add_child(_scene)
|
||
|
||
|
||
func after_each() -> void:
|
||
if is_instance_valid(_scene):
|
||
_scene.queue_free()
|
||
_scene = null
|
||
|
||
|
||
# =============================================================================
|
||
# Scene instantiation (Task #1)
|
||
# =============================================================================
|
||
|
||
func test_scene_instantiates() -> void:
|
||
if _scene == null:
|
||
return
|
||
assert_bool(is_instance_valid(_scene)).override_failure_message(
|
||
"character_creation.tscn must instantiate successfully"
|
||
).is_true()
|
||
|
||
|
||
func test_scene_is_character_creation_class() -> void:
|
||
if _scene == null:
|
||
return
|
||
assert_bool(_scene is CharacterCreation).override_failure_message(
|
||
"instantiated scene must be a CharacterCreation node"
|
||
).is_true()
|
||
|
||
|
||
func test_tab_container_has_five_tabs() -> void:
|
||
if _scene == null:
|
||
return
|
||
var tc: TabContainer = _scene.get_node_or_null("Layout/TabPanel/TabContainer")
|
||
assert_bool(tc != null).override_failure_message("TabContainer must exist").is_true()
|
||
if tc == null:
|
||
return
|
||
assert_int(tc.get_tab_count()).override_failure_message(
|
||
"TabContainer must have exactly 5 tabs (Body/Head/Hair/Clothing/Accessories)"
|
||
).is_equal(5)
|
||
|
||
|
||
func test_tab_names() -> void:
|
||
if _scene == null:
|
||
return
|
||
var tc: TabContainer = _scene.get_node_or_null("Layout/TabPanel/TabContainer")
|
||
if tc == null:
|
||
return
|
||
var expected := ["Body", "Head", "Hair", "Clothing", "Accessories"]
|
||
for i in expected.size():
|
||
assert_str(tc.get_tab_title(i)).override_failure_message(
|
||
"Tab %d must be named '%s'" % [i, expected[i]]
|
||
).is_equal(expected[i])
|
||
|
||
|
||
func test_subviewport_exists() -> void:
|
||
if _scene == null:
|
||
return
|
||
var sv: SubViewport = _scene.get_node_or_null(
|
||
"Layout/PreviewPanel/SubViewportContainer/SubViewport"
|
||
)
|
||
assert_bool(sv != null).override_failure_message("SubViewport must exist in scene tree").is_true()
|
||
|
||
|
||
func test_subviewport_always_updates() -> void:
|
||
if _scene == null:
|
||
return
|
||
var sv: SubViewport = _scene.get_node_or_null(
|
||
"Layout/PreviewPanel/SubViewportContainer/SubViewport"
|
||
)
|
||
if sv == null:
|
||
return
|
||
# render_target_update_mode = 3 = ALWAYS (live 3D preview)
|
||
assert_int(sv.render_target_update_mode).override_failure_message(
|
||
"SubViewport must use ALWAYS update mode (3) for live preview"
|
||
).is_equal(3)
|
||
|
||
|
||
func test_preview_camera_exists() -> void:
|
||
if _scene == null:
|
||
return
|
||
var cam: Camera3D = _scene.get_node_or_null(
|
||
"Layout/PreviewPanel/SubViewportContainer/SubViewport/PreviewCamera"
|
||
)
|
||
assert_bool(cam != null).override_failure_message("PreviewCamera must exist in SubViewport").is_true()
|
||
if cam == null:
|
||
return
|
||
assert_bool(cam.current).override_failure_message("PreviewCamera must be active (current=true)").is_true()
|
||
|
||
|
||
func test_footer_buttons_exist() -> void:
|
||
if _scene == null:
|
||
return
|
||
var back: Button = _scene.get_node_or_null("Footer/BackBtn")
|
||
var rand_btn: Button = _scene.get_node_or_null("Footer/RandomizeBtn")
|
||
var start: Button = _scene.get_node_or_null("Footer/StartBtn")
|
||
assert_bool(back != null).override_failure_message("BackBtn must exist in Footer").is_true()
|
||
assert_bool(rand_btn != null).override_failure_message("RandomizeBtn must exist in Footer").is_true()
|
||
assert_bool(start != null).override_failure_message("StartBtn must exist in Footer").is_true()
|
||
|
||
|
||
# =============================================================================
|
||
# Signal emission (Tasks #1 and #8)
|
||
# =============================================================================
|
||
|
||
func test_creation_cancelled_emits_on_back() -> void:
|
||
if _scene == null:
|
||
return
|
||
var monitor := monitor_signals(_scene)
|
||
_scene._on_back()
|
||
assert_signal(monitor).is_emitted("creation_cancelled")
|
||
|
||
|
||
func test_creation_confirmed_emits_on_start() -> void:
|
||
if _scene == null:
|
||
return
|
||
var monitor := monitor_signals(_scene)
|
||
_scene._on_start()
|
||
assert_signal(monitor).is_emitted("creation_confirmed")
|
||
|
||
|
||
func test_creation_confirmed_carries_descriptor() -> void:
|
||
if _scene == null:
|
||
return
|
||
var received_descriptor: CharacterVisualDescriptor = null
|
||
_scene.creation_confirmed.connect(func(d): received_descriptor = d)
|
||
_scene._on_start()
|
||
assert_bool(received_descriptor != null).override_failure_message(
|
||
"creation_confirmed must pass a CharacterVisualDescriptor"
|
||
).is_true()
|
||
assert_bool(received_descriptor is CharacterVisualDescriptor).is_true()
|
||
|
||
|
||
# =============================================================================
|
||
# Descriptor defaults (Task #1)
|
||
# =============================================================================
|
||
|
||
func test_descriptor_initialized_on_ready() -> void:
|
||
if _scene == null:
|
||
return
|
||
var desc: CharacterVisualDescriptor = null
|
||
_scene.creation_confirmed.connect(func(d): desc = d)
|
||
_scene._on_start()
|
||
assert_bool(desc != null).is_true()
|
||
if desc == null:
|
||
return
|
||
# Should default to a non-Child body type
|
||
assert_bool(desc.body_type != CharacterVisualDescriptor.BodyType.CHILD).override_failure_message(
|
||
"Default body type should not be Child"
|
||
).is_true()
|
||
# Skin tone should be in valid range
|
||
assert_bool(desc.skin_tone >= 0 and desc.skin_tone <= 8).override_failure_message(
|
||
"skin_tone must be 0–8"
|
||
).is_true()
|
||
|
||
|
||
# =============================================================================
|
||
# Rotation (Task #1, D-155)
|
||
# =============================================================================
|
||
|
||
func test_rotate_left_changes_facing() -> void:
|
||
if _scene == null:
|
||
return
|
||
var initial := _scene._facing_idx
|
||
_scene._on_rotate_left()
|
||
assert_int(_scene._facing_idx).override_failure_message(
|
||
"Q/rotate-left must advance facing_idx"
|
||
).is_not_equal(initial)
|
||
|
||
|
||
func test_rotate_right_changes_facing() -> void:
|
||
if _scene == null:
|
||
return
|
||
var initial := _scene._facing_idx
|
||
_scene._on_rotate_right()
|
||
assert_int(_scene._facing_idx).override_failure_message(
|
||
"E/rotate-right must change facing_idx"
|
||
).is_not_equal(initial)
|
||
|
||
|
||
func test_four_rotations_return_to_original() -> void:
|
||
if _scene == null:
|
||
return
|
||
var initial := _scene._facing_idx
|
||
_scene._on_rotate_left()
|
||
_scene._on_rotate_left()
|
||
_scene._on_rotate_left()
|
||
_scene._on_rotate_left()
|
||
assert_int(_scene._facing_idx).override_failure_message(
|
||
"4 left rotations must return to original facing"
|
||
).is_equal(initial)
|
||
|
||
|
||
# =============================================================================
|
||
# Camera angle toggle (Task #1, D-158)
|
||
# =============================================================================
|
||
|
||
func test_cam_angle_cycles_three_presets() -> void:
|
||
if _scene == null:
|
||
return
|
||
var initial := _scene._cam_pitch_idx
|
||
_scene._on_cam_angle_toggle()
|
||
_scene._on_cam_angle_toggle()
|
||
_scene._on_cam_angle_toggle()
|
||
assert_int(_scene._cam_pitch_idx).override_failure_message(
|
||
"3 cam angle toggles must return to original preset"
|
||
).is_equal(initial)
|
||
|
||
|
||
func test_cam_angle_default_is_frontal() -> void:
|
||
if _scene == null:
|
||
return
|
||
# frontal = index 0, pitch -5°
|
||
assert_int(_scene._cam_pitch_idx).override_failure_message(
|
||
"Default camera must be frontal (index 0, -5° per D-158)"
|
||
).is_equal(0)
|
||
|
||
|
||
# =============================================================================
|
||
# Body type selection (Task #2, D-159)
|
||
# =============================================================================
|
||
|
||
func test_body_type_selection_updates_descriptor() -> void:
|
||
if _scene == null:
|
||
return
|
||
_scene._on_body_type_selected(CharacterVisualDescriptor.BodyType.THIN_F)
|
||
var desc: CharacterVisualDescriptor = null
|
||
_scene.creation_confirmed.connect(func(d): desc = d)
|
||
_scene._on_start()
|
||
if desc == null:
|
||
return
|
||
assert_int(desc.body_type as int).override_failure_message(
|
||
"Selecting THIN_F must update descriptor.body_type"
|
||
).is_equal(CharacterVisualDescriptor.BodyType.THIN_F)
|
||
|
||
|
||
func test_all_11_body_types_selectable() -> void:
|
||
if _scene == null:
|
||
return
|
||
# Verify all 11 body types can be selected without error and update descriptor
|
||
var all_types := CharacterVisualDescriptor.BodyType.values()
|
||
assert_int(all_types.size()).override_failure_message(
|
||
"Must have 11 body types per D-159"
|
||
).is_equal(11)
|
||
for bt in all_types:
|
||
_scene._on_body_type_selected(bt)
|
||
assert_int(_scene._descriptor.body_type as int).override_failure_message(
|
||
"Body type selection must update descriptor immediately"
|
||
).is_equal(bt)
|
||
|
||
|
||
# =============================================================================
|
||
# Skin tone (Tasks #2/#3, shared state)
|
||
# =============================================================================
|
||
|
||
func test_skin_tone_selection_updates_descriptor() -> void:
|
||
if _scene == null:
|
||
return
|
||
_scene._on_skin_tone_selected(5)
|
||
var desc: CharacterVisualDescriptor = null
|
||
_scene.creation_confirmed.connect(func(d): desc = d)
|
||
_scene._on_start()
|
||
if desc == null:
|
||
return
|
||
assert_int(desc.skin_tone).override_failure_message(
|
||
"Selecting skin tone index 5 must update descriptor.skin_tone"
|
||
).is_equal(5)
|
||
|
||
|
||
func test_skin_tone_range_valid() -> void:
|
||
if _scene == null:
|
||
return
|
||
# All 9 indices must be selectable without error
|
||
for i in range(9):
|
||
_scene._on_skin_tone_selected(i)
|
||
assert_int(_scene._descriptor.skin_tone).is_equal(8) # last selected
|
||
|
||
|
||
# =============================================================================
|
||
# Hair selection (Task #4)
|
||
# =============================================================================
|
||
|
||
func test_hair_selection_updates_descriptor() -> void:
|
||
if _scene == null:
|
||
return
|
||
_scene._on_hair_selected("short_b")
|
||
assert_str(_scene._descriptor.hair_id).override_failure_message(
|
||
"Selecting hair 'short_b' must update descriptor.hair_id"
|
||
).is_equal("short_b")
|
||
|
||
|
||
func test_facial_hair_none_clears_id() -> void:
|
||
if _scene == null:
|
||
return
|
||
_scene._on_facial_hair_selected("beard")
|
||
assert_str(_scene._descriptor.facial_hair_id).is_equal("beard")
|
||
_scene._on_facial_hair_selected("")
|
||
assert_str(_scene._descriptor.facial_hair_id).is_equal("")
|
||
|
||
|
||
func test_eyebrow_selection_updates_descriptor() -> void:
|
||
if _scene == null:
|
||
return
|
||
_scene._on_eyebrow_selected("thick")
|
||
assert_str(_scene._descriptor.eyebrow_id).override_failure_message(
|
||
"Selecting eyebrow 'thick' must update descriptor.eyebrow_id"
|
||
).is_equal("thick")
|
||
|
||
|
||
# =============================================================================
|
||
# Color derivation helpers (Task #4/#5, D-165)
|
||
# =============================================================================
|
||
|
||
func test_derive_hair_highlight_lightens_primary() -> void:
|
||
if _scene == null:
|
||
return
|
||
var primary := Color(0.4, 0.2, 0.1)
|
||
var highlight := _scene._derive_hair_highlight(primary)
|
||
# Highlight should be lighter than primary
|
||
assert_bool(highlight.v > primary.v).override_failure_message(
|
||
"Hair highlight must be lighter than primary color"
|
||
).is_true()
|
||
|
||
|
||
func test_derive_secondary_darkens_primary() -> void:
|
||
if _scene == null:
|
||
return
|
||
var primary := Color(0.6, 0.5, 0.4)
|
||
var secondary := _scene._derive_secondary(primary)
|
||
assert_bool(secondary.v < primary.v).override_failure_message(
|
||
"Secondary color must be darker than primary"
|
||
).is_true()
|
||
|
||
|
||
func test_derive_accent_shifts_hue() -> void:
|
||
if _scene == null:
|
||
return
|
||
var primary := Color.from_hsv(0.0, 0.5, 0.7)
|
||
var accent := _scene._derive_accent(primary)
|
||
# Accent hue should differ from primary hue
|
||
assert_bool(abs(accent.h - primary.h) > 0.0).override_failure_message(
|
||
"Accent must have shifted hue from primary"
|
||
).is_true()
|
||
|
||
|
||
# =============================================================================
|
||
# Randomize (Task #8)
|
||
# =============================================================================
|
||
|
||
func test_randomize_changes_body_type() -> void:
|
||
if _scene == null:
|
||
return
|
||
# Run randomize multiple times to ensure it doesn't always pick the default
|
||
var changed := false
|
||
var original := _scene._descriptor.body_type
|
||
for _i in 20:
|
||
_scene._on_randomize()
|
||
if _scene._descriptor.body_type != original:
|
||
changed = true
|
||
break
|
||
assert_bool(changed).override_failure_message(
|
||
"Randomize must be able to change body type from default"
|
||
).is_true()
|
||
|
||
|
||
func test_randomize_produces_valid_skin_tone() -> void:
|
||
if _scene == null:
|
||
return
|
||
for _i in 10:
|
||
_scene._on_randomize()
|
||
var tone := _scene._descriptor.skin_tone
|
||
assert_bool(tone >= 0 and tone <= 8).override_failure_message(
|
||
"Randomize must produce skin_tone in range 0–8"
|
||
).is_true()
|
||
|
||
|
||
# =============================================================================
|
||
# Tab keyboard navigation (Task #8)
|
||
# =============================================================================
|
||
|
||
func test_tab_navigation_wraps() -> void:
|
||
if _scene == null:
|
||
return
|
||
var tc: TabContainer = _scene.get_node_or_null("Layout/TabPanel/TabContainer")
|
||
if tc == null:
|
||
return
|
||
tc.current_tab = 4 # last tab
|
||
# Simulate Tab key forward — wraps to 0
|
||
var event := InputEventKey.new()
|
||
event.keycode = KEY_TAB
|
||
event.pressed = true
|
||
event.shift_pressed = false
|
||
_scene._input(event)
|
||
assert_int(tc.current_tab).override_failure_message(
|
||
"Tab key on last tab must wrap to first tab"
|
||
).is_equal(0)
|
||
|
||
|
||
func test_shift_tab_navigates_backward() -> void:
|
||
if _scene == null:
|
||
return
|
||
var tc: TabContainer = _scene.get_node_or_null("Layout/TabPanel/TabContainer")
|
||
if tc == null:
|
||
return
|
||
tc.current_tab = 2
|
||
var event := InputEventKey.new()
|
||
event.keycode = KEY_TAB
|
||
event.pressed = true
|
||
event.shift_pressed = true
|
||
_scene._input(event)
|
||
assert_int(tc.current_tab).override_failure_message(
|
||
"Shift+Tab must navigate to previous tab"
|
||
).is_equal(1)
|
||
|
||
|
||
func test_esc_emits_cancelled() -> void:
|
||
if _scene == null:
|
||
return
|
||
var monitor := monitor_signals(_scene)
|
||
var event := InputEventKey.new()
|
||
event.keycode = KEY_ESCAPE
|
||
event.pressed = true
|
||
_scene._input(event)
|
||
assert_signal(monitor).is_emitted("creation_cancelled")
|
||
|
||
|
||
func test_enter_emits_confirmed() -> void:
|
||
if _scene == null:
|
||
return
|
||
var monitor := monitor_signals(_scene)
|
||
var event := InputEventKey.new()
|
||
event.keycode = KEY_ENTER
|
||
event.pressed = true
|
||
_scene._input(event)
|
||
assert_signal(monitor).is_emitted("creation_confirmed")
|
||
|
||
|
||
func test_q_key_rotates_left() -> void:
|
||
if _scene == null:
|
||
return
|
||
var initial := _scene._facing_idx
|
||
var event := InputEventKey.new()
|
||
event.keycode = KEY_Q
|
||
event.pressed = true
|
||
_scene._input(event)
|
||
assert_int(_scene._facing_idx).override_failure_message(
|
||
"Q key must rotate character (facing_idx change)"
|
||
).is_not_equal(initial)
|
||
|
||
|
||
func test_r_key_triggers_randomize() -> void:
|
||
if _scene == null:
|
||
return
|
||
# Seed to a known state so change is detectable
|
||
_scene._descriptor.body_type = CharacterVisualDescriptor.BodyType.AVERAGE_M
|
||
_scene._descriptor.skin_tone = 0
|
||
var body_before := _scene._descriptor.body_type
|
||
var skin_before := _scene._descriptor.skin_tone
|
||
# Press R up to 10 times — must change at least one observable descriptor field
|
||
var changed := false
|
||
for _i in 10:
|
||
var event := InputEventKey.new()
|
||
event.keycode = KEY_R
|
||
event.pressed = true
|
||
_scene._input(event)
|
||
if _scene._descriptor.body_type != body_before or _scene._descriptor.skin_tone != skin_before:
|
||
changed = true
|
||
break
|
||
assert_bool(changed).override_failure_message(
|
||
"R key must trigger randomize and change body_type or skin_tone"
|
||
).is_true()
|
||
|
||
|
||
# =============================================================================
|
||
# Color picker modal (Task #7, D-165)
|
||
# =============================================================================
|
||
|
||
func test_color_picker_modal_hidden_by_default() -> void:
|
||
if _scene == null:
|
||
return
|
||
var modal: Control = _scene.get_node_or_null("ColorPickerModal")
|
||
assert_bool(modal != null).override_failure_message("ColorPickerModal node must exist").is_true()
|
||
if modal == null:
|
||
return
|
||
assert_bool(not modal.visible).override_failure_message(
|
||
"ColorPickerModal must be hidden at startup"
|
||
).is_true()
|
||
|
||
|
||
func test_open_color_picker_shows_modal() -> void:
|
||
if _scene == null:
|
||
return
|
||
var modal: Control = _scene.get_node_or_null("ColorPickerModal")
|
||
if modal == null:
|
||
return
|
||
var received: Color = Color.WHITE
|
||
_scene._open_color_picker(Color.RED, null, func(c): received = c)
|
||
assert_bool(modal.visible).override_failure_message(
|
||
"_open_color_picker must make modal visible"
|
||
).is_true()
|
||
# Cancel to clean up
|
||
_scene._on_modal_cancel()
|
||
|
||
|
||
func test_modal_cancel_hides_modal() -> void:
|
||
if _scene == null:
|
||
return
|
||
var modal: Control = _scene.get_node_or_null("ColorPickerModal")
|
||
if modal == null:
|
||
return
|
||
_scene._open_color_picker(Color.BLUE, null, func(_c): pass)
|
||
_scene._on_modal_cancel()
|
||
assert_bool(not modal.visible).override_failure_message(
|
||
"Cancel must hide the modal"
|
||
).is_true()
|
||
|
||
|
||
func test_modal_cancel_reverts_to_original() -> void:
|
||
if _scene == null:
|
||
return
|
||
var received: Color = Color.WHITE
|
||
var original := Color.RED
|
||
_scene._open_color_picker(original, null, func(c): received = c)
|
||
# Simulate palette swatch press (changes color)
|
||
_scene._on_modal_palette_swatch_pressed(Color.GREEN, null)
|
||
# Cancel — must revert to original
|
||
_scene._on_modal_cancel()
|
||
assert_bool(received.is_equal_approx(original)).override_failure_message(
|
||
"Cancel must revert callback to original color"
|
||
).is_true()
|
||
|
||
|
||
func test_modal_ok_closes_modal() -> void:
|
||
if _scene == null:
|
||
return
|
||
var modal: Control = _scene.get_node_or_null("ColorPickerModal")
|
||
if modal == null:
|
||
return
|
||
_scene._open_color_picker(Color.WHITE, null, func(_c): pass)
|
||
_scene._on_modal_ok()
|
||
assert_bool(not modal.visible).override_failure_message(
|
||
"OK must hide the modal"
|
||
).is_true()
|
||
|
||
|
||
func test_modal_esc_key_cancels_when_open() -> void:
|
||
if _scene == null:
|
||
return
|
||
var modal: Control = _scene.get_node_or_null("ColorPickerModal")
|
||
if modal == null:
|
||
return
|
||
_scene._open_color_picker(Color.WHITE, null, func(_c): pass)
|
||
assert_bool(modal.visible).is_true()
|
||
var event := InputEventKey.new()
|
||
event.keycode = KEY_ESCAPE
|
||
event.pressed = true
|
||
_scene._input(event)
|
||
assert_bool(not modal.visible).override_failure_message(
|
||
"ESC must cancel and close the modal when open"
|
||
).is_true()
|
||
|
||
|
||
# =============================================================================
|
||
# Palette generation (D-165)
|
||
# =============================================================================
|
||
|
||
func test_palette_has_54_swatches() -> void:
|
||
if _scene == null:
|
||
return
|
||
assert_int(_scene._modal_swatch_btns.size()).override_failure_message(
|
||
"D-165 palette must have 54 swatches (6 rows × 9 cols)"
|
||
).is_equal(54)
|
||
|
||
|
||
func test_recent_colors_slots() -> void:
|
||
if _scene == null:
|
||
return
|
||
assert_int(_scene._modal_recent_btns.size()).override_failure_message(
|
||
"Recent colors must have 9 slots per D-165"
|
||
).is_equal(9)
|
||
|
||
|
||
# =============================================================================
|
||
# Search filter helper
|
||
# =============================================================================
|
||
|
||
func test_search_filter_hides_non_matching_buttons() -> void:
|
||
if _scene == null:
|
||
return
|
||
var grid := GridContainer.new()
|
||
add_child(grid)
|
||
|
||
var btn_a := Button.new()
|
||
btn_a.text = "Alpha"
|
||
grid.add_child(btn_a)
|
||
|
||
var btn_b := Button.new()
|
||
btn_b.text = "Beta"
|
||
grid.add_child(btn_b)
|
||
|
||
_scene._apply_search_filter(grid, "alp")
|
||
assert_bool(btn_a.visible).override_failure_message("'Alpha' should be visible for query 'alp'").is_true()
|
||
assert_bool(not btn_b.visible).override_failure_message("'Beta' should be hidden for query 'alp'").is_true()
|
||
|
||
_scene._apply_search_filter(grid, "")
|
||
assert_bool(btn_a.visible).override_failure_message("All buttons visible with empty query").is_true()
|
||
assert_bool(btn_b.visible).override_failure_message("All buttons visible with empty query").is_true()
|
||
|
||
grid.queue_free()
|