end2end table layouter
Python CI / test (push) Successful in 6m35s

This commit is contained in:
2025-11-07 21:13:01 +01:00
parent 5fe4db4cbe
commit 496f3bf334
5 changed files with 859 additions and 17 deletions
+176 -1
View File
@@ -9,9 +9,11 @@ import pytest
from unittest.mock import Mock, MagicMock, patch
from typing import List, Optional
from pyWebLayout.layout.document_layouter import paragraph_layouter, DocumentLayouter
from pyWebLayout.layout.document_layouter import paragraph_layouter, table_layouter, DocumentLayouter
from pyWebLayout.style.abstract_style import AbstractStyle
from pyWebLayout.style.concrete_style import ConcreteStyle, StyleResolver, RenderingContext
from pyWebLayout.abstract.block import Table, TableRow, TableCell
from pyWebLayout.concrete.table import TableStyle
class TestDocumentLayouter:
@@ -577,6 +579,179 @@ class TestMultiPageLayout:
print(f" - Word spacing constraints: {concrete_style.word_spacing_min}-{concrete_style.word_spacing_max}px")
class TestTableLayouter:
"""Test cases for table layouter functionality."""
def setup_method(self):
"""Set up test fixtures before each test method."""
# Create mock page
self.mock_page = Mock()
self.mock_page.border_size = 20
self.mock_page._current_y_offset = 50
self.mock_page.available_width = 600
self.mock_page.size = (800, 1000)
# Create mock draw and canvas
self.mock_draw = Mock()
self.mock_canvas = Mock()
self.mock_page.draw = self.mock_draw
self.mock_page._canvas = self.mock_canvas
# Create mock table
self.mock_table = Mock(spec=Table)
# Create mock style resolver
self.mock_style_resolver = Mock()
self.mock_page.style_resolver = self.mock_style_resolver
@patch('pyWebLayout.layout.document_layouter.TableRenderer')
def test_table_layouter_success(self, mock_table_renderer_class):
"""Test table_layouter with successful table rendering."""
# Setup mock renderer
mock_renderer = Mock()
mock_table_renderer_class.return_value = mock_renderer
mock_renderer.size = (500, 200) # Table fits on page
# Call function
result = table_layouter(self.mock_table, self.mock_page)
# Verify results
assert result is True
# Verify TableRenderer was created with correct parameters
mock_table_renderer_class.assert_called_once_with(
table=self.mock_table,
origin=(20, 50), # (border_size, current_y_offset)
available_width=600,
draw=self.mock_draw,
style=None,
canvas=self.mock_canvas
)
# Verify render was called
mock_renderer.render.assert_called_once()
# Verify y_offset was updated
assert self.mock_page._current_y_offset == 250 # 50 + 200
@patch('pyWebLayout.layout.document_layouter.TableRenderer')
def test_table_layouter_with_custom_style(self, mock_table_renderer_class):
"""Test table_layouter with custom TableStyle."""
# Create custom style
custom_style = TableStyle(
border_width=2,
border_color=(100, 100, 100),
cell_padding=(10, 10, 10, 10)
)
# Setup mock renderer
mock_renderer = Mock()
mock_table_renderer_class.return_value = mock_renderer
mock_renderer.size = (500, 150)
# Call function with style
result = table_layouter(self.mock_table, self.mock_page, style=custom_style)
# Verify TableRenderer was created with custom style
assert result is True
call_args = mock_table_renderer_class.call_args
assert call_args[1]['style'] == custom_style
@patch('pyWebLayout.layout.document_layouter.TableRenderer')
def test_table_layouter_table_too_large(self, mock_table_renderer_class):
"""Test table_layouter when table doesn't fit on page."""
# Setup mock renderer with table larger than available space
mock_renderer = Mock()
mock_table_renderer_class.return_value = mock_renderer
mock_renderer.size = (500, 1000) # Table height exceeds available space
# Available height = page_size[1] - y_offset - border_size
# = 1000 - 50 - 20 = 930, but table is 1000 pixels tall
# Call function
result = table_layouter(self.mock_table, self.mock_page)
# Verify failure
assert result is False
# Verify render was NOT called
mock_renderer.render.assert_not_called()
# Verify y_offset was NOT updated
assert self.mock_page._current_y_offset == 50
@patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry')
def test_document_layouter_layout_table(self, mock_style_registry_class):
"""Test DocumentLayouter.layout_table method."""
# Setup mocks
mock_style_registry = Mock()
mock_style_registry_class.return_value = mock_style_registry
layouter = DocumentLayouter(self.mock_page)
# Mock the table_layouter function
with patch('pyWebLayout.layout.document_layouter.table_layouter') as mock_table_layouter:
mock_table_layouter.return_value = True
custom_style = TableStyle(border_width=1)
result = layouter.layout_table(self.mock_table, style=custom_style)
# Verify the function was called correctly
mock_table_layouter.assert_called_once_with(
self.mock_table, self.mock_page, custom_style
)
assert result is True
def test_document_layouter_layout_document_with_table(self):
"""Test DocumentLayouter.layout_document with Table elements."""
from pyWebLayout.abstract import Paragraph
from pyWebLayout.abstract.block import Image as AbstractImage
# Create mixed elements
elements = [
Mock(spec=Paragraph),
Mock(spec=Table),
Mock(spec=AbstractImage),
Mock(spec=Table)
]
with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
layouter = DocumentLayouter(self.mock_page)
# Mock the layout methods
layouter.layout_paragraph = Mock(return_value=(True, None, None))
layouter.layout_table = Mock(return_value=True)
layouter.layout_image = Mock(return_value=True)
result = layouter.layout_document(elements)
# Verify all elements were laid out
assert result is True
assert layouter.layout_paragraph.call_count == 1
assert layouter.layout_table.call_count == 2
assert layouter.layout_image.call_count == 1
def test_document_layouter_layout_document_table_failure(self):
"""Test DocumentLayouter.layout_document when table layout fails."""
# Create elements with table that will fail
elements = [
Mock(spec=Table),
Mock(spec=Table)
]
with patch('pyWebLayout.layout.document_layouter.ConcreteStyleRegistry'):
layouter = DocumentLayouter(self.mock_page)
# Mock layout_table: first succeeds, second fails
layouter.layout_table = Mock(side_effect=[True, False])
result = layouter.layout_document(elements)
# Verify it stopped after failure
assert result is False
assert layouter.layout_table.call_count == 2
if __name__ == "__main__":
# Run specific tests for debugging
test = TestDocumentLayouter()