Files
michaelschiemer/tests/Framework/Console/Helpers/TestScreenRenderer.php
Michael Schiemer 74d50a29cc fix(console): improve InteractiveMenu rendering with layout-aware system
- Add LayoutAreas and LayoutArea value objects for coordinated screen rendering
- Add ScreenRendererInterface for testable screen operations
- Extend ScreenManager with clearContentArea() for selective clearing
- Refactor InteractiveMenu to support LayoutAreas via setLayoutAreas()
- Add prepareScreen() method that handles both standalone and layout-aware modes
- Fix cursor positioning to prevent menu bar overwriting
- Add comprehensive tests for layout areas and rendering behavior

This fixes rendering issues where InteractiveMenu would overwrite the menu bar
and cause misalignment of menu items when used within TUI layouts.
2025-11-10 02:00:41 +01:00

143 lines
3.9 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Framework\Console\Helpers;
use App\Framework\Console\Screen\ScreenRendererInterface;
/**
* Test implementation of ScreenRendererInterface that captures all operations
* for assertions in tests.
*/
final class TestScreenRenderer implements ScreenRendererInterface
{
/** @var array<int, array{operation: string, line?: int, column?: int, data?: string}> */
public array $operations = [];
/** @var array<int, string> Line-based buffer for assertions */
public array $buffer = [];
private int $currentLine = 1;
private int $currentColumn = 1;
public function clear(): void
{
$this->operations[] = ['operation' => 'clear'];
$this->buffer = [];
$this->currentLine = 1;
$this->currentColumn = 1;
}
public function clearContentArea(int $startLine): void
{
$this->operations[] = ['operation' => 'clearContentArea', 'line' => $startLine];
// Clear buffer from startLine onwards
$this->buffer = array_slice($this->buffer, 0, $startLine - 1);
$this->currentLine = $startLine;
$this->currentColumn = 1;
}
public function position(int $line, int $column = 1): void
{
$this->operations[] = ['operation' => 'position', 'line' => $line, 'column' => $column];
$this->currentLine = $line;
$this->currentColumn = $column;
}
public function writeRaw(string $data): void
{
$this->operations[] = ['operation' => 'writeRaw', 'data' => $data];
// Handle ANSI escape sequences
if (str_starts_with($data, "\033[")) {
// This is an ANSI code, we might want to parse it
// For now, just record it
return;
}
// Handle regular text - append to current line in buffer
$lines = explode("\n", $data);
foreach ($lines as $index => $line) {
if ($index === 0) {
// First line: append to current line
if (!isset($this->buffer[$this->currentLine - 1])) {
$this->buffer[$this->currentLine - 1] = '';
}
$this->buffer[$this->currentLine - 1] .= $line;
$this->currentColumn += mb_strlen($line);
} else {
// Subsequent lines: new line
$this->currentLine++;
$this->currentColumn = 1;
$this->buffer[$this->currentLine - 1] = $line;
}
}
}
/**
* Get the content of a specific line (1-based)
*/
public function getLine(int $line): ?string
{
return $this->buffer[$line - 1] ?? null;
}
/**
* Get all lines as array
*/
public function getLines(): array
{
return $this->buffer;
}
/**
* Get all operations
*/
public function getOperations(): array
{
return $this->operations;
}
/**
* Clear all recorded operations and buffer
*/
public function reset(): void
{
$this->operations = [];
$this->buffer = [];
$this->currentLine = 1;
$this->currentColumn = 1;
}
/**
* Check if a clearContentArea operation was called with specific start line
*/
public function wasContentAreaCleared(int $startLine): bool
{
foreach ($this->operations as $op) {
if ($op['operation'] === 'clearContentArea' && ($op['line'] ?? 0) === $startLine) {
return true;
}
}
return false;
}
/**
* Check if position was set to specific line/column
*/
public function wasPositioned(int $line, int $column = 1): bool
{
foreach ($this->operations as $op) {
if ($op['operation'] === 'position'
&& ($op['line'] ?? 0) === $line
&& ($op['column'] ?? 1) === $column) {
return true;
}
}
return false;
}
}