output = new TestConsoleOutput(); $this->menu = new InteractiveMenu($this->output); }); it('renders menu in standalone mode (clears entire screen)', function () { $this->menu ->setTitle('Test Menu') ->addItem('Option 1') ->addItem('Option 2'); // Note: showSimple() reads from STDIN, so we can't fully test it // But we can verify the menu structure is correct expect($this->menu)->toBeInstanceOf(InteractiveMenu::class); // When layoutAreas is null, menu operates in standalone mode // This is the default behavior expect(true)->toBeTrue(); // Placeholder assertion }); it('uses layout areas to position content correctly', function () { $terminalSize = new TerminalSize(80, 24); $layoutAreas = LayoutAreas::forTui($terminalSize); $this->menu ->setLayoutAreas($layoutAreas) ->setTitle('Test Menu') ->addItem('Option 1') ->addItem('Option 2'); // The menu should use content area start line (4) for positioning $contentArea = $layoutAreas->contentArea(); expect($contentArea->startLine)->toBe(4); }); it('preserves menu bar when using layout areas', function () { $terminalSize = new TerminalSize(80, 24); $layoutAreas = LayoutAreas::forTui($terminalSize); $this->menu->setLayoutAreas($layoutAreas); // Menu bar should be lines 1-3 $menuBarArea = $layoutAreas->menuBarArea(); expect($menuBarArea)->not->toBeNull(); expect($menuBarArea->startLine)->toBe(1); expect($menuBarArea->endLine)->toBe(3); // Content should start at line 4 $contentArea = $layoutAreas->contentArea(); expect($contentArea->startLine)->toBe(4); }); it('correctly calculates content area when menu bar exists', function () { $terminalSize = new TerminalSize(80, 24); $layoutAreas = LayoutAreas::forTui($terminalSize); $contentArea = $layoutAreas->contentArea(); // Content should start after menu bar (line 4) expect($contentArea->startLine)->toBe(4); // Content should end at terminal height (no status area) expect($contentArea->endLine)->toBe(24); expect($contentArea->height)->toBe(21); // 24 - 4 + 1 }); it('handles standalone layout (no menu bar)', function () { $terminalSize = new TerminalSize(80, 24); $layoutAreas = LayoutAreas::standalone($terminalSize); $menuBarArea = $layoutAreas->menuBarArea(); expect($menuBarArea)->toBeNull(); $contentArea = $layoutAreas->contentArea(); expect($contentArea->startLine)->toBe(1); expect($contentArea->endLine)->toBe(24); }); it('validates layout areas constraints', function () { $terminalSize = new TerminalSize(80, 24); // Content must start after menu bar expect(fn() => LayoutAreas::create( menuBarStartLine: 1, menuBarEndLine: 3, contentStartLine: 2, // Too early! statusStartLine: 0, terminalSize: $terminalSize ))->toThrow(\InvalidArgumentException::class); }); it('allows custom layout areas', function () { $terminalSize = new TerminalSize(80, 24); $layoutAreas = LayoutAreas::create( menuBarStartLine: 1, menuBarEndLine: 2, contentStartLine: 3, statusStartLine: 22, terminalSize: $terminalSize ); $menuBarArea = $layoutAreas->menuBarArea(); expect($menuBarArea->startLine)->toBe(1); expect($menuBarArea->endLine)->toBe(2); $contentArea = $layoutAreas->contentArea(); expect($contentArea->startLine)->toBe(3); expect($contentArea->endLine)->toBe(21); // Before status area $statusArea = $layoutAreas->statusArea(); expect($statusArea)->not->toBeNull(); expect($statusArea->startLine)->toBe(22); expect($statusArea->endLine)->toBe(24); }); it('sets and retrieves layout areas correctly', function () { $terminalSize = new TerminalSize(80, 24); $layoutAreas = LayoutAreas::forTui($terminalSize); $this->menu->setLayoutAreas($layoutAreas); // We can't directly access private property, but we can verify // the menu works correctly with layout areas by checking behavior expect($layoutAreas->contentArea()->startLine)->toBe(4); }); });