107 lines
3.5 KiB
Python
107 lines
3.5 KiB
Python
from genesis.data_models import ChapterMarker, ParsedTemplate
|
|
from genesis.parsers._word_common import heading_level
|
|
from genesis.parsers.word_template_parser import WordTemplateParser
|
|
|
|
from tests.docx_helpers import new_document, save_document
|
|
|
|
|
|
def test_heading_level_parses_numeric_suffix():
|
|
assert heading_level("Heading 1") == 1
|
|
assert heading_level("Heading 2") == 2
|
|
assert heading_level("Heading 3") == 3
|
|
|
|
|
|
def test_heading_level_fallback_on_invalid():
|
|
# 兜底分支(非数字 / 无后缀)→ 1,分支覆盖必须命中
|
|
assert heading_level("Heading X") == 1
|
|
assert heading_level("Heading") == 1
|
|
|
|
|
|
def test_parse_extracts_heading_levels(tmp_path):
|
|
doc = new_document()
|
|
doc.add_heading("1. はじめに", level=1)
|
|
doc.add_heading("2.1 画面遷移図", level=2)
|
|
doc.add_heading("2.1.1 詳細", level=3)
|
|
path = save_document(tmp_path, doc)
|
|
|
|
result = WordTemplateParser().parse(path)
|
|
|
|
assert isinstance(result, ParsedTemplate)
|
|
headings = [s for s in result.sections if s.type == "heading"]
|
|
assert [(s.name, s.level) for s in headings] == [
|
|
("1. はじめに", 1),
|
|
("2.1 画面遷移図", 2),
|
|
("2.1.1 詳細", 3),
|
|
]
|
|
|
|
|
|
def test_parse_extracts_bookmark(tmp_path):
|
|
from docx.oxml.ns import qn
|
|
doc = new_document()
|
|
para = doc.add_paragraph("アンカー")
|
|
bm_start = para._p.makeelement(qn("w:bookmarkStart"), {qn("w:id"): "0", qn("w:name"): "template_start"})
|
|
para._p.insert(0, bm_start)
|
|
# 无 name 的书签:覆盖 if name 假分支,应被跳过
|
|
bm_anon = para._p.makeelement(qn("w:bookmarkStart"), {qn("w:id"): "1"})
|
|
para._p.insert(1, bm_anon)
|
|
path = save_document(tmp_path, doc)
|
|
|
|
result = WordTemplateParser().parse(path)
|
|
|
|
bookmarks = [s for s in result.sections if s.type == "bookmark"]
|
|
assert [s.name for s in bookmarks] == ["template_start"]
|
|
|
|
|
|
def test_parse_extracts_placeholders(tmp_path):
|
|
doc = new_document()
|
|
doc.add_paragraph("{{doc_title}}")
|
|
doc.add_paragraph("{{section:introduction}}")
|
|
doc.add_paragraph("{{section:function_list}}")
|
|
path = save_document(tmp_path, doc)
|
|
|
|
result = WordTemplateParser().parse(path)
|
|
|
|
assert result.placeholders == {
|
|
"doc_title": "{{doc_title}}",
|
|
"section:introduction": "{{section:introduction}}",
|
|
"section:function_list": "{{section:function_list}}",
|
|
}
|
|
ph = [s for s in result.sections if s.type == "placeholder"]
|
|
assert [s.name for s in ph] == ["doc_title", "section:introduction", "section:function_list"]
|
|
|
|
|
|
def test_parse_invalid_placeholder_kept_as_text(tmp_path):
|
|
doc = new_document()
|
|
doc.add_paragraph("{{ invalid }}")
|
|
doc.add_paragraph("ただの {text}")
|
|
path = save_document(tmp_path, doc)
|
|
|
|
result = WordTemplateParser().parse(path)
|
|
|
|
assert result.placeholders == {}
|
|
assert [s for s in result.sections if s.type == "placeholder"] == []
|
|
|
|
|
|
def test_parse_empty_document(tmp_path):
|
|
doc = new_document()
|
|
path = save_document(tmp_path, doc)
|
|
|
|
result = WordTemplateParser().parse(path)
|
|
|
|
assert result.sections == []
|
|
assert result.placeholders == {}
|
|
assert "Normal" in result.styles["defined"]
|
|
|
|
|
|
def test_parse_styles_collected(tmp_path):
|
|
doc = new_document()
|
|
doc.add_heading("章", level=1)
|
|
doc.add_paragraph("本文")
|
|
path = save_document(tmp_path, doc)
|
|
|
|
result = WordTemplateParser().parse(path)
|
|
|
|
assert "Heading 1" in result.styles["used"]
|
|
assert "Normal" in result.styles["used"]
|
|
assert "Heading 1" in result.styles["defined"]
|