import time
from concurrent.futures import ThreadPoolExecutor
from typing import Optional
import pytest
from openhands_cli.user_actions.utils import get_session_prompter
from prompt_toolkit.formatted_text import HTML
from prompt_toolkit.input.defaults import create_pipe_input
from prompt_toolkit.output.defaults import DummyOutput
from tests.utils import _send_keys
def _run_prompt_and_type(
prompt_text: str,
keys: str,
*,
expect_exception: Optional[type[BaseException]] = None,
timeout: float = 2.0,
settle: float = 0.05,
) -> str | None:
"""
Helper to:
1) create a pipe + session,
2) start session.prompt in a background thread,
3) send keys, and
4) return the result or raise the expected exception.
Returns:
- The prompt result (str) if no exception expected.
- None if an exception is expected and raised.
"""
with create_pipe_input() as pipe:
session = get_session_prompter(input=pipe, output=DummyOutput())
with ThreadPoolExecutor(max_workers=1) as ex:
fut = ex.submit(session.prompt, HTML(prompt_text))
# Allow the prompt loop to start consuming input
time.sleep(settle)
_send_keys(pipe, keys)
if expect_exception:
with pytest.raises(expect_exception):
fut.result(timeout=timeout)
return None
return fut.result(timeout=timeout)
@pytest.mark.parametrize(
'desc,keys,expected',
[
('basic single line', 'hello world\r', 'hello world'),
('empty input', '\r', ''),
(
'single multiline via backslash-enter',
'line 1\\\rline 2\r',
'line 1\nline 2',
),
(
'multiple multiline segments',
'first line\\\rsecond line\\\rthird line\r',
'first line\nsecond line\nthird line',
),
(
'backslash-only newline then text',
'\\\rafter newline\r',
'\nafter newline',
),
(
'mixed content (code-like)',
"def function():\\\r return 'hello'\\\r # end of function\r",
"def function():\n return 'hello'\n # end of function",
),
(
'whitespace preservation (including blank line)',
' indented line\\\r\\\r more indented\r',
' indented line\n\n more indented',
),
(
'special characters',
'echo \'hello world\'\\\rgrep -n "pattern" file.txt\r',
'echo \'hello world\'\ngrep -n "pattern" file.txt',
),
],
)
def test_get_session_prompter_scenarios(desc, keys, expected):
"""Covers most behaviors via parametrization to reduce duplication."""
result = _run_prompt_and_type('> ', keys)
assert result == expected
def test_get_session_prompter_keyboard_interrupt():
"""Focused test for Ctrl+C behavior."""
_run_prompt_and_type('> ', '\x03', expect_exception=KeyboardInterrupt)
def test_get_session_prompter_default_parameters():
"""Lightweight sanity check for default construction."""
session = get_session_prompter()
assert session is not None
assert session.multiline is True
assert session.key_bindings is not None
assert session.completer is not None
# Prompt continuation should be callable and return the expected string
cont = session.prompt_continuation
assert callable(cont)
assert cont(80, 1, False) == '...'