66from __future__ import annotations
77
88from functools import partial
9+ import io
10+ import sys
911
1012import pytest
1113
1214from prompt_toolkit .clipboard import ClipboardData , InMemoryClipboard
15+ from prompt_toolkit .data_structures import Size
1316from prompt_toolkit .enums import EditingMode
1417from prompt_toolkit .filters import ViInsertMode
1518from prompt_toolkit .history import InMemoryHistory
1821from prompt_toolkit .key_binding .bindings .named_commands import prefix_meta
1922from prompt_toolkit .key_binding .key_bindings import KeyBindings
2023from prompt_toolkit .output import DummyOutput
24+ from prompt_toolkit .output .plain_text import PlainTextOutput
25+ from prompt_toolkit .output .vt100 import Vt100_Output
2126from prompt_toolkit .shortcuts import PromptSession
2227
2328
@@ -29,6 +34,33 @@ def _history():
2934 return h
3035
3136
37+ def _feed_cli_with_password (text , check_line_ending = True , hide_password = False ):
38+ """
39+ Create a Prompt, feed it with a given user input considered
40+ to be a password, and then return the CLI output to the caller.
41+
42+ This returns an Output object.
43+ """
44+
45+ # If the given text doesn't end with a newline, the interface won't finish.
46+ if check_line_ending :
47+ assert text .endswith ("\r " )
48+
49+ output = PlainTextOutput (stdout = io .StringIO ())
50+
51+ with create_pipe_input () as inp :
52+ inp .send_text (text )
53+ session = PromptSession (
54+ input = inp ,
55+ output = output ,
56+ is_password = True ,
57+ hide_password = hide_password ,
58+ )
59+
60+ _ = session .prompt ()
61+ return output
62+
63+
3264def _feed_cli_with_input (
3365 text ,
3466 editing_mode = EditingMode .EMACS ,
@@ -42,7 +74,7 @@ def _feed_cli_with_input(
4274 Create a Prompt, feed it with the given user input and return the CLI
4375 object.
4476
45- This returns a (result , Application) tuple.
77+ This returns a (Document , Application, Output ) tuple.
4678 """
4779 # If the given text doesn't end with a newline, the interface won't finish.
4880 if check_line_ending :
@@ -64,6 +96,33 @@ def _feed_cli_with_input(
6496 return session .default_buffer .document , session .app
6597
6698
99+ def test_visible_password ():
100+ # Both the `result` and the `cli.current_buffer` displays the password in plain-text,
101+ # but that's not what the user sees on screen.
102+ password = "secret-value\r "
103+ output = _feed_cli_with_password (password , hide_password = False )
104+ output .stdout .seek (0 ) # Reset the stream pointer
105+ actual_output = output .stdout .read ().strip ()
106+
107+ # Test that the string is made up only of `*` characters
108+ assert actual_output == "*" * len (actual_output ), actual_output
109+
110+ # Test that the string is long as much as the original password,
111+ # minus the needed carriage return.
112+ assert actual_output == "*" * len (password .strip ()), actual_output
113+
114+
115+ def test_invisible_password ():
116+ password = "secret-value\r "
117+ output = _feed_cli_with_password (password , hide_password = True )
118+ output .stdout .seek (0 ) # Reset the stream pointer
119+ actual_output = output .stdout .read ().strip ()
120+
121+ # Test that, if the `hide_password` flag is set to True,
122+ # then then prompt won't display anything in the output.
123+ assert actual_output == "" , actual_output
124+
125+
67126def test_simple_text_input ():
68127 # Simple text input, followed by enter.
69128 result , cli = _feed_cli_with_input ("hello\r " )
0 commit comments