206 lines
6.4 KiB
Python
206 lines
6.4 KiB
Python
"""Tests for the overhauled Positioning & Layout UI."""
|
|
|
|
from types import SimpleNamespace
|
|
import importlib
|
|
|
|
import pytest
|
|
|
|
|
|
panels = importlib.import_module("src.ui.panels")
|
|
|
|
|
|
class FakeLayout:
|
|
"""Minimal substitute for Blender layout that records UI calls."""
|
|
|
|
def __init__(self, kind="layout"):
|
|
self.kind = kind
|
|
self.calls = []
|
|
self.children = []
|
|
self.enabled = True
|
|
self.alignment = None
|
|
self.scale_x = 1.0
|
|
self.scale_y = 1.0
|
|
self.use_property_split = False
|
|
self.use_property_decorate = True
|
|
|
|
def _child(self, kind, kwargs):
|
|
child = FakeLayout(kind)
|
|
self.calls.append((kind, kwargs, child))
|
|
self.children.append(child)
|
|
return child
|
|
|
|
def label(self, text="", **kwargs):
|
|
self.calls.append(("label", {"text": text, **kwargs}))
|
|
return None
|
|
|
|
def prop(self, props, prop_name, **kwargs):
|
|
# Access attribute to mirror Blender's behaviour of raising AttributeError
|
|
getattr(props, prop_name)
|
|
data = {"name": prop_name}
|
|
data.update(kwargs)
|
|
self.calls.append(("prop", data))
|
|
return None
|
|
|
|
def separator(self, **kwargs):
|
|
self.calls.append(("separator", kwargs))
|
|
return None
|
|
|
|
def row(self, **kwargs):
|
|
return self._child("row", kwargs)
|
|
|
|
def column(self, **kwargs):
|
|
return self._child("column", kwargs)
|
|
|
|
def box(self):
|
|
return self._child("box", {})
|
|
|
|
def operator(self, operator_id, **kwargs):
|
|
self.calls.append(("operator", {"id": operator_id, **kwargs}))
|
|
return SimpleNamespace()
|
|
|
|
|
|
def collect_calls(layout):
|
|
"""Flatten recorded calls from layout tree."""
|
|
flattened = []
|
|
for entry in layout.calls:
|
|
if len(entry) == 3:
|
|
kind, kwargs, child = entry
|
|
flattened.append((kind, kwargs))
|
|
flattened.extend(collect_calls(child))
|
|
else:
|
|
flattened.append(entry)
|
|
return flattened
|
|
|
|
|
|
def make_default_props(**overrides):
|
|
"""Helper to build a props namespace with defaults for positioning tests."""
|
|
defaults = dict(
|
|
text="Main",
|
|
texture_width=1024,
|
|
texture_height=1024,
|
|
prepend_text="",
|
|
append_text="",
|
|
prepend_margin=10.0,
|
|
append_margin=10.0,
|
|
prepend_append_layout="HORIZONTAL",
|
|
position_mode="PRESET",
|
|
anchor_point="MIDDLE_CENTER",
|
|
margins_linked=False,
|
|
margin_x=0.0,
|
|
margin_y=0.0,
|
|
offset_x=0,
|
|
offset_y=0,
|
|
manual_position_unit="PERCENTAGE",
|
|
manual_position_x=50.0,
|
|
manual_position_y=50.0,
|
|
constrain_to_canvas=True,
|
|
image_overlays=[],
|
|
active_overlay_index=0,
|
|
)
|
|
defaults.update(overrides)
|
|
return SimpleNamespace(**defaults)
|
|
|
|
|
|
def test_text_input_section_exposes_dimensions():
|
|
"""Dimensions should appear alongside the main text control."""
|
|
layout = FakeLayout()
|
|
props = make_default_props()
|
|
|
|
panels.draw_text_input_section(layout, props)
|
|
|
|
calls = collect_calls(layout)
|
|
prop_names = [data["name"] for kind, data in calls if kind == "prop"]
|
|
assert "texture_width" in prop_names
|
|
assert "texture_height" in prop_names
|
|
|
|
|
|
def test_positioning_section_drops_additional_text(monkeypatch):
|
|
"""Positioning panel should no longer render prepend/append controls."""
|
|
layout = FakeLayout()
|
|
props = make_default_props()
|
|
|
|
monkeypatch.setattr("src.utils.system.get_anchor_point_matrix", lambda: [["CENTER"]])
|
|
monkeypatch.setattr(panels, "safe_operator_call", lambda *args, **kwargs: SimpleNamespace())
|
|
|
|
panels.draw_positioning_section(layout, props)
|
|
|
|
calls = collect_calls(layout)
|
|
labels = [data["text"] for kind, data in calls if kind == "label"]
|
|
prop_names = [data["name"] for kind, data in calls if kind == "prop"]
|
|
|
|
assert "Additional Text:" not in labels
|
|
assert "prepend_text" not in prop_names
|
|
assert "append_text" not in prop_names
|
|
|
|
|
|
def test_composition_section_renders_text_component(monkeypatch):
|
|
"""Composition panel should show text component controls within the proper section."""
|
|
layout = FakeLayout()
|
|
text_component = SimpleNamespace(
|
|
enabled=True,
|
|
name="Intro Text",
|
|
component_type="TEXT",
|
|
placement_mode="PREPEND",
|
|
text_content="Intro",
|
|
text_spacing=10.0,
|
|
image_spacing=5.0,
|
|
x_position=0.5,
|
|
y_position=0.5,
|
|
scale=1.0,
|
|
rotation=0.0,
|
|
z_index=0,
|
|
)
|
|
props = make_default_props(image_overlays=[text_component])
|
|
|
|
monkeypatch.setattr(panels, "safe_operator_call", lambda *args, **kwargs: SimpleNamespace())
|
|
monkeypatch.setattr(panels, "has_image_overlays", lambda: True)
|
|
|
|
panels.draw_composition_section(layout, props)
|
|
|
|
calls = collect_calls(layout)
|
|
prop_names = [data["name"] for kind, data in calls if kind == "prop"]
|
|
labels = [data["text"] for kind, data in calls if kind == "label"]
|
|
sections = [text for text in labels if "Components" in text]
|
|
|
|
assert "component_type" in prop_names
|
|
assert "text_content" in prop_names
|
|
assert "placement_mode" in prop_names
|
|
assert "text_spacing" in prop_names
|
|
assert "Prepended Components" in sections
|
|
|
|
|
|
def test_composition_section_renders_image_component(monkeypatch):
|
|
"""Composition panel should show image component controls within the proper section."""
|
|
layout = FakeLayout()
|
|
image_component = SimpleNamespace(
|
|
enabled=True,
|
|
name="Logo",
|
|
component_type="IMAGE",
|
|
placement_mode="ABSOLUTE",
|
|
image_path="/tmp/logo.png",
|
|
x_position=0.5,
|
|
y_position=0.5,
|
|
text_spacing=10.0,
|
|
image_spacing=5.0,
|
|
scale=1.0,
|
|
rotation=0.0,
|
|
z_index=1,
|
|
)
|
|
props = make_default_props(image_overlays=[image_component])
|
|
|
|
monkeypatch.setattr(panels, "safe_operator_call", lambda *args, **kwargs: SimpleNamespace())
|
|
monkeypatch.setattr(panels, "has_image_overlays", lambda: True)
|
|
|
|
panels.draw_composition_section(layout, props)
|
|
|
|
calls = collect_calls(layout)
|
|
prop_names = [data["name"] for kind, data in calls if kind == "prop"]
|
|
labels = [data["text"] for kind, data in calls if kind == "label"]
|
|
sections = [text for text in labels if "Components" in text]
|
|
|
|
assert "component_type" in prop_names
|
|
assert "image_path" in prop_names
|
|
assert "placement_mode" in prop_names
|
|
assert "x_position" in prop_names
|
|
assert "Absolute Components" in sections
|