346 lines
12 KiB
Python
346 lines
12 KiB
Python
"""
|
|
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
|