extends Node # Панель ника и таблица лидеров «по существу» (без сети и окна): настоящая карта Race, игрок, Overlay, события клавиш. # godot --headless --path <проект> res://tools/test/nick_ui_test.tscn -- --client --profile=/tmp/ui_profile.cfg # Проверяет: игрок двигается; после N панель открыта, а «вперёд» не двигает; R/N/Tab/G в поле — не действия (R не меняет готовность, # Tab не открывает таблицу), буквы попадают в поле, открывающая клавиша — нет; кириллица и вставка; предел 16 символов; # Enter сохраняет ник в профиль; после закрытия игрок снова двигается; Esc при первом запуске — пропуск без ника и «больше не спрашивать»; # Tab открывает/закрывает таблицу, игрок при этом не заблокирован. Выводит «ДА/НЕТ», в конце «UI ПРОЙДЕН / ПРОВАЛЕН». var ok := true var level var p var rules func check(cond: bool, text: String) -> void: print((" ДА " if cond else " НЕТ ") + text) if not cond: ok = false func key(code: int, pressed: bool, unicode := 0) -> void: var ev := InputEventKey.new() ev.physical_keycode = code as Key ev.keycode = code as Key ev.unicode = unicode ev.pressed = pressed Input.parse_input_event(ev) func tap(code: int, unicode := 0) -> void: key(code, true, unicode) await get_tree().process_frame key(code, false, unicode) await get_tree().process_frame func hold_forward(sec: float) -> float: var start: Vector3 = p.global_position Input.action_press("move_forward") await get_tree().create_timer(sec).timeout Input.action_release("move_forward") await get_tree().create_timer(0.2).timeout var d: Vector3 = p.global_position - start return Vector2(d.x, d.z).length() func _ready() -> void: _run.call_deferred() func _run() -> void: level = (load("res://maps/race/race.tscn") as PackedScene).instantiate() add_child(level) await get_tree().create_timer(1.0).timeout p = level.get_node("Player") rules = level.get_node("Rules") check(Net.nick_asked == false and Net.my_nick == "", "свежий профиль: ник не задан, не спрашивали") check(Net.my_client_id.length() == 32, "создан client_id из 32 hex-символов (128 бит)") var mouse_works := Input.mouse_mode == Input.MOUSE_MODE_CAPTURED var moved := await hold_forward(0.6) check(moved > 1.0, "без панели игрок бежит вперёд (%.2f м)" % moved) print("-- открываем панель клавишей N") await tap(KEY_N, 110) await get_tree().create_timer(0.2).timeout check(Overlay.nick_open and Net.input_blocked, "N открыла панель и поставила флаг блокировки") check(not p.input_frozen, "флаг гонки input_frozen не тронут") check(Overlay._edit.has_focus() and Overlay._edit.text == "", "поле в фокусе, открывающая клавиша «n» в поле не попала (текст «%s»)" % Overlay._edit.text) moved = await hold_forward(0.7) check(moved < 0.05, "при открытой панели «вперёд» не двигает игрока (%.3f м)" % moved) var was_ready: bool = rules.flags_ready.get(rules.my_id, false) await tap(KEY_R, 114) await tap(KEY_N, 110) await tap(KEY_TAB) await tap(KEY_G, 103) check(rules.flags_ready.get(rules.my_id, false) == was_ready, "R в поле не переключает «готов» в гонке") check(Overlay.nick_open and not Overlay.lb_open, "N и Tab в поле не закрыли панель и не открыли таблицу") check(Overlay._edit.text == "rng", "R, N, G попали в поле как буквы, Tab — нет (текст «%s»)" % Overlay._edit.text) Overlay._edit.text = "" Overlay._edit.insert_text_at_caret("Привет, [b]мир[/b] и ещё длинный хвост") # как вставка из буфера check(Overlay._edit.text.length() <= 16 and Overlay._edit.text.begins_with("Привет"), "вставка с кириллицей обрезана до 16 символов: «%s»" % Overlay._edit.text) Overlay._edit.text = "" await tap(KEY_B, 1080) # «и» с unicode — как набор на русской раскладке await tap(KEY_K, 1082) await tap(KEY_SPACE, 32) await tap(KEY_A, 1103) check(Overlay._edit.text == "ик я", "набор кириллицы клавишами: «%s»" % Overlay._edit.text) Overlay._edit.text = " Мир [b]x " await tap(KEY_ENTER) await get_tree().create_timer(0.2).timeout check(not Overlay.nick_open and not Net.input_blocked, "Enter закрыл панель, блокировка снята") check(Net.my_nick == "Мир [b]x" and Net.nick_of(Net.own_id()) == "Мир [b]x", "ник сохранён и обрезан по краям: «%s»" % Net.my_nick) var cf := ConfigFile.new() cf.load("user://profile.cfg" if not _prof().begins_with("--") else _prof().substr(10)) check(cf.get_value("profile", "nick", "") == "Мир [b]x" and cf.get_value("profile", "asked", false) == true and str(cf.get_value("profile", "client_id", "")).length() == 32, "профиль записан в файл (ник, asked, client_id)") if mouse_works: check(Input.mouse_mode == Input.MOUSE_MODE_CAPTURED, "после закрытия мышь снова захвачена") else: print(" -- режим мыши в headless не хранится: проверка захвата мыши пропущена (смотреть руками)") moved = await hold_forward(0.6) check(moved > 1.0, "после закрытия игрок снова бежит (%.2f м)" % moved) print("-- смена ника: в поле стоит текущий") await tap(KEY_N, 110) await get_tree().create_timer(0.2).timeout check(Overlay.nick_open and Overlay._edit.text == "Мир [b]x" and Overlay._nick_title.text == "Смена ника", "N снова открывает панель, ник в поле, заголовок «Смена ника»") await tap(KEY_ESCAPE) await get_tree().create_timer(0.2).timeout check(not Overlay.nick_open and Net.my_nick == "Мир [b]x", "Esc при смене — отмена, ник прежний") print("-- первый запуск, Esc: пропуск") Net.my_nick = "" Net.nick_asked = false Overlay.open_nick(true) await get_tree().create_timer(0.2).timeout await tap(KEY_ESCAPE) await get_tree().create_timer(0.2).timeout check(not Overlay.nick_open and Net.nick_asked and Net.my_nick == "" and Net.nick_of(Net.own_id()).begins_with("Игрок "), "Esc: панель закрыта, больше не спрашиваем, ник «%s»" % Net.nick_of(Net.own_id())) print("-- таблица лидеров (Tab)") await tap(KEY_TAB) check(Overlay.lb_open and not Net.input_blocked, "Tab открывает таблицу, игрок не заблокирован") moved = await hold_forward(0.5) check(moved > 0.8, "с таблицей игрок двигается (%.2f м)" % moved) await tap(KEY_TAB) check(not Overlay.lb_open, "повторный Tab закрывает") await tap(KEY_TAB) await tap(KEY_ESCAPE) check(not Overlay.lb_open, "Esc закрывает таблицу") print("\nUI ПРОЙДЕН" if ok else "\nUI ПРОВАЛЕН") get_tree().quit(0 if ok else 1) func _prof() -> String: for a in OS.get_cmdline_user_args(): if a.begins_with("--profile="): return a return "user://profile.cfg"