6593ac9b5b
Typing `/model` and pressing Enter did nothing in the TUI; the picker only opened via `/model --save` or `/model <name>`. The completion popup matched both `/model` and `/model-fallback` by prefix, so the exact-match-hide guard (which required a single match) never fired. With the popup still visible, the TUI's Enter handler completed the text instead of submitting the command, so it never executed. Treat the typed prefix as an exact match whenever it equals any matched command name, not only when it is the sole match. This hides the popup on a complete command name so Enter submits it, even when a longer command shares the prefix.
87 lines
3.5 KiB
Python
87 lines
3.5 KiB
Python
"""Tests for CLI SlashCommandCompleter (prompt_toolkit adapter)."""
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
from EvoScientist.cli.interactive import SlashCommandCompleter
|
|
|
|
|
|
def _doc(text: str):
|
|
"""Create a minimal prompt_toolkit Document stub."""
|
|
doc = MagicMock()
|
|
doc.text_before_cursor = text
|
|
return doc
|
|
|
|
|
|
class TestSlashCommandCompleter:
|
|
"""Verify that ``SlashCommandCompleter.get_completions`` correctly
|
|
delegates to the shared ``compute_completions`` engine and translates
|
|
candidates into prompt_toolkit ``Completion`` objects.
|
|
"""
|
|
|
|
def test_top_level_slash_shows_commands(self):
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/he"), None))
|
|
texts = {c.text for c in completions}
|
|
assert "/help" in texts
|
|
|
|
def test_exact_command_no_space_hides(self):
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/help"), None))
|
|
assert completions == []
|
|
|
|
def test_exact_command_hides_even_when_prefix_of_another(self):
|
|
"""Regression for #293: ``/model`` must hide on exact match so Enter
|
|
submits it, even though ``/model-fallback`` shares the prefix. Before
|
|
the fix the popup stayed visible (two prefix matches) and the TUI's
|
|
Enter handler completed the text instead of executing the command.
|
|
"""
|
|
completer = SlashCommandCompleter()
|
|
# Sanity: both commands share the prefix, so a partial prefix lists both.
|
|
partial = {c.text for c in completer.get_completions(_doc("/mode"), None)}
|
|
assert {"/model", "/model-fallback"} <= partial
|
|
# Exact ``/model`` with no trailing space → hide.
|
|
completions = list(completer.get_completions(_doc("/model"), None))
|
|
assert completions == []
|
|
|
|
def test_non_slash_returns_empty(self):
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("hello"), None))
|
|
assert completions == []
|
|
|
|
def test_trailing_space_shows_subcommands(self):
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/mcp "), None))
|
|
texts = {c.text for c in completions}
|
|
assert "list" in texts
|
|
assert "add" in texts
|
|
|
|
def test_subcommand_prefix_filters(self):
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/mcp lis"), None))
|
|
texts = {c.text for c in completions}
|
|
assert texts == {"list"}
|
|
|
|
def test_exact_subcommand_hides(self):
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/mcp list"), None))
|
|
assert completions == []
|
|
|
|
def test_results_sorted_alphabetically(self):
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/"), None))
|
|
texts = [c.text for c in completions]
|
|
assert texts == sorted(texts)
|
|
|
|
def test_display_meta_is_description(self):
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/he"), None))
|
|
for c in completions:
|
|
if c.text == "/help":
|
|
assert c.display_meta is not None
|
|
|
|
def test_subcommand_completions_sorted(self):
|
|
completer = SlashCommandCompleter()
|
|
completions = list(completer.get_completions(_doc("/mcp "), None))
|
|
texts = [c.text for c in completions]
|
|
assert texts == sorted(texts)
|