diff --git a/composer.json b/composer.json index 14c507c4814f..06ac854f2f6c 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,7 @@ "fruitcake/php-cors": "^1.3", "guzzlehttp/guzzle": "^7.8", "guzzlehttp/uri-template": "^1.0", - "laravel/prompts": "^0.1.15", + "laravel/prompts": "^0.1.18", "laravel/serializable-closure": "^1.3", "league/commonmark": "^2.2.1", "league/flysystem": "^3.8.0", diff --git a/src/Illuminate/Console/Concerns/ConfiguresPrompts.php b/src/Illuminate/Console/Concerns/ConfiguresPrompts.php index 2f6a540894fc..cfe62fc36f62 100644 --- a/src/Illuminate/Console/Concerns/ConfiguresPrompts.php +++ b/src/Illuminate/Console/Concerns/ConfiguresPrompts.php @@ -12,6 +12,7 @@ use Laravel\Prompts\SearchPrompt; use Laravel\Prompts\SelectPrompt; use Laravel\Prompts\SuggestPrompt; +use Laravel\Prompts\TextareaPrompt; use Laravel\Prompts\TextPrompt; use stdClass; use Symfony\Component\Console\Input\InputInterface; @@ -40,6 +41,12 @@ protected function configurePrompts(InputInterface $input) $prompt->validate )); + TextareaPrompt::fallbackUsing(fn (TextareaPrompt $prompt) => $this->promptUntilValid( + fn () => $this->components->ask($prompt->label, $prompt->default ?: null, multiline: true) ?? '', + $prompt->required, + $prompt->validate + )); + PasswordPrompt::fallbackUsing(fn (PasswordPrompt $prompt) => $this->promptUntilValid( fn () => $this->components->secret($prompt->label) ?? '', $prompt->required, diff --git a/src/Illuminate/Console/View/Components/Ask.php b/src/Illuminate/Console/View/Components/Ask.php index 9d359b131efd..4540c9ff5f80 100644 --- a/src/Illuminate/Console/View/Components/Ask.php +++ b/src/Illuminate/Console/View/Components/Ask.php @@ -2,6 +2,8 @@ namespace Illuminate\Console\View\Components; +use Symfony\Component\Console\Question\Question; + class Ask extends Component { /** @@ -11,8 +13,13 @@ class Ask extends Component * @param string $default * @return mixed */ - public function render($question, $default = null) + public function render($question, $default = null, $multiline = false) { - return $this->usingQuestionHelper(fn () => $this->output->ask($question, $default)); + return $this->usingQuestionHelper( + fn () => $this->output->askQuestion( + (new Question($question, $default)) + ->setMultiline($multiline) + ) + ); } } diff --git a/tests/Integration/Console/PromptsAssertionTest.php b/tests/Integration/Console/PromptsAssertionTest.php new file mode 100644 index 000000000000..f585f2b05663 --- /dev/null +++ b/tests/Integration/Console/PromptsAssertionTest.php @@ -0,0 +1,165 @@ +<?php + +namespace Illuminate\Tests\Integration\Console; + +use Illuminate\Console\Command; +use Illuminate\Contracts\Console\Kernel; +use Orchestra\Testbench\TestCase; + +use function Laravel\Prompts\confirm; +use function Laravel\Prompts\multiselect; +use function Laravel\Prompts\password; +use function Laravel\Prompts\select; +use function Laravel\Prompts\text; +use function Laravel\Prompts\textarea; + +class PromptsAssertionTest extends TestCase +{ + public function testAssertionForTextPrompt() + { + $this->app[Kernel::class]->registerCommand( + new class extends Command + { + protected $signature = 'test:text'; + + public function handle() + { + $name = text('What is your name?', 'John'); + + $this->line($name); + } + } + ); + + $this + ->artisan('test:text') + ->expectsQuestion('What is your name?', 'Jane') + ->expectsOutput('Jane'); + } + + public function testAssertionForTextareaPrompt() + { + $this->app[Kernel::class]->registerCommand( + new class extends Command + { + protected $signature = 'test:textarea'; + + public function handle() + { + $name = textarea('What is your name?', 'John'); + + $this->line($name); + } + } + ); + + $this + ->artisan('test:textarea') + ->expectsQuestion('What is your name?', 'Jane') + ->expectsOutput('Jane'); + } + + public function testAssertionForPasswordPrompt() + { + $this->app[Kernel::class]->registerCommand( + new class extends Command + { + protected $signature = 'test:password'; + + public function handle() + { + $name = password('What is your password?'); + + $this->line($name); + } + } + ); + + $this + ->artisan('test:password') + ->expectsQuestion('What is your password?', 'secret') + ->expectsOutput('secret'); + } + + public function testAssertionForConfirmPrompt() + { + $this->app[Kernel::class]->registerCommand( + new class extends Command + { + protected $signature = 'test:confirm'; + + public function handle() + { + $confirmed = confirm('Is your name John?'); + + if ($confirmed) { + $this->line('Your name is John.'); + } else { + $this->line('Your name is not John.'); + } + } + } + ); + + $this + ->artisan('test:confirm') + ->expectsConfirmation('Is your name John?', 'no') + ->expectsOutput('Your name is not John.'); + + $this + ->artisan('test:confirm') + ->expectsConfirmation('Is your name John?', 'yes') + ->expectsOutput('Your name is John.'); + } + + public function testAssertionForSelectPrompt() + { + $this->app[Kernel::class]->registerCommand( + new class extends Command + { + protected $signature = 'test:select'; + + public function handle() + { + $name = select( + label: 'What is your name?', + options: ['John', 'Jane'] + ); + + $this->line("Your name is $name."); + } + } + ); + + $this + ->artisan('test:select') + ->expectsChoice('What is your name?', 'Jane', ['John', 'Jane']) + ->expectsOutput('Your name is Jane.'); + } + + public function testAssertionForRequiredMultiselectPrompt() + { + $this->app[Kernel::class]->registerCommand( + new class extends Command + { + protected $signature = 'test:multiselect'; + + public function handle() + { + $names = multiselect( + label: 'Which names do you like?', + options: ['John', 'Jane', 'Sally', 'Jack'], + required: true + ); + + $this->line(sprintf('You like %s.', implode(', ', $names))); + } + } + ); + + $this + ->artisan('test:multiselect') + ->expectsChoice('Which names do you like?', ['John', 'Jane'], ['John', 'Jane', 'Sally', 'Jack']) + ->expectsOutput('You like John, Jane.'); + } +}