| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313 |
- """
- 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 = "<html>{{title}}</html>"
- 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("<html>{{title}}</html>", encoding="utf-8")
- exporter = ReportExporter()
- result = exporter.load_template_from_file(template_path)
- assert result is True
- assert exporter._template == "<html>{{title}}</html>"
- 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 "<style>" in DEFAULT_HTML_TEMPLATE
- assert "</style>" 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
|