1<?php
2
3namespace Symfony\Component\Console\Tests\Helper;
4
5use Symfony\Component\Console\Helper\FormatterHelper;
6use Symfony\Component\Console\Helper\HelperSet;
7use Symfony\Component\Console\Helper\SymfonyQuestionHelper;
8use Symfony\Component\Console\Output\StreamOutput;
9use Symfony\Component\Console\Question\ChoiceQuestion;
10use Symfony\Component\Console\Question\Question;
11
12/**
13 * @group tty
14 */
15class SymfonyQuestionHelperTest extends AbstractQuestionHelperTest
16{
17    public function testAskChoice()
18    {
19        $questionHelper = new SymfonyQuestionHelper();
20
21        $helperSet = new HelperSet([new FormatterHelper()]);
22        $questionHelper->setHelperSet($helperSet);
23
24        $heroes = ['Superman', 'Batman', 'Spiderman'];
25
26        $inputStream = $this->getInputStream("\n1\n  1  \nFabien\n1\nFabien\n1\n0,2\n 0 , 2  \n\n\n");
27
28        $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '2');
29        $question->setMaxAttempts(1);
30        // first answer is an empty answer, we're supposed to receive the default value
31        $this->assertEquals('Spiderman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
32        $this->assertOutputContains('What is your favorite superhero? [Spiderman]', $output);
33
34        $question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
35        $question->setMaxAttempts(1);
36        $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
37        $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $this->createOutputInterface(), $question));
38
39        $question = new ChoiceQuestion('What is your favorite superhero?', $heroes);
40        $question->setErrorMessage('Input "%s" is not a superhero!');
41        $question->setMaxAttempts(2);
42        $this->assertEquals('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
43        $this->assertOutputContains('Input "Fabien" is not a superhero!', $output);
44
45        try {
46            $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '1');
47            $question->setMaxAttempts(1);
48            $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question);
49            $this->fail();
50        } catch (\InvalidArgumentException $e) {
51            $this->assertEquals('Value "Fabien" is invalid', $e->getMessage());
52        }
53
54        $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, null);
55        $question->setMaxAttempts(1);
56        $question->setMultiselect(true);
57
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
62        $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, '0,1');
63        $question->setMaxAttempts(1);
64        $question->setMultiselect(true);
65
66        $this->assertEquals(['Superman', 'Batman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
67        $this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output);
68
69        $question = new ChoiceQuestion('What is your favorite superhero?', $heroes, ' 0 , 1 ');
70        $question->setMaxAttempts(1);
71        $question->setMultiselect(true);
72
73        $this->assertEquals(['Superman', 'Batman'], $questionHelper->ask($this->createStreamableInputInterfaceMock($inputStream), $output = $this->createOutputInterface(), $question));
74        $this->assertOutputContains('What is your favorite superhero? [Superman, Batman]', $output);
75    }
76
77    public function testAskChoiceWithChoiceValueAsDefault()
78    {
79        $questionHelper = new SymfonyQuestionHelper();
80        $helperSet = new HelperSet([new FormatterHelper()]);
81        $questionHelper->setHelperSet($helperSet);
82        $question = new ChoiceQuestion('What is your favorite superhero?', ['Superman', 'Batman', 'Spiderman'], 'Batman');
83        $question->setMaxAttempts(1);
84
85        $this->assertSame('Batman', $questionHelper->ask($this->createStreamableInputInterfaceMock($this->getInputStream("Batman\n")), $output = $this->createOutputInterface(), $question));
86        $this->assertOutputContains('What is your favorite superhero? [Batman]', $output);
87    }
88
89    public function testAskReturnsNullIfValidatorAllowsIt()
90    {
91        $questionHelper = new SymfonyQuestionHelper();
92        $question = new Question('What is your favorite superhero?');
93        $question->setValidator(function ($value) { return $value; });
94        $input = $this->createStreamableInputInterfaceMock($this->getInputStream("\n"));
95        $this->assertNull($questionHelper->ask($input, $this->createOutputInterface(), $question));
96    }
97
98    public function testAskEscapeDefaultValue()
99    {
100        $helper = new SymfonyQuestionHelper();
101        $input = $this->createStreamableInputInterfaceMock($this->getInputStream('\\'));
102        $helper->ask($input, $output = $this->createOutputInterface(), new Question('Can I have a backslash?', '\\'));
103
104        $this->assertOutputContains('Can I have a backslash? [\]', $output);
105    }
106
107    public function testAskEscapeAndFormatLabel()
108    {
109        $helper = new SymfonyQuestionHelper();
110        $input = $this->createStreamableInputInterfaceMock($this->getInputStream('Foo\\Bar'));
111        $helper->ask($input, $output = $this->createOutputInterface(), new Question('Do you want to use Foo\\Bar <comment>or</comment> Foo\\Baz\\?', 'Foo\\Baz'));
112
113        $this->assertOutputContains('Do you want to use Foo\\Bar or Foo\\Baz\\? [Foo\\Baz]:', $output);
114    }
115
116    public function testLabelTrailingBackslash()
117    {
118        $helper = new SymfonyQuestionHelper();
119        $input = $this->createStreamableInputInterfaceMock($this->getInputStream('sure'));
120        $helper->ask($input, $output = $this->createOutputInterface(), new Question('Question with a trailing \\'));
121
122        $this->assertOutputContains('Question with a trailing \\', $output);
123    }
124
125    public function testAskThrowsExceptionOnMissingInput()
126    {
127        $this->expectException('Symfony\Component\Console\Exception\RuntimeException');
128        $this->expectExceptionMessage('Aborted.');
129        $dialog = new SymfonyQuestionHelper();
130        $dialog->ask($this->createStreamableInputInterfaceMock($this->getInputStream('')), $this->createOutputInterface(), new Question('What\'s your name?'));
131    }
132
133    public function testChoiceQuestionPadding()
134    {
135        $choiceQuestion = new ChoiceQuestion('qqq', [
136            'foo' => 'foo',
137            'żółw' => 'bar',
138            'łabądź' => 'baz',
139        ]);
140
141        (new SymfonyQuestionHelper())->ask(
142            $this->createStreamableInputInterfaceMock($this->getInputStream("foo\n")),
143            $output = $this->createOutputInterface(),
144            $choiceQuestion
145        );
146
147        $this->assertOutputContains(<<<EOT
148 qqq:
149  [foo   ] foo
150  [żółw  ] bar
151  [łabądź] baz
152 >
153EOT
154        , $output, true);
155    }
156
157    public function testChoiceQuestionCustomPrompt()
158    {
159        $choiceQuestion = new ChoiceQuestion('qqq', ['foo']);
160        $choiceQuestion->setPrompt(' >ccc> ');
161
162        (new SymfonyQuestionHelper())->ask(
163            $this->createStreamableInputInterfaceMock($this->getInputStream("foo\n")),
164            $output = $this->createOutputInterface(),
165            $choiceQuestion
166        );
167
168        $this->assertOutputContains(<<<EOT
169 qqq:
170  [0] foo
171 >ccc>
172EOT
173        , $output, true);
174    }
175
176    protected function getInputStream($input)
177    {
178        $stream = fopen('php://memory', 'r+', false);
179        fwrite($stream, $input);
180        rewind($stream);
181
182        return $stream;
183    }
184
185    protected function createOutputInterface()
186    {
187        $output = new StreamOutput(fopen('php://memory', 'r+', false));
188        $output->setDecorated(false);
189
190        return $output;
191    }
192
193    protected function createInputInterfaceMock($interactive = true)
194    {
195        $mock = $this->getMockBuilder('Symfony\Component\Console\Input\InputInterface')->getMock();
196        $mock->expects($this->any())
197            ->method('isInteractive')
198            ->willReturn($interactive);
199
200        return $mock;
201    }
202
203    private function assertOutputContains($expected, StreamOutput $output, $normalize = false)
204    {
205        rewind($output->getStream());
206        $stream = stream_get_contents($output->getStream());
207
208        if ($normalize) {
209            $stream = str_replace(\PHP_EOL, "\n", $stream);
210        }
211
212        $this->assertStringContainsString($expected, $stream);
213    }
214}
215