diff --git a/src/Maker/MakeRegistrationForm.php b/src/Maker/MakeRegistrationForm.php index 78c83107ec..dfc291588a 100644 --- a/src/Maker/MakeRegistrationForm.php +++ b/src/Maker/MakeRegistrationForm.php @@ -12,11 +12,13 @@ namespace Symfony\Bundle\MakerBundle\Maker; use Doctrine\Bundle\DoctrineBundle\DoctrineBundle; +use Doctrine\ORM\EntityManager; use Doctrine\ORM\EntityManagerInterface; use Doctrine\ORM\Mapping\Column; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; use Symfony\Bridge\Twig\Mime\TemplatedEmail; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Bundle\MakerBundle\ConsoleStyle; use Symfony\Bundle\MakerBundle\DependencyBuilder; use Symfony\Bundle\MakerBundle\Doctrine\DoctrineHelper; @@ -24,6 +26,7 @@ use Symfony\Bundle\MakerBundle\FileManager; use Symfony\Bundle\MakerBundle\Generator; use Symfony\Bundle\MakerBundle\InputConfiguration; +use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait; use Symfony\Bundle\MakerBundle\Renderer\FormTypeRenderer; use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper; use Symfony\Bundle\MakerBundle\Security\Model\Authenticator; @@ -68,6 +71,8 @@ */ final class MakeRegistrationForm extends AbstractMaker { + use CanGenerateTestsTrait; + private string $userClass; private string $usernameField; private string $passwordField; @@ -104,6 +109,8 @@ public function configureCommand(Command $command, InputConfiguration $inputConf $command ->setHelp(file_get_contents(__DIR__.'/../Resources/help/MakeRegistrationForm.txt')) ; + + $this->addWithTestsOption($command); } public function interact(InputInterface $input, ConsoleStyle $io, Command $command): void @@ -180,6 +187,8 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma $routeNames = array_keys($this->router->getRouteCollection()->all()); $this->redirectRouteName = $io->choice('What route should the user be redirected to after registration?', $routeNames); } + + $this->interactSetGenerateTests($input, $io); } /** @param array $securityData */ @@ -400,6 +409,34 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen $this->fileManager->dumpFile($classDetails->getPath(), $userManipulator->getSourceCode()); } + // Generate PHPUnit Tests + if ($this->shouldGenerateTests()) { + $testClassDetails = $generator->createClassNameDetails( + 'RegistrationControllerTest', + 'Test\\' + ); + + $useStatements = new UseStatementGenerator([ + EntityManager::class, + TemplatedEmail::class, + WebTestCase::class, + $userRepoVars['repository_full_class_name'], + ]); + + $generator->generateFile( + targetPath: sprintf('tests/%s.php', $testClassDetails->getShortName()), + templateName: 'registration/Test.WithVerify.tpl.php', + variables: array_merge([ + 'use_statements' => $useStatements, + 'from_email' => $this->fromEmailAddress, + ], $userRepoVars) + ); + + 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/registration/Test.WithVerify.tpl.php b/src/Resources/skeleton/registration/Test.WithVerify.tpl.php new file mode 100644 index 0000000000..5abc4f738f --- /dev/null +++ b/src/Resources/skeleton/registration/Test.WithVerify.tpl.php @@ -0,0 +1,66 @@ + +namespace App\Tests; + + + +class RegistrationControllerTest extends WebTestCase +{ + public function testRegister(): void + { + $client = static::createClient(); + + $container = static::getContainer(); + $em = $container->get('doctrine.orm.entity_manager'); + $userRepository = $container->get(::class); + + foreach ($userRepository->findAll() as $user) { + $em->remove($user); + } + + $em->flush(); + + self::assertCount(0, $userRepository->findAll()); + + // Register a new user + $client->request('GET', '/register'); + self::assertResponseIsSuccessful(); + + $client->submitForm('Register', [ + 'registration_form[email]' => 'me@example.com', + 'registration_form[plainPassword]' => 'password', + 'registration_form[agreeTerms]' => true, + ]); + + // Ensure the response redirects after submitting the form, the user exists, and is not verified + // self::assertResponseRedirects('/'); + self::assertCount(1, $userRepository->findAll()); + self::assertFalse(($user = $userRepository->findAll()[0])->isVerified()); + + // Ensure the verification email was sent + // Use either assertQueuedEmailCount() || assertEmailCount() depending on your mailer setup + // self::assertQueuedEmailCount(1); + self::assertEmailCount(1); + + self::assertCount(1, $messages = $this->getMailerMessages()); + self::assertEmailAddressContains($messages[0], 'from', ''); + self::assertEmailAddressContains($messages[0], 'to', 'me@example.com'); + self::assertEmailTextBodyContains($messages[0], 'This link will expire in 1 hour.'); + + // Login the new user + $client->followRedirect(); + $client->loginUser($user); + + // Get the verification link from the email + /** @var TemplatedEmail $templatedEmail */ + $templatedEmail = $messages[0]; + $messageBody = $templatedEmail->getHtmlBody(); + + preg_match('#(http://localhost/verify/email.+)">#', $messageBody, $resetLink); + + // "Click" the link, and see if the user is verified + $client->request('GET', $resetLink[1]); + $client->followRedirect(); + + self::assertTrue(static::getContainer()->get(UserRepository::class)->findAll()[0]->isVerified()); + } +} diff --git a/tests/Maker/MakeRegistrationFormTest.php b/tests/Maker/MakeRegistrationFormTest.php index 173a3649f6..b0f36438cd 100644 --- a/tests/Maker/MakeRegistrationFormTest.php +++ b/tests/Maker/MakeRegistrationFormTest.php @@ -225,6 +225,50 @@ public function getTestDetails(): \Generator $this->runRegistrationTest($runner, 'it_generates_registration_form_with_verification.php'); }), ]; + + yield 'it_generates_registration_form_with_verification_and_with_tests' => [$this->createRegistrationFormTest() + ->addExtraDependencies('symfonycasts/verify-email-bundle') + // needed for internal functional test + ->addExtraDependencies('symfony/web-profiler-bundle', 'mailer') + ->run(function (MakerTestRunner $runner) { + $runner->writeFile( + 'config/packages/mailer.yaml', + Yaml::dump(['framework' => [ + 'mailer' => ['dsn' => 'null://null'], + ]]) + ); + + $this->makeUser($runner); + + $output = $runner->runMaker([ + 'n', // add UniqueEntity + 'y', // verify user + 'y', // require authentication to verify user email + 'jr@rushlow.dev', // from email address + 'SymfonyCasts', // From Name + 'n', // no authenticate after + 'app_anonymous', // route number to redirect to + 'y', // Generate tests + ]); + + $this->assertStringContainsString('Success', $output); + + $generatedFiles = [ + 'src/Security/EmailVerifier.php', + 'templates/registration/confirmation_email.html.twig', + 'tests/RegistrationControllerTest.php', + ]; + + foreach ($generatedFiles as $file) { + $this->assertFileExists($runner->getPath($file)); + } + + $runner->runConsole('cache:clear', [], '--env=test'); + + $runner->configureDatabase(); + $runner->runTests(); + }), + ]; } private function makeUser(MakerTestRunner $runner, string $identifier = 'email'): void