Skip to content

Commit 990243e

Browse files
committed
[make:security:form-login] add ability to generate tests
1 parent ff1c8c5 commit 990243e

File tree

3 files changed

+135
-2
lines changed

3 files changed

+135
-2
lines changed

src/Maker/Security/MakeFormLogin.php

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@
1212
namespace Symfony\Bundle\MakerBundle\Maker\Security;
1313

1414
use Doctrine\Bundle\DoctrineBundle\DoctrineBundle;
15+
use Doctrine\ORM\EntityManager;
1516
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
17+
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
1618
use Symfony\Bundle\MakerBundle\ConsoleStyle;
1719
use Symfony\Bundle\MakerBundle\DependencyBuilder;
1820
use Symfony\Bundle\MakerBundle\Exception\RuntimeCommandException;
1921
use Symfony\Bundle\MakerBundle\FileManager;
2022
use Symfony\Bundle\MakerBundle\Generator;
2123
use Symfony\Bundle\MakerBundle\InputConfiguration;
2224
use Symfony\Bundle\MakerBundle\Maker\AbstractMaker;
25+
use Symfony\Bundle\MakerBundle\Maker\Common\CanGenerateTestsTrait;
2326
use Symfony\Bundle\MakerBundle\Security\InteractiveSecurityHelper;
2427
use Symfony\Bundle\MakerBundle\Security\SecurityConfigUpdater;
2528
use Symfony\Bundle\MakerBundle\Security\SecurityControllerBuilder;
@@ -33,6 +36,7 @@
3336
use Symfony\Component\Console\Command\Command;
3437
use Symfony\Component\Console\Input\InputInterface;
3538
use Symfony\Component\HttpFoundation\Response;
39+
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
3640
use Symfony\Component\Routing\Attribute\Route;
3741
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
3842
use Symfony\Component\Yaml\Yaml;
@@ -48,10 +52,13 @@
4852
*/
4953
final class MakeFormLogin extends AbstractMaker
5054
{
55+
use CanGenerateTestsTrait;
56+
5157
private const SECURITY_CONFIG_PATH = 'config/packages/security.yaml';
5258
private YamlSourceManipulator $ysm;
5359
private string $controllerName;
5460
private string $firewallToUpdate;
61+
private string $userClass;
5562
private string $userNameField;
5663
private bool $willLogout;
5764

@@ -70,6 +77,8 @@ public static function getCommandName(): string
7077
public function configureCommand(Command $command, InputConfiguration $inputConfig): void
7178
{
7279
$command->setHelp(file_get_contents(\dirname(__DIR__, 2).'/Resources/help/security/MakeFormLogin.txt'));
80+
81+
$this->configureCommandWithTestsOption($command);
7382
}
7483

7584
public static function getCommandDescription(): string
@@ -116,9 +125,11 @@ public function interact(InputInterface $input, ConsoleStyle $io, Command $comma
116125

117126
$securityHelper = new InteractiveSecurityHelper();
118127
$this->firewallToUpdate = $securityHelper->guessFirewallName($io, $securityData);
119-
$userClass = $securityHelper->guessUserClass($io, $securityData['security']['providers']);
120-
$this->userNameField = $securityHelper->guessUserNameField($io, $userClass, $securityData['security']['providers']);
128+
$this->userClass = $securityHelper->guessUserClass($io, $securityData['security']['providers']);
129+
$this->userNameField = $securityHelper->guessUserNameField($io, $this->userClass, $securityData['security']['providers']);
121130
$this->willLogout = $io->confirm('Do you want to generate a \'/logout\' URL?');
131+
132+
$this->interactSetGenerateTests($input, $io);
122133
}
123134

124135
public function generate(InputInterface $input, ConsoleStyle $io, Generator $generator): void
@@ -167,6 +178,39 @@ public function generate(InputInterface $input, ConsoleStyle $io, Generator $gen
167178
$securityData = $this->securityConfigUpdater->updateForLogout($securityData, $this->firewallToUpdate);
168179
}
169180

181+
if ($this->shouldGenerateTests()) {
182+
$testClassDetails = $generator->createClassNameDetails(
183+
'LoginControllerTest',
184+
'Test\\',
185+
);
186+
187+
$useStatements = new UseStatementGenerator([
188+
sprintf('\%s', $this->userClass),
189+
// $userClassNameDetails->getFullName(),
190+
// $userRepositoryDetails->getFullName(),
191+
EntityManager::class,
192+
WebTestCase::class,
193+
UserPasswordHasherInterface::class,
194+
]);
195+
196+
$generator->generateFile(
197+
targetPath: sprintf('tests/%s.php', $testClassDetails->getShortName()),
198+
templateName: 'security/formLogin/Test.LoginController.tpl.php',
199+
variables: [
200+
'use_statements' => $useStatements,
201+
'user_class' => $this->userClass,
202+
// 'user_short_name' => $userClassNameDetails->getShortName(),
203+
// 'user_repo_short_name' => $userRepositoryDetails->getShortName(),
204+
// 'success_route_path' => null !== $this->controllerResetSuccessRoute ? $this->controllerResetSuccessRoute->getPath() : '/',
205+
// 'from_email' => $this->fromEmailAddress,
206+
],
207+
);
208+
209+
if (!class_exists(WebTestCase::class)) {
210+
$io->caution('You\'ll need to install the `symfony/test-pack` to execute the tests for your new controller.');
211+
}
212+
}
213+
170214
$generator->dumpFile(self::SECURITY_CONFIG_PATH, $securityData);
171215

172216
$generator->writeChanges();
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?= "<?php\n" ?>
2+
namespace App\Tests;
3+
4+
<?= $use_statements ?>
5+
6+
class LoginControllerTest extends WebTestCase
7+
{
8+
public function testLogin(): void
9+
{
10+
$client = static::createClient();
11+
$container = static::getContainer();
12+
$em = $container->get('doctrine.orm.entity_manager');
13+
$userRepository = $em->getRepository(\<?= $user_class ?>::class);
14+
15+
foreach ($userRepository->findAll() as $user) {
16+
$em->remove($user);
17+
}
18+
19+
$em->flush();
20+
21+
self::assertCount(0, $userRepository->findAll());
22+
23+
// Ensure login with invalid credentials shows error message
24+
$client->request('GET', '/login');
25+
self::assertResponseIsSuccessful();
26+
27+
$client->submitForm('Sign in', [
28+
'_username' => '[email protected]',
29+
'_password' => 'password',
30+
]);
31+
32+
self::assertResponseRedirects('/login');
33+
34+
$client->followRedirect();
35+
36+
self::assertSelectorTextContains('.alert-danger', 'Invalid credentials.');
37+
38+
// Ensure login with valid credentials
39+
/** @var UserPasswordHasherInterface $passwordHasher */
40+
$passwordHasher = (static::getContainer())->get('security.user_password_hasher');
41+
42+
$user = (new User())->setEmail('[email protected]');
43+
$user->setPassword($passwordHasher->hashPassword($user, 'password'));
44+
45+
$em->persist($user);
46+
$em->flush();
47+
48+
$client->submitForm('Sign in', [
49+
'_username' => '[email protected]',
50+
'_password' => 'password',
51+
]);
52+
53+
self::assertResponseRedirects('/');
54+
$client->followRedirect();
55+
self::assertSelectorNotExists('.alert-danger');
56+
self::assertResponseIsSuccessful();
57+
}
58+
}

tests/Maker/Security/MakeFormLoginTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,37 @@ public function getTestDetails(): \Generator
9999
$this->assertSame('app_logout', $securityConfig['security']['firewalls']['main']['logout']['path']);
100100
}),
101101
];
102+
103+
yield 'generates_form_login_using_defaults_with_test' => [$this->createMakerTest()
104+
->run(function (MakerTestRunner $runner) {
105+
// Make the UserPasswordHasherInterface available in the test
106+
$runner->renderTemplateFile('security/make-form-login/FixtureController.php', 'src/Controller/FixtureController.php', []);
107+
108+
$this->makeUser($runner);
109+
110+
$output = $runner->runMaker([
111+
'SecurityController', // Controller Name
112+
'y', // Generate Logout,
113+
'y', // Generate tests
114+
]);
115+
116+
$this->assertStringContainsString('Success', $output);
117+
$fixturePath = \dirname(__DIR__, 2).'/fixtures/security/make-form-login/expected';
118+
119+
$this->assertFileEquals($fixturePath.'/SecurityController.php', $runner->getPath('src/Controller/SecurityController.php'));
120+
$this->assertFileEquals($fixturePath.'/login.html.twig', $runner->getPath('templates/security/login.html.twig'));
121+
122+
$securityConfig = $runner->readYaml('config/packages/security.yaml');
123+
124+
$this->assertSame('app_login', $securityConfig['security']['firewalls']['main']['form_login']['login_path']);
125+
$this->assertSame('app_login', $securityConfig['security']['firewalls']['main']['form_login']['check_path']);
126+
$this->assertTrue($securityConfig['security']['firewalls']['main']['form_login']['enable_csrf']);
127+
$this->assertSame('app_logout', $securityConfig['security']['firewalls']['main']['logout']['path']);
128+
129+
$runner->configureDatabase();
130+
$runner->runTests();
131+
}),
132+
];
102133
}
103134

104135
private function runLoginTest(MakerTestRunner $runner): void

0 commit comments

Comments
 (0)