youtube-cli/tests/conftest.py
Jarian Cottingham 06877fb2fc feat: add TUI (Textual User Interface) with download queue system
- New TUI using Textual framework with responsive interface
- Search, results, download, and queue screens for YouTube video management
- Download queue system with sequential background downloads
- Status bar with queue count and download progress indicators
- Comprehensive test suite (97 tests) with 100% pass rate
- Modern Python packaging with pyproject.toml and uv support
- Added requirements-tui.txt for TUI-specific dependencies
- Updated setup.py and install.sh for TUI integration
- Enhanced README.md with TUI usage documentation
2026-02-25 15:59:15 -06:00

236 lines
6.2 KiB
Python

"""
Test fixtures and configuration for YouTube TUI tests
"""
import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
# Test fixtures
@pytest.fixture
def sample_video_data():
"""Sample video data for testing"""
return {
"video_id": "dQw4w9WgXcQ",
"title": "Rick Astley - Never Gonna Give You Up",
"channel": "RickAstleyVEVO",
"channel_id": "UCuZqHn2U8f4o7bVlY8v8w",
"duration": "3:33",
"view_count": "1000000",
"upload_date": "20091025",
"description": "The official video for Rick Astley's hit song.",
"thumbnail_url": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"is_short": False,
}
@pytest.fixture
def sample_video_object(sample_video_data):
"""Sample Video object for testing"""
from youtube_tui.models.video import Video
return Video(**sample_video_data)
@pytest.fixture
def mock_yt_dlp_result():
"""Mock yt-dlp search result"""
return {
"id": "abc123",
"title": "Sample Video",
"author": "Sample Channel",
"channel": "Sample Channel",
"channel_id": "channel123",
"length": "10:30",
"view_count": 150000,
"upload_date": "20240115",
"description": "A sample video description",
"thumbnail": "https://i.ytimg.com/vi/abc123/hqdefault.jpg",
"url": "https://www.youtube.com/watch?v=abc123",
}
@pytest.fixture
def mock_youtube_service():
"""Mock YouTubeService for testing"""
from youtube_cli.main import YouTubeCLI
from youtube_tui.services.youtube import YouTubeService
# Create a real service with mocked cli
with patch.object(YouTubeCLI, "__init__", return_value=None):
service = YouTubeService.__new__(YouTubeService)
service.cli = MagicMock()
service.console = MagicMock()
# Mock async methods
service.search_videos = AsyncMock()
service.download_video = AsyncMock()
service.download_playlist = AsyncMock()
service.get_categories = AsyncMock()
service.is_video_downloaded = AsyncMock()
service.add_to_archive = AsyncMock()
service.get_archive = AsyncMock()
service.get_downloaded_video_ids = AsyncMock()
service.remove_from_archive = AsyncMock()
service._create_video_from_result = MagicMock()
return service
@pytest.fixture
def mock_video():
"""Mock Video object"""
from youtube_tui.models.video import Video
video = Video(
video_id="test123",
title="Test Video",
channel="Test Channel",
channel_id="channel123",
duration="5:00",
view_count="1000",
upload_date="20240101",
description="Test description",
thumbnail_url="https://example.com/thumb.jpg",
url="https://www.youtube.com/watch?v=test123",
is_short=False,
)
return video
@pytest.fixture
def mock_search_results():
"""Mock search results"""
from youtube_tui.models.video import Video
videos = [
Video(
video_id=f"video{i}",
title=f"Video {i}",
channel=f"Channel {i}",
channel_id=f"channel{i}",
duration=f"{i}:00",
view_count=str(i * 1000),
upload_date="20240101",
description=f"Description {i}",
is_short=(i % 3 == 0),
url=f"https://www.youtube.com/watch?v=video{i}",
)
for i in range(1, 16)
]
return videos
@pytest.fixture
def app_config(tmp_path):
"""Temporary app configuration"""
config = {
"download_dir": str(tmp_path / "downloads"),
"default_locations": [
str(tmp_path / "downloads"),
str(tmp_path / "movies"),
],
"max_videos_per_page": 15,
"yt_dlp_args": {
"format": "bestvideo[height=1080]+bestaudio",
},
"network_share_path": str(tmp_path / "network"),
"default_network_subfolder": "General",
}
return config
@pytest.fixture
def mock_app():
"""Mock Textual App for testing screens"""
from textual.app import App
mock_app = MagicMock(spec=App)
mock_app.screen_stack = [None, None, None] # Simulate screen stack
mock_app.notify = MagicMock()
mock_app.exit = MagicMock()
# Mock push_screen and pop_screen
mock_app.push_screen = MagicMock()
mock_app.push_results_screen = MagicMock()
mock_app.pop_screen = MagicMock()
mock_app.action_open_search = MagicMock()
mock_app.run_background = MagicMock()
return mock_app
# Async test utilities
@pytest.fixture
def event_loop():
"""Create an event loop for async tests"""
loop = asyncio.new_event_loop()
yield loop
loop.close()
@pytest.fixture
async def async_mock_search_results():
"""Async mock search results"""
from youtube_tui.models.video import Video
videos = [
Video(
video_id=f"video{i}",
title=f"Video {i}",
channel=f"Channel {i}",
channel_id=f"channel{i}",
duration=f"{i}:00",
view_count=str(i * 1000),
upload_date="20240101",
description=f"Description {i}",
is_short=(i % 3 == 0),
url=f"https://www.youtube.com/watch?v=video{i}",
)
for i in range(1, 16)
]
return videos
# Mock patches
@pytest.fixture
def mock_yt_dlp():
"""Patch yt-dlp for testing"""
with patch("youtube_tui.services.youtube.subprocess") as mock_subprocess:
mock_result = MagicMock()
mock_result.returncode = 0
mock_result.stdout = "2024.01.01"
mock_subprocess.run.return_value = mock_result
yield mock_subprocess
@pytest.fixture
def mock_archive():
"""Mock archive data"""
return {
"video123": {
"url": "https://www.youtube.com/watch?v=video123",
"id": "video123",
"title": "Downloaded Video",
}
}
@pytest.fixture
def mock_archive_file(tmp_path, mock_archive):
"""Create a mock archive file"""
archive_path = tmp_path / "archive.json"
import json
with open(archive_path, "w") as f:
json.dump(mock_archive, f)
return archive_path