mirror of
https://github.com/Hopiu/bowser.git
synced 2026-03-16 19:10:24 +00:00
- Removed unused imports from various test files to streamline code. - Cleaned up whitespace in test cases for better consistency. - Updated dependency management in `uv.lock` to reflect changes in optional dependencies. - Ensured all tests maintain functionality while enhancing clarity and organization.
279 lines
8.4 KiB
Python
279 lines
8.4 KiB
Python
# ruff: noqa: E402
|
|
"""Tests for layout components."""
|
|
|
|
import pytest
|
|
import sys
|
|
from unittest.mock import MagicMock
|
|
|
|
# Mock skia before importing layout modules
|
|
mock_skia = MagicMock()
|
|
mock_font = MagicMock()
|
|
mock_font.measureText = lambda text: len(text) * 7.0 # ~7 pixels per char
|
|
mock_typeface = MagicMock()
|
|
mock_skia.Typeface.MakeDefault.return_value = mock_typeface
|
|
mock_skia.Font.return_value = mock_font
|
|
sys.modules['skia'] = mock_skia
|
|
|
|
from src.layout.document import DocumentLayout, LayoutLine, LayoutBlock
|
|
from src.layout.block import BlockLayout, LineLayout, build_block_layout
|
|
from src.layout.inline import TextLayout, InlineLayout
|
|
from src.parser.html import Element, Text
|
|
|
|
|
|
class TestLayoutLine:
|
|
"""Tests for LayoutLine class."""
|
|
|
|
def test_layout_line_creation(self):
|
|
line = LayoutLine("Hello", 20, 100, 14)
|
|
assert line.text == "Hello"
|
|
assert line.x == 20
|
|
assert line.y == 100
|
|
assert line.font_size == 14
|
|
|
|
def test_layout_line_with_char_positions(self):
|
|
char_positions = [0.0, 5.0, 10.0, 15.0, 20.0, 25.0]
|
|
line = LayoutLine("Hello", 20, 100, 14, char_positions)
|
|
assert line.char_positions == char_positions
|
|
|
|
def test_layout_line_height(self):
|
|
line = LayoutLine("Test", 0, 0, 14)
|
|
# Height should be based on linespace (font_size * 1.4)
|
|
assert line.height == pytest.approx(14 * 1.4, rel=0.1)
|
|
|
|
|
|
class TestLayoutBlock:
|
|
"""Tests for LayoutBlock class."""
|
|
|
|
def test_layout_block_creation(self):
|
|
block = LayoutBlock("p")
|
|
assert block.tag == "p"
|
|
assert block.block_type == "block"
|
|
assert block.lines == []
|
|
|
|
def test_layout_block_with_type(self):
|
|
block = LayoutBlock("li", "list-item")
|
|
assert block.block_type == "list-item"
|
|
|
|
|
|
class TestDocumentLayout:
|
|
"""Tests for DocumentLayout class."""
|
|
|
|
def test_document_layout_creation(self):
|
|
node = Element("html")
|
|
layout = DocumentLayout(node)
|
|
assert layout.node is node
|
|
assert layout.blocks == []
|
|
assert layout.lines == []
|
|
|
|
def test_document_layout_finds_body(self):
|
|
# Create HTML structure: html > body > p
|
|
html = Element("html")
|
|
body = Element("body")
|
|
p = Element("p")
|
|
p.children = [Text("Hello world")]
|
|
body.children = [p]
|
|
html.children = [body]
|
|
|
|
layout = DocumentLayout(html)
|
|
lines = layout.layout(800)
|
|
|
|
assert len(lines) > 0
|
|
assert any("Hello world" in line.text for line in lines)
|
|
|
|
def test_document_layout_returns_empty_without_body(self):
|
|
node = Element("div")
|
|
node.children = []
|
|
layout = DocumentLayout(node)
|
|
lines = layout.layout(800)
|
|
assert lines == []
|
|
|
|
def test_document_layout_handles_headings(self):
|
|
body = Element("body")
|
|
h1 = Element("h1")
|
|
h1.children = [Text("Title")]
|
|
body.children = [h1]
|
|
|
|
layout = DocumentLayout(body)
|
|
lines = layout.layout(800)
|
|
|
|
assert len(lines) == 1
|
|
assert lines[0].text == "Title"
|
|
assert lines[0].font_size == 24 # h1 font size
|
|
|
|
def test_document_layout_handles_paragraphs(self):
|
|
body = Element("body")
|
|
p = Element("p")
|
|
p.children = [Text("Paragraph text")]
|
|
body.children = [p]
|
|
|
|
layout = DocumentLayout(body)
|
|
lines = layout.layout(800)
|
|
|
|
assert len(lines) == 1
|
|
assert lines[0].text == "Paragraph text"
|
|
assert lines[0].font_size == 14 # p font size
|
|
|
|
def test_document_layout_handles_lists(self):
|
|
body = Element("body")
|
|
ul = Element("ul")
|
|
li = Element("li")
|
|
li.children = [Text("List item")]
|
|
ul.children = [li]
|
|
body.children = [ul]
|
|
|
|
layout = DocumentLayout(body)
|
|
lines = layout.layout(800)
|
|
|
|
assert len(lines) == 1
|
|
assert "•" in lines[0].text # Bullet prefix
|
|
assert "List item" in lines[0].text
|
|
|
|
def test_document_layout_word_wrapping(self):
|
|
body = Element("body")
|
|
p = Element("p")
|
|
# Long text that should wrap
|
|
p.children = [
|
|
Text(
|
|
"This is a very long paragraph that should wrap to multiple lines "
|
|
"when the width is narrow enough"
|
|
)
|
|
]
|
|
body.children = [p]
|
|
|
|
layout = DocumentLayout(body)
|
|
lines = layout.layout(200) # Narrow width to force wrapping
|
|
|
|
assert len(lines) > 1 # Should wrap to multiple lines
|
|
|
|
def test_document_layout_char_positions(self):
|
|
body = Element("body")
|
|
p = Element("p")
|
|
p.children = [Text("Hello")]
|
|
body.children = [p]
|
|
|
|
layout = DocumentLayout(body)
|
|
lines = layout.layout(800)
|
|
|
|
assert len(lines) == 1
|
|
# char_positions should have len(text) + 1 entries (including start at 0)
|
|
assert len(lines[0].char_positions) == 6 # "Hello" = 5 chars + 1
|
|
assert lines[0].char_positions[0] == 0.0
|
|
|
|
|
|
class TestBlockLayout:
|
|
"""Tests for BlockLayout class."""
|
|
|
|
def test_block_layout_creation(self):
|
|
node = Element("div")
|
|
layout = BlockLayout(node)
|
|
assert layout.node is node
|
|
assert layout.children == []
|
|
|
|
def test_block_layout_with_parent(self):
|
|
parent_node = Element("body")
|
|
child_node = Element("div")
|
|
parent_layout = BlockLayout(parent_node)
|
|
child_layout = BlockLayout(child_node, parent=parent_layout)
|
|
assert child_layout.parent is parent_layout
|
|
|
|
def test_block_layout_stores_dimensions(self):
|
|
node = Element("div")
|
|
layout = BlockLayout(node)
|
|
layout.x = 10
|
|
layout.y = 20
|
|
layout.width = 100
|
|
layout.height = 50
|
|
assert layout.x == 10
|
|
assert layout.y == 20
|
|
assert layout.width == 100
|
|
assert layout.height == 50
|
|
|
|
|
|
class TestLineLayout:
|
|
"""Tests for LineLayout class."""
|
|
|
|
def test_line_layout_creation(self):
|
|
layout = LineLayout()
|
|
assert layout.words == []
|
|
assert layout.x == 0
|
|
assert layout.y == 0
|
|
|
|
def test_line_layout_add_word(self):
|
|
layout = LineLayout()
|
|
layout.add_word("Hello", 0, 14)
|
|
layout.add_word("World", 50, 14)
|
|
assert len(layout.words) == 2
|
|
|
|
|
|
class TestTextLayout:
|
|
"""Tests for TextLayout class."""
|
|
|
|
def test_text_layout_creation(self):
|
|
node = Text("Hello")
|
|
layout = TextLayout(node, "Hello")
|
|
assert layout.node is node
|
|
assert layout.word == "Hello"
|
|
|
|
def test_text_layout_layout_returns_width(self):
|
|
node = Text("Test")
|
|
layout = TextLayout(node, "Test")
|
|
width = layout.layout(14)
|
|
assert width > 0
|
|
|
|
def test_text_layout_dimensions(self):
|
|
node = Text("Hi")
|
|
layout = TextLayout(node, "Hi")
|
|
layout.x = 5
|
|
layout.y = 10
|
|
assert layout.x == 5
|
|
assert layout.y == 10
|
|
|
|
|
|
class TestInlineLayout:
|
|
"""Tests for InlineLayout class."""
|
|
|
|
def test_inline_layout_creation(self):
|
|
node = Text("Hello")
|
|
layout = InlineLayout(node)
|
|
assert layout.node is node
|
|
assert layout.children == []
|
|
|
|
def test_inline_layout_add_word(self):
|
|
node = Text("Hello World")
|
|
layout = InlineLayout(node)
|
|
layout.add_word("Hello", 14)
|
|
layout.add_word("World", 14)
|
|
assert len(layout.children) == 2
|
|
|
|
def test_inline_layout_layout(self):
|
|
node = Text("Hello World")
|
|
layout = InlineLayout(node)
|
|
layout.add_word("Hello", 14)
|
|
layout.add_word("World", 14)
|
|
lines = layout.layout(0, 0, 1000, 14)
|
|
# Both words should fit on one line with wide width
|
|
assert len(lines) == 1
|
|
|
|
|
|
class TestBuildBlockLayout:
|
|
"""Tests for build_block_layout factory function."""
|
|
|
|
def test_build_block_layout_from_element(self):
|
|
node = Element("p")
|
|
node.children = [Text("Test paragraph")]
|
|
result = build_block_layout(node)
|
|
assert result is not None
|
|
assert result.node is node
|
|
assert result.font_size == 14
|
|
|
|
def test_build_block_layout_heading(self):
|
|
node = Element("h1")
|
|
node.children = [Text("Heading")]
|
|
result = build_block_layout(node, font_size=24)
|
|
assert result.font_size == 24
|
|
|
|
def test_build_block_layout_list_item(self):
|
|
node = Element("li")
|
|
node.children = [Text("Item")]
|
|
result = build_block_layout(node, block_type="list-item", bullet=True)
|
|
assert result.block_type == "list-item"
|