pyPhotoAlbum/tests/test_view_ops_mixin.py
2025-11-11 16:02:02 +00:00

475 lines
13 KiB
Python

"""
Tests for ViewOperationsMixin
"""
import pytest
from unittest.mock import Mock, patch
from PyQt6.QtWidgets import QMainWindow, QDialog
from pyPhotoAlbum.mixins.operations.view_ops import ViewOperationsMixin
from pyPhotoAlbum.project import Project, Page
from pyPhotoAlbum.page_layout import PageLayout
class TestViewWindow(ViewOperationsMixin, QMainWindow):
"""Test window with view operations mixin"""
def __init__(self):
super().__init__()
self.gl_widget = Mock()
self.gl_widget.zoom_level = 1.0
self.gl_widget.current_page_index = 0
self.gl_widget.width = Mock(return_value=800)
self.gl_widget.height = Mock(return_value=600)
self.project = Mock(spec=Project)
self.project.working_dpi = 300
self._update_view_called = False
self._status_message = None
def get_current_page(self):
if hasattr(self, '_current_page'):
return self._current_page
return None
def update_view(self):
self._update_view_called = True
def show_status(self, message, timeout=0):
self._status_message = message
class TestZoomOperations:
"""Test zoom operations"""
def test_zoom_in_success(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.gl_widget.zoom_level = 1.0
window.zoom_in()
assert window.gl_widget.zoom_level == 1.2
assert window._update_view_called
assert "120%" in window._status_message
def test_zoom_in_max_limit(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.gl_widget.zoom_level = 4.5
window.zoom_in()
# 4.5 * 1.2 = 5.4, but clamped to 5.0
assert window.gl_widget.zoom_level == 5.0
assert window._update_view_called
def test_zoom_out_success(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.gl_widget.zoom_level = 1.2
window.zoom_out()
assert window.gl_widget.zoom_level == 1.0
assert window._update_view_called
assert "100%" in window._status_message
def test_zoom_out_min_limit(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.gl_widget.zoom_level = 0.11
window.zoom_out()
# 0.11 / 1.2 ≈ 0.092, but clamped to 0.1
assert window.gl_widget.zoom_level == 0.1
assert window._update_view_called
def test_zoom_fit_success(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
# Setup page
layout = PageLayout(width=210, height=297) # A4 in mm
page = Mock()
page.layout = layout
window.project.pages = [page]
window.zoom_fit()
# Should calculate zoom to fit page in widget
assert window._update_view_called
assert "zoom" in window._status_message.lower()
# Zoom level should be set based on widget size
assert 0.1 <= window.gl_widget.zoom_level <= 5.0
def test_zoom_fit_no_pages(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window.project.pages = []
window.zoom_fit()
# Should return early without error
assert not window._update_view_called
class TestSnappingToggles:
"""Test snapping toggle operations"""
def test_toggle_grid_snap_enable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_grid = False
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_grid_snap()
assert layout.snapping_system.snap_to_grid is True
assert "enabled" in window._status_message.lower()
assert window._update_view_called
def test_toggle_grid_snap_disable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_grid = True
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_grid_snap()
assert layout.snapping_system.snap_to_grid is False
assert "disabled" in window._status_message.lower()
assert window._update_view_called
def test_toggle_grid_snap_no_page(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.toggle_grid_snap()
assert not window._update_view_called
def test_toggle_edge_snap_enable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_edges = False
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_edge_snap()
assert layout.snapping_system.snap_to_edges is True
assert "enabled" in window._status_message.lower()
assert window._update_view_called
def test_toggle_edge_snap_disable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_edges = True
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_edge_snap()
assert layout.snapping_system.snap_to_edges is False
assert "disabled" in window._status_message.lower()
def test_toggle_guide_snap_enable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_guides = False
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_guide_snap()
assert layout.snapping_system.snap_to_guides is True
assert "enabled" in window._status_message.lower()
def test_toggle_guide_snap_disable(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_guides = True
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_guide_snap()
assert layout.snapping_system.snap_to_guides is False
assert "disabled" in window._status_message.lower()
def test_toggle_snap_lines_show(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.show_snap_lines = False
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_snap_lines()
assert layout.show_snap_lines is True
assert "visible" in window._status_message.lower()
def test_toggle_snap_lines_hide(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.show_snap_lines = True
page = Mock()
page.layout = layout
window._current_page = page
window.toggle_snap_lines()
assert layout.show_snap_lines is False
assert "hidden" in window._status_message.lower()
class TestGuideOperations:
"""Test guide add/clear operations"""
def test_add_horizontal_guide(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout(width=210, height=297)
page = Mock()
page.layout = layout
window._current_page = page
window.add_horizontal_guide()
# Guide should be added at vertical center (297 / 2 = 148.5mm)
assert "148.5" in window._status_message
assert window._update_view_called
def test_add_horizontal_guide_no_page(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.add_horizontal_guide()
assert not window._update_view_called
def test_add_vertical_guide(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout(width=210, height=297)
page = Mock()
page.layout = layout
window._current_page = page
window.add_vertical_guide()
# Guide should be added at horizontal center (210 / 2 = 105.0mm)
assert "105.0" in window._status_message
assert window._update_view_called
def test_add_vertical_guide_no_page(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.add_vertical_guide()
assert not window._update_view_called
def test_clear_guides_with_guides(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
# Add some guides
layout.snapping_system.add_guide(100, 'vertical')
layout.snapping_system.add_guide(150, 'horizontal')
page = Mock()
page.layout = layout
window._current_page = page
window.clear_guides()
assert len(layout.snapping_system.guides) == 0
assert "2" in window._status_message # Cleared 2 guides
assert window._update_view_called
def test_clear_guides_no_guides(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
page = Mock()
page.layout = layout
window._current_page = page
window.clear_guides()
assert "0" in window._status_message
assert window._update_view_called
def test_clear_guides_no_page(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.clear_guides()
assert not window._update_view_called
class TestGridSettingsDialog:
"""Test grid settings dialog"""
def test_set_grid_size_no_page(self, qtbot):
"""Test set_grid_size with no current page"""
window = TestViewWindow()
qtbot.addWidget(window)
window._current_page = None
window.set_grid_size()
# Should return early without error
assert not window._update_view_called
def test_set_grid_size_with_page(self, qtbot):
"""Test set_grid_size creates dialog with current page"""
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.grid_size_mm = 10.0
layout.snapping_system.snap_threshold_mm = 5.0
page = Mock()
page.layout = layout
window._current_page = page
# Mock the full dialog workflow
mock_dialog = Mock(spec=QDialog)
mock_dialog.exec.return_value = QDialog.DialogCode.Rejected
with patch('PyQt6.QtWidgets.QDialog', return_value=mock_dialog):
window.set_grid_size()
# Dialog should have been created and exec called
mock_dialog.exec.assert_called_once()
class TestLayoutTabDelegation:
"""Test Layout tab methods delegate to main methods"""
def test_layout_toggle_grid_snap_delegates(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_grid = False
page = Mock()
page.layout = layout
window._current_page = page
window.layout_toggle_grid_snap()
# Should delegate to toggle_grid_snap
assert layout.snapping_system.snap_to_grid is True
assert window._update_view_called
def test_layout_toggle_edge_snap_delegates(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_edges = False
page = Mock()
page.layout = layout
window._current_page = page
window.layout_toggle_edge_snap()
assert layout.snapping_system.snap_to_edges is True
assert window._update_view_called
def test_layout_toggle_guide_snap_delegates(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.snapping_system.snap_to_guides = False
page = Mock()
page.layout = layout
window._current_page = page
window.layout_toggle_guide_snap()
assert layout.snapping_system.snap_to_guides is True
assert window._update_view_called
def test_layout_toggle_snap_lines_delegates(self, qtbot):
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
layout.show_snap_lines = False
page = Mock()
page.layout = layout
window._current_page = page
window.layout_toggle_snap_lines()
assert layout.show_snap_lines is True
assert window._update_view_called
def test_layout_set_grid_size_delegates(self, qtbot):
"""Test layout tab grid size delegates to main method"""
window = TestViewWindow()
qtbot.addWidget(window)
layout = PageLayout()
page = Mock()
page.layout = layout
window._current_page = page
# Mock dialog to verify delegation
mock_dialog = Mock(spec=QDialog)
mock_dialog.exec.return_value = QDialog.DialogCode.Rejected
with patch('PyQt6.QtWidgets.QDialog', return_value=mock_dialog):
window.layout_set_grid_size()
# Verify method was called (dialog creation attempted)
mock_dialog.exec.assert_called_once()