Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

how to test? #1243

Open
yajo opened this issue Sep 13, 2020 · 8 comments
Open

how to test? #1243

yajo opened this issue Sep 13, 2020 · 8 comments

Comments

@yajo
Copy link

yajo commented Sep 13, 2020

I've been trying to use this library but it breaks my tests.

I build a CLI app that asks for some questions to the user. Some uni t tests emulate some content in the STDIN to check the user interaction works as expected. But doing it with thi s library fails.

How are we supposed to test user interactions when using this library? I don't find that on the docs.

@jonathanslenders
Copy link
Member

I think in the most recent versions, the easiest to do it is like this:

from prompt_toolkit.input import create_pipe_input
from prompt_toolkit.application import AppSession, create_app_session
from prompt_toolkit.output import DummyOutput

input = create_pipe_input()  # Create an alternative input device, similar to StringIO.
input.send_text("hello\n")

with create_app_session(input=input, output=DummyOutput()):
    #  run your application here. It will by default take the input/output objects from the AppSession.

If this works and is useful to you, feel free to add it to the documentation.

@yajo
Copy link
Author

yajo commented Sep 18, 2020

OK, let me test that. Thanks!

@yajo
Copy link
Author

yajo commented Sep 25, 2020

It seems to be working, although I cannot find a way to assert what is printed in the user screen. There's no session.output.read() or something like that? How can I get that?

@yajo
Copy link
Author

yajo commented Oct 11, 2020

Finally I decided to test using https://github.com/pexpect/pexpect, which works great on Linux/mac because it uses a TTy and lets you interact with it and check what's happening.

However, pexpect.spawn is unavailable on Windows. Reading their docs, you see that there's a different process there: pexpect.popen_spawn.PopenSpawn. It is not exactly a TTy, but surprisingly it works equally fine on Linux/Mac (when testing a python CLI app based on top of python-prompt-toolkit), but when testing on Windows, the executed app fails with this:

Traceback (most recent call last):
  File "c:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\Scripts\\\\copier", line 5, in <module>
    CopierApp.run()
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\lib\\site-packages\\plumbum\\cli\\application.py", line 577, in run
    inst, retcode = subapp.run(argv, exit=False)
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\lib\\site-packages\\plumbum\\cli\\application.py", line 572, in run
    retcode = inst.main(*tailargs)
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\copier\\cli.py", line 38, in _wrapper
    return method(*args, **kwargs)
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\copier\\cli.py", line 262, in main
    self.parent._copy(
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\copier\\cli.py", line 173, in _copy
    return copy(
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\copier\\main.py", line 135, in copy
    conf = make_config(**locals())
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\copier\\config\\factory.py", line 141, in make_config
    init_args["data_from_asking_user"] = query_user_data(
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\copier\\config\\user_data.py", line 507, in query_user_data
    return questionary.get_answers()
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\copier\\config\\user_data.py", line 367, in get_answers
    self.answers_user = prompt(
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\src\\questionary\\questionary\\prompt.py", line 95, in prompt
    question = create_question_func(**_kwargs)
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\src\\questionary\\questionary\\prompts\\confirm.py", line 98, in confirm
    PromptSession(
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\lib\\site-packages\\prompt_toolkit\\shortcuts\\prompt.py", line 466, in __init__
    self.app = self._create_application(editing_mode, erase_when_done)
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\lib\\site-packages\\prompt_toolkit\\shortcuts\\prompt.py", line 717, in _create_application
    application: Application[_T] = Application(
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\lib\\site-packages\\prompt_toolkit\\application\\application.py", line 271, in __init__
    self.output = output or session.output
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\lib\\site-packages\\prompt_toolkit\\application\\current.py", line 70, in output
    self._output = create_output()
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\lib\\site-packages\\prompt_toolkit\\output\\defaults.py", line 70, in create_output
    return Win32Output(stdout, default_color_depth=color_depth_from_env)
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\lib\\site-packages\\prompt_toolkit\\output\\win32.py", line 107, in __init__
    info = self.get_win32_screen_buffer_info()
  File "C:\\Users\\User\\source\\repos\\copier\\copier\\.venv\\lib\\site-packages\\prompt_toolkit\\output\\win32.py", line 211, in get_win32_screen_buffer_info
    raise NoConsoleScreenBufferError
prompt_toolkit.output.win32.NoConsoleScreenBufferError: No Windows console found. Are you running cmd.exe?

Thus my new question is: is there any way to circumvent that requirement of running under cmd.exe? Because I'm just testing, and I don't really care about the tty control codes interaction between the terminal and python-prompt-toolkit (that's "toolkit's problem"); I only want to test that the app reacts properly and prints the expected output when the user answers with some input (I'm already doing that well in copier-org/copier#260, but I'm asking just to know if this would be possible on Windows too).


Note 1: there's https://github.com/raczben/wexpect for Windows, but it has several issues such as raczben/wexpect#42, raczben/wexpect#39, raczben/wexpect#26 and raczben/wexpect#22 that make it not very useful at the moment.

Note 2: I know it's not "your business" to make this work with pexpect, but I think that having a way to test TUI behavior is very useful for those of us that are developing TUI apps and want to assert their behavior. If we manage to fix this, it would be a great addition to the docs, and you could even start using it on tests here (I see there's no Windows-based CI here...).

@lyz-code
Copy link

I've gathered a small example using @yajo 's method here in case anyone is as lost as I was some hours ago.

@marcgibbons
Copy link

There's no session.output.read()

@yajo This can be achieved like so:

Extending @jonathanslenders 's example:

from io import StringIO

from prompt_toolkit.application import create_app_session
from prompt_toolkit.input import create_pipe_input
from prompt_toolkit.output import create_output

input = create_pipe_input()  # Create an alternative input device, similar to StringIO.
input.send_text("hello\n")

buffer = StringIO()   # Use to capture stdout
with create_app_session(
    input=input, 
    output=create_output(stdout=buffer),
):
    #  run your application here. It will by default take the input/output objects from the AppSession.

buffer.seek(0)
output = buffer.read() 

@jupiterbjy
Copy link

jupiterbjy commented Aug 7, 2024

is create_pipe_input().send_text() changed? create_pipe_input is returning generator contextmgr for current releases.

>>> from prompt_toolkit.input import create_pipe_input
>>> input_pipe = create_pipe_input()
>>> input_pipe.send_text("test")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: '_GeneratorContextManager' object has no attribute 'send_text'

>>>

EDIT: nvm found it, phew - so this changed to return context manager I see. Does that means that input pipe is not reusable?

image

@Alina-Valea-Forter
Copy link

Alina-Valea-Forter commented Aug 29, 2024

Is there a way to programatically check boxes in a dialog with multiple check boxes created with checkboxlist_dialog()?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants