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); }); });