""" These are almost end-to-end tests. They create a Prompt, feed it with some input and check the result. """ from __future__ import annotations from functools import partial import pytest from prompt_toolkit.clipboard import ClipboardData, InMemoryClipboard from prompt_toolkit.enums import EditingMode from prompt_toolkit.filters import ViInsertMode from prompt_toolkit.history import InMemoryHistory from prompt_toolkit.input.defaults import create_pipe_input from prompt_toolkit.input.vt100_parser import ANSI_SEQUENCES from prompt_toolkit.key_binding.bindings.named_commands import prefix_meta from prompt_toolkit.key_binding.key_bindings import KeyBindings from prompt_toolkit.output import DummyOutput from prompt_toolkit.shortcuts import PromptSession def _history(): h = InMemoryHistory() h.append_string("line1 first input") h.append_string("line2 second input") h.append_string("line3 third input") return h def _feed_cli_with_input( text, editing_mode=EditingMode.EMACS, clipboard=None, history=None, multiline=False, check_line_ending=True, key_bindings=None, ): """ Create a Prompt, feed it with the given user input and return the CLI object. This returns a (result, Application) tuple. """ # If the given text doesn't end with a newline, the interface won't finish. if check_line_ending: assert text.endswith("\r") with create_pipe_input() as inp: inp.send_text(text) session = PromptSession( input=inp, output=DummyOutput(), editing_mode=editing_mode, history=history, multiline=multiline, clipboard=clipboard, key_bindings=key_bindings, ) _ = session.prompt() return session.default_buffer.document, session.app def test_simple_text_input(): # Simple text input, followed by enter. result, cli = _feed_cli_with_input("hello\r") assert result.text == "hello" assert cli.current_buffer.text == "hello" def test_emacs_cursor_movements(): """ Test cursor movements with Emacs key bindings. """ # ControlA (beginning-of-line) result, cli = _feed_cli_with_input("hello\x01X\r") assert result.text == "Xhello" # ControlE (end-of-line) result, cli = _feed_cli_with_input("hello\x01X\x05Y\r") assert result.text == "XhelloY" # ControlH or \b result, cli = _feed_cli_with_input("hello\x08X\r") assert result.text == "hellX" # Delete. (Left, left, delete) result, cli = _feed_cli_with_input("hello\x1b[D\x1b[D\x1b[3~\r") assert result.text == "helo" # Left. result, cli = _feed_cli_with_input("hello\x1b[DX\r") assert result.text == "hellXo" # ControlA, right result, cli = _feed_cli_with_input("hello\x01\x1b[CX\r") assert result.text == "hXello" # ControlB (backward-char) result, cli = _feed_cli_with_input("hello\x02X\r") assert result.text == "hellXo" # ControlF (forward-char) result, cli = _feed_cli_with_input("hello\x01\x06X\r") assert result.text == "hXello" # ControlD: delete after cursor. result, cli = _feed_cli_with_input("hello\x01\x04\r") assert result.text == "ello" # ControlD at the end of the input ssshould not do anything. result, cli = _feed_cli_with_input("hello\x04\r") assert result.text == "hello" # Left, Left, ControlK (kill-line) result, cli = _feed_cli_with_input("hello\x1b[D\x1b[D\x0b\r") assert result.text == "hel" # Left, Left Esc- ControlK (kill-line, but negative) result, cli = _feed_cli_with_input("hello\x1b[D\x1b[D\x1b-\x0b\r") assert result.text == "lo" # ControlL: should not influence the result. result, cli = _feed_cli_with_input("hello\x0c\r") assert result.text == "hello" # ControlRight (forward-word) result, cli = _feed_cli_with_input("hello world\x01X\x1b[1;5CY\r") assert result.text == "XhelloY world" # ContrlolLeft (backward-word) result, cli = _feed_cli_with_input("hello world\x1b[1;5DY\r") assert result.text == "hello Yworld" # -f with argument. (forward-word) result, cli = _feed_cli_with_input("hello world abc def\x01\x1b3\x1bfX\r") assert result.text == "hello world abcX def" # -f with negative argument. (forward-word) result, cli = _feed_cli_with_input("hello world abc def\x1b-\x1b3\x1bfX\r") assert result.text == "hello Xworld abc def" # -b with argument. (backward-word) result, cli = _feed_cli_with_input("hello world abc def\x1b3\x1bbX\r") assert result.text == "hello Xworld abc def" # -b with negative argument. (backward-word) result, cli = _feed_cli_with_input("hello world abc def\x01\x1b-\x1b3\x1bbX\r") assert result.text == "hello world abc Xdef" # ControlW (kill-word / unix-word-rubout) result, cli = _feed_cli_with_input("hello world\x17\r") assert result.text == "hello " assert cli.clipboard.get_data().text == "world" result, cli = _feed_cli_with_input("test hello world\x1b2\x17\r") assert result.text == "test " # Escape Backspace (unix-word-rubout) result, cli = _feed_cli_with_input("hello world\x1b\x7f\r") assert result.text == "hello " assert cli.clipboard.get_data().text == "world" result, cli = _feed_cli_with_input("hello world\x1b\x08\r") assert result.text == "hello " assert cli.clipboard.get_data().text == "world" # Backspace (backward-delete-char) result, cli = _feed_cli_with_input("hello world\x7f\r") assert result.text == "hello worl" assert result.cursor_position == len("hello worl") result, cli = _feed_cli_with_input("hello world\x08\r") assert result.text == "hello worl" assert result.cursor_position == len("hello worl") # Delete (delete-char) result, cli = _feed_cli_with_input("hello world\x01\x1b[3~\r") assert result.text == "ello world" assert result.cursor_position == 0 # Escape-\\ (delete-horizontal-space) result, cli = _feed_cli_with_input("hello world\x1b8\x02\x1b\\\r") assert result.text == "helloworld" assert result.cursor_position == len("hello") def test_emacs_kill_multiple_words_and_paste(): # Using control-w twice should place both words on the clipboard. result, cli = _feed_cli_with_input( "hello world test\x17\x17--\x19\x19\r" # Twice c-w. Twice c-y. ) assert result.text == "hello --world testworld test" assert cli.clipboard.get_data().text == "world test" # Using alt-d twice should place both words on the clipboard. result, cli = _feed_cli_with_input( "hello world test" "\x1bb\x1bb" # Twice left. "\x1bd\x1bd" # Twice kill-word. "abc" "\x19" # Paste. "\r" ) assert result.text == "hello abcworld test" assert cli.clipboard.get_data().text == "world test" def test_interrupts(): # ControlC: raise KeyboardInterrupt. with pytest.raises(KeyboardInterrupt): result, cli = _feed_cli_with_input("hello\x03\r") with pytest.raises(KeyboardInterrupt): result, cli = _feed_cli_with_input("hello\x03\r") # ControlD without any input: raises EOFError. with pytest.raises(EOFError): result, cli = _feed_cli_with_input("\x04\r") def test_emacs_yank(): # ControlY (yank) c = InMemoryClipboard(ClipboardData("XYZ")) result, cli = _feed_cli_with_input("hello\x02\x19\r", clipboard=c) assert result.text == "hellXYZo" assert result.cursor_position == len("hellXYZ") def test_quoted_insert(): # ControlQ - ControlB (quoted-insert) result, cli = _feed_cli_with_input("hello\x11\x02\r") assert result.text == "hello\x02" def test_transformations(): # Meta-c (capitalize-word) result, cli = _feed_cli_with_input("hello world\01\x1bc\r") assert result.text == "Hello world" assert result.cursor_position == len("Hello") # Meta-u (uppercase-word) result, cli = _feed_cli_with_input("hello world\01\x1bu\r") assert result.text == "HELLO world" assert result.cursor_position == len("Hello") # Meta-u (downcase-word) result, cli = _feed_cli_with_input("HELLO WORLD\01\x1bl\r") assert result.text == "hello WORLD" assert result.cursor_position == len("Hello") # ControlT (transpose-chars) result, cli = _feed_cli_with_input("hello\x14\r") assert result.text == "helol" assert result.cursor_position == len("hello") # Left, Left, Control-T (transpose-chars) result, cli = _feed_cli_with_input("abcde\x1b[D\x1b[D\x14\r") assert result.text == "abdce" assert result.cursor_position == len("abcd") def test_emacs_other_bindings(): # Transpose characters. result, cli = _feed_cli_with_input("abcde\x14X\r") # Ctrl-T assert result.text == "abcedX" # Left, Left, Transpose. (This is slightly different.) result, cli = _feed_cli_with_input("abcde\x1b[D\x1b[D\x14X\r") assert result.text == "abdcXe" # Clear before cursor. result, cli = _feed_cli_with_input("hello\x1b[D\x1b[D\x15X\r") assert result.text == "Xlo" # unix-word-rubout: delete word before the cursor. # (ControlW). result, cli = _feed_cli_with_input("hello world test\x17X\r") assert result.text == "hello world X" result, cli = _feed_cli_with_input("hello world /some/very/long/path\x17X\r") assert result.text == "hello world X" # (with argument.) result, cli = _feed_cli_with_input("hello world test\x1b2\x17X\r") assert result.text == "hello X" result, cli = _feed_cli_with_input("hello world /some/very/long/path\x1b2\x17X\r") assert result.text == "hello X" # backward-kill-word: delete word before the cursor. # (Esc-ControlH). result, cli = _feed_cli_with_input("hello world /some/very/long/path\x1b\x08X\r") assert result.text == "hello world /some/very/long/X" # (with arguments.) result, cli = _feed_cli_with_input( "hello world /some/very/long/path\x1b3\x1b\x08X\r" ) assert result.text == "hello world /some/very/X" def test_controlx_controlx(): # At the end: go to the start of the line. result, cli = _feed_cli_with_input("hello world\x18\x18X\r") assert result.text == "Xhello world" assert result.cursor_position == 1 # At the start: go to the end of the line. result, cli = _feed_cli_with_input("hello world\x01\x18\x18X\r") assert result.text == "hello worldX" # Left, Left Control-X Control-X: go to the end of the line. result, cli = _feed_cli_with_input("hello world\x1b[D\x1b[D\x18\x18X\r") assert result.text == "hello worldX" def test_emacs_history_bindings(): # Adding a new item to the history. history = _history() result, cli = _feed_cli_with_input("new input\r", history=history) assert result.text == "new input" history.get_strings()[-1] == "new input" # Go up in history, and accept the last item. result, cli = _feed_cli_with_input("hello\x1b[A\r", history=history) assert result.text == "new input" # Esc< (beginning-of-history) result, cli = _feed_cli_with_input("hello\x1b<\r", history=history) assert result.text == "line1 first input" # Esc> (end-of-history) result, cli = _feed_cli_with_input( "another item\x1b[A\x1b[a\x1b>\r", history=history ) assert result.text == "another item" # ControlUp (previous-history) result, cli = _feed_cli_with_input("\x1b[1;5A\r", history=history) assert result.text == "another item" # Esc< ControlDown (beginning-of-history, next-history) result, cli = _feed_cli_with_input("\x1b<\x1b[1;5B\r", history=history) assert result.text == "line2 second input" def test_emacs_reverse_search(): history = _history() # ControlR (reverse-search-history) result, cli = _feed_cli_with_input("\x12input\r\r", history=history) assert result.text == "line3 third input" # Hitting ControlR twice. result, cli = _feed_cli_with_input("\x12input\x12\r\r", history=history) assert result.text == "line2 second input" def test_emacs_arguments(): """ Test various combinations of arguments in Emacs mode. """ # esc 4 result, cli = _feed_cli_with_input("\x1b4x\r") assert result.text == "xxxx" # esc 4 4 result, cli = _feed_cli_with_input("\x1b44x\r") assert result.text == "x" * 44 # esc 4 esc 4 result, cli = _feed_cli_with_input("\x1b4\x1b4x\r") assert result.text == "x" * 44 # esc - right (-1 position to the right, equals 1 to the left.) result, cli = _feed_cli_with_input("aaaa\x1b-\x1b[Cbbbb\r") assert result.text == "aaabbbba" # esc - 3 right result, cli = _feed_cli_with_input("aaaa\x1b-3\x1b[Cbbbb\r") assert result.text == "abbbbaaa" # esc - - - 3 right result, cli = _feed_cli_with_input("aaaa\x1b---3\x1b[Cbbbb\r") assert result.text == "abbbbaaa" def test_emacs_arguments_for_all_commands(): """ Test all Emacs commands with Meta-[0-9] arguments (both positive and negative). No one should crash. """ for key in ANSI_SEQUENCES: # Ignore BracketedPaste. This would hang forever, because it waits for # the end sequence. if key != "\x1b[200~": try: # Note: we add an 'X' after the key, because Ctrl-Q (quoted-insert) # expects something to follow. We add an additional \r, because # Ctrl-R and Ctrl-S (reverse-search) expect that. result, cli = _feed_cli_with_input("hello\x1b4" + key + "X\r\r") result, cli = _feed_cli_with_input("hello\x1b-" + key + "X\r\r") except KeyboardInterrupt: # This exception should only be raised for Ctrl-C assert key == "\x03" def test_emacs_kill_ring(): operations = ( # abc ControlA ControlK "abc\x01\x0b" # def ControlA ControlK "def\x01\x0b" # ghi ControlA ControlK "ghi\x01\x0b" # ControlY (yank) "\x19" ) result, cli = _feed_cli_with_input(operations + "\r") assert result.text == "ghi" result, cli = _feed_cli_with_input(operations + "\x1by\r") assert result.text == "def" result, cli = _feed_cli_with_input(operations + "\x1by\x1by\r") assert result.text == "abc" result, cli = _feed_cli_with_input(operations + "\x1by\x1by\x1by\r") assert result.text == "ghi" def test_emacs_selection(): # Copy/paste empty selection should not do anything. operations = ( "hello" # Twice left. "\x1b[D\x1b[D" # Control-Space "\x00" # ControlW (cut) "\x17" # ControlY twice. (paste twice) "\x19\x19\r" ) result, cli = _feed_cli_with_input(operations) assert result.text == "hello" # Copy/paste one character. operations = ( "hello" # Twice left. "\x1b[D\x1b[D" # Control-Space "\x00" # Right. "\x1b[C" # ControlW (cut) "\x17" # ControlA (Home). "\x01" # ControlY (paste) "\x19\r" ) result, cli = _feed_cli_with_input(operations) assert result.text == "lhelo" def test_emacs_insert_comment(): # Test insert-comment (M-#) binding. result, cli = _feed_cli_with_input("hello\x1b#", check_line_ending=False) assert result.text == "#hello" result, cli = _feed_cli_with_input( "hello\rworld\x1b#", check_line_ending=False, multiline=True ) assert result.text == "#hello\n#world" def test_emacs_record_macro(): operations = ( " " "\x18(" # Start recording macro. C-X( "hello" "\x18)" # Stop recording macro. " " "\x18e" # Execute macro. "\x18e" # Execute macro. "\r" ) result, cli = _feed_cli_with_input(operations) assert result.text == " hello hellohello" def test_emacs_nested_macro(): "Test calling the macro within a macro." # Calling a macro within a macro should take the previous recording (if one # exists), not the one that is in progress. operations = ( "\x18(" # Start recording macro. C-X( "hello" "\x18e" # Execute macro. "\x18)" # Stop recording macro. "\x18e" # Execute macro. "\r" ) result, cli = _feed_cli_with_input(operations) assert result.text == "hellohello" operations = ( "\x18(" # Start recording macro. C-X( "hello" "\x18)" # Stop recording macro. "\x18(" # Start recording macro. C-X( "\x18e" # Execute macro. "world" "\x18)" # Stop recording macro. "\x01\x0b" # Delete all (c-a c-k). "\x18e" # Execute macro. "\r" ) result, cli = _feed_cli_with_input(operations) assert result.text == "helloworld" def test_prefix_meta(): # Test the prefix-meta command. b = KeyBindings() b.add("j", "j", filter=ViInsertMode())(prefix_meta) result, cli = _feed_cli_with_input( "hellojjIX\r", key_bindings=b, editing_mode=EditingMode.VI ) assert result.text == "Xhello" def test_bracketed_paste(): result, cli = _feed_cli_with_input("\x1b[200~hello world\x1b[201~\r") assert result.text == "hello world" result, cli = _feed_cli_with_input("\x1b[200~hello\rworld\x1b[201~\x1b\r") assert result.text == "hello\nworld" # With \r\n endings. result, cli = _feed_cli_with_input("\x1b[200~hello\r\nworld\x1b[201~\x1b\r") assert result.text == "hello\nworld" # With \n endings. result, cli = _feed_cli_with_input("\x1b[200~hello\nworld\x1b[201~\x1b\r") assert result.text == "hello\nworld" def test_vi_cursor_movements(): """ Test cursor movements with Vi key bindings. """ feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) result, cli = feed("\x1b\r") assert result.text == "" assert cli.editing_mode == EditingMode.VI # Esc h a X result, cli = feed("hello\x1bhaX\r") assert result.text == "hellXo" # Esc I X result, cli = feed("hello\x1bIX\r") assert result.text == "Xhello" # Esc I X result, cli = feed("hello\x1bIX\r") assert result.text == "Xhello" # Esc 2hiX result, cli = feed("hello\x1b2hiX\r") assert result.text == "heXllo" # Esc 2h2liX result, cli = feed("hello\x1b2h2liX\r") assert result.text == "hellXo" # Esc \b\b result, cli = feed("hello\b\b\r") assert result.text == "hel" # Esc \b\b result, cli = feed("hello\b\b\r") assert result.text == "hel" # Esc 2h D result, cli = feed("hello\x1b2hD\r") assert result.text == "he" # Esc 2h rX \r result, cli = feed("hello\x1b2hrX\r") assert result.text == "heXlo" def test_vi_operators(): feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) # Esc g~0 result, cli = feed("hello\x1bg~0\r") assert result.text == "HELLo" # Esc gU0 result, cli = feed("hello\x1bgU0\r") assert result.text == "HELLo" # Esc d0 result, cli = feed("hello\x1bd0\r") assert result.text == "o" def test_vi_text_objects(): feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) # Esc gUgg result, cli = feed("hello\x1bgUgg\r") assert result.text == "HELLO" # Esc gUU result, cli = feed("hello\x1bgUU\r") assert result.text == "HELLO" # Esc di( result, cli = feed("before(inside)after\x1b8hdi(\r") assert result.text == "before()after" # Esc di[ result, cli = feed("before[inside]after\x1b8hdi[\r") assert result.text == "before[]after" # Esc da( result, cli = feed("before(inside)after\x1b8hda(\r") assert result.text == "beforeafter" def test_vi_digraphs(): feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) # C-K o/ result, cli = feed("hello\x0bo/\r") assert result.text == "helloø" # C-K /o (reversed input.) result, cli = feed("hello\x0b/o\r") assert result.text == "helloø" # C-K e: result, cli = feed("hello\x0be:\r") assert result.text == "helloë" # C-K xxy (Unknown digraph.) result, cli = feed("hello\x0bxxy\r") assert result.text == "helloy" def test_vi_block_editing(): "Test Vi Control-V style block insertion." feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI, multiline=True) operations = ( # Six lines of text. "-line1\r-line2\r-line3\r-line4\r-line5\r-line6" # Go to the second character of the second line. "\x1bkkkkkkkj0l" # Enter Visual block mode. "\x16" # Go down two more lines. "jj" # Go 3 characters to the right. "lll" # Go to insert mode. "insert" # (Will be replaced.) # Insert stars. "***" # Escape again. "\x1b\r" ) # Control-I result, cli = feed(operations.replace("insert", "I")) assert result.text == "-line1\n-***line2\n-***line3\n-***line4\n-line5\n-line6" # Control-A result, cli = feed(operations.replace("insert", "A")) assert result.text == "-line1\n-line***2\n-line***3\n-line***4\n-line5\n-line6" def test_vi_block_editing_empty_lines(): "Test block editing on empty lines." feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI, multiline=True) operations = ( # Six empty lines. "\r\r\r\r\r" # Go to beginning of the document. "\x1bgg" # Enter Visual block mode. "\x16" # Go down two more lines. "jj" # Go 3 characters to the right. "lll" # Go to insert mode. "insert" # (Will be replaced.) # Insert stars. "***" # Escape again. "\x1b\r" ) # Control-I result, cli = feed(operations.replace("insert", "I")) assert result.text == "***\n***\n***\n\n\n" # Control-A result, cli = feed(operations.replace("insert", "A")) assert result.text == "***\n***\n***\n\n\n" def test_vi_visual_line_copy(): feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI, multiline=True) operations = ( # Three lines of text. "-line1\r-line2\r-line3\r-line4\r-line5\r-line6" # Go to the second character of the second line. "\x1bkkkkkkkj0l" # Enter Visual linemode. "V" # Go down one line. "j" # Go 3 characters to the right (should not do much). "lll" # Copy this block. "y" # Go down one line. "j" # Insert block twice. "2p" # Escape again. "\x1b\r" ) result, cli = feed(operations) assert ( result.text == "-line1\n-line2\n-line3\n-line4\n-line2\n-line3\n-line2\n-line3\n-line5\n-line6" ) def test_vi_visual_empty_line(): """ Test edge case with an empty line in Visual-line mode. """ feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI, multiline=True) # 1. Delete first two lines. operations = ( # Three lines of text. The middle one is empty. "hello\r\rworld" # Go to the start. "\x1bgg" # Visual line and move down. "Vj" # Delete. "d\r" ) result, cli = feed(operations) assert result.text == "world" # 1. Delete middle line. operations = ( # Three lines of text. The middle one is empty. "hello\r\rworld" # Go to middle line. "\x1bggj" # Delete line "Vd\r" ) result, cli = feed(operations) assert result.text == "hello\nworld" def test_vi_character_delete_after_cursor(): "Test 'x' keypress." feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI, multiline=True) # Delete one character. result, cli = feed("abcd\x1bHx\r") assert result.text == "bcd" # Delete multiple character.s result, cli = feed("abcd\x1bH3x\r") assert result.text == "d" # Delete on empty line. result, cli = feed("\x1bo\x1bo\x1bggx\r") assert result.text == "\n\n" # Delete multiple on empty line. result, cli = feed("\x1bo\x1bo\x1bgg10x\r") assert result.text == "\n\n" # Delete multiple on empty line. result, cli = feed("hello\x1bo\x1bo\x1bgg3x\r") assert result.text == "lo\n\n" def test_vi_character_delete_before_cursor(): "Test 'X' keypress." feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI, multiline=True) # Delete one character. result, cli = feed("abcd\x1bX\r") assert result.text == "abd" # Delete multiple character. result, cli = feed("hello world\x1b3X\r") assert result.text == "hello wd" # Delete multiple character on multiple lines. result, cli = feed("hello\x1boworld\x1bgg$3X\r") assert result.text == "ho\nworld" result, cli = feed("hello\x1boworld\x1b100X\r") assert result.text == "hello\nd" # Delete on empty line. result, cli = feed("\x1bo\x1bo\x1b10X\r") assert result.text == "\n\n" def test_vi_character_paste(): feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) # Test 'p' character paste. result, cli = feed("abcde\x1bhhxp\r") assert result.text == "abdce" assert result.cursor_position == 3 # Test 'P' character paste. result, cli = feed("abcde\x1bhhxP\r") assert result.text == "abcde" assert result.cursor_position == 2 def test_vi_temp_navigation_mode(): """ Test c-o binding: go for one action into navigation mode. """ feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) result, cli = feed("abcde" "\x0f" "3h" "x\r") # c-o # 3 times to the left. assert result.text == "axbcde" assert result.cursor_position == 2 result, cli = feed("abcde" "\x0f" "b" "x\r") # c-o # One word backwards. assert result.text == "xabcde" assert result.cursor_position == 1 # In replace mode result, cli = feed( "abcdef" "\x1b" # Navigation mode. "0l" # Start of line, one character to the right. "R" # Replace mode "78" "\x0f" # c-o "l" # One character forwards. "9\r" ) assert result.text == "a78d9f" assert result.cursor_position == 5 def test_vi_macros(): feed = partial(_feed_cli_with_input, editing_mode=EditingMode.VI) # Record and execute macro. result, cli = feed("\x1bqcahello\x1bq@c\r") assert result.text == "hellohello" assert result.cursor_position == 9 # Running unknown macro. result, cli = feed("\x1b@d\r") assert result.text == "" assert result.cursor_position == 0 # When a macro is called within a macro. # It shouldn't result in eternal recursion. result, cli = feed("\x1bqxahello\x1b@xq@x\r") assert result.text == "hellohello" assert result.cursor_position == 9 # Nested macros. result, cli = feed( # Define macro 'x'. "\x1bqxahello\x1bq" # Define macro 'y' which calls 'x'. "qya\x1b@xaworld\x1bq" # Delete line. "2dd" # Execute 'y' "@y\r" ) assert result.text == "helloworld" def test_accept_default(): """ Test `prompt(accept_default=True)`. """ with create_pipe_input() as inp: session = PromptSession(input=inp, output=DummyOutput()) result = session.prompt(default="hello", accept_default=True) assert result == "hello" # Test calling prompt() for a second time. (We had an issue where the # prompt reset between calls happened at the wrong time, breaking this.) result = session.prompt(default="world", accept_default=True) assert result == "world"