diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7d9d61c..7eaf176 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -1,73 +1,36 @@ -name: "Continuous Integration" - -on: - pull_request: - push: - +name: CI +on: [ push, pull_request ] jobs: - phpunit: - name: "Unit Tests" - runs-on: ubuntu-latest + ci: + name: "CI" + runs-on: ${{ matrix.operating-system }} + strategy: + matrix: + operating-system: [ ubuntu-latest, windows-latest, macos-latest ] + php-version: [ 8.3 ] steps: - - name: "Checkout" - uses: actions/checkout@v3 - - - name: "Install PHP" - uses: shivammathur/setup-php@v2 - with: - coverage: "pcov" - php-version: "8.1" - ini-values: memory_limit=-1 - extensions: sodium, fileinfo, redis - - - name: "Install dependencies" - uses: ramsey/composer-install@v2 - with: - dependency-versions: "locked" - - - name: "Run PHPUnit" - run: composer test - psalm: - name: "Static Analysis" - runs-on: ubuntu-latest - steps: + # --------- Setup steps --------- - name: "Checkout" uses: actions/checkout@v3 - - name: "Install PHP" + - name: "Setup PHP" uses: shivammathur/setup-php@v2 with: - php-version: "8.1" + coverage: "pcov" + php-version: "${{ matrix.php-version }}" ini-values: memory_limit=-1 - extensions: sodium, fileinfo, redis + extensions: pcov, xdebug - name: "Install dependencies" uses: ramsey/composer-install@v2 - with: - dependency-versions: "locked" - - - name: "Run Psalm" - run: composer psalm - - phpcs: - name: "Code Style" - runs-on: ubuntu-latest - steps: - - name: "Checkout" - uses: actions/checkout@v3 - - name: "Install PHP" - uses: shivammathur/setup-php@v2 - with: - php-version: "8.1" - ini-values: memory_limit=-1 - extensions: sodium, fileinfo, redis + # --------- Run steps --------- + - name: "Unit Tests" + run: "composer test" - - name: "Install dependencies" - uses: ramsey/composer-install@v2 - with: - dependency-versions: "locked" + - name: "Coding Style" + run: "composer cs-fix" - - name: "Run PHPCS" - run: composer cs-check + - name: "Static code analysis" + run: "composer psalm" diff --git a/docs/README.md b/docs/README.md index dc523b6..170e0fe 100644 --- a/docs/README.md +++ b/docs/README.md @@ -157,14 +157,15 @@ if ($this->hasOption('queue')) { ## Writing output -All helper methods from the Symfony `SymfonyStyle` class are available in the command class. +The package provides a handy Console style that you can use to write output to the console: +Output style ```php -$this->title('Lorem ipsum dolor sit amet'); -$this->section('Adding a new user'); -$this->text('Lorem ipsum dolor sit amet, consectetur adipiscing elit.'); -//And so on... +$this->output->comment('This is a comment'); +$this->output->success('This is a success message'); +$this->output->error('This is an error message'); +$this->output->warning('This is an info message'); +$this->output->note('This is an info message'); +$this->output->info('This is an info message'); +$this->output->caution('This is a line message'); ``` - -You can find the full list of available methods in Symfony's -documentation: [Helper methods](https://symfony.com/doc/current/console/style.html#helper-methods). diff --git a/docs/output-style.png b/docs/output-style.png new file mode 100644 index 0000000..702a2c6 Binary files /dev/null and b/docs/output-style.png differ diff --git a/src/Command.php b/src/Command.php index fa0fcc2..eb8fa98 100644 --- a/src/Command.php +++ b/src/Command.php @@ -4,12 +4,12 @@ namespace Symblaze\Console; +use Symblaze\Console\IO\Helper\InputTrait; +use Symblaze\Console\IO\Helper\OutputTrait; +use Symblaze\Console\IO\Output; use Symfony\Component\Console\Command\Command as SymfonyCommand; -use Symfony\Component\Console\Helper\ProgressBar; -use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; /** * @psalm-api - This file is part of the symblaze/console package. @@ -18,16 +18,11 @@ */ abstract class Command extends SymfonyCommand { - protected InputInterface $input; - protected SymfonyStyle $output; + use InputTrait; + use OutputTrait; - private const VERBOSITY_MAP = [ - 'v' => OutputInterface::VERBOSITY_VERBOSE, - 'vv' => OutputInterface::VERBOSITY_VERY_VERBOSE, - 'vvv' => OutputInterface::VERBOSITY_DEBUG, - 'quiet' => OutputInterface::VERBOSITY_QUIET, - 'normal' => OutputInterface::VERBOSITY_NORMAL, - ]; + protected InputInterface $input; + protected Output $output; protected function configure(): void { @@ -40,185 +35,33 @@ protected function configure(): void public function run(InputInterface $input, OutputInterface $output): int { - $this->input = $input; - $this->output = StyleFactory::create($input, $output); + $this->setInput($input); + $this->setOutput(new Output($input, $output)); return parent::run($input, $output); } - /** - * Determine if the given argument is present. - */ - protected function hasArgument($name): bool - { - return $this->input->hasArgument($name) && ! is_null($this->argument($name)); - } - - /** - * Determine if the given option is present. - */ - protected function hasOption($name): bool - { - return $this->input->hasOption($name) && ! is_null($this->option($name)); - } - - protected function option(string $key): bool|array|string|null - { - return $this->input->getOption($key); - } - - protected function options(): array - { - return $this->input->getOptions(); - } - - /** - * Get the value of a command argument. - */ - protected function argument(string $key): bool|array|string|null - { - return $this->input->getArgument($key); - } - - /** - * Get all the arguments passed to the command. - */ - protected function arguments(): array - { - return $this->input->getArguments(); - } - - /** - * Writes a message to the output and adds a newline at the end. - */ - protected function line(string $message, ?string $style = null, string|int $verbosity = 'normal'): void - { - $styled = $style ? "<$style>$message" : $message; - - $this->output->writeln($styled, $this->parseVerbosity($verbosity)); - } - - protected function info(string|array $message): void - { - $this->output->info($message); - } - - protected function comment(string|array $message): void - { - $this->output->comment($message); - } - - protected function question(string|array $message): void - { - $this->line($message, 'question'); - } - - protected function error(string|array $message): void - { - $this->output->error($message); - } - - protected function warning(string|array $message): void - { - $this->output->warning($message); - } - - protected function success(string|array $message): void - { - $this->output->success($message); - } - - protected function title(string $message): void + public function getInput(): InputInterface { - $this->output->title($message); + return $this->input; } - protected function section(string $message): void + public function setInput(InputInterface $input): static { - $this->output->section($message); - } - - protected function text(string|array $message): void - { - $this->output->text($message); - } - - protected function listing(array $elements): void - { - $this->output->listing($elements); - } - - protected function table(array $headers, array $rows): void - { - $this->output->table($headers, $rows); - } - - protected function horizontalTable(array $headers, array $rows): void - { - $this->output->horizontalTable($headers, $rows); - } - - protected function definitionList(string|array|TableSeparator ...$list): void - { - $this->output->definitionList(...$list); - } - - protected function note(string|array $message): void - { - $this->output->note($message); - } - - protected function caution(string|array $message): void - { - $this->output->caution($message); - } - - protected function ask(string $question, ?string $default = null, ?callable $validator = null): mixed - { - return $this->output->ask($question, $default, $validator); - } - - protected function askHidden(string $question, ?callable $validator = null): mixed - { - return $this->output->askHidden($question, $validator); - } - - protected function confirm(string $question, bool $default = true): bool - { - return $this->output->confirm($question, $default); - } - - protected function choice(string $question, array $choices, mixed $default = null, bool $multiSelect = false): mixed - { - return $this->output->choice($question, $choices, $default, $multiSelect); - } - - protected function progressStart(int $max = 0): void - { - $this->output->progressStart($max); - } - - protected function progressAdvance(int $step = 1): void - { - $this->output->progressAdvance($step); - } + $this->input = $input; - protected function progressFinish(): void - { - $this->output->progressFinish(); + return $this; } - protected function createProgressBar(int $max = 0): ProgressBar + public function getOutput(): Output { - return $this->output->createProgressBar($max); + return $this->output; } - private function parseVerbosity(int|string $level): int + public function setOutput(Output $output): static { - if (is_int($level)) { - return $level; - } + $this->output = $output; - return self::VERBOSITY_MAP[$level] ?? OutputInterface::VERBOSITY_NORMAL; + return $this; } } diff --git a/src/IO/Helper/InputTrait.php b/src/IO/Helper/InputTrait.php new file mode 100644 index 0000000..3bc9d08 --- /dev/null +++ b/src/IO/Helper/InputTrait.php @@ -0,0 +1,55 @@ +input->getArgument($key); + } + + protected function option(string $key): bool|array|string|null + { + return $this->input->getOption($key); + } + + protected function options(): array + { + return $this->input->getOptions(); + } + + /** + * Determine if the given argument is present. + */ + protected function hasArgument($name): bool + { + return $this->input->hasArgument($name) && ! is_null($this->argument($name)); + } + + /** + * Get all the arguments passed to the command. + */ + protected function arguments(): array + { + return $this->input->getArguments(); + } + + /** + * Determine if the given option is present. + */ + protected function hasOption($name): bool + { + return $this->input->hasOption($name) && ! is_null($this->option($name)); + } +} diff --git a/src/IO/Helper/OutputTrait.php b/src/IO/Helper/OutputTrait.php new file mode 100644 index 0000000..2991bd4 --- /dev/null +++ b/src/IO/Helper/OutputTrait.php @@ -0,0 +1,141 @@ +output->horizontalTable($headers, $rows); + } + + protected function progressAdvance(int $step = 1): void + { + $this->output->progressAdvance($step); + } + + protected function error(string|array $message): void + { + $this->output->error($message); + } + + protected function progressFinish(): void + { + $this->output->progressFinish(); + } + + protected function title(string $message): void + { + $this->output->title($message); + } + + protected function confirm(string $question, bool $default = true): bool + { + return $this->output->confirm($question, $default); + } + + /** + * Writes a message to the output and adds a newline at the end. + */ + protected function line(string $message, ?string $style = null): void + { + $styled = $style ? "<$style>$message" : $message; + + $this->output->writeln($styled); + } + + protected function note(string|array $message): void + { + $this->output->note($message); + } + + protected function question(string|array $message): void + { + $this->line($message, 'question'); + } + + protected function definitionList(string|array|TableSeparator ...$list): void + { + $this->output->definitionList(...$list); + } + + protected function table(array $headers, array $rows): void + { + $this->output->table($headers, $rows); + } + + protected function progressStart(int $max = 0): void + { + $this->output->progressStart($max); + } + + protected function warning(string|array $message): void + { + $this->output->warning($message); + } + + protected function createProgressBar(int $max = 0): ProgressBar + { + return $this->output->createProgressBar($max); + } + + protected function comment(string|array $message): void + { + $this->output->comment($message); + } + + protected function info(string|array $message): void + { + $this->output->info($message); + } + + protected function text(string|array $message): void + { + $this->output->text($message); + } + + protected function success(string|array $message): void + { + $this->output->success($message); + } + + protected function askHidden(string $question, ?callable $validator = null): mixed + { + return $this->output->askHidden($question, $validator); + } + + protected function choice(string $question, array $choices, mixed $default = null, bool $multiSelect = false): mixed + { + return $this->output->choice($question, $choices, $default, $multiSelect); + } + + protected function listing(array $elements): void + { + $this->output->listing($elements); + } + + protected function caution(string|array $message): void + { + $this->output->caution($message); + } + + protected function section(string $message): void + { + $this->output->section($message); + } + + protected function ask(string $question, ?string $default = null, ?callable $validator = null): mixed + { + return $this->output->ask($question, $default, $validator); + } +} diff --git a/src/IO/Output.php b/src/IO/Output.php new file mode 100644 index 0000000..4e7087e --- /dev/null +++ b/src/IO/Output.php @@ -0,0 +1,98 @@ +getFormatter())) { + $output = new NullOutput(); + } + + parent::__construct($input, $output); + + $this->bufferedOutput = new TrimmedBufferOutput( + DIRECTORY_SEPARATOR === '\\' ? 4 : 2, + $output->getVerbosity(), + false, + clone $output->getFormatter() + ); + } + + public function listing(array $elements): void + { + $this->autoPrependText(); + + $elements = array_map(static fn ($element) => sprintf(' ➜ %s', $element), $elements); + + $this->writeln($elements); + $this->newLine(); + } + + public function comment(string|array $message): void + { + $this->write(sprintf('➜ %s', $message)); + $this->newLine(); + } + + public function success(string|array $message): void + { + $this->write(sprintf('✔ %s', $message)); + $this->newLine(); + } + + public function error(string|array $message): void + { + $this->write(sprintf('✘ %s', $message)); + $this->newLine(); + } + + public function warning(string|array $message): void + { + $this->write(sprintf('⚠ %s', $message)); + $this->newLine(); + } + + public function note(string|array $message): void + { + $this->write(sprintf('➜ %s', $message)); + $this->newLine(); + } + + public function info(string|array $message): void + { + $this->write(sprintf('ℹ %s', $message)); + $this->newLine(); + } + + public function caution(string|array $message): void + { + $this->write(sprintf('! %s', $message)); + $this->newLine(); + } + + private function autoPrependText(): void + { + $fetched = $this->bufferedOutput->fetch(); + // Prepend new line if last char isn't EOL: + if ($fetched && ! str_ends_with($fetched, "\n")) { + $this->newLine(); + } + } +} diff --git a/src/LegacySymfonyStyle.php b/src/LegacySymfonyStyle.php deleted file mode 100644 index 9070783..0000000 --- a/src/LegacySymfonyStyle.php +++ /dev/null @@ -1,21 +0,0 @@ -getFormatter()) ? - new LegacySymfonyStyle($input) : - new SymfonyStyle($input, $output); - } -} diff --git a/tests/CommandTest.php b/tests/CommandTest.php index cf5a9b9..9d653b5 100644 --- a/tests/CommandTest.php +++ b/tests/CommandTest.php @@ -4,11 +4,10 @@ namespace Symblaze\Console\Tests; -use Symfony\Component\Console\Helper\ProgressBar; -use Symfony\Component\Console\Helper\TableSeparator; +use PHPUnit\Framework\Attributes\Test; +use Symblaze\Console\IO\Output; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; final class CommandTest extends TestCase { @@ -197,409 +196,15 @@ public function get_all_options(): void $this->assertSame($expected, $command->options()); } - /** @test */ - public function write_message_with_line(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('writeln')->with('Hello world'); - - $command->line('Hello world'); - } - - /** @test */ - public function write_styled_message_with_line(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('writeln')->with('Hello world'); - - $command->line('Hello world', 'info'); - } - - /** @test */ - public function write_a_verbose_message_with_line(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('writeln')->with('Hello world', OutputInterface::VERBOSITY_DEBUG); - - $command->line('Hello world', null, 'vvv'); - } - - /** @test */ - public function write_an_info_message(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('info')->with('Hello world'); - - $command->info('Hello world'); - } - - /** @test */ - public function write_a_comment_message(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('comment')->with('Hello world'); - - $command->comment('Hello world'); - } - - /** @test */ - public function write_a_question(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('writeln')->with('Hello world'); - - $command->question('Hello world'); - } - - /** @test */ - public function write_an_error_message(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('error')->with('Hello world'); - - $command->error('Hello world'); - } - - /** @test */ - public function write_a_warn_message(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('warning')->with('Hello world'); - - $command->warning('Hello world'); - } - - /** @test */ - public function write_a_success_message(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('success')->with('Hello world'); - - $command->success('Hello world'); - } - - /** @test */ - public function write_a_title(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('title')->with('Hello world'); - - $command->title('Hello world'); - } - - /** @test */ - public function display_a_section(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('section')->with('Hello world'); - - $command->section('Hello world'); - } - - /** @test */ - public function display_a_single_text_message(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('text')->with('Hello world'); - - $command->text('Hello world'); - } - - /** @test */ - public function display_an_list_of_test_messages(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('text')->with(['Hello world', 'Hello world']); - - $command->text(['Hello world', 'Hello world']); - } - - /** @test */ - public function display_un_ordered_list(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('listing')->with(['Hello world', 'Hello world']); - - $command->listing(['Hello world', 'Hello world']); - } - - /** @test */ - public function display_a_table(): void - { - $headers = ['Header 1', 'Header 2']; - $rows = [ - ['Cell 1-1', 'Cell 1-2'], - ['Cell 2-1', 'Cell 2-2'], - ['Cell 3-1', 'Cell 3-2'], - ]; - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('table')->with($headers, $rows); - - $command->table($headers, $rows); - } - - /** @test */ - public function display_horizontal_table(): void - { - $headers = ['Header 1', 'Header 2']; - $rows = [ - ['Cell 1-1', 'Cell 1-2'], - ['Cell 2-1', 'Cell 2-2'], - ['Cell 3-1', 'Cell 3-2'], - ]; - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('horizontalTable')->with($headers, $rows); - - $command->horizontalTable($headers, $rows); - } - - /** @test */ - public function display_a_definition_list(): void - { - $list = [ - 'This is a title', - ['foo1' => 'bar1'], - ['foo2' => 'bar2'], - ['foo3' => 'bar3'], - new TableSeparator(), - 'This is another title', - ['foo4' => 'bar4'], - ]; - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('definitionList')->with($list); - - $command->definitionList($list); - } - - /** @test */ - public function display_a_note(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('note')->with('Hello world'); - - $command->note('Hello world'); - } - - /** @test */ - public function display_a_list_of_notes(): void - { - $notes = ['Hello world', 'Hello world']; - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('note')->with($notes); - - $command->note($notes); - } - - /** @test */ - public function display_a_caution(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('caution')->with('Hello world'); - - $command->caution('Hello world'); - } - - /** @test */ - public function display_a_list_of_cautions(): void - { - $cautions = ['Hello world', 'Hello world']; - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('caution')->with($cautions); - - $command->caution($cautions); - } - - /** @test */ - public function ask_the_user_to_provide_a_value(): void - { - $question = 'What is your name?'; - $default = 'John Doe'; - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('ask')->with($question, $default)->willReturn($default); - - $this->assertSame('John Doe', $command->ask($question, $default)); - } - - /** @test */ - public function ask_the_user_for_sensitive_data(): void - { - $question = 'What is your password?'; - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $outputMock = $this->createMock(SymfonyStyle::class); - $command->setOutput($outputMock); - - $outputMock->expects($this->once())->method('askHidden')->with($question)->willReturn('secret'); - - $this->assertSame('secret', $command->askHidden($question)); - } - - /** @test */ - public function ask_a_yes_or_no_question(): void - { - $question = 'Do you want to continue?'; - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $output = $this->createMock(SymfonyStyle::class); - $command->setOutput($output); - - $output->expects($this->once())->method('confirm')->with($question)->willReturn(true); - - $this->assertTrue($command->confirm($question)); - } - - /** @test */ - public function ask_a_question_whose_answer_is_constrained_to_a_given_list(): void - { - $question = 'Select the queue to analyze'; - $choices = ['queue1', 'queue2', 'queue3']; - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $output = $this->createMock(SymfonyStyle::class); - $command->setOutput($output); - - $output->expects($this->once())->method('choice')->with($question, $choices)->willReturn('queue1'); - - $this->assertSame('queue1', $command->choice($question, $choices)); - } - - /** @test */ - public function progress_bar_start(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $output = $this->createMock(SymfonyStyle::class); - $command->setOutput($output); - - $output->expects($this->once())->method('progressStart')->with(10); - - $command->progressStart(10); - } - - /** @test */ - public function progress_advance(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $output = $this->createMock(SymfonyStyle::class); - $command->setOutput($output); - - $output->expects($this->once())->method('progressAdvance')->with(10); - - $command->progressAdvance(10); - } - - /** @test */ - public function progress_finish(): void - { - $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $output = $this->createMock(SymfonyStyle::class); - $command->setOutput($output); - - $output->expects($this->once())->method('progressFinish'); - - $command->progressFinish(); - } - - /** @test */ - public function create_progress_bar(): void + #[Test] + public function run_sets_output_helper(): void { $command = new Doubles\MyCommand(); - $command->setInput($this->createMock(InputInterface::class)); - $output = $this->createMock(SymfonyStyle::class); - $command->setOutput($output); + $input = $this->createMock(InputInterface::class); + $output = $this->createMock(OutputInterface::class); - $output->expects($this->once())->method('createProgressBar')->with(10)->willReturn(new ProgressBar($output)); + $command->run($input, $output); - $command->createProgressBar(10); + $this->assertInstanceOf(Output::class, $command->getOutput()); } } diff --git a/tests/Doubles/MyCommand.php b/tests/Doubles/MyCommand.php index 4269407..33f6af1 100644 --- a/tests/Doubles/MyCommand.php +++ b/tests/Doubles/MyCommand.php @@ -6,10 +6,8 @@ use Symblaze\Console\Command; use Symfony\Component\Console\Attribute\AsCommand; -use Symfony\Component\Console\Helper\TableSeparator; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; /** * @method hasArgument(string $name): bool @@ -18,30 +16,6 @@ * @method hasOption(string $name): bool * @method option(string $key): bool|array|string|null * @method options(): array - * @method line(string $message, ?string $style = null, string|int $verbosity = 'normal'): void - * @method info(string|array $message): void - * @method comment(string|array $message): void - * @method question(string|array $message): void - * @method error(string|array $message): void - * @method warning(string|array $message): void - * @method success(string|array $message): void - * @method title(string $message): void - * @method section(string $message): void - * @method text(string|array $message): void - * @method listing(array $elements): void - * @method table(array $headers, array $rows): void - * @method horizontalTable(array $headers, array $rows): void - * @method definitionList(string|array|TableSeparator ...$list): void - * @method note(string|array $message): void - * @method caution(string|array $message): void - * @method ask(string $question, string $default = null, callable $validator = null): mixed - * @method askHidden(string $question, callable $validator = null): mixed - * @method confirm(string $question, bool $default = true): bool - * @method choice(string $question, array $choices, mixed $default = null, bool $multiSelect = false): mixed - * @method progressStart(int $max = 0): void - * @method progressAdvance(int $step = 1): void - * @method progressFinish(): void - * @method createProgressBar(int $max = 0): ProgressBar */ #[AsCommand(name: 'acme:command {required_argument} {optional_argument?} {argument_with_value=default} {--O|option} {--OWV|option_with_value=} {--OWDV|option_with_default=default}')] class MyCommand extends Command @@ -55,14 +29,4 @@ public function __call(string $name, array $arguments) { return parent::$name(...$arguments); } - - public function setOutput(SymfonyStyle $output): void - { - $this->output = $output; - } - - public function setInput(InputInterface $input): void - { - $this->input = $input; - } }