1
0
Files
PPTX/tests/e2e/test_convert_cmd.py
lanyuanxiaoyao 98098dc911 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>
2026-03-05 18:12:05 +08:00

276 lines
8.2 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.
"""
Convert 命令端到端测试
测试 yaml2pptx.py convert 命令的完整功能
"""
import pytest
import subprocess
import sys
from pathlib import Path
from pptx import Presentation
class TestConvertCmd:
"""convert 命令测试类"""
def run_convert(self, *args):
"""辅助函数:运行 convert 命令"""
cmd = ["uv", "run", "python", "yaml2pptx.py", "convert"]
cmd.extend(args)
result = subprocess.run(
cmd, capture_output=True, text=True, cwd=Path(__file__).parent.parent.parent
)
return result
def test_basic_conversion(self, sample_yaml, temp_dir):
"""测试基本转换"""
output = temp_dir / "output.pptx"
result = self.run_convert(str(sample_yaml), str(output))
assert result.returncode == 0
assert output.exists()
assert output.stat().st_size > 0
def test_auto_output_filename(self, sample_yaml, temp_dir):
"""测试自动生成输出文件名"""
# sample_yaml 位于 temp_dir 中,转换时输出也会在 temp_dir
# 但因为 cwd 是项目根目录,所以输出文件的路径需要计算
# 实际上,由于 sample_yaml 使用 tempfile输出会在 temp_dir 中
result = subprocess.run(
[
"uv",
"run",
"python",
"yaml2pptx.py",
"convert",
str(sample_yaml),
],
capture_output=True,
text=True,
cwd=Path(__file__).parent.parent.parent,
)
assert result.returncode == 0, f"Command failed: {result.stderr}"
# 应该生成与输入同名的 .pptx 文件(在 temp_dir 中)
expected_output = temp_dir / "test.pptx"
assert expected_output.exists(), (
f"Expected {expected_output} to exist, but didn't"
)
def test_conversion_with_template(self, temp_dir, sample_template):
"""测试使用模板转换"""
yaml_content = f"""
metadata:
size: "16:9"
slides:
- template: title-slide
vars:
title: "Template Test"
"""
yaml_path = temp_dir / "test.yaml"
yaml_path.write_text(yaml_content)
output = temp_dir / "output.pptx"
result = self.run_convert(
str(yaml_path), str(output), "--template", str(sample_template)
)
assert result.returncode == 0
assert output.exists()
def test_skip_validation(self, sample_yaml, temp_dir):
"""测试跳过验证"""
output = temp_dir / "output.pptx"
result = self.run_convert(str(sample_yaml), str(output), "--skip-validation")
assert result.returncode == 0
assert output.exists()
def test_force_overwrite(self, sample_yaml, temp_dir):
"""测试强制覆盖"""
output = temp_dir / "output.pptx"
# 先创建一个存在的文件
output.write_text("existing")
# 使用 --force 应该覆盖
result = self.run_convert(str(sample_yaml), str(output), "--force")
assert result.returncode == 0
# 文件应该是有效的 PPTX不是原来的文本
assert output.stat().st_size > 1000
def test_existing_file_without_force(self, sample_yaml, temp_dir):
"""测试文件已存在且不使用 --force"""
output = temp_dir / "output.pptx"
# 先创建一个存在的文件
output.write_text("existing")
# 不使用 --force 应该失败或提示
result = self.run_convert(str(sample_yaml), str(output))
# 程序应该拒绝覆盖
assert result.returncode != 0 or "已存在" in result.stderr
def test_invalid_input_file(self, temp_dir):
"""测试无效输入文件"""
nonexistent = temp_dir / "nonexistent.yaml"
output = temp_dir / "output.pptx"
result = self.run_convert(str(nonexistent), str(output))
assert result.returncode != 0
def test_conversion_with_all_element_types(self, temp_dir, sample_image):
"""测试转换包含所有元素类型的 YAML"""
fixtures_yaml = (
Path(__file__).parent.parent
/ "fixtures"
/ "yaml_samples"
/ "full_features.yaml"
)
if not fixtures_yaml.exists():
pytest.skip("full_features.yaml not found")
output = temp_dir / "output.pptx"
result = self.run_convert(str(fixtures_yaml), str(output))
assert result.returncode == 0
assert output.exists()
# 验证生成的 PPTX
prs = Presentation(str(output))
assert len(prs.slides) >= 1
def test_conversion_preserves_chinese_content(self, temp_dir):
"""测试转换保留中文内容"""
yaml_content = """
metadata:
size: "16:9"
slides:
- elements:
- type: text
box: [1, 1, 8, 1]
content: "测试中文内容"
font:
size: 24
"""
yaml_path = temp_dir / "test.yaml"
yaml_path.write_text(yaml_content, encoding="utf-8")
output = temp_dir / "output.pptx"
result = self.run_convert(str(yaml_path), str(output))
assert result.returncode == 0
assert output.exists()
# 验证内容
prs = Presentation(str(output))
text_content = prs.slides[0].shapes[0].text_frame.text
assert "测试中文内容" in text_content
def test_different_slide_sizes(self, temp_dir):
"""测试不同的幻灯片尺寸"""
for size in ["16:9", "4:3"]:
yaml_content = f'''
metadata:
size: "{size}"
slides:
- elements:
- type: text
box: [1, 1, 8, 1]
content: "Size {size}"
font:
size: 24
'''
yaml_path = temp_dir / f"test_{size.replace(':', '')}.yaml"
yaml_path.write_text(yaml_content)
output = temp_dir / f"output_{size.replace(':', '')}.pptx"
result = self.run_convert(str(yaml_path), str(output))
assert result.returncode == 0, f"Failed for size {size}"
assert output.exists()
# 验证尺寸
prs = Presentation(str(output))
if size == "16:9":
assert abs(prs.slide_width.inches - 10.0) < 0.01
assert abs(prs.slide_height.inches - 5.625) < 0.01
else: # 4:3
assert abs(prs.slide_width.inches - 10.0) < 0.01
assert abs(prs.slide_height.inches - 7.5) < 0.01
def test_subdirectory_path_resolution(self, temp_dir):
"""测试子目录中的文件路径解析"""
# 创建子目录结构
doc_dir = temp_dir / "docs"
template_dir = temp_dir / "templates"
doc_dir.mkdir()
template_dir.mkdir()
# 创建模板库文件
template_content = """
metadata:
size: "16:9"
templates:
test-template:
vars:
- name: title
required: true
elements:
- type: text
box: [1, 1, 8, 1]
content: "{title}"
"""
template_path = template_dir / "templates.yaml"
template_path.write_text(template_content)
# 创建文档文件
yaml_content = """
metadata:
size: "16:9"
slides:
- template: test-template
vars:
title: "Test Title"
"""
yaml_path = doc_dir / "test.yaml"
yaml_path.write_text(yaml_content)
output_path = temp_dir / "output.pptx"
# 使用相对路径运行转换
import os
original_cwd = os.getcwd()
try:
os.chdir(temp_dir)
result = subprocess.run(
["uv", "run", "python",
str(Path(original_cwd) / "yaml2pptx.py"),
"convert",
"docs/test.yaml",
"output.pptx",
"--template", "templates/templates.yaml"],
capture_output=True,
text=True
)
assert result.returncode == 0, f"转换失败: {result.stderr}"
assert output_path.exists()
# 验证生成的 PPTX
prs = Presentation(str(output_path))
assert len(prs.slides) == 1
text_content = prs.slides[0].shapes[0].text_frame.text
assert "Test Title" in text_content
finally:
os.chdir(original_cwd)