-
Notifications
You must be signed in to change notification settings - Fork 94
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
373 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
<?php | ||
|
||
use function Laravel\Prompts\table; | ||
use function Laravel\Prompts\watch; | ||
|
||
require __DIR__ . '/../vendor/autoload.php'; | ||
|
||
watch( | ||
function () { | ||
static $iteration = 0; | ||
static $items = []; | ||
|
||
if (count($items) == 5) { | ||
array_shift($items); | ||
} | ||
|
||
$items[] = [$iteration += 1, (new Datetime())->format(DateTime::RFC850)]; | ||
|
||
table( | ||
[ | ||
'Iteration', | ||
'DateTime' | ||
], | ||
$items | ||
); | ||
}, | ||
1, | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?php | ||
|
||
namespace Laravel\Prompts\Contracts; | ||
|
||
interface Buffered | ||
{ | ||
public function buffer(): void; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
<?php | ||
|
||
namespace Laravel\Prompts\Themes\Default; | ||
|
||
use Laravel\Prompts\Contracts\Buffered; | ||
use Laravel\Prompts\Output\BufferedConsoleOutput; | ||
use Laravel\Prompts\Output\ConsoleOutput; | ||
use Laravel\Prompts\Prompt; | ||
use Laravel\Prompts\Watch; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
|
||
class BufferedOutputRenderer extends Renderer | ||
{ | ||
protected static ?OutputInterface $previousOutput = null; | ||
public static function setPreviousOutput(OutputInterface $output): void | ||
{ | ||
static::$previousOutput = $output; | ||
} | ||
|
||
public function __invoke(Buffered&Prompt $watch): string | ||
{ | ||
$bufferedOutput = new BufferedConsoleOutput(); | ||
|
||
$watch::setOutput($bufferedOutput); | ||
|
||
$watch->buffer(); | ||
|
||
$watch::setOutput(static::$previousOutput ?? new ConsoleOutput()); | ||
|
||
return $bufferedOutput->fetch(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
<?php | ||
|
||
namespace Laravel\Prompts; | ||
|
||
use Closure; | ||
use Laravel\Prompts\Contracts\Buffered; | ||
use Laravel\Prompts\Output\BufferedConsoleOutput; | ||
use Laravel\Prompts\Themes\Default\BufferedOutputRenderer; | ||
use PHPUnit\Framework\Assert; | ||
use RuntimeException; | ||
use Symfony\Component\Console\Output\OutputInterface; | ||
use ValueError; | ||
|
||
class Watch extends Prompt implements Buffered | ||
{ | ||
/** | ||
* How many times to fake an iteration | ||
*/ | ||
protected static int $fakeTimes = 1; | ||
|
||
/** | ||
* count of faked iterations | ||
*/ | ||
protected int $fakedTimes = 0; | ||
|
||
/** | ||
* Faking sleep or not | ||
*/ | ||
protected static bool $fakeSleep = true; | ||
|
||
/** | ||
* the amount of seconds slept during intervals in total | ||
*/ | ||
protected static int $sleptSeconds = 0; | ||
|
||
public Closure $watch; | ||
private int $interval; | ||
|
||
/** | ||
* Create a new Watch instance. | ||
*/ | ||
public function __construct(callable $watch, ?int $interval = 2) | ||
{ | ||
static::$sleptSeconds = 0; | ||
$this->watch = $watch(...); | ||
$this->interval = $interval ?? 2; | ||
|
||
if ($this->interval < 0) { | ||
throw new ValueError('watch interval must be greater than or equal to 0'); | ||
} | ||
} | ||
|
||
/** | ||
* displays the watched output and updates after the specified interval. | ||
*/ | ||
public function display(): void | ||
{ | ||
$faked = static::output() instanceof BufferedConsoleOutput; | ||
|
||
while (!$faked || $this->fakedTimes < static::$fakeTimes) { | ||
BufferedOutputRenderer::setPreviousOutput( | ||
static::output() | ||
); | ||
|
||
$this->render(); | ||
|
||
if ($faked) { | ||
$this->fakedTimes++; | ||
|
||
if ($this->fakedTimes >= static::$fakeTimes) { | ||
static::$fakeSleep = true; | ||
break; | ||
} | ||
|
||
if (static::$fakeSleep) { | ||
static::$sleptSeconds += $this->interval; | ||
continue; | ||
} | ||
} | ||
|
||
sleep($this->interval); | ||
} | ||
} | ||
|
||
/** | ||
* Buffers the output from the callable and flushes the output to Prompts | ||
* in order to utilize Prompts neat way of updating lines | ||
*/ | ||
public function buffer(): void | ||
{ | ||
($this->watch)(); | ||
} | ||
|
||
/** | ||
* overrides prompt so it will have the same behavior. | ||
*/ | ||
public function prompt(): bool | ||
{ | ||
$this->display(); | ||
|
||
return true; | ||
} | ||
|
||
/** | ||
* Get the value of the prompt. | ||
*/ | ||
public function value(): bool | ||
{ | ||
return true; | ||
} | ||
|
||
/** | ||
* Tell Prompt how many iterations to fake | ||
*/ | ||
public static function fakeTimes(int $times): void | ||
{ | ||
if (!static::output() instanceof BufferedConsoleOutput) { | ||
throw new RuntimeException('Prompt must be faked before faking iterations.'); | ||
} | ||
|
||
static::$fakeTimes = $times; | ||
} | ||
|
||
/** | ||
* Asserts the amount of seconds slept during intervals in total. | ||
*/ | ||
public static function assertSecondsSleptBetweenIntervals(int $seconds): void | ||
{ | ||
if (!static::output() instanceof BufferedConsoleOutput) { | ||
throw new RuntimeException('Prompt must be faked before asserting.'); | ||
} | ||
|
||
Assert::assertEquals($seconds, static::$sleptSeconds); | ||
} | ||
|
||
/** | ||
* By default, when Prompt is faked, the intervals are faked.. | ||
* This tells Prompt to actually sleep between updates. | ||
*/ | ||
public static function shouldNotFakeIntervalSleep(): void | ||
{ | ||
if (!static::output() instanceof BufferedConsoleOutput) { | ||
throw new RuntimeException('Not faking sleep makes no sense when not faking Prompt.'); | ||
} | ||
|
||
static::$fakeSleep = false; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
<?php | ||
|
||
use Laravel\Prompts\Output\ConsoleOutput; | ||
use Laravel\Prompts\Prompt; | ||
use Laravel\Prompts\Watch; | ||
use function Laravel\Prompts\note; | ||
use function Laravel\Prompts\watch; | ||
|
||
it('it should render', function () { | ||
Prompt::fake(); | ||
|
||
watch(function () { | ||
note('This should render'); | ||
}); | ||
|
||
Watch::assertSecondsSleptBetweenIntervals(0); | ||
|
||
Prompt::assertOutputContains('This should render'); | ||
}); | ||
|
||
it('it should render callable array', function () { | ||
Prompt::fake(); | ||
|
||
$watch = new class() { | ||
public function watch(): void | ||
{ | ||
note('This should render through array'); | ||
} | ||
}; | ||
|
||
watch([$watch, 'watch']); | ||
|
||
Watch::assertSecondsSleptBetweenIntervals(0); | ||
|
||
Prompt::assertOutputContains('This should render through array'); | ||
}); | ||
|
||
it('it should render invokable', function () { | ||
Prompt::fake(); | ||
|
||
$watch = new class() { | ||
public function __invoke(): void | ||
{ | ||
note('This should render through invokable'); | ||
} | ||
}; | ||
|
||
watch($watch); | ||
|
||
Watch::assertSecondsSleptBetweenIntervals(0); | ||
|
||
Prompt::assertOutputContains('This should render through invokable'); | ||
}); | ||
|
||
it('it should render buffered', function () { | ||
Prompt::fake(); | ||
|
||
watch(function () { | ||
|
||
note('This should not render'); | ||
|
||
(fn() => Watch::output()) | ||
->bindTo(null, Watch::class)()->fetch(); | ||
}); | ||
|
||
Prompt::assertOutputDoesntContain('This should not render'); | ||
}); | ||
|
||
it('it should fake sleep when faking', function ( | ||
int $expected, | ||
int $iteration, | ||
int $interval = null | ||
) { | ||
Prompt::fake(); | ||
|
||
Watch::fakeTimes($iteration); | ||
|
||
watch(function () { | ||
}, $interval); | ||
|
||
Watch::assertSecondsSleptBetweenIntervals($expected); | ||
})->with( | ||
[ | ||
['expected' => 2, 'iteration' => 2, 'interval' => 2], | ||
['expected' => 3, 'iteration' => 2, 'interval' => 3], | ||
['expected' => 6, 'iteration' => 3, 'interval' => 3], | ||
['expected' => 4, 'iteration' => 3, 'interval' => null], | ||
] | ||
); | ||
|
||
it('should throw exception with a negative interval ', function () { | ||
Prompt::fake(); | ||
|
||
watch(fn() => null, -1); | ||
|
||
})->throws(ValueError::class); | ||
|
||
it('should sleep 2 seconds by default', function () { | ||
Prompt::fake(); | ||
|
||
Watch::fakeTimes(2); | ||
|
||
watch(function () { | ||
}); | ||
|
||
Watch::assertSecondsSleptBetweenIntervals(2); | ||
}); | ||
|
||
it('should actually sleep at intervals', function () { | ||
|
||
Prompt::fake(); | ||
|
||
Watch::fakeTimes(2); | ||
|
||
Watch::shouldNotFakeIntervalSleep(); | ||
|
||
$start = time(); | ||
|
||
watch(function () { | ||
note('This should render'); | ||
}, 1); | ||
|
||
$end = time(); | ||
|
||
expect($end - $start)->toBe(1); | ||
|
||
Prompt::assertOutputContains('This should render'); | ||
}); | ||
|
||
it('should throw exception when invoking fakeTimes when not faked', function () { | ||
(function () { | ||
Prompt::$output = new ConsoleOutput(); | ||
})->bindTo(null, Prompt::class)(); | ||
Watch::fakeTimes(2); | ||
})->throws(RuntimeException::class); | ||
|
||
it('should throw exception when invoking assertSlept when not faked', function () { | ||
|
||
(function () { | ||
Prompt::$output = new ConsoleOutput(); | ||
})->bindTo(null, Prompt::class)(); | ||
|
||
Watch::assertSecondsSleptBetweenIntervals(2); | ||
})->throws(RuntimeException::class); |