111 lines
3.0 KiB
GDScript
111 lines
3.0 KiB
GDScript
extends SceneTree
|
|
## Headless focus tests for the dev console — verifies the command line keeps (or
|
|
## reclaims) keyboard focus after the actions that were observed to drop it.
|
|
## Run: godot --headless --script tests/console_focus_test.gd
|
|
##
|
|
## Exit code 0 = all passed, 1 = any failure.
|
|
|
|
var _passed: int = 0
|
|
var _failed: int = 0
|
|
|
|
|
|
func _init() -> void:
|
|
_run.call_deferred()
|
|
|
|
|
|
func _run() -> void:
|
|
print("=".repeat(60))
|
|
print("CONSOLE FOCUS TESTS")
|
|
print("=".repeat(60))
|
|
|
|
await test_focus_after_submit()
|
|
await test_focus_after_external_steal()
|
|
|
|
print("=".repeat(60))
|
|
print("Results: %d passed, %d failed" % [_passed, _failed])
|
|
print("=".repeat(60))
|
|
|
|
quit(1 if _failed > 0 else 0)
|
|
|
|
|
|
func _assert_true(condition: bool, desc: String) -> void:
|
|
if condition:
|
|
_passed += 1
|
|
print(" PASS: %s" % desc)
|
|
else:
|
|
_failed += 1
|
|
print(" FAIL: %s" % desc)
|
|
|
|
|
|
func _get_console() -> Node:
|
|
var c: Node = root.get_node_or_null("/root/Console")
|
|
if c == null:
|
|
_assert_true(false, "Console autoload should exist")
|
|
return c
|
|
|
|
|
|
# Drive the real path: open via _toggle (pauses the tree, slide tween grabs focus),
|
|
# type a command as real key events, submit with a real Enter, and confirm the guard
|
|
# keeps the command line focused across the paused submit.
|
|
func test_focus_after_submit() -> void:
|
|
print("\n-- test_focus_after_submit --")
|
|
var menu := _get_console()
|
|
if menu == null:
|
|
return
|
|
|
|
menu._toggle()
|
|
# Let the open slide tween finish and its grab_focus callback fire.
|
|
for _i in 20:
|
|
await process_frame
|
|
_assert_true(paused, "tree paused while console open")
|
|
_assert_true(menu._input_field.has_focus(), "input has focus after open")
|
|
|
|
menu._input_field.text = "clear"
|
|
menu._input_field.caret_column = 5
|
|
_press(KEY_ENTER)
|
|
# The submit and any focus theft settle over the next few idle frames.
|
|
for _i in 5:
|
|
await process_frame
|
|
_assert_true(menu._input_field.has_focus(), "input still has focus after submit")
|
|
|
|
menu._toggle()
|
|
for _i in 20:
|
|
await process_frame
|
|
|
|
|
|
# Push a key down+up through the viewport so LineEdit / _input handle it for real.
|
|
func _press(keycode: Key) -> void:
|
|
var down := InputEventKey.new()
|
|
down.keycode = keycode
|
|
down.physical_keycode = keycode
|
|
down.pressed = true
|
|
root.push_input(down)
|
|
var up := InputEventKey.new()
|
|
up.keycode = keycode
|
|
up.physical_keycode = keycode
|
|
up.pressed = false
|
|
root.push_input(up)
|
|
|
|
|
|
# Simulate anything yanking focus away mid-session (a stray click, a UI button); the
|
|
# guard in _process must pull it straight back while the console is open.
|
|
func test_focus_after_external_steal() -> void:
|
|
print("\n-- test_focus_after_external_steal --")
|
|
var menu := _get_console()
|
|
if menu == null:
|
|
return
|
|
|
|
menu._is_open = true
|
|
menu._panel.visible = true
|
|
menu._input_field.grab_focus()
|
|
await process_frame
|
|
|
|
menu._input_field.release_focus()
|
|
_assert_true(not menu._input_field.has_focus(), "focus released (precondition)")
|
|
|
|
await process_frame
|
|
_assert_true(menu._input_field.has_focus(), "guard reclaims focus next frame")
|
|
|
|
menu._is_open = false
|
|
menu._panel.visible = false
|