container = new DefaultContainer(); $this->output = new TestConsoleOutput(); $this->discoveryRegistry = new DiscoveryRegistry( attributes: new AttributeRegistry(), interfaces: new InterfaceRegistry([]), templates: new TemplateRegistry([]) ); $this->commandHistory = new CommandHistory(); $this->groupRegistry = new CommandGroupRegistry($this->discoveryRegistry); $this->commandList = CommandList::empty(); $this->commandExecutor = new DialogCommandExecutor( $this->output, new \App\Framework\Console\CommandRegistry($this->container, $this->discoveryRegistry), $this->commandHistory, 'test-console' ); }); it('can be instantiated', function () { $dialog = new ConsoleDialog( $this->output, $this->discoveryRegistry, $this->commandHistory, $this->groupRegistry, $this->commandExecutor, $this->commandList, $this->container ); expect($dialog)->toBeInstanceOf(ConsoleDialog::class); }); it('can be instantiated with custom prompt', function () { $dialog = new ConsoleDialog( $this->output, $this->discoveryRegistry, $this->commandHistory, $this->groupRegistry, $this->commandExecutor, $this->commandList, $this->container, 'custom> ' ); expect($dialog)->toBeInstanceOf(ConsoleDialog::class); }); it('has run method', function () { $dialog = new ConsoleDialog( $this->output, $this->discoveryRegistry, $this->commandHistory, $this->groupRegistry, $this->commandExecutor, $this->commandList, $this->container ); expect(method_exists($dialog, 'run'))->toBeTrue(); }); it('has completeCommand method for readline', function () { $dialog = new ConsoleDialog( $this->output, $this->discoveryRegistry, $this->commandHistory, $this->groupRegistry, $this->commandExecutor, $this->commandList, $this->container ); expect(method_exists($dialog, 'completeCommand'))->toBeTrue(); }); it('detects readline availability', function () { $dialog = new ConsoleDialog( $this->output, $this->discoveryRegistry, $this->commandHistory, $this->groupRegistry, $this->commandExecutor, $this->commandList, $this->container ); // Should detect readline if available expect($dialog)->toBeInstanceOf(ConsoleDialog::class); }); }); describe('ConsoleDialog Input Parsing', function () { beforeEach(function () { $this->container = new DefaultContainer(); $this->output = new TestConsoleOutput(); $this->discoveryRegistry = new DiscoveryRegistry( attributes: new AttributeRegistry(), interfaces: new InterfaceRegistry([]), templates: new TemplateRegistry([]) ); $this->commandHistory = new CommandHistory(); $this->groupRegistry = new CommandGroupRegistry($this->discoveryRegistry); $this->commandList = CommandList::empty(); $this->commandExecutor = new DialogCommandExecutor( $this->output, new \App\Framework\Console\CommandRegistry($this->container, $this->discoveryRegistry), $this->commandHistory, 'test-console' ); $this->dialog = new ConsoleDialog( $this->output, $this->discoveryRegistry, $this->commandHistory, $this->groupRegistry, $this->commandExecutor, $this->commandList, $this->container ); }); it('parses simple command', function () { // Use reflection to access private parseInput method $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('parseInput'); $method->setAccessible(true); $result = $method->invoke($this->dialog, 'demo:hello'); expect($result)->toHaveKey('command'); expect($result)->toHaveKey('arguments'); expect($result['command'])->toBe('demo:hello'); expect($result['arguments'])->toBeEmpty(); }); it('parses command with arguments', function () { $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('parseInput'); $method->setAccessible(true); $result = $method->invoke($this->dialog, 'demo:hello arg1 arg2 arg3'); expect($result['command'])->toBe('demo:hello'); expect($result['arguments'])->toBe(['arg1', 'arg2', 'arg3']); }); it('parses command with quoted arguments', function () { $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('parseInput'); $method->setAccessible(true); $result = $method->invoke($this->dialog, 'command "arg with spaces"'); expect($result['command'])->toBe('command'); expect($result['arguments'])->toHaveCount(1); expect($result['arguments'][0])->toBe('arg with spaces'); }); it('parses command with single-quoted arguments', function () { $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('parseInput'); $method->setAccessible(true); $result = $method->invoke($this->dialog, "command 'arg with spaces'"); expect($result['command'])->toBe('command'); expect($result['arguments'])->toHaveCount(1); expect($result['arguments'][0])->toBe('arg with spaces'); }); it('parses empty input', function () { $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('parseInput'); $method->setAccessible(true); $result = $method->invoke($this->dialog, ''); expect($result['command'])->toBe(''); expect($result['arguments'])->toBeEmpty(); }); it('handles multiple spaces between arguments', function () { $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('parseInput'); $method->setAccessible(true); $result = $method->invoke($this->dialog, 'command arg1 arg2'); expect($result['command'])->toBe('command'); expect($result['arguments'])->toBe(['arg1', 'arg2']); }); it('handles escaped quotes in quoted strings', function () { $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('parseInput'); $method->setAccessible(true); $result = $method->invoke($this->dialog, 'command "arg with \\"quotes\\""'); expect($result['command'])->toBe('command'); expect($result['arguments'])->toHaveCount(1); }); }); describe('ConsoleDialog Command Suggestions', function () { beforeEach(function () { $this->container = new DefaultContainer(); $this->output = new TestConsoleOutput(); $this->discoveryRegistry = new DiscoveryRegistry( attributes: new AttributeRegistry(), interfaces: new InterfaceRegistry([]), templates: new TemplateRegistry([]) ); $this->commandHistory = new CommandHistory(); $this->groupRegistry = new CommandGroupRegistry($this->discoveryRegistry); $this->commandList = new CommandList( new ConsoleCommand('demo:hello', 'Hello command'), new ConsoleCommand('demo:colors', 'Colors command'), new ConsoleCommand('db:migrate', 'Migrate command') ); $this->commandExecutor = new DialogCommandExecutor( $this->output, new \App\Framework\Console\CommandRegistry($this->container, $this->discoveryRegistry), $this->commandHistory, 'test-console' ); $this->dialog = new ConsoleDialog( $this->output, $this->discoveryRegistry, $this->commandHistory, $this->groupRegistry, $this->commandExecutor, $this->commandList, $this->container ); }); it('gets command suggestions for partial match', function () { $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('getCommandSuggestions'); $method->setAccessible(true); $suggestions = $method->invoke($this->dialog, 'demo'); expect($suggestions)->toBeArray(); expect($suggestions)->toContain('demo:hello'); expect($suggestions)->toContain('demo:colors'); }); it('gets empty suggestions for no match', function () { $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('getCommandSuggestions'); $method->setAccessible(true); $suggestions = $method->invoke($this->dialog, 'nonexistent'); expect($suggestions)->toBeArray(); }); it('gets case-insensitive suggestions', function () { $reflection = new \ReflectionClass($this->dialog); $method = $reflection->getMethod('getCommandSuggestions'); $method->setAccessible(true); $suggestions = $method->invoke($this->dialog, 'DEMO'); expect($suggestions)->toBeArray(); }); });