454 lines
15 KiB
Python
454 lines
15 KiB
Python
"""
|
|
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
|