""" Tests for PageNavigationMixin """ import pytest from unittest.mock import Mock, MagicMock, patch from PyQt6.QtOpenGLWidgets import QOpenGLWidget from pyPhotoAlbum.mixins.page_navigation import PageNavigationMixin from pyPhotoAlbum.mixins.viewport import ViewportMixin from pyPhotoAlbum.project import Project, Page from pyPhotoAlbum.page_layout import PageLayout from pyPhotoAlbum.models import GhostPageData # Create test widget combining necessary mixins class TestPageNavWidget(PageNavigationMixin, ViewportMixin, QOpenGLWidget): """Test widget combining page navigation and viewport mixins""" pass class TestPageNavigationInitialization: """Test PageNavigationMixin initialization""" def test_initialization_sets_defaults(self, qtbot): """Test that mixin initializes with correct defaults""" widget = TestPageNavWidget() qtbot.addWidget(widget) assert widget.current_page_index == 0 assert widget._page_renderers == [] def test_current_page_index_is_mutable(self, qtbot): """Test that current page index can be changed""" widget = TestPageNavWidget() qtbot.addWidget(widget) widget.current_page_index = 5 assert widget.current_page_index == 5 class TestGetPageAt: """Test _get_page_at method""" def test_get_page_at_no_renderers(self, qtbot): """Test returns None when no renderers""" widget = TestPageNavWidget() qtbot.addWidget(widget) result = widget._get_page_at(100, 100) assert result == (None, -1, None) def test_get_page_at_no_project(self, qtbot): """Test returns None when no project""" widget = TestPageNavWidget() qtbot.addWidget(widget) # Set up renderers but no project mock_renderer = Mock() widget._page_renderers = [(mock_renderer, Mock())] mock_window = Mock() mock_window.project = None widget.window = Mock(return_value=mock_window) result = widget._get_page_at(100, 100) assert result == (None, -1, None) def test_get_page_at_finds_page(self, qtbot): """Test finds page at coordinates""" widget = TestPageNavWidget() qtbot.addWidget(widget) # Create project with page mock_window = Mock() mock_window.project = Project(name="Test") page = Page(layout=PageLayout(width=210, height=297), page_number=1) mock_window.project.pages = [page] # Create renderer that returns True for is_point_in_page mock_renderer = Mock() mock_renderer.is_point_in_page = Mock(return_value=True) widget._page_renderers = [(mock_renderer, page)] widget.window = Mock(return_value=mock_window) result_page, result_index, result_renderer = widget._get_page_at(100, 100) assert result_page is page assert result_index == 0 assert result_renderer is mock_renderer def test_get_page_at_multiple_pages(self, qtbot): """Test finds correct page when multiple pages exist""" widget = TestPageNavWidget() qtbot.addWidget(widget) mock_window = Mock() mock_window.project = Project(name="Test") page1 = Page(layout=PageLayout(width=210, height=297), page_number=1) page2 = Page(layout=PageLayout(width=210, height=297), page_number=2) page3 = Page(layout=PageLayout(width=210, height=297), page_number=3) mock_window.project.pages = [page1, page2, page3] # First renderer returns False, second returns True renderer1 = Mock() renderer1.is_point_in_page = Mock(return_value=False) renderer2 = Mock() renderer2.is_point_in_page = Mock(return_value=True) renderer3 = Mock() renderer3.is_point_in_page = Mock(return_value=False) widget._page_renderers = [(renderer1, page1), (renderer2, page2), (renderer3, page3)] widget.window = Mock(return_value=mock_window) result_page, result_index, result_renderer = widget._get_page_at(100, 100) assert result_page is page2 assert result_index == 1 assert result_renderer is renderer2 class TestGetPagePositions: """Test _get_page_positions method""" def test_get_page_positions_no_project(self, qtbot): """Test returns empty list when no project""" widget = TestPageNavWidget() qtbot.addWidget(widget) mock_window = Mock() del mock_window.project # No project attribute widget.window = Mock(return_value=mock_window) result = widget._get_page_positions() assert result == [] def test_get_page_positions_single_page(self, qtbot): """Test calculates positions for single page""" widget = TestPageNavWidget() qtbot.addWidget(widget) mock_window = Mock() mock_window.project = Project(name="Test") mock_window.project.working_dpi = 96 mock_window.project.page_spacing_mm = 10 mock_window.project.page_size_mm = (210, 297) page = Page(layout=PageLayout(width=210, height=297), page_number=1) mock_window.project.pages = [page] # Mock calculate_page_layout_with_ghosts mock_window.project.calculate_page_layout_with_ghosts = Mock(return_value=[ ('page', page, 0) ]) widget.window = Mock(return_value=mock_window) result = widget._get_page_positions() # Should have one page entry assert len(result) >= 1 assert result[0][0] == 'page' assert result[0][1] is page def test_get_page_positions_includes_ghosts(self, qtbot): """Test includes ghost pages in result""" widget = TestPageNavWidget() qtbot.addWidget(widget) mock_window = Mock() mock_window.project = Project(name="Test") mock_window.project.working_dpi = 96 mock_window.project.page_spacing_mm = 10 mock_window.project.page_size_mm = (210, 297) page = Page(layout=PageLayout(width=210, height=297), page_number=1) mock_window.project.pages = [page] # Mock with ghost page mock_window.project.calculate_page_layout_with_ghosts = Mock(return_value=[ ('page', page, 0), ('ghost', None, 1) ]) widget.window = Mock(return_value=mock_window) result = widget._get_page_positions() # Should have page + ghost assert len(result) >= 2 page_types = [r[0] for r in result] assert 'page' in page_types assert 'ghost' in page_types class TestCheckGhostPageClick: """Test _check_ghost_page_click method""" def test_check_ghost_page_no_renderers(self, qtbot): """Test returns False when no renderers""" widget = TestPageNavWidget() qtbot.addWidget(widget) result = widget._check_ghost_page_click(100, 100) assert result is False def test_check_ghost_page_no_project(self, qtbot): """Test returns False when no project""" widget = TestPageNavWidget() qtbot.addWidget(widget) widget._page_renderers = [] mock_window = Mock() del mock_window.project widget.window = Mock(return_value=mock_window) result = widget._check_ghost_page_click(100, 100) assert result is False @patch('pyPhotoAlbum.page_renderer.PageRenderer') def test_check_ghost_page_click_on_ghost(self, mock_page_renderer_class, qtbot): """Test clicking on ghost page creates new page""" widget = TestPageNavWidget() qtbot.addWidget(widget) widget.zoom_level = 1.0 widget.pan_offset = [0, 0] # Mock the update method widget.update = Mock() # Setup project mock_window = Mock() mock_window.project = Project(name="Test") mock_window.project.working_dpi = 96 mock_window.project.page_spacing_mm = 10 mock_window.project.page_size_mm = (210, 297) mock_window.project.pages = [] # Mock _get_page_positions to return a ghost ghost = GhostPageData(page_size=(210, 297)) widget._get_page_positions = Mock(return_value=[ ('ghost', ghost, 100) ]) # Mock PageRenderer to say click is in page mock_renderer_instance = Mock() mock_renderer_instance.is_point_in_page = Mock(return_value=True) mock_page_renderer_class.return_value = mock_renderer_instance widget.window = Mock(return_value=mock_window) # Click on ghost page result = widget._check_ghost_page_click(150, 150) # Should return True and create page assert result is True assert len(mock_window.project.pages) == 1 assert widget.update.called @patch('pyPhotoAlbum.page_renderer.PageRenderer') def test_check_ghost_page_click_outside_ghost(self, mock_page_renderer_class, qtbot): """Test clicking outside ghost page returns False""" widget = TestPageNavWidget() qtbot.addWidget(widget) widget.zoom_level = 1.0 widget.pan_offset = [0, 0] mock_window = Mock() mock_window.project = Project(name="Test") mock_window.project.working_dpi = 96 mock_window.project.page_spacing_mm = 10 mock_window.project.page_size_mm = (210, 297) mock_window.project.pages = [] ghost = GhostPageData(page_size=(210, 297)) widget._get_page_positions = Mock(return_value=[ ('ghost', ghost, 100) ]) # Mock renderer to say click is NOT in page mock_renderer_instance = Mock() mock_renderer_instance.is_point_in_page = Mock(return_value=False) mock_page_renderer_class.return_value = mock_renderer_instance widget.window = Mock(return_value=mock_window) result = widget._check_ghost_page_click(5000, 5000) assert result is False assert len(mock_window.project.pages) == 0 class TestUpdatePageStatus: """Test _update_page_status method""" def test_update_page_status_no_project(self, qtbot): """Test does nothing when no project""" widget = TestPageNavWidget() qtbot.addWidget(widget) mock_window = Mock() mock_window.project = None widget.window = Mock(return_value=mock_window) # Should not raise exception widget._update_page_status(100, 100) def test_update_page_status_no_renderers(self, qtbot): """Test does nothing when no renderers""" widget = TestPageNavWidget() qtbot.addWidget(widget) mock_window = Mock() mock_window.project = Project(name="Test") mock_window.project.pages = [] widget.window = Mock(return_value=mock_window) widget._update_page_status(100, 100) def test_update_page_status_on_page(self, qtbot): """Test updates status bar when on a page""" widget = TestPageNavWidget() qtbot.addWidget(widget) widget.zoom_level = 1.0 mock_window = Mock() mock_window.project = Project(name="Test") page = Page(layout=PageLayout(width=210, height=297), page_number=1) page.get_page_count = Mock(return_value=1) page.is_double_spread = False mock_window.project.pages = [page] mock_window.status_bar = Mock() mock_renderer = Mock() mock_renderer.is_point_in_page = Mock(return_value=True) widget._page_renderers = [(mock_renderer, page)] widget.window = Mock(return_value=mock_window) widget._update_page_status(100, 100) # Status bar should be updated assert mock_window.status_bar.showMessage.called call_args = mock_window.status_bar.showMessage.call_args[0][0] assert "Page 1" in call_args