"""Extended tests for TUI widgets to improve coverage.""" import json from datetime import datetime from io import StringIO from pathlib import Path import pytest from rich.console import Console from rich.text import Text from src.models.evaluation import ( EvaluationScore, VerseEvaluation, ) from src.tui.data_provider import TUIDataProvider, VerseData from src.tui.widgets import ( ChapterQualitySummary, DetailPanel, EvaluationSummaryTable, ) @pytest.fixture def temp_data_dir(tmp_path: Path) -> Path: """Create a temporary data directory with test data.""" # Create source directories sources_dir = tmp_path / "sources" (sources_dir / "greek").mkdir(parents=True) (sources_dir / "english" / "kjv").mkdir(parents=True) # Create Greek source greek_data = {"1:1": {"text": "Ἐν ἀρχῇ", "translit": "En archē"}} with (sources_dir / "greek" / "john.json").open("w") as f: json.dump(greek_data, f) # Create KJV source kjv_data = {"1:1": "In the beginning was the Word"} with (sources_dir / "english" / "kjv" / "john.json").open("w") as f: json.dump(kjv_data, f) # Create output directory with translations output_dir = tmp_path / "output" / "john" output_dir.mkdir(parents=True) chapter_data = { "book": "john", "chapter": 1, "verses": { "1": { "text": "初めに言葉があった。", "model": "claude-sonnet-4", "timestamp": "2026-01-13T00:00:00Z", "glossary_terms_applied": [], }, }, } with (output_dir / "chapter_01.json").open("w") as f: json.dump(chapter_data, f) # Create evaluation file with all score levels eval_data = { "book": "john", "chapter": 1, "evaluator_model": "claude-opus-4-5", "evaluated_at": "2026-01-13T00:00:00Z", "verses": { "1": { "scores": { "theological_accuracy": { "score": 95, "confidence": 90, "reasoning": "Excellent", }, "source_fidelity": { "score": 75, "confidence": 85, "reasoning": "Good but could improve", }, "naturalness": { "score": 65, "confidence": 80, "reasoning": "Below threshold", }, }, "overall_score": 78.3, "flags": ["naturalness_concern"], "notes": "Consider improving naturalness", "timestamp": "2026-01-13T00:00:00Z", }, }, "aggregate": { "scores": { "theological_accuracy": 95.0, "source_fidelity": 75.0, "naturalness": 65.0, }, "overall": 78.3, "flagged_count": 1, "flagged_verses": [1], }, } with (output_dir / "chapter_01_eval.json").open("w") as f: json.dump(eval_data, f) # Create metadata directory metadata_dir = tmp_path / "metadata" metadata_dir.mkdir(parents=True) return tmp_path def _render_to_string(renderable) -> str: """Render content to a string for testing.""" console = Console(file=StringIO(), force_terminal=True, width=120) console.print(renderable) return console.file.getvalue() class TestDetailPanelScoreColors: """Tests for DetailPanel score color coding.""" def test_render_with_excellent_score(self, temp_data_dir: Path) -> None: """Test rendering with excellent score (>= 90).""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") # Create verse data with excellent score panel.current_verse = VerseData( book="john", chapter=1, verse=1, greek_text="Ἐν ἀρχῇ", kjv_text="In the beginning", japanese_reference=None, glossary_terms=[], translation_text="初めに言葉があった。", translation_model="claude-sonnet-4", translation_timestamp="2026-01-13", evaluation=VerseEvaluation( verse_number=1, evaluator_model="test", timestamp=datetime.utcnow(), scores={ "theological_accuracy": EvaluationScore( dimension="theological_accuracy", score=95.0, confidence=90.0, reasoning="Excellent", ), }, flags=[], notes=None, ), ) rendered_str = _render_to_string(panel.render()) # Should show excellent score indicator assert "95" in rendered_str def test_render_with_concern_score(self, temp_data_dir: Path) -> None: """Test rendering with concerning score (< 70).""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") # Create verse data with low score panel.current_verse = VerseData( book="john", chapter=1, verse=1, greek_text="Ἐν ἀρχῇ", kjv_text="In the beginning", japanese_reference=None, glossary_terms=[], translation_text="初めに言葉があった。", translation_model="claude-sonnet-4", translation_timestamp="2026-01-13", evaluation=VerseEvaluation( verse_number=1, evaluator_model="test", timestamp=datetime.utcnow(), scores={ "naturalness": EvaluationScore( dimension="naturalness", score=55.0, confidence=80.0, reasoning="Needs work", ), }, flags=["naturalness_concern"], notes="Needs improvement", ), ) rendered_str = _render_to_string(panel.render()) assert "55" in rendered_str def test_render_with_multiple_flags(self, temp_data_dir: Path) -> None: """Test rendering verse with multiple flags.""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") panel.current_verse = VerseData( book="john", chapter=1, verse=1, greek_text="Ἐν ἀρχῇ", kjv_text="In the beginning", japanese_reference=None, glossary_terms=[], translation_text="初めに言葉があった。", translation_model="claude-sonnet-4", translation_timestamp="2026-01-13", evaluation=VerseEvaluation( verse_number=1, evaluator_model="test", timestamp=datetime.utcnow(), scores={ "theological_accuracy": EvaluationScore( dimension="theological_accuracy", score=60.0, confidence=70.0, reasoning="Concern", ), "source_fidelity": EvaluationScore( dimension="source_fidelity", score=55.0, confidence=65.0, reasoning="Issue", ), }, flags=["theological_concern", "source_fidelity_concern"], notes="Multiple issues", ), ) rendered_str = _render_to_string(panel.render()) # Should contain flag indicators assert "concern" in rendered_str.lower() or "flag" in rendered_str.lower() class TestEvaluationSummaryTableScoreBars: """Tests for EvaluationSummaryTable visual bars.""" def test_render_with_varied_scores(self) -> None: """Test rendering with various score levels.""" table = EvaluationSummaryTable(id="eval-table") table.set_scores({ "theological_accuracy": (95.0, 90.0), # Excellent "source_fidelity": (75.0, 85.0), # Good "naturalness": (55.0, 80.0), # Concern "readability": (85.0, 90.0), # Good "glossary_consistency": (100.0, 100.0), # Perfect "literary_quality": (65.0, 75.0), # Below threshold }) rendered_str = _render_to_string(table.render()) # Should contain all dimension scores assert "95" in rendered_str assert "75" in rendered_str assert "55" in rendered_str def test_render_with_scores_uses_bars(self) -> None: """Test rendering includes visual bars.""" table = EvaluationSummaryTable(id="eval-table") table.set_scores({"theological_accuracy": (90.0, 95.0)}) rendered_str = _render_to_string(table.render()) # Should contain bar characters assert "█" in rendered_str or "░" in rendered_str class TestChapterQualitySummary: """Tests for ChapterQualitySummary widget.""" def test_render_empty(self, temp_data_dir: Path) -> None: """Test rendering with no chapter selected.""" provider = TUIDataProvider(temp_data_dir) widget = ChapterQualitySummary(provider, id="quality-summary") rendered = widget.render() rendered_str = _render_to_string(rendered) assert "Select a chapter" in rendered_str or "Chapter" in rendered_str def test_render_with_chapter_data(self, temp_data_dir: Path) -> None: """Test rendering with chapter selected.""" provider = TUIDataProvider(temp_data_dir) widget = ChapterQualitySummary(provider, id="quality-summary") widget.update_chapter("john", 1) rendered_str = _render_to_string(widget.render()) # Should show chapter info assert "JOHN" in rendered_str or "john" in rendered_str.lower() def test_render_with_no_eval_data(self, temp_data_dir: Path) -> None: """Test rendering chapter with no evaluation.""" provider = TUIDataProvider(temp_data_dir) widget = ChapterQualitySummary(provider, id="quality-summary") # Select a chapter that doesn't have evaluation widget.update_chapter("john", 99) rendered_str = _render_to_string(widget.render()) assert "No evaluation" in rendered_str or "--evaluate" in rendered_str class TestDimensionNameFormatting: """Tests for dimension name formatting in widgets.""" def test_format_dimension_names_table(self) -> None: """Test dimension name formatting in EvaluationSummaryTable.""" table = EvaluationSummaryTable(id="eval-table") # Test that dimension names are formatted formatted = table._format_dimension_name("theological_accuracy") assert "Theolog" in formatted or "theological" in formatted.lower() formatted = table._format_dimension_name("source_fidelity") assert "Fidelity" in formatted or "source" in formatted.lower() formatted = table._format_dimension_name("glossary_consistency") assert "Glossary" in formatted or "consistency" in formatted.lower() def test_format_dimension_names_panel(self, temp_data_dir: Path) -> None: """Test dimension name formatting in DetailPanel.""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") formatted = panel._format_dimension_name("theological_accuracy") assert "Theolog" in formatted formatted = panel._format_dimension_name("unknown_dimension") # Unknown dimensions should be title-cased assert "Unknown" in formatted or "Dimension" in formatted class TestScoreColorHelpers: """Tests for score color helper methods.""" def test_get_score_color_excellent(self) -> None: """Test color for excellent scores.""" table = EvaluationSummaryTable(id="eval-table") color = table._get_score_color(95.0) assert color == "green" def test_get_score_color_good(self) -> None: """Test color for good scores.""" table = EvaluationSummaryTable(id="eval-table") color = table._get_score_color(80.0) assert color == "yellow" def test_get_score_color_concern(self) -> None: """Test color for concerning scores.""" table = EvaluationSummaryTable(id="eval-table") color = table._get_score_color(65.0) assert color == "orange1" def test_get_score_color_poor(self) -> None: """Test color for poor scores.""" table = EvaluationSummaryTable(id="eval-table") color = table._get_score_color(50.0) assert color == "red" class TestDetailPanelHelpers: """Tests for DetailPanel helper methods.""" def test_get_quality_indicator_excellent(self, temp_data_dir: Path) -> None: """Test indicator for excellent quality.""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") indicator = panel._get_quality_indicator(95.0) assert indicator == "●" def test_get_quality_indicator_good(self, temp_data_dir: Path) -> None: """Test indicator for good quality.""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") indicator = panel._get_quality_indicator(80.0) assert indicator == "◐" def test_get_quality_indicator_concern(self, temp_data_dir: Path) -> None: """Test indicator for concerning quality.""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") indicator = panel._get_quality_indicator(65.0) assert indicator == "○" def test_get_quality_indicator_poor(self, temp_data_dir: Path) -> None: """Test indicator for poor quality.""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") indicator = panel._get_quality_indicator(50.0) assert indicator == "✗" class TestFlagFormatting: """Tests for flag formatting in DetailPanel.""" def test_format_flag_theological(self, temp_data_dir: Path) -> None: """Test formatting theological flag.""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") formatted = panel._format_flag("theological_concern") assert "theological" in formatted or "concern" in formatted def test_format_flag_source(self, temp_data_dir: Path) -> None: """Test formatting source fidelity flag.""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") formatted = panel._format_flag("source_fidelity_concern") assert "source" in formatted or "fidelity" in formatted def test_format_flag_with_low(self, temp_data_dir: Path) -> None: """Test formatting flag with 'low' keyword.""" provider = TUIDataProvider(temp_data_dir) panel = DetailPanel(provider, id="detail") formatted = panel._format_flag("low_confidence") # 'low' should be replaced with arrow assert "↓" in formatted or "confidence" in formatted