test: add comprehensive pytest test suite
Add complete test infrastructure for yaml2pptx project with 245+ tests covering unit, integration, and end-to-end scenarios. Test structure: - Unit tests: elements, template system, validators, loaders, utils - Integration tests: presentation and rendering flows - E2E tests: CLI commands (convert, check, preview) Key features: - PptxFileValidator for Level 2 PPTX validation (file structure, element count, content matching, position tolerance) - Comprehensive fixtures for test data consistency - Mock-based testing for external dependencies - Test images generated with PIL/Pillow - Boundary case coverage for edge scenarios Dependencies added: - pytest, pytest-cov, pytest-mock - pillow (for test image generation) Documentation updated: - README.md: test running instructions - README_DEV.md: test development guide Co-authored-by: OpenSpec change: add-comprehensive-tests
This commit is contained in:
340
tests/conftest.py
Normal file
340
tests/conftest.py
Normal file
@@ -0,0 +1,340 @@
|
||||
"""
|
||||
pytest 配置文件 - 共享 fixtures
|
||||
"""
|
||||
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from PIL import Image
|
||||
import pytest
|
||||
|
||||
# 添加项目根目录到 sys.path
|
||||
project_root = Path(__file__).parent.parent
|
||||
sys.path.insert(0, str(project_root))
|
||||
|
||||
|
||||
# ============= 基础 Fixtures =============
|
||||
|
||||
@pytest.fixture
|
||||
def temp_dir(tmp_path):
|
||||
"""临时目录 fixture,使用 pytest 内置 tmp_path"""
|
||||
return tmp_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def project_root_dir():
|
||||
"""项目根目录"""
|
||||
return Path(__file__).parent.parent
|
||||
|
||||
|
||||
# ============= YAML 文件 Fixtures =============
|
||||
|
||||
MINIMAL_YAML = """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
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_yaml(temp_dir):
|
||||
"""创建最小测试 YAML 文件"""
|
||||
yaml_path = temp_dir / "test.yaml"
|
||||
yaml_path.write_text(MINIMAL_YAML, encoding='utf-8')
|
||||
return yaml_path
|
||||
|
||||
|
||||
# ============= 图片 Fixtures =============
|
||||
|
||||
@pytest.fixture
|
||||
def sample_image(temp_dir):
|
||||
"""创建测试图片文件(使用 Pillow 生成简单的 PNG)"""
|
||||
img_path = temp_dir / "test_image.png"
|
||||
# 创建一个简单的红色图片
|
||||
img = Image.new('RGB', (100, 100), color='red')
|
||||
img.save(img_path, 'PNG')
|
||||
return img_path
|
||||
|
||||
|
||||
# ============= 模板 Fixtures =============
|
||||
|
||||
TEMPLATE_YAML = """vars:
|
||||
- name: title
|
||||
required: true
|
||||
- name: subtitle
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
elements:
|
||||
- type: text
|
||||
box: [1, 2, 8, 1]
|
||||
content: "{title}"
|
||||
font:
|
||||
size: 44
|
||||
bold: true
|
||||
align: center
|
||||
|
||||
- type: text
|
||||
box: [1, 3.5, 8, 0.5]
|
||||
content: "{subtitle}"
|
||||
visible: "{subtitle != ''}"
|
||||
font:
|
||||
size: 24
|
||||
align: center
|
||||
"""
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def sample_template(temp_dir):
|
||||
"""创建测试模板目录和文件"""
|
||||
template_dir = temp_dir / "templates"
|
||||
template_dir.mkdir()
|
||||
(template_dir / "title-slide.yaml").write_text(TEMPLATE_YAML, encoding='utf-8')
|
||||
return template_dir
|
||||
|
||||
|
||||
# ============= PPTX 验证 Fixture =============
|
||||
|
||||
@pytest.fixture
|
||||
def pptx_validator():
|
||||
"""PPTX 文件验证器实例"""
|
||||
from tests.conftest_pptx import PptxFileValidator
|
||||
return PptxFileValidator()
|
||||
|
||||
|
||||
# ============= 测试数据目录 Fixture =============
|
||||
|
||||
@pytest.fixture
|
||||
def fixtures_dir():
|
||||
"""测试数据目录路径"""
|
||||
return Path(__file__).parent / "fixtures"
|
||||
|
||||
|
||||
# ============= 额外的边界情况 Fixtures =============
|
||||
|
||||
@pytest.fixture
|
||||
def edge_case_yaml_files(fixtures_dir):
|
||||
"""所有边界情况 YAML 文件的路径"""
|
||||
edge_cases_dir = fixtures_dir / "yaml_samples" / "edge_cases"
|
||||
if edge_cases_dir.exists():
|
||||
return list(edge_cases_dir.glob("*.yaml"))
|
||||
return []
|
||||
|
||||
|
||||
@pytest.fixture(params=["16:9", "4:3"])
|
||||
def slide_size(request):
|
||||
"""参数化的幻灯片尺寸"""
|
||||
return request.param
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def complex_template(temp_dir):
|
||||
"""创建复杂模板(包含多个变量和条件)"""
|
||||
template_content = """
|
||||
vars:
|
||||
- name: title
|
||||
required: true
|
||||
- name: subtitle
|
||||
required: false
|
||||
default: ""
|
||||
- name: author
|
||||
required: false
|
||||
default: ""
|
||||
- name: date
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
elements:
|
||||
- type: shape
|
||||
box: [0, 0, 10, 5.625]
|
||||
shape: rectangle
|
||||
fill: "#2c3e50"
|
||||
|
||||
- type: text
|
||||
box: [1, 1.5, 8, 1]
|
||||
content: "{title}"
|
||||
font:
|
||||
size: 44
|
||||
bold: true
|
||||
color: "#ffffff"
|
||||
align: center
|
||||
|
||||
- type: text
|
||||
box: [1, 2.8, 8, 0.6]
|
||||
content: "{subtitle}"
|
||||
visible: "{subtitle != ''}"
|
||||
font:
|
||||
size: 24
|
||||
color: "#ecf0f1"
|
||||
align: center
|
||||
|
||||
- type: text
|
||||
box: [1, 4, 8, 0.5]
|
||||
content: "{author}"
|
||||
visible: "{author != ''}"
|
||||
font:
|
||||
size: 18
|
||||
color: "#bdc3c7"
|
||||
align: center
|
||||
|
||||
- type: text
|
||||
box: [1, 4.8, 8, 0.4]
|
||||
content: "{date}"
|
||||
visible: "{date != ''}"
|
||||
font:
|
||||
size: 14
|
||||
color: "#95a5a6"
|
||||
align: center
|
||||
"""
|
||||
template_dir = temp_dir / "templates"
|
||||
template_dir.mkdir()
|
||||
(template_dir / "complex-slide.yaml").write_text(template_content)
|
||||
return template_dir
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def yaml_with_all_elements(temp_dir):
|
||||
"""创建包含所有元素类型的 YAML"""
|
||||
yaml_content = """
|
||||
metadata:
|
||||
size: 16:9
|
||||
|
||||
slides:
|
||||
- background:
|
||||
color: "#ffffff"
|
||||
elements:
|
||||
- type: text
|
||||
box: [1, 1, 3, 0.5]
|
||||
content: "Text Element"
|
||||
font:
|
||||
size: 24
|
||||
color: "#333333"
|
||||
align: left
|
||||
|
||||
- type: text
|
||||
box: [1, 1.8, 3, 0.5]
|
||||
content: "Center Text"
|
||||
font:
|
||||
size: 20
|
||||
color: "#666666"
|
||||
align: center
|
||||
|
||||
- type: text
|
||||
box: [1, 2.6, 3, 0.5]
|
||||
content: "Right Text"
|
||||
font:
|
||||
size: 18
|
||||
color: "#999999"
|
||||
align: right
|
||||
|
||||
- type: shape
|
||||
box: [5, 1, 2, 1]
|
||||
shape: rectangle
|
||||
fill: "#4a90e2"
|
||||
line:
|
||||
color: "#000000"
|
||||
width: 1
|
||||
|
||||
- type: shape
|
||||
box: [5, 2.2, 2, 2]
|
||||
shape: ellipse
|
||||
fill: "#e24a4a"
|
||||
line:
|
||||
color: "#ffffff"
|
||||
width: 2
|
||||
|
||||
- type: shape
|
||||
box: [5, 4.5, 2, 1]
|
||||
shape: rounded_rectangle
|
||||
fill: "#4ae290"
|
||||
line:
|
||||
color: "#333333"
|
||||
width: 1
|
||||
|
||||
- type: table
|
||||
position: [1, 3.5]
|
||||
col_widths: [2, 2, 2]
|
||||
data:
|
||||
- ["Header 1", "Header 2", "Header 3"]
|
||||
- ["Data 1", "Data 2", "Data 3"]
|
||||
- ["Data 4", "Data 5", "Data 6"]
|
||||
style:
|
||||
font_size: 12
|
||||
header_bg: "#4a90e2"
|
||||
header_color: "#ffffff"
|
||||
"""
|
||||
yaml_path = temp_dir / "all_elements.yaml"
|
||||
yaml_path.write_text(yaml_content)
|
||||
return yaml_path
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def invalid_yaml_samples(fixtures_dir):
|
||||
"""所有无效 YAML 样本的路径"""
|
||||
invalid_dir = fixtures_dir / "yaml_samples" / "invalid"
|
||||
if invalid_dir.exists():
|
||||
return list(invalid_dir.glob("*.yaml"))
|
||||
return []
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def multiple_slides_yaml(temp_dir):
|
||||
"""创建多张幻灯片的 YAML"""
|
||||
yaml_content = """
|
||||
metadata:
|
||||
size: 16:9
|
||||
|
||||
slides:
|
||||
# 第一张:标题页
|
||||
- background:
|
||||
color: "#4a90e2"
|
||||
elements:
|
||||
- type: text
|
||||
box: [1, 2, 8, 1]
|
||||
content: "Title Slide"
|
||||
font:
|
||||
size: 48
|
||||
bold: true
|
||||
color: "#ffffff"
|
||||
align: center
|
||||
|
||||
# 第二张:内容页
|
||||
- background:
|
||||
color: "#ffffff"
|
||||
elements:
|
||||
- type: text
|
||||
box: [1, 1, 8, 1]
|
||||
content: "Content Slide 1"
|
||||
font:
|
||||
size: 32
|
||||
color: "#333333"
|
||||
|
||||
# 第三张:内容页
|
||||
- background:
|
||||
color: "#ffffff"
|
||||
elements:
|
||||
- type: text
|
||||
box: [1, 1, 8, 1]
|
||||
content: "Content Slide 2"
|
||||
font:
|
||||
size: 32
|
||||
color: "#333333"
|
||||
|
||||
- type: shape
|
||||
box: [3, 2.5, 4, 2]
|
||||
shape: rectangle
|
||||
fill: "#e74c3c"
|
||||
"""
|
||||
yaml_path = temp_dir / "multiple_slides.yaml"
|
||||
yaml_path.write_text(yaml_content)
|
||||
return yaml_path
|
||||
Reference in New Issue
Block a user