dreader-application/tests/test_ereader_highlighting.py
2025-11-12 18:52:08 +00:00

317 lines
10 KiB
Python

"""
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 preserve the input mode
self.assertEqual(result_img.mode, 'RGB')
if __name__ == '__main__':
unittest.main()