""" Tests for the Statistics Panel UI component (Story 7.23). """ import pytest from datetime import datetime, timedelta from typing import List, Tuple from PyQt6.QtWidgets import QApplication from src.ui.stats_panel import ( ChartType, TimeRange, DailyStats, GlossaryUsage, StatisticsData, StatisticsPanel, MATPLOTLIB_AVAILABLE, ) @pytest.fixture def app(qtbot): """Create QApplication for tests.""" return QApplication.instance() or QApplication([]) @pytest.fixture def sample_stats_data(): """Create sample statistics data.""" data = StatisticsData() # Basic counts data.total_words = 500000 data.translated_words = 225000 data.remaining_words = 275000 data.total_chapters = 100 data.completed_chapters = 45 data.failed_chapters = 2 # Daily stats base_date = datetime.now() for i in range(30): data.daily_stats.append(DailyStats( date=base_date - timedelta(days=29-i), words_translated=7000 + i * 200, chapters_completed=3 + i // 10, files_completed=1, errors_count=0 if i % 5 != 0 else 1, work_time_minutes=120 )) # Speed history for i in range(100): data.speed_history.append(( base_date - timedelta(minutes=100-i), 500 + i * 2 + (i % 10) * 10 )) # Error counts data.error_counts = { "NetworkError": 5, "ParseError": 3, "FileError": 2, "UnknownError": 1 } # Glossary usage for i in range(20): data.glossary_usage.append(GlossaryUsage( term=f"Term{i}", source=f"术语{i}", target=f"Term{i} EN", usage_count=100 - i * 4, category="character" if i % 2 == 0 else "skill" )) # Time tracking data.total_time_hours = 120 data.average_wpm = 520 return data class TestChartType: """Tests for ChartType enum.""" def test_type_values(self): """Test ChartType enum values.""" assert ChartType.PIE_PROGRESS.value == "pie_progress" assert ChartType.BAR_DAILY.value == "bar_daily" assert ChartType.LINE_SPEED.value == "line_speed" assert ChartType.BAR_ERRORS.value == "bar_errors" assert ChartType.BAR_GLOSSARY.value == "bar_glossary" class TestTimeRange: """Tests for TimeRange enum.""" def test_range_values(self): """Test TimeRange enum values.""" assert TimeRange.TODAY.value == "today" assert TimeRange.WEEK.value == "week" assert TimeRange.MONTH.value == "month" assert TimeRange.ALL.value == "all" class TestDailyStats: """Tests for DailyStats.""" def test_creation(self): """Test creating DailyStats.""" stats = DailyStats( date=datetime.now(), words_translated=10000, chapters_completed=5, files_completed=2, errors_count=0, work_time_minutes=120 ) assert stats.words_translated == 10000 assert stats.chapters_completed == 5 class TestGlossaryUsage: """Tests for GlossaryUsage.""" def test_creation(self): """Test creating GlossaryUsage.""" usage = GlossaryUsage( term="test", source="测试", target="test_en", usage_count=50, category="character" ) assert usage.term == "test" assert usage.usage_count == 50 class TestStatisticsData: """Tests for StatisticsData.""" def test_default_values(self): """Test default StatisticsData values.""" data = StatisticsData() assert data.total_words == 0 assert data.translated_words == 0 assert data.remaining_words == 0 assert isinstance(data.daily_stats, list) assert isinstance(data.speed_history, list) assert isinstance(data.error_counts, dict) assert isinstance(data.glossary_usage, list) @pytest.mark.skipif(not MATPLOTLIB_AVAILABLE, reason="Matplotlib not available") class TestStatisticsPanel: """Tests for StatisticsPanel widget.""" def test_initialization(self, qtbot): """Test panel initialization.""" panel = StatisticsPanel() qtbot.addWidget(panel) assert panel is not None def test_set_data(self, qtbot, sample_stats_data): """Test setting statistics data.""" panel = StatisticsPanel() qtbot.addWidget(panel) panel.set_data(sample_stats_data) assert panel._data == sample_stats_data def test_time_range_filter(self, qtbot, sample_stats_data): """Test time range filtering.""" panel = StatisticsPanel() qtbot.addWidget(panel) panel.set_data(sample_stats_data) # Test ALL (default) assert panel._current_time_range == TimeRange.ALL # Test WEEK panel._current_time_range = TimeRange.WEEK filtered = panel._filter_by_time_range(sample_stats_data.daily_stats) assert len(filtered) <= 7 # At most 7 days in a week def test_summary_cards_update(self, qtbot, sample_stats_data): """Test summary card updates.""" panel = StatisticsPanel() qtbot.addWidget(panel) panel.set_data(sample_stats_data) panel._update_summary_cards() # Check values are displayed total_text = panel._total_words_label.value_label.text() assert "500,000" in total_text or "500000" in total_text def test_refresh(self, qtbot, sample_stats_data): """Test refresh method.""" panel = StatisticsPanel() qtbot.addWidget(panel) panel.set_data(sample_stats_data) # Should not raise panel.refresh() class TestStatisticsDialog: """Tests for StatisticsDialog.""" def test_initialization_without_data(self, qtbot): """Test dialog initialization without data.""" from src.ui.stats_panel import StatisticsDialog dialog = StatisticsDialog() qtbot.addWidget(dialog) assert dialog is not None def test_initialization_with_data(self, qtbot, sample_stats_data): """Test dialog initialization with data.""" from src.ui.stats_panel import StatisticsDialog dialog = StatisticsDialog(sample_stats_data) qtbot.addWidget(dialog) assert dialog is not None def test_set_data(self, qtbot, sample_stats_data): """Test setting data in dialog.""" from src.ui.stats_panel import StatisticsDialog dialog = StatisticsDialog() qtbot.addWidget(dialog) dialog.set_data(sample_stats_data) assert dialog._panel._data == sample_stats_data def test_refresh(self, qtbot, sample_stats_data): """Test refresh in dialog.""" from src.ui.stats_panel import StatisticsDialog dialog = StatisticsDialog(sample_stats_data) qtbot.addWidget(dialog) # Should not raise dialog.refresh() class TestMatplotlibAvailability: """Tests for matplotlib availability check.""" def test_matplotlib_flag_exists(self): """Test that MATPLOTLIB_AVAILABLE is defined.""" assert isinstance(MATPLOTLIB_AVAILABLE, bool)