Preformu / tests /test_docx_export.py
Kevinshh's picture
feat: 意图保真(intent-fidelity) + 描述性梳理技能 + 相容性引擎升级; 修复转置宽表解析/CQA对账/澄清交互/功能切换串显; .gitignore 排除专利与机密Demo数据
0e6887b
Raw
History Blame Contribute Delete
5.64 kB
"""HTML → 专业 Word(.docx) 转换器测试。
覆盖:表格转 Word 表格、标题分级、Markdown/AI 噪声剥离、style/svg 丢弃、
中文与产物可被 python-docx 重新打开(结构有效)。
"""
from __future__ import annotations
from io import BytesIO
import pytest
from services.docx_export import html_to_docx, docx_available
pytestmark = pytest.mark.skipif(not docx_available(), reason="python-docx 未安装")
_SAMPLE_HTML = """
<!DOCTYPE html><html><head><style>.x{color:red}</style><title>T</title></head>
<body><div class="report-container">
<div class="report-header">
<div class="brand-name">Pharma K</div>
<div class="brand-subtitle">专家系统</div>
<div class="report-type">质量属性梳理报告</div>
<div class="report-date">2026-06-07 00:56</div>
</div>
<div class="kpi-row">
<div class="kpi-card"><div class="kpi-value">37</div><div class="kpi-label">分组</div></div>
<div class="kpi-card"><div class="kpi-value">64</div><div class="kpi-label">观测</div></div>
</div>
<div class="section">
<div class="section-title">各质量属性梳理</div>
<div class="section-body">
<p>溶化时限符合限度。**这段含粗体**和 *斜体* 应被清理。</p>
<p>* 这是一个伪列表行</p>
</div>
</div>
<div class="section">
<div class="section-title">含量均匀度(接受值判定)</div>
<div class="section-body">
<table><thead><tr><th>规格</th><th>AV</th><th>判定</th></tr></thead>
<tbody><tr><td>20μg</td><td>5.074</td><td>符合</td></tr>
<tr><td>40μg</td><td>3.418</td><td>符合</td></tr></tbody></table>
</div>
</div>
<svg><rect/></svg>
</div></body></html>
"""
def _open(data: bytes):
from docx import Document
return Document(BytesIO(data))
def test_returns_valid_docx_bytes():
data = html_to_docx(_SAMPLE_HTML)
assert data and data[:2] == b"PK" # docx 是 zip,魔数 PK
doc = _open(data) # 可被 python-docx 重新打开
assert doc is not None
def test_tables_become_real_word_tables():
doc = _open(html_to_docx(_SAMPLE_HTML))
assert len(doc.tables) >= 1
cu = [t for t in doc.tables if t.rows and t.rows[0].cells[0].text == "规格"]
assert cu, "含量均匀度表应转为 Word 表格且表头为规格"
tbl = cu[0]
assert tbl.rows[0].cells[1].text == "AV"
assert tbl.cell(1, 0).text == "20μg"
assert tbl.cell(1, 2).text == "符合"
def test_markdown_and_ai_noise_stripped():
doc = _open(html_to_docx(_SAMPLE_HTML))
all_text = "\n".join(p.text for p in doc.paragraphs)
assert "**" not in all_text and "*" not in all_text
assert "这段含粗体" in all_text
assert "斜体" in all_text
def test_style_and_svg_dropped():
doc = _open(html_to_docx(_SAMPLE_HTML))
all_text = "\n".join(p.text for p in doc.paragraphs)
assert "color:red" not in all_text
assert "rect" not in all_text
def test_headings_present():
doc = _open(html_to_docx(_SAMPLE_HTML))
texts = [p.text for p in doc.paragraphs]
assert "Pharma K" in texts
assert any("各质量属性梳理" in t for t in texts)
def test_empty_html_returns_none():
assert html_to_docx("") is None
def test_meta_void_tags_do_not_swallow_body():
"""回归:<head> 中的 void 元素 <meta>(无闭合)不得吞掉 <body>(致空文档)。"""
html = (
'<!DOCTYPE html><html lang="zh-CN"><head>'
'<meta charset="UTF-8">'
'<meta name="viewport" content="width=device-width, initial-scale=1.0">'
'<title>T</title><style>.x{color:red}</style></head>'
'<body><div class="section"><div class="section-title">分节标题</div>'
'<div class="section-body"><p>正文内容</p></div></div></body></html>'
)
doc = _open(html_to_docx(html))
texts = [p.text for p in doc.paragraphs]
assert any("分节标题" in t for t in texts)
assert any("正文内容" in t for t in texts)
# ---------------------------------------------------------------------------
# adaptive-report-visualization 任务 12:内联 PNG → Word 图片
# ---------------------------------------------------------------------------
def _real_png_datauri():
"""用 ChartService 生成一张真实 PNG 的 data-uri(需 matplotlib)。"""
pytest.importorskip("matplotlib")
from services.chart_service import ChartService
res = ChartService().grouped_bar(["A", "B"], [1.0, 2.0], title="t")
assert res.ok
return res.image_base64
def test_inline_png_embedded_as_word_picture():
png = _real_png_datauri()
html = (
"<html><body><div class='section'>"
"<div class='section-title'>图表化呈现</div>"
f"<figure><img src=\"{png}\"><figcaption>含量均匀度</figcaption></figure>"
"<table><tr><th>单位</th><th>值</th></tr><tr><td>1</td><td>99.19</td></tr></table>"
"</div></body></html>"
)
doc = _open(html_to_docx(html))
# 图片被嵌入为 inline shape;配套数据表保留。
assert len(doc.inline_shapes) >= 1
assert len(doc.tables) >= 1
assert any("99.19" in c.text for t in doc.tables for r in t.rows for c in r.cells)
def test_bad_data_uri_skipped_without_crash():
html = "<html><body><p>前</p><img src='data:image/png;base64,@@@bad@@@'><p>后</p></body></html>"
data = html_to_docx(html)
assert data and data[:2] == b"PK" # 不崩溃,仍产出有效 docx
doc = _open(data)
texts = "\n".join(p.text for p in doc.paragraphs)
assert "前" in texts and "后" in texts