diff --git a/ReadMe.md b/ReadMe.md index ba443670..9b22c8b8 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -123,6 +123,14 @@ Syntax: --only-resource : Set this flag if you want to create only resource. + --only-nova-resource : Set this flag if you want to create only nova resource. + + --only-nova-tests : Set this flag if you want to create only nova resource tests. + + --resource-name[=RESOURCE-NAME] : Override the default (App\\Nova\\ModelResource) Nova resource name. Used only with --only-nova-tests. + + please, use flag variable with double screening and double quotes without Nova directory, for example --resource-name="Resources\\Banner\\BannerResource" + #### Mode combination options --only-entity : Generate stack of classes to work with entity inside the app (Migration/Model/Service/Repository) diff --git a/composer.json b/composer.json index 825c5857..8053b2aa 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,8 @@ "psr-4": { "RonasIT\\Support\\Tests\\": "tests/", "RonasIT\\Support\\Tests\\Support\\": "tests/Support/", - "App\\Nova\\": "tests/Support/Nova/" + "App\\Nova\\": "tests/Support/Nova/", + "Laravel\\Nova\\": "tests/Support/NovaResource" }, "files": [ "tests/TestCase.php" diff --git a/src/Commands/MakeEntityCommand.php b/src/Commands/MakeEntityCommand.php index 680a28b4..3da3a153 100644 --- a/src/Commands/MakeEntityCommand.php +++ b/src/Commands/MakeEntityCommand.php @@ -55,7 +55,8 @@ class MakeEntityCommand extends Command {--only-seeder : Set this flag if you want to create only seeder.} {--only-nova-resource : Set this flag if you want to create only nova resource.} {--only-nova-tests : Set this flag if you want to create only nova resource tests.} - + {--resource-name= : Override the default Nova resource name. Used only with --only-nova-tests.} + {--methods=CRUD : Set types of methods to create. Affect on routes, requests classes, controller\'s methods and tests methods.} {--i|integer=* : Add integer field to entity.} @@ -218,6 +219,7 @@ protected function runGeneration($generator) ->setFields($this->getFields()) ->setRelations($this->getRelations()) ->setCrudOptions($this->getCrudOptions()) + ->setMetaData(['resource_name' => $this->option('resource-name')]) ->generate(); } diff --git a/src/Generators/EntityGenerator.php b/src/Generators/EntityGenerator.php index e8dd2366..aed5228e 100644 --- a/src/Generators/EntityGenerator.php +++ b/src/Generators/EntityGenerator.php @@ -90,6 +90,11 @@ public function setRelations(RelationsDTO $relations) return $this; } + public function setMetaData(array $data): self + { + return $this; + } + public function __construct() { $this->paths = config('entity-generator.paths'); diff --git a/src/Generators/NovaTestGenerator.php b/src/Generators/NovaTestGenerator.php index 080336ba..d37ec09b 100644 --- a/src/Generators/NovaTestGenerator.php +++ b/src/Generators/NovaTestGenerator.php @@ -8,27 +8,58 @@ use RonasIT\Support\Events\SuccessCreateMessage; use RonasIT\Support\Exceptions\ClassAlreadyExistsException; use RonasIT\Support\Exceptions\ClassNotExistsException; +use RecursiveIteratorIterator; +use RecursiveDirectoryIterator; +use RonasIT\Support\Exceptions\EntityCreateException; +use Generator; class NovaTestGenerator extends AbstractTestsGenerator { - protected $novaModelName; + protected ?string $resourceName; + + protected ?string $fullNovaResourcePath = null; + + protected ?string $shortNovaResourceName; + + protected string $novaPath; + + public function __construct() + { + $this->novaPath = app_path('Nova'); + + parent::__construct(); + } public function generate(): void { if (class_exists(NovaServiceProvider::class)) { - if (!$this->doesNovaResourceExists()) { + if (!$this->classExists('models', $this->model)) { + $this->throwFailureException( + ClassNotExistsException::class, + "Cannot create Nova{$this->model}Resource Test cause {$this->model} does not exist.", + "Create a {$this->model} Model by himself or run command 'php artisan make:entity {$this->model} --only-model'." + ); + } + + $resource = $this->resourceName ?? $this->getNovaResource(); + + $this->shortNovaResourceName = Str::afterLast($resource, '\\'); + + $this->fullNovaResourcePath = "App\\Nova\\{$resource}"; + + if (!class_exists($this->fullNovaResourcePath)) { $this->throwFailureException( ClassNotExistsException::class, - "Cannot create Nova{$this->model}Test cause {$this->model} Nova resource does not exist.", - "Create {$this->model} Nova resource." + "Cannot create Nova{$this->shortNovaResourceName}Test cause {$this->resourceName} Nova resource does not exist.", + "Create {$this->resourceName} Nova resource." ); } - if ($this->classExists('nova', "Nova{$this->model}Test")) { + if ($this->classExists('nova', "Nova{$this->shortNovaResourceName}Test")) { $this->throwFailureException( ClassAlreadyExistsException::class, - "Cannot create Nova{$this->model}Test cause it's already exist.", - "Remove Nova{$this->model}Test." + "Cannot create Nova{$this->shortNovaResourceName}Test cause it's already exist.", + "Remove Nova{$this->shortNovaResourceName}Test." ); } @@ -38,6 +69,13 @@ public function generate(): void } } + public function setMetaData(array $data): self + { + $this->resourceName = !empty($data['resource_name']) ? Str::studly($data['resource_name']) : null; + + return $this; + } + public function generateTests(): void { if (!$this->isStubExists('nova_test')) { @@ -58,9 +96,9 @@ public function generateTests(): void 'filters' => $filters, ]); - $this->saveClass('tests', "Nova{$this->model}Test", $fileContent); + $this->saveClass('tests', "Nova{$this->shortNovaResourceName}Test", $fileContent); - event(new SuccessCreateMessage("Created a new Nova test: Nova{$this->model}Test")); + event(new SuccessCreateMessage("Created a new Nova test: Nova{$this->shortNovaResourceName}Test")); } protected function getActions(): array @@ -83,22 +121,22 @@ protected function getActions(): array protected function loadNovaActions() { - return app("\\App\\Nova\\{$this->novaModelName}")->actions(new NovaRequest()); + return app("{$this->fullNovaResourcePath}")->actions(new NovaRequest()); } protected function loadNovaFields() { - return app("\\App\\Nova\\{$this->novaModelName}")->fields(new NovaRequest()); + return app("{$this->fullNovaResourcePath}")->fields(new NovaRequest()); } protected function loadNovaFilters() { - return app("\\App\\Nova\\{$this->novaModelName}")->filters(new NovaRequest()); + return app("{$this->fullNovaResourcePath}")->filters(new NovaRequest()); } public function getTestClassName(): string { - return "Nova{$this->model}Test"; + return "Nova{$this->shortNovaResourceName}Test"; } protected function isFixtureNeeded($type): bool @@ -106,23 +144,59 @@ protected function isFixtureNeeded($type): bool return true; } - protected function doesNovaResourceExists(): bool + protected function getNovaResource(): string + { + $commonResources = $this->getCommonNovaResources(); + + if (count($commonResources) > 1) { + $commonResources = implode(', ', $commonResources); + + $this->throwFailureException( + EntityCreateException::class, + "Cannot create Nova{$this->model}Resource Test cause was found a lot of suitable resources: $commonResources", + "Please, use --resource-name option" + ); + } + + return array_pop($commonResources); + } + + protected function getNovaFiles(): Generator + { + $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->novaPath)); + + foreach ($iterator as $file) { + if ($file->isFile() && $file->getExtension() === 'php') { + yield $file; + } + } + } + + protected function getCommonNovaResources(): array { - $possibleNovaModelNames = [ - "{$this->model}NovaResource", - "{$this->model}Resource", - $this->model - ]; + $resources = []; + + foreach ($this->getNovaFiles() as $file) { + $relativePath = Str::after($file->getPathname(), $this->novaPath . DIRECTORY_SEPARATOR); - foreach ($possibleNovaModelNames as $modelName) { - if ($this->classExists('nova', $modelName)) { - $this->novaModelName = $modelName; + $class = str_replace(['/', '.php'], ['\\', ''], $relativePath); - return true; + if ($this->isNovaResource($class) && $this->isResourceNameContainModel($class)) { + $resources[] = $class; } } - return false; + return $resources; + } + + protected function isNovaResource(string $resource): bool + { + return is_subclass_of("App\\Nova\\{$resource}", "Laravel\\Nova\\Resource"); + } + + protected function isResourceNameContainModel(string $resource): bool + { + return Str::afterLast(str_replace('Resource', '', $resource), '\\') === $this->model; } protected function collectFilters(): array @@ -160,7 +234,7 @@ protected function getFiltersFromFields(): array protected function getFilters(): array { - $filters= []; + $filters = []; $novaResourceFilters = $this->loadNovaFilters(); foreach ($novaResourceFilters as $filter) { diff --git a/stubs/nova_resource.blade.php b/stubs/nova_resource.blade.php index 760d5af9..c51e3512 100644 --- a/stubs/nova_resource.blade.php +++ b/stubs/nova_resource.blade.php @@ -6,6 +6,7 @@ @foreach($types as $fieldType) use Laravel\Nova\Fields\{{$fieldType}}; @endforeach +use Laravel\Nova\Resource; class {{$model}}Resource extends Resource { diff --git a/tests/CommandTest.php b/tests/CommandTest.php index 1f387706..f60804db 100644 --- a/tests/CommandTest.php +++ b/tests/CommandTest.php @@ -8,6 +8,7 @@ use RonasIT\Support\Tests\Support\Command\CommandMockTrait; use RonasIT\Support\Tests\Support\Command\Models\Post; use UnexpectedValueException; +use App\Nova\PostResource; class CommandTest extends TestCase { @@ -51,6 +52,7 @@ public function testCallCommand() $this->mockGenerator(); $this->mockGettingModelInstance(new Post()); + $this->app->instance(PostResource::class, new PostResource()); $this->mockDBTransactionStartRollback(2); $this @@ -79,11 +81,11 @@ public function testCallCommand() $this->assertGeneratedFileEquals('update_request.json', 'tests/fixtures/PostTest/update_post_request.json'); $this->assertGeneratedFileEquals('validation.php', 'lang/en/validation.php'); $this->assertGeneratedFileEquals('nova_resource.php', 'app/Nova/PostResource.php'); - $this->assertGeneratedFileEquals('nova_test.php', 'tests/NovaPostTest.php'); - $this->assertGeneratedFileEquals('nova_dump.php', 'tests/fixtures/NovaPostTest/nova_post_dump.sql'); - $this->assertGeneratedFileEquals('create_request.json', 'tests/fixtures/NovaPostTest/create_post_request.json'); - $this->assertGeneratedFileEquals('create_response.json', 'tests/fixtures/NovaPostTest/create_post_response.json'); - $this->assertGeneratedFileEquals('update_request.json', 'tests/fixtures/NovaPostTest/update_post_request.json'); + $this->assertGeneratedFileEquals('nova_test.php', 'tests/NovaPostResourceTest.php'); + $this->assertGeneratedFileEquals('nova_dump.php', 'tests/fixtures/NovaPostResourceTest/nova_post_dump.sql'); + $this->assertGeneratedFileEquals('create_request.json', 'tests/fixtures/NovaPostResourceTest/create_post_request.json'); + $this->assertGeneratedFileEquals('create_response.json', 'tests/fixtures/NovaPostResourceTest/create_post_response.json'); + $this->assertGeneratedFileEquals('update_request.json', 'tests/fixtures/NovaPostResourceTest/update_post_request.json'); } public function testMakeOnly() @@ -123,6 +125,52 @@ public function testMakeOnly() $this->assertFileDoesNotExist('tests/fixtures/NovaPostTest/update_post_request.json'); } + public function testMakeOnlyNovaTest(): void + { + Carbon::setTestNow('2016-10-20 11:05:00'); + + $this->mockFilesystemWithPostModelAndResource(); + + config([ + 'entity-generator.paths.models' => 'RonasIT\Support\Tests\Support\Command\Models', + 'entity-generator.paths.factories' => 'RonasIT\Support\Tests\Support\Command\Factories', + ]); + + $this->mockGeneratorOnlyNovaTests(); + + $this + ->artisan('make:entity Post --only-nova-tests --resource-name=PostResource') + ->assertSuccessful(); + + $this->assertFileDoesNotExist('app/Repositories/PostRepository.php'); + $this->assertFileDoesNotExist('database/migrations/2016_10_20_110500_posts_create_table.php'); + $this->assertFileDoesNotExist('database/factories/PostFactory.php'); + $this->assertFileDoesNotExist('database/seeders/PostSeeder.php'); + $this->assertFileDoesNotExist('app/Models/Post.php'); + $this->assertFileDoesNotExist('app/Services/PostService.php'); + $this->assertFileDoesNotExist('app/Http/Requests/Post/CreatePostRequest.php'); + $this->assertFileDoesNotExist('app/Http/Requests/Post/GetPostRequest.php'); + $this->assertFileDoesNotExist('app/Http/Requests/Post/SearchPostsRequest.php'); + $this->assertFileDoesNotExist('app/Http/Requests/Post/UpdatePostRequest.php'); + $this->assertFileDoesNotExist('app/Http/Requests/Post/DeletePostRequest.php'); + $this->assertFileDoesNotExist('app/Http/Controllers/PostController.php'); + $this->assertFileDoesNotExist('app/Http/Resources/Post/PostResource.php'); + $this->assertFileDoesNotExist('app/Http/Resources/Post/PostsCollectionResource.php'); + $this->assertFileDoesNotExist('routes/api.php'); + $this->assertFileDoesNotExist('tests/PostTest.php'); + $this->assertFileDoesNotExist('tests/fixtures/PostTest/dump.sql'); + $this->assertFileDoesNotExist('tests/fixtures/PostTest/create_post_request.json'); + $this->assertFileDoesNotExist('tests/fixtures/PostTest/create_post_response.json'); + $this->assertFileDoesNotExist('tests/fixtures/PostTest/update_post_request.json'); + $this->assertFileDoesNotExist('lang/en/validation.php'); + $this->assertFileDoesNotExist('app/Nova/PostResource.php'); + $this->assertGeneratedFileEquals('nova_test.php', 'tests/NovaPostResourceTest.php'); + $this->assertGeneratedFileEquals('nova_dump.php', 'tests/fixtures/NovaPostResourceTest/nova_post_dump.sql'); + $this->assertGeneratedFileEquals('create_request.json', 'tests/fixtures/NovaPostResourceTest/create_post_request.json'); + $this->assertGeneratedFileEquals('create_response.json', 'tests/fixtures/NovaPostResourceTest/create_post_response.json'); + $this->assertGeneratedFileEquals('update_request.json', 'tests/fixtures/NovaPostResourceTest/update_post_request.json'); + } + public function testCallWithNotDefaultConfig() { $this->app->instance('path.base', $this->generatedFileBasePath); diff --git a/tests/NovaTestGeneratorTest.php b/tests/NovaTestGeneratorTest.php index 06297b39..af06d88a 100644 --- a/tests/NovaTestGeneratorTest.php +++ b/tests/NovaTestGeneratorTest.php @@ -2,7 +2,6 @@ namespace RonasIT\Support\Tests; -use RonasIT\Support\Tests\Support\Models\WelcomeBonus; use RonasIT\Support\Events\SuccessCreateMessage; use RonasIT\Support\Events\WarningEvent; use RonasIT\Support\Exceptions\ClassAlreadyExistsException; @@ -10,6 +9,9 @@ use RonasIT\Support\Generators\NovaTestGenerator; use RonasIT\Support\Tests\Support\NovaTestGeneratorTest\NovaTestGeneratorMockTrait; use Laravel\Nova\NovaServiceProvider; +use RonasIT\Support\Tests\Support\Models\WelcomeBonus; +use RonasIT\Support\Exceptions\EntityCreateException; +use RonasIT\Support\Tests\Support\Models\Post; class NovaTestGeneratorTest extends TestCase { @@ -24,68 +26,84 @@ public function setUp(): void public function testGenerateResourceNotExists() { - $this->mockNovaServiceProviderExists(); - $this->mockClass(NovaTestGenerator::class, [ - $this->classExistsMethodCall(['nova', 'PostNovaResource'], false), - $this->classExistsMethodCall(['nova', 'PostResource'], false), - $this->classExistsMethodCall(['nova', 'Post'], false), + $this->classExistsMethodCall(['models', 'News']), ]); + $this->mockNativeGeneratorFunctions( + $this->nativeClassExistsMethodCall([NovaServiceProvider::class, true]), + $this->nativeClassExistsMethodCall(["App\Nova\NewsResource"], false), + ); + $this->assertExceptionThrew( className: ClassNotExistsException::class, - message: 'Cannot create NovaPostTest cause Post Nova resource does not exist. Create Post Nova resource.', + message: 'Cannot create NovaNewsResourceTest cause NewsResource Nova resource does not exist. Create NewsResource Nova resource.', ); app(NovaTestGenerator::class) - ->setModel('Post') + ->setModel('News') + ->setMetaData(['resource_name' => 'NewsResource']) ->generate(); } public function testGenerateNovaTestAlreadyExists() { - $this->mockNovaServiceProviderExists(); - $this->mockClass(NovaTestGenerator::class, [ - $this->classExistsMethodCall(['nova', 'PostNovaResource']), - $this->classExistsMethodCall(['nova', 'NovaPostTest']) + $this->classExistsMethodCall(['models', 'Post']), + $this->classExistsMethodCall(['nova', 'NovaPostResourceTest']), ]); + $this->mockNativeGeneratorFunctions( + $this->nativeClassExistsMethodCall([NovaServiceProvider::class, true]), + $this->nativeClassExistsMethodCall(["App\Nova\Resources\PostResource"]), + ); + $this->assertExceptionThrew( className: ClassAlreadyExistsException::class, - message: "Cannot create NovaPostTest cause it's already exist. Remove NovaPostTest.", + message: "Cannot create NovaPostResourceTest cause it's already exist. Remove NovaPostResourceTest.", ); app(NovaTestGenerator::class) ->setModel('Post') + ->setMetaData(['resource_name' => 'Resources\PostResource']) ->generate(); } public function testNovaTestStubNotExist() { + config([ + 'entity-generator.paths.models' => 'RonasIT/Support/Tests/Support/Models', + 'entity-generator.stubs.nova_test' => 'incorrect_stub', + ]); + $this->mockNativeGeneratorFunctions( $this->nativeClassExistsMethodCall([NovaServiceProvider::class, true]), + $this->nativeClassExistsMethodCall(["App\Nova\WelcomeBonusResource"]), $this->nativeClassExistsMethodCall([WelcomeBonus::class, true]), ); - $this->mockNovaRequestClassCall(); - - config([ - 'entity-generator.paths.models' => 'RonasIT/Support/Tests/Support/Models', - 'entity-generator.stubs.nova_test' => 'incorrect_stub', + $this->mockClass(NovaTestGenerator::class, [ + $this->classExistsMethodCall(['models', 'WelcomeBonus']), + $this->classExistsMethodCall(['nova', 'NovaWelcomeBonusResourceTest'], false), + $this->classExistsMethodCall(['models', 'User'], false), + $this->classExistsMethodCall(['factories', 'WelcomeBonusFactory'], false), + $this->classExistsMethodCall(['factories', 'WelcomeBonusFactory'], false), ]); + $this->mockNovaRequestClassCall(); + $this->mockDBTransactionStartRollback(); app(NovaTestGenerator::class) ->setModel('WelcomeBonus') + ->setMetaData(['resource_name' => 'WelcomeBonusResource']) ->generate(); $this->assertFileDoesNotExist('tests/NovaWelcomeBonusTest.php'); - $this->assertGeneratedFileEquals('dump.sql', 'tests/fixtures/NovaWelcomeBonusTest/nova_welcome_bonus_dump.sql'); - $this->assertGeneratedFileEquals('create_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusTest/create_welcome_bonus_request.json'); - $this->assertGeneratedFileEquals('create_welcome_bonus_response.json', 'tests/fixtures/NovaWelcomeBonusTest/create_welcome_bonus_response.json'); - $this->assertGeneratedFileEquals('update_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusTest/update_welcome_bonus_request.json'); + $this->assertGeneratedFileEquals('dump.sql', 'tests/fixtures/NovaWelcomeBonusResourceTest/nova_welcome_bonus_dump.sql'); + $this->assertGeneratedFileEquals('create_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusResourceTest/create_welcome_bonus_request.json'); + $this->assertGeneratedFileEquals('create_welcome_bonus_response.json', 'tests/fixtures/NovaWelcomeBonusResourceTest/create_welcome_bonus_response.json'); + $this->assertGeneratedFileEquals('update_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusResourceTest/update_welcome_bonus_request.json'); $this->assertEventPushed( className: WarningEvent::class, @@ -95,8 +113,6 @@ className: WarningEvent::class, public function testDumpStubNotExist() { - $this->mockNovaServiceProviderExists(); - $this->mockNovaRequestClassCall(); config([ @@ -104,15 +120,28 @@ public function testDumpStubNotExist() 'entity-generator.stubs.dump' => 'incorrect_stub', ]); + $this->mockClass(NovaTestGenerator::class, [ + $this->classExistsMethodCall(['models', 'WelcomeBonus']), + $this->classExistsMethodCall(['nova', 'NovaWelcomeBonusResourceTest'], false), + $this->classExistsMethodCall(['models', 'User'], false), + $this->classExistsMethodCall(['factories', 'WelcomeBonusFactory'], false), + ]); + + $this->mockNativeGeneratorFunctions( + $this->nativeClassExistsMethodCall([NovaServiceProvider::class, true]), + $this->nativeClassExistsMethodCall(["App\Nova\WelcomeBonusResource"]), + ); + app(NovaTestGenerator::class) ->setModel('WelcomeBonus') + ->setMetaData(['resource_name' => 'WelcomeBonusResource']) ->generate(); - $this->assertGeneratedFileEquals('created_resource_test.php', 'tests/NovaWelcomeBonusTest.php'); - $this->assertFileDoesNotExist('tests/fixtures/NovaWelcomeBonusTest/nova_welcome_bonus_dump.sql'); - $this->assertGeneratedFileEquals('create_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusTest/create_welcome_bonus_request.json'); - $this->assertGeneratedFileEquals('create_welcome_bonus_response.json', 'tests/fixtures/NovaWelcomeBonusTest/create_welcome_bonus_response.json'); - $this->assertGeneratedFileEquals('update_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusTest/update_welcome_bonus_request.json'); + $this->assertGeneratedFileEquals('created_resource_test.php', 'tests/NovaWelcomeBonusResourceTest.php'); + $this->assertFileDoesNotExist('tests/fixtures/NovaWelcomeBonusResourceTest/nova_welcome_bonus_dump.sql'); + $this->assertGeneratedFileEquals('create_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusResourceTest/create_welcome_bonus_request.json'); + $this->assertGeneratedFileEquals('create_welcome_bonus_response.json', 'tests/fixtures/NovaWelcomeBonusResourceTest/create_welcome_bonus_response.json'); + $this->assertGeneratedFileEquals('update_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusResourceTest/update_welcome_bonus_request.json'); $this->assertEventPushed( className: WarningEvent::class, @@ -126,10 +155,112 @@ public function testSuccess() 'entity-generator.paths.models' => 'RonasIT/Support/Tests/Support/Models', ]); + $this->mockClass(NovaTestGenerator::class, [ + $this->classExistsMethodCall(['models', 'WelcomeBonus']), + $this->classExistsMethodCall(['nova', 'NovaWelcomeBonusResourceTest'], false), + $this->classExistsMethodCall(['models', 'User'], false), + $this->classExistsMethodCall(['factories', 'WelcomeBonusFactory'], false), + $this->classExistsMethodCall(['factories', 'WelcomeBonusFactory'], false), + ]); + + $this->mockDBTransactionStartRollback(); + + $this->mockNativeGeneratorFunctions( + $this->nativeClassExistsMethodCall([NovaServiceProvider::class, true]), + $this->nativeClassExistsMethodCall(["App\Nova\WelcomeBonusResource"]), + $this->nativeClassExistsMethodCall([WelcomeBonus::class, true]), + ); + + $this->mockNovaRequestClassCall(); + + app(NovaTestGenerator::class) + ->setModel('WelcomeBonus') + ->setMetaData(['resource_name' => 'WelcomeBonusResource']) + ->generate(); + + $this->assertGeneratedFileEquals('created_resource_test.php', 'tests/NovaWelcomeBonusResourceTest.php'); + $this->assertGeneratedFileEquals('dump.sql', 'tests/fixtures/NovaWelcomeBonusResourceTest/nova_welcome_bonus_dump.sql'); + $this->assertGeneratedFileEquals('create_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusResourceTest/create_welcome_bonus_request.json'); + $this->assertGeneratedFileEquals('create_welcome_bonus_response.json', 'tests/fixtures/NovaWelcomeBonusResourceTest/create_welcome_bonus_response.json'); + $this->assertGeneratedFileEquals('update_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusResourceTest/update_welcome_bonus_request.json'); + } + + public function testWithManySameResources() + { + $this->mockNovaServiceProviderExists(); + + $this->mockNovaRequestClassCall(); + + $this->mockClass(NovaTestGenerator::class, [ + $this->classExistsMethodCall(['models', 'WelcomeBonus']), + ]); + + $this->assertExceptionThrew( + className: EntityCreateException::class, + message: 'Cannot create NovaWelcomeBonusResource Test cause was found a lot of suitable resources: WelcomeBonusResource, Resources\WelcomeBonus Please, use --resource-name option', + ); + + app(NovaTestGenerator::class) + ->setModel('WelcomeBonus') + ->setMetaData(['resource_name' => null]) + ->generate(); + } + + public function testSuccessWithoutSetMetaData() + { + config([ + 'entity-generator.paths.models' => 'RonasIT/Support/Tests/Support/Models', + ]); + + $this->mockClass(NovaTestGenerator::class, [ + $this->classExistsMethodCall(['models', 'Post']), + $this->classExistsMethodCall(['nova', 'NovaPostResourceTest'], false), + $this->classExistsMethodCall(['models', 'User'], false), + $this->classExistsMethodCall(['factories', 'PostFactory'], false), + $this->classExistsMethodCall(['factories', 'PostFactory'], false), + ]); + + $this->mockDBTransactionStartRollback(); + + $this->mockNativeGeneratorFunctions( + $this->nativeClassExistsMethodCall([NovaServiceProvider::class, true]), + $this->nativeClassExistsMethodCall(["App\Nova\Resources\PostResource"]), + $this->nativeClassExistsMethodCall([Post::class, true]), + ); + + $this->mockNovaRequestClassCall(); + + app(NovaTestGenerator::class) + ->setModel('Post') + ->setMetaData(['resource_name' => null]) + ->generate(); + + $this->assertGeneratedFileEquals('created_post_resource_test.php', 'tests/NovaPostResourceTest.php'); + $this->assertGeneratedFileEquals('post_dump.sql', 'tests/fixtures/NovaPostResourceTest/nova_post_dump.sql'); + $this->assertGeneratedFileEquals('create_post_request.json', 'tests/fixtures/NovaPostResourceTest/create_post_request.json'); + $this->assertGeneratedFileEquals('create_post_response.json', 'tests/fixtures/NovaPostResourceTest/create_post_response.json'); + $this->assertGeneratedFileEquals('update_post_request.json', 'tests/fixtures/NovaPostResourceTest/update_post_request.json'); + } + + public function testSuccessWithNestedFile(): void + { + config([ + 'entity-generator.paths.models' => 'RonasIT/Support/Tests/Support/Models', + ]); + + $this->mockClass(NovaTestGenerator::class, [ + $this->classExistsMethodCall(['models', 'WelcomeBonus']), + $this->classExistsMethodCall(['nova', 'NovaWelcomeBonusDraftResourceTest'], false), + $this->classExistsMethodCall(['models', 'User'], false), + $this->classExistsMethodCall(['factories', 'WelcomeBonusFactory'], false), + $this->classExistsMethodCall(['factories', 'WelcomeBonusFactory'], false), + ]); + $this->mockDBTransactionStartRollback(); $this->mockNativeGeneratorFunctions( $this->nativeClassExistsMethodCall([NovaServiceProvider::class, true]), + $this->nativeClassExistsMethodCall(['App\Nova\Resources\WelcomeBonusDraftResource']), $this->nativeClassExistsMethodCall([WelcomeBonus::class, true]), ); @@ -137,13 +268,30 @@ public function testSuccess() app(NovaTestGenerator::class) ->setModel('WelcomeBonus') + ->setMetaData(['resource_name' => 'Resources\WelcomeBonusDraftResource']) ->generate(); - $this->assertGeneratedFileEquals('created_resource_test.php', 'tests/NovaWelcomeBonusTest.php'); - $this->assertGeneratedFileEquals('dump.sql', 'tests/fixtures/NovaWelcomeBonusTest/nova_welcome_bonus_dump.sql'); - $this->assertGeneratedFileEquals('create_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusTest/create_welcome_bonus_request.json'); - $this->assertGeneratedFileEquals('create_welcome_bonus_response.json', 'tests/fixtures/NovaWelcomeBonusTest/create_welcome_bonus_response.json'); - $this->assertGeneratedFileEquals('update_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusTest/update_welcome_bonus_request.json'); + $this->assertGeneratedFileEquals('created_resource_test.php', 'tests/NovaWelcomeBonusDraftResourceTest.php'); + $this->assertGeneratedFileEquals('dump.sql', 'tests/fixtures/NovaWelcomeBonusDraftResourceTest/nova_welcome_bonus_dump.sql'); + $this->assertGeneratedFileEquals('create_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusDraftResourceTest/create_welcome_bonus_request.json'); + $this->assertGeneratedFileEquals('create_welcome_bonus_response.json', 'tests/fixtures/NovaWelcomeBonusDraftResourceTest/create_welcome_bonus_response.json'); + $this->assertGeneratedFileEquals('update_welcome_bonus_request.json', 'tests/fixtures/NovaWelcomeBonusDraftResourceTest/update_welcome_bonus_request.json'); + } + + public function testSetIncorrectModel(): void + { + $this->mockNovaServiceProviderExists(); + + $this->assertExceptionThrew( + className: ClassNotExistsException::class, + message: "Cannot create NovaSomeUndefinedModelResource Test cause SomeUndefinedModel does not exist. " + . "Create a SomeUndefinedModel Model by himself or run command 'php artisan make:entity SomeUndefinedModel --only-model'.", + ); + + app(NovaTestGenerator::class) + ->setModel('SomeUndefinedModel') + ->setMetaData(['resource_name' => null]) + ->generate(); } public function testGenerateNovaPackageNotInstall() diff --git a/tests/Support/Command/CommandMockTrait.php b/tests/Support/Command/CommandMockTrait.php index 9813dfdc..e7ab624a 100644 --- a/tests/Support/Command/CommandMockTrait.php +++ b/tests/Support/Command/CommandMockTrait.php @@ -5,7 +5,6 @@ use RonasIT\Support\Generators\NovaTestGenerator; use RonasIT\Support\Tests\Support\FileSystemMock; use RonasIT\Support\Tests\Support\GeneratorMockTrait; -use RonasIT\Support\Tests\Support\NovaResourceGeneratorTest\SchemaManager; trait CommandMockTrait { @@ -21,11 +20,22 @@ public function mockFilesystemPostModelExists(): void $fileSystemMock->setStructure(); } + public function mockFilesystemWithPostModelAndResource(): void + { + $fileSystemMock = new FileSystemMock(); + + $fileSystemMock->models = ['Post.php' => $this->mockPhpFileContent()]; + $fileSystemMock->novaModels = ['PostResource.php' => $this->mockPhpFileContent()]; + $fileSystemMock->config = ['entity-generator.php' => '']; + + $fileSystemMock->setStructure(); + } + public function mockFilesystem(): void { $fileSystemMock = new FileSystemMock(); - $fileSystemMock->routes = [ 'api.php' => $this->mockPhpFileContent()]; + $fileSystemMock->routes = ['api.php' => $this->mockPhpFileContent()]; $fileSystemMock->config = ['entity-generator.php' => '']; $fileSystemMock->translations = []; @@ -53,6 +63,38 @@ public function mockGenerator(): void $this->nativeClassExistsMethodCall(['RonasIT\Support\Tests\Support\Command\Models\Post', true]), $this->nativeClassExistsMethodCall(['Laravel\Nova\NovaServiceProvider', true]), $this->nativeClassExistsMethodCall(['Laravel\Nova\NovaServiceProvider', true]), + $this->nativeClassExistsMethodCall(['App\Nova\PostResource']), + $this->nativeClassExistsMethodCall(['RonasIT\Support\Tests\Support\Command\Models\Post', true]), + ); + } + + public function mockGeneratorOnlyNovaTests(): void + { + $this->mockClass(NovaTestGenerator::class, [ + $this->functionCall( + name: 'loadNovaActions', + result: [], + ), + $this->functionCall( + name: 'loadNovaFields', + result: [], + ), + $this->functionCall( + name: 'loadNovaFilters', + result: [], + ), + $this->classExistsMethodCall(['models', 'Post']), + $this->classExistsMethodCall(['nova', 'NovaPostResourceTest'], false), + $this->classExistsMethodCall(['models', 'User'], false), + $this->classExistsMethodCall(['factories', 'PostFactory']), + $this->classExistsMethodCall(['factories', 'PostFactory']), + ]); + + $this->mockDBTransactionStartRollback(); + + $this->mockNativeGeneratorFunctions( + $this->nativeClassExistsMethodCall(['Laravel\Nova\NovaServiceProvider', true]), + $this->nativeClassExistsMethodCall(['App\Nova\PostResource']), $this->nativeClassExistsMethodCall(['RonasIT\Support\Tests\Support\Command\Models\Post', true]), ); } diff --git a/tests/Support/FileSystemMock.php b/tests/Support/FileSystemMock.php index 11c84f27..425cbf5d 100644 --- a/tests/Support/FileSystemMock.php +++ b/tests/Support/FileSystemMock.php @@ -8,6 +8,7 @@ class FileSystemMock { public ?array $novaModels = null; + public ?array $novaResources = null; public ?array $novaActions = null; public ?array $models = null; public ?array $controllers = null; @@ -32,6 +33,18 @@ public function setStructure(): void } } + if (!is_null($this->novaResources)) { + if (!array_key_exists('Nova', $structure['app'])) { + $structure['app']['Nova'] = []; + } + + $structure['app']['Nova']['Resources'] = []; + + foreach ($this->novaResources as $novaResource => $content) { + $structure['app']['Nova']['Resources'][$novaResource] = $content; + } + } + if (!is_null($this->novaActions)) { if (!array_key_exists('Nova', $structure['app'])) { $structure['app']['Nova'] = []; diff --git a/tests/Support/Models/News.php b/tests/Support/Models/News.php new file mode 100644 index 00000000..e1c6c5ac --- /dev/null +++ b/tests/Support/Models/News.php @@ -0,0 +1,25 @@ + $this->mockPhpFileContent(), ]; + $fileSystemMock->novaResources = [ + 'WelcomeBonusDraftResource.php' => $this->mockPhpFileContent(), + 'WelcomeBonus.php' => $this->mockPhpFileContent(), + 'PostResource.php' => $this->mockPhpFileContent(), + ]; + $fileSystemMock->models = [ 'WelcomeBonus.php' => $this->mockPhpFileContent(), + 'Post.php' => $this->mockPhpFileContent(), + 'News.php' => $this->mockPhpFileContent(), ]; $fileSystemMock->testFixtures = [ diff --git a/tests/fixtures/CommandTest/nova_resource.php b/tests/fixtures/CommandTest/nova_resource.php index b653b10b..a43b5419 100644 --- a/tests/fixtures/CommandTest/nova_resource.php +++ b/tests/fixtures/CommandTest/nova_resource.php @@ -6,6 +6,7 @@ use Illuminate\Http\Request; use Laravel\Nova\Fields\ID; use Laravel\Nova\Fields\Text; +use Laravel\Nova\Resource; class PostResource extends Resource { diff --git a/tests/fixtures/NovaResourceGeneratorTest/created_resource.php b/tests/fixtures/NovaResourceGeneratorTest/created_resource.php index 742c6158..f0de6419 100644 --- a/tests/fixtures/NovaResourceGeneratorTest/created_resource.php +++ b/tests/fixtures/NovaResourceGeneratorTest/created_resource.php @@ -7,6 +7,7 @@ use Laravel\Nova\Fields\Boolean; use Laravel\Nova\Fields\Text; use Laravel\Nova\Fields\ID; +use Laravel\Nova\Resource; class PostResource extends Resource { diff --git a/tests/fixtures/NovaResourceGeneratorTest/created_resource_without_command_line_fields.php b/tests/fixtures/NovaResourceGeneratorTest/created_resource_without_command_line_fields.php index 9d3f57e1..7ae28fb4 100644 --- a/tests/fixtures/NovaResourceGeneratorTest/created_resource_without_command_line_fields.php +++ b/tests/fixtures/NovaResourceGeneratorTest/created_resource_without_command_line_fields.php @@ -6,6 +6,7 @@ use Illuminate\Http\Request; use Laravel\Nova\Fields\ID; use Laravel\Nova\Fields\Text; +use Laravel\Nova\Resource; class PostResource extends Resource { diff --git a/tests/fixtures/NovaTestGeneratorTest/create_post_request.json b/tests/fixtures/NovaTestGeneratorTest/create_post_request.json new file mode 100644 index 00000000..538b482c --- /dev/null +++ b/tests/fixtures/NovaTestGeneratorTest/create_post_request.json @@ -0,0 +1,4 @@ +{ + "title": 1, + "name": 1 +} \ No newline at end of file diff --git a/tests/fixtures/NovaTestGeneratorTest/create_post_response.json b/tests/fixtures/NovaTestGeneratorTest/create_post_response.json new file mode 100644 index 00000000..ea1ee8ea --- /dev/null +++ b/tests/fixtures/NovaTestGeneratorTest/create_post_response.json @@ -0,0 +1,5 @@ +{ + "id": 1, + "title": 1, + "name": 1 +} \ No newline at end of file diff --git a/tests/fixtures/NovaTestGeneratorTest/created_post_resource_test.php b/tests/fixtures/NovaTestGeneratorTest/created_post_resource_test.php new file mode 100644 index 00000000..c3ce5c8d --- /dev/null +++ b/tests/fixtures/NovaTestGeneratorTest/created_post_resource_test.php @@ -0,0 +1,261 @@ +skipDocumentationCollecting(); + } + + public function testCreate(): void + { + $data = $this->getJsonFixture('create_post_request'); + + $response = $this->novaActingAs(self::$user)->novaCreateResourceAPICall(Post::class, $data); + + $response->assertCreated(); + + $this->assertEqualsFixture('create_post_response', $response->json()); + + // TODO: Need to remove last argument after first successful start + self::$postState->assertChangesEqualsFixture('create_posts_state', true); + } + + public function testCreateNoAuth(): void + { + $response = $this->novaCreateResourceAPICall(Post::class); + + $response->assertUnauthorized(); + + self::$postState->assertNotChanged(); + } + + public function testCreateValidationError(): void + { + $response = $this->novaActingAs(self::$user)->novaCreateResourceAPICall(Post::class); + + $response->assertUnprocessable(); + + // TODO: Need to remove last argument after first successful start + $this->assertEqualsFixture('create_validation_response', $response->json(), true); + + self::$postState->assertNotChanged(); + } + + public function testUpdate(): void + { + $data = $this->getJsonFixture('update_post_request'); + + $response = $this->novaActingAs(self::$user)->novaUpdateResourceAPICall(Post::class, 1, $data); + + $response->assertNoContent(); + + // TODO: Need to remove last argument after first successful start + self::$postState->assertChangesEqualsFixture('update_posts_state', true); + } + + public function testUpdateNotExists(): void + { + $data = $this->getJsonFixture('update_post_request'); + + $response = $this->novaActingAs(self::$user)->novaUpdateResourceAPICall(Post::class, 0, $data); + + $response->assertNotFound(); + } + + public function testUpdateNoAuth(): void + { + $response = $this->novaUpdateResourceAPICall(Post::class, 1); + + $response->assertUnauthorized(); + } + + public function testUpdateValidationError(): void + { + $response = $this->novaActingAs(self::$user)->novaUpdateResourceAPICall(Post::class, 4); + + $response->assertUnprocessable(); + + // TODO: Need to remove last argument after first successful start + $this->assertEqualsFixture('update_validation_response', $response->json(), true); + } + + public function testGetUpdatableFields(): void + { + $response = $this->novaActingAs(self::$user)->novaGetUpdatableFieldsAPICall(Post::class, 1); + + $response->assertOk(); + + // TODO: Need to remove last argument after first successful start + $this->assertEqualsFixture('get_updatable_fields_response', $response->json(), true); + } + + public function testDelete(): void + { + $response = $this->novaActingAs(self::$user)->novaDeleteResourceAPICall(Post::class, [1, 2]); + + $response->assertOk(); + + // TODO: Need to remove last argument after first successful start + self::$postState->assertChangesEqualsFixture('delete_posts_state', true); + } + + public function testDeleteNotExists(): void + { + $response = $this->novaActingAs(self::$user)->novaDeleteResourceAPICall(Post::class, [0]); + + $response->assertNotFound(); + } + + public function testDeleteNoAuth(): void + { + $response = $this->novaDeleteResourceAPICall(Post::class, [1, 2]); + + $response->assertUnauthorized(); + } + + public function testGet(): void + { + $response = $this->novaActingAs(self::$user)->novaGetResourceAPICall(Post::class, 1); + + $response->assertOk(); + + // TODO: Need to remove last argument after first successful start + $this->assertEqualsFixture('get_post_response', $response->json(), true); + } + + public function testGetNotExists(): void + { + $response = $this->novaActingAs(self::$user)->novaGetResourceAPICall(Post::class, 0); + + $response->assertNotFound(); + } + + public function testGetNoAuth(): void + { + $response = $this->novaGetResourceAPICall(Post::class, 1); + + $response->assertUnauthorized(); + } + + public function testSearchUnauthorized(): void + { + $response = $this->novaSearchResourceAPICall(Post::class); + + $response->assertUnauthorized(); + } + + public function testGetFieldsVisibleOnCreate(): void + { + $response = $this->novaActingAs(self::$user)->novaGetCreationFieldsAPICall(Post::class); + + $response->assertOk(); + + // TODO: Need to remove last argument after first successful start + $this->assertEqualsFixture('get_fields_visible_on_create_response', $response->json(), true); + } + + public static function getRunPostActionsData(): array + { + return [ + [ + 'action' => PublishPostAction::class, + 'request' => [ + 'resources' => '1,2', + ], + 'state' => 'run_publish_post_action_state', + ], + [ + 'action' => UnPublishPostAction::class, + 'request' => [ + 'resources' => '1,2', + ], + 'state' => 'run_un_publish_post_action_state', + ], + ]; + } + + #[DataProvider('getRunPostActionsData')] + public function testRunPostActions($action, $request, $state): void + { + $response = $this->novaActingAs(self::$user)->novaRunActionAPICall(Post::class, $action, $request); + + $response->assertOk(); + + $this->assertEmpty($response->getContent()); + + // TODO: Need to remove last argument after first successful start + self::$postState->assertChangesEqualsFixture($state, true); + } + + public static function getPostActionsData(): array + { + return [ + [ + 'resources' => [1, 2], + 'fixture' => 'get_post_actions_publish_post_action', + ], + [ + 'resources' => [1, 2], + 'fixture' => 'get_post_actions_un_publish_post_action', + ], + ]; + } + + #[DataProvider('getPostActionsData')] + public function testGetPostActions(array $resources, string $fixture): void + { + $response = $this->novaActingAs(self::$user)->novaGetActionsAPICall(Post::class, $resources); + + $response->assertOk(); + + // TODO: Need to remove last argument after first successful start + $this->assertEqualsFixture($fixture, $response->json(), true); + } + + public static function getPostFiltersData(): array + { + return [ + [ + 'request' => [ + 'TextField:description_field' => $this->novaSearchParams(['search term']), + ], + 'fixture' => 'filter_post_by_text_field', + ], + [ + 'request' => [ + 'RonasIT\Support\Tests\Support\NovaTestGeneratorTest\CreatedAtFilter' => $this->novaSearchParams(['search term']), + ], + 'fixture' => 'filter_post_by_created_at_filter', + ], + ]; + } + + #[DataProvider('getPostFiltersData')] + public function testFilterPost(array $request, string $fixture): void + { + $response = $this->novaActingAs(self::$user)->novaSearchResourceAPICall(Post::class, $request); + + $response->assertOk(); + + // TODO: Need to remove last argument after first successful start + $this->assertEqualsFixture($fixture, $response->json(), true); + } +} diff --git a/tests/fixtures/NovaTestGeneratorTest/post_dump.sql b/tests/fixtures/NovaTestGeneratorTest/post_dump.sql new file mode 100644 index 00000000..15371a53 --- /dev/null +++ b/tests/fixtures/NovaTestGeneratorTest/post_dump.sql @@ -0,0 +1,3 @@ +INSERT INTO "posts"(id, title, name, created_at, updated_at) VALUES + (1, 1, 1, '2016-10-20 11:05:00', '2016-10-20 11:05:00'); + diff --git a/tests/fixtures/NovaTestGeneratorTest/update_post_request.json b/tests/fixtures/NovaTestGeneratorTest/update_post_request.json new file mode 100644 index 00000000..538b482c --- /dev/null +++ b/tests/fixtures/NovaTestGeneratorTest/update_post_request.json @@ -0,0 +1,4 @@ +{ + "title": 1, + "name": 1 +} \ No newline at end of file