- 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
218 lines
6.4 KiB
PHP
218 lines
6.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace Tests\Framework\Console;
|
|
|
|
use App\Framework\Console\ProgressBar;
|
|
use Tests\Framework\Console\Helpers\TestConsoleOutput;
|
|
|
|
describe('ProgressBar', function () {
|
|
beforeEach(function () {
|
|
$this->output = new TestConsoleOutput();
|
|
});
|
|
|
|
it('creates progress bar with default values', function () {
|
|
$bar = new ProgressBar($this->output);
|
|
|
|
expect($bar)->toBeInstanceOf(ProgressBar::class);
|
|
});
|
|
|
|
it('creates progress bar with custom total and width', function () {
|
|
$bar = new ProgressBar($this->output, total: 200, width: 80);
|
|
|
|
expect($bar)->toBeInstanceOf(ProgressBar::class);
|
|
});
|
|
|
|
it('ensures total is at least 1', function () {
|
|
$bar = new ProgressBar($this->output, total: 0);
|
|
|
|
// Should not throw, total should be adjusted to 1
|
|
expect($bar)->toBeInstanceOf(ProgressBar::class);
|
|
});
|
|
|
|
it('sets format', function () {
|
|
$bar = new ProgressBar($this->output);
|
|
$bar->setFormat('%bar% %current%/%total%');
|
|
|
|
// Format should be set and placeholders should be replaced
|
|
$bar->start();
|
|
$bar->advance(10);
|
|
$bar->finish();
|
|
|
|
$output = $this->output->getOutput();
|
|
// Placeholders should be replaced with actual values
|
|
expect($output)->toContain('10');
|
|
expect($output)->toContain('100');
|
|
});
|
|
|
|
it('sets bar characters', function () {
|
|
$bar = new ProgressBar($this->output);
|
|
$bar->setBarCharacters('#', '.', '>');
|
|
|
|
$bar->start();
|
|
$bar->advance(10);
|
|
$bar->finish();
|
|
|
|
// Should use custom characters
|
|
expect($this->output->getOutput())->toContain('#');
|
|
});
|
|
|
|
it('sets redraw frequency', function () {
|
|
$bar = new ProgressBar($this->output);
|
|
$bar->setRedrawFrequency(5);
|
|
|
|
// Should not throw
|
|
expect($bar)->toBeInstanceOf(ProgressBar::class);
|
|
});
|
|
|
|
it('ensures redraw frequency is at least 1', function () {
|
|
$bar = new ProgressBar($this->output);
|
|
$bar->setRedrawFrequency(0);
|
|
|
|
// Should not throw, frequency should be adjusted to 1
|
|
expect($bar)->toBeInstanceOf(ProgressBar::class);
|
|
});
|
|
|
|
it('advances progress', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->start();
|
|
$bar->advance(10);
|
|
$bar->finish();
|
|
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('advances by custom step', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->start();
|
|
$bar->advance(25);
|
|
$bar->finish();
|
|
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('sets progress directly', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->start();
|
|
$bar->setCurrent(50);
|
|
$bar->finish();
|
|
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('does not exceed total', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->start();
|
|
$bar->setCurrent(150);
|
|
$bar->finish();
|
|
|
|
// Should cap at 100
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('does not go below zero', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->start();
|
|
$bar->setCurrent(-10);
|
|
$bar->finish();
|
|
|
|
// Should cap at 0
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('starts progress bar', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->start();
|
|
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('finishes progress bar', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->start();
|
|
$bar->advance(50);
|
|
$bar->finish();
|
|
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
expect($this->output->newLineCount)->toBeGreaterThan(0);
|
|
});
|
|
|
|
it('completes progress when finishing', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->start();
|
|
$bar->advance(50);
|
|
$bar->finish();
|
|
|
|
// Should complete to 100%
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('formats progress with all placeholders', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->setFormat('%bar% %current%/%total% %percent%% %elapsed%s %remaining%s');
|
|
$bar->start();
|
|
$bar->advance(50);
|
|
$bar->finish();
|
|
|
|
$output = $this->output->getOutput();
|
|
// Placeholders should be replaced with actual values
|
|
expect($output)->toContain('50');
|
|
expect($output)->toContain('100');
|
|
expect($output)->toContain('%'); // Percent sign should be present
|
|
});
|
|
|
|
it('handles edge case with total of 1', function () {
|
|
$bar = new ProgressBar($this->output, total: 1);
|
|
$bar->start();
|
|
$bar->advance(1);
|
|
$bar->finish();
|
|
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('handles very large total', function () {
|
|
$bar = new ProgressBar($this->output, total: 1000000);
|
|
$bar->start();
|
|
$bar->advance(100000);
|
|
$bar->finish();
|
|
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('respects redraw frequency', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->setRedrawFrequency(10);
|
|
$bar->start();
|
|
|
|
// Advance multiple times - should only redraw every 10th time
|
|
for ($i = 0; $i < 25; $i++) {
|
|
$bar->advance(1);
|
|
}
|
|
|
|
$bar->finish();
|
|
|
|
// Should have output
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('always redraws on finish', function () {
|
|
$bar = new ProgressBar($this->output, total: 100);
|
|
$bar->setRedrawFrequency(100);
|
|
$bar->start();
|
|
$bar->advance(50);
|
|
$bar->finish();
|
|
|
|
// Should redraw on finish even if frequency not reached
|
|
expect($this->output->capturedLines)->not->toBeEmpty();
|
|
});
|
|
|
|
it('handles negative total by adjusting to 1', function () {
|
|
$bar = new ProgressBar($this->output, total: -10);
|
|
|
|
// Should not throw, total should be adjusted to 1
|
|
expect($bar)->toBeInstanceOf(ProgressBar::class);
|
|
});
|
|
});
|
|
|