pyPhotoAlbum/tests/test_keyboard_navigation_mixin.py
Duncan Tourolle b18a780a33
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
increase test coverage
2025-11-28 19:54:41 +01:00

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)