2025-12-08 16:57:25 +03:00
|
|
|
|
import pytest
|
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
from docx import Document
|
2025-12-08 17:18:30 +03:00
|
|
|
|
|
|
|
|
|
|
from src.core.converter import process_document
|
2025-12-08 17:09:16 +03:00
|
|
|
|
from src.handlers.builtin import handle_heading, handle_paragraph, handle_image, handle_list, handle_table
|
2025-12-08 16:57:25 +03:00
|
|
|
|
from src.core.style_registry import StyleRegistry
|
|
|
|
|
|
|
|
|
|
|
|
# Тесты для обработчиков
|
|
|
|
|
|
def test_handle_heading():
|
|
|
|
|
|
doc = Document()
|
|
|
|
|
|
style_reg = StyleRegistry(Path("resources/style_config.yaml"))
|
|
|
|
|
|
node = {"type": "heading", "level": 1, "children": [{"type": "text", "raw": "Заголовок 1"}]}
|
|
|
|
|
|
handle_heading(node, doc, Path("."), style_reg)
|
|
|
|
|
|
assert len(doc.paragraphs) == 1
|
|
|
|
|
|
assert doc.paragraphs[0].style.name == "Heading 1"
|
|
|
|
|
|
|
|
|
|
|
|
def test_handle_paragraph():
|
|
|
|
|
|
doc = Document()
|
|
|
|
|
|
style_reg = StyleRegistry(Path("resources/style_config.yaml"))
|
|
|
|
|
|
node = {"type": "paragraph", "children": [{"type": "text", "raw": "Текст параграфа"}]}
|
|
|
|
|
|
handle_paragraph(node, doc, Path("."), style_reg)
|
|
|
|
|
|
assert len(doc.paragraphs) == 1
|
|
|
|
|
|
assert doc.paragraphs[0].text == "Текст параграфа"
|
|
|
|
|
|
|
|
|
|
|
|
def test_handle_image():
|
|
|
|
|
|
from PIL import Image
|
|
|
|
|
|
doc = Document()
|
|
|
|
|
|
style_reg = StyleRegistry(Path("resources/style_config.yaml"))
|
|
|
|
|
|
style_reg.ensure_styles_in_doc(doc)
|
2025-12-08 19:28:50 +03:00
|
|
|
|
node = {"type": "image", "src": "images/logo.png", "alt": "Логотип"}
|
2025-12-08 16:57:25 +03:00
|
|
|
|
|
|
|
|
|
|
# Создаем реальное изображение
|
|
|
|
|
|
img_path = Path("tests/fixtures/images/logo.png")
|
|
|
|
|
|
img_path.parent.mkdir(exist_ok=True)
|
|
|
|
|
|
img = Image.new("RGB", (100, 100), color="red")
|
|
|
|
|
|
img.save(img_path)
|
|
|
|
|
|
|
2025-12-08 19:28:50 +03:00
|
|
|
|
# images_dir должен указывать на папку images/
|
|
|
|
|
|
handle_image(node, doc, Path("tests/fixtures/images"), style_reg)
|
2025-12-08 16:57:25 +03:00
|
|
|
|
|
|
|
|
|
|
# После вызова должно быть 2 параграфа: картинка и подпись
|
|
|
|
|
|
assert len(doc.paragraphs) == 2
|
|
|
|
|
|
assert doc.paragraphs[1].text == "Логотип" # подпись
|
|
|
|
|
|
|
|
|
|
|
|
def test_handle_paragraph_with_inline():
|
|
|
|
|
|
doc = Document()
|
|
|
|
|
|
style_reg = StyleRegistry(Path("resources/style_config.yaml"))
|
|
|
|
|
|
node = {
|
|
|
|
|
|
"type": "paragraph",
|
|
|
|
|
|
"children": [
|
|
|
|
|
|
{"type": "text", "raw": "Текст "},
|
|
|
|
|
|
{"type": "strong", "children": [{"type": "text", "raw": "жирный"}]},
|
|
|
|
|
|
{"type": "text", "raw": " и "},
|
|
|
|
|
|
{"type": "emphasis", "children": [{"type": "text", "raw": "курсив"}]},
|
|
|
|
|
|
{"type": "text", "raw": "."},
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
handle_paragraph(node, doc, Path("."), style_reg)
|
|
|
|
|
|
|
|
|
|
|
|
p = doc.paragraphs[0]
|
|
|
|
|
|
runs = p.runs
|
|
|
|
|
|
assert len(runs) == 5
|
|
|
|
|
|
assert runs[0].text == "Текст "
|
|
|
|
|
|
assert runs[1].text == "жирный"
|
|
|
|
|
|
assert runs[1].bold is True
|
|
|
|
|
|
assert runs[2].text == " и "
|
|
|
|
|
|
assert runs[3].text == "курсив"
|
|
|
|
|
|
assert runs[3].italic is True
|
2025-12-08 17:09:16 +03:00
|
|
|
|
assert runs[4].text == "."
|
|
|
|
|
|
|
|
|
|
|
|
def test_handle_list():
|
|
|
|
|
|
doc = Document()
|
|
|
|
|
|
style_reg = StyleRegistry(Path("resources/style_config.yaml"))
|
|
|
|
|
|
node = {
|
|
|
|
|
|
"type": "list",
|
|
|
|
|
|
"ordered": False,
|
|
|
|
|
|
"children": [
|
|
|
|
|
|
{"type": "list_item", "children": [{"type": "text", "raw": "Элемент 1"}]},
|
|
|
|
|
|
{"type": "list_item", "children": [{"type": "text", "raw": "Элемент 2"}]},
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
handle_list(node, doc, Path("."), style_reg)
|
|
|
|
|
|
|
|
|
|
|
|
assert len(doc.paragraphs) == 2
|
|
|
|
|
|
assert doc.paragraphs[0].style.name == "List Bullet"
|
|
|
|
|
|
assert doc.paragraphs[0].text == "Элемент 1"
|
|
|
|
|
|
assert doc.paragraphs[1].text == "Элемент 2"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_handle_table():
|
|
|
|
|
|
doc = Document()
|
|
|
|
|
|
style_reg = StyleRegistry(Path("resources/style_config.yaml"))
|
|
|
|
|
|
node = {
|
|
|
|
|
|
"type": "table",
|
|
|
|
|
|
"children": [
|
2025-12-08 19:28:50 +03:00
|
|
|
|
{ # table_head
|
|
|
|
|
|
"type": "table_head",
|
2025-12-08 17:09:16 +03:00
|
|
|
|
"children": [
|
|
|
|
|
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 1"}]},
|
|
|
|
|
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 2"}]},
|
|
|
|
|
|
]
|
|
|
|
|
|
},
|
2025-12-08 19:28:50 +03:00
|
|
|
|
{ # table_body
|
|
|
|
|
|
"type": "table_body",
|
2025-12-08 17:09:16 +03:00
|
|
|
|
"children": [
|
2025-12-08 19:28:50 +03:00
|
|
|
|
{ # table_row
|
|
|
|
|
|
"type": "table_row",
|
|
|
|
|
|
"children": [
|
|
|
|
|
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Ячейка 1"}]},
|
|
|
|
|
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Ячейка 2"}]},
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
2025-12-08 17:09:16 +03:00
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
handle_table(node, doc, Path("."), style_reg)
|
|
|
|
|
|
|
|
|
|
|
|
assert len(doc.tables) == 1
|
|
|
|
|
|
table = doc.tables[0]
|
|
|
|
|
|
assert len(table.rows) == 2
|
|
|
|
|
|
assert table.cell(0, 0).text == "Заголовок 1"
|
|
|
|
|
|
assert table.cell(1, 1).text == "Ячейка 2"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_handle_table_with_br():
|
|
|
|
|
|
doc = Document()
|
|
|
|
|
|
style_reg = StyleRegistry(Path("resources/style_config.yaml"))
|
|
|
|
|
|
node = {
|
|
|
|
|
|
"type": "table",
|
|
|
|
|
|
"children": [
|
2025-12-08 19:28:50 +03:00
|
|
|
|
{ # table_head
|
|
|
|
|
|
"type": "table_head",
|
2025-12-08 17:09:16 +03:00
|
|
|
|
"children": [
|
2025-12-08 19:28:50 +03:00
|
|
|
|
{ # table_row
|
|
|
|
|
|
"type": "table_row",
|
|
|
|
|
|
"children": [
|
|
|
|
|
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 1"}]},
|
|
|
|
|
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Заголовок 2"}]},
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
2025-12-08 17:09:16 +03:00
|
|
|
|
]
|
|
|
|
|
|
},
|
2025-12-08 19:28:50 +03:00
|
|
|
|
{ # table_body
|
|
|
|
|
|
"type": "table_body",
|
2025-12-08 17:09:16 +03:00
|
|
|
|
"children": [
|
2025-12-08 19:28:50 +03:00
|
|
|
|
{ # row 1
|
|
|
|
|
|
"type": "table_row",
|
|
|
|
|
|
"children": [
|
|
|
|
|
|
{"type": "table_cell", "children": [
|
|
|
|
|
|
{"type": "text", "raw": "Первая строка"},
|
|
|
|
|
|
{"type": "softbreak"}, # или linebreak/html
|
|
|
|
|
|
{"type": "text", "raw": "Вторая строка"}
|
|
|
|
|
|
]},
|
|
|
|
|
|
{"type": "table_cell", "children": [{"type": "text", "raw": "Ячейка 2"}]},
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
2025-12-08 17:09:16 +03:00
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
handle_table(node, doc, Path("."), style_reg)
|
|
|
|
|
|
|
|
|
|
|
|
assert len(doc.tables) == 1
|
|
|
|
|
|
table = doc.tables[0]
|
|
|
|
|
|
cell_text = table.cell(1, 0).text # "Первая строка\nВторая строка"
|
|
|
|
|
|
assert "Первая строка" in cell_text
|
|
|
|
|
|
assert "Вторая строка" in cell_text
|
2025-12-08 17:18:30 +03:00
|
|
|
|
|