Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Console ApplicationTester does not allow multiple prompts on invalid Question input #37046

Copy link
Copy link
Closed
@weierophinney

Description

@weierophinney
Issue body actions

Symfony version(s) affected: 5.0.0, 5.1.0

Description

When using the QuestionHelper and testing via an ApplicationTester, if $maxAttempts has not been set, the question will stop prompting for input after the first question response, regardless of whether or not more input are available.

How to reproduce

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Tester\ApplicationTester;

require './vendor/autoload.php';

class QuestionCommand extends Command
{
    protected function configure(): void
    {
        $this->setDescription('A command that asks a question');
    }

    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $question = new Question('This is a promptable question');
        $question->setValidator(function ($value) {
            if (! preg_match('/^[A-Z][A-Za-z0-9_]+$/', $value)) {
                throw new RuntimeException('Question requires a valid class name');
            }
            return $value;
        });

        /** @var QuestionHelper $helper */
        $helper = $this->getHelper('question');
        $class = $helper->ask($input, $output, $question);

        $output->writeln(sprintf('<info>%s</info>', $class));

        return 0;
    }
}

$application = new Application('test');
$application->add(new QuestionCommand('question'));
$application->setAutoExit(false);

$tester = new ApplicationTester($application);
$tester->setInputs(['', 'not-a-class', 'also not a class', 'FinallyAClass']);

$statusCode = $tester->run(
    ['command' => 'question'],
    ['interactive' => true]
);

printf("Status code: %d\n", $statusCode);

Expected result:

Status code: 0

Actual result:

Status code: 1

Possible Solution

In both version 4 and version 5 code, QuestionHelper::validateAttempts() has the following loop defined:

while (null === $attempts || $attempts--)

In version 5, the following line is added at the end of the loop:

$attempts = $attempts ?? -(int) $this->isTty();

This means that you will always run the loop exactly 1 time when $attempts is set to null, as isTty() always returns false when run under the ApplicationTester (which uses a PHP memory stream to represent the TTY input stream).

An $attempts value of null is meant to indicate no maximum, and to prompt indefinitely; more importantly, it is the default value.

In order to preserve previous (and documented) behavior, I think this should likely read:

$attempts = $attempts === null ? null : -(int) $this->isTtty();

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.