pyPhotoAlbum/tests/test_zorder_ops_mixin.py
Duncan Tourolle ca21f3ae4c
All checks were successful
Python CI / test (push) Successful in 1m6s
Lint / lint (push) Successful in 1m10s
Tests / test (3.10) (push) Successful in 53s
Tests / test (3.11) (push) Successful in 52s
Tests / test (3.9) (push) Successful in 50s
more tests and gnoem+fedora installer
2025-11-11 12:51:15 +01:00

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