Files
michaelschiemer/tests/Framework/Console/CommandListTest.php
Michael Schiemer 8f3c15ddbb fix(console): comprehensive TUI rendering fixes
- Fix Enter key detection: handle multiple Enter key formats (\n, \r, \r\n)
- Reduce flickering: lower render frequency from 60 FPS to 30 FPS
- Fix menu bar visibility: re-render menu bar after content to prevent overwriting
- Fix content positioning: explicit line positioning for categories and commands
- Fix line shifting: clear lines before writing, control newlines manually
- Limit visible items: prevent overflow with maxVisibleCategories/Commands
- Improve CPU usage: increase sleep interval when no events processed

This fixes:
- Enter key not working for selection
- Strong flickering of the application
- Menu bar not visible or being overwritten
- Top half of selection list not displayed
- Lines being shifted/misaligned
2025-11-10 11:06:07 +01:00

192 lines
6.8 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Framework\Console;
use App\Framework\Console\CommandList;
use App\Framework\Console\ConsoleCommand;
use App\Framework\Console\Exceptions\CommandNotFoundException;
use App\Framework\Console\Exceptions\DuplicateCommandException;
describe('CommandList', function () {
it('creates empty command list', function () {
$list = CommandList::empty();
expect($list->count())->toBe(0);
expect($list->isEmpty())->toBeTrue();
});
it('creates command list with commands', function () {
$command1 = new ConsoleCommand('test:command1', 'Description 1');
$command2 = new ConsoleCommand('test:command2', 'Description 2');
$list = new CommandList($command1, $command2);
expect($list->count())->toBe(2);
expect($list->has('test:command1'))->toBeTrue();
expect($list->has('test:command2'))->toBeTrue();
});
it('throws exception for duplicate command names', function () {
$command1 = new ConsoleCommand('test:command', 'Description 1');
$command2 = new ConsoleCommand('test:command', 'Description 2');
expect(fn () => new CommandList($command1, $command2))
->toThrow(DuplicateCommandException::class);
});
it('adds command to list', function () {
$command1 = new ConsoleCommand('test:command1', 'Description 1');
$command2 = new ConsoleCommand('test:command2', 'Description 2');
$list = CommandList::empty();
$list = $list->add($command1);
$list = $list->add($command2);
expect($list->count())->toBe(2);
expect($list->has('test:command1'))->toBeTrue();
expect($list->has('test:command2'))->toBeTrue();
});
it('throws exception when adding duplicate command', function () {
$command1 = new ConsoleCommand('test:command', 'Description 1');
$command2 = new ConsoleCommand('test:command', 'Description 2');
$list = new CommandList($command1);
expect(fn () => $list->add($command2))
->toThrow(DuplicateCommandException::class);
});
it('gets command by name', function () {
$command = new ConsoleCommand('test:command', 'Description');
$list = new CommandList($command);
$retrieved = $list->get('test:command');
expect($retrieved)->toBe($command);
expect($retrieved->name)->toBe('test:command');
expect($retrieved->description)->toBe('Description');
});
it('throws exception when getting non-existent command', function () {
$list = CommandList::empty();
expect(fn () => $list->get('nonexistent:command'))
->toThrow(CommandNotFoundException::class);
});
it('returns all command names', function () {
$command1 = new ConsoleCommand('test:command1', 'Description 1');
$command2 = new ConsoleCommand('test:command2', 'Description 2');
$command3 = new ConsoleCommand('test:command3', 'Description 3');
$list = new CommandList($command1, $command2, $command3);
$names = $list->getNames();
expect($names)->toHaveCount(3);
expect($names)->toContain('test:command1');
expect($names)->toContain('test:command2');
expect($names)->toContain('test:command3');
});
it('finds similar commands using Levenshtein distance', function () {
$command1 = new ConsoleCommand('test:hello', 'Description');
$command2 = new ConsoleCommand('test:help', 'Description');
$command3 = new ConsoleCommand('test:world', 'Description');
$command4 = new ConsoleCommand('test:hell', 'Description');
$list = new CommandList($command1, $command2, $command3, $command4);
// 'test:helo' is similar to 'test:hello' (distance 1)
$similar = $list->findSimilar('test:helo', 2);
expect($similar)->toContain('test:hello');
expect($similar)->toContain('test:help');
expect($similar)->toContain('test:hell');
});
it('returns empty array when no similar commands found', function () {
$command = new ConsoleCommand('test:command', 'Description');
$list = new CommandList($command);
$similar = $list->findSimilar('completely:different', 3);
expect($similar)->toBeEmpty();
});
it('respects max distance parameter', function () {
$command1 = new ConsoleCommand('test:hello', 'Description');
$command2 = new ConsoleCommand('test:world', 'Description');
$list = new CommandList($command1, $command2);
// 'test:helo' has distance 1 from 'test:hello', but distance 4 from 'test:world'
$similar = $list->findSimilar('test:helo', 1);
expect($similar)->toContain('test:hello');
expect($similar)->not->toContain('test:world');
});
it('does not include exact match in similar results', function () {
$command = new ConsoleCommand('test:command', 'Description');
$list = new CommandList($command);
$similar = $list->findSimilar('test:command', 3);
// Exact match should not be included (distance 0)
expect($similar)->toBeEmpty();
});
it('implements IteratorAggregate', function () {
$command1 = new ConsoleCommand('test:command1', 'Description 1');
$command2 = new ConsoleCommand('test:command2', 'Description 2');
$list = new CommandList($command1, $command2);
$commands = [];
foreach ($list as $name => $command) {
$commands[$name] = $command;
}
expect($commands)->toHaveCount(2);
expect($commands['test:command1'])->toBe($command1);
expect($commands['test:command2'])->toBe($command2);
});
it('implements Countable', function () {
$command1 = new ConsoleCommand('test:command1', 'Description 1');
$command2 = new ConsoleCommand('test:command2', 'Description 2');
$list = new CommandList($command1, $command2);
expect(count($list))->toBe(2);
});
it('converts to array', function () {
$command1 = new ConsoleCommand('test:command1', 'Description 1');
$command2 = new ConsoleCommand('test:command2', 'Description 2');
$list = new CommandList($command1, $command2);
$array = $list->toArray();
expect($array)->toHaveCount(2);
expect($array)->toHaveKey('test:command1');
expect($array)->toHaveKey('test:command2');
});
it('returns all commands as array', function () {
$command1 = new ConsoleCommand('test:command1', 'Description 1');
$command2 = new ConsoleCommand('test:command2', 'Description 2');
$list = new CommandList($command1, $command2);
$allCommands = $list->getAllCommands();
expect($allCommands)->toHaveCount(2);
expect($allCommands)->toContain($command1);
expect($allCommands)->toContain($command2);
});
});