330 lines
11 KiB
Python
330 lines
11 KiB
Python
"""
|
|
Tests for AlignmentOperationsMixin
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import Mock, patch
|
|
from PyQt6.QtWidgets import QMainWindow
|
|
from pyPhotoAlbum.mixins.operations.alignment_ops import AlignmentOperationsMixin
|
|
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 AlignmentOperationsMixin
|
|
class TestAlignmentWindow(AlignmentOperationsMixin, QMainWindow):
|
|
"""Test window with alignment operations mixin"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
# Mock GL widget
|
|
self.gl_widget = Mock()
|
|
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
|
|
self._require_selection_count = None
|
|
|
|
def require_selection(self, min_count=1):
|
|
"""Track require_selection calls"""
|
|
self._require_selection_count = min_count
|
|
return len(self.gl_widget.selected_elements) >= min_count
|
|
|
|
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 TestGetSelectedElementsList:
|
|
"""Test _get_selected_elements_list helper"""
|
|
|
|
def test_get_selected_elements_list(self, qtbot):
|
|
"""Test getting selected elements as list"""
|
|
window = TestAlignmentWindow()
|
|
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}
|
|
|
|
result = window._get_selected_elements_list()
|
|
|
|
assert isinstance(result, list)
|
|
assert len(result) == 2
|
|
assert element1 in result
|
|
assert element2 in result
|
|
|
|
def test_get_selected_elements_list_empty(self, qtbot):
|
|
"""Test getting empty list when nothing selected"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
window.gl_widget.selected_elements = set()
|
|
|
|
result = window._get_selected_elements_list()
|
|
|
|
assert result == []
|
|
|
|
|
|
class TestAlignLeft:
|
|
"""Test align_left method"""
|
|
|
|
@patch('pyPhotoAlbum.mixins.operations.alignment_ops.AlignmentManager')
|
|
def test_align_left_success(self, mock_manager, qtbot):
|
|
"""Test aligning elements to the left"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
element1 = ImageData(image_path="/test1.jpg", x=100, y=0, width=100, height=100)
|
|
element2 = ImageData(image_path="/test2.jpg", x=200, y=100, width=100, height=100)
|
|
|
|
window.gl_widget.selected_elements = {element1, element2}
|
|
|
|
# Mock AlignmentManager to return changes
|
|
mock_manager.align_left.return_value = [
|
|
(element1, (100, 0)),
|
|
(element2, (200, 100))
|
|
]
|
|
|
|
window.align_left()
|
|
|
|
# Should call AlignmentManager
|
|
assert mock_manager.align_left.called
|
|
assert window._update_view_called
|
|
assert "aligned" in window._status_message.lower()
|
|
assert "left" in window._status_message.lower()
|
|
|
|
@patch('pyPhotoAlbum.mixins.operations.alignment_ops.AlignmentManager')
|
|
def test_align_left_no_changes(self, mock_manager, qtbot):
|
|
"""Test align left when no changes needed"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100)
|
|
element2 = ImageData(image_path="/test2.jpg", x=0, y=100, width=100, height=100)
|
|
|
|
window.gl_widget.selected_elements = {element1, element2}
|
|
|
|
# Mock AlignmentManager to return no changes
|
|
mock_manager.align_left.return_value = []
|
|
|
|
window.align_left()
|
|
|
|
# Should not update view or show status
|
|
assert not window._update_view_called
|
|
|
|
def test_align_left_insufficient_selection(self, qtbot):
|
|
"""Test align left with fewer than 2 elements"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100)
|
|
window.gl_widget.selected_elements = {element1}
|
|
|
|
window.align_left()
|
|
|
|
# Should check for minimum 2 elements
|
|
assert window._require_selection_count == 2
|
|
assert not window._update_view_called
|
|
|
|
|
|
class TestAlignRight:
|
|
"""Test align_right method"""
|
|
|
|
@patch('pyPhotoAlbum.mixins.operations.alignment_ops.AlignmentManager')
|
|
def test_align_right_success(self, mock_manager, qtbot):
|
|
"""Test aligning elements to the right"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
element1 = ImageData(image_path="/test1.jpg", x=100, y=0, width=100, height=100)
|
|
element2 = ImageData(image_path="/test2.jpg", x=200, y=100, width=150, height=100)
|
|
|
|
window.gl_widget.selected_elements = {element1, element2}
|
|
|
|
mock_manager.align_right.return_value = [
|
|
(element1, (100, 0)),
|
|
(element2, (200, 100))
|
|
]
|
|
|
|
window.align_right()
|
|
|
|
assert mock_manager.align_right.called
|
|
assert window._update_view_called
|
|
assert "right" in window._status_message.lower()
|
|
|
|
|
|
class TestAlignTop:
|
|
"""Test align_top method"""
|
|
|
|
@patch('pyPhotoAlbum.mixins.operations.alignment_ops.AlignmentManager')
|
|
def test_align_top_success(self, mock_manager, qtbot):
|
|
"""Test aligning elements to the top"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
element1 = ImageData(image_path="/test1.jpg", x=0, y=50, 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}
|
|
|
|
mock_manager.align_top.return_value = [
|
|
(element1, (0, 50)),
|
|
(element2, (100, 100))
|
|
]
|
|
|
|
window.align_top()
|
|
|
|
assert mock_manager.align_top.called
|
|
assert window._update_view_called
|
|
assert "top" in window._status_message.lower()
|
|
|
|
|
|
class TestAlignBottom:
|
|
"""Test align_bottom method"""
|
|
|
|
@patch('pyPhotoAlbum.mixins.operations.alignment_ops.AlignmentManager')
|
|
def test_align_bottom_success(self, mock_manager, qtbot):
|
|
"""Test aligning elements to the bottom"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
element1 = ImageData(image_path="/test1.jpg", x=0, y=50, width=100, height=100)
|
|
element2 = ImageData(image_path="/test2.jpg", x=100, y=100, width=100, height=150)
|
|
|
|
window.gl_widget.selected_elements = {element1, element2}
|
|
|
|
mock_manager.align_bottom.return_value = [
|
|
(element1, (0, 50)),
|
|
(element2, (100, 100))
|
|
]
|
|
|
|
window.align_bottom()
|
|
|
|
assert mock_manager.align_bottom.called
|
|
assert window._update_view_called
|
|
assert "bottom" in window._status_message.lower()
|
|
|
|
|
|
class TestAlignHorizontalCenter:
|
|
"""Test align_horizontal_center method"""
|
|
|
|
@patch('pyPhotoAlbum.mixins.operations.alignment_ops.AlignmentManager')
|
|
def test_align_horizontal_center_success(self, mock_manager, qtbot):
|
|
"""Test aligning elements to horizontal center"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100)
|
|
element2 = ImageData(image_path="/test2.jpg", x=200, y=100, width=100, height=100)
|
|
|
|
window.gl_widget.selected_elements = {element1, element2}
|
|
|
|
mock_manager.align_horizontal_center.return_value = [
|
|
(element1, (0, 0)),
|
|
(element2, (200, 100))
|
|
]
|
|
|
|
window.align_horizontal_center()
|
|
|
|
assert mock_manager.align_horizontal_center.called
|
|
assert window._update_view_called
|
|
assert "horizontal center" in window._status_message.lower()
|
|
|
|
|
|
class TestAlignVerticalCenter:
|
|
"""Test align_vertical_center method"""
|
|
|
|
@patch('pyPhotoAlbum.mixins.operations.alignment_ops.AlignmentManager')
|
|
def test_align_vertical_center_success(self, mock_manager, qtbot):
|
|
"""Test aligning elements to vertical center"""
|
|
window = TestAlignmentWindow()
|
|
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=200, width=100, height=100)
|
|
|
|
window.gl_widget.selected_elements = {element1, element2}
|
|
|
|
mock_manager.align_vertical_center.return_value = [
|
|
(element1, (0, 0)),
|
|
(element2, (100, 200))
|
|
]
|
|
|
|
window.align_vertical_center()
|
|
|
|
assert mock_manager.align_vertical_center.called
|
|
assert window._update_view_called
|
|
assert "vertical center" in window._status_message.lower()
|
|
|
|
|
|
class TestAlignmentCommandPattern:
|
|
"""Test alignment operations with command pattern for undo/redo"""
|
|
|
|
@patch('pyPhotoAlbum.mixins.operations.alignment_ops.AlignmentManager')
|
|
def test_alignment_creates_command(self, mock_manager, qtbot):
|
|
"""Test that alignment creates a command for undo"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
element1 = ImageData(image_path="/test1.jpg", x=100, y=0, width=100, height=100)
|
|
element2 = ImageData(image_path="/test2.jpg", x=200, y=100, width=100, height=100)
|
|
|
|
window.gl_widget.selected_elements = {element1, element2}
|
|
|
|
mock_manager.align_left.return_value = [
|
|
(element1, (100, 0)),
|
|
(element2, (200, 100))
|
|
]
|
|
|
|
# Should have no commands initially
|
|
assert not window.project.history.can_undo()
|
|
|
|
window.align_left()
|
|
|
|
# Should have created a command
|
|
assert window.project.history.can_undo()
|
|
|
|
@patch('pyPhotoAlbum.mixins.operations.alignment_ops.AlignmentManager')
|
|
def test_alignment_undo_redo(self, mock_manager, qtbot):
|
|
"""Test that alignment can be undone and redone"""
|
|
window = TestAlignmentWindow()
|
|
qtbot.addWidget(window)
|
|
|
|
element1 = ImageData(image_path="/test1.jpg", x=100, y=0, width=100, height=100)
|
|
element2 = ImageData(image_path="/test2.jpg", x=200, y=100, width=100, height=100)
|
|
|
|
window.gl_widget.selected_elements = {element1, element2}
|
|
|
|
# Mock alignment to return changes (command will handle actual moves)
|
|
mock_manager.align_top.return_value = [
|
|
(element1, (100, 0)),
|
|
(element2, (200, 100))
|
|
]
|
|
|
|
# Execute alignment - command created
|
|
window.align_top()
|
|
|
|
# Should have created a command
|
|
assert window.project.history.can_undo()
|
|
|
|
# Can redo after undo
|
|
window.project.history.undo()
|
|
assert window.project.history.can_redo()
|
|
|
|
# Redo works
|
|
window.project.history.redo()
|
|
assert not window.project.history.can_redo() # Nothing left to redo
|