diff --git a/config/blueprint.php b/config/blueprint.php index 63d63025..8c532249 100644 --- a/config/blueprint.php +++ b/config/blueprint.php @@ -87,6 +87,19 @@ */ 'fake_nullables' => true, + /* + |-------------------------------------------------------------------------- + | Method Return Type Declarations + |-------------------------------------------------------------------------- + | + | Enable or disable method return typehinting for blueprint generated + | methods. Enabling this will enforce code strictness which increases + | readability of code and will lower maintenance cost. This will only + | Work for projects running PHP v7.0 or higher. + | + */ + 'use_return_types' => false, + /* |-------------------------------------------------------------------------- | Use Guarded diff --git a/src/Blueprint.php b/src/Blueprint.php index f0948ad1..c94127e0 100644 --- a/src/Blueprint.php +++ b/src/Blueprint.php @@ -30,6 +30,16 @@ public static function appPath() return str_replace('\\', '/', config('blueprint.app_path')); } + public static function supportsReturnTypeHits() + { + return boolval(config('blueprint.use_return_types')) && self::isPHP7OrHigher(); + } + + public static function isPHP7OrHigher() + { + return version_compare(PHP_VERSION, '7.0.0', '>='); + } + public static function isLaravel8OrHigher() { return version_compare(App::version(), '8.0.0', '>='); diff --git a/src/Generators/ControllerGenerator.php b/src/Generators/ControllerGenerator.php index 8c6a6851..84df5846 100644 --- a/src/Generators/ControllerGenerator.php +++ b/src/Generators/ControllerGenerator.php @@ -163,6 +163,10 @@ protected function buildMethods(Controller $controller) $method = str_replace('{{ body }}', trim($body), $method); } + if (Blueprint::supportsReturnTypeHits()) { + $method = str_replace('request)', 'request): \Illuminate\Http\Response', $method); + } + $methods .= PHP_EOL.$method; } diff --git a/src/Generators/FactoryGenerator.php b/src/Generators/FactoryGenerator.php index 6221245c..69655711 100644 --- a/src/Generators/FactoryGenerator.php +++ b/src/Generators/FactoryGenerator.php @@ -82,6 +82,10 @@ protected function populateStub(string $stub, Model $model) ], "use", $stub); } + if (Blueprint::supportsReturnTypeHits()) { + $stub = str_replace('definition()', 'definition(): array', $stub); + } + $stub = str_replace('use {{ namespacedModel }};', $this->buildImports($model), $stub); return $stub; diff --git a/src/Generators/MigrationGenerator.php b/src/Generators/MigrationGenerator.php index ca2f6a97..6be06d2d 100644 --- a/src/Generators/MigrationGenerator.php +++ b/src/Generators/MigrationGenerator.php @@ -2,6 +2,7 @@ namespace Blueprint\Generators; +use Blueprint\Blueprint; use Blueprint\Contracts\Generator; use Blueprint\Models\Model; use Blueprint\Tree; @@ -106,6 +107,10 @@ protected function populateStub(string $stub, Model $model) $stub = str_replace('{{ table }}', $model->tableName(), $stub); $stub = str_replace('{{ definition }}', $this->buildDefinition($model), $stub); + if (Blueprint::supportsReturnTypeHits()) { + $stub = str_replace(['up()','down()'], ['up(): void','down(): void'], $stub); + } + if ($this->hasForeignKeyConstraints) { $stub = $this->disableForeignKeyConstraints($stub); } diff --git a/src/Generators/ModelGenerator.php b/src/Generators/ModelGenerator.php index c2994da6..12beb688 100644 --- a/src/Generators/ModelGenerator.php +++ b/src/Generators/ModelGenerator.php @@ -162,6 +162,7 @@ protected function buildRelationships(Model $model) } foreach ($model->relationships() as $type => $references) { + $custom_template = $template; foreach ($references as $reference) { $key = null; $class = null; @@ -207,7 +208,14 @@ protected function buildRelationships(Model $model) } elseif (in_array($type, ['hasMany', 'belongsToMany', 'morphMany'])) { $method_name = Str::plural($column_name); } - $method = str_replace('{{ method }}', Str::camel($method_name), $template); + if (Blueprint::supportsReturnTypeHits()) { + $custom_template = str_replace( + '{{ method }}()', + '{{ method }}(): ' . Str::of('\Illuminate\Database\Eloquent\Relations\\')->append(Str::studly($type)), + $custom_template + ); + } + $method = str_replace('{{ method }}', Str::camel($method_name), $custom_template); $method = str_replace('null', $relationship, $method); $phpDoc = str_replace('{{ namespacedReturnClass }}', '\Illuminate\Database\Eloquent\Relations\\'.Str::ucfirst($type), $commentTemplate); diff --git a/src/Generators/SeederGenerator.php b/src/Generators/SeederGenerator.php index 641eadba..2af44534 100644 --- a/src/Generators/SeederGenerator.php +++ b/src/Generators/SeederGenerator.php @@ -63,6 +63,11 @@ protected function populateStub(string $stub, string $model) } else { $stub = str_replace('{{ body }}', $this->build($model), $stub); } + + if (Blueprint::supportsReturnTypeHits()) { + $stub = str_replace('public function run()', 'public function run(): void', $stub); + } + return $stub; } diff --git a/src/Generators/StatementGenerator.php b/src/Generators/StatementGenerator.php index 86c77ed0..b48df0f6 100644 --- a/src/Generators/StatementGenerator.php +++ b/src/Generators/StatementGenerator.php @@ -2,6 +2,7 @@ namespace Blueprint\Generators; +use Blueprint\Blueprint; use Blueprint\Contracts\Generator; abstract class StatementGenerator implements Generator diff --git a/src/Generators/Statements/FormRequestGenerator.php b/src/Generators/Statements/FormRequestGenerator.php index 8e29004f..826a5776 100644 --- a/src/Generators/Statements/FormRequestGenerator.php +++ b/src/Generators/Statements/FormRequestGenerator.php @@ -81,6 +81,10 @@ protected function populateStub(string $stub, string $name, $context, ValidateSt $stub = str_replace('{{ class }}', $name, $stub); $stub = str_replace('{{ rules }}', $this->buildRules($context, $validateStatement), $stub); + if (Blueprint::supportsReturnTypeHits()) { + $stub = str_replace(['authorize()','rules()'], ['authorize(): bool','rules(): array'], $stub); + } + return $stub; } diff --git a/src/Generators/Statements/JobGenerator.php b/src/Generators/Statements/JobGenerator.php index b808a85c..e16de5e4 100644 --- a/src/Generators/Statements/JobGenerator.php +++ b/src/Generators/Statements/JobGenerator.php @@ -61,6 +61,10 @@ protected function populateStub(string $stub, DispatchStatement $dispatchStateme $stub = str_replace('{{ class }}', $dispatchStatement->job(), $stub); $stub = str_replace('{{ properties }}', $this->buildConstructor($dispatchStatement), $stub); + if (Blueprint::supportsReturnTypeHits()) { + $stub = str_replace('public function handle()', 'public function handle(): void', $stub); + } + return $stub; } } diff --git a/src/Generators/Statements/MailGenerator.php b/src/Generators/Statements/MailGenerator.php index f4431729..4499c33b 100644 --- a/src/Generators/Statements/MailGenerator.php +++ b/src/Generators/Statements/MailGenerator.php @@ -65,6 +65,10 @@ protected function populateStub(string $stub, SendStatement $sendStatement) $stub = str_replace('{{ class }}', $sendStatement->mail(), $stub); $stub = str_replace('{{ properties }}', $this->buildConstructor($sendStatement), $stub); + if (Blueprint::supportsReturnTypeHits()) { + $stub = str_replace('build()', sprintf('build(): %s', $sendStatement->mail()), $stub); + } + return $stub; } } diff --git a/src/Generators/Statements/NotificationGenerator.php b/src/Generators/Statements/NotificationGenerator.php index 06c667d0..ed1419f7 100644 --- a/src/Generators/Statements/NotificationGenerator.php +++ b/src/Generators/Statements/NotificationGenerator.php @@ -65,6 +65,22 @@ protected function populateStub(string $stub, SendStatement $sendStatement) $stub = str_replace('{{ class }}', $sendStatement->mail(), $stub); $stub = str_replace('{{ properties }}', $this->buildConstructor($sendStatement), $stub); + if (Blueprint::supportsReturnTypeHits()) { + $stub = str_replace( + [ + 'via($notifiable)', + 'toMail($notifiable)', + 'toArray($notifiable)', + ], + [ + 'via($notifiable): array', + sprintf('toMail($notifiable): %s', $sendStatement->mail()), + 'toArray($notifiable): array' + ], + $stub + ); + } + return $stub; } } diff --git a/src/Generators/Statements/ResourceGenerator.php b/src/Generators/Statements/ResourceGenerator.php index 11187407..650e2a33 100644 --- a/src/Generators/Statements/ResourceGenerator.php +++ b/src/Generators/Statements/ResourceGenerator.php @@ -86,6 +86,9 @@ protected function populateStub(string $stub, Controller $controller, ResourceSt $stub = str_replace('{{ resource }}', $resource->collection() ? 'resource collection' : 'resource', $stub); $stub = str_replace('{{ body }}', $this->buildData($resource), $stub); + if (Blueprint::supportsReturnTypeHits()) { + $stub = str_replace('toArray($request)', 'toArray($request): array', $stub); + } return $stub; } diff --git a/src/Generators/TestGenerator.php b/src/Generators/TestGenerator.php index 840b2d4c..6bf31129 100644 --- a/src/Generators/TestGenerator.php +++ b/src/Generators/TestGenerator.php @@ -507,9 +507,15 @@ protected function buildTestCases(Controller $controller) $body .= PHP_EOL.PHP_EOL; $body .= implode(PHP_EOL.PHP_EOL, array_map([$this, 'buildLines'], array_filter($assertions))); - $test_case = str_replace('{{ method }}', $this->buildTestCaseName($name, $tested_bits), $test_case); + $test_case_name = $this->buildTestCaseName($name, $tested_bits); + $test_case = str_replace('{{ method }}', $test_case_name, $test_case); $test_case = str_replace('{{ body }}', trim($body), $test_case); + if (Blueprint::supportsReturnTypeHits()) { + $test_case = str_replace("$test_case_name()", "$test_case_name(): void", $test_case); + $test_case = str_replace("uses_form_request_validation()", "uses_form_request_validation(): void", $test_case); + } + $test_cases .= PHP_EOL.$test_case.PHP_EOL; } diff --git a/src/Tracer.php b/src/Tracer.php index 7567ac05..8e37c3e1 100644 --- a/src/Tracer.php +++ b/src/Tracer.php @@ -69,8 +69,7 @@ private function loadModel(string $class) } $reflectionClass = new \ReflectionClass($class); - if ( - !$reflectionClass->isSubclassOf(\Illuminate\Database\Eloquent\Model::class) || + if (!$reflectionClass->isSubclassOf(\Illuminate\Database\Eloquent\Model::class) || (class_exists('Jenssegers\Mongodb\Eloquent\Model') && $reflectionClass->isSubclassOf('Jenssegers\Mongodb\Eloquent\Model')) ) { diff --git a/src/Translators/Rules.php b/src/Translators/Rules.php index 91c580f0..c6b594ea 100644 --- a/src/Translators/Rules.php +++ b/src/Translators/Rules.php @@ -2,11 +2,29 @@ namespace Blueprint\Translators; -use Illuminate\Support\Str; use Blueprint\Models\Column; +use Illuminate\Support\Str; class Rules { + const INTEGER_TYPES = [ + 'integer', + 'tinyInteger', + 'smallInteger', + 'mediumInteger', + 'bigInteger', + 'increments', + 'tinyIncrements', + 'smallIncrements', + 'mediumIncrements', + 'bigIncrements', + 'unsignedBigInteger', + 'unsignedInteger', + 'unsignedMediumInteger', + 'unsignedSmallInteger', + 'unsignedTinyInteger', + ]; + public static function fromColumn(string $context, Column $column) { $rules = []; @@ -25,23 +43,7 @@ public static function fromColumn(string $context, Column $column) $rules = array_merge($rules, ['integer', 'exists:' . Str::plural($reference) . ',id']); } - if (in_array($column->dataType(), [ - 'integer', - 'tinyInteger', - 'smallInteger', - 'mediumInteger', - 'bigInteger', - 'increments', - 'tinyIncrements', - 'smallIncrements', - 'mediumIncrements', - 'bigIncrements', - 'unsignedBigInteger', - 'unsignedInteger', - 'unsignedMediumInteger', - 'unsignedSmallInteger', - 'unsignedTinyInteger', - ])) { + if (in_array($column->dataType(), self::INTEGER_TYPES)) { array_push($rules, 'integer'); if (Str::startsWith($column->dataType(), 'unsigned')) { diff --git a/tests/Feature/Generators/ControllerGeneratorTest.php b/tests/Feature/Generators/ControllerGeneratorTest.php index ec069f58..6607b12d 100644 --- a/tests/Feature/Generators/ControllerGeneratorTest.php +++ b/tests/Feature/Generators/ControllerGeneratorTest.php @@ -164,6 +164,39 @@ public function output_respects_configuration() $this->assertEquals(['created' => ['src/path/Other/Http/UserController.php']], $this->subject->output($tree)); } + + /** + * @test + * @environment-setup useLaravel8 + */ + public function output_using_return_types() + { + $this->app['config']->set('blueprint.use_return_types', true); + + $this->files->expects('stub') + ->with('controller.class.stub') + ->andReturn($this->stub('controller.class.stub')); + + $this->files->expects('stub') + ->with('controller.method.stub') + ->andReturn($this->stub('controller.method.stub')); + + $this->files->expects('exists') + ->with('app/Http/Controllers') + ->andReturnFalse(); + + $this->files->expects('makeDirectory') + ->with('app/Http/Controllers', 0755, true); + + $this->files->expects('put') + ->with('app/Http/Controllers/PostController.php', $this->fixture('controllers/return-type-declarations.php')); + + $tokens = $this->blueprint->parse($this->fixture('drafts/readme-example.yaml')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => ['app/Http/Controllers/PostController.php']], $this->subject->output($tree)); + } + public function controllerTreeDataProvider() { return [ diff --git a/tests/Feature/Generators/FactoryGeneratorTest.php b/tests/Feature/Generators/FactoryGeneratorTest.php index ada29a72..90e71618 100644 --- a/tests/Feature/Generators/FactoryGeneratorTest.php +++ b/tests/Feature/Generators/FactoryGeneratorTest.php @@ -2,11 +2,11 @@ namespace Tests\Feature\Generators; -use Blueprint\Tree; -use Tests\TestCase; use Blueprint\Blueprint; use Blueprint\Generators\FactoryGenerator; +use Blueprint\Tree; use Illuminate\Support\Facades\App; +use Tests\TestCase; /** * @see FactoryGenerator @@ -144,6 +144,31 @@ public function output_ignores_nullables_if_fake_nullables_configuration_is_set_ $this->assertEquals(['created' => ['database/factories/PostFactory.php']], $this->subject->output($tree)); } + /** + * @test + * @environment-setup useLaravel8 + */ + public function output_using_return_types() + { + $this->app['config']->set('blueprint.use_return_types', true); + + $this->files->expects('stub') + ->with($this->factoryStub) + ->andReturn($this->stub($this->factoryStub)); + + $this->files->expects('exists') + ->with('database/factories') + ->andReturnTrue(); + + $this->files->expects('put') + ->with('database/factories/PostFactory.php', $this->fixture('factories/return-type-declarations-laravel8.php')); + + $tokens = $this->blueprint->parse($this->fixture('drafts/readme-example.yaml')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => ['database/factories/PostFactory.php']], $this->subject->output($tree)); + } + /** * @test * @environment-setup useLaravel8 diff --git a/tests/Feature/Generators/MigrationGeneratorTest.php b/tests/Feature/Generators/MigrationGeneratorTest.php index 4d5af5de..2b4e52e4 100644 --- a/tests/Feature/Generators/MigrationGeneratorTest.php +++ b/tests/Feature/Generators/MigrationGeneratorTest.php @@ -54,6 +54,10 @@ public function output_writes_nothing_for_empty_tree() */ public function output_writes_migration_for_model_tree($definition, $path, $migration) { + if ($migration === 'migrations/return-type-declarations.php') { + $this->app['config']->set('blueprint.use_return_types', true); + } + $this->files->expects('stub') ->with('migration.stub') ->andReturn($this->stub('migration.stub')); @@ -82,6 +86,10 @@ public function output_writes_migration_for_model_tree($definition, $path, $migr */ public function output_updates_migration_for_model_tree($definition, $path, $migration) { + if ($migration === 'migrations/return-type-declarations.php') { + $this->app['config']->set('blueprint.use_return_types', true); + } + $this->files->expects('stub') ->with('migration.stub') ->andReturn($this->stub('migration.stub')); diff --git a/tests/Feature/Generators/ModelGeneratorTest.php b/tests/Feature/Generators/ModelGeneratorTest.php index f1ddd3e9..6c64a12f 100644 --- a/tests/Feature/Generators/ModelGeneratorTest.php +++ b/tests/Feature/Generators/ModelGeneratorTest.php @@ -51,6 +51,9 @@ public function output_generates_nothing_for_empty_tree() */ public function output_generates_models($definition, $path, $model) { + if ($model === 'models/return-type-declarations.php') { + $this->app['config']->set('blueprint.use_return_types', true); + } $this->files->expects('stub') ->with($this->modelStub) ->andReturn($this->stub($this->modelStub)); @@ -688,6 +691,7 @@ public function laravel8ModelTreeDataProvider() ['drafts/resource-statements.yaml', 'app/User.php', 'models/resource-statements-laravel8.php'], ['drafts/all-column-types.yaml', 'app/AllType.php', 'models/all-column-types-laravel8.php'], ['drafts/alias-relationships.yaml', 'app/Salesman.php', 'models/alias-relationships-laravel8.php'], + ['drafts/alias-relationships.yaml', 'app/Salesman.php', 'models/return-type-declarations.php'], ['drafts/uuid-shorthand-invalid-relationship.yaml', 'app/AgeCohort.php', 'models/uuid-shorthand-invalid-relationship-laravel8.php'], ]; } diff --git a/tests/Feature/Generators/SeederGeneratorTest.php b/tests/Feature/Generators/SeederGeneratorTest.php index 2a68e227..199ce362 100644 --- a/tests/Feature/Generators/SeederGeneratorTest.php +++ b/tests/Feature/Generators/SeederGeneratorTest.php @@ -69,6 +69,28 @@ public function output_generates_seeders() $this->assertEquals(['created' => ['database/seeders/PostSeeder.php', 'database/seeders/CommentSeeder.php']], $this->subject->output($tree)); } + /** + * @test + * @environment-setup useLaravel8 + */ + public function output_using_return_types() + { + $this->app['config']->set('blueprint.use_return_types', true); + + $this->files->expects('stub') + ->with($this->seederStub) + ->andReturn($this->stub($this->seederStub)); + + $this->files->expects('put') + ->with('database/seeders/PostSeeder.php', $this->fixture('seeders/PostSeeder-return-type-declarations.php')); + $this->files->expects('put') + ->with('database/seeders/CommentSeeder.php', $this->fixture('seeders/CommentSeeder-return-type-declarations.php')); + + $tokens = $this->blueprint->parse($this->fixture('drafts/seeders.yaml')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => ['database/seeders/PostSeeder.php', 'database/seeders/CommentSeeder.php']], $this->subject->output($tree)); + } /** * @test diff --git a/tests/Feature/Generators/Statements/FormRequestGeneratorTest.php b/tests/Feature/Generators/Statements/FormRequestGeneratorTest.php index d2d3fbbf..6b063d5a 100644 --- a/tests/Feature/Generators/Statements/FormRequestGeneratorTest.php +++ b/tests/Feature/Generators/Statements/FormRequestGeneratorTest.php @@ -220,6 +220,36 @@ public function it_respects_configuration() $this->assertEquals(['created' => ['src/path/Http/Requests/PostStoreRequest.php']], $this->subject->output($tree)); } + /** + * @test + */ + public function output_using_return_types() + { + $this->app['config']->set('blueprint.namespace', 'Some\\Other\\App'); + $this->app['config']->set('blueprint.app_path', 'src/path'); + $this->app['config']->set('blueprint.use_return_types', true); + + $this->files->expects('stub') + ->with('request.stub') + ->andReturn($this->stub('request.stub')); + + $this->files->expects('exists') + ->with('src/path/Http/Requests') + ->andReturns(false); + $this->files->expects('exists') + ->with('src/path/Http/Requests/PostStoreRequest.php') + ->andReturnFalse(); + $this->files->expects('makeDirectory') + ->with('src/path/Http/Requests', 0755, true); + $this->files->expects('put') + ->with('src/path/Http/Requests/PostStoreRequest.php', $this->fixture('form-requests/return-type-declarations.php')); + + $tokens = $this->blueprint->parse($this->fixture('drafts/readme-example.yaml')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => ['src/path/Http/Requests/PostStoreRequest.php']], $this->subject->output($tree)); + } + /** * @test */ diff --git a/tests/Feature/Generators/Statements/JobGeneratorTest.php b/tests/Feature/Generators/Statements/JobGeneratorTest.php index 7ec197ec..62abe937 100644 --- a/tests/Feature/Generators/Statements/JobGeneratorTest.php +++ b/tests/Feature/Generators/Statements/JobGeneratorTest.php @@ -129,6 +129,7 @@ public function it_respects_configuration() { $this->app['config']->set('blueprint.namespace', 'Some\\App'); $this->app['config']->set('blueprint.app_path', 'src/path'); + $this->app['config']->set('blueprint.use_return_types', true); $this->files->expects('stub') ->with('job.stub') diff --git a/tests/Feature/Generators/Statements/MailGeneratorTest.php b/tests/Feature/Generators/Statements/MailGeneratorTest.php index d08fd206..bc0fa277 100644 --- a/tests/Feature/Generators/Statements/MailGeneratorTest.php +++ b/tests/Feature/Generators/Statements/MailGeneratorTest.php @@ -150,4 +150,34 @@ public function it_respects_configuration() $this->assertEquals(['created' => ['src/path/Mail/ReviewPost.php']], $this->subject->output($tree)); } + + /** + * @test + */ + public function output_using_return_types() + { + $this->app['config']->set('blueprint.namespace', 'Some\\Other\\App'); + $this->app['config']->set('blueprint.app_path', 'src/path'); + $this->app['config']->set('blueprint.use_return_types', true); + + $this->files->expects('stub') + ->with('mail.stub') + ->andReturn($this->stub('mail.stub')); + + $this->files->expects('exists') + ->with('src/path/Mail') + ->andReturnFalse(); + $this->files->expects('exists') + ->with('src/path/Mail/ReviewPost.php') + ->andReturnFalse(); + $this->files->expects('makeDirectory') + ->with('src/path/Mail', 0755, true); + $this->files->expects('put') + ->with('src/path/Mail/ReviewPost.php', $this->fixture('mailables/return-type-declarations.php')); + + $tokens = $this->blueprint->parse($this->fixture('drafts/readme-example.yaml')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => ['src/path/Mail/ReviewPost.php']], $this->subject->output($tree)); + } } diff --git a/tests/Feature/Generators/Statements/NotificationGeneratorTest.php b/tests/Feature/Generators/Statements/NotificationGeneratorTest.php index c63948f2..177e55ce 100644 --- a/tests/Feature/Generators/Statements/NotificationGeneratorTest.php +++ b/tests/Feature/Generators/Statements/NotificationGeneratorTest.php @@ -154,6 +154,36 @@ public function it_respects_configuration() $this->assertEquals(['created' => ['src/path/Notification/ReviewNotification.php']], $this->subject->output($tree)); } + /** + * @test + */ + public function output_using_return_types() + { + $this->app['config']->set('blueprint.namespace', 'Some\\Other\\App'); + $this->app['config']->set('blueprint.app_path', 'src/path'); + $this->app['config']->set('blueprint.use_return_types', true); + + $this->files->expects('stub') + ->with('notification.stub') + ->andReturn($this->stub('notification.stub')); + + $this->files->expects('exists') + ->with('src/path/Notification') + ->andReturnFalse(); + $this->files->expects('exists') + ->with('src/path/Notification/ReviewNotification.php') + ->andReturnFalse(); + $this->files->expects('makeDirectory') + ->with('src/path/Notification', 0755, true); + $this->files->expects('put') + ->with('src/path/Notification/ReviewNotification.php', $this->fixture('notifications/return-type-declarations.php')); + + $tokens = $this->blueprint->parse($this->fixture('drafts/readme-example-notification-facade.yaml')); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => ['src/path/Notification/ReviewNotification.php']], $this->subject->output($tree)); + } + public function notificationDraftProvider() { return [ diff --git a/tests/Feature/Generators/TestGeneratorTest.php b/tests/Feature/Generators/TestGeneratorTest.php index 9baff84e..f53e14d6 100644 --- a/tests/Feature/Generators/TestGeneratorTest.php +++ b/tests/Feature/Generators/TestGeneratorTest.php @@ -307,6 +307,43 @@ public function output_generates_tests_with_models_with_custom_namespace_correct $this->assertEquals(['created' => [$path]], $this->subject->output($tree)); } + /** + * @test + * @environment-setup useLaravel8 + */ + public function output_using_return_types() + { + $definition = 'drafts/readme-example.yaml'; + $path = 'tests/Feature/Http/Controllers/PostControllerTest.php'; + $test = 'tests/return-type-declarations.php'; + + $this->app['config']->set('blueprint.use_return_types', true); + + $this->files->expects('stub') + ->with('test.class.stub') + ->andReturn($this->stub('test.class.stub')); + + $this->files->expects('stub') + ->with('test.case.stub') + ->andReturn($this->stub('test.case.stub')); + + $dirname = dirname($path); + $this->files->expects('exists') + ->with($dirname) + ->andReturnFalse(); + + $this->files->expects('makeDirectory') + ->with($dirname, 0755, true); + + $this->files->expects('put') + ->with($path, $this->fixture($test)); + + $tokens = $this->blueprint->parse($this->fixture($definition)); + $tree = $this->blueprint->analyze($tokens); + + $this->assertEquals(['created' => [$path]], $this->subject->output($tree)); + } + public function controllerTreeDataProvider() { return [ diff --git a/tests/fixtures/controllers/return-type-declarations.php b/tests/fixtures/controllers/return-type-declarations.php new file mode 100644 index 00000000..6ff3075a --- /dev/null +++ b/tests/fixtures/controllers/return-type-declarations.php @@ -0,0 +1,44 @@ +validated()); + + Mail::to($post->author->email)->send(new ReviewPost($post)); + + SyncMedia::dispatch($post); + + event(new NewPost($post)); + + $request->session()->flash('post.title', $post->title); + + return redirect()->route('post.index'); + } +} diff --git a/tests/fixtures/factories/return-type-declarations-laravel8.php b/tests/fixtures/factories/return-type-declarations-laravel8.php new file mode 100644 index 00000000..a3b8a17e --- /dev/null +++ b/tests/fixtures/factories/return-type-declarations-laravel8.php @@ -0,0 +1,33 @@ + $this->faker->sentence(4), + 'content' => $this->faker->paragraphs(3, true), + 'published_at' => $this->faker->dateTime(), + 'author_id' => User::factory(), + ]; + } +} diff --git a/tests/fixtures/form-requests/return-type-declarations.php b/tests/fixtures/form-requests/return-type-declarations.php new file mode 100644 index 00000000..fdfcc0b2 --- /dev/null +++ b/tests/fixtures/form-requests/return-type-declarations.php @@ -0,0 +1,32 @@ + ['required', 'string', 'max:400'], + 'content' => ['required', 'string'], + 'author_id' => ['required', 'integer', 'exists:users,id'], + ]; + } +} diff --git a/tests/fixtures/jobs/job-configured.php b/tests/fixtures/jobs/job-configured.php index df244fe9..0d7b82d9 100644 --- a/tests/fixtures/jobs/job-configured.php +++ b/tests/fixtures/jobs/job-configured.php @@ -29,7 +29,7 @@ public function __construct($post) * * @return void */ - public function handle() + public function handle(): void { // } diff --git a/tests/fixtures/mailables/return-type-declarations.php b/tests/fixtures/mailables/return-type-declarations.php new file mode 100644 index 00000000..4bd172bb --- /dev/null +++ b/tests/fixtures/mailables/return-type-declarations.php @@ -0,0 +1,35 @@ +post = $post; + } + + /** + * Build the message. + * + * @return $this + */ + public function build(): ReviewPost + { + return $this->view('view.name'); + } +} diff --git a/tests/fixtures/migrations/return-type-declarations.php b/tests/fixtures/migrations/return-type-declarations.php new file mode 100644 index 00000000..c2624180 --- /dev/null +++ b/tests/fixtures/migrations/return-type-declarations.php @@ -0,0 +1,35 @@ +id(); + $table->string('title', 400); + $table->longText('content'); + $table->timestamp('published_at')->nullable(); + $table->unsignedBigInteger('author_id'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down(): void + { + Schema::dropIfExists('posts'); + } +} diff --git a/tests/fixtures/models/return-type-declarations.php b/tests/fixtures/models/return-type-declarations.php new file mode 100644 index 00000000..9badb0c0 --- /dev/null +++ b/tests/fixtures/models/return-type-declarations.php @@ -0,0 +1,45 @@ + 'integer', + ]; + + + public function lead(): \Illuminate\Database\Eloquent\Relations\HasOne + { + return $this->hasOne(\App\User::class); + } + + public function methodNames(): \Illuminate\Database\Eloquent\Relations\HasMany + { + return $this->hasMany(\App\ClassName::class); + } + + public function methodName(): \Illuminate\Database\Eloquent\Relations\BelongsTo + { + return $this->belongsTo(\App\ClassName::class); + } +} diff --git a/tests/fixtures/notifications/return-type-declarations.php b/tests/fixtures/notifications/return-type-declarations.php new file mode 100644 index 00000000..4fe1ce1c --- /dev/null +++ b/tests/fixtures/notifications/return-type-declarations.php @@ -0,0 +1,64 @@ +post = $post; + } + + /** + * Get the notification's delivery channels. + * + * @param mixed $notifiable + * @return array + */ + public function via($notifiable): array + { + return ['mail']; + } + + /** + * Get the mail representation of the notification. + * + * @param mixed $notifiable + * @return \Illuminate\Notifications\Messages\MailMessage + */ + public function toMail($notifiable): ReviewNotification + { + return (new MailMessage) + ->line('The introduction to the notification.') + ->action('Notification Action', 'https://laravel.com') + ->line('Thank you for using our application!'); + } + + /** + * Get the array representation of the notification. + * + * @param mixed $notifiable + * @return array + */ + public function toArray($notifiable): array + { + return [ + // + ]; + } +} diff --git a/tests/fixtures/resources/user-return-type-declarations.php b/tests/fixtures/resources/user-return-type-declarations.php new file mode 100644 index 00000000..eb6eabaf --- /dev/null +++ b/tests/fixtures/resources/user-return-type-declarations.php @@ -0,0 +1,23 @@ + $this->id, + 'name' => $this->name, + 'email' => $this->email, + ]; + } +} diff --git a/tests/fixtures/seeders/CommentSeeder-return-type-declarations.php b/tests/fixtures/seeders/CommentSeeder-return-type-declarations.php new file mode 100644 index 00000000..43840e0a --- /dev/null +++ b/tests/fixtures/seeders/CommentSeeder-return-type-declarations.php @@ -0,0 +1,19 @@ +count(5)->create(); + } +} diff --git a/tests/fixtures/seeders/PostSeeder-return-type-declarations.php b/tests/fixtures/seeders/PostSeeder-return-type-declarations.php new file mode 100644 index 00000000..15e37ab1 --- /dev/null +++ b/tests/fixtures/seeders/PostSeeder-return-type-declarations.php @@ -0,0 +1,19 @@ +count(5)->create(); + } +} diff --git a/tests/fixtures/tests/return-type-declarations.php b/tests/fixtures/tests/return-type-declarations.php new file mode 100644 index 00000000..c6346532 --- /dev/null +++ b/tests/fixtures/tests/return-type-declarations.php @@ -0,0 +1,92 @@ +count(3)->create(); + + $response = $this->get(route('post.index')); + + $response->assertOk(); + $response->assertViewIs('post.index'); + $response->assertViewHas('posts'); + } + + + /** + * @test + */ + public function store_uses_form_request_validation(): void + { + $this->assertActionUsesFormRequest( + \App\Http\Controllers\PostController::class, + 'store', + \App\Http\Requests\PostStoreRequest::class + ); + } + + /** + * @test + */ + public function store_saves_and_redirects(): void + { + $title = $this->faker->sentence(4); + $content = $this->faker->paragraphs(3, true); + $author = User::factory()->create(); + + Mail::fake(); + Queue::fake(); + Event::fake(); + + $response = $this->post(route('post.store'), [ + 'title' => $title, + 'content' => $content, + 'author_id' => $author->id, + ]); + + $posts = Post::query() + ->where('title', $title) + ->where('content', $content) + ->where('author_id', $author->id) + ->get(); + $this->assertCount(1, $posts); + $post = $posts->first(); + + $response->assertRedirect(route('post.index')); + $response->assertSessionHas('post.title', $post->title); + + Mail::assertSent(ReviewPost::class, function ($mail) use ($post) { + return $mail->hasTo($post->author->email) && $mail->post->is($post); + }); + Queue::assertPushed(SyncMedia::class, function ($job) use ($post) { + return $job->post->is($post); + }); + Event::assertDispatched(NewPost::class, function ($event) use ($post) { + return $event->post->is($post); + }); + } +}