Skip to content

Commit

Permalink
bug #19100 [Console] Fixed SymfonyQuestionHelper multi-choice with de…
Browse files Browse the repository at this point in the history
…faults (sstok)

This PR was submitted for the 2.8 branch but it was merged into the 2.7 branch instead (closes #19100).

Discussion
----------

[Console] Fixed SymfonyQuestionHelper multi-choice with defaults

|Q            |A  |
|---          |---|
|Bug Fix?     |yes|
|New Feature? |no |
|BC Breaks?   |no |
|Deprecations?|no |
|Tests Pass?  |yes|
|Fixed Tickets|   |
|License      |MIT|
|Doc PR       |   |

When you use the SymfonyStyle with a multi-choice question and multiple defaults.
You get an notice because the key is used as-is `0,1` instead of splitting the value.

This pull-request changes the SymfonyQuestionHelper to checks if the Choice is a multi-choice
and displays the selected values correctly `[blue, yellow]`.

Note: Tests are missing, but both the SymfonyStyle and SymfonyQuestionHelper classes
have almost no tests. Making it really hard 😇 hopefully #19097 will make this easier 👍

Commits
-------

a8f6f85 Fixed SymfonyQuestionHelper multi-choice with defaults
  • Loading branch information
fabpot committed Jun 22, 2016
2 parents 80057b0 + a8f6f85 commit dadf570
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 0 deletions.
12 changes: 12 additions & 0 deletions src/Symfony/Component/Console/Helper/SymfonyQuestionHelper.php
Expand Up @@ -66,6 +66,18 @@ protected function writePrompt(OutputInterface $output, Question $question)

break;

case $question instanceof ChoiceQuestion && $question->isMultiSelect():
$choices = $question->getChoices();
$default = explode(',', $default);

foreach ($default as $key => $value) {
$default[$key] = $choices[trim($value)];
}

$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, implode(', ', $default));

break;

case $question instanceof ChoiceQuestion:
$choices = $question->getChoices();
$text = sprintf(' <info>%s</info> [<comment>%s</comment>]:', $text, $choices[$default]);
Expand Down
10 changes: 10 additions & 0 deletions src/Symfony/Component/Console/Question/ChoiceQuestion.php
Expand Up @@ -66,6 +66,16 @@ public function setMultiselect($multiselect)
return $this;
}

/**
* Returns whether the choices are multiselect.
*
* @return bool
*/
public function isMultiselect()
{
return $this->multiselect;
}

/**
* Gets the prompt for choices.
*
Expand Down
@@ -0,0 +1,109 @@
<?php

namespace Symfony\Component\Console\Tests\Helper;

use Symfony\Component\Console\Helper\FormatterHelper;
use Symfony\Component\Console\Helper\HelperSet;
use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
use Symfony\Component\Console\Output\StreamOutput;
use Symfony\Component\Console\Question\ChoiceQuestion;

/**
* @group tty
*/
class SymfonyQuestionHelperTest extends \PHPUnit_Framework_TestCase
{
public function testAskChoice()
{
$questionHelper = new SymfonyQuestionHelper();

$helperSet = new HelperSet(array(new FormatterHelper()));
$questionHelper->setHelperSet($helperSet);

$heroes = array('Superman', 'Batman', 'Spiderman');

$questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"));

$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
$question->setMaxAttempts(1);
// first answer is an empty answer, we're supposed to receive the default value
$this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('What is your favorite superhero? [Spiderman]', $output);

$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setMaxAttempts(1);
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));

$question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
$question->setErrorMessage('Input "%s" is not a superhero!');
$question->setMaxAttempts(2);
$this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('Input "Fabien" is not a superhero!', $output);

try {
$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1');
$question->setMaxAttempts(1);
$questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question);
$this->fail();
} catch (\InvalidArgumentException $e) {
$this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
}

$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
$question->setMaxAttempts(1);
$question->setMultiselect(true);

$this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
$this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));

$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
$question->setMaxAttempts(1);
$question->setMultiselect(true);

$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output);

$question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
$question->setMaxAttempts(1);
$question->setMultiselect(true);

$this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
$this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output);
}

protected function getInputStream($input)
{
$stream = fopen('php://memory', 'r+', false);
fwrite($stream, $input);
rewind($stream);

return $stream;
}

protected function createOutputInterface()
{
$output = new StreamOutput(fopen('php://memory', 'r+', false));
$output->setDecorated(false);

return $output;
}

protected function createInputInterfaceMock($interactive = true)
{
$mock = $this->getMock('Symfony\Component\Console\Input\InputInterface');
$mock->expects($this->any())
->method('isInteractive')
->will($this->returnValue($interactive));

return $mock;
}

private function assertOutputContains($expected, StreamOutput $output)
{
rewind($output->getStream());
$stream = stream_get_contents($output->getStream());
$this->assertContains($expected, $stream);
}
}

0 comments on commit dadf570

Please sign in to comment.