Files
archived-console/Tests/Command/CompleteCommandTest.php
Nicolas Grekas 87fb10facd Merge branch '6.4' into 7.4
* 6.4:
  [RateLimiter] Fix retryAfter when consuming exactly all remaining tokens in FixedWindow and TokenBucket
  [RateLimiter] Fix retryAfter value on last token consume (SlidingWindow)
  [RateLimiter] Fix reservations outside the second fixed window
  [Filesystem] makePathRelative with existing files, remove ending /
  [Config][Routing] Fix exclude option being ignored for non-glob and PSR-4 resources
  [Serializer][Validator] Fix propertyPath in ConstraintViolationListNormalizer with MetadataAwareNameConverter
  [Messenger][Amqp] Don't use retry routing key when sending to failure transport
  [Messenger] Fix re-sending failed messages to a different failure transport
  [DependencyInjection] Fix #[AsTaggedItem] discovery through multi-level decoration chains
  [DependencyInjection] Fix PriorityTaggedServiceTrait not discovering #[AsTaggedItem] on decorated services
  [Mailer] Clarify the purpose of SentMessage's "message id" concept
  [TwigBridge] Fix Bootstrap 4 form error layout
  [Form] Fix merging POST params and files when collection entries have mismatched indices
  [Validator] Fix type error for non-array items when Unique::fields is set
  [HttpKernel] Fix default locale ignored when Accept-Language has no enabled-locale match
  [FrameworkBundle] Make `ConfigDebugCommand` use its container to resolve env vars
  [Console] Fix various completion edge cases
  [Messenger][AmazonSqs] Add test for default queue_name when not set in DSN path or options
2026-02-25 17:50:00 +01:00

160 lines
6.7 KiB
PHP

<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Console\Tests\Command;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Command\CompleteCommand;
use Symfony\Component\Console\Completion\CompletionInput;
use Symfony\Component\Console\Completion\CompletionSuggestions;
use Symfony\Component\Console\Completion\Output\BashCompletionOutput;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Tester\CommandTester;
class CompleteCommandTest extends TestCase
{
private CompleteCommand $command;
private Application $application;
private CommandTester $tester;
protected function setUp(): void
{
$this->command = new CompleteCommand();
$this->application = new Application();
$this->application->addCommand(new CompleteCommandTest_HelloCommand());
$this->application->getDefinition()
->addOption(new InputOption('global-option', null, InputOption::VALUE_REQUIRED, suggestedValues: ['foo', 'bar', 'baz']));
$this->command->setApplication($this->application);
$this->tester = new CommandTester($this->command);
}
public function testRequiredShellOption()
{
$this->expectExceptionMessage('The "--shell" option must be set.');
$this->execute([]);
}
public function testUnsupportedShellOption()
{
$this->expectExceptionMessage('Shell completion is not supported for your shell: "unsupported" (supported: "bash", "fish", "zsh").');
$this->execute(['--shell' => 'unsupported']);
}
public function testAdditionalShellSupport()
{
$this->expectNotToPerformAssertions();
$this->command = new CompleteCommand(['supported' => BashCompletionOutput::class]);
$this->command->setApplication($this->application);
$this->tester = new CommandTester($this->command);
$this->execute(['--shell' => 'supported', '--current' => '1', '--input' => ['bin/console']]);
// verify that the default set of shells is still supported
$this->execute(['--shell' => 'bash', '--current' => '1', '--input' => ['bin/console']]);
}
#[DataProvider('provideInputAndCurrentOptionValues')]
public function testInputAndCurrentOptionValidation(array $input, ?string $exceptionMessage)
{
if ($exceptionMessage) {
$this->expectExceptionMessage($exceptionMessage);
}
$this->execute($input + ['--shell' => 'bash']);
if (!$exceptionMessage) {
$this->tester->assertCommandIsSuccessful();
}
}
public static function provideInputAndCurrentOptionValues()
{
yield [[], 'The "--current" option must be set and it must be an integer'];
yield [['--current' => 'a'], 'The "--current" option must be set and it must be an integer'];
yield [['--current' => '1', '--input' => ['bin/console']], null];
yield [['--current' => '2', '--input' => ['bin/console']], 'Current index is invalid, it must be the number of input tokens or one more.'];
yield [['--current' => '1', '--input' => ['bin/console', 'cache:clear']], null];
yield [['--current' => '2', '--input' => ['bin/console', 'cache:clear']], null];
}
#[DataProvider('provideCompleteCommandNameInputs')]
public function testCompleteCommandName(array $input, array $suggestions)
{
$this->execute(['--current' => '1', '--input' => $input]);
$this->assertEquals(implode("\n", $suggestions).\PHP_EOL, $this->tester->getDisplay());
}
public static function provideCompleteCommandNameInputs()
{
yield 'empty' => [['bin/console'], ['help', 'list', 'completion', 'hello', 'ahoy', 'h', 'ahah']];
yield 'partial' => [['bin/console', 'he'], ['help', 'list', 'completion', 'hello', 'ahoy', 'h', 'ahah']];
yield 'complete-shortcut-name' => [['bin/console', 'hell'], ['hello']];
yield 'complete-aliases' => [['bin/console', 'ah'], ['ahoy']];
yield 'short-alias-completes-to-name' => [['bin/console', 'h'], ['hello']];
yield 'ambiguous-of-same-command-completes-to-first-match' => [['bin/console', 'ah'], ['ahoy']];
}
#[DataProvider('provideCompleteCommandInputDefinitionInputs')]
public function testCompleteCommandInputDefinition(array $input, array $suggestions)
{
$this->execute(['--current' => '2', '--input' => $input]);
$this->assertEquals(implode("\n", $suggestions).\PHP_EOL, $this->tester->getDisplay());
}
public static function provideCompleteCommandInputDefinitionInputs()
{
yield 'definition' => [['bin/console', 'hello', '-'], ['--help', '--silent', '--quiet', '--verbose', '--version', '--ansi', '--no-ansi', '--no-interaction', '--global-option']];
yield 'custom' => [['bin/console', 'hello'], ['Fabien', 'Robin', 'Wouter']];
yield 'definition-aliased' => [['bin/console', 'ahoy', '-'], ['--help', '--silent', '--quiet', '--verbose', '--version', '--ansi', '--no-ansi', '--no-interaction', '--global-option']];
yield 'custom-aliased' => [['bin/console', 'ahoy'], ['Fabien', 'Robin', 'Wouter']];
yield 'custom-aliased-input' => [['bin/console', 'ahoy', 'Fa'], ['Fabien', 'Robin', 'Wouter']];
yield 'global-option-values' => [['bin/console', '--global-option'], ['foo', 'bar', 'baz']];
yield 'global-option-with-command-values' => [['bin/console', 'ahoy', '--global-option'], ['foo', 'bar', 'baz']];
}
private function execute(array $input)
{
// run in verbose mode to assert exceptions
$this->tester->execute($input ? ($input + ['--shell' => 'bash', '--api-version' => CompleteCommand::COMPLETION_API_VERSION]) : $input, ['verbosity' => OutputInterface::VERBOSITY_DEBUG]);
}
}
class CompleteCommandTest_HelloCommand extends Command
{
public function configure(): void
{
$this->setName('hello')
->setAliases(['ahoy', 'h', 'ahah'])
->setDescription('Hello test command')
->addArgument('name', InputArgument::REQUIRED)
;
}
public function complete(CompletionInput $input, CompletionSuggestions $suggestions): void
{
if ($input->mustSuggestArgumentValuesFor('name')) {
$suggestions->suggestValues(['Fabien', 'Robin', 'Wouter']);
return;
}
parent::complete($input, $suggestions);
}
}