Skip to content

[0.1.9] Add non-interactive mode #73

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Concerns/FakesInputOutput.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ trait FakesInputOutput
*/
public static function fake(array $keys = []): void
{
// Force interactive mode when testing because we will be mocking the terminal.
static::interactive();

$mock = \Mockery::mock(Terminal::class);

$mock->shouldReceive('write')->byDefault();
Expand Down
37 changes: 37 additions & 0 deletions src/Concerns/Interactivity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Laravel\Prompts\Concerns;

use Laravel\Prompts\Exceptions\NonInteractiveValidationException;

trait Interactivity
{
/**
* Whether to render the prompt interactively.
*/
protected static bool $interactive;

/**
* Set interactive mode.
*/
public static function interactive(bool $interactive = true): void
{
static::$interactive = $interactive;
}

/**
* Return the default value if it passes validation.
*/
protected function default(): mixed
{
$default = $this->value();

$this->validate($default);

if ($this->state === 'error') {
throw new NonInteractiveValidationException($this->error);
}

return $default;
}
}
10 changes: 10 additions & 0 deletions src/Exceptions/NonInteractiveValidationException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

namespace Laravel\Prompts\Exceptions;

use RuntimeException;

class NonInteractiveValidationException extends RuntimeException
{
//
}
9 changes: 8 additions & 1 deletion src/Prompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ abstract class Prompt
use Concerns\Events;
use Concerns\FakesInputOutput;
use Concerns\Fallback;
use Concerns\Interactivity;
use Concerns\Themes;

/**
Expand Down Expand Up @@ -73,6 +74,12 @@ abstract public function value(): mixed;
*/
public function prompt(): mixed
{
static::$interactive ??= stream_isatty(STDIN);

if (! static::$interactive) {
return $this->default();
}

$this->capturePreviousNewLines();

if (static::shouldFallback()) {
Expand Down Expand Up @@ -304,7 +311,7 @@ private function validate(mixed $value): void
{
$this->validated = true;

if (($this->required ?? false) && ($value === '' || $value === [] || $value === false)) {
if (($this->required ?? false) && ($value === '' || $value === [] || $value === false || $value === null)) {
$this->state = 'error';
$this->error = is_string($this->required) ? $this->required : 'Required.';

Expand Down
5 changes: 5 additions & 0 deletions src/SearchPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ class SearchPrompt extends Prompt
*/
public int $firstVisible = 0;

/**
* Whether user input is required.
*/
public bool|string $required = true;

/**
* The cached matches.
*
Expand Down
9 changes: 9 additions & 0 deletions src/SelectPrompt.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ class SelectPrompt extends Prompt
*/
public array $options;

/**
* Whether user input is required.
*/
public bool|string $required = true;

/**
* Create a new SelectPrompt instance.
*
Expand Down Expand Up @@ -82,6 +87,10 @@ public function __construct(
*/
public function value(): int|string|null
{
if (static::$interactive === false) {
return $this->default;
}

if (array_is_list($this->options)) {
return $this->options[$this->highlighted] ?? null;
} else {
Expand Down
19 changes: 19 additions & 0 deletions tests/Feature/ConfirmPromptTest.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php

use Laravel\Prompts\ConfirmPrompt;
use Laravel\Prompts\Exceptions\NonInteractiveValidationException;
use Laravel\Prompts\Key;
use Laravel\Prompts\Prompt;

Expand Down Expand Up @@ -99,3 +100,21 @@

expect($result)->toBeFalse();
});

it('returns the default value when non-interactive', function () {
Prompt::interactive(false);

$result = confirm('Would you like to continue?', false);

expect($result)->toBeFalse();
});

it('validates the default value when non-interactive', function () {
Prompt::interactive(false);

confirm(
'Would you like to continue?',
default: false,
required: true,
);
})->throws(NonInteractiveValidationException::class, 'Required.');
35 changes: 35 additions & 0 deletions tests/Feature/MultiselectPromptTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Laravel\Prompts\Exceptions\NonInteractiveValidationException;
use Laravel\Prompts\Key;
use Laravel\Prompts\MultiSelectPrompt;
use Laravel\Prompts\Prompt;
Expand Down Expand Up @@ -153,3 +154,37 @@

expect($result)->toBe(['green', 'blue']);
});

it('returns an empty array when non-interactive', function () {
Prompt::interactive(false);

$result = multiselect('What is your favorite color?', [
'Red',
'Green',
'Blue',
]);

expect($result)->toBe([]);
});

it('returns the default value when non-interactive', function () {
Prompt::interactive(false);

$result = multiselect('What is your favorite color?', [
'Red',
'Green',
'Blue',
], default: ['Green']);

expect($result)->toBe(['Green']);
});

it('validates the default value when non-interactive', function () {
Prompt::interactive(false);

multiselect('What is your favorite color?', [
'Red',
'Green',
'Blue',
], required: true);
})->throws(NonInteractiveValidationException::class, 'Required.');
15 changes: 15 additions & 0 deletions tests/Feature/PasswordPromptTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Laravel\Prompts\Exceptions\NonInteractiveValidationException;
use Laravel\Prompts\Key;
use Laravel\Prompts\PasswordPrompt;
use Laravel\Prompts\Prompt;
Expand Down Expand Up @@ -64,3 +65,17 @@

expect($result)->toBe('result');
});

it('returns an empty string when non-interactive', function () {
Prompt::interactive(false);

$result = password('What is the password?');

expect($result)->toBe('');
});

it('fails validation when non-interactive', function () {
Prompt::interactive(false);

password('What is the password?', required: true);
})->throws(NonInteractiveValidationException::class, 'Required.');
7 changes: 7 additions & 0 deletions tests/Feature/SearchPromptTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Laravel\Prompts\Exceptions\NonInteractiveValidationException;
use Laravel\Prompts\Key;
use Laravel\Prompts\Prompt;
use Laravel\Prompts\SearchPrompt;
Expand Down Expand Up @@ -116,3 +117,9 @@

expect($result)->toBe('blue');
});

it('fails when when non-interactive', function () {
Prompt::interactive(false);

search('What is your favorite color?', fn () => []);
})->throws(NonInteractiveValidationException::class, 'Required.');
39 changes: 39 additions & 0 deletions tests/Feature/SelectPromptTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Laravel\Prompts\Exceptions\NonInteractiveValidationException;
use Laravel\Prompts\Key;
use Laravel\Prompts\Prompt;
use Laravel\Prompts\SelectPrompt;
Expand Down Expand Up @@ -224,3 +225,41 @@

expect($result)->toBe('Green');
});

it('fails when there is no default in non-interactive mode', function () {
Prompt::interactive(false);

select('What is your favorite color?', [
'Red',
'Green',
'Blue',
]);
})->throws(NonInteractiveValidationException::class, 'Required.');

it('returns the default value when non-interactive', function () {
Prompt::interactive(false);

$result = select('What is your favorite color?', [
'Red',
'Green',
'Blue',
], default: 'Green');

expect($result)->toBe('Green');
});

it('validates the default value when non-interactive', function () {
Prompt::interactive(false);

select(
label: 'What is your favorite color?',
options: [
'None',
'Red',
'Green',
'Blue',
],
default: 'None',
validate: fn ($value) => $value === 'None' ? 'Required.' : null,
);
})->throws(NonInteractiveValidationException::class, 'Required.');
35 changes: 35 additions & 0 deletions tests/Feature/SuggestPromptTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Laravel\Prompts\Exceptions\NonInteractiveValidationException;
use Laravel\Prompts\Key;
use Laravel\Prompts\Prompt;
use Laravel\Prompts\SuggestPrompt;
Expand Down Expand Up @@ -117,3 +118,37 @@

expect($result)->toBe('Black');
});

it('returns an empty string when non-interactive', function () {
Prompt::interactive(false);

$result = suggest('What is your favorite color?', [
'Red',
'Green',
'Blue',
]);

expect($result)->toBe('');
});

it('returns the default value when non-interactive', function () {
Prompt::interactive(false);

$result = suggest('What is your favorite color?', [
'Red',
'Green',
'Blue',
], default: 'Yellow');

expect($result)->toBe('Yellow');
});

it('validates the default value when non-interactive', function () {
Prompt::interactive(false);

suggest('What is your favorite color?', [
'Red',
'Green',
'Blue',
], required: true);
})->throws(NonInteractiveValidationException::class, 'Required.');
23 changes: 23 additions & 0 deletions tests/Feature/TextPromptTest.php
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?php

use Laravel\Prompts\Exceptions\NonInteractiveValidationException;
use Laravel\Prompts\Key;
use Laravel\Prompts\Prompt;
use Laravel\Prompts\TextPrompt;
Expand Down Expand Up @@ -91,3 +92,25 @@

expect($result)->toBe('Jess');
});

it('returns an empty string when non-interactive', function () {
Prompt::interactive(false);

$result = text('What is your name?');

expect($result)->toBe('');
});

it('returns the default value when non-interactive', function () {
Prompt::interactive(false);

$result = text('What is your name?', default: 'Taylor');

expect($result)->toBe('Taylor');
});

it('validates the default value when non-interactive', function () {
Prompt::interactive(false);

text('What is your name?', required: true);
})->throws(NonInteractiveValidationException::class, 'Required.');