QuestionHelperTest.php 48 KB

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