""" Tests for the Report Exporter UI component (Story 7.17). """ import pytest from pathlib import Path import tempfile import json from datetime import datetime, timedelta from PyQt6.QtWidgets import QApplication from src.ui.report_exporter import ( ReportFormat, ReportSection, TranslationStatistics, ErrorEntry, ChapterProgress, ReportData, ReportExporter, DEFAULT_HTML_TEMPLATE, ) @pytest.fixture def app(qtbot): """Create QApplication for tests.""" return QApplication.instance() or QApplication([]) @pytest.fixture def sample_statistics(): """Create sample statistics.""" stats = TranslationStatistics() stats.total_files = 10 stats.completed_files = 5 stats.pending_files = 4 stats.failed_files = 1 stats.total_chapters = 100 stats.completed_chapters = 45 stats.failed_chapters = 1 stats.pending_chapters = 54 stats.total_words = 500000 stats.translated_words = 225000 stats.remaining_words = 275000 stats.elapsed_time_seconds = 7200 # 2 hours stats.estimated_remaining_seconds = 7200 stats.words_per_minute = 520.0 stats.chapters_per_hour = 22.5 stats.total_errors = 3 stats.error_types = {"NetworkError": 2, "ParseError": 1} stats.glossary_terms_used = 50 stats.glossary_hit_count = 150 return stats @pytest.fixture def sample_report_data(sample_statistics): """Create sample report data.""" data = ReportData() data.title = "测试翻译报告" data.project_name = "测试项目" data.statistics = sample_statistics # Add chapter progress for i in range(5): data.chapter_progress.append(ChapterProgress( file_name=f"file_{i}.txt", chapter_name=f"Chapter {i+1}", status="completed" if i < 3 else "pending", word_count=5000, translated_words=5000 if i < 3 else 0, start_time=datetime.now() - timedelta(hours=2-i), end_time=datetime.now() - timedelta(hours=2-i-0.5) if i < 3 else None )) # Add errors data.errors = [ ErrorEntry( timestamp=datetime.now() - timedelta(minutes=30), file="file_4.txt", chapter="Chapter 5", error_type="NetworkError", message="Connection timeout" ) ] # Add daily progress for i in range(7): date = datetime.now() - timedelta(days=6-i) data.daily_progress[date.strftime("%Y-%m-%d")] = 30000 + i * 5000 data.notes = "这是测试备注" return data class TestReportFormat: """Tests for ReportFormat enum.""" def test_format_values(self): """Test ReportFormat enum values.""" assert ReportFormat.HTML.value == "html" assert ReportFormat.PDF.value == "pdf" assert ReportFormat.JSON.value == "json" class TestTranslationStatistics: """Tests for TranslationStatistics.""" def test_completion_percentage(self, sample_statistics): """Test completion percentage calculation.""" assert sample_statistics.completion_percentage == 45.0 def test_completion_percentage_zero_total(self): """Test completion with zero total words.""" stats = TranslationStatistics() assert stats.completion_percentage == 0.0 def test_formatted_elapsed_time(self, sample_statistics): """Test elapsed time formatting.""" assert "2小时" in sample_statistics.formatted_elapsed_time def test_formatted_eta(self, sample_statistics): """Test ETA formatting.""" assert "2小时" in sample_statistics.formatted_eta def test_formatted_eta_negative(self): """Test ETA with negative value.""" stats = TranslationStatistics() stats.estimated_remaining_seconds = -1 assert "计算中" in stats.formatted_eta class TestChapterProgress: """Tests for ChapterProgress.""" def test_completion_percentage(self): """Test chapter completion calculation.""" progress = ChapterProgress( file_name="test.txt", chapter_name="Chapter 1", status="completed", word_count=5000, translated_words=5000 ) assert progress.completion_percentage == 100.0 def test_partial_completion(self): """Test partial chapter completion.""" progress = ChapterProgress( file_name="test.txt", chapter_name="Chapter 1", status="pending", word_count=5000, translated_words=2500 ) assert progress.completion_percentage == 50.0 def test_zero_word_count(self): """Test completion with zero word count.""" progress = ChapterProgress( file_name="test.txt", chapter_name="Chapter 1", status="pending", word_count=0, translated_words=0 ) assert progress.completion_percentage == 0.0 class TestReportExporter: """Tests for ReportExporter.""" def test_initialization(self, app): """Test exporter initialization.""" exporter = ReportExporter() assert exporter is not None def test_export_html(self, app, sample_report_data, tmp_path): """Test HTML export.""" exporter = ReportExporter() output_path = tmp_path / "test_report.html" result = exporter.export_html(sample_report_data, output_path) assert result is True assert output_path.exists() # Verify content content = output_path.read_text(encoding="utf-8") assert sample_report_data.title in content assert sample_report_data.project_name in content assert str(sample_report_data.statistics.translated_words) in content def test_export_json(self, app, sample_report_data, tmp_path): """Test JSON export.""" exporter = ReportExporter() output_path = tmp_path / "test_report.json" result = exporter.export_json(sample_report_data, output_path) assert result is True assert output_path.exists() # Verify JSON content with open(output_path, "r", encoding="utf-8") as f: data = json.load(f) assert data["title"] == sample_report_data.title assert data["project_name"] == sample_report_data.project_name assert data["statistics"]["total_words"] == sample_report_data.statistics.total_words def test_export_method(self, app, sample_report_data, tmp_path): """Test the export() method with different formats.""" exporter = ReportExporter() # Test HTML export html_path = tmp_path / "report.html" result = exporter.export(sample_report_data, ReportFormat.HTML, html_path) assert result is True assert html_path.exists() # Test JSON export json_path = tmp_path / "report.json" result = exporter.export(sample_report_data, ReportFormat.JSON, json_path) assert result is True assert json_path.exists() def test_set_template(self, app): """Test setting custom template.""" exporter = ReportExporter() custom_template = "{{title}}" exporter.set_template(custom_template) assert exporter._template == custom_template def test_load_template_from_file(self, app, sample_report_data, tmp_path): """Test loading template from file.""" template_path = tmp_path / "template.html" template_path.write_text("{{title}}", encoding="utf-8") exporter = ReportExporter() result = exporter.load_template_from_file(template_path) assert result is True assert exporter._template == "{{title}}" def test_load_nonexistent_template(self, app, tmp_path): """Test loading non-existent template file.""" exporter = ReportExporter() result = exporter.load_template_from_file(tmp_path / "nonexistent.html") assert result is False class TestReportData: """Tests for ReportData.""" def test_default_values(self): """Test default ReportData values.""" data = ReportData() assert data.title == "翻译报告" assert data.project_name == "" assert data.source_language == "中文" assert data.target_language == "英文" assert isinstance(data.statistics, TranslationStatistics) assert isinstance(data.chapter_progress, list) assert isinstance(data.errors, list) def test_custom_values(self): """Test ReportData with custom values.""" data = ReportData( title="Custom Title", project_name="My Project", source_language="Japanese", target_language="English" ) assert data.title == "Custom Title" assert data.project_name == "My Project" assert data.source_language == "Japanese" assert data.target_language == "English" class TestHtmlTemplate: """Tests for HTML template.""" def test_template_structure(self): """Test that template has required structure.""" assert "{{title}}" in DEFAULT_HTML_TEMPLATE assert "{{generated_at}}" in DEFAULT_HTML_TEMPLATE assert "{{completion_percentage}}" in DEFAULT_HTML_TEMPLATE assert "{{translated_words}}" in DEFAULT_HTML_TEMPLATE def test_template_has_styles(self): """Test that template includes CSS styles.""" assert "" in DEFAULT_HTML_TEMPLATE def test_template_has_charts_section(self): """Test that template has sections for charts.""" assert "翻译概览" in DEFAULT_HTML_TEMPLATE assert "详细统计" in DEFAULT_HTML_TEMPLATE assert "章节进度" in DEFAULT_HTML_TEMPLATE