diff --git a/src/Maker/MakeCrud.php b/src/Maker/MakeCrud.php index e2a6ea53b..b2817e35d 100644 --- a/src/Maker/MakeCrud.php +++ b/src/Maker/MakeCrud.php @@ -14,8 +14,11 @@ use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; use Doctrine\Inflector\InflectorFactory; use Doctrine\ORM\EntityManagerInterface; +use Doctrine\ORM\EntityRepository; use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Bundle\FrameworkBundle\KernelBrowser; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\MakerBundle\ConsoleStyle; use Symfony\Bundle\MakerBundle\DependencyBuilder; use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper; @@ -46,6 +49,7 @@ final class MakeCrud extends AbstractMaker private $formTypeRenderer; private $inflector; private $controllerClassName; + private $generateTests = false; public function __construct(DoctrineHelper $doctrineHelper, FormTypeRenderer $formTypeRenderer) { @@ -95,6 +99,8 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma sprintf('Choose a name for your controller class (e.g. %s)', $defaultControllerClass), $defaultControllerClass ); + + $this->generateTests = $io->confirm('Do you want to generate tests for the controller?. [Experimental]', false); } public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void @@ -232,6 +238,49 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen ); } + if ($this->generateTests) { + $testClassDetails = $generator->createClassNameDetails( + $entityClassDetails->getRelativeNameWithoutSuffix(), + 'Test\\Controller\\', + 'ControllerTest' + ); + + $useStatements = new UseStatementGenerator([ + $entityClassDetails->getFullName(), + WebTestCase::class, + KernelBrowser::class, + $repositoryClassName, + ]); + + $usesEntityManager = EntityManagerInterface::class === $repositoryClassName; + + if ($usesEntityManager) { + $useStatements->addUseStatement(EntityRepository::class); + } + + $generator->generateFile( + 'tests/Controller/'.$testClassDetails->getShortName().'.php', + $usesEntityManager ? 'crud/test/Test.EntityManager.tpl.php' : 'crud/test/Test.tpl.php', + [ + 'use_statements' => $useStatements, + 'entity_full_class_name' => $entityClassDetails->getFullName(), + 'entity_class_name' => $entityClassDetails->getShortName(), + 'entity_var_singular' => $entityVarSingular, + 'route_path' => Str::asRoutePath($controllerClassDetails->getRelativeNameWithoutSuffix()), + 'route_name' => $routeName, + 'class_name' => Str::getShortClassName($testClassDetails->getFullName()), + 'namespace' => Str::getNamespace($testClassDetails->getFullName()), + 'form_fields' => $entityDoctrineDetails->getFormFields(), + 'repository_class_name' => $usesEntityManager ? EntityManagerInterface::class : $repositoryVars['repository_class_name'], + 'form_field_prefix' => strtolower(Str::asSnakeCase($entityTwigVarSingular)), + ] + ); + + if (!class_exists(WebTestCase::class)) { + $io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.'); + } + } + $generator->writeChanges(); $this->writeSuccessMessage($io); diff --git a/src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php b/src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php new file mode 100644 index 000000000..a951d7088 --- /dev/null +++ b/src/Resources/skeleton/crud/test/Test.EntityManager.tpl.php @@ -0,0 +1,123 @@ + + + +namespace ; + + + +class extends WebTestCase +{ + + private $client; + + private $manager; + + private $repository; + private $path = '/'; + + protected function setUp(): void + { + $this->client = static::createClient(); + $this->manager = (static::getContainer()->get('doctrine'))->getManager(); + $this->repository = $this->manager->getRepository(::class); + + foreach ($this->repository->findAll() as $object) { + $this->manager->remove($object); + } + + $this->manager->flush(); + } + + public function testIndex(): void + { + $crawler = $this->client->request('GET', $this->path); + + self::assertResponseStatusCodeSame(200); + self::assertPageTitleContains(' index'); + + // Use the $crawler to perform additional assertions e.g. + // self::assertSame('Some text on the page', $crawler->filter('.p')->first()); + } + + public function testNew(): void + { + $this->markTestIncomplete(); + $this->client->request('GET', sprintf('%snew', $this->path)); + + self::assertResponseStatusCodeSame(200); + + $this->client->submitForm('Save', [ + $typeOptions): ?> + '[]' => 'Testing', + + ]); + + self::assertResponseRedirects('/sweet/food/'); + + self::assertSame(1, $this->getRepository()->count([])); + } + + public function testShow(): void + { + $this->markTestIncomplete(); + $fixture = new (); + $typeOptions): ?> + $fixture->set('My Title'); + + + $this->repository->add($fixture, true); + + $this->client->request('GET', sprintf('%s%s', $this->path, $fixture->getId())); + + self::assertResponseStatusCodeSame(200); + self::assertPageTitleContains(''); + + // Use assertions to check that the properties are properly displayed. + } + + public function testEdit(): void + { + $this->markTestIncomplete(); + $fixture = new (); + $typeOptions): ?> + $fixture->set('Value'); + + + $this->manager->persist($fixture); + $this->manager->flush(); + + $this->client->request('GET', sprintf('%s%s/edit', $this->path, $fixture->getId())); + + $this->client->submitForm('Update', [ + $typeOptions): ?> + '[]' => 'Something New', + + ]); + + self::assertResponseRedirects('/'); + + $fixture = $this->repository->findAll(); + + $typeOptions): ?> + self::assertSame('Something New', $fixture[0]->get()); + + } + + public function testRemove(): void + { + $this->markTestIncomplete(); + $fixture = new (); + $typeOptions): ?> + $fixture->set('Value'); + + + $$this->manager->remove($fixture); + $this->manager->flush(); + + $this->client->request('GET', sprintf('%s%s', $this->path, $fixture->getId())); + $this->client->submitForm('Delete'); + + self::assertResponseRedirects('/'); + self::assertSame(0, $this->repository->count([])); + } +} diff --git a/src/Resources/skeleton/crud/test/Test.tpl.php b/src/Resources/skeleton/crud/test/Test.tpl.php new file mode 100644 index 000000000..8404bec21 --- /dev/null +++ b/src/Resources/skeleton/crud/test/Test.tpl.php @@ -0,0 +1,116 @@ + + + +namespace ; + + + +class extends WebTestCase +{ + + private $client; + + private $repository; + private $path = '/'; + + protected function setUp(): void + { + $this->client = static::createClient(); + $this->repository = (static::getContainer()->get('doctrine'))->getRepository(::class); + + foreach ($this->repository->findAll() as $object) { + $this->repository->remove($object, true); + } + } + + public function testIndex(): void + { + $crawler = $this->client->request('GET', $this->path); + + self::assertResponseStatusCodeSame(200); + self::assertPageTitleContains(' index'); + + // Use the $crawler to perform additional assertions e.g. + // self::assertSame('Some text on the page', $crawler->filter('.p')->first()); + } + + public function testNew(): void + { + $this->markTestIncomplete(); + $this->client->request('GET', sprintf('%snew', $this->path)); + + self::assertResponseStatusCodeSame(200); + + $this->client->submitForm('Save', [ + $typeOptions): ?> + '[]' => 'Testing', + + ]); + + self::assertResponseRedirects('/sweet/food/'); + + self::assertSame(1, $this->repository->count([])); + } + + public function testShow(): void + { + $this->markTestIncomplete(); + $fixture = new (); + $typeOptions): ?> + $fixture->set('My Title'); + + + $this->repository->add($fixture, true); + + $this->client->request('GET', sprintf('%s%s', $this->path, $fixture->getId())); + + self::assertResponseStatusCodeSame(200); + self::assertPageTitleContains(''); + + // Use assertions to check that the properties are properly displayed. + } + + public function testEdit(): void + { + $this->markTestIncomplete(); + $fixture = new (); + $typeOptions): ?> + $fixture->set('My Title'); + + + $this->repository->add($fixture, true); + + $this->client->request('GET', sprintf('%s%s/edit', $this->path, $fixture->getId())); + + $this->client->submitForm('Update', [ + $typeOptions): ?> + '[]' => 'Something New', + + ]); + + self::assertResponseRedirects('/'); + + $fixture = $this->repository->findAll(); + + $typeOptions): ?> + self::assertSame('Something New', $fixture[0]->get()); + + } + + public function testRemove(): void + { + $this->markTestIncomplete(); + $fixture = new (); + $typeOptions): ?> + $fixture->set('My Title'); + + + $this->repository->add($fixture, true); + + $this->client->request('GET', sprintf('%s%s', $this->path, $fixture->getId())); + $this->client->submitForm('Delete'); + + self::assertResponseRedirects('/'); + self::assertSame(0, $this->repository->count([])); + } +} diff --git a/tests/Maker/MakeCrudTest.php b/tests/Maker/MakeCrudTest.php index abb5a249b..5d81d1eaf 100644 --- a/tests/Maker/MakeCrudTest.php +++ b/tests/Maker/MakeCrudTest.php @@ -35,9 +35,9 @@ public function getTestDetails(): \Generator ); $output = $runner->runMaker([ - // entity class name - 'SweetFood', - '', // default controller + 'SweetFood', // entity class name + '', // default controller, + 'n', // Generate Tests ]); $this->assertStringContainsString('created: src/Controller/SweetFoodController.php', $output); @@ -55,9 +55,9 @@ public function getTestDetails(): \Generator ); $output = $runner->runMaker([ - // entity class name - 'SweetFood', - 'SweetFoodAdminController', // default controller + 'SweetFood', // entity class name + 'SweetFoodAdminController', // default controller, + 'y', // Generate Tests ]); $this->assertStringContainsString('created: src/Controller/SweetFoodAdminController.php', $output); @@ -67,6 +67,55 @@ public function getTestDetails(): \Generator }), ]; + yield 'it_generates_crud_with_tests' => [$this->createMakerTest() + ->addExtraDependencies('symfony/test-pack') + ->run(function (MakerTestRunner $runner) { + $runner->copy( + $this->getFixturePath('SweetFood.php', $runner), + 'src/Entity/SweetFood.php' + ); + + $output = $runner->runMaker([ + 'SweetFood', // Entity Class Name + '', // Default Controller, + 'y', // Generate Tests + ]); + + $this->assertStringContainsString('created: src/Controller/SweetFoodController.php', $output); + $this->assertStringContainsString('created: src/Form/SweetFoodType.php', $output); + $this->assertStringContainsString('created: tests/Controller/SweetFoodControllerTest.php', $output); + + $this->runCrudTest($runner, 'it_generates_basic_crud.php'); + }), + ]; + + yield 'it_generates_crud_custom_repository_with_test' => [$this->createMakerTest() + ->addExtraDependencies('symfony/test-pack') + ->run(function (MakerTestRunner $runner) { + $runner->copy( + $this->getFixturePath('SweetFoodCustomRepository.php', $runner), + 'src/Entity/SweetFood.php' + ); + + $runner->copy( + 'make-crud/SweetFoodRepository.php', + 'src/Repository/SweetFoodRepository.php' + ); + + $output = $runner->runMaker([ + 'SweetFood', // Entity Class Name + '', // Default Controller, + 'y', // Generate Tests + ]); + + $this->assertStringContainsString('created: src/Controller/SweetFoodController.php', $output); + $this->assertStringContainsString('created: src/Form/SweetFoodType.php', $output); + $this->assertStringContainsString('created: tests/Controller/SweetFoodControllerTest.php', $output); + + $this->runCrudTest($runner, 'it_generates_basic_crud.php'); + }), + ]; + yield 'it_generates_crud_with_custom_root_namespace' => [$this->createMakerTest() ->changeRootNamespace('Custom') ->run(function (MakerTestRunner $runner) { @@ -81,9 +130,9 @@ public function getTestDetails(): \Generator ); $output = $runner->runMaker([ - // entity class name - 'SweetFood', - '', // default controller + 'SweetFood', // entity class name + '', // default controller, + 'n', // Generate Tests ]); $this->assertStringContainsString('created: src/Controller/SweetFoodController.php', $output); @@ -105,9 +154,9 @@ public function getTestDetails(): \Generator ); $output = $runner->runMaker([ - // entity class name - 'SweetFood', - '', // default controller + 'SweetFood', // entity class name + '', // default controller, + 'n', // Generate Tests ]); $this->assertStringContainsString('created: src/Controller/SweetFoodController.php', $output); @@ -132,9 +181,9 @@ public function getTestDetails(): \Generator $runner->deleteFile('templates/base.html.twig'); $output = $runner->runMaker([ - // entity class name - 'SweetFood', - '', // default controller + 'SweetFood', // entity class name + '', // default controller, + 'n', // Generate Tests ]); $this->assertStringContainsString('created: src/Controller/SweetFoodController.php', $output);