Skip to content

Commit 60c9171

Browse files
committed
test(center window): Update regression tests
1 parent faada24 commit 60c9171

11 files changed

+88
-19
lines changed

tests/conftest.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ def _mock_context(config: Optional[MockConfiguration] = None) -> tuple[contextli
135135

136136
if config.patch_tkinter:
137137
patches.extend([patch("tkinter.Tk"), patch("tkinter.Toplevel")])
138+
patches.append(patch.object(BaseWindow, "center_window_on_screen"))
138139

139140
if config.patch_icon_setup:
140141
patches.append(patch.object(BaseWindow, "_setup_application_icon"))

tests/test_frontend_tkinter_base_window.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1488,6 +1488,8 @@ def test_centers_window_on_single_monitor(self) -> None:
14881488

14891489
# Mock window with 300x200 dimensions
14901490
mock_window = MagicMock()
1491+
mock_window.winfo_width.return_value = 1
1492+
mock_window.winfo_height.return_value = 1
14911493
mock_window.winfo_reqwidth.return_value = 300
14921494
mock_window.winfo_reqheight.return_value = 200
14931495
mock_window.winfo_pointerx.return_value = 960 # Center of monitor
@@ -1527,6 +1529,8 @@ def test_centers_window_on_monitor_with_pointer(self) -> None:
15271529

15281530
# Mock 400x300 window with pointer on second monitor
15291531
mock_window = MagicMock()
1532+
mock_window.winfo_width.return_value = 1
1533+
mock_window.winfo_height.return_value = 1
15301534
mock_window.winfo_reqwidth.return_value = 400
15311535
mock_window.winfo_reqheight.return_value = 300
15321536
mock_window.winfo_pointerx.return_value = 2880 # Middle of second monitor (1920 + 960)
@@ -1560,6 +1564,8 @@ def test_handles_pointer_outside_any_monitor(self) -> None:
15601564

15611565
# Mock 200x150 window with pointer outside bounds
15621566
mock_window = MagicMock()
1567+
mock_window.winfo_width.return_value = 1
1568+
mock_window.winfo_height.return_value = 1
15631569
mock_window.winfo_reqwidth.return_value = 200
15641570
mock_window.winfo_reqheight.return_value = 150
15651571
mock_window.winfo_pointerx.return_value = 5000 # Way outside
@@ -1592,6 +1598,8 @@ def test_respects_monitor_bounds(self) -> None:
15921598
mock_monitors.return_value = [monitor]
15931599

15941600
mock_window = MagicMock()
1601+
mock_window.winfo_width.return_value = 1
1602+
mock_window.winfo_height.return_value = 1
15951603
mock_window.winfo_reqwidth.return_value = 500
15961604
mock_window.winfo_reqheight.return_value = 400
15971605
mock_window.winfo_pointerx.return_value = 512 # Center of monitor
@@ -1626,6 +1634,8 @@ def test_handles_monitor_with_offset_coordinates(self) -> None:
16261634

16271635
# Mock 300x200 window with pointer on this offset monitor
16281636
mock_window = MagicMock()
1637+
mock_window.winfo_width.return_value = 1
1638+
mock_window.winfo_height.return_value = 1
16291639
mock_window.winfo_reqwidth.return_value = 300
16301640
mock_window.winfo_reqheight.return_value = 200
16311641
mock_window.winfo_pointerx.return_value = 1060 # On the offset monitor
@@ -1654,6 +1664,8 @@ def test_handles_empty_monitor_list_gracefully(self) -> None:
16541664

16551665
# Mock window with fallback screen dimensions
16561666
mock_window = MagicMock()
1667+
mock_window.winfo_width.return_value = 1
1668+
mock_window.winfo_height.return_value = 1
16571669
mock_window.winfo_reqwidth.return_value = 200
16581670
mock_window.winfo_reqheight.return_value = 150
16591671
mock_window.winfo_screenwidth.return_value = 1920
@@ -1682,6 +1694,8 @@ def test_handles_get_monitors_exception_gracefully(self) -> None:
16821694

16831695
# Mock window with fallback screen dimensions
16841696
mock_window = MagicMock()
1697+
mock_window.winfo_width.return_value = 1
1698+
mock_window.winfo_height.return_value = 1
16851699
mock_window.winfo_reqwidth.return_value = 300
16861700
mock_window.winfo_reqheight.return_value = 200
16871701
mock_window.winfo_screenwidth.return_value = 1920
@@ -1713,6 +1727,8 @@ def test_calls_update_idletasks_before_positioning(self) -> None:
17131727
mock_monitors.return_value = [monitor]
17141728

17151729
mock_window = MagicMock()
1730+
mock_window.winfo_width.return_value = 1
1731+
mock_window.winfo_height.return_value = 1
17161732
mock_window.winfo_reqwidth.return_value = 400
17171733
mock_window.winfo_reqheight.return_value = 300
17181734
mock_window.winfo_pointerx.return_value = 960

tests/test_frontend_tkinter_component_editor_base.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -999,6 +999,16 @@ def editor_for_ui_tests(self, mock_filesystem: MagicMock) -> ComponentEditorWind
999999
editor.template_manager = MagicMock()
10001000
editor.complexity_var = MagicMock()
10011001

1002+
# Set winfo return values so center_window_on_screen works without TypeError
1003+
editor.root.winfo_width.return_value = 1
1004+
editor.root.winfo_height.return_value = 1
1005+
editor.root.winfo_reqwidth.return_value = WINDOW_WIDTH_PIX
1006+
editor.root.winfo_reqheight.return_value = 600
1007+
editor.root.winfo_pointerx.return_value = 0
1008+
editor.root.winfo_pointery.return_value = 0
1009+
editor.root.winfo_screenwidth.return_value = 1920
1010+
editor.root.winfo_screenheight.return_value = 1080
1011+
10021012
return editor
10031013

10041014
def test_user_sees_proper_window_setup_with_title_and_geometry(
@@ -1016,7 +1026,7 @@ def test_user_sees_proper_window_setup_with_title_and_geometry(
10161026

10171027
# Assert: Window should be configured properly
10181028
editor_for_ui_tests.root.title.assert_called_once()
1019-
editor_for_ui_tests.root.geometry.assert_called_once_with(f"{WINDOW_WIDTH_PIX}x600")
1029+
editor_for_ui_tests.root.geometry.assert_any_call(f"{WINDOW_WIDTH_PIX}x600")
10201030
editor_for_ui_tests.root.protocol.assert_called_once_with("WM_DELETE_WINDOW", editor_for_ui_tests.on_closing)
10211031

10221032
@patch("ardupilot_methodic_configurator.frontend_tkinter_component_editor_base.ttk.Style")

tests/test_frontend_tkinter_connection_selection.py

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@
2929

3030
_MOD = "ardupilot_methodic_configurator.frontend_tkinter_connection_selection"
3131

32+
33+
def _mock_basewindow_init(self, root_tk=None) -> None: # noqa: ARG001 # pylint: disable=unused-argument
34+
"""Module-level BaseWindow.__init__ replacement used across connection-selection tests."""
35+
self.root = MagicMock()
36+
self.main_frame = MagicMock()
37+
self.dpi_scaling_factor = 1.0
38+
self.root.winfo_width.return_value = 1
39+
self.root.winfo_height.return_value = 1
40+
self.root.winfo_reqwidth.return_value = 520
41+
self.root.winfo_reqheight.return_value = 462
42+
self.root.winfo_pointerx.return_value = 0
43+
self.root.winfo_pointery.return_value = 0
44+
self.root.winfo_screenwidth.return_value = 1920
45+
self.root.winfo_screenheight.return_value = 1080
46+
47+
3248
# ---------------------------------------------------------------------------
3349
# Shared fixtures
3450
# ---------------------------------------------------------------------------
@@ -100,10 +116,6 @@ def connection_window(mock_fc: MagicMock): # noqa: ANN201 # yields; complex re
100116
"""
101117
mock_widgets = MagicMock()
102118

103-
def _mock_basewindow_init(self, root_tk=None) -> None: # noqa: ARG001, pylint: disable=unused-argument
104-
self.root = MagicMock()
105-
self.main_frame = MagicMock()
106-
107119
with (
108120
patch("tkinter.Tk"),
109121
patch("tkinter.Toplevel"),
@@ -1422,11 +1434,6 @@ def test_init_replaces_colon_with_newline_in_introduction_text(self, mock_fc: Ma
14221434
THEN: The introduction label text should have ':\\n' in place of ':'
14231435
AND: The raw colon should not appear on the same line as the following text
14241436
"""
1425-
1426-
def _mock_basewindow_init(self, root_tk=None) -> None: # noqa: ARG001 # pylint: disable=unused-argument
1427-
self.root = MagicMock()
1428-
self.main_frame = MagicMock()
1429-
14301437
mock_comport = MagicMock()
14311438
mock_comport.device = "COM1"
14321439
mock_fc.comport = mock_comport
@@ -1481,11 +1488,6 @@ def test_init_uses_plain_text_when_comport_set_and_no_colon(self, mock_fc: Magic
14811488
THEN: The introduction label text should match the result string exactly (no replacement)
14821489
AND: No newline should be inserted into the text
14831490
"""
1484-
1485-
def _mock_basewindow_init(self, root_tk=None) -> None: # noqa: ARG001 # pylint: disable=unused-argument
1486-
self.root = MagicMock()
1487-
self.main_frame = MagicMock()
1488-
14891491
mock_comport = MagicMock()
14901492
mock_comport.device = "COM1"
14911493
mock_fc.comport = mock_comport # Not None, so goes to elif/else

tests/test_frontend_tkinter_motor_test.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,7 @@ def fake_base_init(self) -> None:
744744
self.main_frame = MagicMock()
745745
self.put_image_in_label = MagicMock()
746746
self.calculate_scaled_image_size = lambda value: value
747+
self.dpi_scaling_factor = 1.0
747748

748749
mocker.patch("ardupilot_methodic_configurator.frontend_tkinter_motor_test.BaseWindow.__init__", fake_base_init)
749750
view_mock = MagicMock()
@@ -779,6 +780,7 @@ def fake_base_init(self) -> None:
779780
self.main_frame = MagicMock()
780781
self.put_image_in_label = MagicMock()
781782
self.calculate_scaled_image_size = lambda value: value
783+
self.dpi_scaling_factor = 1.0
782784

783785
mocker.patch("ardupilot_methodic_configurator.frontend_tkinter_motor_test.BaseWindow.__init__", fake_base_init)
784786
view_mock = MagicMock()

tests/test_frontend_tkinter_parameter_editor.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,14 @@ def __init__(self, *args: object, **kwargs: object) -> None: # pylint: disable=
182182
self.after = MagicMock()
183183
self.winfo_fpixels = MagicMock(return_value=96.0)
184184
self.winfo_reqheight = MagicMock(return_value=630)
185+
self.winfo_width = MagicMock(return_value=1)
186+
self.winfo_height = MagicMock(return_value=1)
187+
self.winfo_reqwidth = MagicMock(return_value=990)
188+
self.winfo_pointerx = MagicMock(return_value=0)
189+
self.winfo_pointery = MagicMock(return_value=0)
190+
self.winfo_screenwidth = MagicMock(return_value=1920)
191+
self.winfo_screenheight = MagicMock(return_value=1080)
192+
self.update = MagicMock()
185193

186194

187195
@pytest.fixture

tests/test_frontend_tkinter_project_creator.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ def configured_creator_window(mock_project_manager) -> VehicleProjectCreatorWind
4747
patch.object(BaseWindow, "_setup_application_icon"),
4848
patch.object(BaseWindow, "_setup_theme_and_styling"),
4949
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
50+
patch.object(BaseWindow, "center_window_on_screen"),
5051
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Label"),
5152
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.LabelFrame"),
5253
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Button"),
@@ -218,6 +219,7 @@ def test_window_creates_dynamic_settings_checkboxes(self, mock_project_manager)
218219
patch.object(BaseWindow, "_setup_application_icon"),
219220
patch.object(BaseWindow, "_setup_theme_and_styling"),
220221
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
222+
patch.object(BaseWindow, "center_window_on_screen"),
221223
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Label"),
222224
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.LabelFrame"),
223225
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Button"),
@@ -266,6 +268,7 @@ def test_window_adapts_size_based_on_settings_count(self, mock_project_manager)
266268
patch.object(BaseWindow, "_setup_application_icon"),
267269
patch.object(BaseWindow, "_setup_theme_and_styling"),
268270
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
271+
patch.object(BaseWindow, "center_window_on_screen"),
269272
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Label"),
270273
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.LabelFrame"),
271274
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Button"),
@@ -347,6 +350,7 @@ def test_user_workflow_from_startup_to_project_creation(self, mock_project_manag
347350
patch.object(BaseWindow, "_setup_application_icon"),
348351
patch.object(BaseWindow, "_setup_theme_and_styling"),
349352
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
353+
patch.object(BaseWindow, "center_window_on_screen"),
350354
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Label"),
351355
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.LabelFrame"),
352356
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Button"),
@@ -404,6 +408,7 @@ def test_user_workflow_with_flight_controller_connected(self, mock_project_manag
404408
patch.object(BaseWindow, "_setup_application_icon"),
405409
patch.object(BaseWindow, "_setup_theme_and_styling"),
406410
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
411+
patch.object(BaseWindow, "center_window_on_screen"),
407412
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Label"),
408413
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.LabelFrame"),
409414
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.ttk.Button"),

tests/test_frontend_tkinter_project_creator_integration.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def configured_creator_window(mock_project_manager: MagicMock) -> VehicleProject
8888
patch.object(BaseWindow, "_setup_application_icon"),
8989
patch.object(BaseWindow, "_setup_theme_and_styling"),
9090
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
91+
patch.object(BaseWindow, "center_window_on_screen"),
9192
patch("tkinter.ttk.Label"),
9293
patch("tkinter.ttk.LabelFrame"),
9394
patch("tkinter.ttk.Button"),
@@ -125,6 +126,7 @@ def configured_opener_window(mock_project_manager: MagicMock) -> VehicleProjectO
125126
patch.object(BaseWindow, "_setup_application_icon"),
126127
patch.object(BaseWindow, "_setup_theme_and_styling"),
127128
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
129+
patch.object(BaseWindow, "center_window_on_screen"),
128130
patch("tkinter.ttk.Label"),
129131
patch("tkinter.ttk.LabelFrame"),
130132
patch("tkinter.ttk.Button"),
@@ -246,11 +248,13 @@ def test_user_can_switch_between_creating_and_opening_existing_projects(
246248
patch.object(BaseWindow, "_setup_application_icon"),
247249
patch.object(BaseWindow, "_setup_theme_and_styling"),
248250
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
251+
patch.object(BaseWindow, "center_window_on_screen"),
249252
patch("tkinter.ttk.Label"),
250253
patch("tkinter.ttk.LabelFrame"),
251254
patch("tkinter.ttk.Button"),
252255
patch("tkinter.ttk.Frame"),
253256
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.VehicleDirectorySelectionWidgets"),
257+
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.tk.StringVar"),
254258
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.DirectorySelectionWidgets"),
255259
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.PathEntryWidget"),
256260
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.NewVehicleProjectSettings"),
@@ -307,11 +311,13 @@ def test_flight_controller_connection_affects_both_creator_and_opener_options(
307311
patch.object(BaseWindow, "_setup_application_icon"),
308312
patch.object(BaseWindow, "_setup_theme_and_styling"),
309313
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
314+
patch.object(BaseWindow, "center_window_on_screen"),
310315
patch("tkinter.ttk.Label"),
311316
patch("tkinter.ttk.LabelFrame"),
312317
patch("tkinter.ttk.Button"),
313318
patch("tkinter.ttk.Frame"),
314319
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.VehicleDirectorySelectionWidgets"),
320+
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.tk.StringVar"),
315321
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.DirectorySelectionWidgets"),
316322
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.PathEntryWidget"),
317323
patch("ardupilot_methodic_configurator.frontend_tkinter_project_creator.NewVehicleProjectSettings"),

tests/test_frontend_tkinter_project_opener.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ def configured_opener_window(mock_project_manager) -> VehicleProjectOpenerWindow
4848
patch.object(BaseWindow, "_setup_application_icon"),
4949
patch.object(BaseWindow, "_setup_theme_and_styling"),
5050
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
51+
patch.object(BaseWindow, "center_window_on_screen"),
5152
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.Label"),
5253
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.LabelFrame"),
5354
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.Button"),
@@ -286,6 +287,7 @@ def test_option3_button_is_disabled_when_no_last_directory_available(self, mock_
286287
patch.object(BaseWindow, "_setup_application_icon"),
287288
patch.object(BaseWindow, "_setup_theme_and_styling"),
288289
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
290+
patch.object(BaseWindow, "center_window_on_screen"),
289291
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.Label"),
290292
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.LabelFrame"),
291293
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.Button") as mock_button,
@@ -331,6 +333,7 @@ def test_option3_button_is_enabled_when_last_directory_is_available(self, mock_p
331333
patch.object(BaseWindow, "_setup_application_icon"),
332334
patch.object(BaseWindow, "_setup_theme_and_styling"),
333335
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
336+
patch.object(BaseWindow, "center_window_on_screen"),
334337
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.Label"),
335338
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.LabelFrame"),
336339
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.Button") as mock_button,
@@ -398,6 +401,7 @@ def test_user_workflow_from_startup_to_template_creation(
398401
patch.object(BaseWindow, "_setup_application_icon"),
399402
patch.object(BaseWindow, "_setup_theme_and_styling"),
400403
patch.object(BaseWindow, "_get_dpi_scaling_factor", return_value=1.0),
404+
patch.object(BaseWindow, "center_window_on_screen"),
401405
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.Label"),
402406
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.LabelFrame"),
403407
patch("ardupilot_methodic_configurator.frontend_tkinter_project_opener.ttk.Button"),

0 commit comments

Comments
 (0)