- 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
192 lines
6.8 KiB
PHP
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);
|
|
});
|
|
});
|
|
|