""" 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()