screen-locker/screen_locker/tests/conftest.py
Krzysztof kuhy Rudnicki aaca61a830 WIP: Enforce 500-line limit - split batch 1
Split 16+ files. 27 files still need splitting. See session notes.
2026-03-16 22:46:48 +01:00

114 lines
3.3 KiB
Python

"""Shared fixtures and helpers for screen_locker tests."""
from __future__ import annotations
from pathlib import Path
import tkinter as tk
from typing import TYPE_CHECKING, NamedTuple
from unittest.mock import MagicMock, patch
import pytest
from python_pkg.screen_locker.screen_lock import ScreenLocker
if TYPE_CHECKING:
from collections.abc import Generator
class RunningData(NamedTuple):
"""Running workout data for tests."""
distance: str
time_mins: str
pace: str
class StrengthData(NamedTuple):
"""Strength workout data for tests."""
exercises: str
sets: str
reps: str
weights: str
total_weight: str
@pytest.fixture
def mock_tk() -> Generator[MagicMock]:
"""Mock tkinter module for testing without display."""
with patch("python_pkg.screen_locker.screen_lock.tk") as mock:
# Set up Tk root mock
mock_root = MagicMock()
mock_root.winfo_screenwidth.return_value = 1920
mock_root.winfo_screenheight.return_value = 1080
mock.Tk.return_value = mock_root
# Set up Frame mock
mock_frame = MagicMock()
mock_frame.winfo_children.return_value = []
mock.Frame.return_value = mock_frame
# Set up TclError as actual exception class
mock.TclError = tk.TclError
yield mock
@pytest.fixture
def mock_sys_exit() -> Generator[MagicMock]:
"""Mock sys.exit to prevent test termination."""
with patch("python_pkg.screen_locker.screen_lock.sys.exit") as mock:
yield mock
@pytest.fixture
def _mock_sys_exit(mock_sys_exit: MagicMock) -> MagicMock:
"""Alias for mock_sys_exit when the return value is unused."""
return mock_sys_exit
@pytest.fixture
def temp_log_file(tmp_path: Path) -> Path:
"""Create a temporary log file path."""
return tmp_path / "workout_log.json"
def create_locker(
_mock_tk: MagicMock,
tmp_path: Path,
*,
demo_mode: bool = True,
has_logged: bool = False,
) -> ScreenLocker:
"""Create a ScreenLocker instance for testing."""
with (
patch.object(Path, "resolve", return_value=tmp_path),
patch.object(ScreenLocker, "has_logged_today", return_value=has_logged),
patch.object(ScreenLocker, "_start_phone_check"),
):
return ScreenLocker(demo_mode=demo_mode)
def setup_running_entries(locker: ScreenLocker, data: RunningData) -> None:
"""Set up mock running entry widgets."""
locker.distance_entry = MagicMock()
locker.distance_entry.get.return_value = data.distance
locker.time_entry = MagicMock()
locker.time_entry.get.return_value = data.time_mins
locker.pace_entry = MagicMock()
locker.pace_entry.get.return_value = data.pace
def setup_strength_entries(locker: ScreenLocker, data: StrengthData) -> None:
"""Set up mock strength entry widgets."""
locker.exercises_entry = MagicMock()
locker.exercises_entry.get.return_value = data.exercises
locker.sets_entry = MagicMock()
locker.sets_entry.get.return_value = data.sets
locker.reps_entry = MagicMock()
locker.reps_entry.get.return_value = data.reps
locker.weights_entry = MagicMock()
locker.weights_entry.get.return_value = data.weights
locker.total_weight_entry = MagicMock()
locker.total_weight_entry.get.return_value = data.total_weight