4 * This file is part of the Symfony package.
6 * (c) Fabien Potencier <fabien@symfony.com>
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
12 namespace Symfony\Component\Console\Tests\Helper;
14 use PHPUnit\Framework\TestCase;
15 use Symfony\Component\Console\Formatter\OutputFormatter;
16 use Symfony\Component\Console\Helper\QuestionHelper;
17 use Symfony\Component\Console\Helper\HelperSet;
18 use Symfony\Component\Console\Helper\FormatterHelper;
19 use Symfony\Component\Console\Output\StreamOutput;
20 use Symfony\Component\Console\Question\ChoiceQuestion;
21 use Symfony\Component\Console\Question\ConfirmationQuestion;
22 use Symfony\Component\Console\Question\Question;
27 class QuestionHelperTest extends TestCase
29 public function testAskChoice()
31 $questionHelper = new QuestionHelper();
33 $helperSet = new HelperSet(array(new FormatterHelper()));
34 $questionHelper->setHelperSet($helperSet);
36 $heroes = array('Superman', 'Batman', 'Spiderman');
38 $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"));
40 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
41 $question->setMaxAttempts(1);
42 // first answer is an empty answer, we're supposed to receive the default value
43 $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
45 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
46 $question->setMaxAttempts(1);
47 $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
48 $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
50 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
51 $question->setErrorMessage('Input "%s" is not a superhero!');
52 $question->setMaxAttempts(2);
53 $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
55 rewind($output->getStream());
56 $stream = stream_get_contents($output->getStream());
57 $this->assertContains('Input "Fabien" is not a superhero!', $stream);
60 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1');
61 $question->setMaxAttempts(1);
62 $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question);
64 } catch (\InvalidArgumentException $e) {
65 $this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
68 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
69 $question->setMaxAttempts(1);
70 $question->setMultiselect(true);
72 $this->assertEquals(array('Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
73 $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
74 $this->assertEquals(array('Superman', 'Spiderman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
76 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
77 $question->setMaxAttempts(1);
78 $question->setMultiselect(true);
80 $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
82 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
83 $question->setMaxAttempts(1);
84 $question->setMultiselect(true);
86 $this->assertEquals(array('Superman', 'Batman'), $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
89 public function testAsk()
91 $dialog = new QuestionHelper();
93 $dialog->setInputStream($this->getInputStream("\n8AM\n"));
95 $question = new Question('What time is it?', '2PM');
96 $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
98 $question = new Question('What time is it?', '2PM');
99 $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question));
101 rewind($output->getStream());
102 $this->assertEquals('What time is it?', stream_get_contents($output->getStream()));
105 public function testAskWithAutocomplete()
107 if (!$this->hasSttyAvailable()) {
108 $this->markTestSkipped('`stty` is required to test autocomplete functionality');
112 // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE>
114 // <UP ARROW><UP ARROW><NEWLINE>
115 // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE>
116 // <DOWN ARROW><NEWLINE>
117 // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE>
118 // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE>
119 $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\n\033[A\033[A\033[A\033[A\033[A\tTest\n\033[B\nS\177\177\033[B\033[B\nF00\177\177oo\t\n");
121 $dialog = new QuestionHelper();
122 $dialog->setInputStream($inputStream);
123 $helperSet = new HelperSet(array(new FormatterHelper()));
124 $dialog->setHelperSet($helperSet);
126 $question = new Question('Please select a bundle', 'FrameworkBundle');
127 $question->setAutocompleterValues(array('AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'));
129 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
130 $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
131 $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
132 $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
133 $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
134 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
135 $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
136 $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
139 public function testAskWithAutocompleteWithNonSequentialKeys()
141 if (!$this->hasSttyAvailable()) {
142 $this->markTestSkipped('`stty` is required to test autocomplete functionality');
145 // <UP ARROW><UP ARROW><NEWLINE><DOWN ARROW><DOWN ARROW><NEWLINE>
146 $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n");
148 $dialog = new QuestionHelper();
149 $dialog->setInputStream($inputStream);
150 $dialog->setHelperSet(new HelperSet(array(new FormatterHelper())));
152 $question = new ChoiceQuestion('Please select a bundle', array(1 => 'AcmeDemoBundle', 4 => 'AsseticBundle'));
153 $question->setMaxAttempts(1);
155 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
156 $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
159 public function testAskHiddenResponse()
161 if ('\\' === DIRECTORY_SEPARATOR) {
162 $this->markTestSkipped('This test is not supported on Windows');
165 $dialog = new QuestionHelper();
166 $dialog->setInputStream($this->getInputStream("8AM\n"));
168 $question = new Question('What time is it?');
169 $question->setHidden(true);
171 $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
175 * @dataProvider getAskConfirmationData
177 public function testAskConfirmation($question, $expected, $default = true)
179 $dialog = new QuestionHelper();
181 $dialog->setInputStream($this->getInputStream($question."\n"));
182 $question = new ConfirmationQuestion('Do you like French fries?', $default);
183 $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel'));
186 public function getAskConfirmationData()
190 array('', false, false),
198 public function testAskConfirmationWithCustomTrueAnswer()
200 $dialog = new QuestionHelper();
202 $dialog->setInputStream($this->getInputStream("j\ny\n"));
203 $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
204 $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
205 $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i');
206 $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
209 public function testAskAndValidate()
211 $dialog = new QuestionHelper();
212 $helperSet = new HelperSet(array(new FormatterHelper()));
213 $dialog->setHelperSet($helperSet);
215 $error = 'This is not a color!';
216 $validator = function ($color) use ($error) {
217 if (!in_array($color, array('white', 'black'))) {
218 throw new \InvalidArgumentException($error);
224 $question = new Question('What color was the white horse of Henry IV?', 'white');
225 $question->setValidator($validator);
226 $question->setMaxAttempts(2);
228 $dialog->setInputStream($this->getInputStream("\nblack\n"));
229 $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
230 $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question));
232 $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n"));
234 $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
236 } catch (\InvalidArgumentException $e) {
237 $this->assertEquals($error, $e->getMessage());
242 * @dataProvider simpleAnswerProvider
244 public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue)
246 $possibleChoices = array(
252 $dialog = new QuestionHelper();
253 $dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
254 $helperSet = new HelperSet(array(new FormatterHelper()));
255 $dialog->setHelperSet($helperSet);
257 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
258 $question->setMaxAttempts(1);
259 $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
261 $this->assertSame($expectedValue, $answer);
264 public function simpleAnswerProvider()
267 array(0, 'My environment 1'),
268 array(1, 'My environment 2'),
269 array(2, 'My environment 3'),
270 array('My environment 1', 'My environment 1'),
271 array('My environment 2', 'My environment 2'),
272 array('My environment 3', 'My environment 3'),
277 * @dataProvider specialCharacterInMultipleChoice
279 public function testSpecialCharacterChoiceFromMultipleChoiceList($providedAnswer, $expectedValue)
281 $possibleChoices = array(
286 $dialog = new QuestionHelper();
287 $dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
288 $helperSet = new HelperSet(array(new FormatterHelper()));
289 $dialog->setHelperSet($helperSet);
291 $question = new ChoiceQuestion('Please select the directory', $possibleChoices);
292 $question->setMaxAttempts(1);
293 $question->setMultiselect(true);
294 $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
296 $this->assertSame($expectedValue, $answer);
299 public function specialCharacterInMultipleChoice()
302 array('.', array('.')),
303 array('., src', array('.', 'src')),
308 * @dataProvider mixedKeysChoiceListAnswerProvider
310 public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue)
312 $possibleChoices = array(
313 '0' => 'No environment',
314 '1' => 'My environment 1',
315 'env_2' => 'My environment 2',
316 3 => 'My environment 3',
319 $dialog = new QuestionHelper();
320 $dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
321 $helperSet = new HelperSet(array(new FormatterHelper()));
322 $dialog->setHelperSet($helperSet);
324 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
325 $question->setMaxAttempts(1);
326 $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
328 $this->assertSame($expectedValue, $answer);
331 public function mixedKeysChoiceListAnswerProvider()
335 array('No environment', '0'),
337 array('env_2', 'env_2'),
339 array('My environment 1', '1'),
344 * @dataProvider answerProvider
346 public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue)
348 $possibleChoices = array(
349 'env_1' => 'My environment 1',
350 'env_2' => 'My environment',
351 'env_3' => 'My environment',
354 $dialog = new QuestionHelper();
355 $dialog->setInputStream($this->getInputStream($providedAnswer."\n"));
356 $helperSet = new HelperSet(array(new FormatterHelper()));
357 $dialog->setHelperSet($helperSet);
359 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
360 $question->setMaxAttempts(1);
361 $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
363 $this->assertSame($expectedValue, $answer);
367 * @expectedException \InvalidArgumentException
368 * @expectedExceptionMessage The provided answer is ambiguous. Value should be one of env_2 or env_3.
370 public function testAmbiguousChoiceFromChoicelist()
372 $possibleChoices = array(
373 'env_1' => 'My first environment',
374 'env_2' => 'My environment',
375 'env_3' => 'My environment',
378 $dialog = new QuestionHelper();
379 $dialog->setInputStream($this->getInputStream("My environment\n"));
380 $helperSet = new HelperSet(array(new FormatterHelper()));
381 $dialog->setHelperSet($helperSet);
383 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices);
384 $question->setMaxAttempts(1);
386 $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
389 public function answerProvider()
392 array('env_1', 'env_1'),
393 array('env_2', 'env_2'),
394 array('env_3', 'env_3'),
395 array('My environment 1', 'env_1'),
399 public function testNoInteraction()
401 $dialog = new QuestionHelper();
402 $question = new Question('Do you have a job?', 'not yet');
403 $this->assertEquals('not yet', $dialog->ask($this->createInputInterfaceMock(false), $this->createOutputInterface(), $question));
407 * @requires function mb_strwidth
409 public function testChoiceOutputFormattingQuestionForUtf8Keys()
411 $question = 'Lorem ipsum?';
412 $possibleChoices = array(
417 $outputShown = array(
419 ' [<info>foo </info>] foo',
420 ' [<info>żółw </info>] bar',
421 ' [<info>łabądź</info>] baz',
423 $output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock();
424 $output->method('getFormatter')->willReturn(new OutputFormatter());
426 $dialog = new QuestionHelper();
427 $dialog->setInputStream($this->getInputStream("\n"));
428 $helperSet = new HelperSet(array(new FormatterHelper()));
429 $dialog->setHelperSet($helperSet);
431 $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown));
433 $question = new ChoiceQuestion($question, $possibleChoices, 'foo');
434 $dialog->ask($this->createInputInterfaceMock(), $output, $question);
438 * @expectedException \Symfony\Component\Console\Exception\RuntimeException
439 * @expectedExceptionMessage Aborted
441 public function testAskThrowsExceptionOnMissingInput()
443 $dialog = new QuestionHelper();
444 $dialog->setInputStream($this->getInputStream(''));
446 $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), new Question('What\'s your name?'));
450 * @expectedException \Symfony\Component\Console\Exception\RuntimeException
451 * @expectedExceptionMessage Aborted
453 public function testAskThrowsExceptionOnMissingInputWithValidator()
455 $dialog = new QuestionHelper();
456 $dialog->setInputStream($this->getInputStream(''));
458 $question = new Question('What\'s your name?');
459 $question->setValidator(function () {
461 throw new \Exception('A value is required.');
465 $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question);
469 * @expectedException \LogicException
470 * @expectedExceptionMessage Choice question must have at least 1 choice available.
472 public function testEmptyChoices()
474 new ChoiceQuestion('Question', array(), 'irrelevant');
477 protected function getInputStream($input)
479 $stream = fopen('php://memory', 'r+', false);
480 fwrite($stream, $input);
486 protected function createOutputInterface()
488 return new StreamOutput(fopen('php://memory', 'r+', false));
491 protected function createInputInterfaceMock($interactive = true)
493 $mock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock();
494 $mock->expects($this->any())
495 ->method('isInteractive')
496 ->will($this->returnValue($interactive));
501 private function hasSttyAvailable()
503 exec('stty 2>&1', $output, $exitcode);
505 return $exitcode === 0;