Some clean up and added interactable images.
Python CI / test (push) Successful in 6m34s

This commit is contained in:
2025-11-07 22:56:35 +01:00
parent 15305011dc
commit 49d4e551f8
7 changed files with 391 additions and 58 deletions
+12 -12
View File
@@ -88,11 +88,11 @@ class TestLink(unittest.TestCase):
callback=self.mock_callback,
params=params
)
result = link.execute()
# Should call callback with location and params
self.mock_callback.assert_called_once_with("/api/save", action="save", id=123)
# Should call callback with location, point (None when not provided), and params
self.mock_callback.assert_called_once_with("/api/save", None, action="save", id=123)
self.assertEqual(result, "callback_result")
def test_function_link_execution(self):
@@ -104,11 +104,11 @@ class TestLink(unittest.TestCase):
callback=self.mock_callback,
params=params
)
result = link.execute()
# Should call callback with location and params
self.mock_callback.assert_called_once_with("save_document", data="test")
# Should call callback with location, point (None when not provided), and params
self.mock_callback.assert_called_once_with("save_document", None, data="test")
self.assertEqual(result, "callback_result")
def test_api_link_without_callback(self):
@@ -204,11 +204,11 @@ class TestButton(unittest.TestCase):
"""Test executing enabled button."""
params = {"data": "test_data"}
button = Button("Test", self.mock_callback, params=params, enabled=True)
result = button.execute()
# Should call callback with params
self.mock_callback.assert_called_once_with(data="test_data")
# Should call callback with point (None when not provided) and params
self.mock_callback.assert_called_once_with(None, data="test_data")
self.assertEqual(result, "button_clicked")
def test_button_execute_disabled(self):
+18 -9
View File
@@ -442,28 +442,37 @@ class TestInteractionCallbacks(unittest.TestCase):
self.font = Font(font_size=12, colour=(0, 0, 0))
self.mock_draw = Mock()
self.callback_result = "callback_executed"
self.callback = Mock(return_value=self.callback_result)
# Link callback: receives (location, point, **params)
def link_callback(location, point, **params):
return "callback_executed"
self.link_callback = link_callback
# Button callback: receives (point, **params)
def button_callback(point, **params):
return "callback_executed"
self.button_callback = button_callback
def test_link_text_interaction(self):
"""Test that LinkText properly handles interaction"""
# Use a FUNCTION link type which calls the callback, not INTERNAL which returns location
link = Link("test_function", LinkType.FUNCTION, self.callback)
link = Link("test_function", LinkType.FUNCTION, self.link_callback)
renderable = LinkText(link, "Test Link", self.font, self.mock_draw)
# Simulate interaction
result = renderable.interact(np.array([10, 10]))
# Should execute the link's callback
self.assertEqual(result, self.callback_result)
def test_button_text_interaction(self):
"""Test that ButtonText properly handles interaction"""
button = Button("Test Button", self.callback)
button = Button("Test Button", self.button_callback)
renderable = ButtonText(button, self.font, self.mock_draw)
# Simulate interaction
result = renderable.interact(np.array([10, 10]))
# Should execute the button's callback
self.assertEqual(result, self.callback_result)
+191
View File
@@ -0,0 +1,191 @@
"""
Unit tests for InteractiveImage functionality.
These tests verify that InteractiveImage can properly detect taps
and trigger callbacks when used in rendered content.
"""
import unittest
import tempfile
from pathlib import Path
from PIL import Image as PILImage
import numpy as np
from pyWebLayout.abstract.interactive_image import InteractiveImage
class TestInteractiveImage(unittest.TestCase):
"""Test InteractiveImage interaction and bounds detection"""
def setUp(self):
"""Create a temporary test image"""
self.temp_dir = tempfile.mkdtemp()
self.test_image_path = Path(self.temp_dir) / "test.png"
# Create a simple test image
img = PILImage.new('RGB', (100, 100), color='red')
img.save(self.test_image_path)
def test_create_interactive_image(self):
"""Test that InteractiveImage can be created"""
callback_called = []
def callback(point):
callback_called.append(point)
return "clicked!"
img = InteractiveImage(
source=str(self.test_image_path),
alt_text="Test Image",
callback=callback
)
self.assertIsNotNone(img)
self.assertEqual(img._source, str(self.test_image_path))
self.assertEqual(img._alt_text, "Test Image")
self.assertIsNotNone(img._callback)
def test_interact_with_bounds_set(self):
"""Test that interaction works when bounds are properly set"""
callback_result = []
def callback(point):
callback_result.append("Book selected!")
return "/path/to/book.epub"
img = InteractiveImage(
source=str(self.test_image_path),
alt_text="Test Image",
width=100,
height=100,
callback=callback
)
# Simulate what a renderer would do: set the bounds
img.set_rendered_bounds(origin=(50, 50), size=(100, 100))
# Tap inside the image bounds
result = img.interact((75, 75))
# Should trigger callback and return result
self.assertEqual(result, "/path/to/book.epub")
self.assertEqual(len(callback_result), 1)
def test_interact_outside_bounds(self):
"""Test that interaction fails when point is outside bounds"""
callback_called = []
def callback(point):
callback_called.append(True)
return "clicked!"
img = InteractiveImage(
source=str(self.test_image_path),
alt_text="Test Image",
width=100,
height=100,
callback=callback
)
# Set bounds: image at (50, 50) with size (100, 100)
img.set_rendered_bounds(origin=(50, 50), size=(100, 100))
# Tap outside the image bounds
result = img.interact((25, 25)) # Above and left of image
# Should NOT trigger callback
self.assertIsNone(result)
self.assertEqual(len(callback_called), 0)
def test_in_object_detection(self):
"""Test that in_object correctly detects points inside/outside bounds"""
img = InteractiveImage(
source=str(self.test_image_path),
width=100,
height=100
)
# Set bounds: image at (100, 200) with size (100, 100)
# So it occupies x: 100-200, y: 200-300
img.set_rendered_bounds(origin=(100, 200), size=(100, 100))
# Test points inside
self.assertTrue(img.in_object((100, 200))) # Top-left corner
self.assertTrue(img.in_object((150, 250))) # Center
self.assertTrue(img.in_object((199, 299))) # Bottom-right (just inside)
# Test points outside
self.assertFalse(img.in_object((99, 200))) # Just left
self.assertFalse(img.in_object((100, 199))) # Just above
self.assertFalse(img.in_object((200, 200))) # Just right
self.assertFalse(img.in_object((100, 300))) # Just below
self.assertFalse(img.in_object((50, 50))) # Far away
def test_create_and_add_to(self):
"""Test the convenience factory method"""
callback_result = []
def callback(point):
return "added!"
# Create a mock parent with children list
class MockParent:
def __init__(self):
self._children = []
parent = MockParent()
img = InteractiveImage.create_and_add_to(
parent,
source=str(self.test_image_path),
alt_text="Test",
callback=callback
)
# Should be added to parent's children
self.assertIn(img, parent._children)
self.assertIsInstance(img, InteractiveImage)
def test_no_callback_returns_none(self):
"""Test that interact returns None when no callback is set"""
img = InteractiveImage(
source=str(self.test_image_path),
width=100,
height=100,
callback=None # No callback
)
img.set_rendered_bounds(origin=(0, 0), size=(100, 100))
# Tap inside bounds
result = img.interact((50, 50))
# Should return None (no callback to call)
self.assertIsNone(result)
def test_multiple_images_independent_bounds(self):
"""Test that multiple InteractiveImages have independent bounds"""
def callback1(point):
return "image1"
def callback2(point):
return "image2"
img1 = InteractiveImage(source=str(self.test_image_path), width=50, height=50, callback=callback1)
img2 = InteractiveImage(source=str(self.test_image_path), width=50, height=50, callback=callback2)
# Set different bounds
img1.set_rendered_bounds(origin=(0, 0), size=(50, 50))
img2.set_rendered_bounds(origin=(100, 100), size=(50, 50))
# Tap in img1's bounds
self.assertEqual(img1.interact((25, 25)), "image1")
self.assertIsNone(img2.interact((25, 25)))
# Tap in img2's bounds
self.assertIsNone(img1.interact((125, 125)))
self.assertEqual(img2.interact((125, 125)), "image2")
if __name__ == '__main__':
unittest.main()