laravel/prompts

Support for Laravel Console Tests

hettiger opened this issue · 4 comments

It seems like Laravel Console Tests are not supported. E.g. the function Laravel\Prompts\search seems to be just blocking the test runner.

I've tried to upgrade churchtools/changelogger to use Laravel Prompts.
Everything seems to be working fine.
However, the tests do fail / never finish execution.

E.g. this test just blocks: https://github.com/hettiger/changelogger/blob/2a795d5a42075ff32ff1d10e0976a3e067488c0f/tests/Feature/Commands/NewChangelogTest.php#L12-L33

It seems like the ->expectsQuestion('Type of change', 'New feature') call fails to provide the answer to e.g. the following prompt:

https://github.com/hettiger/changelogger/blob/2a795d5a42075ff32ff1d10e0976a3e067488c0f/app/Commands/NewCommand.php#L90-L97

Please add support for Laravel Console Tests to Laravel Prompts. That would be awesome.

composer show
$ composer show
brick/math                          0.11.0   Arbitrary-precision arithmetic library
doctrine/inflector                  2.0.8    PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.
doctrine/instantiator               2.0.0    A small, lightweight utility to instantiate objects in PHP without invoking their constructors
dragonmantank/cron-expression       v3.3.2   CRON for PHP: Calculate the next or previous run date and determine if a CRON expression is due
filp/whoops                         2.15.3   php error handling for cool kids
graham-campbell/result-type         v1.1.1   An Implementation Of The Result Type
hamcrest/hamcrest-php               v2.0.1   This is the PHP port of Hamcrest Matchers
illuminate/bus                      v10.17.1 The Illuminate Bus package.
illuminate/cache                    v10.17.1 The Illuminate Cache package.
illuminate/collections              v10.17.1 The Illuminate Collections package.
illuminate/conditionable            v10.17.1 The Illuminate Conditionable package.
illuminate/config                   v10.17.1 The Illuminate Config package.
illuminate/console                  v10.17.1 The Illuminate Console package.
illuminate/container                v10.17.1 The Illuminate Container package.
illuminate/contracts                v10.17.1 The Illuminate Contracts package.
illuminate/events                   v10.17.1 The Illuminate Events package.
illuminate/filesystem               v10.17.1 The Illuminate Filesystem package.
illuminate/macroable                v10.17.1 The Illuminate Macroable package.
illuminate/pipeline                 v10.17.1 The Illuminate Pipeline package.
illuminate/process                  v10.17.1 The Illuminate Process package.
illuminate/support                  v10.17.1 The Illuminate Support package.
illuminate/testing                  v10.17.1 The Illuminate Testing package.
illuminate/view                     v10.17.1 The Illuminate View package.
jolicode/jolinotif                  v2.5.2   Send desktop notifications on Windows, Linux, MacOS.
laravel-zero/foundation             v10.12.0 This is a mirror from illuminate/foundation.
laravel-zero/framework              v10.1.1  The Laravel Zero Framework.
laravel/prompts                     v0.1.3
league/flysystem                    3.15.1   File storage abstraction for PHP
league/flysystem-local              3.15.0   Local filesystem adapter for Flysystem.
league/mime-type-detection          1.12.0   Mime-type detection for Flysystem
mockery/mockery                     1.6.4    Mockery is a simple yet flexible PHP mock object framework
myclabs/deep-copy                   1.11.1   Create deep copies (clones) of your objects
nesbot/carbon                       2.68.1   An API extension for DateTime that supports 281 different languages.
nikic/php-parser                    v4.16.0  A PHP parser written in PHP
nunomaduro/collision                v7.8.0   Cli error handling for console/command-line PHP applications.
nunomaduro/laravel-console-summary  v1.10.0  A Beautiful Laravel Console Summary for your Laravel/Laravel Zero commands.
nunomaduro/laravel-console-task     v1.8.0   Laravel Console Task is a output method for your Laravel/Laravel Zero commands.
nunomaduro/laravel-desktop-notifier v2.7.0   Send notifications to your desktop from your Laravel commands. An JoliNotif wrapper for Laravel 5.
nunomaduro/termwind                 v1.15.1  Its like Tailwind CSS, but for the console.
phar-io/manifest                    2.0.3    Component for reading phar.io manifest information from a PHP Archive (PHAR)
phar-io/version                     3.2.1    Library for handling version information and constraints
phpoption/phpoption                 1.9.1    Option Type for PHP
phpunit/php-code-coverage           9.2.27   Library that provides collection, processing, and rendering functionality for PHP code coverage information.
phpunit/php-file-iterator           3.0.6    FilterIterator implementation that filters files based on a list of suffixes.
phpunit/php-invoker                 3.1.1    Invoke callables with a timeout
phpunit/php-text-template           2.0.4    Simple template engine.
phpunit/php-timer                   5.0.3    Utility class for timing
phpunit/phpunit                     9.6.10   The PHP Unit Testing framework.
psr/container                       2.0.2    Common Container Interface (PHP FIG PSR-11)
psr/event-dispatcher                1.0.0    Standard interfaces for event handling.
psr/log                             3.0.0    Common interface for logging libraries
psr/simple-cache                    3.0.0    Common interfaces for simple caching
ramsey/collection                   2.0.0    A PHP library for representing and manipulating collections.
ramsey/uuid                         4.7.4    A PHP library for generating and working with universally unique identifiers (UUIDs).
sebastian/cli-parser                1.0.1    Library for parsing CLI options
sebastian/code-unit                 1.0.8    Collection of value objects that represent the PHP code units
sebastian/code-unit-reverse-lookup  2.0.3    Looks up which function or method a line of code belongs to
sebastian/comparator                4.0.8    Provides the functionality to compare PHP values for equality
sebastian/complexity                2.0.2    Library for calculating the complexity of PHP code units
sebastian/diff                      4.0.5    Diff implementation
sebastian/environment               5.1.5    Provides functionality to handle HHVM/PHP environments
sebastian/exporter                  4.0.5    Provides the functionality to export PHP variables for visualization
sebastian/global-state              5.0.6    Snapshotting of global state
sebastian/lines-of-code             1.0.3    Library for counting the lines of code in PHP source code
sebastian/object-enumerator         4.0.4    Traverses array structures and object graphs to enumerate all referenced objects
sebastian/object-reflector          2.0.4    Allows reflection of object attributes, including inherited and non-public ones
sebastian/recursion-context         4.0.5    Provides functionality to recursively process PHP variables
sebastian/resource-operations       3.0.3    Provides a list of PHP built-in functions that operate on resources
sebastian/type                      3.2.1    Collection of value objects that represent the types of the PHP type system
sebastian/version                   3.0.2    Library that helps with managing the version number of Git-hosted PHP projects
symfony/console                     v6.3.2   Eases the creation of beautiful and testable command line interfaces
symfony/deprecation-contracts       v3.3.0   A generic function and convention to trigger deprecation notices
symfony/error-handler               v6.3.2   Provides tools to manage errors and ease debugging PHP code
symfony/event-dispatcher            v6.3.2   Provides tools that allow your application components to communicate with each other by dispatching events and listening to them
symfony/event-dispatcher-contracts  v3.3.0   Generic abstractions related to dispatching event
symfony/finder                      v6.3.3   Finds files and directories via an intuitive fluent interface
symfony/polyfill-ctype              v1.27.0  Symfony polyfill for ctype functions
symfony/polyfill-intl-grapheme      v1.27.0  Symfony polyfill for intl's grapheme_* functions
symfony/polyfill-intl-normalizer    v1.27.0  Symfony polyfill for intl's Normalizer class and related functions
symfony/polyfill-mbstring           v1.27.0  Symfony polyfill for the Mbstring extension
symfony/polyfill-php80              v1.27.0  Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions
symfony/process                     v6.3.2   Executes commands in sub-processes
symfony/service-contracts           v3.3.0   Generic abstractions related to writing services
symfony/string                      v6.3.2   Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way
symfony/translation                 v6.3.3   Provides tools to internationalize your application
symfony/translation-contracts       v3.3.0   Generic abstractions related to translation
symfony/var-dumper                  v6.3.3   Provides mechanisms for walking through any arbitrary PHP variable
symfony/yaml                        v5.4.23  Loads and dumps YAML files
theseer/tokenizer                   1.2.1    A small library for converting tokenized PHP source code into XML and potentially other formats
vlucas/phpdotenv                    v5.5.0   Loads environment variables from `.env` to `getenv()`, `$_ENV` and `$_SERVER` automagically.
voku/portable-ascii                 2.0.1    Portable ASCII library - performance optimized (ascii) string functions for php.
webmozart/assert                    1.11.0   Assertions to validate method input/output with nice error messages.

Hi @hettiger,

In laravel/framework ^10.17, we configure Prompts to fall back to the Symfony implementation when running unit tests, which allows the expectQuestion test helper to work:

https://github.com/laravel/framework/blob/dd5c5178274e64d0384dc30bf2c8139b00dba098/src/Illuminate/Console/Concerns/ConfiguresPrompts.php#L27

I've just double-checked, and the expectQuestion test helper works for me in a fresh Laravel install. The problem relates to how Laravel Zero sets up your application, but there are a few ways to fix it.

The problem is that app()->runningUnitTest() returns false in Laravel Zero and your project. This is because:

  • In the config/app.php file from Laravel Zero (and your repo) the env key is hardcoded to development.
  • The phpunit.xml.dist file from Laravel Zero (and your repo) doesn't set the APP_ENV environment variable to testing as Laravel does.

I'm not sure if there is a reason that Laravel Zero does this, but a solution we can propose (and also apply to your repo) is:

  • Use env('APP_ENV', 'development') in config/app.php.
  • Add <env name="APP_ENV" value="testing"/> to phpunit.xml.dist

With these changes in place, your tests pass.

If the above changes cause other issues, an alternative would be to directly call \Laravel\Prompts\Prompt::fallbackWhen(true) in the tests, probably somewhere central like TestCase or CreatesApplication.

sc85 commented

In my case I was able to get it to work by asserting twice with the same question but first expecting a question and after that expecting a choice.

->expectsQuestion('Type of change', 'New feature') // Question with input to prompt the search
->expectsChoice(
    'Type of change',
    'New feature', // Final selection
   [
       0, // Index of a possible selection
       'New feature', // Value of a selection
   ],
)

I came up with this after switching my local env to the fallback mode and noticed that this is basically the behavior of search in fallback mode.

The problem is that app()->runningUnitTest() returns false in Laravel Zero and your project. This is because:

  • In the config/app.php file from Laravel Zero (and your repo) the env key is hardcoded to development.
  • The phpunit.xml.dist file from Laravel Zero (and your repo) doesn't set the APP_ENV environment variable to testing as Laravel does.

I'm not sure if there is a reason that Laravel Zero does this, but a solution we can propose (and also apply to your repo) is:

  • Use env('APP_ENV', 'development') in config/app.php.
  • Add <env name="APP_ENV" value="testing"/> to phpunit.xml.dist

With these changes in place, your tests pass.

Awesome, thank you for the working solution and detailed explanation @jessarcher

@sc85 @jessarcher

I've run into exactly the same problem using standard Laravel with all packages up to date. I've had to amend my asserts to

$this->artisan(GenerateTranslations::class, ['filename' => 'en.messages'])
        ->expectsQuestion('Choose your target language', 'JA')
        ->expectsChoice('Choose your target language', 'JA', ['JA', 'Japanese'])

I've checked app()->runningInUnitTests() and that returns true. So for me it actually makes more sense to add a conditional and ask a standard question rather than a search.

Here's my code snippet:

$availableLanguages = collect(Language::cases())->mapWithKeys(
            fn (Language $language) => [$language->value => $language->name]
        );

        if (app()->runningUnitTests()) {
            $targetLanguage = Language::tryFrom($this->ask('Choose your target language'));
        } else {
            $targetLanguage = Language::tryFrom(
                search(
                    'Choose your target language',
                    fn ($value) => $availableLanguages
                        ->except($sourceLanguage->value)
                        ->when($value, fn (Collection $collection, string $value) => $collection->filter(
                            fn ($language) => str_contains(strtolower($language), strtolower($value))
                        ))
                        ->toArray(),
                )
            );
        }

Without this tests fail with a Mock exception Mockery\Exception\InvalidCountException: Method askQuestion(<Closure===true>) from Mockery_2_Illuminate_Console_OutputStyle should be called