Skip to content

Commit

Permalink
[11.x] Fix prompting for missing array arguments on artisan command (#…
Browse files Browse the repository at this point in the history
…50850)

* [11.x] Fixed prompting for missing array arguments on artisan command

* fixed prompting for missing array arguments on artisan command

* fixed testing failure

fixed:tests/Console/ConsoleApplicationTest.php

* Update src/Illuminate/Console/Concerns/PromptsForMissingInput.php

Co-authored-by: Jess Archer <[email protected]>

* Maintain array type when prompting for missing array argument

---------

Co-authored-by: Jess Archer <[email protected]>
  • Loading branch information
macocci7 and jessarcher authored Apr 4, 2024
1 parent a98a17b commit 29acd21
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 6 deletions.
19 changes: 13 additions & 6 deletions src/Illuminate/Console/Concerns/PromptsForMissingInput.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use Closure;
use Illuminate\Contracts\Console\PromptsForMissingInput as PromptsForMissingInputContract;
use Illuminate\Support\Arr;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

Expand Down Expand Up @@ -37,25 +39,30 @@ protected function interact(InputInterface $input, OutputInterface $output)
protected function promptForMissingArguments(InputInterface $input, OutputInterface $output)
{
$prompted = collect($this->getDefinition()->getArguments())
->filter(fn ($argument) => $argument->isRequired() && is_null($input->getArgument($argument->getName())))
->filter(fn ($argument) => $argument->getName() !== 'command')
->each(function ($argument) use ($input) {
->reject(fn (InputArgument $argument) => $argument->getName() === 'command')
->filter(fn (InputArgument $argument) => $argument->isRequired() && match (true) {
$argument->isArray() => empty($input->getArgument($argument->getName())),
default => is_null($input->getArgument($argument->getName())),
})
->each(function (InputArgument $argument) use ($input) {
$label = $this->promptForMissingArgumentsUsing()[$argument->getName()] ??
'What is '.lcfirst($argument->getDescription() ?: ('the '.$argument->getName())).'?';

if ($label instanceof Closure) {
return $input->setArgument($argument->getName(), $label());
return $input->setArgument($argument->getName(), $argument->isArray() ? Arr::wrap($label()) : $label());
}

if (is_array($label)) {
[$label, $placeholder] = $label;
}

$input->setArgument($argument->getName(), text(
$answer = text(
label: $label,
placeholder: $placeholder ?? '',
validate: fn ($value) => empty($value) ? "The {$argument->getName()} is required." : null,
));
);

$input->setArgument($argument->getName(), $argument->isArray() ? [$answer] : $answer);
})
->isNotEmpty();

Expand Down
41 changes: 41 additions & 0 deletions tests/Console/ConsoleApplicationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Illuminate\Contracts\Foundation\Application as ApplicationContract;
use Illuminate\Events\Dispatcher as EventsDispatcher;
use Illuminate\Foundation\Application as FoundationApplication;
use Illuminate\Tests\Console\Fixtures\FakeCommandWithArrayInputPrompting;
use Illuminate\Tests\Console\Fixtures\FakeCommandWithInputPrompting;
use Mockery as m;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -160,6 +161,7 @@ public function testCommandInputPromptsWhenRequiredArgumentIsMissing()
$statusCode = $app->call('fake-command-for-testing');

$this->assertTrue($command->prompted);
$this->assertSame('foo', $command->argument('name'));
$this->assertSame(0, $statusCode);
}

Expand All @@ -178,6 +180,45 @@ public function testCommandInputDoesntPromptWhenRequiredArgumentIsPassed()
]);

$this->assertFalse($command->prompted);
$this->assertSame('foo', $command->argument('name'));
$this->assertSame(0, $statusCode);
}

public function testCommandInputPromptsWhenRequiredArgumentsAreMissing()
{
$app = new Application(
$laravel = new \Illuminate\Foundation\Application(__DIR__),
$events = m::mock(Dispatcher::class, ['dispatch' => null, 'fire' => null]),
'testing'
);

$app->addCommands([$command = new FakeCommandWithArrayInputPrompting()]);

$command->setLaravel($laravel);

$statusCode = $app->call('fake-command-for-testing-array');

$this->assertTrue($command->prompted);
$this->assertSame(['foo'], $command->argument('names'));
$this->assertSame(0, $statusCode);
}

public function testCommandInputDoesntPromptWhenRequiredArgumentsArePassed()
{
$app = new Application(
$app = new \Illuminate\Foundation\Application(__DIR__),
$events = m::mock(Dispatcher::class, ['dispatch' => null, 'fire' => null]),
'testing'
);

$app->addCommands([$command = new FakeCommandWithArrayInputPrompting()]);

$statusCode = $app->call('fake-command-for-testing-array', [
'names' => ['foo', 'bar', 'baz'],
]);

$this->assertFalse($command->prompted);
$this->assertSame(['foo', 'bar', 'baz'], $command->argument('names'));
$this->assertSame(0, $statusCode);
}

Expand Down
33 changes: 33 additions & 0 deletions tests/Console/Fixtures/FakeCommandWithArrayInputPrompting.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Illuminate\Tests\Console\Fixtures;

use Illuminate\Console\Command;
use Illuminate\Contracts\Console\PromptsForMissingInput;
use Laravel\Prompts\Prompt;
use Laravel\Prompts\TextPrompt;
use Symfony\Component\Console\Input\InputInterface;

class FakeCommandWithArrayInputPrompting extends Command implements PromptsForMissingInput
{
protected $signature = 'fake-command-for-testing-array {names* : An array argument}';

public $prompted = false;

protected function configurePrompts(InputInterface $input)
{
Prompt::interactive(true);
Prompt::fallbackWhen(true);

TextPrompt::fallbackUsing(function () {
$this->prompted = true;

return 'foo';
});
}

public function handle(): int
{
return self::SUCCESS;
}
}

0 comments on commit 29acd21

Please sign in to comment.