import unittest import os import shutil import sys from ebooklib import epub # Ensure import path sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) from abogen.book_parser import get_book_parser class TestEpubHtmlNavParsing(unittest.TestCase): """ Tests for EPUB 3 HTML5 Navigation Document parsing logic (_parse_html_nav_li). """ def setUp(self): self.test_dir = "tests/test_data_nav" if os.path.exists(self.test_dir): shutil.rmtree(self.test_dir) os.makedirs(self.test_dir) self.epub_path = os.path.join(self.test_dir, "nav_test.epub") def tearDown(self): if os.path.exists(self.test_dir): shutil.rmtree(self.test_dir) def _create_epub_with_custom_nav(self, nav_html_content): """ Creates an EPUB with a manually injected HTML Navigation Document. """ book = epub.EpubBook() book.set_identifier("navtest123") book.set_title("Nav Test Book") # Add some content files c1 = epub.EpubHtml(title="Chapter 1", file_name="chap1.xhtml", lang="en") c1.content = "
Text 1
" book.add_item(c1) c2 = epub.EpubHtml(title="Chapter 2", file_name="chap2.xhtml", lang="en") c2.content = "Text 2
" book.add_item(c2) # Create the Nav item manually to control the HTML structure exactly # Use EpubHtml + OPF patching because EpubNav forces auto-generation nav = epub.EpubHtml(title="Nav", file_name="nav.xhtml") nav.content = nav_html_content book.add_item(nav) # We must set spine manually book.spine = [nav, c1, c2] epub.write_epub(self.epub_path, book) # Patch the OPF to remove toc="ncx" default which causes crash # because we intentionally excluded the legacy NCX file. import zipfile with zipfile.ZipFile(self.epub_path, "r") as zin: opf_content = zin.read("EPUB/content.opf").decode("utf-8") opf_content = opf_content.replace('toc="ncx"', "") # Repack TEMP_EPUB = self.epub_path + ".temp" with zipfile.ZipFile(TEMP_EPUB, "w") as zout: for item in zin.infolist(): if item.filename == "EPUB/content.opf": zout.writestr(item, opf_content) else: zout.writestr(item, zin.read(item.filename)) shutil.move(TEMP_EPUB, self.epub_path) def test_basic_html_nav_parsing(self): """ Test parsing of a standard flat list of links. """ nav_html = """ """ self._create_epub_with_custom_nav(nav_html) parser = get_book_parser(self.epub_path) parser.process_content() chapters = parser.get_chapters() # Filter out "Nav" or "Introduction" prefix content found from the Nav file itself chapters = [c for c in chapters if "Chapter" in c[1] or "Section" in c[1]] self.assertEqual(len(chapters), 2) self.assertEqual(chapters[0][1], "Chapter 1") self.assertEqual(chapters[1][1], "Chapter 2") def test_nested_html_nav_parsing(self): """ Test parsing of nested lists (Sub-chapters). """ nav_html = """ """ # Note: In this test setup, chap2 is serving as "Section 1.1" effectively self._create_epub_with_custom_nav(nav_html) parser = get_book_parser(self.epub_path) parser.process_content() chapters = parser.get_chapters() ids = [c[1] for c in chapters] self.assertIn("Chapter 1", ids) self.assertIn("Section 1.1", ids) def test_span_header_parsing(self): """ Test parsing of