@@ -150,3 +150,47 @@ def test_raises_if_loader_returns_none(self, monkeypatch, qapp):
150150 )
151151 with pytest .raises (RuntimeError , match = "QUiLoader failed" ):
152152 MainWindow (MagicMock ())
153+
154+
155+ class TestSaveHandler :
156+ def test_warning_shown_when_filepath_empty (self , window , monkeypatch ):
157+ """Empty fileNameEdit triggers QMessageBox.warning — lines 240-244."""
158+ warnings : list = []
159+ monkeypatch .setattr (
160+ "src.views.main_window.QMessageBox.warning" ,
161+ lambda * a : warnings .append (a ),
162+ )
163+ window ._file_name_edit .setText ("" )
164+ window ._on_save_clicked ()
165+ assert len (warnings ) == 1
166+
167+ def test_save_delegates_to_viewmodel (self , window , qtbot ):
168+ """Non-empty filepath triggers ViewModel.save_file — line 246."""
169+ window ._file_name_edit .setText ("/tmp/out.txt" )
170+ window ._plain_text_edit .setPlainText ("some content" )
171+ with qtbot .waitSignal (window ._viewmodel .file_saved , timeout = 1000 ):
172+ window ._on_save_clicked ()
173+
174+
175+ class TestCleanHandler :
176+ def test_checking_trim_checkbox_triggers_cleaning (
177+ self , window , mock_text_svc , qtbot
178+ ):
179+ """stateChanged → _on_clean_requested — lines 253-258."""
180+ window ._viewmodel .load_file ("/tmp/test.txt" )
181+ qtbot .wait (10 )
182+ with qtbot .waitSignal (window ._viewmodel .content_updated , timeout = 1000 ):
183+ window ._trim_cb .setChecked (True )
184+
185+ def test_clean_options_reflect_checkbox_states (
186+ self , window , mock_text_svc , qtbot
187+ ):
188+ window ._viewmodel .load_file ("/tmp/test.txt" )
189+ qtbot .wait (10 )
190+ window ._clean_cb .setChecked (False )
191+ window ._remove_tabs_cb .setChecked (True )
192+ window ._on_clean_requested ()
193+ call_args = mock_text_svc .apply_options .call_args
194+ opts = call_args [0 ][1 ]
195+ assert opts .remove_tabs is True
196+ assert opts .clean_whitespace is False
0 commit comments