""" 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 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()