output = new ConsoleOutput(); $this->menu = new InteractiveMenu($this->output); }); it('can be instantiated', function () { expect($this->menu)->toBeInstanceOf(InteractiveMenu::class); }); it('sets title', function () { $menu = $this->menu->setTitle('Test Menu'); expect($menu)->toBe($this->menu); // Fluent interface }); it('adds menu items', function () { $menu = $this->menu ->addItem('Option 1', null, 'value1') ->addItem('Option 2', null, 'value2'); expect($menu)->toBe($this->menu); }); it('adds menu items with callable actions', function () { $action = fn () => 'action result'; $menu = $this->menu->addItem('Option 1', $action); expect($menu)->toBe($this->menu); }); it('adds separators', function () { $menu = $this->menu ->addItem('Option 1') ->addSeparator() ->addItem('Option 2'); expect($menu)->toBe($this->menu); }); it('shows numbers by default', function () { $menu = $this->menu->showNumbers(true); expect($menu)->toBe($this->menu); }); it('hides numbers', function () { $menu = $this->menu->showNumbers(false); expect($menu)->toBe($this->menu); }); it('has showSimple method', function () { expect(method_exists($this->menu, 'showSimple'))->toBeTrue(); }); it('has showInteractive method', function () { expect(method_exists($this->menu, 'showInteractive'))->toBeTrue(); }); it('builds menu with fluent interface', function () { $menu = $this->menu ->setTitle('Test Menu') ->addItem('Option 1', null, 'val1') ->addSeparator() ->addItem('Option 2', null, 'val2') ->showNumbers(true); expect($menu)->toBe($this->menu); }); it('handles empty menu', function () { // Should not throw expect($this->menu)->toBeInstanceOf(InteractiveMenu::class); }); it('handles menu with only separators', function () { $menu = $this->menu ->addSeparator() ->addSeparator(); expect($menu)->toBe($this->menu); }); it('handles very long menu item labels', function () { $longLabel = str_repeat('A', 200); $menu = $this->menu->addItem($longLabel); expect($menu)->toBe($this->menu); }); it('handles menu with many items', function () { $menu = $this->menu; for ($i = 1; $i <= 100; $i++) { $menu = $menu->addItem("Option {$i}", null, "value{$i}"); } expect($menu)->toBe($this->menu); }); }); describe('InteractiveMenu Rendering', function () { beforeEach(function () { $this->output = new TestConsoleOutput(); $this->menu = new InteractiveMenu($this->output); }); it('renders menu title in showSimple', function () { $this->menu ->setTitle('Test Menu') ->addItem('Option 1'); // showSimple requires STDIN, so we can't fully test it // But we can verify the structure expect(method_exists($this->menu, 'showSimple'))->toBeTrue(); }); it('renders menu items in showSimple', function () { $this->menu ->addItem('Option 1') ->addItem('Option 2') ->addItem('Option 3'); // showSimple requires STDIN expect(method_exists($this->menu, 'showSimple'))->toBeTrue(); }); it('renders separators in showSimple', function () { $this->menu ->addItem('Option 1') ->addSeparator() ->addItem('Option 2'); // showSimple requires STDIN expect(method_exists($this->menu, 'showSimple'))->toBeTrue(); }); it('renders interactive menu', function () { $this->menu ->setTitle('Interactive Menu') ->addItem('Option 1') ->addItem('Option 2'); // showInteractive requires STDIN and terminal capabilities expect(method_exists($this->menu, 'showInteractive'))->toBeTrue(); }); });