v2.2.1 (#403)
Co-authored-by: Quentin Torroba <quentin.torroba@mistral.ai> Co-authored-by: Vincent Guilloux <vincent.guilloux@mistral.ai> Co-authored-by: Thomas Kenbeek <thomas.kenbeek@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
parent
ec7f3b25ea
commit
a560a47ce8
52 changed files with 1026 additions and 562 deletions
|
|
@ -8,7 +8,15 @@ from unittest.mock import MagicMock, mock_open, patch
|
|||
import pytest
|
||||
from textual.app import App
|
||||
|
||||
from vibe.cli.clipboard import _copy_osc52, copy_selection_to_clipboard
|
||||
from vibe.cli.clipboard import (
|
||||
_copy_osc52,
|
||||
_copy_pbcopy,
|
||||
_copy_to_clipboard,
|
||||
_copy_wl_copy,
|
||||
_copy_xclip,
|
||||
_read_clipboard,
|
||||
copy_selection_to_clipboard,
|
||||
)
|
||||
|
||||
|
||||
class MockWidget:
|
||||
|
|
@ -79,9 +87,9 @@ def test_copy_selection_to_clipboard_no_notification(
|
|||
mock_app.notify.assert_not_called()
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard._copy_osc52")
|
||||
@patch("vibe.cli.clipboard._copy_to_clipboard")
|
||||
def test_copy_selection_to_clipboard_success(
|
||||
mock_copy_osc52: MagicMock, mock_app: MagicMock
|
||||
mock_copy_to_clipboard: MagicMock, mock_app: MagicMock
|
||||
) -> None:
|
||||
widget = MockWidget(
|
||||
text_selection=SimpleNamespace(), get_selection_result=("selected text", None)
|
||||
|
|
@ -91,7 +99,7 @@ def test_copy_selection_to_clipboard_success(
|
|||
result = copy_selection_to_clipboard(mock_app)
|
||||
|
||||
assert result == "selected text"
|
||||
mock_copy_osc52.assert_called_once_with("selected text")
|
||||
mock_copy_to_clipboard.assert_called_once_with("selected text")
|
||||
mock_app.notify.assert_called_once_with(
|
||||
'"selected text" copied to clipboard',
|
||||
severity="information",
|
||||
|
|
@ -100,21 +108,21 @@ def test_copy_selection_to_clipboard_success(
|
|||
)
|
||||
|
||||
|
||||
@patch("vibe.cli.clipboard._copy_osc52")
|
||||
def test_copy_selection_to_clipboard_failure(
|
||||
mock_copy_osc52: MagicMock, mock_app: MagicMock
|
||||
@patch("vibe.cli.clipboard._copy_to_clipboard")
|
||||
def test_copy_selection_to_clipboard_shows_failure_when_all_strategies_raise(
|
||||
mock_copy_to_clipboard: MagicMock, mock_app: MagicMock
|
||||
) -> None:
|
||||
"""When _copy_to_clipboard raises (all strategies failed), user sees 'Failed to copy' toast."""
|
||||
widget = MockWidget(
|
||||
text_selection=SimpleNamespace(), get_selection_result=("selected text", None)
|
||||
)
|
||||
mock_app.query.return_value = [widget]
|
||||
|
||||
mock_copy_osc52.side_effect = Exception("OSC52 failed")
|
||||
mock_copy_to_clipboard.side_effect = RuntimeError("All clipboard strategies failed")
|
||||
|
||||
result = copy_selection_to_clipboard(mock_app)
|
||||
|
||||
assert result is None
|
||||
mock_copy_osc52.assert_called_once_with("selected text")
|
||||
mock_copy_to_clipboard.assert_called_once_with("selected text")
|
||||
mock_app.notify.assert_called_once_with(
|
||||
"Failed to copy - clipboard not available", severity="warning", timeout=3
|
||||
)
|
||||
|
|
@ -131,11 +139,13 @@ def test_copy_selection_to_clipboard_multiple_widgets(mock_app: MagicMock) -> No
|
|||
widget3 = MockWidget(text_selection=None)
|
||||
mock_app.query.return_value = [widget1, widget2, widget3]
|
||||
|
||||
with patch("vibe.cli.clipboard._copy_osc52") as mock_copy_osc52:
|
||||
with patch("vibe.cli.clipboard._copy_to_clipboard") as mock_copy_to_clipboard:
|
||||
result = copy_selection_to_clipboard(mock_app)
|
||||
|
||||
assert result == "first selection\nsecond selection"
|
||||
mock_copy_osc52.assert_called_once_with("first selection\nsecond selection")
|
||||
mock_copy_to_clipboard.assert_called_once_with(
|
||||
"first selection\nsecond selection"
|
||||
)
|
||||
mock_app.notify.assert_called_once_with(
|
||||
'"first selection\u23cesecond selection" copied to clipboard',
|
||||
severity="information",
|
||||
|
|
@ -151,11 +161,11 @@ def test_copy_selection_to_clipboard_preview_shortening(mock_app: MagicMock) ->
|
|||
)
|
||||
mock_app.query.return_value = [widget]
|
||||
|
||||
with patch("vibe.cli.clipboard._copy_osc52") as mock_copy_osc52:
|
||||
with patch("vibe.cli.clipboard._copy_to_clipboard") as mock_copy_to_clipboard:
|
||||
result = copy_selection_to_clipboard(mock_app)
|
||||
|
||||
assert result == long_text
|
||||
mock_copy_osc52.assert_called_once_with(long_text)
|
||||
|
||||
mock_copy_to_clipboard.assert_called_once_with(long_text)
|
||||
notification_call = mock_app.notify.call_args
|
||||
assert notification_call is not None
|
||||
assert '"' in notification_call[0][0]
|
||||
|
|
@ -163,6 +173,111 @@ def test_copy_selection_to_clipboard_preview_shortening(mock_app: MagicMock) ->
|
|||
assert len(notification_call[0][0]) < len(long_text) + 30
|
||||
|
||||
|
||||
def test_copy_to_clipboard_stops_after_verified_copy() -> None:
|
||||
"""Stops iterating once _read_clipboard confirms the text landed."""
|
||||
mock_first = MagicMock()
|
||||
mock_second = MagicMock()
|
||||
|
||||
with (
|
||||
patch("vibe.cli.clipboard._COPY_METHODS", [mock_first, mock_second]),
|
||||
patch("vibe.cli.clipboard._read_clipboard", return_value="hello"),
|
||||
):
|
||||
_copy_to_clipboard("hello")
|
||||
|
||||
mock_first.assert_called_once_with("hello")
|
||||
mock_second.assert_not_called()
|
||||
|
||||
|
||||
def test_copy_to_clipboard_tries_all_when_verify_fails() -> None:
|
||||
"""Tries all strategies when _read_clipboard never confirms."""
|
||||
mock_first = MagicMock()
|
||||
mock_second = MagicMock()
|
||||
|
||||
with (
|
||||
patch("vibe.cli.clipboard._COPY_METHODS", [mock_first, mock_second]),
|
||||
patch("vibe.cli.clipboard._read_clipboard", return_value=None),
|
||||
):
|
||||
_copy_to_clipboard("hello")
|
||||
|
||||
mock_first.assert_called_once_with("hello")
|
||||
mock_second.assert_called_once_with("hello")
|
||||
|
||||
|
||||
def test_copy_to_clipboard_raises_when_all_strategies_raise() -> None:
|
||||
"""RuntimeError is raised when every strategy fails."""
|
||||
mock_osc52 = MagicMock(side_effect=OSError("no tty"))
|
||||
mock_pyperclip = MagicMock(side_effect=RuntimeError("pyperclip unavailable"))
|
||||
|
||||
with (
|
||||
patch("vibe.cli.clipboard._COPY_METHODS", [mock_osc52, mock_pyperclip]),
|
||||
pytest.raises(RuntimeError, match="All clipboard strategies failed"),
|
||||
):
|
||||
_copy_to_clipboard("anything")
|
||||
|
||||
|
||||
def test_read_clipboard_returns_first_successful_reader() -> None:
|
||||
mock_reader = MagicMock(return_value="hello")
|
||||
mock_reader2 = MagicMock(side_effect=RuntimeError("no clipboard"))
|
||||
with patch(
|
||||
"vibe.cli.clipboard._READ_CLIPBOARD_METHODS", [mock_reader, mock_reader2]
|
||||
):
|
||||
assert _read_clipboard() == "hello"
|
||||
mock_reader.assert_called_once()
|
||||
mock_reader2.assert_not_called()
|
||||
|
||||
|
||||
def test_read_clipboard_falls_through_on_failure() -> None:
|
||||
failing = MagicMock(side_effect=RuntimeError("no clipboard"))
|
||||
with patch("vibe.cli.clipboard._READ_CLIPBOARD_METHODS", [failing]):
|
||||
assert _read_clipboard() is None
|
||||
|
||||
|
||||
def test_read_clipboard_skips_failing_reader() -> None:
|
||||
failing = MagicMock(side_effect=RuntimeError("broken"))
|
||||
working = MagicMock(return_value="hello")
|
||||
with patch("vibe.cli.clipboard._READ_CLIPBOARD_METHODS", [failing, working]):
|
||||
assert _read_clipboard() == "hello"
|
||||
working.assert_called_once()
|
||||
|
||||
|
||||
@patch("subprocess.run")
|
||||
def test_copy_pbcopy(mock_run: MagicMock) -> None:
|
||||
_copy_pbcopy("hello")
|
||||
mock_run.assert_called_once_with(["pbcopy"], input=b"hello", check=True)
|
||||
|
||||
|
||||
@patch("subprocess.run")
|
||||
def test_copy_xclip(mock_run: MagicMock) -> None:
|
||||
_copy_xclip("hello")
|
||||
mock_run.assert_called_once_with(
|
||||
["xclip", "-selection", "clipboard"], input=b"hello", check=True
|
||||
)
|
||||
|
||||
|
||||
@patch("subprocess.run")
|
||||
def test_copy_wl_copy(mock_run: MagicMock) -> None:
|
||||
_copy_wl_copy("hello")
|
||||
mock_run.assert_called_once_with(["wl-copy"], input=b"hello", check=True)
|
||||
|
||||
|
||||
def test_copy_methods_includes_available_commands() -> None:
|
||||
"""_COPY_METHODS is built at import time using _has_cmd; re-import with mocked shutil.which."""
|
||||
import importlib
|
||||
|
||||
import vibe.cli.clipboard as mod
|
||||
|
||||
with patch(
|
||||
"shutil.which",
|
||||
side_effect=lambda cmd: "/usr/bin/xclip" if cmd == "xclip" else None,
|
||||
):
|
||||
importlib.reload(mod)
|
||||
assert mod._copy_xclip in mod._COPY_METHODS
|
||||
assert mod._copy_pbcopy not in mod._COPY_METHODS
|
||||
assert mod._copy_wl_copy not in mod._COPY_METHODS
|
||||
|
||||
importlib.reload(mod)
|
||||
|
||||
|
||||
@patch("builtins.open", new_callable=mock_open)
|
||||
def test_copy_osc52_writes_correct_sequence(
|
||||
mock_file: MagicMock, monkeypatch: pytest.MonkeyPatch
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue