Large clean up
Python CI / test (push) Failing after 5m5s

This commit is contained in:
2025-06-28 21:10:30 +02:00
parent d0153c6397
commit 56a6ec19e8
60 changed files with 2016 additions and 12198 deletions
View File
+572
View File
@@ -0,0 +1,572 @@
"""
Unit tests for abstract block elements.
Tests the core abstract block classes that form the foundation of the document model.
"""
import unittest
import os
import tempfile
import shutil
import threading
import time
from PIL import Image as PILImage
from pyWebLayout.abstract.block import (
Block, BlockType, Paragraph, Heading, HeadingLevel, Quote, CodeBlock,
HList, ListStyle, ListItem, Table, TableRow, TableCell,
HorizontalRule, Image
)
from pyWebLayout.abstract.inline import Word, LineBreak
from pyWebLayout.style import Font
# Flask server for testing URL functionality
try:
from flask import Flask, send_file
FLASK_AVAILABLE = True
except ImportError:
FLASK_AVAILABLE = False
class TestBlockElements(unittest.TestCase):
"""Test cases for basic block elements."""
def test_paragraph_creation(self):
"""Test creating and using paragraphs."""
paragraph = Paragraph()
self.assertEqual(paragraph.block_type, BlockType.PARAGRAPH)
self.assertEqual(paragraph.word_count, 0)
self.assertIsNone(paragraph.parent)
# Add words
font = Font()
word1 = Word("Hello", font)
word2 = Word("World", font)
paragraph.add_word(word1)
paragraph.add_word(word2)
self.assertEqual(paragraph.word_count, 2)
# Test word iteration
words = list(paragraph.words())
self.assertEqual(len(words), 2)
self.assertEqual(words[0][1].text, "Hello")
self.assertEqual(words[1][1].text, "World")
def test_heading_levels(self):
"""Test heading creation with different levels."""
h1 = Heading(HeadingLevel.H1)
h3 = Heading(HeadingLevel.H3)
h6 = Heading(HeadingLevel.H6)
self.assertEqual(h1.level, HeadingLevel.H1)
self.assertEqual(h3.level, HeadingLevel.H3)
self.assertEqual(h6.level, HeadingLevel.H6)
self.assertEqual(h1.block_type, BlockType.HEADING)
# Test level modification
h1.level = HeadingLevel.H2
self.assertEqual(h1.level, HeadingLevel.H2)
def test_quote_nesting(self):
"""Test blockquote with nested content."""
quote = Quote()
# Add nested paragraphs
p1 = Paragraph()
p2 = Paragraph()
quote.add_block(p1)
quote.add_block(p2)
self.assertEqual(p1.parent, quote)
self.assertEqual(p2.parent, quote)
# Test block iteration
blocks = list(quote.blocks())
self.assertEqual(len(blocks), 2)
self.assertEqual(blocks[0], p1)
self.assertEqual(blocks[1], p2)
def test_code_block(self):
"""Test code block functionality."""
code = CodeBlock("python")
self.assertEqual(code.language, "python")
self.assertEqual(code.line_count, 0)
# Add code lines
code.add_line("def hello():")
code.add_line(" print('Hello!')")
self.assertEqual(code.line_count, 2)
# Test line iteration
lines = list(code.lines())
self.assertEqual(len(lines), 2)
self.assertEqual(lines[0][1], "def hello():")
self.assertEqual(lines[1][1], " print('Hello!')")
# Test language modification
code.language = "javascript"
self.assertEqual(code.language, "javascript")
def test_list_creation(self):
"""Test list creation and item management."""
# Unordered list
ul = HList(ListStyle.UNORDERED)
self.assertEqual(ul.style, ListStyle.UNORDERED)
self.assertEqual(ul.item_count, 0)
# Add list items
item1 = ListItem()
item2 = ListItem()
ul.add_item(item1)
ul.add_item(item2)
self.assertEqual(ul.item_count, 2)
self.assertEqual(item1.parent, ul)
self.assertEqual(item2.parent, ul)
# Test item iteration
items = list(ul.items())
self.assertEqual(len(items), 2)
# Test list style change
ul.style = ListStyle.ORDERED
self.assertEqual(ul.style, ListStyle.ORDERED)
def test_definition_list(self):
"""Test definition list with terms."""
dl = HList(ListStyle.DEFINITION)
# Add definition items with terms
dt1 = ListItem(term="Python")
dt2 = ListItem(term="JavaScript")
dl.add_item(dt1)
dl.add_item(dt2)
self.assertEqual(dt1.term, "Python")
self.assertEqual(dt2.term, "JavaScript")
# Test term modification
dt1.term = "Python 3"
self.assertEqual(dt1.term, "Python 3")
def test_table_structure(self):
"""Test table, row, and cell structure."""
table = Table(caption="Test Table")
self.assertEqual(table.caption, "Test Table")
self.assertEqual(table.row_count["total"], 0)
# Create rows and cells
header_row = TableRow()
data_row = TableRow()
# Header cells
h1 = TableCell(is_header=True)
h2 = TableCell(is_header=True)
header_row.add_cell(h1)
header_row.add_cell(h2)
# Data cells
d1 = TableCell(is_header=False)
d2 = TableCell(is_header=False, colspan=2)
data_row.add_cell(d1)
data_row.add_cell(d2)
# Add rows to table
table.add_row(header_row, "header")
table.add_row(data_row, "body")
# Test structure
self.assertEqual(table.row_count["header"], 1)
self.assertEqual(table.row_count["body"], 1)
self.assertEqual(table.row_count["total"], 2)
# Test cell properties
self.assertTrue(h1.is_header)
self.assertFalse(d1.is_header)
self.assertEqual(d2.colspan, 2)
self.assertEqual(d2.rowspan, 1) # Default
# Test row cell count
self.assertEqual(header_row.cell_count, 2)
self.assertEqual(data_row.cell_count, 2)
def test_table_sections(self):
"""Test table header, body, and footer sections."""
table = Table()
# Add rows to different sections
header = TableRow()
body1 = TableRow()
body2 = TableRow()
footer = TableRow()
table.add_row(header, "header")
table.add_row(body1, "body")
table.add_row(body2, "body")
table.add_row(footer, "footer")
# Test section iteration
header_rows = list(table.header_rows())
body_rows = list(table.body_rows())
footer_rows = list(table.footer_rows())
self.assertEqual(len(header_rows), 1)
self.assertEqual(len(body_rows), 2)
self.assertEqual(len(footer_rows), 1)
# Test all_rows iteration
all_rows = list(table.all_rows())
self.assertEqual(len(all_rows), 4)
# Check section labels
sections = [section for section, row in all_rows]
self.assertEqual(sections, ["header", "body", "body", "footer"])
def test_image_loading(self):
"""Test image element properties."""
# Test with basic properties
img = Image("test.jpg", "Test image", 100, 200)
self.assertEqual(img.source, "test.jpg")
self.assertEqual(img.alt_text, "Test image")
self.assertEqual(img.width, 100)
self.assertEqual(img.height, 200)
# Test property modification
img.source = "new.png"
img.alt_text = "New image"
img.width = 150
img.height = 300
self.assertEqual(img.source, "new.png")
self.assertEqual(img.alt_text, "New image")
self.assertEqual(img.width, 150)
self.assertEqual(img.height, 300)
# Test dimensions tuple
self.assertEqual(img.get_dimensions(), (150, 300))
def test_aspect_ratio_calculation(self):
"""Test image aspect ratio calculations."""
# Test with specified dimensions
img = Image("test.jpg", width=400, height=200)
self.assertEqual(img.get_aspect_ratio(), 2.0) # 400/200
# Test with only one dimension
img2 = Image("test.jpg", width=300)
self.assertIsNone(img2.get_aspect_ratio()) # No height specified
# Test scaled dimensions
scaled = img.calculate_scaled_dimensions(max_width=200, max_height=150)
# Should scale down proportionally
self.assertEqual(scaled[0], 200) # Width limited by max_width
self.assertEqual(scaled[1], 100) # Height scaled proportionally
def test_simple_elements(self):
"""Test simple block elements."""
hr = HorizontalRule()
br = LineBreak()
self.assertEqual(hr.block_type, BlockType.HORIZONTAL_RULE)
self.assertEqual(br.block_type, BlockType.LINE_BREAK)
# These elements have no additional properties
self.assertIsNone(hr.parent)
self.assertIsNone(br.parent)
class TestImagePIL(unittest.TestCase):
"""Test cases for Image class with PIL functionality."""
@classmethod
def setUpClass(cls):
"""Set up temporary directory and test images."""
cls.temp_dir = tempfile.mkdtemp()
cls.sample_image_path = "tests/data/sample_image.jpg"
# Create test images in different formats
cls._create_test_images()
# Start Flask server for URL testing if Flask is available
if FLASK_AVAILABLE:
cls._start_flask_server()
@classmethod
def tearDownClass(cls):
"""Clean up temporary directory and stop Flask server."""
shutil.rmtree(cls.temp_dir, ignore_errors=True)
if FLASK_AVAILABLE and hasattr(cls, 'flask_thread'):
cls.flask_server_running = False
cls.flask_thread.join(timeout=2)
@classmethod
def _create_test_images(cls):
"""Create test images in different formats."""
# Load the sample image
if os.path.exists(cls.sample_image_path):
with PILImage.open(cls.sample_image_path) as img:
cls.original_size = img.size
# Save in different formats
cls.jpg_path = os.path.join(cls.temp_dir, "test.jpg")
cls.png_path = os.path.join(cls.temp_dir, "test.png")
cls.bmp_path = os.path.join(cls.temp_dir, "test.bmp")
cls.gif_path = os.path.join(cls.temp_dir, "test.gif")
img.save(cls.jpg_path, "JPEG")
img.save(cls.png_path, "PNG")
img.save(cls.bmp_path, "BMP")
# Convert to RGB for GIF (GIF doesn't support transparency from RGBA)
rgb_img = img.convert("RGB")
rgb_img.save(cls.gif_path, "GIF")
else:
# Create a simple test image if sample doesn't exist
cls.original_size = (100, 100)
test_img = PILImage.new("RGB", cls.original_size, (255, 0, 0))
cls.jpg_path = os.path.join(cls.temp_dir, "test.jpg")
cls.png_path = os.path.join(cls.temp_dir, "test.png")
cls.bmp_path = os.path.join(cls.temp_dir, "test.bmp")
cls.gif_path = os.path.join(cls.temp_dir, "test.gif")
test_img.save(cls.jpg_path, "JPEG")
test_img.save(cls.png_path, "PNG")
test_img.save(cls.bmp_path, "BMP")
test_img.save(cls.gif_path, "GIF")
@classmethod
def _start_flask_server(cls):
"""Start a Flask server for URL testing."""
cls.flask_app = Flask(__name__)
cls.flask_port = 5555 # Use a specific port for testing
cls.flask_server_running = True
@cls.flask_app.route('/test.jpg')
def serve_test_image():
return send_file(cls.jpg_path, mimetype='image/jpeg')
def run_flask():
cls.flask_app.run(host='127.0.0.1', port=cls.flask_port, debug=False,
use_reloader=False, threaded=True)
cls.flask_thread = threading.Thread(target=run_flask, daemon=True)
cls.flask_thread.start()
# Wait for server to start
time.sleep(1)
def test_image_url_detection(self):
"""Test URL detection functionality."""
img = Image()
# Test URL detection
self.assertTrue(img._is_url("http://example.com/image.jpg"))
self.assertTrue(img._is_url("https://example.com/image.png"))
self.assertTrue(img._is_url("ftp://example.com/image.gif"))
# Test non-URL detection
self.assertFalse(img._is_url("image.jpg"))
self.assertFalse(img._is_url("/path/to/image.png"))
self.assertFalse(img._is_url("../relative/path.gif"))
self.assertFalse(img._is_url(""))
def test_load_local_image_jpg(self):
"""Test loading local JPG image."""
img = Image(self.jpg_path)
file_path, pil_img = img.load_image_data()
self.assertIsNotNone(pil_img)
self.assertEqual(file_path, self.jpg_path)
self.assertEqual(pil_img.size, self.original_size)
self.assertEqual(img.width, self.original_size[0])
self.assertEqual(img.height, self.original_size[1])
def test_load_local_image_png(self):
"""Test loading local PNG image."""
img = Image(self.png_path)
file_path, pil_img = img.load_image_data()
self.assertIsNotNone(pil_img)
self.assertEqual(file_path, self.png_path)
self.assertEqual(pil_img.size, self.original_size)
def test_load_local_image_bmp(self):
"""Test loading local BMP image."""
img = Image(self.bmp_path)
file_path, pil_img = img.load_image_data()
self.assertIsNotNone(pil_img)
self.assertEqual(file_path, self.bmp_path)
self.assertEqual(pil_img.size, self.original_size)
def test_load_local_image_gif(self):
"""Test loading local GIF image."""
img = Image(self.gif_path)
file_path, pil_img = img.load_image_data()
self.assertIsNotNone(pil_img)
self.assertEqual(file_path, self.gif_path)
self.assertEqual(pil_img.size, self.original_size)
def test_load_nonexistent_image(self):
"""Test loading non-existent image."""
img = Image("nonexistent.jpg")
file_path, pil_img = img.load_image_data()
self.assertIsNone(pil_img)
self.assertIsNone(file_path)
def test_load_empty_source(self):
"""Test loading with empty source."""
img = Image("")
file_path, pil_img = img.load_image_data()
self.assertIsNone(pil_img)
self.assertIsNone(file_path)
def test_auto_update_dimensions(self):
"""Test automatic dimension updating."""
img = Image(self.jpg_path, width=50, height=50) # Wrong initial dimensions
# Test with auto-update enabled (default)
file_path, pil_img = img.load_image_data(auto_update_dimensions=True)
self.assertEqual(img.width, self.original_size[0])
self.assertEqual(img.height, self.original_size[1])
def test_no_auto_update_dimensions(self):
"""Test loading without automatic dimension updating."""
original_width, original_height = 50, 50
img = Image(self.jpg_path, width=original_width, height=original_height)
# Test with auto-update disabled
file_path, pil_img = img.load_image_data(auto_update_dimensions=False)
self.assertEqual(img.width, original_width) # Should remain unchanged
self.assertEqual(img.height, original_height) # Should remain unchanged
def test_get_image_info(self):
"""Test getting detailed image information."""
img = Image(self.jpg_path)
info = img.get_image_info()
self.assertIsInstance(info, dict)
self.assertIn('format', info)
self.assertIn('mode', info)
self.assertIn('size', info)
self.assertIn('width', info)
self.assertIn('height', info)
self.assertEqual(info['size'], self.original_size)
self.assertEqual(info['width'], self.original_size[0])
self.assertEqual(info['height'], self.original_size[1])
def test_get_image_info_different_formats(self):
"""Test getting image info for different formats."""
formats_and_paths = [
('JPEG', self.jpg_path),
('PNG', self.png_path),
('BMP', self.bmp_path),
('GIF', self.gif_path),
]
for expected_format, path in formats_and_paths:
with self.subTest(format=expected_format):
img = Image(path)
info = img.get_image_info()
self.assertEqual(info['format'], expected_format)
self.assertEqual(info['size'], self.original_size)
def test_get_image_info_nonexistent(self):
"""Test getting image info for non-existent image."""
img = Image("nonexistent.jpg")
info = img.get_image_info()
self.assertEqual(info, {})
@unittest.skipUnless(FLASK_AVAILABLE, "Flask not available for URL testing")
def test_load_image_from_url(self):
"""Test loading image from URL."""
url = f"http://127.0.0.1:{self.flask_port}/test.jpg"
img = Image(url)
file_path, pil_img = img.load_image_data()
self.assertIsNotNone(pil_img)
self.assertIsNotNone(file_path)
self.assertTrue(file_path.endswith('.tmp')) # Should be a temp file
self.assertEqual(pil_img.size, self.original_size)
# Check that dimensions were updated
self.assertEqual(img.width, self.original_size[0])
self.assertEqual(img.height, self.original_size[1])
@unittest.skipUnless(FLASK_AVAILABLE, "Flask not available for URL testing")
def test_get_image_info_from_url(self):
"""Test getting image info from URL."""
url = f"http://127.0.0.1:{self.flask_port}/test.jpg"
img = Image(url)
info = img.get_image_info()
self.assertIsInstance(info, dict)
self.assertEqual(info['format'], 'JPEG')
self.assertEqual(info['size'], self.original_size)
def test_load_invalid_url(self):
"""Test loading from invalid URL."""
img = Image("http://nonexistent.domain/image.jpg")
file_path, pil_img = img.load_image_data()
self.assertIsNone(pil_img)
self.assertIsNone(file_path)
def test_multiple_loads_cleanup(self):
"""Test that multiple loads don't leave temp files."""
img = Image(self.jpg_path)
# Load multiple times
for _ in range(3):
file_path, pil_img = img.load_image_data()
self.assertIsNotNone(pil_img)
def test_original_sample_image(self):
"""Test loading the original sample image if it exists."""
if os.path.exists(self.sample_image_path):
img = Image(self.sample_image_path)
file_path, pil_img = img.load_image_data()
self.assertIsNotNone(pil_img)
self.assertEqual(file_path, self.sample_image_path)
# Test that we can get image info
info = img.get_image_info()
self.assertIsInstance(info, dict)
self.assertIn('format', info)
self.assertIn('size', info)
if __name__ == '__main__':
unittest.main()
+687
View File
@@ -0,0 +1,687 @@
"""
Unit tests for abstract document elements.
Tests the Document, Chapter, Book, and MetadataType classes that handle
document structure and metadata management.
"""
import unittest
from pyWebLayout.abstract.document import Document, Chapter, Book, MetadataType
from pyWebLayout.abstract.block import Paragraph, Heading, HeadingLevel, BlockType
from pyWebLayout.abstract.inline import Word, FormattedSpan
from pyWebLayout.style import Font, FontWeight, FontStyle, TextDecoration
class TestMetadataType(unittest.TestCase):
"""Test cases for MetadataType enum."""
def test_metadata_types(self):
"""Test that all expected metadata types exist."""
expected_types = [
'TITLE', 'AUTHOR', 'DESCRIPTION', 'KEYWORDS', 'LANGUAGE',
'PUBLICATION_DATE', 'MODIFIED_DATE', 'PUBLISHER', 'IDENTIFIER',
'COVER_IMAGE', 'CUSTOM'
]
for type_name in expected_types:
self.assertTrue(hasattr(MetadataType, type_name))
# Test custom type has expected value
self.assertEqual(MetadataType.CUSTOM.value, 100)
class TestDocument(unittest.TestCase):
"""Test cases for Document class."""
def setUp(self):
"""Set up test fixtures."""
self.doc = Document("Test Document", "en-US")
self.font = Font()
def test_document_creation(self):
"""Test document creation with basic parameters."""
self.assertEqual(self.doc.get_title(), "Test Document")
self.assertEqual(self.doc.get_metadata(MetadataType.LANGUAGE), "en-US")
self.assertEqual(len(self.doc.blocks), 0)
def test_document_creation_minimal(self):
"""Test document creation with minimal parameters."""
doc = Document()
self.assertIsNone(doc.get_title())
self.assertEqual(doc.get_metadata(MetadataType.LANGUAGE), "en-US")
def test_metadata_management(self):
"""Test setting and getting metadata."""
# Set various metadata types
self.doc.set_metadata(MetadataType.AUTHOR, "John Doe")
self.doc.set_metadata(MetadataType.DESCRIPTION, "A test document")
self.doc.set_metadata(MetadataType.KEYWORDS, ["test", "document"])
# Test retrieval
self.assertEqual(self.doc.get_metadata(MetadataType.AUTHOR), "John Doe")
self.assertEqual(self.doc.get_metadata(MetadataType.DESCRIPTION), "A test document")
self.assertEqual(self.doc.get_metadata(MetadataType.KEYWORDS), ["test", "document"])
# Test non-existent metadata
self.assertIsNone(self.doc.get_metadata(MetadataType.PUBLISHER))
def test_title_convenience_methods(self):
"""Test title getter and setter convenience methods."""
# Test setting title
self.doc.set_title("New Title")
self.assertEqual(self.doc.get_title(), "New Title")
# Test that it's also in metadata
self.assertEqual(self.doc.get_metadata(MetadataType.TITLE), "New Title")
def test_block_management(self):
"""Test adding and managing blocks."""
# Create some blocks
para1 = Paragraph()
para2 = Paragraph()
heading = Heading(HeadingLevel.H1)
# Add blocks
self.doc.add_block(para1)
self.doc.add_block(heading)
self.doc.add_block(para2)
# Test blocks list
self.assertEqual(len(self.doc.blocks), 3)
self.assertEqual(self.doc.blocks[0], para1)
self.assertEqual(self.doc.blocks[1], heading)
self.assertEqual(self.doc.blocks[2], para2)
def test_anchor_management(self):
"""Test named anchor functionality."""
heading = Heading(HeadingLevel.H1)
para = Paragraph()
# Add anchors
self.doc.add_anchor("intro", heading)
self.doc.add_anchor("content", para)
# Test retrieval
self.assertEqual(self.doc.get_anchor("intro"), heading)
self.assertEqual(self.doc.get_anchor("content"), para)
self.assertIsNone(self.doc.get_anchor("nonexistent"))
def test_resource_management(self):
"""Test document resource management."""
# Add various resources
self.doc.add_resource("image1", {"type": "image", "path": "test.jpg"})
self.doc.add_resource("style1", {"type": "css", "content": "body {}"})
# Test retrieval
image = self.doc.get_resource("image1")
self.assertEqual(image["type"], "image")
self.assertEqual(image["path"], "test.jpg")
style = self.doc.get_resource("style1")
self.assertEqual(style["type"], "css")
# Test non-existent resource
self.assertIsNone(self.doc.get_resource("nonexistent"))
def test_stylesheet_management(self):
"""Test stylesheet addition."""
# Add stylesheets
css1 = {"href": "style.css", "type": "text/css"}
css2 = {"href": "theme.css", "type": "text/css"}
self.doc.add_stylesheet(css1)
self.doc.add_stylesheet(css2)
# Test that stylesheets are stored
self.assertEqual(len(self.doc._stylesheets), 2)
self.assertEqual(self.doc._stylesheets[0], css1)
self.assertEqual(self.doc._stylesheets[1], css2)
def test_script_management(self):
"""Test script addition."""
# Add scripts
script1 = "console.log('Hello');"
script2 = "document.ready(function(){});"
self.doc.add_script(script1)
self.doc.add_script(script2)
# Test that scripts are stored
self.assertEqual(len(self.doc._scripts), 2)
self.assertEqual(self.doc._scripts[0], script1)
self.assertEqual(self.doc._scripts[1], script2)
def test_find_blocks_by_type(self):
"""Test finding blocks by type."""
# Create blocks of different types
para1 = Paragraph()
para2 = Paragraph()
heading1 = Heading(HeadingLevel.H1)
heading2 = Heading(HeadingLevel.H2)
# Add blocks to document
self.doc.add_block(para1)
self.doc.add_block(heading1)
self.doc.add_block(para2)
self.doc.add_block(heading2)
# Test finding paragraphs
paragraphs = self.doc.find_blocks_by_type(BlockType.PARAGRAPH)
self.assertEqual(len(paragraphs), 2)
self.assertIn(para1, paragraphs)
self.assertIn(para2, paragraphs)
# Test finding headings
headings = self.doc.find_blocks_by_type(BlockType.HEADING)
self.assertEqual(len(headings), 2)
self.assertIn(heading1, headings)
self.assertIn(heading2, headings)
def test_find_headings(self):
"""Test finding heading blocks specifically."""
# Create mixed blocks
para = Paragraph()
h1 = Heading(HeadingLevel.H1)
h2 = Heading(HeadingLevel.H2)
# Add words to headings for title extraction
word1 = Word("Chapter", self.font)
word2 = Word("One", self.font)
h1.add_word(word1)
h1.add_word(word2)
word3 = Word("Section", self.font)
h2.add_word(word3)
self.doc.add_block(para)
self.doc.add_block(h1)
self.doc.add_block(h2)
# Test finding headings
headings = self.doc.find_headings()
self.assertEqual(len(headings), 2)
self.assertIn(h1, headings)
self.assertIn(h2, headings)
self.assertNotIn(para, headings)
def test_generate_table_of_contents(self):
"""Test table of contents generation."""
# Create headings with content
h1 = Heading(HeadingLevel.H1)
h2 = Heading(HeadingLevel.H2)
h3 = Heading(HeadingLevel.H3)
# Add words to headings
h1.add_word(Word("Introduction", self.font))
h2.add_word(Word("Getting", self.font))
h2.add_word(Word("Started", self.font))
h3.add_word(Word("Installation", self.font))
self.doc.add_block(h1)
self.doc.add_block(h2)
self.doc.add_block(h3)
# Generate TOC
toc = self.doc.generate_table_of_contents()
# Test TOC structure
self.assertEqual(len(toc), 3)
# Test first entry
level, title, block = toc[0]
self.assertEqual(level, 1) # H1
self.assertEqual(title, "Introduction")
self.assertEqual(block, h1)
# Test second entry
level, title, block = toc[1]
self.assertEqual(level, 2) # H2
self.assertEqual(title, "Getting Started")
self.assertEqual(block, h2)
# Test third entry
level, title, block = toc[2]
self.assertEqual(level, 3) # H3
self.assertEqual(title, "Installation")
self.assertEqual(block, h3)
class TestChapter(unittest.TestCase):
"""Test cases for Chapter class."""
def setUp(self):
"""Set up test fixtures."""
self.chapter = Chapter("Test Chapter", 1)
def test_chapter_creation(self):
"""Test chapter creation."""
self.assertEqual(self.chapter.title, "Test Chapter")
self.assertEqual(self.chapter.level, 1)
self.assertEqual(len(self.chapter.blocks), 0)
def test_chapter_creation_minimal(self):
"""Test chapter creation with minimal parameters."""
chapter = Chapter()
self.assertIsNone(chapter.title)
self.assertEqual(chapter.level, 1)
def test_title_property(self):
"""Test title property getter and setter."""
# Test setter
self.chapter.title = "New Chapter Title"
self.assertEqual(self.chapter.title, "New Chapter Title")
# Test setting to None
self.chapter.title = None
self.assertIsNone(self.chapter.title)
def test_level_property(self):
"""Test level property."""
self.assertEqual(self.chapter.level, 1)
# Level should be read-only (no setter test)
# This is by design based on the class definition
def test_block_management(self):
"""Test adding blocks to chapter."""
para1 = Paragraph()
para2 = Paragraph()
heading = Heading(HeadingLevel.H2)
# Add blocks
self.chapter.add_block(para1)
self.chapter.add_block(heading)
self.chapter.add_block(para2)
# Test blocks list
self.assertEqual(len(self.chapter.blocks), 3)
self.assertEqual(self.chapter.blocks[0], para1)
self.assertEqual(self.chapter.blocks[1], heading)
self.assertEqual(self.chapter.blocks[2], para2)
def test_metadata_management(self):
"""Test chapter metadata."""
# Set metadata
self.chapter.set_metadata("author", "Jane Doe")
self.chapter.set_metadata("word_count", 1500)
self.chapter.set_metadata("tags", ["intro", "basics"])
# Test retrieval
self.assertEqual(self.chapter.get_metadata("author"), "Jane Doe")
self.assertEqual(self.chapter.get_metadata("word_count"), 1500)
self.assertEqual(self.chapter.get_metadata("tags"), ["intro", "basics"])
# Test non-existent metadata
self.assertIsNone(self.chapter.get_metadata("nonexistent"))
class TestBook(unittest.TestCase):
"""Test cases for Book class."""
def setUp(self):
"""Set up test fixtures."""
self.book = Book("Test Book", "Author Name", "en-US")
def test_book_creation(self):
"""Test book creation with all parameters."""
self.assertEqual(self.book.get_title(), "Test Book")
self.assertEqual(self.book.get_author(), "Author Name")
self.assertEqual(self.book.get_metadata(MetadataType.LANGUAGE), "en-US")
self.assertEqual(len(self.book.chapters), 0)
def test_book_creation_minimal(self):
"""Test book creation with minimal parameters."""
book = Book()
self.assertIsNone(book.get_title())
self.assertIsNone(book.get_author())
self.assertEqual(book.get_metadata(MetadataType.LANGUAGE), "en-US")
def test_book_creation_partial(self):
"""Test book creation with partial parameters."""
book = Book(title="Just Title")
self.assertEqual(book.get_title(), "Just Title")
self.assertIsNone(book.get_author())
def test_author_convenience_methods(self):
"""Test author getter and setter convenience methods."""
# Test setting author
self.book.set_author("New Author")
self.assertEqual(self.book.get_author(), "New Author")
# Test that it's also in metadata
self.assertEqual(self.book.get_metadata(MetadataType.AUTHOR), "New Author")
def test_chapter_management(self):
"""Test adding and managing chapters."""
# Create chapters
ch1 = Chapter("Introduction", 1)
ch2 = Chapter("Getting Started", 1)
ch3 = Chapter("Advanced Topics", 1)
# Add chapters
self.book.add_chapter(ch1)
self.book.add_chapter(ch2)
self.book.add_chapter(ch3)
# Test chapters list
self.assertEqual(len(self.book.chapters), 3)
self.assertEqual(self.book.chapters[0], ch1)
self.assertEqual(self.book.chapters[1], ch2)
self.assertEqual(self.book.chapters[2], ch3)
def test_create_chapter(self):
"""Test creating chapters through the book."""
# Create chapter with title and level
ch1 = self.book.create_chapter("Chapter 1", 1)
self.assertEqual(ch1.title, "Chapter 1")
self.assertEqual(ch1.level, 1)
self.assertEqual(len(self.book.chapters), 1)
self.assertEqual(self.book.chapters[0], ch1)
# Create chapter with minimal parameters
ch2 = self.book.create_chapter()
self.assertIsNone(ch2.title)
self.assertEqual(ch2.level, 1)
self.assertEqual(len(self.book.chapters), 2)
def test_generate_book_toc(self):
"""Test table of contents generation for book."""
# Create chapters with different levels
ch1 = Chapter("Introduction", 1)
ch2 = Chapter("Getting Started", 1)
ch3 = Chapter("Basic Concepts", 2)
ch4 = Chapter("Advanced Topics", 1)
ch5 = Chapter("Best Practices", 2)
# Add chapters to book
self.book.add_chapter(ch1)
self.book.add_chapter(ch2)
self.book.add_chapter(ch3)
self.book.add_chapter(ch4)
self.book.add_chapter(ch5)
# Generate TOC
toc = self.book.generate_table_of_contents()
# Test TOC structure
self.assertEqual(len(toc), 5)
# Test entries
expected = [
(1, "Introduction", ch1),
(1, "Getting Started", ch2),
(2, "Basic Concepts", ch3),
(1, "Advanced Topics", ch4),
(2, "Best Practices", ch5)
]
for i, (exp_level, exp_title, exp_chapter) in enumerate(expected):
level, title, chapter = toc[i]
self.assertEqual(level, exp_level)
self.assertEqual(title, exp_title)
self.assertEqual(chapter, exp_chapter)
def test_generate_book_toc_with_untitled_chapters(self):
"""Test TOC generation with chapters that have no title."""
# Create chapters, some without titles
ch1 = Chapter("Introduction", 1)
ch2 = Chapter(None, 1) # No title
ch3 = Chapter("Conclusion", 1)
self.book.add_chapter(ch1)
self.book.add_chapter(ch2)
self.book.add_chapter(ch3)
# Generate TOC
toc = self.book.generate_table_of_contents()
# Should only include chapters with titles
self.assertEqual(len(toc), 2)
level, title, chapter = toc[0]
self.assertEqual(title, "Introduction")
self.assertEqual(chapter, ch1)
level, title, chapter = toc[1]
self.assertEqual(title, "Conclusion")
self.assertEqual(chapter, ch3)
def test_book_inherits_document_features(self):
"""Test that Book inherits all Document functionality."""
# Test that book can use all document methods
# Add blocks directly to book
para = Paragraph()
self.book.add_block(para)
self.assertEqual(len(self.book.blocks), 1)
# Test metadata
self.book.set_metadata(MetadataType.PUBLISHER, "Test Publisher")
self.assertEqual(self.book.get_metadata(MetadataType.PUBLISHER), "Test Publisher")
# Test anchors
heading = Heading(HeadingLevel.H1)
self.book.add_anchor("preface", heading)
self.assertEqual(self.book.get_anchor("preface"), heading)
class TestDocumentFontRegistry(unittest.TestCase):
"""Test cases for Document font registry functionality."""
def setUp(self):
"""Set up test fixtures."""
self.doc = Document("Test Document", "en-US")
def test_get_or_create_font_creates_new_font(self):
"""Test that get_or_create_font creates a new font when none exists."""
font = self.doc.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
self.assertEqual(font.font_size, 14)
self.assertEqual(font.colour, (255, 0, 0))
self.assertEqual(font.weight, FontWeight.BOLD)
# Check that font is stored in registry
self.assertEqual(len(self.doc._fonts), 1)
def test_get_or_create_font_reuses_existing_font(self):
"""Test that get_or_create_font reuses existing fonts."""
# Create first font
font1 = self.doc.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Create second font with same properties
font2 = self.doc.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Should return the same font object
self.assertIs(font1, font2)
# Should only have one font in registry
self.assertEqual(len(self.doc._fonts), 1)
def test_get_or_create_font_creates_different_fonts(self):
"""Test that different font properties create different fonts."""
# Create first font
font1 = self.doc.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Create font with different size
font2 = self.doc.get_or_create_font(
font_size=16,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Create font with different color
font3 = self.doc.get_or_create_font(
font_size=14,
colour=(0, 255, 0),
weight=FontWeight.BOLD
)
# Create font with different weight
font4 = self.doc.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.NORMAL
)
# All should be different objects
self.assertIsNot(font1, font2)
self.assertIsNot(font1, font3)
self.assertIsNot(font1, font4)
self.assertIsNot(font2, font3)
self.assertIsNot(font2, font4)
self.assertIsNot(font3, font4)
# Should have four fonts in registry
self.assertEqual(len(self.doc._fonts), 4)
def test_get_or_create_font_with_all_parameters(self):
"""Test get_or_create_font with all parameters."""
font = self.doc.get_or_create_font(
font_path="path/to/font.ttf",
font_size=18,
colour=(128, 64, 192),
weight=FontWeight.BOLD,
style=FontStyle.ITALIC,
decoration=TextDecoration.UNDERLINE,
background=(255, 255, 255, 128),
language="fr_FR",
min_hyphenation_width=80
)
self.assertEqual(font._font_path, "path/to/font.ttf")
self.assertEqual(font.font_size, 18)
self.assertEqual(font.colour, (128, 64, 192))
self.assertEqual(font.weight, FontWeight.BOLD)
self.assertEqual(font.style, FontStyle.ITALIC)
self.assertEqual(font.decoration, TextDecoration.UNDERLINE)
self.assertEqual(font.background, (255, 255, 255, 128))
self.assertEqual(font.language, "fr_FR")
self.assertEqual(font.min_hyphenation_width, 80)
def test_get_or_create_font_with_defaults(self):
"""Test get_or_create_font with default values."""
font = self.doc.get_or_create_font()
# Should create font with default values
self.assertIsNotNone(font)
self.assertEqual(font.font_size, 16) # Default font size
self.assertEqual(font.colour, (0, 0, 0)) # Default black color
self.assertEqual(font.weight, FontWeight.NORMAL)
self.assertEqual(font.style, FontStyle.NORMAL)
self.assertEqual(font.decoration, TextDecoration.NONE)
class TestChapterFontRegistry(unittest.TestCase):
"""Test cases for Chapter font registry functionality."""
def setUp(self):
"""Set up test fixtures."""
self.doc = Document("Test Document", "en-US")
self.chapter = Chapter("Test Chapter", 1, parent=self.doc)
def test_chapter_uses_parent_font_registry(self):
"""Test that chapter uses parent document's font registry."""
# Create font through chapter - should delegate to parent
font1 = self.chapter.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Create same font through document - should return same object
font2 = self.doc.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Should be the same font object
self.assertIs(font1, font2)
# Should be stored in document's registry, not chapter's
self.assertEqual(len(self.doc._fonts), 1)
self.assertEqual(len(self.chapter._fonts), 0)
def test_chapter_without_parent_manages_own_fonts(self):
"""Test that chapter without parent manages its own fonts."""
# Create chapter without parent
standalone_chapter = Chapter("Standalone Chapter", 1)
# Create font through chapter
font1 = standalone_chapter.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Create same font again - should reuse
font2 = standalone_chapter.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Should be the same font object
self.assertIs(font1, font2)
# Should be stored in chapter's own registry
self.assertEqual(len(standalone_chapter._fonts), 1)
def test_chapter_parent_assignment(self):
"""Test that chapter parent assignment works correctly."""
# Create chapter with parent
chapter_with_parent = Chapter("Chapter with Parent", 1, parent=self.doc)
self.assertEqual(chapter_with_parent._parent, self.doc)
# Create chapter without parent
chapter_without_parent = Chapter("Chapter without Parent", 1)
self.assertIsNone(chapter_without_parent._parent)
class TestBookFontRegistry(unittest.TestCase):
"""Test cases for Book font registry functionality."""
def setUp(self):
"""Set up test fixtures."""
self.book = Book("Test Book", "Author Name", "en-US")
def test_book_inherits_document_font_registry(self):
"""Test that Book inherits Document's font registry functionality."""
# Create font through book
font1 = self.book.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Create same font again - should reuse
font2 = self.book.get_or_create_font(
font_size=14,
colour=(255, 0, 0),
weight=FontWeight.BOLD
)
# Should be the same font object
self.assertIs(font1, font2)
# Should have one font in registry
self.assertEqual(len(self.book._fonts), 1)
if __name__ == '__main__':
unittest.main()
+529
View File
@@ -0,0 +1,529 @@
"""
Unit tests for abstract functional elements.
Tests the Link, Button, Form, FormField, and related classes that handle
interactive functionality and user interface elements.
"""
import unittest
from unittest.mock import Mock, patch
from pyWebLayout.abstract.functional import (
Link, LinkType, Button, Form, FormField, FormFieldType
)
class TestLinkType(unittest.TestCase):
"""Test cases for LinkType enum."""
def test_link_types(self):
"""Test that all expected link types exist."""
expected_types = ['INTERNAL', 'EXTERNAL', 'API', 'FUNCTION']
for type_name in expected_types:
self.assertTrue(hasattr(LinkType, type_name))
# Test specific values
self.assertEqual(LinkType.INTERNAL.value, 1)
self.assertEqual(LinkType.EXTERNAL.value, 2)
self.assertEqual(LinkType.API.value, 3)
self.assertEqual(LinkType.FUNCTION.value, 4)
class TestLink(unittest.TestCase):
"""Test cases for Link class."""
def setUp(self):
"""Set up test fixtures."""
self.mock_callback = Mock(return_value="callback_result")
def test_link_creation_minimal(self):
"""Test link creation with minimal parameters."""
link = Link("test-location")
self.assertEqual(link.location, "test-location")
self.assertEqual(link.link_type, LinkType.INTERNAL) # Default
self.assertEqual(link.params, {})
self.assertIsNone(link.title)
self.assertIsNone(link._callback)
def test_link_creation_full(self):
"""Test link creation with all parameters."""
params = {"param1": "value1", "param2": "value2"}
link = Link(
location="https://example.com",
link_type=LinkType.EXTERNAL,
callback=self.mock_callback,
params=params,
title="Example Link"
)
self.assertEqual(link.location, "https://example.com")
self.assertEqual(link.link_type, LinkType.EXTERNAL)
self.assertEqual(link.params, params)
self.assertEqual(link.title, "Example Link")
self.assertEqual(link._callback, self.mock_callback)
def test_internal_link_execution(self):
"""Test executing internal links."""
link = Link("#section1", LinkType.INTERNAL)
result = link.execute()
# Internal links should return the location
self.assertEqual(result, "#section1")
def test_external_link_execution(self):
"""Test executing external links."""
link = Link("https://example.com", LinkType.EXTERNAL)
result = link.execute()
# External links should return the location
self.assertEqual(result, "https://example.com")
def test_api_link_execution(self):
"""Test executing API links with callback."""
params = {"action": "save", "id": 123}
link = Link(
location="/api/save",
link_type=LinkType.API,
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)
self.assertEqual(result, "callback_result")
def test_function_link_execution(self):
"""Test executing function links with callback."""
params = {"data": "test"}
link = Link(
location="save_document",
link_type=LinkType.FUNCTION,
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")
self.assertEqual(result, "callback_result")
def test_api_link_without_callback(self):
"""Test API link without callback returns location."""
link = Link("/api/endpoint", LinkType.API)
result = link.execute()
# Without callback, should return location
self.assertEqual(result, "/api/endpoint")
def test_function_link_without_callback(self):
"""Test function link without callback returns location."""
link = Link("function_name", LinkType.FUNCTION)
result = link.execute()
# Without callback, should return location
self.assertEqual(result, "function_name")
def test_link_properties(self):
"""Test link property access."""
params = {"key": "value"}
link = Link(
location="test",
link_type=LinkType.API,
params=params,
title="Test Title"
)
# Test all property getters
self.assertEqual(link.location, "test")
self.assertEqual(link.link_type, LinkType.API)
self.assertEqual(link.params, params)
self.assertEqual(link.title, "Test Title")
class TestButton(unittest.TestCase):
"""Test cases for Button class."""
def setUp(self):
"""Set up test fixtures."""
self.mock_callback = Mock(return_value="button_clicked")
def test_button_creation_minimal(self):
"""Test button creation with minimal parameters."""
button = Button("Click Me", self.mock_callback)
self.assertEqual(button.label, "Click Me")
self.assertEqual(button._callback, self.mock_callback)
self.assertEqual(button.params, {})
self.assertTrue(button.enabled)
def test_button_creation_full(self):
"""Test button creation with all parameters."""
params = {"action": "submit", "form_id": "test_form"}
button = Button(
label="Submit",
callback=self.mock_callback,
params=params,
enabled=False
)
self.assertEqual(button.label, "Submit")
self.assertEqual(button._callback, self.mock_callback)
self.assertEqual(button.params, params)
self.assertFalse(button.enabled)
def test_button_label_property(self):
"""Test button label getter and setter."""
button = Button("Original", self.mock_callback)
# Test getter
self.assertEqual(button.label, "Original")
# Test setter
button.label = "New Label"
self.assertEqual(button.label, "New Label")
def test_button_enabled_property(self):
"""Test button enabled getter and setter."""
button = Button("Test", self.mock_callback, enabled=True)
# Test initial state
self.assertTrue(button.enabled)
# Test setter
button.enabled = False
self.assertFalse(button.enabled)
button.enabled = True
self.assertTrue(button.enabled)
def test_button_execute_enabled(self):
"""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")
self.assertEqual(result, "button_clicked")
def test_button_execute_disabled(self):
"""Test executing disabled button."""
button = Button("Test", self.mock_callback, enabled=False)
result = button.execute()
# Should not call callback and return None
self.mock_callback.assert_not_called()
self.assertIsNone(result)
def test_button_execute_no_callback(self):
"""Test executing button without callback."""
button = Button("Test", None, enabled=True)
result = button.execute()
# Should return None
self.assertIsNone(result)
class TestFormFieldType(unittest.TestCase):
"""Test cases for FormFieldType enum."""
def test_form_field_types(self):
"""Test that all expected form field types exist."""
expected_types = [
'TEXT', 'PASSWORD', 'CHECKBOX', 'RADIO', 'SELECT', 'TEXTAREA',
'NUMBER', 'DATE', 'TIME', 'EMAIL', 'URL', 'COLOR', 'RANGE', 'HIDDEN'
]
for type_name in expected_types:
self.assertTrue(hasattr(FormFieldType, type_name))
# Test some specific values
self.assertEqual(FormFieldType.TEXT.value, 1)
self.assertEqual(FormFieldType.PASSWORD.value, 2)
self.assertEqual(FormFieldType.HIDDEN.value, 14)
class TestFormField(unittest.TestCase):
"""Test cases for FormField class."""
def test_form_field_creation_minimal(self):
"""Test form field creation with minimal parameters."""
field = FormField("username", FormFieldType.TEXT)
self.assertEqual(field.name, "username")
self.assertEqual(field.field_type, FormFieldType.TEXT)
self.assertEqual(field.label, "username") # Default to name
self.assertIsNone(field.value)
self.assertFalse(field.required)
self.assertEqual(field.options, [])
self.assertIsNone(field.form)
def test_form_field_creation_full(self):
"""Test form field creation with all parameters."""
options = [("value1", "Label 1"), ("value2", "Label 2")]
field = FormField(
name="country",
field_type=FormFieldType.SELECT,
label="Country",
value="value1",
required=True,
options=options
)
self.assertEqual(field.name, "country")
self.assertEqual(field.field_type, FormFieldType.SELECT)
self.assertEqual(field.label, "Country")
self.assertEqual(field.value, "value1")
self.assertTrue(field.required)
self.assertEqual(field.options, options)
def test_form_field_value_property(self):
"""Test form field value getter and setter."""
field = FormField("test", FormFieldType.TEXT, value="initial")
# Test getter
self.assertEqual(field.value, "initial")
# Test setter
field.value = "new_value"
self.assertEqual(field.value, "new_value")
def test_form_field_form_property(self):
"""Test form field form getter and setter."""
field = FormField("test", FormFieldType.TEXT)
mock_form = Mock()
# Initial state
self.assertIsNone(field.form)
# Test setter
field.form = mock_form
self.assertEqual(field.form, mock_form)
def test_form_field_properties(self):
"""Test all form field property getters."""
options = [("opt1", "Option 1")]
field = FormField(
name="test_field",
field_type=FormFieldType.CHECKBOX,
label="Test Field",
value=True,
required=True,
options=options
)
# Test all getters
self.assertEqual(field.name, "test_field")
self.assertEqual(field.field_type, FormFieldType.CHECKBOX)
self.assertEqual(field.label, "Test Field")
self.assertTrue(field.value)
self.assertTrue(field.required)
self.assertEqual(field.options, options)
class TestForm(unittest.TestCase):
"""Test cases for Form class."""
def setUp(self):
"""Set up test fixtures."""
self.mock_callback = Mock(return_value="form_submitted")
def test_form_creation_minimal(self):
"""Test form creation with minimal parameters."""
form = Form("test_form")
self.assertEqual(form.form_id, "test_form")
self.assertIsNone(form.action)
self.assertIsNone(form._callback)
self.assertEqual(len(form._fields), 0)
def test_form_creation_full(self):
"""Test form creation with all parameters."""
form = Form(
form_id="contact_form",
action="/submit",
callback=self.mock_callback
)
self.assertEqual(form.form_id, "contact_form")
self.assertEqual(form.action, "/submit")
self.assertEqual(form._callback, self.mock_callback)
def test_form_field_management(self):
"""Test adding and retrieving form fields."""
form = Form("test_form")
# Create fields
field1 = FormField("username", FormFieldType.TEXT, value="john")
field2 = FormField("password", FormFieldType.PASSWORD, value="secret")
field3 = FormField("email", FormFieldType.EMAIL, value="john@example.com")
# Add fields
form.add_field(field1)
form.add_field(field2)
form.add_field(field3)
# Test that fields are stored correctly
self.assertEqual(len(form._fields), 3)
# Test field retrieval
self.assertEqual(form.get_field("username"), field1)
self.assertEqual(form.get_field("password"), field2)
self.assertEqual(form.get_field("email"), field3)
self.assertIsNone(form.get_field("nonexistent"))
# Test that fields have form reference
self.assertEqual(field1.form, form)
self.assertEqual(field2.form, form)
self.assertEqual(field3.form, form)
def test_form_get_values(self):
"""Test getting form values."""
form = Form("test_form")
# Add fields with values
form.add_field(FormField("name", FormFieldType.TEXT, value="John Doe"))
form.add_field(FormField("age", FormFieldType.NUMBER, value=30))
form.add_field(FormField("subscribe", FormFieldType.CHECKBOX, value=True))
# Get values
values = form.get_values()
expected = {
"name": "John Doe",
"age": 30,
"subscribe": True
}
self.assertEqual(values, expected)
def test_form_get_values_empty(self):
"""Test getting values from empty form."""
form = Form("empty_form")
values = form.get_values()
self.assertEqual(values, {})
def test_form_execute_with_callback(self):
"""Test executing form with callback."""
form = Form("test_form", callback=self.mock_callback)
# Add some fields
form.add_field(FormField("field1", FormFieldType.TEXT, value="value1"))
form.add_field(FormField("field2", FormFieldType.TEXT, value="value2"))
result = form.execute()
# Should call callback with form_id and values
expected_values = {"field1": "value1", "field2": "value2"}
self.mock_callback.assert_called_once_with("test_form", expected_values)
self.assertEqual(result, "form_submitted")
def test_form_execute_without_callback(self):
"""Test executing form without callback."""
form = Form("test_form")
# Add a field
form.add_field(FormField("test", FormFieldType.TEXT, value="test_value"))
result = form.execute()
# Should return the form values
expected = {"test": "test_value"}
self.assertEqual(result, expected)
def test_form_properties(self):
"""Test form property getters."""
form = Form("test_form", action="/submit")
self.assertEqual(form.form_id, "test_form")
self.assertEqual(form.action, "/submit")
class TestFormIntegration(unittest.TestCase):
"""Integration tests for form functionality."""
def test_complete_form_workflow(self):
"""Test a complete form creation and submission workflow."""
# Create form
form = Form("registration_form", action="/register")
# Add various field types
form.add_field(FormField(
"username", FormFieldType.TEXT,
label="Username", required=True, value="testuser"
))
form.add_field(FormField(
"password", FormFieldType.PASSWORD,
label="Password", required=True, value="secret123"
))
form.add_field(FormField(
"email", FormFieldType.EMAIL,
label="Email", required=True, value="test@example.com"
))
form.add_field(FormField(
"country", FormFieldType.SELECT,
label="Country", value="US",
options=[("US", "United States"), ("CA", "Canada"), ("UK", "United Kingdom")]
))
form.add_field(FormField(
"newsletter", FormFieldType.CHECKBOX,
label="Subscribe to newsletter", value=True
))
# Test form state
self.assertEqual(len(form._fields), 5)
# Test individual field access
username_field = form.get_field("username")
self.assertEqual(username_field.value, "testuser")
self.assertTrue(username_field.required)
# Test getting all values
values = form.get_values()
expected = {
"username": "testuser",
"password": "secret123",
"email": "test@example.com",
"country": "US",
"newsletter": True
}
self.assertEqual(values, expected)
# Test form submission
result = form.execute()
self.assertEqual(result, expected)
def test_form_field_modification(self):
"""Test modifying form fields after creation."""
form = Form("test_form")
# Add field
field = FormField("test", FormFieldType.TEXT, value="initial")
form.add_field(field)
# Modify field value
field.value = "modified"
# Test that form reflects the change
values = form.get_values()
self.assertEqual(values["test"], "modified")
# Test getting the modified field
retrieved_field = form.get_field("test")
self.assertEqual(retrieved_field.value, "modified")
if __name__ == '__main__':
unittest.main()
+858
View File
@@ -0,0 +1,858 @@
"""
Unit tests for abstract inline elements.
Tests the Word and FormattedSpan classes that handle inline text elements
and formatting within documents.
"""
import unittest
from unittest.mock import Mock, patch, MagicMock
from pyWebLayout.abstract.inline import Word, FormattedSpan, LineBreak
from pyWebLayout.style import Font
class TestWord(unittest.TestCase):
"""Test cases for Word class."""
def setUp(self):
"""Set up test fixtures."""
self.font = Font()
# Note: Font background is a tuple (255, 255, 255, 0) by default
# Note: Font language is set via constructor parameter (language - with typo)
def test_word_creation_minimal(self):
"""Test word creation with minimal parameters."""
word = Word("hello", self.font)
self.assertEqual(word.text, "hello")
self.assertEqual(word.style, self.font)
self.assertIsNone(word.previous)
self.assertIsNone(word.next)
self.assertEqual(len(word.possible_hyphenation()),0)
def test_word_hyphenation(self):
"""Test word creation with minimal parameters."""
word = Word("amsterdam", self.font)
self.assertEqual(word.text, "amsterdam")
self.assertEqual(word.style, self.font)
self.assertIsNone(word.previous)
self.assertIsNone(word.next)
self.assertEqual(len(word.possible_hyphenation()),3)
def test_word_creation_with_previous(self):
"""Test word creation with previous word reference."""
word1 = Word("first", self.font)
word2 = Word("second", self.font, previous=word1)
self.assertEqual(word2.previous, word1)
self.assertIsNone(word1.previous)
self.assertEqual(word1.next, word2)
self.assertIsNone(word2.next)
def test_word_creation_with_background_override(self):
"""Test word creation with background color override."""
word = Word("test", self.font, background="yellow")
self.assertEqual(word.background, "yellow")
# Original font background should be unchanged - it's a tuple
self.assertEqual(word.style.background, (255, 255, 255, 0))
def test_word_properties(self):
"""Test word property getters."""
word1 = Word("first", self.font)
word2 = Word("second", self.font, background="blue", previous=word1)
# Test all properties
self.assertEqual(word2.text, "second")
self.assertEqual(word2.style, self.font)
self.assertEqual(word2.background, "blue")
self.assertEqual(word2.previous, word1)
self.assertIsNone(word2.next)
def test_add_next_word(self):
"""Test linking words with add_next method."""
word1 = Word("first", self.font)
word2 = Word("second", self.font)
word3 = Word("third", self.font)
# Link words
word1.add_next(word2)
word2.add_next(word3)
# Test forward links
self.assertEqual(word1.next, word2)
self.assertEqual(word2.next, word3)
self.assertIsNone(word3.next)
# Backward links should remain as set in constructor
self.assertIsNone(word1.previous)
self.assertIsNone(word2.previous)
self.assertIsNone(word3.previous)
def test_word_chain(self):
"""Test creating a chain of linked words."""
word1 = Word("first", self.font)
word2 = Word("second", self.font, previous=word1)
word3 = Word("third", self.font, previous=word2)
# Test complete chain
self.assertIsNone(word1.previous)
self.assertEqual(word1.next, word2)
self.assertEqual(word2.previous, word1)
self.assertEqual(word2.next, word3)
self.assertEqual(word3.previous, word2)
self.assertIsNone(word3.next)
def test_word_create_and_add_to_with_style_override(self):
"""Test Word.create_and_add_to with explicit style parameter."""
# Create alternate font
alt_font = Font()
# Create mock container
mock_container = Mock()
mock_container.style = self.font
mock_container.add_word = Mock()
# Ensure _words doesn't interfere
del mock_container._words
# Create word with style override
word = Word.create_and_add_to("test", mock_container, style=alt_font)
# Test that word uses the override style, not container style
self.assertEqual(word.style, alt_font)
self.assertNotEqual(word.style, self.font)
def test_word_create_and_add_to_with_background_override(self):
"""Test Word.create_and_add_to with explicit background parameter."""
# Create mock container
mock_container = Mock()
mock_container.style = self.font
mock_container.background = "container_bg"
mock_container.add_word = Mock()
# Ensure _words doesn't interfere
del mock_container._words
# Create word with background override
word = Word.create_and_add_to("test", mock_container, background="override_bg")
# Test that word uses the override background
self.assertEqual(word.background, "override_bg")
def test_word_create_and_add_to_inherit_container_background(self):
"""Test Word.create_and_add_to inheriting background from container."""
# Create mock container with background
mock_container = Mock()
mock_container.style = self.font
mock_container.background = "container_bg"
mock_container.add_word = Mock()
# Ensure _words doesn't interfere
del mock_container._words
# Create word without background override
word = Word.create_and_add_to("test", mock_container)
# Test that word inherits container background
self.assertEqual(word.background, "container_bg")
def test_word_create_and_add_to_with_words_list_previous(self):
"""Test Word.create_and_add_to linking with previous word from _words list."""
# Create mock container with _words list
mock_container = Mock()
mock_container.style = self.font
mock_container.add_word = Mock()
# Create existing word and add to container's _words list
existing_word = Word("previous", self.font)
mock_container._words = [existing_word]
# Create new word
word = Word.create_and_add_to("current", mock_container)
# Test that words are linked
self.assertEqual(word.previous, existing_word)
self.assertEqual(existing_word.next, word)
def test_word_create_and_add_to_with_words_method_previous(self):
"""Test Word.create_and_add_to linking with previous word from words() method."""
# Create a simple container that implements words() method
class SimpleContainer:
def __init__(self, font):
self.style = font
self.existing_word = Word("previous", font)
def words(self):
yield ("span1", self.existing_word)
def add_word(self, word):
pass # Simple implementation
container = SimpleContainer(self.font)
# Create new word
word = Word.create_and_add_to("current", container)
# Test that words are linked
self.assertEqual(word.previous, container.existing_word)
self.assertEqual(container.existing_word.next, word)
def test_word_create_and_add_to_no_style_error(self):
"""Test Word.create_and_add_to raises error when container has no style."""
# Create container without style
class BadContainer:
def add_word(self, word):
pass
container = BadContainer()
# Test that AttributeError is raised
with self.assertRaises(AttributeError) as context:
Word.create_and_add_to("test", container)
self.assertIn("must have a 'style' property", str(context.exception))
def test_word_create_and_add_to_no_add_word_error(self):
"""Test Word.create_and_add_to raises error when container has no add_word method."""
# Create container without add_word
class BadContainer:
def __init__(self, font):
self.style = font
container = BadContainer(self.font)
# Test that AttributeError is raised
with self.assertRaises(AttributeError) as context:
Word.create_and_add_to("test", container)
self.assertIn("must have an 'add_word' method", str(context.exception))
def test_word_create_and_add_to_parameter_inspection_word_object(self):
"""Test Word.create_and_add_to with add_word method that expects Word object."""
# Create container with add_word method that has 'word' parameter name
class WordObjectContainer:
def __init__(self, font):
self.style = font
self.added_words = []
def add_word(self, word): # Parameter named 'word' indicates it expects Word object
self.added_words.append(word)
container = WordObjectContainer(self.font)
# Create and add word
word = Word.create_and_add_to("test", container)
# Test that the Word object was passed to add_word
self.assertEqual(len(container.added_words), 1)
self.assertEqual(container.added_words[0], word)
self.assertIsInstance(container.added_words[0], Word)
def test_word_create_and_add_to_parameter_inspection_word_obj(self):
"""Test Word.create_and_add_to with add_word method that has 'word_obj' parameter."""
class WordObjContainer:
def __init__(self, font):
self.style = font
self.added_words = []
def add_word(self, word_obj): # Parameter named 'word_obj' indicates it expects Word object
self.added_words.append(word_obj)
container = WordObjContainer(self.font)
word = Word.create_and_add_to("test", container)
self.assertEqual(len(container.added_words), 1)
self.assertEqual(container.added_words[0], word)
def test_word_create_and_add_to_parameter_inspection_word_object(self):
"""Test Word.create_and_add_to with add_word method that has 'word_object' parameter."""
class WordObjectContainer:
def __init__(self, font):
self.style = font
self.added_words = []
def add_word(self, word_object): # Parameter named 'word_object' indicates it expects Word object
self.added_words.append(word_object)
container = WordObjectContainer(self.font)
word = Word.create_and_add_to("test", container)
self.assertEqual(len(container.added_words), 1)
self.assertEqual(container.added_words[0], word)
def test_word_create_and_add_to_parameter_inspection_text_fallback_with_words_list(self):
"""Test Word.create_and_add_to with add_word that expects text but container has _words list."""
class TextExpectingContainer:
def __init__(self, font):
self.style = font
self._words = [] # Has _words list
self.add_word_calls = []
def add_word(self, text): # Parameter named 'text' suggests it expects string
# This simulates FormattedSpan.add_word behavior
self.add_word_calls.append(text)
# In real FormattedSpan, this would create a Word internally
container = TextExpectingContainer(self.font)
word = Word.create_and_add_to("test", container)
# Word should be added to _words list directly, not via add_word
self.assertEqual(len(container._words), 1)
self.assertEqual(container._words[0], word)
# add_word should not have been called since it expects text
self.assertEqual(len(container.add_word_calls), 0)
def test_word_create_and_add_to_parameter_inspection_fallback_without_words_list(self):
"""Test Word.create_and_add_to fallback when container doesn't have _words list."""
class TextExpectingContainer:
def __init__(self, font):
self.style = font
# No _words list
self.added_words = []
def add_word(self, text): # Parameter suggests text but we'll pass Word as fallback
self.added_words.append(text)
container = TextExpectingContainer(self.font)
word = Word.create_and_add_to("test", container)
# Should fallback to calling add_word with Word object
self.assertEqual(len(container.added_words), 1)
self.assertEqual(container.added_words[0], word)
def test_word_create_and_add_to_no_parameters_edge_case(self):
"""Test Word.create_and_add_to with add_word method that has no parameters."""
class NoParamsContainer:
def __init__(self, font):
self.style = font
self.add_word_called = False
def add_word(self): # No parameters - edge case
self.add_word_called = True
container = NoParamsContainer(self.font)
# The current implementation will fail when calling add_word(word) with a no-parameter method
with self.assertRaises(TypeError) as context:
Word.create_and_add_to("test", container)
self.assertIn("takes 1 positional argument but 2 were given", str(context.exception))
def test_word_create_and_add_to_linking_behavior_with_existing_words(self):
"""Test Word.create_and_add_to properly links with existing words in container."""
# Create container with existing words
class ContainerWithWords:
def __init__(self, font):
self.style = font
self._words = []
# Add an existing word
existing_word = Word("existing", font)
self._words.append(existing_word)
def add_word(self, word):
self._words.append(word)
container = ContainerWithWords(self.font)
existing_word = container._words[0]
# Create new word
new_word = Word.create_and_add_to("new", container)
# Test linking
self.assertEqual(new_word.previous, existing_word)
self.assertEqual(existing_word.next, new_word)
self.assertEqual(len(container._words), 2)
self.assertEqual(container._words[1], new_word)
def test_word_create_and_add_to_linking_behavior_with_words_method(self):
"""Test Word.create_and_add_to properly links with words from container.words() method."""
class ContainerWithWordsMethod:
def __init__(self, font):
self.style = font
self.existing_word1 = Word("first", font)
self.existing_word2 = Word("second", font)
self.existing_word1.add_next(self.existing_word2)
self.added_words = []
def words(self):
yield ("span1", self.existing_word1)
yield ("span2", self.existing_word2)
def add_word(self, word):
self.added_words.append(word)
container = ContainerWithWordsMethod(self.font)
# Create new word
new_word = Word.create_and_add_to("third", container)
# Should link to the last word returned by words() method
self.assertEqual(new_word.previous, container.existing_word2)
self.assertEqual(container.existing_word2.next, new_word)
def test_word_create_and_add_to_linking_behavior_empty_words_method(self):
"""Test Word.create_and_add_to with empty words() method."""
class EmptyWordsContainer:
def __init__(self, font):
self.style = font
def words(self):
# Empty iterator
return iter([])
def add_word(self, word):
pass
container = EmptyWordsContainer(self.font)
# Create word
word = Word.create_and_add_to("test", container)
# Should have no previous word
self.assertIsNone(word.previous)
def test_word_create_and_add_to_linking_behavior_words_method_exception(self):
"""Test Word.create_and_add_to with words() method that raises exception."""
class ExceptionWordsContainer:
def __init__(self, font):
self.style = font
def words(self):
raise TypeError("Error in words method")
def add_word(self, word):
pass
container = ExceptionWordsContainer(self.font)
# Create word - should handle exception gracefully
word = Word.create_and_add_to("test", container)
# Should have no previous word due to exception
self.assertIsNone(word.previous)
class TestFormattedSpan(unittest.TestCase):
"""Test cases for FormattedSpan class."""
def setUp(self):
"""Set up test fixtures."""
self.font = Font()
# Font background is a tuple, not a string
def test_formatted_span_creation_minimal(self):
"""Test formatted span creation with minimal parameters."""
span = FormattedSpan(self.font)
self.assertEqual(span.style, self.font)
self.assertEqual(span.background, self.font.background)
self.assertEqual(len(span.words), 0)
def test_formatted_span_creation_with_background(self):
"""Test formatted span creation with background override."""
span = FormattedSpan(self.font, background="yellow")
self.assertEqual(span.style, self.font)
self.assertEqual(span.background, "yellow")
self.assertNotEqual(span.background, self.font.background)
def test_formatted_span_properties(self):
"""Test formatted span property getters."""
span = FormattedSpan(self.font, background="blue")
self.assertEqual(span.style, self.font)
self.assertEqual(span.background, "blue")
self.assertIsInstance(span.words, list)
self.assertEqual(len(span.words), 0)
def test_add_single_word(self):
"""Test adding a single word to formatted span."""
span = FormattedSpan(self.font)
word = span.add_word("hello")
# Test returned word
self.assertIsInstance(word, Word)
self.assertEqual(word.text, "hello")
self.assertEqual(word.style, self.font)
self.assertEqual(word.background, self.font.background)
self.assertIsNone(word.previous)
# Test span state
self.assertEqual(len(span.words), 1)
self.assertEqual(span.words[0], word)
def test_add_multiple_words(self):
"""Test adding multiple words to formatted span."""
span = FormattedSpan(self.font)
word1 = span.add_word("first")
word2 = span.add_word("second")
word3 = span.add_word("third")
# Test span contains all words
self.assertEqual(len(span.words), 3)
self.assertEqual(span.words[0], word1)
self.assertEqual(span.words[1], word2)
self.assertEqual(span.words[2], word3)
# Test word linking
self.assertIsNone(word1.previous)
self.assertEqual(word1.next, word2)
self.assertEqual(word2.previous, word1)
self.assertEqual(word2.next, word3)
self.assertEqual(word3.previous, word2)
self.assertIsNone(word3.next)
def test_add_word_with_background_override(self):
"""Test that added words inherit span background."""
span = FormattedSpan(self.font, background="red")
word = span.add_word("test")
# Word should inherit span's background
self.assertEqual(word.background, "red")
self.assertEqual(word.style, self.font)
def test_word_style_consistency(self):
"""Test that all words in span have consistent style."""
span = FormattedSpan(self.font, background="green")
words = []
for text in ["this", "is", "a", "test"]:
words.append(span.add_word(text))
# All words should have same style and background
for word in words:
self.assertEqual(word.style, self.font)
self.assertEqual(word.background, "green")
def test_word_chain_integrity(self):
"""Test that word chain is properly maintained."""
span = FormattedSpan(self.font)
words = []
for i in range(5):
words.append(span.add_word(f"word{i}"))
# Test complete chain
for i in range(5):
word = words[i]
# Test previous link
if i == 0:
self.assertIsNone(word.previous)
else:
self.assertEqual(word.previous, words[i-1])
# Test next link
if i == 4:
self.assertIsNone(word.next)
else:
self.assertEqual(word.next, words[i+1])
def test_empty_span_operations(self):
"""Test operations on empty formatted span."""
span = FormattedSpan(self.font)
# Test empty state
self.assertEqual(len(span.words), 0)
self.assertEqual(span.words, [])
# Add first word
first_word = span.add_word("first")
self.assertIsNone(first_word.previous)
self.assertIsNone(first_word.next)
def test_formatted_span_create_and_add_to_with_container_style(self):
"""Test FormattedSpan.create_and_add_to with container that has style property."""
# Create mock container with style and add_span method
mock_container = Mock()
mock_container.style = self.font
mock_container.add_span = Mock()
# Remove background so it inherits from font
del mock_container.background
# Create and add span
span = FormattedSpan.create_and_add_to(mock_container)
# Test that span was created with correct properties
self.assertIsInstance(span, FormattedSpan)
self.assertEqual(span.style, self.font)
self.assertEqual(span.background, self.font.background)
# Test that add_span was called
mock_container.add_span.assert_called_once_with(span)
def test_formatted_span_create_and_add_to_with_style_override(self):
"""Test FormattedSpan.create_and_add_to with explicit style parameter."""
# Create alternate font
alt_font = Font()
# Create mock container
mock_container = Mock()
mock_container.style = self.font
mock_container.add_span = Mock()
# Create span with style override
span = FormattedSpan.create_and_add_to(mock_container, style=alt_font)
# Test that span uses the override style, not container style
self.assertEqual(span.style, alt_font)
self.assertNotEqual(span.style, self.font)
def test_formatted_span_create_and_add_to_with_background_override(self):
"""Test FormattedSpan.create_and_add_to with explicit background parameter."""
# Create mock container
mock_container = Mock()
mock_container.style = self.font
mock_container.background = "container_bg"
mock_container.add_span = Mock()
# Create span with background override
span = FormattedSpan.create_and_add_to(mock_container, background="override_bg")
# Test that span uses the override background
self.assertEqual(span.background, "override_bg")
def test_formatted_span_create_and_add_to_inherit_container_background(self):
"""Test FormattedSpan.create_and_add_to inheriting background from container."""
# Create mock container with background
mock_container = Mock()
mock_container.style = self.font
mock_container.background = "container_bg"
mock_container.add_span = Mock()
# Create span without background override
span = FormattedSpan.create_and_add_to(mock_container)
# Test that span inherits container background
self.assertEqual(span.background, "container_bg")
def test_formatted_span_create_and_add_to_no_style_error(self):
"""Test FormattedSpan.create_and_add_to raises error when container has no style."""
# Create mock container without style
mock_container = Mock()
del mock_container.style
mock_container.add_span = Mock()
# Test that AttributeError is raised
with self.assertRaises(AttributeError) as context:
FormattedSpan.create_and_add_to(mock_container)
self.assertIn("must have a 'style' property", str(context.exception))
def test_formatted_span_create_and_add_to_no_add_span_error(self):
"""Test FormattedSpan.create_and_add_to raises error when container has no add_span method."""
# Create mock container without add_span
mock_container = Mock()
mock_container.style = self.font
del mock_container.add_span
# Test that AttributeError is raised
with self.assertRaises(AttributeError) as context:
FormattedSpan.create_and_add_to(mock_container)
self.assertIn("must have an 'add_span' method", str(context.exception))
class TestWordFormattedSpanIntegration(unittest.TestCase):
"""Integration tests between Word and FormattedSpan classes."""
def setUp(self):
"""Set up test fixtures."""
self.font = Font()
# Font background is a tuple, not a string
def test_sentence_creation(self):
"""Test creating a complete sentence with formatted span."""
span = FormattedSpan(self.font)
sentence_words = ["The", "quick", "brown", "fox", "jumps"]
words = []
for word_text in sentence_words:
words.append(span.add_word(word_text))
# Test sentence structure
self.assertEqual(len(span.words), 5)
# Test word content
for i, expected_text in enumerate(sentence_words):
self.assertEqual(words[i].text, expected_text)
# Test linking
for i in range(5):
if i > 0:
self.assertEqual(words[i].previous, words[i-1])
if i < 4:
self.assertEqual(words[i].next, words[i+1])
def test_multiple_spans_same_style(self):
"""Test creating multiple spans with the same style."""
font = Font()
span1 = FormattedSpan(font)
span2 = FormattedSpan(font)
# Add words to both spans
span1_words = [span1.add_word("First"), span1.add_word("span")]
span2_words = [span2.add_word("Second"), span2.add_word("span")]
# Test that spans are independent
self.assertEqual(len(span1.words), 2)
self.assertEqual(len(span2.words), 2)
# Test that words in different spans are not linked
self.assertIsNone(span1_words[1].next)
self.assertIsNone(span2_words[0].previous)
# But words within spans are linked
self.assertEqual(span1_words[0].next, span1_words[1])
self.assertEqual(span2_words[1].previous, span2_words[0])
def test_span_style_inheritance(self):
"""Test that words properly inherit span styling."""
font = Font()
# Font background is a tuple (255, 255, 255, 0)
# Test with span background override
span = FormattedSpan(font, background="lightgreen")
word1 = span.add_word("styled")
word2 = span.add_word("text")
# Both words should have span's background, not font's
self.assertEqual(word1.background, "lightgreen")
self.assertEqual(word2.background, "lightgreen")
# But they should have font's other properties
self.assertEqual(word1.style, font)
self.assertEqual(word2.style, font)
def test_word_modification_after_creation(self):
"""Test modifying words after they've been added to span."""
span = FormattedSpan(self.font)
word = span.add_word("original")
# Verify initial state
self.assertEqual(word.text, "original")
self.assertEqual(len(span.words), 1)
# Words are immutable by design (no setter for text property)
# But we can test that the reference is maintained
self.assertEqual(span.words[0], word)
self.assertEqual(span.words[0].text, "original")
class TestLineBreak(unittest.TestCase):
"""Test cases for LineBreak class."""
def test_line_break_creation(self):
"""Test line break creation."""
line_break = LineBreak()
# Test initial state
self.assertIsNotNone(line_break.block_type)
self.assertIsNone(line_break.parent)
def test_line_break_block_type(self):
"""Test line break block type property."""
line_break = LineBreak()
# Import BlockType to verify the correct type
from pyWebLayout.abstract.block import BlockType
self.assertEqual(line_break.block_type, BlockType.LINE_BREAK)
def test_line_break_parent_property(self):
"""Test line break parent property getter and setter."""
line_break = LineBreak()
# Test initial state
self.assertIsNone(line_break.parent)
# Test setter
mock_parent = Mock()
line_break.parent = mock_parent
self.assertEqual(line_break.parent, mock_parent)
# Test setting to None
line_break.parent = None
self.assertIsNone(line_break.parent)
def test_line_break_create_and_add_to_with_add_line_break(self):
"""Test LineBreak.create_and_add_to with container that has add_line_break method."""
# Create mock container with add_line_break method
mock_container = Mock()
mock_container.add_line_break = Mock()
# Create and add line break
line_break = LineBreak.create_and_add_to(mock_container)
# Test that line break was created
self.assertIsInstance(line_break, LineBreak)
# Test that add_line_break was called
mock_container.add_line_break.assert_called_once_with(line_break)
def test_line_break_create_and_add_to_with_add_element(self):
"""Test LineBreak.create_and_add_to with container that has add_element method."""
# Create mock container without add_line_break but with add_element
mock_container = Mock()
del mock_container.add_line_break # Ensure no add_line_break method
mock_container.add_element = Mock()
# Create and add line break
line_break = LineBreak.create_and_add_to(mock_container)
# Test that line break was created
self.assertIsInstance(line_break, LineBreak)
# Test that add_element was called
mock_container.add_element.assert_called_once_with(line_break)
def test_line_break_create_and_add_to_with_add_word(self):
"""Test LineBreak.create_and_add_to with container that has add_word method."""
# Create mock container with only add_word method
mock_container = Mock()
del mock_container.add_line_break # Ensure no add_line_break method
del mock_container.add_element # Ensure no add_element method
mock_container.add_word = Mock()
# Create and add line break
line_break = LineBreak.create_and_add_to(mock_container)
# Test that line break was created
self.assertIsInstance(line_break, LineBreak)
# Test that add_word was called
mock_container.add_word.assert_called_once_with(line_break)
def test_line_break_create_and_add_to_fallback(self):
"""Test LineBreak.create_and_add_to fallback behavior when no add methods available."""
# Create mock container without any add methods
mock_container = Mock()
del mock_container.add_line_break
del mock_container.add_element
del mock_container.add_word
# Create and add line break
line_break = LineBreak.create_and_add_to(mock_container)
# Test that line break was created
self.assertIsInstance(line_break, LineBreak)
# Test that parent was set manually
self.assertEqual(line_break.parent, mock_container)
if __name__ == '__main__':
unittest.main()