Fix nested chapter anchors and Chinese ordinal navigation

This commit is contained in:
Kai Tan
2026-10-08 18:18:49 +08:00
parent 5dbd7bd240
commit 7f1ae23fe2
4 changed files with 105 additions and 4 deletions
+36
View File
@@ -279,3 +279,39 @@ def test_number_only_part_heading_is_bound_without_matching_prose():
assert reason is None and [h['id'] for h in headings] == ['part']
assert _headings(parse_html('<p>Part IV</p>'), 'part.xhtml', 'Part IV')[0] == []
assert normalize_heading_html({'role': 'part', 'number': 4}, '第四部分') == '第四部'
def test_nested_toc_anchor_binds_heading_and_normalizes_visible_ordinal():
from src.common.html import parse_html
from src.preprocessing.navigation import _headings, normalize_heading_html
soup = parse_html('<h2 id="ch1"><a id="sec3" href="contents.xhtml#rsec3">'
'<span class="sr-only">Chapter</span><span class="chno">1</span> '
'<span class="chaptit">Obsession</span></a></h2>'
'<p class="stars">° ° °</p><p>Unrelated prose.</p>')
headings, reason = _headings(soup, 'chapter.xhtml#sec3', 'Chapter 1. Obsession')
assert reason is None and [h['id'] for h in headings] == ['ch1']
translated = str(soup.h2.a).replace('Chapter', '第一章').replace('Obsession', '痴迷')
record = {'role': 'chapter', 'number': 1}
normalized = normalize_heading_html(record, translated)
result = parse_html(normalized)
assert result.find(class_='chno').get_text() == '第一章'
assert result.find(class_='sr-only')['aria-hidden'] == 'true'
assert result.find('a')['id'] == 'sec3'
assert result.find('a')['href'] == 'contents.xhtml#rsec3'
assert normalize_heading_html(record, normalized) == normalized
from src.common.data_model import BookMetaData, ManifestEntry
from src.preprocessing.navigation import navigation_labels
entry = ManifestEntry(entry_id='chapter.xhtml#ch1', file_path='chapter.xhtml', element_id='ch1',
original_text='Chapter 1 Obsession', translated_text='第一章 痴迷',
translated_html=normalized)
structure = BookStructure(metadata=BookMetaData(), navigation_titles=[{
**record, 'entry_id': entry.entry_id, 'target': 'chapter.xhtml#sec3'}])
assert navigation_labels(structure, [entry])['chapter.xhtml#sec3'] == '第一章:痴迷'
split = '<span class="sr-only">第</span><span class="chno">九</span>章 <span>考试帝国扩张</span>'
ninth = parse_html(normalize_heading_html({'role':'chapter','number':9}, split))
assert ninth.find(class_='chno').get_text() == '第九章'
assert '章章' not in ninth.get_text()
assert not any(str(n).strip() == '章' for n in ninth.find_all(string=True))
# A nested anchor in ordinary prose must still not become a title.
assert not _headings(parse_html('<p><a id="prose">A sentence.</a></p>'),
'chapter.xhtml#prose', 'Chapter 1. Obsession')[0]