from unittest.mock import Mock, patch from bouquin.pomodoro_timer import PomodoroTimer, PomodoroManager def test_pomodoro_timer_init(qtbot, app, fresh_db): """Test PomodoroTimer initialization.""" task_text = "Write unit tests" timer = PomodoroTimer(task_text) qtbot.addWidget(timer) assert timer._task_text == task_text assert timer._elapsed_seconds == 0 assert timer._running is False assert timer.time_label.text() == "00:00:00" assert timer.stop_btn.isEnabled() is False def test_pomodoro_timer_start(qtbot, app): """Test starting the timer.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) timer._toggle_timer() assert timer._running is True assert timer.stop_btn.isEnabled() is True def test_pomodoro_timer_pause(qtbot, app): """Test pausing the timer.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) # Start the timer timer._toggle_timer() assert timer._running is True # Pause the timer timer._toggle_timer() assert timer._running is False def test_pomodoro_timer_resume(qtbot, app): """Test resuming the timer after pause.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) # Start, pause, then resume timer._toggle_timer() # Start timer._toggle_timer() # Pause timer._toggle_timer() # Resume assert timer._running is True def test_pomodoro_timer_tick(qtbot, app): """Test timer tick increments elapsed time.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) initial_time = timer._elapsed_seconds timer._tick() assert timer._elapsed_seconds == initial_time + 1 def test_pomodoro_timer_display_update(qtbot, app): """Test display updates with various elapsed times.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) # Test 0 seconds timer._elapsed_seconds = 0 timer._update_display() assert timer.time_label.text() == "00:00:00" # Test 65 seconds (1 min 5 sec) timer._elapsed_seconds = 65 timer._update_display() assert timer.time_label.text() == "00:01:05" # Test 3665 seconds (1 hour 1 min 5 sec) timer._elapsed_seconds = 3665 timer._update_display() assert timer.time_label.text() == "01:01:05" # Test 3600 seconds (1 hour exactly) timer._elapsed_seconds = 3600 timer._update_display() assert timer.time_label.text() == "01:00:00" def test_pomodoro_timer_stop_and_log_running(qtbot, app): """Test stopping the timer while it's running.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) # Start the timer timer._toggle_timer() timer._elapsed_seconds = 100 # Connect a mock to the signal signal_received = [] timer.timerStopped.connect(lambda s, t: signal_received.append((s, t))) timer._stop_and_log() assert timer._running is False assert len(signal_received) == 1 assert signal_received[0][0] == 100 # elapsed seconds assert signal_received[0][1] == "Test task" def test_pomodoro_timer_stop_and_log_paused(qtbot, app): """Test stopping the timer when it's paused.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) timer._elapsed_seconds = 50 signal_received = [] timer.timerStopped.connect(lambda s, t: signal_received.append((s, t))) timer._stop_and_log() assert len(signal_received) == 1 assert signal_received[0][0] == 50 def test_pomodoro_timer_multiple_ticks(qtbot, app): """Test multiple timer ticks.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) for i in range(10): timer._tick() assert timer._elapsed_seconds == 10 assert "00:00:10" in timer.time_label.text() def test_pomodoro_timer_modal_state(qtbot, app): """Test that timer is non-modal.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) assert timer.isModal() is False def test_pomodoro_timer_window_title(qtbot, app): """Test timer window title.""" timer = PomodoroTimer("Test task") qtbot.addWidget(timer) # Window title should contain some reference to timer/pomodoro assert len(timer.windowTitle()) > 0 def test_pomodoro_manager_init(app, fresh_db): """Test PomodoroManager initialization.""" parent = Mock() manager = PomodoroManager(fresh_db, parent) assert manager._db is fresh_db assert manager._parent is parent assert manager._active_timer is None def test_pomodoro_manager_start_timer(qtbot, app, fresh_db): """Test starting a timer through the manager.""" from PySide6.QtWidgets import QWidget parent = QWidget() qtbot.addWidget(parent) manager = PomodoroManager(fresh_db, parent) line_text = "Important task" date_iso = "2024-01-15" manager.start_timer_for_line(line_text, date_iso) assert manager._active_timer is not None assert manager._active_timer._task_text == line_text qtbot.addWidget(manager._active_timer) def test_pomodoro_manager_replace_active_timer(qtbot, app, fresh_db): """Test that starting a new timer closes the previous one.""" from PySide6.QtWidgets import QWidget parent = QWidget() qtbot.addWidget(parent) manager = PomodoroManager(fresh_db, parent) # Start first timer manager.start_timer_for_line("Task 1", "2024-01-15") first_timer = manager._active_timer qtbot.addWidget(first_timer) first_timer.show() # Start second timer manager.start_timer_for_line("Task 2", "2024-01-16") second_timer = manager._active_timer qtbot.addWidget(second_timer) assert first_timer is not second_timer assert second_timer._task_text == "Task 2" def test_pomodoro_manager_on_timer_stopped_minimum_hours( qtbot, app, fresh_db, monkeypatch ): """Test that timer stopped with very short time logs minimum hours.""" parent = Mock() manager = PomodoroManager(fresh_db, parent) # Mock TimeLogDialog to avoid actually showing it mock_dialog = Mock() mock_dialog.hours_spin = Mock() mock_dialog.note = Mock() mock_dialog.exec = Mock() with patch("bouquin.pomodoro_timer.TimeLogDialog", return_value=mock_dialog): manager._on_timer_stopped(10, "Quick task", "2024-01-15") # Should set minimum of 0.25 hours mock_dialog.hours_spin.setValue.assert_called_once() hours_set = mock_dialog.hours_spin.setValue.call_args[0][0] assert hours_set >= 0.25 def test_pomodoro_manager_on_timer_stopped_rounding(qtbot, app, fresh_db, monkeypatch): """Test that elapsed time is properly rounded to decimal hours.""" parent = Mock() manager = PomodoroManager(fresh_db, parent) mock_dialog = Mock() mock_dialog.hours_spin = Mock() mock_dialog.note = Mock() mock_dialog.exec = Mock() with patch("bouquin.pomodoro_timer.TimeLogDialog", return_value=mock_dialog): # Test with 1800 seconds (30 minutes) manager._on_timer_stopped(1800, "Task", "2024-01-15") mock_dialog.hours_spin.setValue.assert_called_once() hours_set = mock_dialog.hours_spin.setValue.call_args[0][0] # Should round up and be a multiple of 0.25 assert hours_set > 0 assert hours_set * 4 == int(hours_set * 4) # Multiple of 0.25 def test_pomodoro_manager_on_timer_stopped_prefills_note( qtbot, app, fresh_db, monkeypatch ): """Test that timer stopped pre-fills the note in time log dialog.""" parent = Mock() manager = PomodoroManager(fresh_db, parent) mock_dialog = Mock() mock_dialog.hours_spin = Mock() mock_dialog.note = Mock() mock_dialog.exec = Mock() task_text = "Write documentation" with patch("bouquin.pomodoro_timer.TimeLogDialog", return_value=mock_dialog): manager._on_timer_stopped(3600, task_text, "2024-01-15") mock_dialog.note.setText.assert_called_once_with(task_text) def test_pomodoro_manager_timer_stopped_signal_connection( qtbot, app, fresh_db, monkeypatch ): """Test that timer stopped signal is properly connected.""" from PySide6.QtWidgets import QWidget parent = QWidget() qtbot.addWidget(parent) manager = PomodoroManager(fresh_db, parent) # Mock TimeLogDialog mock_dialog = Mock() mock_dialog.hours_spin = Mock() mock_dialog.note = Mock() mock_dialog.exec = Mock() with patch("bouquin.pomodoro_timer.TimeLogDialog", return_value=mock_dialog): manager.start_timer_for_line("Task", "2024-01-15") timer = manager._active_timer qtbot.addWidget(timer) # Simulate timer stopped timer._elapsed_seconds = 1000 timer._stop_and_log() # TimeLogDialog should have been created assert mock_dialog.exec.called def test_pomodoro_timer_accepts_parent(qtbot, app): """Test that timer accepts a parent widget.""" from PySide6.QtWidgets import QWidget parent = QWidget() qtbot.addWidget(parent) timer = PomodoroTimer("Task", parent) qtbot.addWidget(timer) assert timer.parent() is parent def test_pomodoro_manager_no_active_timer_initially(app, fresh_db): """Test that manager starts with no active timer.""" parent = Mock() manager = PomodoroManager(fresh_db, parent) assert manager._active_timer is None def test_pomodoro_timer_start_stop_cycle(qtbot, app): """Test a complete start-stop cycle.""" timer = PomodoroTimer("Complete cycle") qtbot.addWidget(timer) signal_received = [] timer.timerStopped.connect(lambda s, t: signal_received.append((s, t))) # Start timer._toggle_timer() assert timer._running is True # Simulate some ticks for _ in range(5): timer._tick() # Stop timer._stop_and_log() assert timer._running is False assert len(signal_received) == 1 assert signal_received[0][0] == 5 def test_pomodoro_timer_long_elapsed_time(qtbot, app): """Test display with very long elapsed time.""" timer = PomodoroTimer("Long task") qtbot.addWidget(timer) # Set to 2 hours, 34 minutes, 56 seconds timer._elapsed_seconds = 2 * 3600 + 34 * 60 + 56 timer._update_display() assert timer.time_label.text() == "02:34:56"