1
0
Files
PPTX/tests/unit/test_template.py
lanyuanxiaoyao 2fd8bc1b4a feat: 为metadata、模板和幻灯片添加description字段支持
添加可选的description字段用于文档目的,不影响渲染输出。

主要更改:
- core/presentation.py: 添加metadata.description属性
- core/template.py: 添加template.description属性
- tests: 添加16个新测试用例验证description功能
- docs: 更新README.md和README_DEV.md文档
- specs: 新增page-description规范文件
2026-03-04 13:22:33 +08:00

1058 lines
33 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
模板系统单元测试
测试 Template 类的初始化、变量解析、条件渲染和模板渲染功能
"""
import pytest
from pathlib import Path
from loaders.yaml_loader import YAMLError
from core.template import Template
from core.presentation import Presentation
# ============= 模板初始化测试 =============
class TestTemplateInit:
"""Template 初始化测试类"""
def test_init_without_template_dir_raises_error(self, temp_dir):
"""测试未指定模板目录会引发错误"""
with pytest.raises(YAMLError, match="未指定模板目录"):
Template("title-slide", templates_dir=None)
def test_init_with_path_separator_in_name_raises_error(self, temp_dir):
"""测试模板名称包含路径分隔符会引发错误"""
with pytest.raises(YAMLError, match="模板名称不能包含路径分隔符"):
Template("../etc/passwd", templates_dir=temp_dir)
def test_init_with_nonexistent_template_raises_error(self, temp_dir):
"""测试模板文件不存在会引发错误"""
with pytest.raises(YAMLError, match="模板文件不存在"):
Template("nonexistent", templates_dir=temp_dir)
def test_init_with_valid_template(self, sample_template):
"""测试使用有效模板初始化"""
template = Template("title-slide", templates_dir=sample_template)
assert template.data is not None
assert "elements" in template.data
assert "vars" in template.data
# ============= 变量解析测试 =============
class TestResolveValue:
"""resolve_value 方法测试类"""
def test_resolve_value_simple_variable(self, sample_template):
"""测试解析简单变量"""
template = Template("title-slide", templates_dir=sample_template)
result = template.resolve_value("{title}", {"title": "My Title"})
assert result == "My Title"
def test_resolve_value_multiple_variables(self, sample_template):
"""测试解析多个变量"""
template = Template("title-slide", templates_dir=sample_template)
result = template.resolve_value(
"{title} - {subtitle}", {"title": "Main", "subtitle": "Sub"}
)
assert result == "Main - Sub"
def test_resolve_value_undefined_variable_raises_error(self, sample_template):
"""测试未定义变量会引发错误"""
template = Template("title-slide", templates_dir=sample_template)
with pytest.raises(YAMLError, match="未定义的变量"):
template.resolve_value("{undefined}", {"title": "Test"})
def test_resolve_value_preserves_non_string(self, sample_template):
"""测试非字符串值保持原样"""
template = Template("title-slide", templates_dir=sample_template)
assert template.resolve_value(123, {}) == 123
assert template.resolve_value(None, {}) is None
assert template.resolve_value(["list"], {}) == ["list"]
def test_resolve_value_converts_to_integer(self, sample_template):
"""测试结果转换为整数"""
template = Template("title-slide", templates_dir=sample_template)
result = template.resolve_value("{value}", {"value": "42"})
assert result == 42
assert isinstance(result, int)
def test_resolve_value_converts_to_float(self, sample_template):
"""测试结果转换为浮点数"""
template = Template("title-slide", templates_dir=sample_template)
result = template.resolve_value("{value}", {"value": "3.14"})
assert result == 3.14
assert isinstance(result, float)
# ============= resolve_element 测试 =============
class TestResolveElement:
"""resolve_element 方法测试类"""
def test_resolve_element_dict(self, sample_template):
"""测试解析字典元素"""
template = Template("title-slide", templates_dir=sample_template)
elem = {"content": "{title}", "box": [1, 2, 3, 4]}
result = template.resolve_element(elem, {"title": "Test"})
assert result["content"] == "Test"
assert result["box"] == [1, 2, 3, 4]
def test_resolve_element_list(self, sample_template):
"""测试解析列表元素"""
template = Template("title-slide", templates_dir=sample_template)
elem = ["{a}", "{b}", "static"]
result = template.resolve_element(elem, {"a": "A", "b": "B"})
assert result == ["A", "B", "static"]
def test_resolve_element_nested_structure(self, sample_template):
"""测试解析嵌套结构"""
template = Template("title-slide", templates_dir=sample_template)
elem = {"font": {"size": "{size}", "color": "#000000"}}
result = template.resolve_element(elem, {"size": "24"})
assert result["font"]["size"] == 24
assert result["font"]["color"] == "#000000"
def test_resolve_element_excludes_visible(self, sample_template):
"""测试 visible 字段被排除"""
template = Template("title-slide", templates_dir=sample_template)
elem = {"content": "test", "visible": "{condition}"}
result = template.resolve_element(elem, {})
assert "visible" not in result
# ============= 条件渲染测试 =============
class TestEvaluateCondition:
"""evaluate_condition 方法测试类"""
def test_evaluate_condition_with_non_empty_variable(self, sample_template):
"""测试非空变量条件为真"""
template = Template("title-slide", templates_dir=sample_template)
result = template.evaluate_condition(
"{subtitle != ''}", {"subtitle": "Test Subtitle"}
)
assert result is True
def test_evaluate_condition_with_empty_variable(self, sample_template):
"""测试空变量条件为假"""
template = Template("title-slide", templates_dir=sample_template)
result = template.evaluate_condition("{subtitle != ''}", {"subtitle": ""})
assert result is False
def test_evaluate_condition_with_missing_variable(self, sample_template):
"""测试缺失变量会抛出错误"""
template = Template("title-slide", templates_dir=sample_template)
with pytest.raises(YAMLError, match="条件表达式中的变量未定义"):
template.evaluate_condition("{subtitle != ''}", {})
def test_evaluate_condition_complex_logic(self, sample_template):
"""测试复杂逻辑表达式"""
template = Template("title-slide", templates_dir=sample_template)
result = template.evaluate_condition(
"{count > 0 and status == 'active'}",
{"count": 5, "status": "active"}
)
assert result is True
def test_evaluate_condition_member_test(self, sample_template):
"""测试成员测试表达式"""
template = Template("title-slide", templates_dir=sample_template)
result = template.evaluate_condition(
"{status in ['draft', 'review']}",
{"status": "draft"}
)
assert result is True
def test_evaluate_condition_math_operation(self, sample_template):
"""测试数学运算表达式"""
template = Template("title-slide", templates_dir=sample_template)
result = template.evaluate_condition(
"{(price * discount) > 50}",
{"price": 100, "discount": 0.8}
)
assert result is True
# ============= 模板渲染测试 =============
class TestRender:
"""render 方法测试类"""
def test_render_with_required_variable(self, sample_template):
"""测试渲染包含必需变量的模板"""
template = Template("title-slide", templates_dir=sample_template)
result = template.render({"title": "My Presentation"})
# 由于条件渲染subtitle元素被跳过只返回1个元素
assert len(result) == 1
assert result[0]["content"] == "My Presentation"
def test_render_with_optional_variable(self, sample_template):
"""测试渲染包含可选变量的模板"""
template = Template("title-slide", templates_dir=sample_template)
result = template.render({"title": "Test", "subtitle": "Subtitle"})
assert len(result) == 2 # 两个元素
def test_render_without_optional_variable(self, sample_template):
"""测试不提供可选变量"""
template = Template("title-slide", templates_dir=sample_template)
result = template.render({"title": "Test"})
# subtitle 元素应该被跳过visible 条件)
assert len(result) == 1
def test_render_missing_required_variable_raises_error(self, sample_template):
"""测试缺少必需变量会引发错误"""
template = Template("title-slide", templates_dir=sample_template)
with pytest.raises(YAMLError, match="缺少必需变量"):
template.render({}) # 缺少 title
def test_render_with_default_value(self, temp_dir):
"""测试使用默认值"""
# 创建带默认值的模板
template_content = """
vars:
- name: title
required: true
- name: subtitle
required: false
default: "Default Subtitle"
elements:
- type: text
content: "{title}"
- type: text
content: "{subtitle}"
"""
template_file = temp_dir / "templates" / "default-test.yaml"
template_file.parent.mkdir(exist_ok=True)
template_file.write_text(template_content)
template = Template("default-test", templates_dir=temp_dir / "templates")
result = template.render({"title": "Test"})
assert len(result) == 2
def test_render_filters_elements_by_visible_condition(self, sample_template):
"""测试根据 visible 条件过滤元素"""
template = Template("title-slide", templates_dir=sample_template)
# 有 subtitle - 应该显示两个元素
result_with = template.render({"title": "Test", "subtitle": "Sub"})
assert len(result_with) == 2
# 无 subtitle - 应该只显示一个元素
result_without = template.render({"title": "Test"})
assert len(result_without) == 1
# ============= 边界情况补充测试 =============
class TestTemplateBoundaryCases:
"""模板系统边界情况测试"""
def test_nested_variable_resolution(self, temp_dir):
"""测试嵌套变量解析"""
template_content = """
vars:
- name: title
required: true
- name: full_title
required: false
default: "{title} - Extended"
elements:
- type: text
content: "{full_title}"
box: [0, 0, 1, 1]
font: {}
"""
template_file = temp_dir / "templates" / "nested.yaml"
template_file.parent.mkdir(exist_ok=True)
template_file.write_text(template_content)
template = Template("nested", templates_dir=temp_dir / "templates")
result = template.render({"title": "Main"})
# 默认值中的变量应该被解析
assert result[0]["content"] == "Main - Extended"
def test_variable_with_special_characters(self, temp_dir):
"""测试变量包含特殊字符"""
template_content = """
vars:
- name: title
required: true
elements:
- type: text
content: "{title}"
box: [0, 0, 1, 1]
font: {}
"""
template_file = temp_dir / "templates" / "special.yaml"
template_file.parent.mkdir(exist_ok=True)
template_file.write_text(template_content)
template = Template("special", templates_dir=temp_dir / "templates")
special_values = [
"Test & Data",
"Test <Script>",
'Test "Quotes"',
"Test 'Apostrophe'",
"测试中文",
"Test: colon",
"Test; semi",
]
for value in special_values:
result = template.render({"title": value})
assert result[0]["content"] == value
def test_variable_with_numeric_value(self, temp_dir):
"""测试数字变量值"""
template_content = """
vars:
- name: width
required: true
elements:
- type: text
content: "Width: {width}"
box: [0, 0, 1, 1]
font: {}
"""
template_file = temp_dir / "templates" / "numeric.yaml"
template_file.parent.mkdir(exist_ok=True)
template_file.write_text(template_content)
template = Template("numeric", templates_dir=temp_dir / "templates")
result = template.render({"width": "100"})
# 应该保持为字符串(因为是在 content 中)
assert result[0]["content"] == "Width: 100"
def test_empty_vars_list(self, temp_dir):
"""测试空的 vars 列表"""
template_content = """
vars: []
elements:
- type: text
content: "Static Content"
box: [0, 0, 1, 1]
font: {}
"""
template_file = temp_dir / "templates" / "novars.yaml"
template_file.parent.mkdir(exist_ok=True)
template_file.write_text(template_content)
template = Template("novars", templates_dir=temp_dir / "templates")
result = template.render({})
assert len(result) == 1
def test_multiple_visible_conditions(self, temp_dir):
"""测试多个可见性条件"""
template_content = """
vars:
- name: title
required: true
- name: subtitle
required: false
default: ""
- name: footer
required: false
default: ""
elements:
- type: text
content: "{title}"
box: [0, 0, 1, 1]
font: {}
- type: text
content: "{subtitle}"
visible: "{subtitle != ''}"
box: [0, 1, 1, 1]
font: {}
- type: text
content: "{footer}"
visible: "{footer != ''}"
box: [0, 2, 1, 1]
font: {}
"""
template_file = temp_dir / "templates" / "multi-condition.yaml"
template_file.parent.mkdir(exist_ok=True)
template_file.write_text(template_content)
template = Template("multi-condition", templates_dir=temp_dir / "templates")
# 只有 title
result1 = template.render({"title": "Test"})
assert len(result1) == 1
# 有 subtitle 和 footer
result2 = template.render(
{"title": "Test", "subtitle": "Sub", "footer": "Foot"}
)
assert len(result2) == 3
def test_variable_in_position(self, temp_dir):
"""测试变量在位置中使用"""
template_content = """
vars:
- name: x_pos
required: true
elements:
- type: text
content: "Test"
box: ["{x_pos}", 1, 1, 1]
font: {}
"""
template_file = temp_dir / "templates" / "pos-var.yaml"
template_file.parent.mkdir(exist_ok=True)
template_file.write_text(template_content)
template = Template("pos-var", templates_dir=temp_dir / "templates")
result = template.render({"x_pos": "2"})
# 变量应该被解析为数字
assert result[0]["box"][0] == 2
def test_empty_template_elements(self, temp_dir):
"""测试空元素列表的模板"""
template_content = """
vars: []
elements: []
"""
template_file = temp_dir / "templates" / "empty.yaml"
template_file.parent.mkdir(exist_ok=True)
template_file.write_text(template_content)
template = Template("empty", templates_dir=temp_dir / "templates")
result = template.render({})
assert result == []
def test_variable_replacement_in_font(self, temp_dir):
"""测试字体属性中的变量替换"""
template_content = """
vars:
- name: font_size
required: true
- name: text_color
required: true
elements:
- type: text
content: "Styled Text"
box: [0, 0, 1, 1]
font:
size: "{font_size}"
color: "{text_color}"
bold: true
"""
template_file = temp_dir / "templates" / "font-vars.yaml"
template_file.parent.mkdir(exist_ok=True)
template_file.write_text(template_content)
template = Template("font-vars", templates_dir=temp_dir / "templates")
result = template.render({"font_size": "24", "text_color": "#ff0000"})
assert result[0]["font"]["size"] == 24
assert result[0]["font"]["color"] == "#ff0000"
assert result[0]["font"]["color"] == "#ff0000"
# ============= Template.from_data() 方法测试 =============
class TestTemplateFromData:
"""Template.from_data() 类方法测试类"""
def test_from_data_with_valid_template(self):
"""测试从字典创建模板"""
template_data = {
'vars': [
{'name': 'title', 'required': True},
{'name': 'subtitle', 'required': False, 'default': ''}
],
'elements': [
{'type': 'text', 'box': [1, 2, 8, 1], 'content': '{title}'},
{'type': 'text', 'box': [1, 3.5, 8, 0.5], 'content': '{subtitle}', 'visible': "{subtitle != ''}"}
]
}
template = Template.from_data(template_data, 'test-template')
assert template.data == template_data
assert template.vars_def == {'title': {'name': 'title', 'required': True}, 'subtitle': {'name': 'subtitle', 'required': False, 'default': ''}}
assert template.elements == template_data['elements']
assert len(template.elements) == 2
def test_from_data_without_vars(self):
"""测试没有 vars 的模板"""
template_data = {
'elements': [
{'type': 'text', 'box': [1, 2, 8, 1], 'content': 'Static Content'}
]
}
template = Template.from_data(template_data, 'no-vars-template')
assert template.vars_def == {}
assert len(template.elements) == 1
def test_from_data_with_empty_vars(self):
"""测试 vars 为空列表的模板"""
template_data = {
'vars': [],
'elements': [
{'type': 'text', 'box': [1, 2, 8, 1], 'content': 'Static'}
]
}
template = Template.from_data(template_data, 'empty-vars-template')
assert template.vars_def == {}
def test_from_data_render_with_vars(self):
"""测试 from_data 创建的模板可以正常渲染"""
template_data = {
'vars': [
{'name': 'title', 'required': True}
],
'elements': [
{'type': 'text', 'box': [1, 2, 8, 1], 'content': '{title}'}
]
}
template = Template.from_data(template_data, 'test-template')
result = template.render({'title': 'My Title'})
assert len(result) == 1
assert result[0]['content'] == 'My Title'
def test_from_data_render_with_default_value(self):
"""测试 from_data 创建的模板支持默认值"""
template_data = {
'vars': [
{'name': 'title', 'required': True},
{'name': 'subtitle', 'required': False, 'default': 'Default Subtitle'}
],
'elements': [
{'type': 'text', 'box': [1, 2, 8, 1], 'content': '{title}'},
{'type': 'text', 'box': [1, 3.5, 8, 0.5], 'content': '{subtitle}', 'visible': "{subtitle != ''}"}
]
}
template = Template.from_data(template_data, 'test-template')
# 不提供 subtitle应该使用默认值
result = template.render({'title': 'My Title'})
assert len(result) == 2
assert result[1]['content'] == 'Default Subtitle'
def test_from_data_with_conditional_element(self):
"""测试 from_data 创建的模板支持条件渲染"""
template_data = {
'vars': [
{'name': 'subtitle', 'required': False, 'default': ''}
],
'elements': [
{'type': 'text', 'box': [1, 2, 8, 1], 'content': '{title}'},
{'type': 'text', 'box': [1, 3.5, 8, 0.5], 'content': '{subtitle}', 'visible': "{subtitle != ''}"}
]
}
template = Template.from_data(template_data, 'test-template')
# 不提供 subtitle元素应该被过滤
result = template.render({'title': 'Main', 'subtitle': ''})
assert len(result) == 1
assert result[0]['content'] == 'Main'
# 提供 subtitle元素应该被显示
result = template.render({'title': 'Main', 'subtitle': 'Subtitle'})
assert len(result) == 2
def test_from_data_with_nested_variable_in_default(self):
"""测试 from_data 创建的模板支持嵌套变量默认值"""
template_data = {
'vars': [
{'name': 'title', 'required': True},
{'name': 'full_title', 'required': False, 'default': '{title} - Extended'}
],
'elements': [
{'type': 'text', 'box': [1, 2, 8, 1], 'content': '{full_title}'}
]
}
template = Template.from_data(template_data, 'test-template')
result = template.render({'title': 'Main'})
# 默认值中的 {title} 应该被替换
assert result[0]['content'] == 'Main - Extended'
def test_from_data_rejects_template_reference_in_elements(self):
"""测试内联模板元素中引用其他模板应该被拒绝"""
template_data = {
'vars': [
{'name': 'title', 'required': True}
],
'elements': [
{'type': 'text', 'box': [1, 2, 8, 1], 'content': '{title}', 'template': 'other-template'}
]
}
template = Template.from_data(template_data, 'test-template')
# 渲染时应该抛出错误
with pytest.raises(YAMLError, match="内联模板不支持相互引用"):
template.render({'title': 'My Title'})
class TestInlineTemplates:
"""内联模板功能集成测试类"""
def test_presentation_with_inline_templates(self, temp_dir):
"""测试演示文稿包含内联模板定义"""
yaml_content = '''
metadata:
size: "16:9"
templates:
my-template:
vars:
- name: title
required: true
elements:
- type: text
box: [1, 2, 8, 1]
content: "{title}"
slides:
- template: my-template
vars:
title: "内联模板测试"
'''
yaml_file = temp_dir / "inline_test.yaml"
yaml_file.write_text(yaml_content, encoding='utf-8')
from core.presentation import Presentation
pres = Presentation(str(yaml_file))
assert 'my-template' in pres.inline_templates
assert len(pres.inline_templates) == 1
def test_presentation_without_inline_templates(self, temp_dir):
"""测试演示文稿不包含内联模板定义"""
yaml_content = '''
metadata:
size: "16:9"
slides:
- elements:
- type: text
box: [1, 2, 8, 1]
content: "直接元素"
'''
yaml_file = temp_dir / "no_inline_test.yaml"
yaml_file.write_text(yaml_content, encoding='utf-8')
from core.presentation import Presentation
pres = Presentation(str(yaml_file))
assert hasattr(pres, 'inline_templates')
assert pres.inline_templates == {}
class TestInlineTemplateNameConflict:
"""内联和外部模板同名冲突测试类"""
def test_inline_and_external_template_same_name_raises_error(self, temp_dir):
"""测试内联和外部模板同名时抛出 ERROR"""
from core.presentation import Presentation
# 创建外部模板
templates_dir = temp_dir / "templates"
templates_dir.mkdir(exist_ok=True)
external_template_content = '''vars:
- name: title
required: true
elements:
- type: text
content: "{title}"
'''
(templates_dir / "conflict-template.yaml").write_text(external_template_content, encoding='utf-8')
# 创建演示文稿 YAML包含同名内联模板
yaml_content = '''metadata:
size: "16:9"
templates:
conflict-template:
vars:
- name: title
required: true
elements:
- type: text
content: "{title}"
slides:
- template: conflict-template
vars:
title: "测试"
'''
yaml_file = temp_dir / "test.yaml"
yaml_file.write_text(yaml_content, encoding='utf-8')
# 应该抛出 YAMLError
with pytest.raises(YAMLError, match="模板名称冲突"):
pres = Presentation(str(yaml_file), templates_dir=str(templates_dir))
# 手动调用 get_template 来触发冲突检测
pres.get_template('conflict-template')
def test_inline_only_no_conflict(self, temp_dir):
"""测试只有内联模板时正常工作"""
yaml_content = '''
metadata:
size: "16:9"
templates:
my-template:
vars:
- name: title
required: true
elements:
- type: text
content: "{title}"
slides:
- template: my-template
vars:
title: "测试"
'''
yaml_file = temp_dir / "inline_only.yaml"
yaml_file.write_text(yaml_content, encoding='utf-8')
from core.presentation import Presentation
pres = Presentation(str(yaml_file))
# 不提供外部模板目录,应该正常加载
assert 'my-template' in pres.inline_templates
slide_data = pres.data['slides'][0]
rendered = pres.render_slide(slide_data)
assert len(rendered['elements']) == 1
class TestInlineTemplateErrorHandling:
"""内联模板错误处理测试类"""
def test_templates_not_a_dict_raises_error(self, temp_dir):
"""测试 templates 字段不是字典时抛出错误"""
yaml_content = '''
metadata:
size: "16:9"
templates: "invalid_templates_value"
slides:
- elements:
- type: text
content: "test"
'''
yaml_file = temp_dir / "invalid_templates.yaml"
yaml_file.write_text(yaml_content, encoding='utf-8')
from loaders.yaml_loader import YAMLError
from core.presentation import Presentation
with pytest.raises(YAMLError, match="'templates' 必须是一个字典"):
Presentation(str(yaml_file))
def test_template_missing_elements_raises_error(self, temp_dir):
"""测试模板缺少 elements 字段时抛出错误"""
yaml_content = '''
metadata:
size: "16:9"
templates:
no-elements:
vars:
- name: title
required: true
slides:
- elements:
- type: text
content: "test"
'''
yaml_file = temp_dir / "no_elements.yaml"
yaml_file.write_text(yaml_content, encoding='utf-8')
from loaders.yaml_loader import YAMLError
from core.presentation import Presentation
with pytest.raises(YAMLError, match="缺少必需字段 'elements'"):
Presentation(str(yaml_file))
def test_template_var_missing_name_raises_error(self, temp_dir):
"""测试变量定义缺少 name 字段时抛出错误"""
yaml_content = '''
metadata:
size: "16:9"
templates:
test-template:
vars:
- required: true
elements:
- type: text
content: "{title}"
slides:
- template: test-template
vars:
title: "Test"
'''
yaml_file = temp_dir / "invalid_var.yaml"
yaml_file.write_text(yaml_content, encoding='utf-8')
from loaders.yaml_loader import YAMLError
from core.presentation import Presentation
with pytest.raises(YAMLError, match="缺少必需字段 'name'"):
Presentation(str(yaml_file))
def test_missing_required_variable_raises_error(self, temp_dir):
"""测试缺少必需变量时抛出错误"""
yaml_content = '''
metadata:
size: "16:9"
templates:
title-slide:
vars:
- name: title
required: true
elements:
- type: text
content: "{title}"
slides:
- template: title-slide
vars:
# 缺少必需的 title 变量
subtitle: "Test"
'''
yaml_file = temp_dir / "missing_var.yaml"
yaml_file.write_text(yaml_content, encoding='utf-8')
from core.presentation import Presentation
pres = Presentation(str(yaml_file))
slide_data = pres.data['slides'][0]
# 渲染时应该抛出错误
with pytest.raises(YAMLError, match="缺少必需变量: title"):
pres.render_slide(slide_data)
def test_backward_compatibility_without_templates_field(self, temp_dir):
"""测试不使用 templates 字段时保持向后兼容"""
# 复用现有的 sample_yaml fixture
yaml_content = '''
metadata:
size: "16:9"
slides:
- background:
color: "#ffffff"
elements:
- type: text
box: [1, 1, 8, 1]
content: "Hello, World!"
font:
size: 44
bold: true
color: "#333333"
align: center
'''
yaml_file = temp_dir / "backward_test.yaml"
yaml_file.write_text(yaml_content, encoding='utf-8')
from core.presentation import Presentation
# 不使用 templates 字段,应该保持现有行为
pres = Presentation(str(yaml_file))
# 不应该有 inline_templates 属性(因为字段不存在)
assert not hasattr(pres, 'inline_templates') or pres.inline_templates == {}
# 渲染应该正常工作
slide_data = pres.data['slides'][0]
rendered = pres.render_slide(slide_data)
assert len(rendered['elements']) == 1
# ============= Description 字段测试 =============
class TestTemplateDescription:
"""Template description 字段测试类"""
def test_template_with_description(self, temp_dir):
"""测试模板包含 description 字段时正确加载"""
template_content = """
description: "用于章节标题页的模板,包含主标题和副标题"
vars:
- name: title
required: true
- name: subtitle
required: false
default: ""
elements:
- type: text
content: "{title}"
box: [1, 1, 8, 1]
font:
size: 44
bold: true
- type: text
content: "{subtitle}"
box: [1, 2, 8, 1]
font:
size: 24
"""
template_path = temp_dir / "test-template.yaml"
template_path.write_text(template_content)
template = Template("test-template", templates_dir=temp_dir)
assert template.description == "用于章节标题页的模板,包含主标题和副标题"
def test_template_without_description(self, sample_template):
"""测试模板不包含 description 字段时正常工作"""
template = Template("title-slide", templates_dir=sample_template)
assert template.description is None
def test_template_description_empty_string(self, temp_dir):
"""测试模板 description 为空字符串时正常工作"""
template_content = """
description: ""
vars:
- name: title
elements:
- type: text
content: "{title}"
box: [1, 1, 8, 1]
font: {}
"""
template_path = temp_dir / "test-template.yaml"
template_path.write_text(template_content)
template = Template("test-template", templates_dir=temp_dir)
assert template.description == ""
def test_template_description_chinese_characters(self, temp_dir):
"""测试模板 description 包含中文字符时正确处理"""
template_content = """
description: "这是中文模板描述,用于标题页面"
vars:
- name: title
elements:
- type: text
content: "{title}"
box: [1, 1, 8, 1]
font: {}
"""
template_path = temp_dir / "test-template.yaml"
template_path.write_text(template_content)
template = Template("test-template", templates_dir=temp_dir)
assert "这是中文模板描述" in template.description
assert "标题页面" in template.description
def test_template_description_multiline(self, temp_dir):
"""测试模板 description 支持多行文本"""
template_content = """
description: |
这是一个多行描述。
第一行说明模板的用途。
第二行说明使用场景。
vars:
- name: title
elements:
- type: text
content: "{title}"
box: [1, 1, 8, 1]
font: {}
"""
template_path = temp_dir / "test-template.yaml"
template_path.write_text(template_content)
template = Template("test-template", templates_dir=temp_dir)
# 多行文本应该被正确读取
assert "这是一个多行描述" in template.description
assert "第一行说明模板的用途" in template.description
assert "第二行说明使用场景" in template.description
def test_inline_template_with_description(self, temp_dir):
"""测试内联模板包含 description 字段"""
yaml_content = """
metadata:
size: "16:9"
templates:
test-template:
description: "内联模板描述"
vars:
- name: title
elements:
- type: text
content: "{title}"
box: [1, 1, 8, 1]
font: {}
slides:
- template: test-template
vars:
title: "Test"
"""
yaml_path = temp_dir / "test.yaml"
yaml_path.write_text(yaml_content)
pres = Presentation(str(yaml_path))
template = pres.get_template("test-template")
assert template.description == "内联模板描述"
def test_template_description_does_not_affect_rendering(self, temp_dir):
"""测试 description 不影响模板渲染"""
template_content = """
description: "这段描述不应该影响渲染"
vars:
- name: title
elements:
- type: text
content: "{title}"
box: [1, 1, 8, 1]
font:
size: 44
"""
template_path = temp_dir / "test-template.yaml"
template_path.write_text(template_content)
template = Template("test-template", templates_dir=temp_dir)
# 渲染应该正常工作description 不影响结果
result = template.render({"title": "Test Title"})
assert len(result) == 1
assert result[0]["content"] == "Test Title"