"""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