pyWebLayout/tests/io_tests/test_html_link_interactivity.py
Duncan Tourolle 00314c9b4f
All checks were successful
Python CI / test (push) Successful in 6m49s
ADDITIOANL TEST
2025-11-08 19:39:10 +01:00

206 lines
8.0 KiB
Python

"""
Test HTML link interactivity when rendering pages.
This test verifies that HTML links parsed via parse_html_string() are
properly interactive and can be queried/detected when rendered on a page.
"""
import unittest
from pyWebLayout.io.readers.html_extraction import parse_html_string
from pyWebLayout.concrete.page import Page
from pyWebLayout.style.page_style import PageStyle
from pyWebLayout.abstract.inline import LinkedWord
class TestHTMLLinkInteractivity(unittest.TestCase):
"""Test that HTML links are interactive when rendered."""
def test_simple_link_parsing(self):
"""Test that parse_html_string creates LinkedWord objects for links."""
html = '''
<div>
<p>Click <a href="action:back_to_library">here</a> to go back.</p>
<p>Or visit <a href="setting:font_increase">this setting</a>.</p>
</div>
'''
blocks = parse_html_string(html)
# Should have 2 paragraphs
self.assertEqual(len(blocks), 2)
# First paragraph - check for LinkedWord
para1_words = list(blocks[0].words)
linked_words_1 = [w for w in para1_words if isinstance(w, LinkedWord)]
# "here" should be a LinkedWord
self.assertEqual(len(linked_words_1), 1)
self.assertEqual(linked_words_1[0].text, "here")
self.assertEqual(linked_words_1[0].location, "action:back_to_library")
# Second paragraph
para2_words = list(blocks[1].words)
linked_words_2 = [w for w in para2_words if isinstance(w, LinkedWord)]
# "this" and "setting" should be LinkedWords
self.assertEqual(len(linked_words_2), 2)
link_texts = [w.text for w in linked_words_2]
self.assertIn("this", link_texts)
self.assertIn("setting", link_texts)
def test_link_rendering_on_page(self):
"""Test that links are properly rendered and detectable on a page."""
from pyWebLayout.layout.document_layouter import DocumentLayouter
html = '''
<div>
<h1>Settings</h1>
<p>Font Size: <a href="setting:font_decrease">-</a> | <a href="setting:font_increase">+</a></p>
<p><a href="action:back_to_library">Back to Library</a></p>
</div>
'''
blocks = parse_html_string(html)
# Create a page and use DocumentLayouter to properly layout blocks
page_style = PageStyle()
page = Page((600, 800), page_style)
layouter = DocumentLayouter(page)
# Layout all blocks
for block in blocks:
layouter.layout_document([block])
# Render the page
rendered_image = page.render()
# Verify the page rendered
self.assertIsNotNone(rendered_image)
self.assertEqual(rendered_image.size, (600, 800))
# Now verify we can detect the links via query_point
# We need to find the positions of the linked words
found_links = []
# Scan the blocks for LinkedWords
for block in blocks:
if hasattr(block, 'words'):
for word in block.words:
if isinstance(word, LinkedWord):
found_links.append({
'text': word.text,
'location': word.location,
'link_type': word.link_type
})
# Verify we found the expected links
# -, +, Back, to, Library (5 LinkedWords total)
self.assertGreater(len(found_links), 0, "Should find at least some LinkedWords")
link_locations = [link['location'] for link in found_links]
self.assertIn("setting:font_decrease", link_locations)
self.assertIn("setting:font_increase", link_locations)
self.assertIn("action:back_to_library", link_locations)
def test_link_query_point_detection(self):
"""Test that query_point can detect links on a rendered page."""
from pyWebLayout.layout.document_layouter import DocumentLayouter
html = '''
<p>Click <a href="action:test">here</a> to test.</p>
'''
blocks = parse_html_string(html)
# Create a page
page_style = PageStyle()
page = Page((400, 200), page_style)
layouter = DocumentLayouter(page)
# Layout the block
for block in blocks:
layouter.layout_document([block])
# Get the rendered canvas
_ = page.draw # Ensure canvas exists
# Find the LinkedWord in the blocks
link_word = None
for block in blocks:
if hasattr(block, 'words'):
for word in block.words:
if isinstance(word, LinkedWord) and word.location == "action:test":
link_word = word
break
# Verify we found the link
self.assertIsNotNone(link_word)
self.assertEqual(link_word.text, "here")
self.assertEqual(link_word.location, "action:test")
# Test that query_point can detect the link
# Try querying a point in the middle of the page where text should be
# Note: The exact coordinates depend on the layout, but we can test the API
result = page.query_point((100, 50))
if result:
# If we hit something, verify we can access its properties
self.assertIsNotNone(result.object_type)
# Link detection would show is_interactive=True for links
if result.is_interactive:
self.assertIsNotNone(result.link_target)
def test_settings_overlay_button_html(self):
"""Test the specific HTML pattern used for settings overlay buttons."""
# This is the pattern used in the settings overlay
html = '''
<div>
<h2 style="text-align: center; font-size: 18px; font-weight: bold; margin: 10px 0;">Settings</h2>
<p style="padding: 15px; margin: 5px 0; background-color: #dc3545; text-align: center; border-radius: 5px;">
<a href="action:back_to_library" style="text-decoration: none; color: white; font-weight: bold; font-size: 14px;">◄ Back to Library</a>
</p>
<p style="padding: 10px; margin: 5px 0; background-color: #f8f9fa; border-radius: 5px;">
<span style="font-weight: bold;">Font Size: 100%</span><br>
<a href="setting:font_decrease" style="text-decoration: none; color: #007bff; margin: 0 10px;">[-]</a>
<a href="setting:font_increase" style="text-decoration: none; color: #007bff; margin: 0 10px;">[+]</a>
</p>
</div>
'''
blocks = parse_html_string(html)
# Find all LinkedWords
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)
# Verify we found the expected links
self.assertGreater(len(all_linked_words), 0, "Should find LinkedWords in settings HTML")
# Check for specific link targets
link_targets = {word.location for word in all_linked_words}
self.assertIn("action:back_to_library", link_targets,
"Should find 'Back to Library' link")
self.assertIn("setting:font_decrease", link_targets,
"Should find font decrease link")
self.assertIn("setting:font_increase", link_targets,
"Should find font increase link")
# Verify the link texts
back_to_library_words = [w for w in all_linked_words
if w.location == "action:back_to_library"]
self.assertGreater(len(back_to_library_words), 0,
"Should have words linked to back_to_library action")
# Print debug info
print(f"\nFound {len(all_linked_words)} linked words:")
for word in all_linked_words:
print(f" - '{word.text}' -> {word.location}")
if __name__ == '__main__':
unittest.main()