1<?php 2 3/* 4 * This file is part of the Symfony package. 5 * 6 * (c) Fabien Potencier <fabien@symfony.com> 7 * 8 * For the full copyright and license information, please view the LICENSE 9 * file that was distributed with this source code. 10 */ 11 12namespace Symfony\Component\Console\Tests\Helper; 13 14use Symfony\Component\Console\Exception\InvalidArgumentException; 15use Symfony\Component\Console\Formatter\OutputFormatter; 16use Symfony\Component\Console\Helper\FormatterHelper; 17use Symfony\Component\Console\Helper\HelperSet; 18use Symfony\Component\Console\Helper\QuestionHelper; 19use Symfony\Component\Console\Output\StreamOutput; 20use Symfony\Component\Console\Question\ChoiceQuestion; 21use Symfony\Component\Console\Question\ConfirmationQuestion; 22use Symfony\Component\Console\Question\Question; 23use Symfony\Component\Console\Terminal; 24 25/** 26 * @group tty 27 */ 28class QuestionHelperTest extends AbstractQuestionHelperTest 29{ 30 public function testAskChoice() 31 { 32 $questionHelper = new QuestionHelper(); 33 34 $helperSet = new HelperSet([new FormatterHelper()]); 35 $questionHelper->setHelperSet($helperSet); 36 37 $heroes = ['Superman', 'Batman', 'Spiderman']; 38 39 $inputStream = $this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"); 40 41 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); 42 $question->setMaxAttempts(1); 43 // first answer is an empty answer, we're supposed to receive the default value 44 $this->assertEquals('Spiderman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 45 46 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); 47 $question->setMaxAttempts(1); 48 $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 49 $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 50 51 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); 52 $question->setErrorMessage('Input "%s" is not a superhero!'); 53 $question->setMaxAttempts(2); 54 $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question)); 55 56 rewind($output->getStream()); 57 $stream = stream_get_contents($output->getStream()); 58 $this->assertStringContainsString('Input "Fabien" is not a superhero!', $stream); 59 60 try { 61 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); 62 $question->setMaxAttempts(1); 63 $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question); 64 $this->fail(); 65 } catch (\InvalidArgumentException $e) { 66 $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); 67 } 68 69 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); 70 $question->setMaxAttempts(1); 71 $question->setMultiselect(true); 72 73 $this->assertEquals(['Batman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 74 $this->assertEquals(['Superman', 'Spiderman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 75 $this->assertEquals(['Superman', 'Spiderman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 76 77 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); 78 $question->setMaxAttempts(1); 79 $question->setMultiselect(true); 80 81 $this->assertEquals(['Superman', 'Batman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 82 83 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); 84 $question->setMaxAttempts(1); 85 $question->setMultiselect(true); 86 87 $this->assertEquals(['Superman', 'Batman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 88 89 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, 0); 90 // We are supposed to get the default value since we are not in interactive mode 91 $this->assertEquals('Superman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, true), $this->createOutputInterface(), $question)); 92 } 93 94 public function testAskChoiceNonInteractive() 95 { 96 $questionHelper = new QuestionHelper(); 97 98 $helperSet = new HelperSet([new FormatterHelper()]); 99 $questionHelper->setHelperSet($helperSet); 100 $inputStream = $this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n"); 101 102 $heroes = ['Superman', 'Batman', 'Spiderman']; 103 104 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0'); 105 106 $this->assertSame('Superman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); 107 108 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, 'Batman'); 109 $this->assertSame('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); 110 111 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); 112 $this->assertNull($questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); 113 114 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0'); 115 $question->setValidator(null); 116 $this->assertSame('Superman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); 117 118 try { 119 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); 120 $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question); 121 } catch (\InvalidArgumentException $e) { 122 $this->assertSame('Value "" is invalid', $e->getMessage()); 123 } 124 125 $question = new ChoiceQuestion('Who are your favorite superheros?', $heroes, '0, 1'); 126 $question->setMultiselect(true); 127 $this->assertSame(['Superman', 'Batman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); 128 129 $question = new ChoiceQuestion('Who are your favorite superheros?', $heroes, '0, 1'); 130 $question->setMultiselect(true); 131 $question->setValidator(null); 132 $this->assertSame(['Superman', 'Batman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); 133 134 $question = new ChoiceQuestion('Who are your favorite superheros?', $heroes, '0, Batman'); 135 $question->setMultiselect(true); 136 $this->assertSame(['Superman', 'Batman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); 137 138 $question = new ChoiceQuestion('Who are your favorite superheros?', $heroes, null); 139 $question->setMultiselect(true); 140 $this->assertNull($questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question)); 141 142 $question = new ChoiceQuestion('Who are your favorite superheros?', ['a' => 'Batman', 'b' => 'Superman'], 'a'); 143 $this->assertSame('a', $questionHelper->ask($this->createStreamableInputInterfaceMock('', false), $this->createOutputInterface(), $question), 'ChoiceQuestion validator returns the key if it\'s a string'); 144 145 try { 146 $question = new ChoiceQuestion('Who are your favorite superheros?', $heroes, ''); 147 $question->setMultiselect(true); 148 $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream, false), $this->createOutputInterface(), $question); 149 } catch (\InvalidArgumentException $e) { 150 $this->assertSame('Value "" is invalid', $e->getMessage()); 151 } 152 } 153 154 public function testAsk() 155 { 156 $dialog = new QuestionHelper(); 157 158 $inputStream = $this->getInputStream("\n8AM\n"); 159 160 $question = new Question('What time is it?', '2PM'); 161 $this->assertEquals('2PM', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 162 163 $question = new Question('What time is it?', '2PM'); 164 $this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question)); 165 166 rewind($output->getStream()); 167 $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); 168 } 169 170 public function testAskWithAutocomplete() 171 { 172 if (!Terminal::hasSttyAvailable()) { 173 $this->markTestSkipped('`stty` is required to test autocomplete functionality'); 174 } 175 176 // Acm<NEWLINE> 177 // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE> 178 // <NEWLINE> 179 // <UP ARROW><UP ARROW><UP ARROW><NEWLINE> 180 // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE> 181 // <DOWN ARROW><NEWLINE> 182 // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE> 183 // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE> 184 // F⭐<TAB><BACKSPACE><BACKSPACE>⭐<TAB><NEWLINE> 185 $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\033[A\n\033[A\033[A\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\nF⭐\t\177\177⭐\t\n"); 186 187 $dialog = new QuestionHelper(); 188 $helperSet = new HelperSet([new FormatterHelper()]); 189 $dialog->setHelperSet($helperSet); 190 191 $question = new Question('Please select a bundle', 'FrameworkBundle'); 192 $question->setAutocompleterValues(['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle', 'F⭐Y']); 193 194 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 195 $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 196 $this->assertEquals('FrameworkBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 197 $this->assertEquals('SecurityBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 198 $this->assertEquals('FooBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 199 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 200 $this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 201 $this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 202 $this->assertEquals('F⭐Y', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 203 } 204 205 public function testAskWithAutocompleteWithNonSequentialKeys() 206 { 207 if (!Terminal::hasSttyAvailable()) { 208 $this->markTestSkipped('`stty` is required to test autocomplete functionality'); 209 } 210 211 // <UP ARROW><UP ARROW><NEWLINE><DOWN ARROW><DOWN ARROW><NEWLINE> 212 $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); 213 214 $dialog = new QuestionHelper(); 215 $dialog->setHelperSet(new HelperSet([new FormatterHelper()])); 216 217 $question = new ChoiceQuestion('Please select a bundle', [1 => 'AcmeDemoBundle', 4 => 'AsseticBundle']); 218 $question->setMaxAttempts(1); 219 220 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 221 $this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 222 } 223 224 public function testAskWithAutocompleteWithExactMatch() 225 { 226 if (!Terminal::hasSttyAvailable()) { 227 $this->markTestSkipped('`stty` is required to test autocomplete functionality'); 228 } 229 230 $inputStream = $this->getInputStream("b\n"); 231 232 $possibleChoices = [ 233 'a' => 'berlin', 234 'b' => 'copenhagen', 235 'c' => 'amsterdam', 236 ]; 237 238 $dialog = new QuestionHelper(); 239 $dialog->setHelperSet(new HelperSet([new FormatterHelper()])); 240 241 $question = new ChoiceQuestion('Please select a city', $possibleChoices); 242 $question->setMaxAttempts(1); 243 244 $this->assertSame('b', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 245 } 246 247 public function getInputs() 248 { 249 return [ 250 ['$'], // 1 byte character 251 ['¢'], // 2 bytes character 252 ['€'], // 3 bytes character 253 [''], // 4 bytes character 254 ]; 255 } 256 257 /** 258 * @dataProvider getInputs 259 */ 260 public function testAskWithAutocompleteWithMultiByteCharacter($character) 261 { 262 if (!Terminal::hasSttyAvailable()) { 263 $this->markTestSkipped('`stty` is required to test autocomplete functionality'); 264 } 265 266 $inputStream = $this->getInputStream("$character\n"); 267 268 $possibleChoices = [ 269 '$' => '1 byte character', 270 '¢' => '2 bytes character', 271 '€' => '3 bytes character', 272 '' => '4 bytes character', 273 ]; 274 275 $dialog = new QuestionHelper(); 276 $dialog->setHelperSet(new HelperSet([new FormatterHelper()])); 277 278 $question = new ChoiceQuestion('Please select a character', $possibleChoices); 279 $question->setMaxAttempts(1); 280 281 $this->assertSame($character, $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 282 } 283 284 public function testAutocompleteWithTrailingBackslash() 285 { 286 if (!Terminal::hasSttyAvailable()) { 287 $this->markTestSkipped('`stty` is required to test autocomplete functionality'); 288 } 289 290 $inputStream = $this->getInputStream('E'); 291 292 $dialog = new QuestionHelper(); 293 $helperSet = new HelperSet([new FormatterHelper()]); 294 $dialog->setHelperSet($helperSet); 295 296 $question = new Question(''); 297 $expectedCompletion = 'ExampleNamespace\\'; 298 $question->setAutocompleterValues([$expectedCompletion]); 299 300 $output = $this->createOutputInterface(); 301 $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $output, $question); 302 303 $outputStream = $output->getStream(); 304 rewind($outputStream); 305 $actualOutput = stream_get_contents($outputStream); 306 307 // Shell control (esc) sequences are not so important: we only care that 308 // <hl> tag is interpreted correctly and replaced 309 $irrelevantEscSequences = [ 310 "\0337" => '', // Save cursor position 311 "\0338" => '', // Restore cursor position 312 "\033[K" => '', // Clear line from cursor till the end 313 ]; 314 315 $importantActualOutput = strtr($actualOutput, $irrelevantEscSequences); 316 317 // Remove colors (e.g. "\033[30m", "\033[31;41m") 318 $importantActualOutput = preg_replace('/\033\[\d+(;\d+)?m/', '', $importantActualOutput); 319 320 $this->assertEquals($expectedCompletion, $importantActualOutput); 321 } 322 323 public function testAskHiddenResponse() 324 { 325 if ('\\' === \DIRECTORY_SEPARATOR) { 326 $this->markTestSkipped('This test is not supported on Windows'); 327 } 328 329 $dialog = new QuestionHelper(); 330 331 $question = new Question('What time is it?'); 332 $question->setHidden(true); 333 334 $this->assertEquals('8AM', $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("8AM\n")), $this->createOutputInterface(), $question)); 335 } 336 337 /** 338 * @dataProvider getAskConfirmationData 339 */ 340 public function testAskConfirmation($question, $expected, $default = true) 341 { 342 $dialog = new QuestionHelper(); 343 344 $inputStream = $this->getInputStream($question."\n"); 345 $question = new ConfirmationQuestion('Do you like French fries?', $default); 346 $this->assertEquals($expected, $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); 347 } 348 349 public function getAskConfirmationData() 350 { 351 return [ 352 ['', true], 353 ['', false, false], 354 ['y', true], 355 ['yes', true], 356 ['n', false], 357 ['no', false], 358 ]; 359 } 360 361 public function testAskConfirmationWithCustomTrueAnswer() 362 { 363 $dialog = new QuestionHelper(); 364 365 $inputStream = $this->getInputStream("j\ny\n"); 366 $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); 367 $this->assertTrue($dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 368 $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); 369 $this->assertTrue($dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 370 } 371 372 public function testAskAndValidate() 373 { 374 $dialog = new QuestionHelper(); 375 $helperSet = new HelperSet([new FormatterHelper()]); 376 $dialog->setHelperSet($helperSet); 377 378 $error = 'This is not a color!'; 379 $validator = function ($color) use ($error) { 380 if (!\in_array($color, ['white', 'black'])) { 381 throw new \InvalidArgumentException($error); 382 } 383 384 return $color; 385 }; 386 387 $question = new Question('What color was the white horse of Henry IV?', 'white'); 388 $question->setValidator($validator); 389 $question->setMaxAttempts(2); 390 391 $inputStream = $this->getInputStream("\nblack\n"); 392 $this->assertEquals('white', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 393 $this->assertEquals('black', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 394 395 try { 396 $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("green\nyellow\norange\n")), $this->createOutputInterface(), $question); 397 $this->fail(); 398 } catch (\InvalidArgumentException $e) { 399 $this->assertEquals($error, $e->getMessage()); 400 } 401 } 402 403 /** 404 * @dataProvider simpleAnswerProvider 405 */ 406 public function testSelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) 407 { 408 $possibleChoices = [ 409 'My environment 1', 410 'My environment 2', 411 'My environment 3', 412 ]; 413 414 $dialog = new QuestionHelper(); 415 $helperSet = new HelperSet([new FormatterHelper()]); 416 $dialog->setHelperSet($helperSet); 417 418 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); 419 $question->setMaxAttempts(1); 420 $answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question); 421 422 $this->assertSame($expectedValue, $answer); 423 } 424 425 public function simpleAnswerProvider() 426 { 427 return [ 428 [0, 'My environment 1'], 429 [1, 'My environment 2'], 430 [2, 'My environment 3'], 431 ['My environment 1', 'My environment 1'], 432 ['My environment 2', 'My environment 2'], 433 ['My environment 3', 'My environment 3'], 434 ]; 435 } 436 437 /** 438 * @dataProvider specialCharacterInMultipleChoice 439 */ 440 public function testSpecialCharacterChoiceFromMultipleChoiceList($providedAnswer, $expectedValue) 441 { 442 $possibleChoices = [ 443 '.', 444 'src', 445 ]; 446 447 $dialog = new QuestionHelper(); 448 $inputStream = $this->getInputStream($providedAnswer."\n"); 449 $helperSet = new HelperSet([new FormatterHelper()]); 450 $dialog->setHelperSet($helperSet); 451 452 $question = new ChoiceQuestion('Please select the directory', $possibleChoices); 453 $question->setMaxAttempts(1); 454 $question->setMultiselect(true); 455 $answer = $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question); 456 457 $this->assertSame($expectedValue, $answer); 458 } 459 460 public function specialCharacterInMultipleChoice() 461 { 462 return [ 463 ['.', ['.']], 464 ['., src', ['.', 'src']], 465 ]; 466 } 467 468 /** 469 * @dataProvider mixedKeysChoiceListAnswerProvider 470 */ 471 public function testChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) 472 { 473 $possibleChoices = [ 474 '0' => 'No environment', 475 '1' => 'My environment 1', 476 'env_2' => 'My environment 2', 477 3 => 'My environment 3', 478 ]; 479 480 $dialog = new QuestionHelper(); 481 $helperSet = new HelperSet([new FormatterHelper()]); 482 $dialog->setHelperSet($helperSet); 483 484 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); 485 $question->setMaxAttempts(1); 486 $answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question); 487 488 $this->assertSame($expectedValue, $answer); 489 } 490 491 public function mixedKeysChoiceListAnswerProvider() 492 { 493 return [ 494 ['0', '0'], 495 ['No environment', '0'], 496 ['1', '1'], 497 ['env_2', 'env_2'], 498 [3, '3'], 499 ['My environment 1', '1'], 500 ]; 501 } 502 503 /** 504 * @dataProvider answerProvider 505 */ 506 public function testSelectChoiceFromChoiceList($providedAnswer, $expectedValue) 507 { 508 $possibleChoices = [ 509 'env_1' => 'My environment 1', 510 'env_2' => 'My environment', 511 'env_3' => 'My environment', 512 ]; 513 514 $dialog = new QuestionHelper(); 515 $helperSet = new HelperSet([new FormatterHelper()]); 516 $dialog->setHelperSet($helperSet); 517 518 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); 519 $question->setMaxAttempts(1); 520 $answer = $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream($providedAnswer."\n")), $this->createOutputInterface(), $question); 521 522 $this->assertSame($expectedValue, $answer); 523 } 524 525 public function testAmbiguousChoiceFromChoicelist() 526 { 527 $this->expectException('InvalidArgumentException'); 528 $this->expectExceptionMessage('The provided answer is ambiguous. Value should be one of "env_2" or "env_3".'); 529 $possibleChoices = [ 530 'env_1' => 'My first environment', 531 'env_2' => 'My environment', 532 'env_3' => 'My environment', 533 ]; 534 535 $dialog = new QuestionHelper(); 536 $helperSet = new HelperSet([new FormatterHelper()]); 537 $dialog->setHelperSet($helperSet); 538 539 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); 540 $question->setMaxAttempts(1); 541 542 $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("My environment\n")), $this->createOutputInterface(), $question); 543 } 544 545 public function answerProvider() 546 { 547 return [ 548 ['env_1', 'env_1'], 549 ['env_2', 'env_2'], 550 ['env_3', 'env_3'], 551 ['My environment 1', 'env_1'], 552 ]; 553 } 554 555 public function testNoInteraction() 556 { 557 $dialog = new QuestionHelper(); 558 $question = new Question('Do you have a job?', 'not yet'); 559 $this->assertEquals('not yet', $dialog->ask($this->createStreamableInputInterfaceMock(null, false), $this->createOutputInterface(), $question)); 560 } 561 562 /** 563 * @requires function mb_strwidth 564 */ 565 public function testChoiceOutputFormattingQuestionForUtf8Keys() 566 { 567 $question = 'Lorem ipsum?'; 568 $possibleChoices = [ 569 'foo' => 'foo', 570 'żółw' => 'bar', 571 'łabądź' => 'baz', 572 ]; 573 $outputShown = [ 574 $question, 575 ' [<info>foo </info>] foo', 576 ' [<info>żółw </info>] bar', 577 ' [<info>łabądź</info>] baz', 578 ]; 579 $output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock(); 580 $output->method('getFormatter')->willReturn(new OutputFormatter()); 581 582 $dialog = new QuestionHelper(); 583 $helperSet = new HelperSet([new FormatterHelper()]); 584 $dialog->setHelperSet($helperSet); 585 586 $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown)); 587 588 $question = new ChoiceQuestion($question, $possibleChoices, 'foo'); 589 $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream("\n")), $output, $question); 590 } 591 592 /** 593 * @group legacy 594 */ 595 public function testLegacyAskChoice() 596 { 597 $questionHelper = new QuestionHelper(); 598 599 $helperSet = new HelperSet([new FormatterHelper()]); 600 $questionHelper->setHelperSet($helperSet); 601 602 $heroes = ['Superman', 'Batman', 'Spiderman']; 603 604 $questionHelper->setInputStream($this->getInputStream("\n1\n 1 \nFabien\n1\nFabien\n1\n0,2\n 0 , 2 \n\n\n")); 605 606 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2'); 607 $question->setMaxAttempts(1); 608 // first answer is an empty answer, we're supposed to receive the default value 609 $this->assertEquals('Spiderman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 610 611 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); 612 $question->setMaxAttempts(1); 613 $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 614 $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 615 616 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes); 617 $question->setErrorMessage('Input "%s" is not a superhero!'); 618 $question->setMaxAttempts(2); 619 $this->assertEquals('Batman', $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); 620 621 rewind($output->getStream()); 622 $stream = stream_get_contents($output->getStream()); 623 $this->assertStringContainsString('Input "Fabien" is not a superhero!', $stream); 624 625 try { 626 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1'); 627 $question->setMaxAttempts(1); 628 $questionHelper->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question); 629 $this->fail(); 630 } catch (\InvalidArgumentException $e) { 631 $this->assertEquals('Value "Fabien" is invalid', $e->getMessage()); 632 } 633 634 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null); 635 $question->setMaxAttempts(1); 636 $question->setMultiselect(true); 637 638 $this->assertEquals(['Batman'], $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 639 $this->assertEquals(['Superman', 'Spiderman'], $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 640 $this->assertEquals(['Superman', 'Spiderman'], $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 641 642 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1'); 643 $question->setMaxAttempts(1); 644 $question->setMultiselect(true); 645 646 $this->assertEquals(['Superman', 'Batman'], $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 647 648 $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 '); 649 $question->setMaxAttempts(1); 650 $question->setMultiselect(true); 651 652 $this->assertEquals(['Superman', 'Batman'], $questionHelper->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 653 } 654 655 /** 656 * @group legacy 657 */ 658 public function testLegacyAsk() 659 { 660 $dialog = new QuestionHelper(); 661 662 $dialog->setInputStream($this->getInputStream("\n8AM\n")); 663 664 $question = new Question('What time is it?', '2PM'); 665 $this->assertEquals('2PM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 666 667 $question = new Question('What time is it?', '2PM'); 668 $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $output = $this->createOutputInterface(), $question)); 669 670 rewind($output->getStream()); 671 $this->assertEquals('What time is it?', stream_get_contents($output->getStream())); 672 } 673 674 /** 675 * @group legacy 676 */ 677 public function testLegacyAskWithAutocomplete() 678 { 679 if (!Terminal::hasSttyAvailable()) { 680 $this->markTestSkipped('`stty` is required to test autocomplete functionality'); 681 } 682 683 // Acm<NEWLINE> 684 // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE> 685 // <NEWLINE> 686 // <UP ARROW><UP ARROW><UP ARROW><NEWLINE> 687 // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE> 688 // <DOWN ARROW><NEWLINE> 689 // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE> 690 // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE> 691 // F⭐<TAB><BACKSPACE><BACKSPACE>⭐<TAB><NEWLINE> 692 $inputStream = $this->getInputStream("Acm\nAc\177\177s\tTest\n\n\033[A\033[A\033[A\n\033[A\033[A\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\nF⭐\t⭐\t\n"); 693 694 $dialog = new QuestionHelper(); 695 $dialog->setInputStream($inputStream); 696 $helperSet = new HelperSet([new FormatterHelper()]); 697 $dialog->setHelperSet($helperSet); 698 699 $question = new Question('Please select a bundle', 'FrameworkBundle'); 700 $question->setAutocompleterValues(['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle', 'F⭐Y']); 701 702 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 703 $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 704 $this->assertEquals('FrameworkBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 705 $this->assertEquals('SecurityBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 706 $this->assertEquals('FooBundleTest', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 707 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 708 $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 709 $this->assertEquals('FooBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 710 $this->assertEquals('F⭐Y', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 711 } 712 713 /** 714 * @group legacy 715 */ 716 public function testLegacyAskWithAutocompleteWithNonSequentialKeys() 717 { 718 if (!Terminal::hasSttyAvailable()) { 719 $this->markTestSkipped('`stty` is required to test autocomplete functionality'); 720 } 721 722 // <UP ARROW><UP ARROW><NEWLINE><DOWN ARROW><DOWN ARROW><NEWLINE> 723 $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); 724 725 $dialog = new QuestionHelper(); 726 $dialog->setInputStream($inputStream); 727 $dialog->setHelperSet(new HelperSet([new FormatterHelper()])); 728 729 $question = new ChoiceQuestion('Please select a bundle', [1 => 'AcmeDemoBundle', 4 => 'AsseticBundle']); 730 $question->setMaxAttempts(1); 731 732 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 733 $this->assertEquals('AsseticBundle', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 734 } 735 736 /** 737 * @group legacy 738 */ 739 public function testLegacyAskHiddenResponse() 740 { 741 if ('\\' === \DIRECTORY_SEPARATOR) { 742 $this->markTestSkipped('This test is not supported on Windows'); 743 } 744 745 $dialog = new QuestionHelper(); 746 $dialog->setInputStream($this->getInputStream("8AM\n")); 747 748 $question = new Question('What time is it?'); 749 $question->setHidden(true); 750 751 $this->assertEquals('8AM', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 752 } 753 754 /** 755 * @group legacy 756 * @dataProvider getAskConfirmationData 757 */ 758 public function testLegacyAskConfirmation($question, $expected, $default = true) 759 { 760 $dialog = new QuestionHelper(); 761 762 $dialog->setInputStream($this->getInputStream($question."\n")); 763 $question = new ConfirmationQuestion('Do you like French fries?', $default); 764 $this->assertEquals($expected, $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question), 'confirmation question should '.($expected ? 'pass' : 'cancel')); 765 } 766 767 /** 768 * @group legacy 769 */ 770 public function testLegacyAskConfirmationWithCustomTrueAnswer() 771 { 772 $dialog = new QuestionHelper(); 773 774 $dialog->setInputStream($this->getInputStream("j\ny\n")); 775 $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); 776 $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 777 $question = new ConfirmationQuestion('Do you like French fries?', false, '/^(j|y)/i'); 778 $this->assertTrue($dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 779 } 780 781 /** 782 * @group legacy 783 */ 784 public function testLegacyAskAndValidate() 785 { 786 $dialog = new QuestionHelper(); 787 $helperSet = new HelperSet([new FormatterHelper()]); 788 $dialog->setHelperSet($helperSet); 789 790 $error = 'This is not a color!'; 791 $validator = function ($color) use ($error) { 792 if (!\in_array($color, ['white', 'black'])) { 793 throw new \InvalidArgumentException($error); 794 } 795 796 return $color; 797 }; 798 799 $question = new Question('What color was the white horse of Henry IV?', 'white'); 800 $question->setValidator($validator); 801 $question->setMaxAttempts(2); 802 803 $dialog->setInputStream($this->getInputStream("\nblack\n")); 804 $this->assertEquals('white', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 805 $this->assertEquals('black', $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question)); 806 807 $dialog->setInputStream($this->getInputStream("green\nyellow\norange\n")); 808 try { 809 $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); 810 $this->fail(); 811 } catch (\InvalidArgumentException $e) { 812 $this->assertEquals($error, $e->getMessage()); 813 } 814 } 815 816 /** 817 * @group legacy 818 * @dataProvider simpleAnswerProvider 819 */ 820 public function testLegacySelectChoiceFromSimpleChoices($providedAnswer, $expectedValue) 821 { 822 $possibleChoices = [ 823 'My environment 1', 824 'My environment 2', 825 'My environment 3', 826 ]; 827 828 $dialog = new QuestionHelper(); 829 $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); 830 $helperSet = new HelperSet([new FormatterHelper()]); 831 $dialog->setHelperSet($helperSet); 832 833 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); 834 $question->setMaxAttempts(1); 835 $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); 836 837 $this->assertSame($expectedValue, $answer); 838 } 839 840 /** 841 * @group legacy 842 * @dataProvider mixedKeysChoiceListAnswerProvider 843 */ 844 public function testLegacyChoiceFromChoicelistWithMixedKeys($providedAnswer, $expectedValue) 845 { 846 $possibleChoices = [ 847 '0' => 'No environment', 848 '1' => 'My environment 1', 849 'env_2' => 'My environment 2', 850 3 => 'My environment 3', 851 ]; 852 853 $dialog = new QuestionHelper(); 854 $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); 855 $helperSet = new HelperSet([new FormatterHelper()]); 856 $dialog->setHelperSet($helperSet); 857 858 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); 859 $question->setMaxAttempts(1); 860 $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); 861 862 $this->assertSame($expectedValue, $answer); 863 } 864 865 /** 866 * @group legacy 867 * @dataProvider answerProvider 868 */ 869 public function testLegacySelectChoiceFromChoiceList($providedAnswer, $expectedValue) 870 { 871 $possibleChoices = [ 872 'env_1' => 'My environment 1', 873 'env_2' => 'My environment', 874 'env_3' => 'My environment', 875 ]; 876 877 $dialog = new QuestionHelper(); 878 $dialog->setInputStream($this->getInputStream($providedAnswer."\n")); 879 $helperSet = new HelperSet([new FormatterHelper()]); 880 $dialog->setHelperSet($helperSet); 881 882 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); 883 $question->setMaxAttempts(1); 884 $answer = $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); 885 886 $this->assertSame($expectedValue, $answer); 887 } 888 889 /** 890 * @group legacy 891 */ 892 public function testLegacyAmbiguousChoiceFromChoicelist() 893 { 894 $this->expectException('InvalidArgumentException'); 895 $this->expectExceptionMessage('The provided answer is ambiguous. Value should be one of "env_2" or "env_3".'); 896 $possibleChoices = [ 897 'env_1' => 'My first environment', 898 'env_2' => 'My environment', 899 'env_3' => 'My environment', 900 ]; 901 902 $dialog = new QuestionHelper(); 903 $dialog->setInputStream($this->getInputStream("My environment\n")); 904 $helperSet = new HelperSet([new FormatterHelper()]); 905 $dialog->setHelperSet($helperSet); 906 907 $question = new ChoiceQuestion('Please select the environment to load', $possibleChoices); 908 $question->setMaxAttempts(1); 909 910 $dialog->ask($this->createInputInterfaceMock(), $this->createOutputInterface(), $question); 911 } 912 913 /** 914 * @requires function mb_strwidth 915 * @group legacy 916 */ 917 public function testLegacyChoiceOutputFormattingQuestionForUtf8Keys() 918 { 919 $question = 'Lorem ipsum?'; 920 $possibleChoices = [ 921 'foo' => 'foo', 922 'żółw' => 'bar', 923 'łabądź' => 'baz', 924 ]; 925 $outputShown = [ 926 $question, 927 ' [<info>foo </info>] foo', 928 ' [<info>żółw </info>] bar', 929 ' [<info>łabądź</info>] baz', 930 ]; 931 $output = $this->getMockBuilder('\Symfony\Component\Console\Output\OutputInterface')->getMock(); 932 $output->method('getFormatter')->willReturn(new OutputFormatter()); 933 934 $dialog = new QuestionHelper(); 935 $dialog->setInputStream($this->getInputStream("\n")); 936 $helperSet = new HelperSet([new FormatterHelper()]); 937 $dialog->setHelperSet($helperSet); 938 939 $output->expects($this->once())->method('writeln')->with($this->equalTo($outputShown)); 940 941 $question = new ChoiceQuestion($question, $possibleChoices, 'foo'); 942 $dialog->ask($this->createInputInterfaceMock(), $output, $question); 943 } 944 945 public function testAskThrowsExceptionOnMissingInput() 946 { 947 $this->expectException('Symfony\Component\Console\Exception\RuntimeException'); 948 $this->expectExceptionMessage('Aborted.'); 949 $dialog = new QuestionHelper(); 950 $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new Question('What\'s your name?')); 951 } 952 953 public function testAskThrowsExceptionOnMissingInputForChoiceQuestion() 954 { 955 $this->expectException('Symfony\Component\Console\Exception\RuntimeException'); 956 $this->expectExceptionMessage('Aborted.'); 957 $dialog = new QuestionHelper(); 958 $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new ChoiceQuestion('Choice', ['a', 'b'])); 959 } 960 961 public function testAskThrowsExceptionOnMissingInputWithValidator() 962 { 963 $this->expectException('Symfony\Component\Console\Exception\RuntimeException'); 964 $this->expectExceptionMessage('Aborted.'); 965 $dialog = new QuestionHelper(); 966 967 $question = new Question('What\'s your name?'); 968 $question->setValidator(function ($value) { 969 if (!$value) { 970 throw new \Exception('A value is required.'); 971 } 972 }); 973 974 $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), $question); 975 } 976 977 public function testEmptyChoices() 978 { 979 $this->expectException('LogicException'); 980 $this->expectExceptionMessage('Choice question must have at least 1 choice available.'); 981 new ChoiceQuestion('Question', [], 'irrelevant'); 982 } 983 984 public function testTraversableAutocomplete() 985 { 986 if (!Terminal::hasSttyAvailable()) { 987 $this->markTestSkipped('`stty` is required to test autocomplete functionality'); 988 } 989 990 // Acm<NEWLINE> 991 // Ac<BACKSPACE><BACKSPACE>s<TAB>Test<NEWLINE> 992 // <NEWLINE> 993 // <UP ARROW><UP ARROW><NEWLINE> 994 // <UP ARROW><UP ARROW><UP ARROW><UP ARROW><UP ARROW><TAB>Test<NEWLINE> 995 // <DOWN ARROW><NEWLINE> 996 // S<BACKSPACE><BACKSPACE><DOWN ARROW><DOWN ARROW><NEWLINE> 997 // F00<BACKSPACE><BACKSPACE>oo<TAB><NEWLINE> 998 $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"); 999 1000 $dialog = new QuestionHelper(); 1001 $helperSet = new HelperSet([new FormatterHelper()]); 1002 $dialog->setHelperSet($helperSet); 1003 1004 $question = new Question('Please select a bundle', 'FrameworkBundle'); 1005 $question->setAutocompleterValues(new AutocompleteValues(['irrelevant' => 'AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'])); 1006 1007 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1008 $this->assertEquals('AsseticBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1009 $this->assertEquals('FrameworkBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1010 $this->assertEquals('SecurityBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1011 $this->assertEquals('FooBundleTest', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1012 $this->assertEquals('AcmeDemoBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1013 $this->assertEquals('AsseticBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1014 $this->assertEquals('FooBundle', $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1015 } 1016 1017 public function testDisableStty() 1018 { 1019 if (!Terminal::hasSttyAvailable()) { 1020 $this->markTestSkipped('`stty` is required to test autocomplete functionality'); 1021 } 1022 1023 $this->expectException(InvalidArgumentException::class); 1024 $this->expectExceptionMessage('invalid'); 1025 1026 QuestionHelper::disableStty(); 1027 $dialog = new QuestionHelper(); 1028 $dialog->setHelperSet(new HelperSet([new FormatterHelper()])); 1029 1030 $question = new ChoiceQuestion('Please select a bundle', [1 => 'AcmeDemoBundle', 4 => 'AsseticBundle']); 1031 $question->setMaxAttempts(1); 1032 1033 // <UP ARROW><UP ARROW><NEWLINE><DOWN ARROW><DOWN ARROW><NEWLINE> 1034 // Gives `AcmeDemoBundle` with stty 1035 $inputStream = $this->getInputStream("\033[A\033[A\n\033[B\033[B\n"); 1036 1037 try { 1038 $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question); 1039 } finally { 1040 $reflection = new \ReflectionProperty(QuestionHelper::class, 'stty'); 1041 $reflection->setAccessible(true); 1042 $reflection->setValue(null, true); 1043 } 1044 } 1045 1046 public function testTraversableMultiselectAutocomplete() 1047 { 1048 // <NEWLINE> 1049 // F<TAB><NEWLINE> 1050 // A<3x UP ARROW><TAB>,F<TAB><NEWLINE> 1051 // F00<BACKSPACE><BACKSPACE>o<TAB>,A<DOWN ARROW>,<SPACE>SecurityBundle<NEWLINE> 1052 // Acme<TAB>,<SPACE>As<TAB><29x BACKSPACE>S<TAB><NEWLINE> 1053 // Ac<TAB>,As<TAB><3x BACKSPACE>d<TAB><NEWLINE> 1054 $inputStream = $this->getInputStream("\nF\t\nA\033[A\033[A\033[A\t,F\t\nF00\177\177o\t,A\033[B\t, SecurityBundle\nAcme\t, As\t\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177\177S\t\nAc\t,As\t\177\177\177d\t\n"); 1055 1056 $dialog = new QuestionHelper(); 1057 $helperSet = new HelperSet([new FormatterHelper()]); 1058 $dialog->setHelperSet($helperSet); 1059 1060 $question = new ChoiceQuestion( 1061 'Please select a bundle (defaults to AcmeDemoBundle and AsseticBundle)', 1062 ['AcmeDemoBundle', 'AsseticBundle', 'SecurityBundle', 'FooBundle'], 1063 '0,1' 1064 ); 1065 1066 // This tests that autocomplete works for all multiselect choices entered by the user 1067 $question->setMultiselect(true); 1068 1069 $this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1070 $this->assertEquals(['FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1071 $this->assertEquals(['AsseticBundle', 'FooBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1072 $this->assertEquals(['FooBundle', 'AsseticBundle', 'SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1073 $this->assertEquals(['SecurityBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1074 $this->assertEquals(['AcmeDemoBundle', 'AsseticBundle'], $dialog->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question)); 1075 } 1076 1077 protected function getInputStream($input) 1078 { 1079 $stream = fopen('php://memory', 'r+', false); 1080 fwrite($stream, $input); 1081 rewind($stream); 1082 1083 return $stream; 1084 } 1085 1086 protected function createOutputInterface() 1087 { 1088 return new StreamOutput(fopen('php://memory', 'r+', false)); 1089 } 1090 1091 protected function createInputInterfaceMock($interactive = true) 1092 { 1093 $mock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock(); 1094 $mock->expects($this->any()) 1095 ->method('isInteractive') 1096 ->willReturn($interactive); 1097 1098 return $mock; 1099 } 1100} 1101 1102class AutocompleteValues implements \IteratorAggregate 1103{ 1104 private $values; 1105 1106 public function __construct(array $values) 1107 { 1108 $this->values = $values; 1109 } 1110 1111 public function getIterator() 1112 { 1113 return new \ArrayIterator($this->values); 1114 } 1115} 1116