""" Unit tests for DynamicPage class. """ import pytest from PIL import Image from pyWebLayout.concrete.dynamic_page import DynamicPage, SizeConstraints from pyWebLayout.concrete.text import Line, Text from pyWebLayout.style.fonts import Font from pyWebLayout.style.page_style import PageStyle from pyWebLayout.style import Alignment class TestSizeConstraints: """Test SizeConstraints dataclass.""" def test_default_constraints(self): """Test default constraint values.""" constraints = SizeConstraints() assert constraints.min_width is None assert constraints.max_width is None assert constraints.min_height is None assert constraints.max_height is None def test_custom_constraints(self): """Test custom constraint values.""" constraints = SizeConstraints( min_width=100, max_width=500, min_height=50, max_height=1000 ) assert constraints.min_width == 100 assert constraints.max_width == 500 assert constraints.min_height == 50 assert constraints.max_height == 1000 class TestDynamicPage: """Test DynamicPage class.""" def test_initialization(self): """Test DynamicPage initialization.""" page = DynamicPage() assert page.size == (0, 0) # Starts with zero size assert not page._is_measured assert not page._is_laid_out assert page._render_offset == 0 assert page.constraints is not None def test_initialization_with_constraints(self): """Test initialization with custom constraints.""" constraints = SizeConstraints(min_width=200, max_width=800) page = DynamicPage(constraints=constraints) assert page.constraints.min_width == 200 assert page.constraints.max_width == 800 def test_initialization_with_style(self): """Test initialization with custom style.""" style = PageStyle(border_width=2, padding=(10, 20, 10, 20)) page = DynamicPage(style=style) assert page.style.border_width == 2 assert page.style.padding_top == 10 def test_measure_empty_page(self): """Test measuring an empty page.""" page = DynamicPage() width, height = page.measure() # Empty page should have minimal size (just padding/borders) assert width > 0 # At least padding/borders assert height > 0 assert page._is_measured def test_measure_with_constraints(self): """Test measuring respects constraints.""" constraints = SizeConstraints(min_width=300, min_height=200) page = DynamicPage(constraints=constraints) width, height = page.measure() assert width >= 300 assert height >= 200 def test_measure_caching(self): """Test that measurement is cached.""" page = DynamicPage() # First measurement size1 = page.measure() # Second measurement should return cached value size2 = page.measure() assert size1 == size2 assert page._is_measured def test_get_min_width(self): """Test get_min_width.""" page = DynamicPage() min_width = page.get_min_width() assert min_width > 0 assert isinstance(min_width, int) def test_get_preferred_width(self): """Test get_preferred_width.""" page = DynamicPage() pref_width = page.get_preferred_width() assert pref_width > 0 assert isinstance(pref_width, int) def test_measure_content_height(self): """Test measure_content_height.""" page = DynamicPage() content_height = page.measure_content_height() assert content_height > 0 assert isinstance(content_height, int) def test_layout(self): """Test layout method.""" page = DynamicPage() target_size = (400, 600) page.layout(target_size) assert page.size == target_size assert page._is_laid_out assert page._dirty # Should be marked for re-render def test_render_without_layout(self): """Test rendering without explicit layout (auto-sizing).""" page = DynamicPage() image = page.render() assert isinstance(image, Image.Image) assert image.size[0] > 0 assert image.size[1] > 0 def test_render_with_layout(self): """Test rendering after explicit layout.""" page = DynamicPage() page.layout((500, 700)) image = page.render() assert isinstance(image, Image.Image) assert image.size == (500, 700) def test_add_child_invalidates_cache(self): """Test that adding a child invalidates measurement caches.""" page = DynamicPage() # Measure to populate cache page.measure() assert page._is_measured # Add a child (mock renderable) class MockRenderable: def __init__(self): self.size = (100, 50) self._origin = (0, 0) @property def origin(self): return self._origin def render(self): pass page.add_child(MockRenderable()) # Caches should be invalidated assert not page._is_measured assert page._intrinsic_size is None def test_clear_children_invalidates_cache(self): """Test that clearing children invalidates caches.""" page = DynamicPage() # Measure to populate cache page.measure() assert page._is_measured # Clear children page.clear_children() # Caches should be invalidated assert not page._is_measured def test_pagination_reset(self): """Test pagination reset.""" page = DynamicPage() page._render_offset = 100 page.reset_pagination() assert page._render_offset == 0 def test_has_more_content_false(self): """Test has_more_content when all content is rendered.""" page = DynamicPage() # Set render offset to total height total_height = page.measure_content_height() page._render_offset = total_height assert not page.has_more_content() def test_has_more_content_true(self): """Test has_more_content when content remains.""" page = DynamicPage() # Offset is less than total page._render_offset = 0 assert page.has_more_content() def test_min_width_measurement(self): """Test min width measures longest word.""" page = DynamicPage() # Min width should be at least padding/borders min_width = page.get_min_width() assert min_width > 0 def test_invalidate_caches(self): """Test cache invalidation.""" page = DynamicPage() # Populate caches page.measure() page.get_min_width() page.get_preferred_width() page.measure_content_height() assert page._is_measured assert page._intrinsic_size is not None assert page._min_width_cache is not None assert page._preferred_width_cache is not None assert page._content_height_cache is not None # Invalidate page.invalidate_caches() assert not page._is_measured assert page._intrinsic_size is None assert page._min_width_cache is None assert page._preferred_width_cache is None assert page._content_height_cache is None assert not page._is_laid_out def test_measure_with_available_width(self): """Test measurement with available_width constraint.""" page = DynamicPage() width, height = page.measure(available_width=300) # Width should respect available_width assert width <= 300 def test_constraints_override_available_width(self): """Test that constraints override available_width.""" constraints = SizeConstraints(min_width=400) page = DynamicPage(constraints=constraints) width, height = page.measure(available_width=300) # Should use min_width constraint, not available_width assert width >= 400 def test_render_partial_empty_page(self): """Test partial rendering on empty page.""" page = DynamicPage() rendered = page.render_partial(available_height=100) assert rendered >= 0 assert isinstance(rendered, int) def test_method_chaining_add_child(self): """Test that add_child returns self for chaining.""" page = DynamicPage() class MockRenderable: def __init__(self): self.size = (50, 50) self._origin = (0, 0) @property def origin(self): return self._origin result = page.add_child(MockRenderable()) assert result is page def test_method_chaining_clear_children(self): """Test that clear_children returns self for chaining.""" page = DynamicPage() result = page.clear_children() assert result is page if __name__ == '__main__': pytest.main([__file__, '-v'])