yet more tests
This commit is contained in:
parent
00314c9b4f
commit
1ea870eef5
137
tests/concrete/test_linkedword_hyphenation.py
Normal file
137
tests/concrete/test_linkedword_hyphenation.py
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
"""
|
||||||
|
Test that LinkedWord objects remain as LinkText even when hyphenated.
|
||||||
|
|
||||||
|
This is a regression test for the bug where hyphenated LinkedWords
|
||||||
|
were being converted to regular Text objects instead of LinkText.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from PIL import Image, ImageDraw
|
||||||
|
from pyWebLayout.concrete.text import Line
|
||||||
|
from pyWebLayout.concrete.functional import LinkText
|
||||||
|
from pyWebLayout.abstract.inline import LinkedWord
|
||||||
|
from pyWebLayout.abstract.functional import LinkType
|
||||||
|
from pyWebLayout.style import Font, Alignment
|
||||||
|
|
||||||
|
|
||||||
|
class TestLinkedWordHyphenation(unittest.TestCase):
|
||||||
|
"""Test that LinkedWords become LinkText objects even when hyphenated."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""Set up test canvas and drawing context."""
|
||||||
|
self.canvas = Image.new('RGB', (800, 600), color='white')
|
||||||
|
self.draw = ImageDraw.Draw(self.canvas)
|
||||||
|
self.font = Font(font_size=12)
|
||||||
|
|
||||||
|
def test_short_linkedword_no_hyphenation(self):
|
||||||
|
"""Test that a short LinkedWord that fits becomes a LinkText."""
|
||||||
|
# Create a line with enough space
|
||||||
|
line = Line(
|
||||||
|
spacing=(5, 15),
|
||||||
|
origin=(0, 0),
|
||||||
|
size=(200, 30),
|
||||||
|
draw=self.draw,
|
||||||
|
halign=Alignment.LEFT
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a LinkedWord that will fit without hyphenation
|
||||||
|
linked_word = LinkedWord(
|
||||||
|
text="click",
|
||||||
|
style=self.font,
|
||||||
|
location="action:test",
|
||||||
|
link_type=LinkType.API
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the word to the line
|
||||||
|
success, overflow = line.add_word(linked_word)
|
||||||
|
|
||||||
|
# Verify it was added successfully
|
||||||
|
self.assertTrue(success)
|
||||||
|
self.assertIsNone(overflow)
|
||||||
|
|
||||||
|
# Verify it became a LinkText object
|
||||||
|
self.assertEqual(len(line._text_objects), 1)
|
||||||
|
self.assertIsInstance(line._text_objects[0], LinkText)
|
||||||
|
self.assertEqual(line._text_objects[0].link.location, "action:test")
|
||||||
|
|
||||||
|
def test_long_linkedword_with_hyphenation(self):
|
||||||
|
"""Test that a long LinkedWord that needs hyphenation preserves LinkText."""
|
||||||
|
# Create a narrow line to force hyphenation
|
||||||
|
line = Line(
|
||||||
|
spacing=(5, 15),
|
||||||
|
origin=(0, 0),
|
||||||
|
size=(80, 30),
|
||||||
|
draw=self.draw,
|
||||||
|
halign=Alignment.LEFT
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a long LinkedWord that will need hyphenation
|
||||||
|
linked_word = LinkedWord(
|
||||||
|
text="https://example.com/very-long-url",
|
||||||
|
style=self.font,
|
||||||
|
location="https://example.com/very-long-url",
|
||||||
|
link_type=LinkType.EXTERNAL
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the word to the line
|
||||||
|
success, overflow = line.add_word(linked_word)
|
||||||
|
|
||||||
|
# The word should either:
|
||||||
|
# 1. Fit completely and be a LinkText
|
||||||
|
# 2. Be hyphenated, and BOTH parts should be LinkText
|
||||||
|
|
||||||
|
if overflow is not None:
|
||||||
|
# Word was hyphenated
|
||||||
|
# The first part should be in the line
|
||||||
|
self.assertTrue(success)
|
||||||
|
self.assertGreater(len(line._text_objects), 0)
|
||||||
|
|
||||||
|
# Both parts should be LinkText (this is the bug we're testing for)
|
||||||
|
for text_obj in line._text_objects:
|
||||||
|
self.assertIsInstance(text_obj, LinkText,
|
||||||
|
f"Hyphenated LinkedWord part should be LinkText, got {type(text_obj)}")
|
||||||
|
self.assertEqual(text_obj.link.location, linked_word.location)
|
||||||
|
|
||||||
|
# The overflow should also be LinkText if it's hyphenated
|
||||||
|
if isinstance(overflow, LinkText):
|
||||||
|
self.assertEqual(overflow.link.location, linked_word.location)
|
||||||
|
else:
|
||||||
|
# Word fit without hyphenation
|
||||||
|
self.assertTrue(success)
|
||||||
|
self.assertEqual(len(line._text_objects), 1)
|
||||||
|
self.assertIsInstance(line._text_objects[0], LinkText)
|
||||||
|
|
||||||
|
def test_linkedword_title_preserved_after_hyphenation(self):
|
||||||
|
"""Test that link metadata (title) is preserved when hyphenated."""
|
||||||
|
# Create a narrow line
|
||||||
|
line = Line(
|
||||||
|
spacing=(5, 15),
|
||||||
|
origin=(0, 0),
|
||||||
|
size=(60, 30),
|
||||||
|
draw=self.draw,
|
||||||
|
halign=Alignment.LEFT
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create a LinkedWord with title that will likely be hyphenated
|
||||||
|
linked_word = LinkedWord(
|
||||||
|
text="documentation",
|
||||||
|
style=self.font,
|
||||||
|
location="https://docs.example.com",
|
||||||
|
link_type=LinkType.EXTERNAL,
|
||||||
|
title="View Documentation"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Add the word
|
||||||
|
success, overflow = line.add_word(linked_word)
|
||||||
|
|
||||||
|
# Verify metadata is preserved
|
||||||
|
if overflow is not None:
|
||||||
|
# If hyphenated, both parts should have link metadata
|
||||||
|
for text_obj in line._text_objects:
|
||||||
|
if isinstance(text_obj, LinkText):
|
||||||
|
self.assertEqual(text_obj.link.location, "https://docs.example.com")
|
||||||
|
self.assertEqual(text_obj.link.title, "View Documentation")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
142
tests/layout/test_html_links_in_ereader.py
Normal file
142
tests/layout/test_html_links_in_ereader.py
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
"""
|
||||||
|
End-to-end test for HTML links in EreaderLayoutManager.
|
||||||
|
|
||||||
|
This test mimics exactly what the dreader application does:
|
||||||
|
1. Load HTML with links via parse_html_string
|
||||||
|
2. Create an EreaderLayoutManager
|
||||||
|
3. Render a page
|
||||||
|
4. Query for interactive elements
|
||||||
|
|
||||||
|
This should reveal if links are actually interactive after full rendering.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
from pyWebLayout.io.readers.html_extraction import parse_html_string
|
||||||
|
from pyWebLayout.layout.ereader_manager import EreaderLayoutManager
|
||||||
|
from pyWebLayout.abstract.inline import LinkedWord
|
||||||
|
from pyWebLayout.concrete.functional import LinkText
|
||||||
|
|
||||||
|
|
||||||
|
class TestHTMLLinksInEreader(unittest.TestCase):
|
||||||
|
"""Test HTML link interactivity in the full ereader pipeline."""
|
||||||
|
|
||||||
|
def test_settings_overlay_links_are_interactive(self):
|
||||||
|
"""Test that settings overlay HTML creates interactive links."""
|
||||||
|
# This is realistic settings overlay HTML
|
||||||
|
html = '''
|
||||||
|
<div>
|
||||||
|
<h2>Settings</h2>
|
||||||
|
<p>
|
||||||
|
<a href="action:back_to_library">Back to Library</a>
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Font Size:
|
||||||
|
<a href="setting:font_decrease">[-]</a>
|
||||||
|
<a href="setting:font_increase">[+]</a>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
'''
|
||||||
|
|
||||||
|
# Step 1: Parse HTML to blocks
|
||||||
|
blocks = parse_html_string(html)
|
||||||
|
|
||||||
|
# Verify LinkedWords were created
|
||||||
|
all_linked_words = []
|
||||||
|
for block in blocks:
|
||||||
|
if hasattr(block, 'words'):
|
||||||
|
for word in block.words:
|
||||||
|
if isinstance(word, LinkedWord):
|
||||||
|
all_linked_words.append(word)
|
||||||
|
|
||||||
|
self.assertGreater(len(all_linked_words), 0, "Should create LinkedWords from HTML")
|
||||||
|
print(f"\n Created {len(all_linked_words)} LinkedWords from HTML")
|
||||||
|
|
||||||
|
# Step 2: Create EreaderLayoutManager (like the dreader app does)
|
||||||
|
page_size = (400, 600)
|
||||||
|
manager = EreaderLayoutManager(
|
||||||
|
blocks=blocks,
|
||||||
|
page_size=page_size,
|
||||||
|
document_id="test_settings"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 3: Get the rendered page
|
||||||
|
page = manager.get_current_page()
|
||||||
|
self.assertIsNotNone(page)
|
||||||
|
|
||||||
|
# Step 4: Render to image
|
||||||
|
rendered_image = page.render()
|
||||||
|
self.assertIsNotNone(rendered_image)
|
||||||
|
print(f" Rendered page: {rendered_image.size}")
|
||||||
|
|
||||||
|
# Step 5: Find all interactive elements by scanning the page
|
||||||
|
# This is the CRITICAL test - are there LinkText objects in the page?
|
||||||
|
interactive_elements = []
|
||||||
|
|
||||||
|
# Scan through all children of the page
|
||||||
|
if hasattr(page, '_children'):
|
||||||
|
for child in page._children:
|
||||||
|
# Check if child is a Line
|
||||||
|
if hasattr(child, '_text_objects'):
|
||||||
|
for text_obj in child._text_objects:
|
||||||
|
if isinstance(text_obj, LinkText):
|
||||||
|
interactive_elements.append({
|
||||||
|
'type': 'LinkText',
|
||||||
|
'text': text_obj._text,
|
||||||
|
'location': text_obj.link.location,
|
||||||
|
'is_interactive': hasattr(text_obj, 'execute')
|
||||||
|
})
|
||||||
|
|
||||||
|
print(f" Found {len(interactive_elements)} LinkText objects in rendered page")
|
||||||
|
for elem in interactive_elements:
|
||||||
|
print(f" - '{elem['text']}' -> {elem['location']}")
|
||||||
|
|
||||||
|
# THIS IS THE KEY ASSERTION
|
||||||
|
self.assertGreater(len(interactive_elements), 0,
|
||||||
|
"Settings overlay should have interactive LinkText objects after rendering!")
|
||||||
|
|
||||||
|
# Verify the expected links are present
|
||||||
|
locations = {elem['location'] for elem in interactive_elements}
|
||||||
|
self.assertIn("action:back_to_library", locations,
|
||||||
|
"Should find 'Back to Library' link")
|
||||||
|
self.assertIn("setting:font_decrease", locations,
|
||||||
|
"Should find font decrease link")
|
||||||
|
self.assertIn("setting:font_increase", locations,
|
||||||
|
"Should find font increase link")
|
||||||
|
|
||||||
|
def test_query_point_detects_links(self):
|
||||||
|
"""Test that query_point can detect LinkText objects."""
|
||||||
|
html = '<p><a href="action:test">Click here</a> to test.</p>'
|
||||||
|
|
||||||
|
blocks = parse_html_string(html)
|
||||||
|
manager = EreaderLayoutManager(
|
||||||
|
blocks=blocks,
|
||||||
|
page_size=(400, 200),
|
||||||
|
document_id="test_query"
|
||||||
|
)
|
||||||
|
|
||||||
|
page = manager.get_current_page()
|
||||||
|
page.render()
|
||||||
|
|
||||||
|
# Try to query various points on the page
|
||||||
|
# We don't know exact coordinates, so scan a grid
|
||||||
|
found_link = False
|
||||||
|
for y in range(20, 100, 10):
|
||||||
|
for x in range(20, 380, 20):
|
||||||
|
result = page.query_point((x, y))
|
||||||
|
if result and result.is_interactive:
|
||||||
|
print(f"\n Found interactive element at ({x}, {y})")
|
||||||
|
print(f" Type: {result.object_type}")
|
||||||
|
print(f" Link target: {result.link_target}")
|
||||||
|
print(f" Text: {result.text}")
|
||||||
|
found_link = True
|
||||||
|
self.assertEqual(result.link_target, "action:test")
|
||||||
|
break
|
||||||
|
if found_link:
|
||||||
|
break
|
||||||
|
|
||||||
|
self.assertTrue(found_link,
|
||||||
|
"Should be able to detect link via query_point somewhere on the page")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Loading…
x
Reference in New Issue
Block a user