feat: 实现模板库metadata和跨域字体引用系统
实现了统一的metadata结构和字体作用域系统,支持文档和模板库之间的单向字体引用。 主要变更: - 模板库必须包含metadata字段(包括size、fonts、fonts_default) - 实现文档和模板库的size一致性校验 - 实现字体作用域系统(文档可引用模板库字体,反之不可) - 实现跨域循环引用检测 - 实现fonts_default级联规则(模板库→文档→系统默认) - 添加错误代码常量(SIZE_MISMATCH、FONT_NOT_FOUND等) - 更新文档和开发者指南 测试覆盖: - 新增33个测试(单元测试20个,集成测试13个) - 所有457个测试通过 Breaking Changes: - 模板库文件必须包含metadata字段 - 模板库metadata.size为必填字段 - 文档和模板库的size必须一致 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
241
tests/integration/test_template_metadata_integration.py
Normal file
241
tests/integration/test_template_metadata_integration.py
Normal file
@@ -0,0 +1,241 @@
|
||||
"""
|
||||
模板库 metadata 和跨域字体引用集成测试
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
from core.presentation import Presentation
|
||||
from loaders.yaml_loader import YAMLError
|
||||
|
||||
|
||||
class TestCrossDomainFontReference:
|
||||
"""跨域字体引用集成测试"""
|
||||
|
||||
def test_document_element_references_template_font(self, temp_dir):
|
||||
"""测试文档元素引用模板库字体"""
|
||||
# 创建模板库
|
||||
template_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
fonts:
|
||||
template-title:
|
||||
family: "SimSun"
|
||||
size: 36
|
||||
templates:
|
||||
test:
|
||||
elements: []
|
||||
"""
|
||||
template_path = temp_dir / "templates.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
# 创建文档
|
||||
doc_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
slides:
|
||||
- elements:
|
||||
- type: text
|
||||
content: "测试"
|
||||
box: [1, 1, 8, 1]
|
||||
font: "@template-title"
|
||||
"""
|
||||
doc_path = temp_dir / "doc.yaml"
|
||||
doc_path.write_text(doc_content)
|
||||
|
||||
# 应该正常加载和渲染
|
||||
pres = Presentation(str(doc_path), str(template_path))
|
||||
result = pres.render_slide(pres.data['slides'][0])
|
||||
|
||||
# 验证字体已解析
|
||||
assert len(result['elements']) == 1
|
||||
elem = result['elements'][0]
|
||||
assert elem.font.family == "SimSun"
|
||||
assert elem.font.size == 36
|
||||
|
||||
def test_template_element_cannot_reference_document_font(self, temp_dir):
|
||||
"""测试模板元素不能引用文档字体"""
|
||||
# 创建模板库
|
||||
template_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
templates:
|
||||
test:
|
||||
elements:
|
||||
- type: text
|
||||
content: "测试"
|
||||
box: [1, 1, 8, 1]
|
||||
font: "@doc-title"
|
||||
"""
|
||||
template_path = temp_dir / "templates.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
# 创建文档
|
||||
doc_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
fonts:
|
||||
doc-title:
|
||||
family: "Arial"
|
||||
size: 44
|
||||
slides:
|
||||
- template: test
|
||||
"""
|
||||
doc_path = temp_dir / "doc.yaml"
|
||||
doc_path.write_text(doc_content)
|
||||
|
||||
# 应该在渲染时抛出错误
|
||||
pres = Presentation(str(doc_path), str(template_path))
|
||||
with pytest.raises(YAMLError, match="引用的字体配置不存在"):
|
||||
pres.render_slide(pres.data['slides'][0])
|
||||
|
||||
|
||||
class TestSizeConsistencyIntegration:
|
||||
"""Size 一致性校验集成测试"""
|
||||
|
||||
def test_size_mismatch_prevents_loading(self, temp_dir):
|
||||
"""测试 size 不一致时阻止加载"""
|
||||
# 创建模板库
|
||||
template_content = """
|
||||
metadata:
|
||||
size: "4:3"
|
||||
templates:
|
||||
test:
|
||||
elements: []
|
||||
"""
|
||||
template_path = temp_dir / "templates.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
# 创建文档
|
||||
doc_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
slides:
|
||||
- elements: []
|
||||
"""
|
||||
doc_path = temp_dir / "doc.yaml"
|
||||
doc_path.write_text(doc_content)
|
||||
|
||||
# 应该在初始化时抛出错误
|
||||
with pytest.raises(YAMLError, match="文档尺寸.*与模板库尺寸.*不一致"):
|
||||
Presentation(str(doc_path), str(template_path))
|
||||
|
||||
|
||||
class TestCompleteRenderingFlow:
|
||||
"""完整渲染流程集成测试"""
|
||||
|
||||
def test_full_rendering_with_cross_domain_fonts(self, temp_dir):
|
||||
"""测试完整渲染流程(包含跨域字体引用)"""
|
||||
# 创建模板库
|
||||
template_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
fonts:
|
||||
template-base:
|
||||
family: "SimSun"
|
||||
size: 18
|
||||
template-title:
|
||||
parent: "@template-base"
|
||||
size: 36
|
||||
bold: true
|
||||
templates:
|
||||
title-slide:
|
||||
elements:
|
||||
- type: text
|
||||
content: "标题"
|
||||
box: [1, 1, 8, 1]
|
||||
font: "@template-title"
|
||||
"""
|
||||
template_path = temp_dir / "templates.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
# 创建文档
|
||||
doc_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
fonts:
|
||||
doc-body:
|
||||
parent: "@template-base"
|
||||
size: 24
|
||||
slides:
|
||||
- template: title-slide
|
||||
- elements:
|
||||
- type: text
|
||||
content: "正文"
|
||||
box: [1, 2, 8, 1]
|
||||
font: "@doc-body"
|
||||
"""
|
||||
doc_path = temp_dir / "doc.yaml"
|
||||
doc_path.write_text(doc_content)
|
||||
|
||||
# 应该正常加载和渲染
|
||||
pres = Presentation(str(doc_path), str(template_path))
|
||||
|
||||
# 渲染第一张幻灯片(使用模板)
|
||||
slide1 = pres.render_slide(pres.data['slides'][0])
|
||||
assert len(slide1['elements']) == 1
|
||||
elem1 = slide1['elements'][0]
|
||||
assert elem1.font.family == "SimSun"
|
||||
assert elem1.font.size == 36
|
||||
assert elem1.font.bold is True
|
||||
|
||||
# 渲染第二张幻灯片(文档元素引用模板库字体)
|
||||
slide2 = pres.render_slide(pres.data['slides'][1])
|
||||
assert len(slide2['elements']) == 1
|
||||
elem2 = slide2['elements'][0]
|
||||
assert elem2.font.family == "SimSun"
|
||||
assert elem2.font.size == 24
|
||||
|
||||
|
||||
class TestTableDualFontFields:
|
||||
"""表格双字体字段集成测试"""
|
||||
|
||||
def test_table_font_and_header_font(self, temp_dir):
|
||||
"""测试表格的 font 和 header_font 字段"""
|
||||
# 创建模板库
|
||||
template_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
fonts:
|
||||
table-body:
|
||||
family: "Arial"
|
||||
size: 14
|
||||
table-header:
|
||||
family: "Arial"
|
||||
size: 16
|
||||
bold: true
|
||||
templates:
|
||||
table-slide:
|
||||
elements:
|
||||
- type: table
|
||||
position: [1, 1]
|
||||
font: "@table-body"
|
||||
header_font: "@table-header"
|
||||
data:
|
||||
- ["列1", "列2"]
|
||||
- ["数据1", "数据2"]
|
||||
"""
|
||||
template_path = temp_dir / "templates.yaml"
|
||||
template_path.write_text(template_content)
|
||||
|
||||
# 创建文档
|
||||
doc_content = """
|
||||
metadata:
|
||||
size: "16:9"
|
||||
slides:
|
||||
- template: table-slide
|
||||
"""
|
||||
doc_path = temp_dir / "doc.yaml"
|
||||
doc_path.write_text(doc_content)
|
||||
|
||||
# 应该正常加载和渲染
|
||||
pres = Presentation(str(doc_path), str(template_path))
|
||||
result = pres.render_slide(pres.data['slides'][0])
|
||||
|
||||
# 验证表格字体已解析
|
||||
assert len(result['elements']) == 1
|
||||
table = result['elements'][0]
|
||||
assert table.font.family == "Arial"
|
||||
assert table.font.size == 14
|
||||
assert table.header_font.family == "Arial"
|
||||
assert table.header_font.size == 16
|
||||
assert table.header_font.bold is True
|
||||
Reference in New Issue
Block a user