""" Integration tests for EbookReader highlighting functionality. Tests the highlighting API in EbookReader including: - highlight_word() - highlight_selection() - remove_highlight() - list_highlights() - get_highlights_for_current_page() - clear_highlights() - Highlight rendering on pages """ import unittest import tempfile import shutil from pathlib import Path from PIL import Image from dreader.application import EbookReader from pyWebLayout.core.highlight import HighlightColor class TestEbookReaderHighlighting(unittest.TestCase): """Test EbookReader highlighting API""" def setUp(self): """Create temporary directories and reader""" self.temp_dir = tempfile.mkdtemp() self.bookmarks_dir = Path(self.temp_dir) / "bookmarks" self.highlights_dir = Path(self.temp_dir) / "highlights" self.reader = EbookReader( page_size=(800, 1000), bookmarks_dir=str(self.bookmarks_dir), highlights_dir=str(self.highlights_dir) ) def tearDown(self): """Clean up""" self.reader.close() shutil.rmtree(self.temp_dir) def test_highlight_manager_not_initialized_before_load(self): """Test that highlight manager is None before loading a book""" self.assertIsNone(self.reader.highlight_manager) def test_highlight_manager_initialized_after_load(self): """Test that highlight manager is initialized after loading""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) self.assertIsNotNone(self.reader.highlight_manager) self.assertEqual(self.reader.highlight_manager.document_id, "test") def test_highlight_word_without_book(self): """Test highlighting returns None when no book loaded""" highlight_id = self.reader.highlight_word(100, 100) self.assertIsNone(highlight_id) def test_highlight_word(self): """Test highlighting a word at a pixel location""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) # Get a page to ensure content is rendered page_img = self.reader.get_current_page() self.assertIsNotNone(page_img) # Try to highlight a word (coordinates may not hit text in test) highlight_id = self.reader.highlight_word( 200, 300, color=HighlightColor.YELLOW.value, note="Test highlight" ) # If we hit text, should return an ID if highlight_id: self.assertIsInstance(highlight_id, str) self.assertGreater(len(highlight_id), 0) # Verify it was added highlights = self.reader.list_highlights() self.assertGreater(len(highlights), 0) def test_highlight_selection(self): """Test highlighting a range of words""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) # Get a page page_img = self.reader.get_current_page() self.assertIsNotNone(page_img) # Try to highlight a selection highlight_id = self.reader.highlight_selection( start=(100, 200), end=(400, 250), color=HighlightColor.BLUE.value, tags=["selection", "test"] ) # If selection hit text, should return an ID if highlight_id: self.assertIsInstance(highlight_id, str) highlights = self.reader.list_highlights() found = False for h in highlights: if h.id == highlight_id: found = True self.assertEqual(h.color, HighlightColor.BLUE.value) self.assertIn("selection", h.tags) break self.assertTrue(found) def test_list_highlights_empty(self): """Test listing highlights when none exist""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) highlights = self.reader.list_highlights() self.assertEqual(len(highlights), 0) def test_remove_highlight(self): """Test removing a highlight""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) page_img = self.reader.get_current_page() # Create a highlight directly via manager for testing from pyWebLayout.core.highlight import Highlight highlight = Highlight( id="test-highlight", bounds=[(100, 200, 50, 15)], color=HighlightColor.YELLOW.value, text="Test" ) self.reader.highlight_manager.add_highlight(highlight) # Verify it exists self.assertEqual(len(self.reader.list_highlights()), 1) # Remove it result = self.reader.remove_highlight("test-highlight") self.assertTrue(result) # Verify it's gone self.assertEqual(len(self.reader.list_highlights()), 0) def test_remove_nonexistent_highlight(self): """Test removing a highlight that doesn't exist""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) result = self.reader.remove_highlight("nonexistent") self.assertFalse(result) def test_clear_highlights(self): """Test clearing all highlights""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) # Add some highlights directly from pyWebLayout.core.highlight import Highlight for i in range(3): highlight = Highlight( id=f"test-{i}", bounds=[(100 + i * 50, 200, 50, 15)], color=HighlightColor.YELLOW.value, text=f"Word{i}" ) self.reader.highlight_manager.add_highlight(highlight) self.assertEqual(len(self.reader.list_highlights()), 3) self.reader.clear_highlights() self.assertEqual(len(self.reader.list_highlights()), 0) def test_get_highlights_for_current_page(self): """Test getting highlights for current page""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) # Add highlights at different locations from pyWebLayout.core.highlight import Highlight # On page highlight1 = Highlight( id="on-page", bounds=[(100, 200, 50, 15)], color=HighlightColor.YELLOW.value, text="OnPage" ) # Off page (beyond page height) highlight2 = Highlight( id="off-page", bounds=[(100, 5000, 50, 15)], color=HighlightColor.YELLOW.value, text="OffPage" ) self.reader.highlight_manager.add_highlight(highlight1) self.reader.highlight_manager.add_highlight(highlight2) # Get highlights for current page page_highlights = self.reader.get_highlights_for_current_page() # Should only get the on-page highlight self.assertEqual(len(page_highlights), 1) self.assertEqual(page_highlights[0].id, "on-page") def test_get_current_page_with_highlights(self): """Test that get_current_page renders highlights""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) # Get page without highlights page_no_highlights = self.reader.get_current_page(include_highlights=False) self.assertIsNotNone(page_no_highlights) # Add a highlight from pyWebLayout.core.highlight import Highlight highlight = Highlight( id="test", bounds=[(100, 200, 50, 15)], color=HighlightColor.YELLOW.value, text="Test" ) self.reader.highlight_manager.add_highlight(highlight) # Get page with highlights page_with_highlights = self.reader.get_current_page(include_highlights=True) self.assertIsNotNone(page_with_highlights) # Both should be valid images self.assertIsInstance(page_no_highlights, Image.Image) self.assertIsInstance(page_with_highlights, Image.Image) # Should have same size self.assertEqual(page_no_highlights.size, page_with_highlights.size) def test_highlight_persistence(self): """Test that highlights persist across reader instances""" # Load book and add highlight success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) from pyWebLayout.core.highlight import Highlight highlight = Highlight( id="persistent", bounds=[(100, 200, 50, 15)], color=HighlightColor.YELLOW.value, text="Persisted" ) self.reader.highlight_manager.add_highlight(highlight) self.assertEqual(len(self.reader.list_highlights()), 1) # Close reader self.reader.close() # Create new reader for same book new_reader = EbookReader( page_size=(800, 1000), bookmarks_dir=str(self.bookmarks_dir), highlights_dir=str(self.highlights_dir) ) success = new_reader.load_epub("tests/data/test.epub") self.assertTrue(success) # Should load existing highlights highlights = new_reader.list_highlights() self.assertEqual(len(highlights), 1) self.assertEqual(highlights[0].id, "persistent") self.assertEqual(highlights[0].text, "Persisted") new_reader.close() def test_render_highlights_helper(self): """Test the _render_highlights helper method""" success = self.reader.load_epub("tests/data/test.epub") self.assertTrue(success) # Create a simple test image from PIL import Image as PILImage test_img = PILImage.new('RGB', (100, 100), color='white') from pyWebLayout.core.highlight import Highlight highlights = [ Highlight( id="h1", bounds=[(10, 10, 30, 20)], color=(255, 255, 0, 100), text="Test" ) ] # Render highlights result_img = self.reader._render_highlights(test_img, highlights) self.assertIsNotNone(result_img) self.assertIsInstance(result_img, PILImage.Image) self.assertEqual(result_img.size, test_img.size) # Result should be RGBA for transparency self.assertEqual(result_img.mode, 'RGBA') if __name__ == '__main__': unittest.main()