From 00314c9b4f6b3ca55139d1ba20dd2b390ab335ec Mon Sep 17 00:00:00 2001 From: Duncan Tourolle Date: Sat, 8 Nov 2025 19:39:10 +0100 Subject: [PATCH] ADDITIOANL TEST --- tests/io_tests/test_html_link_end_to_end.py | 163 ++++++++++++++ .../io_tests/test_html_link_interactivity.py | 205 ++++++++++++++++++ 2 files changed, 368 insertions(+) create mode 100644 tests/io_tests/test_html_link_end_to_end.py create mode 100644 tests/io_tests/test_html_link_interactivity.py diff --git a/tests/io_tests/test_html_link_end_to_end.py b/tests/io_tests/test_html_link_end_to_end.py new file mode 100644 index 0000000..91bf079 --- /dev/null +++ b/tests/io_tests/test_html_link_end_to_end.py @@ -0,0 +1,163 @@ +""" +End-to-end test for HTML link interactivity with query_point. + +This test verifies the complete flow: +1. HTML with links -> parse_html_string -> LinkedWord objects +2. LinkedWord objects -> DocumentLayouter -> Rendered page +3. Rendered page -> query_point -> Interactive link detection +""" + +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.layout.document_layouter import DocumentLayouter +from pyWebLayout.abstract.inline import LinkedWord + + +class TestHTMLLinkEndToEnd(unittest.TestCase): + """Test complete HTML link workflow from parsing to interaction.""" + + def test_complete_link_workflow(self): + """Test the complete workflow: HTML -> parsing -> layout -> query.""" + # Step 1: Create HTML with a link + html = '

Click this link to test.

' + + # Step 2: Parse HTML to blocks + blocks = parse_html_string(html) + self.assertEqual(len(blocks), 1) + + # Step 3: Verify LinkedWord was created + paragraph = blocks[0] + linked_words = [w for w in paragraph.words if isinstance(w, LinkedWord)] + self.assertEqual(len(linked_words), 2) # "this" and "link" + + # Verify link properties + for word in linked_words: + self.assertEqual(word.location, "action:test_action") + self.assertIn(word.text, ["this", "link"]) + + # Step 4: Layout on a page + page_style = PageStyle() + page = Page((400, 200), page_style) + layouter = DocumentLayouter(page) + layouter.layout_document([paragraph]) + + # Step 5: Render the page + rendered = page.render() + self.assertIsNotNone(rendered) + + # Step 6: Test query_point functionality + # The query system should be able to detect interactive elements + # Note: Exact coordinates depend on layout, so we test the capability + # rather than specific pixel positions + + # Try to query various points on the page + # The page should respond to queries (even if we don't hit the exact link) + result = page.query_point((100, 50)) + # Result might be None if we didn't hit any text, but the API should work + # The key is that if we DID hit a link, it would be detected + + print(f"Query result: {result}") + if result and result.is_interactive: + # If we hit an interactive element, it should have link info + self.assertIsNotNone(result.link_target) + print(f"Found interactive link: {result.link_target}") + + def test_settings_overlay_complete_workflow(self): + """Test the complete workflow with actual settings overlay HTML.""" + # This is the exact HTML pattern used for settings overlays + html = ''' +
+

Settings

+

+ Back to Library +

+

+ Font Size: + [-] + [+] +

+
+ ''' + + # Parse HTML + blocks = parse_html_string(html) + + # Collect 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) + + # Check for specific actions + actions = {word.location for word in all_linked_words} + self.assertIn("action:back_to_library", actions) + self.assertIn("setting:font_decrease", actions) + self.assertIn("setting:font_increase", actions) + + # Layout and render + page_style = PageStyle(padding=(10, 10, 10, 10)) + page = Page((400, 300), page_style) + layouter = DocumentLayouter(page) + + for block in blocks: + layouter.layout_document([block]) + + rendered = page.render() + self.assertIsNotNone(rendered) + + print(f"\nSettings overlay test:") + print(f" Found {len(all_linked_words)} linked words") + print(f" Actions: {actions}") + print(f" Rendered: {rendered.size}") + + # The links are successfully created and rendered! + # In a real application, query_point would be used to detect clicks on these links + + def test_link_metadata_preserved(self): + """Test that link metadata (title, type) is preserved through the workflow.""" + html = ''' +

+ External + Internal + API +

+ ''' + + blocks = parse_html_string(html) + paragraph = blocks[0] + linked_words = [w for w in paragraph.words if isinstance(w, LinkedWord)] + + # Should have 3 LinkedWords (one for each link) + self.assertEqual(len(linked_words), 3) + + # Check that link metadata is preserved + links_by_text = {w.text: w for w in linked_words} + + # External link + external = links_by_text.get("External") + self.assertIsNotNone(external) + self.assertEqual(external.location, "https://example.com") + self.assertEqual(external.link_title, "Example Site") + + # Internal link + internal = links_by_text.get("Internal") + self.assertIsNotNone(internal) + self.assertEqual(internal.location, "#section2") + self.assertEqual(internal.link_title, "Go to Section 2") + + # API link + api = links_by_text.get("API") + self.assertIsNotNone(api) + self.assertTrue(api.location.startswith("javascript:")) + self.assertEqual(api.link_title, "Alert") + + +if __name__ == '__main__': + unittest.main() diff --git a/tests/io_tests/test_html_link_interactivity.py b/tests/io_tests/test_html_link_interactivity.py new file mode 100644 index 0000000..f41f52d --- /dev/null +++ b/tests/io_tests/test_html_link_interactivity.py @@ -0,0 +1,205 @@ +""" +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 = ''' +
+

Click here to go back.

+

Or visit this setting.

+
+ ''' + + 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 = ''' +
+

Settings

+

Font Size: - | +

+

Back to Library

+
+ ''' + + 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 = ''' +

Click here to test.

+ ''' + + 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 = ''' +
+

Settings

+

+ ◄ Back to Library +

+

+ Font Size: 100%
+ [-] + [+] +

+
+ ''' + + 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()