output = new ConsoleOutput(); }); it('parses simple arguments', function () { $input = new ConsoleInput(['arg1', 'arg2', 'arg3'], $this->output); expect($input->getArgument(0))->toBe('arg1'); expect($input->getArgument(1))->toBe('arg2'); expect($input->getArgument(2))->toBe('arg3'); }); it('returns default value for missing arguments', function () { $input = new ConsoleInput(['arg1'], $this->output); expect($input->getArgument(0))->toBe('arg1'); expect($input->getArgument(1, 'default'))->toBe('default'); expect($input->getArgument(2))->toBeNull(); }); it('parses long options with equals sign', function () { $input = new ConsoleInput(['--option=value', '--flag'], $this->output); expect($input->getOption('option'))->toBe('value'); expect($input->hasOption('flag'))->toBeTrue(); expect($input->getOption('flag'))->toBeTrue(); }); it('parses long options with space', function () { // Note: Simple parser doesn't support --option value, only --option=value // This test verifies the current behavior $input = new ConsoleInput(['--option', 'value'], $this->output); // Simple parser treats 'value' as a separate argument, not as option value expect($input->hasOption('option'))->toBeTrue(); expect($input->getArgument(0))->toBe('value'); }); it('parses short options', function () { $input = new ConsoleInput(['-f', '-o', 'value'], $this->output); expect($input->hasOption('f'))->toBeTrue(); // Simple parser treats 'value' as argument, not as option value expect($input->getArgument(0))->toBe('value'); }); it('parses mixed arguments and options', function () { $input = new ConsoleInput(['arg1', '--option=value', 'arg2', '-f'], $this->output); expect($input->getArgument(0))->toBe('arg1'); expect($input->getArgument(1))->toBe('arg2'); expect($input->getOption('option'))->toBe('value'); expect($input->hasOption('f'))->toBeTrue(); }); it('returns all arguments', function () { $input = new ConsoleInput(['arg1', 'arg2', 'arg3'], $this->output); $args = $input->getArguments(); expect($args)->toBe(['arg1', 'arg2', 'arg3']); }); it('returns all options', function () { $input = new ConsoleInput(['--opt1=val1', '--opt2', '-f'], $this->output); $options = $input->getOptions(); expect($options)->toHaveKey('opt1'); expect($options)->toHaveKey('opt2'); expect($options)->toHaveKey('f'); }); it('returns default value for missing options', function () { $input = new ConsoleInput([], $this->output); expect($input->getOption('missing', 'default'))->toBe('default'); expect($input->getOption('missing'))->toBeNull(); }); it('supports enhanced parsing with ArgumentParser', function () { $parser = ArgumentParser::create() ->requiredString('name') ->integer('age', required: false, default: 18) ->build(); $input = new ConsoleInput(['--name=John', '--age=25'], $this->output, $parser); expect($input->hasEnhancedParsing())->toBeTrue(); expect($input->getString('name'))->toBe('John'); expect($input->getInt('age'))->toBe(25); }); it('throws exception when accessing enhanced parsing without parser', function () { $input = new ConsoleInput(['--option=value'], $this->output); expect($input->hasEnhancedParsing())->toBeFalse(); expect(fn () => $input->getParsedArguments()) ->toThrow(\RuntimeException::class, 'Enhanced parsing not available'); }); it('validates required arguments with enhanced parsing', function () { $parser = ArgumentParser::create() ->requiredString('name') ->build(); $input = new ConsoleInput(['--name=John'], $this->output, $parser); expect($input->require('name'))->toBe('John'); }); it('throws exception for missing required arguments', function () { $parser = ArgumentParser::create() ->requiredString('name') ->build(); // Exception is thrown during parsing, not when calling require() expect(fn () => new ConsoleInput([], $this->output, $parser)) ->toThrow(\InvalidArgumentException::class, "Required argument 'name' is missing"); }); it('converts types correctly with enhanced parsing', function () { $parser = ArgumentParser::create() ->integer('count') ->addArgument(new \App\Framework\Console\ArgumentDefinition('active', ArgumentType::BOOLEAN)) ->addArgument(new \App\Framework\Console\ArgumentDefinition('items', ArgumentType::ARRAY)) ->build(); $input = new ConsoleInput([ '--count=42', '--active=true', '--items=item1,item2,item3' ], $this->output, $parser); expect($input->getInt('count'))->toBe(42); expect($input->getBool('active'))->toBeTrue(); expect($input->getArray('items'))->toBe(['item1', 'item2', 'item3']); }); it('handles kebab-case option names', function () { $input = new ConsoleInput(['--dry-run', '--output-file=test.txt'], $this->output); expect($input->hasOption('dry-run'))->toBeTrue(); expect($input->getOption('output-file'))->toBe('test.txt'); }); it('can set argument parser after construction', function () { $input = new ConsoleInput(['--name=John'], $this->output); $parser = ArgumentParser::create() ->optionalString('name') ->build(); $input->setArgumentParser($parser); expect($input->hasEnhancedParsing())->toBeTrue(); // Note: setArgumentParser re-parses, but needs the raw arguments // The simple parser already parsed --name=John, so we can verify it's available expect($input->getOption('name'))->toBe('John'); }); }); describe('ConsoleInput Interactive Methods', function () { it('has ask method that delegates to InteractivePrompter', function () { $output = new ConsoleOutput(); $input = new ConsoleInput([], $output); // Verify method exists - actual interactive behavior requires STDIN expect(method_exists($input, 'ask'))->toBeTrue(); }); it('has confirm method that delegates to InteractivePrompter', function () { $output = new ConsoleOutput(); $input = new ConsoleInput([], $output); // Verify method exists - actual interactive behavior requires STDIN expect(method_exists($input, 'confirm'))->toBeTrue(); }); it('has askPassword method that delegates to InteractivePrompter', function () { $output = new ConsoleOutput(); $input = new ConsoleInput([], $output); // Verify method exists - actual interactive behavior requires STDIN expect(method_exists($input, 'askPassword'))->toBeTrue(); }); });