240 lines
7.7 KiB
Python
240 lines
7.7 KiB
Python
"""Tests for terminal search functionality."""
|
|
|
|
import pytest
|
|
|
|
from development_hub.terminal_display import TerminalDisplay
|
|
|
|
|
|
class TestTerminalSearch:
|
|
"""Tests for terminal display search."""
|
|
|
|
def test_search_empty_pattern(self, qtbot):
|
|
"""Empty search pattern returns no matches."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
count = display.search("")
|
|
assert count == 0
|
|
assert display.search_match_count == 0
|
|
|
|
def test_search_no_matches(self, qtbot):
|
|
"""Search with no matches returns 0."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
display.feed(b"Hello world\r\n")
|
|
count = display.search("xyz")
|
|
assert count == 0
|
|
assert display.current_match_number == 0
|
|
|
|
def test_search_single_match(self, qtbot):
|
|
"""Search finds a single match."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
display.feed(b"Hello world\r\n")
|
|
count = display.search("world")
|
|
assert count == 1
|
|
assert display.current_match_number == 1
|
|
|
|
def test_search_multiple_matches(self, qtbot):
|
|
"""Search finds multiple matches."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
display.feed(b"test one test two test three\r\n")
|
|
count = display.search("test")
|
|
assert count == 3
|
|
assert display.current_match_number == 1
|
|
|
|
def test_search_case_insensitive(self, qtbot):
|
|
"""Search is case-insensitive by default."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
display.feed(b"Hello HELLO hello\r\n")
|
|
count = display.search("hello")
|
|
assert count == 3
|
|
|
|
def test_search_case_sensitive(self, qtbot):
|
|
"""Case-sensitive search works."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
display.feed(b"Hello HELLO hello\r\n")
|
|
count = display.search("Hello", case_sensitive=True)
|
|
assert count == 1
|
|
|
|
def test_next_match_cycles(self, qtbot):
|
|
"""Next match cycles through matches."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
display.feed(b"a b a c a\r\n")
|
|
display.search("a")
|
|
assert display.current_match_number == 1
|
|
|
|
display.next_match()
|
|
assert display.current_match_number == 2
|
|
|
|
display.next_match()
|
|
assert display.current_match_number == 3
|
|
|
|
display.next_match() # Cycles back
|
|
assert display.current_match_number == 1
|
|
|
|
def test_prev_match_cycles(self, qtbot):
|
|
"""Previous match cycles through matches backwards."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
display.feed(b"a b a c a\r\n")
|
|
display.search("a")
|
|
assert display.current_match_number == 1
|
|
|
|
display.prev_match() # Cycles to end
|
|
assert display.current_match_number == 3
|
|
|
|
display.prev_match()
|
|
assert display.current_match_number == 2
|
|
|
|
def test_clear_search(self, qtbot):
|
|
"""Clear search removes matches."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
display.feed(b"test test test\r\n")
|
|
display.search("test")
|
|
assert display.search_match_count == 3
|
|
|
|
display.clear_search()
|
|
assert display.search_match_count == 0
|
|
assert display.current_match_number == 0
|
|
|
|
def test_next_match_no_matches(self, qtbot):
|
|
"""Next match returns False when no matches."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
result = display.next_match()
|
|
assert result is False
|
|
|
|
def test_prev_match_no_matches(self, qtbot):
|
|
"""Previous match returns False when no matches."""
|
|
display = TerminalDisplay(rows=10, cols=40)
|
|
qtbot.addWidget(display)
|
|
|
|
result = display.prev_match()
|
|
assert result is False
|
|
|
|
|
|
class TestProjectFilter:
|
|
"""Tests for project list filtering."""
|
|
|
|
def test_filter_by_title(self, qtbot, tmp_path):
|
|
"""Filter matches project titles."""
|
|
from pathlib import Path
|
|
from development_hub.project_list import ProjectListWidget
|
|
from unittest.mock import patch
|
|
from development_hub.project_discovery import Project
|
|
|
|
# Create temp directories so exists() works
|
|
p1 = tmp_path / "p1"
|
|
p2 = tmp_path / "p2"
|
|
p3 = tmp_path / "p3"
|
|
p1.mkdir()
|
|
p2.mkdir()
|
|
p3.mkdir()
|
|
|
|
mock_projects = [
|
|
Project(key="project-one", path=p1, title="Project One"),
|
|
Project(key="project-two", path=p2, title="Project Two"),
|
|
Project(key="other-thing", path=p3, title="Other Thing"),
|
|
]
|
|
|
|
with patch('development_hub.project_list.discover_projects', return_value=mock_projects):
|
|
widget = ProjectListWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
# Initially shows all
|
|
assert widget.list_widget.count() == 3
|
|
|
|
# Filter by "one"
|
|
widget.filter_input.setText("one")
|
|
assert widget.list_widget.count() == 1
|
|
assert widget.list_widget.item(0).text() == "Project One"
|
|
|
|
def test_filter_by_key(self, qtbot, tmp_path):
|
|
"""Filter matches project keys."""
|
|
from development_hub.project_list import ProjectListWidget
|
|
from unittest.mock import patch
|
|
from development_hub.project_discovery import Project
|
|
|
|
p1 = tmp_path / "p1"
|
|
p2 = tmp_path / "p2"
|
|
p1.mkdir()
|
|
p2.mkdir()
|
|
|
|
mock_projects = [
|
|
Project(key="dev-hub", path=p1, title="Development Hub"),
|
|
Project(key="cmd-forge", path=p2, title="CmdForge"),
|
|
]
|
|
|
|
with patch('development_hub.project_list.discover_projects', return_value=mock_projects):
|
|
widget = ProjectListWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
widget.filter_input.setText("hub")
|
|
assert widget.list_widget.count() == 1
|
|
assert widget.list_widget.item(0).text() == "Development Hub"
|
|
|
|
def test_filter_case_insensitive(self, qtbot, tmp_path):
|
|
"""Filter is case-insensitive."""
|
|
from development_hub.project_list import ProjectListWidget
|
|
from unittest.mock import patch
|
|
from development_hub.project_discovery import Project
|
|
|
|
p1 = tmp_path / "p1"
|
|
p1.mkdir()
|
|
|
|
mock_projects = [
|
|
Project(key="test", path=p1, title="Test Project"),
|
|
]
|
|
|
|
with patch('development_hub.project_list.discover_projects', return_value=mock_projects):
|
|
widget = ProjectListWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
widget.filter_input.setText("TEST")
|
|
assert widget.list_widget.count() == 1
|
|
|
|
def test_filter_clears(self, qtbot, tmp_path):
|
|
"""Clearing filter shows all projects."""
|
|
from development_hub.project_list import ProjectListWidget
|
|
from unittest.mock import patch
|
|
from development_hub.project_discovery import Project
|
|
|
|
p1 = tmp_path / "a"
|
|
p2 = tmp_path / "b"
|
|
p1.mkdir()
|
|
p2.mkdir()
|
|
|
|
mock_projects = [
|
|
Project(key="a", path=p1, title="Alpha"),
|
|
Project(key="b", path=p2, title="Beta"),
|
|
]
|
|
|
|
with patch('development_hub.project_list.discover_projects', return_value=mock_projects):
|
|
widget = ProjectListWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
widget.filter_input.setText("alpha")
|
|
assert widget.list_widget.count() == 1
|
|
|
|
widget.filter_input.clear()
|
|
assert widget.list_widget.count() == 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pytest.main([__file__, "-v"])
|