v2.18.3 (#861)
Co-authored-by: Clément Drouin <clement.drouin@mistral.ai> Co-authored-by: Laure Hugo <201583486+laure0303@users.noreply.github.com> Co-authored-by: Paul VEZIA <166131032+le-codeur-rapide@users.noreply.github.com> Co-authored-by: Peter Evers <peter.evers@mistral.ai> Co-authored-by: Jules YZERD <newtonlormont@gmail.com> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
d50704e694
commit
4e495f658d
130 changed files with 1042 additions and 552 deletions
|
|
@ -4,9 +4,11 @@ from typing import Any, cast
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from textual.content import Content
|
||||
from textual.widgets import OptionList
|
||||
|
||||
from tests.stubs.fake_connector_registry import FakeConnectorRegistry
|
||||
from vibe.cli.textual_ui.shortcut_hints import SHORTCUT_STYLE
|
||||
from vibe.cli.textual_ui.widgets.connector_auth_app import (
|
||||
ConnectorAuthApp,
|
||||
_AuthOptionId,
|
||||
|
|
@ -167,13 +169,15 @@ class TestAuthActions:
|
|||
app._toggle_url()
|
||||
assert app._auth_url_visible is True
|
||||
text = detail.update.call_args.args[0]
|
||||
assert "https://auth.example.com/oauth" in text
|
||||
assert "Once authenticated" in text
|
||||
assert isinstance(text, Content)
|
||||
assert "https://auth.example.com/oauth" in text.plain
|
||||
assert "Once authenticated" in text.plain
|
||||
|
||||
app._toggle_url()
|
||||
assert app._auth_url_visible is False
|
||||
text = detail.update.call_args.args[0]
|
||||
assert "https://auth.example.com/oauth" not in text
|
||||
assert isinstance(text, Content)
|
||||
assert "https://auth.example.com/oauth" not in text.plain
|
||||
|
||||
def test_toggle_url_noop_without_url(self) -> None:
|
||||
app = _make_app()
|
||||
|
|
@ -193,8 +197,10 @@ class TestDetailText:
|
|||
app._update_detail_text()
|
||||
|
||||
text = detail.update.call_args.args[0]
|
||||
assert "Once authenticated, press R to refresh" in text
|
||||
assert "https://auth.example.com" not in text
|
||||
assert isinstance(text, Content)
|
||||
assert "Once authenticated, press r to refresh" in text.plain
|
||||
assert "https://auth.example.com" not in text.plain
|
||||
assert any(span.style == SHORTCUT_STYLE for span in text.spans)
|
||||
|
||||
def test_with_url_visible(self) -> None:
|
||||
app = cast(Any, _make_app())
|
||||
|
|
@ -206,11 +212,13 @@ class TestDetailText:
|
|||
app._update_detail_text()
|
||||
|
||||
text = detail.update.call_args.args[0]
|
||||
assert "https://auth.example.com/oauth" in text
|
||||
assert "Once authenticated, press R to refresh" in text
|
||||
assert isinstance(text, Content)
|
||||
assert "https://auth.example.com/oauth" in text.plain
|
||||
assert "Once authenticated, press r to refresh" in text.plain
|
||||
assert any(span.style == SHORTCUT_STYLE for span in text.spans)
|
||||
# URL should come before the footer
|
||||
url_pos = text.index("https://auth.example.com/oauth")
|
||||
footer_pos = text.index("Once authenticated")
|
||||
url_pos = text.plain.index("https://auth.example.com/oauth")
|
||||
footer_pos = text.plain.index("Once authenticated")
|
||||
assert url_pos < footer_pos
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,10 +5,12 @@ from typing import Any
|
|||
from unittest.mock import AsyncMock, MagicMock
|
||||
|
||||
import pytest
|
||||
from textual.content import Content
|
||||
from textual.widgets.option_list import Option
|
||||
|
||||
from tests.stubs.fake_connector_registry import FakeConnectorRegistry
|
||||
from tests.stubs.fake_mcp_registry import FakeMCPRegistry
|
||||
from vibe.cli.textual_ui.shortcut_hints import SHORTCUT_STYLE
|
||||
from vibe.cli.textual_ui.widgets.mcp_app import (
|
||||
_LIST_VIEW_HELP_AUTH,
|
||||
_LIST_VIEW_HELP_TOOLS,
|
||||
|
|
@ -431,9 +433,10 @@ class TestConnectorAuthRequested:
|
|||
|
||||
app.post_message.assert_not_called()
|
||||
option_list.add_option.assert_called_once()
|
||||
assert "press R to refresh" in str(
|
||||
option_list.add_option.call_args.args[0].prompt
|
||||
)
|
||||
prompt = option_list.add_option.call_args.args[0].prompt
|
||||
assert isinstance(prompt, Content)
|
||||
assert "press r to refresh" in prompt.plain
|
||||
assert any(span.style == SHORTCUT_STYLE for span in prompt.spans)
|
||||
|
||||
def test_connected_connector_with_no_indexed_tools_shows_message(self) -> None:
|
||||
registry = MagicMock()
|
||||
|
|
|
|||
|
|
@ -72,6 +72,31 @@ class TestQuestionAppState:
|
|||
assert len(app.answers) == 0
|
||||
assert len(app.other_texts) == 0
|
||||
|
||||
def test_more_than_four_options_accepted_and_rendered(self):
|
||||
from vibe.cli.textual_ui.widgets.question_app import QuestionApp
|
||||
|
||||
args = AskUserQuestionArgs(
|
||||
questions=[
|
||||
Question(
|
||||
question="Pick an analysis type",
|
||||
header="Analysis",
|
||||
options=[
|
||||
Choice(label="Financial"),
|
||||
Choice(label="Operational"),
|
||||
Choice(label="Strategic"),
|
||||
Choice(label="Competitive"),
|
||||
Choice(label="Multi-source"),
|
||||
],
|
||||
)
|
||||
]
|
||||
)
|
||||
|
||||
app = QuestionApp(args)
|
||||
|
||||
assert app.max_options == 5
|
||||
# 5 options + Other = 6 (no Submit for single-select)
|
||||
assert app._total_options == 6
|
||||
|
||||
def test_total_options_single_select(self, single_question_args):
|
||||
from vibe.cli.textual_ui.widgets.question_app import QuestionApp
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ async def _wait_for_error_message_containing(
|
|||
deadline = time.monotonic() + timeout
|
||||
while time.monotonic() < deadline:
|
||||
for error in vibe_app.query(ErrorMessage):
|
||||
if text in error._error:
|
||||
if text in str(error._error):
|
||||
return error
|
||||
await pilot.pause(0.05)
|
||||
raise TimeoutError(
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ from unittest.mock import AsyncMock, MagicMock, patch
|
|||
import pytest
|
||||
from textual.app import WINDOWS
|
||||
|
||||
from tests.conftest import build_test_vibe_app
|
||||
from tests.conftest import build_test_vibe_app, build_test_vibe_config
|
||||
from vibe.cli.textual_ui.app import VibeApp, _run_app_with_cleanup
|
||||
from vibe.cli.textual_ui.quit_manager import QUIT_CONFIRM_DELAY, QuitManager
|
||||
|
||||
|
|
@ -187,6 +187,19 @@ class TestActionDeleteRightOrQuit:
|
|||
"Ctrl+D", "1 queued message will be discarded"
|
||||
)
|
||||
|
||||
def test_quits_immediately_when_confirmation_disabled(self) -> None:
|
||||
app = build_test_vibe_app(
|
||||
config=build_test_vibe_config(ask_confirmation_on_exit=False)
|
||||
)
|
||||
with (
|
||||
patch.object(app, "_get_chat_input", return_value=None),
|
||||
patch.object(app, "_force_quit") as mock_quit,
|
||||
patch.object(app._quit_manager, "request_confirmation") as mock_confirm,
|
||||
):
|
||||
app.action_delete_right_or_quit()
|
||||
mock_quit.assert_called_once()
|
||||
mock_confirm.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_shutdown_cleanup_cancels_in_flight_tasks(app: VibeApp) -> None:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import pytest
|
|||
from rich.text import Text
|
||||
from textual.widgets import OptionList
|
||||
|
||||
from vibe.cli.textual_ui.shortcut_hints import SHORTCUT_STYLE
|
||||
from vibe.cli.textual_ui.widgets.session_picker import (
|
||||
SessionPickerApp,
|
||||
_format_relative_time,
|
||||
|
|
@ -170,7 +171,7 @@ class TestSessionPickerAppBindings:
|
|||
|
||||
def test_has_delete_binding(self) -> None:
|
||||
assert "d" in self._get_binding_keys()
|
||||
assert "D" in self._get_binding_keys()
|
||||
assert "D" not in self._get_binding_keys()
|
||||
|
||||
|
||||
class TestSessionPickerSessionRemoval:
|
||||
|
|
@ -192,9 +193,9 @@ class TestSessionPickerSessionRemoval:
|
|||
|
||||
assert_delete_state(picker, kind="confirmation", option_id="session-a")
|
||||
assert option_list.replaced_prompts[-1].option_id == "session-a"
|
||||
assert (
|
||||
"Press D again to delete" in option_list.replaced_prompts[-1].prompt.plain
|
||||
)
|
||||
prompt = option_list.replaced_prompts[-1].prompt
|
||||
assert "Press d again to delete" in prompt.plain
|
||||
assert any(span.style == SHORTCUT_STYLE for span in prompt.spans)
|
||||
assert posted_messages == []
|
||||
|
||||
def test_second_delete_request_posts_delete_message(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue