""" Unit tests for the highlight system. Tests Highlight, HighlightColor, HighlightManager, and integration with query system. """ import unittest import tempfile import shutil from pathlib import Path from pyWebLayout.core.highlight import ( Highlight, HighlightColor, HighlightManager, create_highlight_from_query_result ) from pyWebLayout.core.query import QueryResult, SelectionRange class TestHighlightColor(unittest.TestCase): """Test HighlightColor enum""" def test_colors_defined(self): """Test all expected colors are defined""" expected_colors = ['YELLOW', 'GREEN', 'BLUE', 'PINK', 'ORANGE', 'PURPLE', 'RED'] for color_name in expected_colors: self.assertTrue(hasattr(HighlightColor, color_name)) color = getattr(HighlightColor, color_name) self.assertIsInstance(color.value, tuple) self.assertEqual(len(color.value), 4) # RGBA def test_yellow_is_default(self): """Test yellow highlight color""" yellow = HighlightColor.YELLOW.value self.assertEqual(yellow, (255, 255, 0, 100)) class TestHighlight(unittest.TestCase): """Test Highlight dataclass""" def test_init_basic(self): """Test basic Highlight creation""" highlight = Highlight( id="test-id", bounds=[(10, 20, 50, 15)], color=(255, 255, 0, 100), text="Hello" ) self.assertEqual(highlight.id, "test-id") self.assertEqual(len(highlight.bounds), 1) self.assertEqual(highlight.bounds[0], (10, 20, 50, 15)) self.assertEqual(highlight.color, (255, 255, 0, 100)) self.assertEqual(highlight.text, "Hello") self.assertIsNone(highlight.note) self.assertEqual(highlight.tags, []) def test_init_with_metadata(self): """Test Highlight with full metadata""" highlight = Highlight( id="test-id", bounds=[(10, 20, 50, 15)], color=(255, 255, 0, 100), text="Hello", note="Important word", tags=["important", "keyword"], timestamp=1234567890.0, start_word_index=5, end_word_index=5 ) self.assertEqual(highlight.note, "Important word") self.assertEqual(highlight.tags, ["important", "keyword"]) self.assertEqual(highlight.timestamp, 1234567890.0) self.assertEqual(highlight.start_word_index, 5) self.assertEqual(highlight.end_word_index, 5) def test_to_dict(self): """Test Highlight serialization""" highlight = Highlight( id="test-id", bounds=[(10, 20, 50, 15), (70, 20, 40, 15)], color=(255, 255, 0, 100), text="Hello world", note="Test note", tags=["test"], timestamp=1234567890.0 ) data = highlight.to_dict() self.assertEqual(data['id'], "test-id") self.assertEqual(len(data['bounds']), 2) self.assertEqual(data['bounds'][0], (10, 20, 50, 15)) self.assertEqual(data['color'], (255, 255, 0, 100)) self.assertEqual(data['text'], "Hello world") self.assertEqual(data['note'], "Test note") self.assertEqual(data['tags'], ["test"]) self.assertEqual(data['timestamp'], 1234567890.0) def test_from_dict(self): """Test Highlight deserialization""" data = { 'id': "test-id", 'bounds': [[10, 20, 50, 15], [70, 20, 40, 15]], 'color': [255, 255, 0, 100], 'text': "Hello world", 'note': "Test note", 'tags': ["test"], 'timestamp': 1234567890.0, 'start_word_index': 5, 'end_word_index': 6 } highlight = Highlight.from_dict(data) self.assertEqual(highlight.id, "test-id") self.assertEqual(len(highlight.bounds), 2) self.assertEqual(highlight.bounds[0], (10, 20, 50, 15)) self.assertEqual(highlight.color, (255, 255, 0, 100)) self.assertEqual(highlight.text, "Hello world") self.assertEqual(highlight.note, "Test note") self.assertEqual(highlight.tags, ["test"]) self.assertEqual(highlight.start_word_index, 5) self.assertEqual(highlight.end_word_index, 6) class TestHighlightManager(unittest.TestCase): """Test HighlightManager class""" def setUp(self): """Create temporary directory for highlights""" self.temp_dir = tempfile.mkdtemp() self.manager = HighlightManager( document_id="test-doc", highlights_dir=self.temp_dir ) def tearDown(self): """Clean up temporary directory""" shutil.rmtree(self.temp_dir) def test_init(self): """Test HighlightManager initialization""" self.assertEqual(self.manager.document_id, "test-doc") self.assertEqual(self.manager.highlights_dir, Path(self.temp_dir)) self.assertEqual(len(self.manager.highlights), 0) def test_add_highlight(self): """Test adding a highlight""" highlight = Highlight( id="test-1", bounds=[(10, 20, 50, 15)], color=(255, 255, 0, 100), text="Test" ) self.manager.add_highlight(highlight) self.assertEqual(len(self.manager.highlights), 1) self.assertIn("test-1", self.manager.highlights) self.assertEqual(self.manager.highlights["test-1"], highlight) def test_remove_highlight(self): """Test removing a highlight""" highlight = Highlight( id="test-1", bounds=[(10, 20, 50, 15)], color=(255, 255, 0, 100), text="Test" ) self.manager.add_highlight(highlight) self.assertEqual(len(self.manager.highlights), 1) result = self.manager.remove_highlight("test-1") self.assertTrue(result) self.assertEqual(len(self.manager.highlights), 0) def test_remove_nonexistent_highlight(self): """Test removing a highlight that doesn't exist""" result = self.manager.remove_highlight("nonexistent") self.assertFalse(result) def test_get_highlight(self): """Test getting a highlight by ID""" highlight = Highlight( id="test-1", bounds=[(10, 20, 50, 15)], color=(255, 255, 0, 100), text="Test" ) self.manager.add_highlight(highlight) retrieved = self.manager.get_highlight("test-1") self.assertIsNotNone(retrieved) self.assertEqual(retrieved.id, "test-1") self.assertEqual(retrieved.text, "Test") def test_list_highlights(self): """Test listing all highlights""" highlight1 = Highlight( id="test-1", bounds=[(10, 20, 50, 15)], color=(255, 255, 0, 100), text="First" ) highlight2 = Highlight( id="test-2", bounds=[(100, 20, 50, 15)], color=(100, 255, 100, 100), text="Second" ) self.manager.add_highlight(highlight1) self.manager.add_highlight(highlight2) highlights = self.manager.list_highlights() self.assertEqual(len(highlights), 2) self.assertIn(highlight1, highlights) self.assertIn(highlight2, highlights) def test_clear_all(self): """Test clearing all highlights""" highlight1 = Highlight( id="test-1", bounds=[(10, 20, 50, 15)], color=(255, 255, 0, 100), text="First" ) highlight2 = Highlight( id="test-2", bounds=[(100, 20, 50, 15)], color=(100, 255, 100, 100), text="Second" ) self.manager.add_highlight(highlight1) self.manager.add_highlight(highlight2) self.assertEqual(len(self.manager.highlights), 2) self.manager.clear_all() self.assertEqual(len(self.manager.highlights), 0) def test_persistence(self): """Test that highlights are persisted to disk""" highlight = Highlight( id="test-1", bounds=[(10, 20, 50, 15)], color=(255, 255, 0, 100), text="Persisted" ) self.manager.add_highlight(highlight) # Create new manager for same document new_manager = HighlightManager( document_id="test-doc", highlights_dir=self.temp_dir ) # Should load existing highlights self.assertEqual(len(new_manager.highlights), 1) self.assertIn("test-1", new_manager.highlights) self.assertEqual(new_manager.highlights["test-1"].text, "Persisted") def test_get_highlights_for_page(self): """Test filtering highlights by page bounds""" # Highlight on page highlight1 = Highlight( id="test-1", bounds=[(100, 100, 50, 15)], color=(255, 255, 0, 100), text="On page" ) # Highlight off page highlight2 = Highlight( id="test-2", bounds=[(1000, 1000, 50, 15)], color=(255, 255, 0, 100), text="Off page" ) self.manager.add_highlight(highlight1) self.manager.add_highlight(highlight2) # Page bounds (0, 0, 800, 1000) page_bounds = (0, 0, 800, 1000) page_highlights = self.manager.get_highlights_for_page(page_bounds) self.assertEqual(len(page_highlights), 1) self.assertEqual(page_highlights[0].id, "test-1") class TestCreateHighlightFromQueryResult(unittest.TestCase): """Test create_highlight_from_query_result function""" def test_create_from_single_result(self): """Test creating highlight from single QueryResult""" result = QueryResult( object=object(), object_type="text", bounds=(10, 20, 50, 15), text="Hello" ) highlight = create_highlight_from_query_result( result, color=(255, 255, 0, 100), note="Test note", tags=["test"] ) self.assertIsNotNone(highlight.id) self.assertEqual(len(highlight.bounds), 1) self.assertEqual(highlight.bounds[0], (10, 20, 50, 15)) self.assertEqual(highlight.color, (255, 255, 0, 100)) self.assertEqual(highlight.text, "Hello") self.assertEqual(highlight.note, "Test note") self.assertEqual(highlight.tags, ["test"]) self.assertIsNotNone(highlight.timestamp) def test_create_from_selection_range(self): """Test creating highlight from SelectionRange""" results = [ QueryResult(object(), "text", (10, 20, 30, 15), text="Hello"), QueryResult(object(), "text", (45, 20, 35, 15), text="world") ] sel_range = SelectionRange((10, 20), (80, 35), results) highlight = create_highlight_from_query_result( sel_range, color=(100, 255, 100, 100), note="Multi-word" ) self.assertIsNotNone(highlight.id) self.assertEqual(len(highlight.bounds), 2) self.assertEqual(highlight.bounds[0], (10, 20, 30, 15)) self.assertEqual(highlight.bounds[1], (45, 20, 35, 15)) self.assertEqual(highlight.color, (100, 255, 100, 100)) self.assertEqual(highlight.text, "Hello world") self.assertEqual(highlight.note, "Multi-word") if __name__ == '__main__': unittest.main()