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