All checks were successful
Python CI / test (push) Successful in 1m28s
Lint / lint (push) Successful in 1m4s
Tests / test (3.11) (push) Successful in 1m41s
Tests / test (3.12) (push) Successful in 1m42s
Tests / test (3.13) (push) Successful in 1m35s
Tests / test (3.14) (push) Successful in 1m15s
804 lines
30 KiB
Python
804 lines
30 KiB
Python
"""
|
|
Tests for KeyboardNavigationMixin
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import Mock, MagicMock, patch
|
|
from PyQt6.QtCore import Qt
|
|
from PyQt6.QtOpenGLWidgets import QOpenGLWidget
|
|
from pyPhotoAlbum.mixins.keyboard_navigation import KeyboardNavigationMixin
|
|
from pyPhotoAlbum.mixins.viewport import ViewportMixin
|
|
from pyPhotoAlbum.project import Project, Page
|
|
from pyPhotoAlbum.page_layout import PageLayout
|
|
from pyPhotoAlbum.models import ImageData, TextBoxData
|
|
|
|
|
|
# Create test widget combining necessary mixins
|
|
class KeyboardNavWidget(KeyboardNavigationMixin, ViewportMixin, QOpenGLWidget):
|
|
"""Test widget combining keyboard navigation and viewport mixins"""
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
self.selected_elements = set()
|
|
|
|
|
|
class TestNavigateToNextPage:
|
|
"""Test _navigate_to_next_page method"""
|
|
|
|
def test_navigate_next_no_project_attribute(self, qtbot):
|
|
"""Test does nothing when main window has no project attribute"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
mock_window = Mock()
|
|
del mock_window.project
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
# Should not raise exception
|
|
widget._navigate_to_next_page()
|
|
|
|
def test_navigate_next_no_project(self, qtbot):
|
|
"""Test does nothing when project is None"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = None
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget._navigate_to_next_page()
|
|
|
|
def test_navigate_next_empty_pages(self, qtbot):
|
|
"""Test does nothing when project has no pages"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.pages = []
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget._navigate_to_next_page()
|
|
|
|
def test_navigate_next_at_last_page(self, qtbot):
|
|
"""Test does nothing when already at last page"""
|
|
widget = KeyboardNavWidget()
|
|
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
|
|
page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
|
|
mock_window.project.pages = [page1, page2]
|
|
mock_window._get_most_visible_page_index = Mock(return_value=1) # Last page
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
initial_pan = widget.pan_offset.copy()
|
|
widget._navigate_to_next_page()
|
|
|
|
# Should not navigate (already at last page)
|
|
|
|
def test_navigate_next_to_next_page(self, qtbot):
|
|
"""Test navigates to next page successfully"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.zoom_level = 1.0
|
|
widget.pan_offset = [0, 0]
|
|
|
|
# Need to set widget size for calculations
|
|
with patch.object(widget, "height", return_value=800):
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
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]
|
|
mock_window._get_most_visible_page_index = Mock(return_value=0) # First page
|
|
mock_window.project.get_page_display_name = Mock(return_value="Page 2")
|
|
mock_window.show_status = Mock()
|
|
mock_window.update_scrollbars = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
# Mock update method
|
|
widget.update = Mock()
|
|
|
|
widget._navigate_to_next_page()
|
|
|
|
# Should have scrolled to page 2
|
|
mock_window.show_status.assert_called_once_with("Navigated to Page 2", 2000)
|
|
widget.update.assert_called()
|
|
|
|
def test_navigate_next_without_status_bar(self, qtbot):
|
|
"""Test navigation works even without show_status method"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.zoom_level = 1.0
|
|
widget.pan_offset = [0, 0]
|
|
|
|
with patch.object(widget, "height", return_value=800):
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
|
|
mock_window.project.pages = [page1, page2]
|
|
mock_window._get_most_visible_page_index = Mock(return_value=0)
|
|
mock_window.update_scrollbars = Mock()
|
|
# No show_status attribute
|
|
del mock_window.show_status
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget.update = Mock()
|
|
|
|
# Should not raise exception
|
|
widget._navigate_to_next_page()
|
|
widget.update.assert_called()
|
|
|
|
|
|
class TestNavigateToPreviousPage:
|
|
"""Test _navigate_to_previous_page method"""
|
|
|
|
def test_navigate_prev_no_project_attribute(self, qtbot):
|
|
"""Test does nothing when main window has no project attribute"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
mock_window = Mock()
|
|
del mock_window.project
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget._navigate_to_previous_page()
|
|
|
|
def test_navigate_prev_no_project(self, qtbot):
|
|
"""Test does nothing when project is None"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = None
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget._navigate_to_previous_page()
|
|
|
|
def test_navigate_prev_empty_pages(self, qtbot):
|
|
"""Test does nothing when project has no pages"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.pages = []
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget._navigate_to_previous_page()
|
|
|
|
def test_navigate_prev_at_first_page(self, qtbot):
|
|
"""Test does nothing when already at first page"""
|
|
widget = KeyboardNavWidget()
|
|
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
|
|
page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
|
|
mock_window.project.pages = [page1, page2]
|
|
mock_window._get_most_visible_page_index = Mock(return_value=0) # First page
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget._navigate_to_previous_page()
|
|
|
|
# Should not navigate (already at first page)
|
|
|
|
def test_navigate_prev_to_previous_page(self, qtbot):
|
|
"""Test navigates to previous page successfully"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.zoom_level = 1.0
|
|
widget.pan_offset = [0, 0]
|
|
|
|
with patch.object(widget, "height", return_value=800):
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
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]
|
|
mock_window._get_most_visible_page_index = Mock(return_value=2) # Third page
|
|
mock_window.project.get_page_display_name = Mock(return_value="Page 2")
|
|
mock_window.show_status = Mock()
|
|
mock_window.update_scrollbars = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget.update = Mock()
|
|
|
|
widget._navigate_to_previous_page()
|
|
|
|
# Should have scrolled to page 2
|
|
mock_window.show_status.assert_called_once_with("Navigated to Page 2", 2000)
|
|
widget.update.assert_called()
|
|
|
|
def test_navigate_prev_without_status_bar(self, qtbot):
|
|
"""Test navigation works even without show_status method"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.zoom_level = 1.0
|
|
widget.pan_offset = [0, 0]
|
|
|
|
with patch.object(widget, "height", return_value=800):
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
|
|
mock_window.project.pages = [page1, page2]
|
|
mock_window._get_most_visible_page_index = Mock(return_value=1)
|
|
mock_window.update_scrollbars = Mock()
|
|
del mock_window.show_status
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget.update = Mock()
|
|
|
|
widget._navigate_to_previous_page()
|
|
widget.update.assert_called()
|
|
|
|
|
|
class TestScrollToPage:
|
|
"""Test _scroll_to_page method"""
|
|
|
|
def test_scroll_to_page_no_project_attribute(self, qtbot):
|
|
"""Test does nothing when main window has no project attribute"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
mock_window = Mock()
|
|
del mock_window.project
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
page = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
widget._scroll_to_page(page, 0)
|
|
|
|
def test_scroll_to_page_first_page(self, qtbot):
|
|
"""Test scrolling to first page"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.zoom_level = 1.0
|
|
widget.pan_offset = [0, 0]
|
|
|
|
with patch.object(widget, "height", return_value=800):
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
|
|
mock_window.project.pages = [page1, page2]
|
|
mock_window.update_scrollbars = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget.update = Mock()
|
|
|
|
widget._scroll_to_page(page1, 0)
|
|
|
|
# Should have updated pan offset
|
|
assert widget.pan_offset[1] != 0 # Y offset should be set
|
|
widget.update.assert_called()
|
|
mock_window.update_scrollbars.assert_called()
|
|
|
|
def test_scroll_to_page_second_page(self, qtbot):
|
|
"""Test scrolling to second page accounts for first page height"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.zoom_level = 1.0
|
|
widget.pan_offset = [0, 0]
|
|
|
|
with patch.object(widget, "height", return_value=800):
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
|
|
mock_window.project.pages = [page1, page2]
|
|
mock_window.update_scrollbars = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget.update = Mock()
|
|
|
|
# Scroll to first page
|
|
widget._scroll_to_page(page1, 0)
|
|
first_page_offset = widget.pan_offset[1]
|
|
|
|
# Scroll to second page
|
|
widget._scroll_to_page(page2, 1)
|
|
second_page_offset = widget.pan_offset[1]
|
|
|
|
# Second page offset should be different (accounting for first page height + spacing)
|
|
assert second_page_offset != first_page_offset
|
|
|
|
def test_scroll_to_page_with_different_zoom(self, qtbot):
|
|
"""Test scrolling respects zoom level"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.pan_offset = [0, 0]
|
|
|
|
with patch.object(widget, "height", return_value=800):
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
page2 = Page(layout=PageLayout(width=210, height=297), page_number=2)
|
|
mock_window.project.pages = [page1, page2]
|
|
mock_window.update_scrollbars = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget.update = Mock()
|
|
|
|
# Test at 100% zoom
|
|
widget.zoom_level = 1.0
|
|
widget._scroll_to_page(page2, 1)
|
|
offset_100 = widget.pan_offset[1]
|
|
|
|
# Test at 50% zoom
|
|
widget.zoom_level = 0.5
|
|
widget._scroll_to_page(page2, 1)
|
|
offset_50 = widget.pan_offset[1]
|
|
|
|
# Offsets should be different due to zoom
|
|
assert offset_100 != offset_50
|
|
|
|
def test_scroll_to_page_calls_clamp_if_available(self, qtbot):
|
|
"""Test that clamp_pan_offset is called if available"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.zoom_level = 1.0
|
|
widget.pan_offset = [0, 0]
|
|
widget.clamp_pan_offset = Mock()
|
|
|
|
with patch.object(widget, "height", return_value=800):
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
mock_window.project.pages = [page1]
|
|
mock_window.update_scrollbars = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget.update = Mock()
|
|
|
|
widget._scroll_to_page(page1, 0)
|
|
|
|
# clamp_pan_offset should have been called
|
|
widget.clamp_pan_offset.assert_called()
|
|
|
|
def test_scroll_to_page_without_update_scrollbars(self, qtbot):
|
|
"""Test scrolling works even without update_scrollbars method"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.zoom_level = 1.0
|
|
widget.pan_offset = [0, 0]
|
|
|
|
with patch.object(widget, "height", return_value=800):
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
page1 = Page(layout=PageLayout(width=210, height=297), page_number=1)
|
|
mock_window.project.pages = [page1]
|
|
del mock_window.update_scrollbars
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
widget.update = Mock()
|
|
|
|
# Should not raise exception
|
|
widget._scroll_to_page(page1, 0)
|
|
widget.update.assert_called()
|
|
|
|
|
|
class TestMoveViewportWithArrowKeys:
|
|
"""Test _move_viewport_with_arrow_keys method"""
|
|
|
|
def test_move_viewport_up(self, qtbot):
|
|
"""Test moving viewport up with up arrow"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.pan_offset = [0, 0]
|
|
widget.update = Mock()
|
|
|
|
widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
# Pan Y should increase by 50
|
|
assert widget.pan_offset[1] == 50
|
|
assert widget.pan_offset[0] == 0
|
|
widget.update.assert_called()
|
|
|
|
def test_move_viewport_down(self, qtbot):
|
|
"""Test moving viewport down with down arrow"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.pan_offset = [0, 0]
|
|
widget.update = Mock()
|
|
|
|
widget._move_viewport_with_arrow_keys(Qt.Key.Key_Down)
|
|
|
|
# Pan Y should decrease by 50
|
|
assert widget.pan_offset[1] == -50
|
|
assert widget.pan_offset[0] == 0
|
|
widget.update.assert_called()
|
|
|
|
def test_move_viewport_left(self, qtbot):
|
|
"""Test moving viewport left with left arrow"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.pan_offset = [0, 0]
|
|
widget.update = Mock()
|
|
|
|
widget._move_viewport_with_arrow_keys(Qt.Key.Key_Left)
|
|
|
|
# Pan X should increase by 50
|
|
assert widget.pan_offset[0] == 50
|
|
assert widget.pan_offset[1] == 0
|
|
widget.update.assert_called()
|
|
|
|
def test_move_viewport_right(self, qtbot):
|
|
"""Test moving viewport right with right arrow"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.pan_offset = [0, 0]
|
|
widget.update = Mock()
|
|
|
|
widget._move_viewport_with_arrow_keys(Qt.Key.Key_Right)
|
|
|
|
# Pan X should decrease by 50
|
|
assert widget.pan_offset[0] == -50
|
|
assert widget.pan_offset[1] == 0
|
|
widget.update.assert_called()
|
|
|
|
def test_move_viewport_multiple_moves(self, qtbot):
|
|
"""Test multiple consecutive moves accumulate"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.pan_offset = [0, 0]
|
|
widget.update = Mock()
|
|
|
|
widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)
|
|
widget._move_viewport_with_arrow_keys(Qt.Key.Key_Right)
|
|
|
|
# Should have moved up (Y+50) and right (X-50)
|
|
assert widget.pan_offset[0] == -50
|
|
assert widget.pan_offset[1] == 50
|
|
|
|
def test_move_viewport_calls_clamp_if_available(self, qtbot):
|
|
"""Test that clamp_pan_offset is called if available"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.pan_offset = [0, 0]
|
|
widget.clamp_pan_offset = Mock()
|
|
widget.update = Mock()
|
|
|
|
widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
widget.clamp_pan_offset.assert_called()
|
|
|
|
def test_move_viewport_updates_scrollbars_if_available(self, qtbot):
|
|
"""Test that update_scrollbars is called if available"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.pan_offset = [0, 0]
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.pages = []
|
|
mock_window.update_scrollbars = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
mock_window.update_scrollbars.assert_called()
|
|
|
|
def test_move_viewport_without_scrollbars(self, qtbot):
|
|
"""Test movement works even without update_scrollbars method"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.pan_offset = [0, 0]
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.pages = []
|
|
del mock_window.update_scrollbars
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
# Should not raise exception
|
|
widget._move_viewport_with_arrow_keys(Qt.Key.Key_Up)
|
|
assert widget.pan_offset[1] == 50
|
|
|
|
|
|
class TestMoveSelectedElementsWithArrowKeys:
|
|
"""Test _move_selected_elements_with_arrow_keys method"""
|
|
|
|
def test_move_elements_no_project_attribute(self, qtbot):
|
|
"""Test does nothing when main window has no project attribute"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
mock_window = Mock()
|
|
del mock_window.project
|
|
widget.window = Mock(return_value=mock_window)
|
|
|
|
# Should not raise exception
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
def test_move_elements_no_selected_elements(self, qtbot):
|
|
"""Test works when no elements are selected (just updates view)"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
widget.selected_elements = set()
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
# Update is called even with no selected elements
|
|
widget.update.assert_called()
|
|
|
|
def test_move_elements_up(self, qtbot):
|
|
"""Test moving elements up with up arrow"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
# Create element without parent page (no snapping)
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
# Y should decrease by 1mm
|
|
assert element.position == (5, 4)
|
|
widget.update.assert_called()
|
|
|
|
def test_move_elements_down(self, qtbot):
|
|
"""Test moving elements down with down arrow"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Down)
|
|
|
|
# Y should increase by 1mm
|
|
assert element.position == (5, 6)
|
|
widget.update.assert_called()
|
|
|
|
def test_move_elements_left(self, qtbot):
|
|
"""Test moving elements left with left arrow"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Left)
|
|
|
|
# X should decrease by 1mm
|
|
assert element.position == (4, 5)
|
|
widget.update.assert_called()
|
|
|
|
def test_move_elements_right(self, qtbot):
|
|
"""Test moving elements right with right arrow"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Right)
|
|
|
|
# X should increase by 1mm
|
|
assert element.position == (6, 5)
|
|
widget.update.assert_called()
|
|
|
|
def test_move_multiple_elements(self, qtbot):
|
|
"""Test moving multiple selected elements"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element1 = ImageData(image_path="test1.jpg", width=10, height=10, x=5, y=5)
|
|
element2 = TextBoxData(text_content="Test", width=20, height=20, x=10, y=10)
|
|
widget.selected_elements = {element1, element2}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.show_status = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
# Both elements should move
|
|
assert element1.position == (5, 4)
|
|
assert element2.position == (10, 9)
|
|
mock_window.show_status.assert_called_once_with("Moved 2 elements", 1000)
|
|
|
|
def test_move_elements_with_snapping(self, qtbot):
|
|
"""Test moving elements with snapping enabled"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
# Create element with parent page
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
|
|
# Create mock page with snapping system
|
|
mock_page = Mock()
|
|
mock_snapping_system = Mock()
|
|
mock_snapping_system.snap_position = Mock(return_value=(5.5, 4.5)) # Snapped position
|
|
mock_layout = Mock()
|
|
mock_layout.snapping_system = mock_snapping_system
|
|
mock_layout.size = (210, 297)
|
|
mock_page.layout = mock_layout
|
|
|
|
element._parent_page = mock_page
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.project.working_dpi = 96
|
|
mock_window.show_status = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
# Position should be the snapped position
|
|
assert element.position == (5.5, 4.5)
|
|
# Snapping system should have been called
|
|
mock_snapping_system.snap_position.assert_called_once()
|
|
|
|
def test_move_elements_without_snapping(self, qtbot):
|
|
"""Test moving elements without parent page (no snapping)"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
# No _parent_page attribute
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.show_status = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
# Position should be directly set (no snapping)
|
|
assert element.position == (5, 4)
|
|
|
|
def test_move_elements_shows_status_single_element(self, qtbot):
|
|
"""Test status message for single element"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.show_status = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
mock_window.show_status.assert_called_once_with("Moved 1 element", 1000)
|
|
|
|
def test_move_elements_shows_status_multiple_elements(self, qtbot):
|
|
"""Test status message for multiple elements"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element1 = ImageData(image_path="test1.jpg", width=10, height=10, x=5, y=5)
|
|
element2 = ImageData(image_path="test2.jpg", width=10, height=10, x=10, y=10)
|
|
element3 = ImageData(image_path="test3.jpg", width=10, height=10, x=15, y=15)
|
|
widget.selected_elements = {element1, element2, element3}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.show_status = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Right)
|
|
|
|
mock_window.show_status.assert_called_once_with("Moved 3 elements", 1000)
|
|
|
|
def test_move_elements_without_status_bar(self, qtbot):
|
|
"""Test movement works even without show_status method"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
del mock_window.show_status
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
# Should not raise exception
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
assert element.position == (5, 4)
|
|
widget.update.assert_called()
|
|
|
|
def test_move_elements_element_without_parent_page_attribute(self, qtbot):
|
|
"""Test moving elements that don't have _parent_page attribute"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
# Ensure no _parent_page attribute exists
|
|
if hasattr(element, "_parent_page"):
|
|
delattr(element, "_parent_page")
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.show_status = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
# Should use direct positioning (no snapping)
|
|
assert element.position == (5, 4)
|
|
|
|
def test_move_elements_with_none_parent_page(self, qtbot):
|
|
"""Test moving elements with _parent_page set to None"""
|
|
widget = KeyboardNavWidget()
|
|
qtbot.addWidget(widget)
|
|
|
|
element = ImageData(image_path="test.jpg", width=10, height=10, x=5, y=5)
|
|
element._parent_page = None
|
|
widget.selected_elements = {element}
|
|
|
|
mock_window = Mock()
|
|
mock_window.project = Project(name="Test")
|
|
mock_window.show_status = Mock()
|
|
widget.window = Mock(return_value=mock_window)
|
|
widget.update = Mock()
|
|
|
|
widget._move_selected_elements_with_arrow_keys(Qt.Key.Key_Up)
|
|
|
|
# Should use direct positioning (no snapping)
|
|
assert element.position == (5, 4)
|