""" Tests for ZOrderOperationsMixin """ import pytest from unittest.mock import Mock, MagicMock, patch from PyQt6.QtWidgets import QMainWindow from pyPhotoAlbum.mixins.operations.zorder_ops import ZOrderOperationsMixin from pyPhotoAlbum.models import ImageData, TextBoxData from pyPhotoAlbum.project import Project, Page from pyPhotoAlbum.page_layout import PageLayout from pyPhotoAlbum.commands import CommandHistory # Create test window with ZOrderOperationsMixin class TestZOrderWindow(ZOrderOperationsMixin, QMainWindow): """Test window with z-order operations mixin""" def __init__(self): super().__init__() # Mock GL widget self.gl_widget = Mock() self.gl_widget.selected_element = None self.gl_widget.selected_elements = set() # Mock project self.project = Mock() self.project.history = CommandHistory() # Track method calls self._update_view_called = False self._status_message = None def get_current_page(self): """Return mock current page""" if hasattr(self, '_current_page'): return self._current_page return None def update_view(self): """Track update_view calls""" self._update_view_called = True def show_status(self, message, timeout=0): """Track status messages""" self._status_message = message class TestBringToFront: """Test bring_to_front method""" def test_bring_to_front_success(self, qtbot): """Test bringing element to front""" window = TestZOrderWindow() qtbot.addWidget(window) # Setup page with elements layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) element3 = ImageData(image_path="/test3.jpg", x=200, y=200, width=100, height=100) layout.elements = [element1, element2, element3] page = Mock() page.layout = layout window._current_page = page # Select element1 (at index 0, should move to index 2) window.gl_widget.selected_element = element1 window.bring_to_front() # Element should now be at end assert layout.elements[-1] == element1 assert layout.elements == [element2, element3, element1] assert window._update_view_called assert "front" in window._status_message.lower() def test_bring_to_front_already_at_front(self, qtbot): """Test bringing element that's already at front""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) layout.elements = [element1, element2] page = Mock() page.layout = layout window._current_page = page # Select element2 (already at front) window.gl_widget.selected_element = element2 window.bring_to_front() # Order should not change assert layout.elements == [element1, element2] assert "already" in window._status_message.lower() def test_bring_to_front_no_selection(self, qtbot): """Test bring to front with no selection""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() page = Mock() page.layout = layout window._current_page = page window.gl_widget.selected_element = None window.bring_to_front() # Should do nothing assert not window._update_view_called def test_bring_to_front_no_page(self, qtbot): """Test bring to front with no current page""" window = TestZOrderWindow() qtbot.addWidget(window) window.gl_widget.selected_element = ImageData(image_path="/test.jpg", x=0, y=0, width=100, height=100) window._current_page = None window.bring_to_front() # Should do nothing assert not window._update_view_called class TestSendToBack: """Test send_to_back method""" def test_send_to_back_success(self, qtbot): """Test sending element to back""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) element3 = ImageData(image_path="/test3.jpg", x=200, y=200, width=100, height=100) layout.elements = [element1, element2, element3] page = Mock() page.layout = layout window._current_page = page # Select element3 (at index 2, should move to index 0) window.gl_widget.selected_element = element3 window.send_to_back() # Element should now be at start assert layout.elements[0] == element3 assert layout.elements == [element3, element1, element2] assert window._update_view_called assert "back" in window._status_message.lower() def test_send_to_back_already_at_back(self, qtbot): """Test sending element that's already at back""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) layout.elements = [element1, element2] page = Mock() page.layout = layout window._current_page = page # Select element1 (already at back) window.gl_widget.selected_element = element1 window.send_to_back() # Order should not change assert layout.elements == [element1, element2] assert "already" in window._status_message.lower() def test_send_to_back_no_selection(self, qtbot): """Test send to back with no selection""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() page = Mock() page.layout = layout window._current_page = page window.gl_widget.selected_element = None window.send_to_back() # Should do nothing assert not window._update_view_called class TestBringForward: """Test bring_forward method""" def test_bring_forward_success(self, qtbot): """Test bringing element forward one position""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) element3 = ImageData(image_path="/test3.jpg", x=200, y=200, width=100, height=100) layout.elements = [element1, element2, element3] page = Mock() page.layout = layout window._current_page = page # Select element1 (at index 0, should move to index 1) window.gl_widget.selected_element = element1 window.bring_forward() # Element should move forward one position assert layout.elements == [element2, element1, element3] assert window._update_view_called assert "forward" in window._status_message.lower() def test_bring_forward_already_at_front(self, qtbot): """Test bringing forward element that's already at front""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) layout.elements = [element1, element2] page = Mock() page.layout = layout window._current_page = page # Select element2 (already at front) window.gl_widget.selected_element = element2 window.bring_forward() # Order should not change assert layout.elements == [element1, element2] assert "already" in window._status_message.lower() class TestSendBackward: """Test send_backward method""" def test_send_backward_success(self, qtbot): """Test sending element backward one position""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) element3 = ImageData(image_path="/test3.jpg", x=200, y=200, width=100, height=100) layout.elements = [element1, element2, element3] page = Mock() page.layout = layout window._current_page = page # Select element3 (at index 2, should move to index 1) window.gl_widget.selected_element = element3 window.send_backward() # Element should move backward one position assert layout.elements == [element1, element3, element2] assert window._update_view_called assert "backward" in window._status_message.lower() def test_send_backward_already_at_back(self, qtbot): """Test sending backward element that's already at back""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) layout.elements = [element1, element2] page = Mock() page.layout = layout window._current_page = page # Select element1 (already at back) window.gl_widget.selected_element = element1 window.send_backward() # Order should not change assert layout.elements == [element1, element2] assert "already" in window._status_message.lower() class TestSwapOrder: """Test swap_order method""" def test_swap_order_success(self, qtbot): """Test swapping z-order of two elements""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) element3 = ImageData(image_path="/test3.jpg", x=200, y=200, width=100, height=100) layout.elements = [element1, element2, element3] page = Mock() page.layout = layout window._current_page = page # Select element1 and element3 window.gl_widget.selected_elements = {element1, element3} window.swap_order() # Elements should be swapped assert layout.elements == [element3, element2, element1] assert window._update_view_called assert "swapped" in window._status_message.lower() def test_swap_order_wrong_count(self, qtbot): """Test swap with wrong number of selections""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) layout.elements = [element1] page = Mock() page.layout = layout window._current_page = page # Select only one element window.gl_widget.selected_elements = {element1} window.swap_order() # Should show error message assert "exactly 2" in window._status_message.lower() assert not window._update_view_called def test_swap_order_no_page(self, qtbot): """Test swap with no current page""" window = TestZOrderWindow() qtbot.addWidget(window) element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) window.gl_widget.selected_elements = {element1, element2} window._current_page = None window.swap_order() # Should do nothing assert not window._update_view_called def test_swap_order_elements_not_on_page(self, qtbot): """Test swap with elements not on current page""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() page_element = ImageData(image_path="/page.jpg", x=0, y=0, width=100, height=100) layout.elements = [page_element] page = Mock() page.layout = layout window._current_page = page # Select elements not on the page other1 = ImageData(image_path="/other1.jpg", x=0, y=0, width=100, height=100) other2 = ImageData(image_path="/other2.jpg", x=100, y=100, width=100, height=100) window.gl_widget.selected_elements = {other1, other2} window.swap_order() # Should show error assert "not found" in window._status_message.lower() assert not window._update_view_called class TestZOrderWithCommandPattern: """Test z-order operations with command pattern for undo/redo""" def test_bring_to_front_creates_command(self, qtbot): """Test that bring_to_front creates a command for undo""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) layout.elements = [element1, element2] page = Mock() page.layout = layout window._current_page = page window.gl_widget.selected_element = element1 # Should have no commands initially assert not window.project.history.can_undo() window.bring_to_front() # Should have created a command assert window.project.history.can_undo() def test_send_to_back_undo_redo(self, qtbot): """Test that send_to_back can be undone and redone""" window = TestZOrderWindow() qtbot.addWidget(window) layout = PageLayout() element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100) element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=100) element3 = ImageData(image_path="/test3.jpg", x=200, y=200, width=100, height=100) layout.elements = [element1, element2, element3] page = Mock() page.layout = layout window._current_page = page window.gl_widget.selected_element = element3 # Execute operation window.send_to_back() assert layout.elements[0] == element3 # Undo window.project.history.undo() assert layout.elements == [element1, element2, element3] # Redo window.project.history.redo() assert layout.elements[0] == element3