diff --git a/spyder/widgets/findreplace.py b/spyder/widgets/findreplace.py index 587eb0a321e..b700afadb80 100644 --- a/spyder/widgets/findreplace.py +++ b/spyder/widgets/findreplace.py @@ -113,7 +113,8 @@ def __init__(self, parent, enable_replace=False): replace_with = QLabel(_("Replace with:")) self.replace_text = PatternComboBox(self, adjust_to_minimum=False, tip=_('Replace string')) - + self.replace_text.valid.connect( + lambda _: self.replace_find(focus_replace_text=True)) self.replace_button = create_toolbutton(self, text=_('Replace/find'), icon=ima.icon('DialogApplyButton'), @@ -344,7 +345,7 @@ def find(self, changed=True, forward=True, return found @Slot() - def replace_find(self): + def replace_find(self, focus_replace_text=False): """Replace and find""" if (self.editor is not None): replace_text = to_text_string(self.replace_text.currentText()) @@ -419,3 +420,5 @@ def replace_find(self): self.all_check.setCheckState(Qt.Unchecked) if cursor is not None: cursor.endEditBlock() + if focus_replace_text: + self.replace_text.setFocus() diff --git a/spyder/widgets/tests/__init__.py b/spyder/widgets/tests/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/spyder/widgets/tests/test_codeeditor_upper_lower.py b/spyder/widgets/tests/test_codeeditor.py similarity index 100% rename from spyder/widgets/tests/test_codeeditor_upper_lower.py rename to spyder/widgets/tests/test_codeeditor.py diff --git a/spyder/widgets/tests/test_editor.py b/spyder/widgets/tests/test_editor.py index 53cda52f731..99b60b88897 100644 --- a/spyder/widgets/tests/test_editor.py +++ b/spyder/widgets/tests/test_editor.py @@ -8,72 +8,136 @@ Tests for editor.py """ +# Standard library imports +try: + from unittest.mock import Mock +except ImportError: + from mock import Mock # Python 2 + # Third party imports import pytest +from qtpy.QtCore import Qt # Local imports from spyder.utils.fixtures import setup_editor +from spyder.widgets.editor import EditorStack +from spyder.widgets.findreplace import FindReplace + +# Qt Test Fixtures +#-------------------------------- +@pytest.fixture +def base_editor_bot(qtbot): + editor_stack = EditorStack(None, []) + editor_stack.set_introspector(Mock()) + editor_stack.set_find_widget(Mock()) + editor_stack.set_io_actions(Mock(), Mock(), Mock(), Mock()) + return editor_stack, qtbot + +@pytest.fixture +def editor_bot(base_editor_bot): + """ + Set up EditorStack with CodeEditor containing some Python code. + The cursor is at the empty line below the code. + Returns tuple with EditorStack and CodeEditor. + """ + editor_stack, qtbot = base_editor_bot + text = ('a = 1\n' + 'print(a)\n' + '\n' + 'x = 2') # a newline is added at end + finfo = editor_stack.new('foo.py', 'utf-8', text) + qtbot.addWidget(editor_stack) + return editor_stack, finfo.editor, qtbot + +@pytest.fixture +def editor_find_replace_bot(base_editor_bot): + editor_stack, qtbot = base_editor_bot + text = ('spam bacon\n' + 'spam sausage\n' + 'spam egg') + finfo = editor_stack.new('spam.py', 'utf-8', text) + find_replace = FindReplace(None, enable_replace=True) + editor_stack.set_find_widget(find_replace) + find_replace.set_editor(finfo.editor) + qtbot.addWidget(editor_stack) + return editor_stack, finfo.editor, find_replace, qtbot -def test_run_top_line(qtbot): - editorStack, editor = setup_editor(qtbot) +# Tests +#------------------------------- +def test_run_top_line(editor_bot): + editor_stack, editor, qtbot = editor_bot editor.go_to_line(1) # line number is one based editor.move_cursor(3) - with qtbot.waitSignal(editorStack.exec_in_extconsole) as blocker: - editorStack.run_selection() + with qtbot.waitSignal(editor_stack.exec_in_extconsole) as blocker: + editor_stack.run_selection() assert blocker.signal_triggered assert blocker.args[0] == 'a = 1' # check cursor moves to start of next line; note line number is zero based - assert editor.get_cursor_line_column() == (1, 0) + assert editor.get_cursor_line_column() == (1, 0) -def test_run_last_nonempty_line(qtbot): - editorStack, editor = setup_editor(qtbot) +def test_run_last_nonempty_line(editor_bot): + editor_stack, editor, qtbot = editor_bot editor.go_to_line(4) - with qtbot.waitSignal(editorStack.exec_in_extconsole) as blocker: - editorStack.run_selection() + with qtbot.waitSignal(editor_stack.exec_in_extconsole) as blocker: + editor_stack.run_selection() assert blocker.signal_triggered assert blocker.args[0] == 'x = 2' assert editor.get_cursor_line_column() == (4, 0) # check cursor moves down -def test_run_empty_line_in_middle(qtbot): - editorStack, editor = setup_editor(qtbot) +def test_run_empty_line_in_middle(editor_bot): + editor_stack, editor, qtbot = editor_bot editor.go_to_line(3) - with qtbot.assertNotEmitted(editorStack.exec_in_extconsole): - editorStack.run_selection() + with qtbot.assertNotEmitted(editor_stack.exec_in_extconsole): + editor_stack.run_selection() assert editor.get_cursor_line_column() == (3, 0) # check cursor moves down +def test_run_last_line_when_empty(editor_bot): + editor_stack, editor, qtbot = editor_bot + with qtbot.assertNotEmitted(editor_stack.exec_in_extconsole): + editor_stack.run_selection() + # check cursor doesn't move + assert editor.get_cursor_line_column() == (4, 0) -def test_run_last_line_when_empty(qtbot): - editorStack, editor = setup_editor(qtbot) - with qtbot.assertNotEmitted(editorStack.exec_in_extconsole): - editorStack.run_selection() - assert editor.get_cursor_line_column() == (4, 0) # check cursor doesn't move - -def test_run_last_line_when_nonempty(qtbot): - editorStack, editor = setup_editor(qtbot) +def test_run_last_line_when_nonempty(editor_bot): + editor_stack, editor, qtbot = editor_bot editor.stdkey_backspace() # delete empty line at end old_text = editor.toPlainText() - with qtbot.waitSignal(editorStack.exec_in_extconsole) as blocker: - editorStack.run_selection() + with qtbot.waitSignal(editor_stack.exec_in_extconsole) as blocker: + editor_stack.run_selection() assert blocker.signal_triggered assert blocker.args[0] == 'x = 2' expected_new_text = old_text + editor.get_line_separator() - assert editor.toPlainText() == expected_new_text # check blank line got added + # check blank line got added + assert editor.toPlainText() == expected_new_text assert editor.get_cursor_line_column() == (4, 0) # check cursor moves down def test_find_replace_case_sensitive(qtbot): - editorStack, editor = setup_editor(qtbot) - editorStack.find_widget.case_button.setChecked(True) + editor_stack, editor = setup_editor(qtbot) + editor_stack.find_widget.case_button.setChecked(True) text = ' test \nTEST \nTest \ntesT ' editor.set_text(text) - editorStack.find_widget.search_text.add_text('test') - editorStack.find_widget.replace_text.add_text('pass') - editorStack.find_widget.replace_find() - editorStack.find_widget.replace_find() - editorStack.find_widget.replace_find() - editorStack.find_widget.replace_find() + editor_stack.find_widget.search_text.add_text('test') + editor_stack.find_widget.replace_text.add_text('pass') + editor_stack.find_widget.replace_find() + editor_stack.find_widget.replace_find() + editor_stack.find_widget.replace_find() + editor_stack.find_widget.replace_find() editor_text = editor.toPlainText() assert editor_text == ' pass \nTEST \nTest \ntesT ' +def test_replace_current_selected_line(editor_find_replace_bot): + editor_stack, editor, finder, qtbot = editor_find_replace_bot + expected_new_text = ('ham bacon\n' + 'spam sausage\n' + 'spam egg') + old_text = editor.toPlainText() + finder.show() + finder.show_replace() + qtbot.keyClicks(finder.search_text, 'spam') + qtbot.keyClicks(finder.replace_text, 'ham') + qtbot.keyPress(finder.replace_text, Qt.Key_Return) + assert editor.toPlainText()[0:-1] == expected_new_text + if __name__ == "__main__": pytest.main()