4b6817381b
Benchmark image — build + push to ECR (any adapter) / build + push (push) Waiting to run
CI / quality (ubuntu-latest) (push) Waiting to run
CI / test (tools-runtime) (push) Waiting to run
CI / test (e2e-general) (push) Waiting to run
CI / test (cli-runtime) (push) Waiting to run
CI / test (e2e-provider-and-openclaw) (push) Waiting to run
CI / test (integrations-and-misc) (push) Waiting to run
CI / coverage-report (push) Blocked by required conditions
CI / test-kubernetes (push) Waiting to run
CI / should-run-thorough (push) Waiting to run
CI / test-thorough (cloudwatch-demo) (push) Blocked by required conditions
CI / test-thorough (flink-ecs) (push) Blocked by required conditions
CI / test-thorough (upstream-lambda) (push) Blocked by required conditions
CI / test-thorough (prefect-ecs-fargate) (push) Blocked by required conditions
CodeQL / Analyze (python) (push) Waiting to run
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Blocked by required conditions
Release / publish-release (push) Blocked by required conditions
Release / publish-main-release (push) Blocked by required conditions
Release / prepare (push) Waiting to run
Release / verify (push) Blocked by required conditions
Release / build-python-dist (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Blocked by required conditions
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Blocked by required conditions
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Waiting to run
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Waiting to run
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
123 lines
3.9 KiB
Python
123 lines
3.9 KiB
Python
"""Tests for inline raw-terminal choice menu rendering."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import io
|
|
import re
|
|
import sys
|
|
from types import SimpleNamespace
|
|
|
|
from surfaces.interactive_shell.ui.components import choice_menu
|
|
|
|
_ANSI_RE = re.compile(r"\x1b\[[0-9;:]*[A-Za-z]")
|
|
|
|
|
|
def test_draw_menu_uses_carriage_return_newlines(monkeypatch) -> None:
|
|
"""Raw-mode terminals do not translate LF to CRLF for us.
|
|
|
|
Plain ``\n`` makes each line begin at the previous line's ending column,
|
|
which renders the picker as a diagonal staircase. The inline menu should
|
|
write explicit ``\r\n`` newlines and reset to column zero for every row.
|
|
"""
|
|
out = io.StringIO()
|
|
monkeypatch.setattr(sys, "stdout", out)
|
|
monkeypatch.setattr(choice_menu, "_cols", lambda: 80)
|
|
|
|
choice_menu._draw_menu(
|
|
title="integrations",
|
|
crumb="/integrations",
|
|
labels=["/integrations list", "/integrations verify"],
|
|
index=0,
|
|
erase_lines=0,
|
|
)
|
|
|
|
rendered = out.getvalue()
|
|
plain = _ANSI_RE.sub("", rendered)
|
|
assert "\n" in rendered
|
|
assert all(rendered[index - 1] == "\r" for index, char in enumerate(rendered) if char == "\n")
|
|
assert "\rintegrations" in plain
|
|
assert "\r/integrations" in plain
|
|
assert "\r > /integrations list" in plain
|
|
|
|
|
|
def test_erase_menu_block_resets_to_column_zero(monkeypatch) -> None:
|
|
out = io.StringIO()
|
|
monkeypatch.setattr(sys, "stdout", out)
|
|
|
|
choice_menu._erase_menu("crumb", ["one", "two"])
|
|
|
|
rendered = out.getvalue()
|
|
assert rendered.startswith("\r\x1b[")
|
|
assert "A\r\x1b[J" in rendered
|
|
assert rendered.endswith("\r")
|
|
|
|
|
|
def test_reset_tty_column_writes_carriage_return(monkeypatch) -> None:
|
|
out = io.StringIO()
|
|
monkeypatch.setattr(sys, "stdout", out)
|
|
|
|
choice_menu.reset_tty_column()
|
|
|
|
assert out.getvalue() == "\r"
|
|
|
|
|
|
def test_pick_ignores_unmapped_keys(monkeypatch) -> None:
|
|
out = io.StringIO()
|
|
actions = iter(["ignore", "enter"])
|
|
monkeypatch.setattr(sys, "stdout", out)
|
|
monkeypatch.setattr(choice_menu, "_cols", lambda: 80)
|
|
monkeypatch.setattr(choice_menu, "_read_action", lambda: next(actions))
|
|
|
|
assert choice_menu._pick(title="test", crumb="", labels=["one"]) == 0
|
|
|
|
rendered = out.getvalue()
|
|
assert rendered.count("test") == 2
|
|
assert "A\r\x1b[J" in rendered
|
|
|
|
|
|
def test_read_action_treats_space_as_enter(monkeypatch) -> None:
|
|
monkeypatch.setattr(choice_menu.os, "name", "nt")
|
|
monkeypatch.setitem(sys.modules, "msvcrt", SimpleNamespace(getch=lambda: b" "))
|
|
|
|
assert choice_menu._read_action() == "enter"
|
|
|
|
|
|
def test_read_action_treats_right_arrow_as_enter(monkeypatch) -> None:
|
|
keys = iter([b"\xe0", b"M"])
|
|
monkeypatch.setattr(choice_menu.os, "name", "nt")
|
|
monkeypatch.setitem(sys.modules, "msvcrt", SimpleNamespace(getch=lambda: next(keys)))
|
|
|
|
assert choice_menu._read_action() == "enter"
|
|
|
|
|
|
def test_repl_choose_one_starts_at_initial_value(monkeypatch) -> None:
|
|
out = io.StringIO()
|
|
actions = iter(["enter"])
|
|
monkeypatch.setattr(choice_menu, "repl_tty_interactive", lambda: True)
|
|
monkeypatch.setattr(
|
|
"surfaces.interactive_shell.ui.components.cpr_stdin.drain_stale_cpr_bytes",
|
|
lambda: None,
|
|
)
|
|
monkeypatch.setattr(sys, "stdout", out)
|
|
monkeypatch.setattr(choice_menu, "_cols", lambda: 80)
|
|
monkeypatch.setattr(choice_menu, "_read_action", lambda: next(actions))
|
|
|
|
result = choice_menu.repl_choose_one(
|
|
title="theme",
|
|
breadcrumb="/theme",
|
|
choices=[("green", "green"), ("blue", "blue (current)"), ("pink", "pink")],
|
|
initial_value="blue",
|
|
)
|
|
|
|
assert result == "blue"
|
|
plain = _ANSI_RE.sub("", out.getvalue())
|
|
assert "> blue (current)" in plain
|
|
|
|
|
|
def test_read_action_ignores_left_arrow(monkeypatch) -> None:
|
|
keys = iter([b"\xe0", b"K"])
|
|
monkeypatch.setattr(choice_menu.os, "name", "nt")
|
|
monkeypatch.setitem(sys.modules, "msvcrt", SimpleNamespace(getch=lambda: next(keys)))
|
|
|
|
assert choice_menu._read_action() == "ignore"
|