192 lines
6.1 KiB
Python
192 lines
6.1 KiB
Python
"""
|
|
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()
|