148 lines
4.5 KiB
Python
148 lines
4.5 KiB
Python
"""Tests for WordRenderer — Markdown → .docx mapping (U2)."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from docx import Document
|
|
|
|
from agentkit.documents.renderers.word_renderer import WordRenderer
|
|
|
|
|
|
def _render(markdown: str, tmp_path: Path) -> Path:
|
|
"""Render markdown to a temp .docx and return the path."""
|
|
out = tmp_path / "test.docx"
|
|
WordRenderer().render(markdown, out)
|
|
return out
|
|
|
|
|
|
def _read_paragraphs(path: Path) -> list[str]:
|
|
"""Return all paragraph texts from a .docx."""
|
|
doc = Document(str(path))
|
|
return [p.text for p in doc.paragraphs]
|
|
|
|
|
|
def test_heading_levels(tmp_path: Path) -> None:
|
|
"""# / ## / ### map to heading levels 1/2/3."""
|
|
md = "# Title\n## Subtitle\n### Section\n"
|
|
path = _render(md, tmp_path)
|
|
doc = Document(str(path))
|
|
headings = [(p.style.name, p.text) for p in doc.paragraphs if p.text]
|
|
assert ("Heading 1", "Title") in headings
|
|
assert ("Heading 2", "Subtitle") in headings
|
|
assert ("Heading 3", "Section") in headings
|
|
|
|
|
|
def test_paragraphs(tmp_path: Path) -> None:
|
|
"""Plain text lines become paragraphs."""
|
|
md = "First paragraph.\n\nSecond paragraph.\n"
|
|
path = _render(md, tmp_path)
|
|
texts = _read_paragraphs(path)
|
|
assert "First paragraph." in texts
|
|
assert "Second paragraph." in texts
|
|
|
|
|
|
def test_bullet_list(tmp_path: Path) -> None:
|
|
"""Bullet items use List Bullet style."""
|
|
md = "- Apple\n- Banana\n- Cherry\n"
|
|
path = _render(md, tmp_path)
|
|
doc = Document(str(path))
|
|
bullets = [p for p in doc.paragraphs if p.style.name == "List Bullet"]
|
|
assert len(bullets) == 3
|
|
assert bullets[0].text == "Apple"
|
|
assert bullets[1].text == "Banana"
|
|
assert bullets[2].text == "Cherry"
|
|
|
|
|
|
def test_numbered_list(tmp_path: Path) -> None:
|
|
"""Numbered items use List Number style."""
|
|
md = "1. First\n2. Second\n3. Third\n"
|
|
path = _render(md, tmp_path)
|
|
doc = Document(str(path))
|
|
numbers = [p for p in doc.paragraphs if p.style.name == "List Number"]
|
|
assert len(numbers) == 3
|
|
assert numbers[0].text == "First"
|
|
assert numbers[1].text == "Second"
|
|
|
|
|
|
def test_table(tmp_path: Path) -> None:
|
|
"""GFM table maps to a docx table with correct cells."""
|
|
md = "| Name | Age |\n| --- | --- |\n| Alice | 30 |\n| Bob | 25 |\n"
|
|
path = _render(md, tmp_path)
|
|
doc = Document(str(path))
|
|
assert len(doc.tables) == 1
|
|
table = doc.tables[0]
|
|
# 3 rows (header + 2 data), 2 cols
|
|
assert len(table.rows) == 3
|
|
assert len(table.columns) == 2
|
|
assert table.cell(0, 0).text == "Name"
|
|
assert table.cell(0, 1).text == "Age"
|
|
assert table.cell(1, 0).text == "Alice"
|
|
assert table.cell(2, 1).text == "25"
|
|
|
|
|
|
def test_bold_inline(tmp_path: Path) -> None:
|
|
"""**bold** produces a bold run."""
|
|
md = "This has **bold** text.\n"
|
|
path = _render(md, tmp_path)
|
|
doc = Document(str(path))
|
|
para = doc.paragraphs[0]
|
|
bold_runs = [r for r in para.runs if r.bold]
|
|
assert len(bold_runs) == 1
|
|
assert bold_runs[0].text == "bold"
|
|
|
|
|
|
def test_italic_inline(tmp_path: Path) -> None:
|
|
"""*italic* produces an italic run."""
|
|
md = "This has *italic* text.\n"
|
|
path = _render(md, tmp_path)
|
|
doc = Document(str(path))
|
|
para = doc.paragraphs[0]
|
|
italic_runs = [r for r in para.runs if r.italic]
|
|
assert len(italic_runs) == 1
|
|
assert italic_runs[0].text == "italic"
|
|
|
|
|
|
def test_empty_markdown(tmp_path: Path) -> None:
|
|
"""Empty Markdown produces a valid (empty) document."""
|
|
path = _render("", tmp_path)
|
|
assert path.exists()
|
|
doc = Document(str(path))
|
|
# No paragraphs with text
|
|
assert all(not p.text for p in doc.paragraphs)
|
|
|
|
|
|
def test_mixed_content(tmp_path: Path) -> None:
|
|
"""Heading + paragraph + list + table renders without error."""
|
|
md = """# Report
|
|
|
|
This is the intro.
|
|
|
|
- Point one
|
|
- Point two
|
|
|
|
| Col A | Col B |
|
|
| ----- | ----- |
|
|
| 1 | 2 |
|
|
|
|
Final paragraph.
|
|
"""
|
|
path = _render(md, tmp_path)
|
|
assert path.exists()
|
|
doc = Document(str(path))
|
|
# Should have at least one heading, one table, two bullet items
|
|
headings = [p for p in doc.paragraphs if p.style.name.startswith("Heading")]
|
|
assert len(headings) >= 1
|
|
assert len(doc.tables) == 1
|
|
bullets = [p for p in doc.paragraphs if p.style.name == "List Bullet"]
|
|
assert len(bullets) == 2
|
|
|
|
|
|
def test_chinese_text(tmp_path: Path) -> None:
|
|
"""Chinese characters render correctly in paragraphs and headings."""
|
|
md = "# 中文标题\n\n这是中文段落。\n"
|
|
path = _render(md, tmp_path)
|
|
texts = _read_paragraphs(path)
|
|
assert "中文标题" in texts
|
|
assert "这是中文段落。" in texts
|