add new demo symfony files

This commit is contained in:
2021-04-17 22:11:27 +02:00
parent fe90d3d32c
commit 92359d6292
194 changed files with 32985 additions and 1 deletions

View File

@@ -0,0 +1,124 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Command;
use App\Command\AddUserCommand;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\Console\Tester\CommandTester;
class AddUserCommandTest extends KernelTestCase
{
private $userData = [
'username' => 'chuck_norris',
'password' => 'foobar',
'email' => 'chuck@norris.com',
'full-name' => 'Chuck Norris',
];
protected function setUp(): void
{
exec('stty 2>&1', $output, $exitcode);
$isSttySupported = 0 === $exitcode;
if ('Windows' === PHP_OS_FAMILY || !$isSttySupported) {
$this->markTestSkipped('`stty` is required to test this command.');
}
}
/**
* @dataProvider isAdminDataProvider
*
* This test provides all the arguments required by the command, so the
* command runs non-interactively and it won't ask for any argument.
*/
public function testCreateUserNonInteractive(bool $isAdmin): void
{
$input = $this->userData;
if ($isAdmin) {
$input['--admin'] = 1;
}
$this->executeCommand($input);
$this->assertUserCreated($isAdmin);
}
/**
* @dataProvider isAdminDataProvider
*
* This test doesn't provide all the arguments required by the command, so
* the command runs interactively and it will ask for the value of the missing
* arguments.
* See https://symfony.com/doc/current/components/console/helpers/questionhelper.html#testing-a-command-that-expects-input
*/
public function testCreateUserInteractive(bool $isAdmin): void
{
$this->executeCommand(
// these are the arguments (only 1 is passed, the rest are missing)
$isAdmin ? ['--admin' => 1] : [],
// these are the responses given to the questions asked by the command
// to get the value of the missing required arguments
array_values($this->userData)
);
$this->assertUserCreated($isAdmin);
}
/**
* This is used to execute the same test twice: first for normal users
* (isAdmin = false) and then for admin users (isAdmin = true).
*/
public function isAdminDataProvider(): ?\Generator
{
yield [false];
yield [true];
}
/**
* This helper method checks that the user was correctly created and saved
* in the database.
*/
private function assertUserCreated(bool $isAdmin): void
{
$container = self::$container;
/** @var \App\Entity\User $user */
$user = $container->get(UserRepository::class)->findOneByEmail($this->userData['email']);
$this->assertNotNull($user);
$this->assertSame($this->userData['full-name'], $user->getFullName());
$this->assertSame($this->userData['username'], $user->getUsername());
$this->assertTrue($container->get('security.password_encoder')->isPasswordValid($user, $this->userData['password']));
$this->assertSame($isAdmin ? ['ROLE_ADMIN'] : ['ROLE_USER'], $user->getRoles());
}
/**
* This helper method abstracts the boilerplate code needed to test the
* execution of a command.
*
* @param array $arguments All the arguments passed when executing the command
* @param array $inputs The (optional) answers given to the command when it asks for the value of the missing arguments
*/
private function executeCommand(array $arguments, array $inputs = []): void
{
self::bootKernel();
// this uses a special testing container that allows you to fetch private services
$command = self::$container->get(AddUserCommand::class);
$command->setApplication(new Application(self::$kernel));
$commandTester = new CommandTester($command);
$commandTester->setInputs($inputs);
$commandTester->execute($arguments);
}
}

View File

@@ -0,0 +1,194 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Controller\Admin;
use App\Repository\PostRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;
/**
* Functional test for the controllers defined inside the BlogController used
* for managing the blog in the backend.
*
* See https://symfony.com/doc/current/testing.html#functional-tests
*
* Whenever you test resources protected by a firewall, consider using the
* technique explained in:
* https://symfony.com/doc/current/testing/http_authentication.html
*
* Execute the application tests using this command (requires PHPUnit to be installed):
*
* $ cd your-symfony-project/
* $ ./vendor/bin/phpunit
*/
class BlogControllerTest extends WebTestCase
{
/**
* @dataProvider getUrlsForRegularUsers
*/
public function testAccessDeniedForRegularUsers(string $httpMethod, string $url): void
{
$client = static::createClient([], [
'PHP_AUTH_USER' => 'john_user',
'PHP_AUTH_PW' => 'kitten',
]);
$client->request($httpMethod, $url);
$this->assertResponseStatusCodeSame(Response::HTTP_FORBIDDEN);
}
public function getUrlsForRegularUsers(): ?\Generator
{
yield ['GET', '/en/admin/post/'];
yield ['GET', '/en/admin/post/1'];
yield ['GET', '/en/admin/post/1/edit'];
yield ['POST', '/en/admin/post/1/delete'];
}
public function testAdminBackendHomePage(): void
{
$client = static::createClient([], [
'PHP_AUTH_USER' => 'jane_admin',
'PHP_AUTH_PW' => 'kitten',
]);
$client->request('GET', '/en/admin/post/');
$this->assertResponseIsSuccessful();
$this->assertSelectorExists(
'body#admin_post_index #main tbody tr',
'The backend homepage displays all the available posts.'
);
}
/**
* This test changes the database contents by creating a new blog post. However,
* thanks to the DAMADoctrineTestBundle and its PHPUnit listener, all changes
* to the database are rolled back when this test completes. This means that
* all the application tests begin with the same database contents.
*/
public function testAdminNewPost(): void
{
$postTitle = 'Blog Post Title '.mt_rand();
$postSummary = $this->generateRandomString(255);
$postContent = $this->generateRandomString(1024);
$client = static::createClient([], [
'PHP_AUTH_USER' => 'jane_admin',
'PHP_AUTH_PW' => 'kitten',
]);
$client->request('GET', '/en/admin/post/new');
$client->submitForm('Create post', [
'post[title]' => $postTitle,
'post[summary]' => $postSummary,
'post[content]' => $postContent,
]);
$this->assertResponseRedirects('/en/admin/post/', Response::HTTP_FOUND);
/** @var \App\Entity\Post $post */
$post = self::$container->get(PostRepository::class)->findOneByTitle($postTitle);
$this->assertNotNull($post);
$this->assertSame($postSummary, $post->getSummary());
$this->assertSame($postContent, $post->getContent());
}
public function testAdminNewDuplicatedPost(): void
{
$postTitle = 'Blog Post Title '.mt_rand();
$postSummary = $this->generateRandomString(255);
$postContent = $this->generateRandomString(1024);
$client = static::createClient([], [
'PHP_AUTH_USER' => 'jane_admin',
'PHP_AUTH_PW' => 'kitten',
]);
$crawler = $client->request('GET', '/en/admin/post/new');
$form = $crawler->selectButton('Create post')->form([
'post[title]' => $postTitle,
'post[summary]' => $postSummary,
'post[content]' => $postContent,
]);
$client->submit($form);
// post titles must be unique, so trying to create the same post twice should result in an error
$client->submit($form);
$this->assertSelectorTextSame('form .form-group.has-error label', 'Title');
$this->assertSelectorTextContains('form .form-group.has-error .help-block', 'This title was already used in another blog post, but they must be unique.');
}
public function testAdminShowPost(): void
{
$client = static::createClient([], [
'PHP_AUTH_USER' => 'jane_admin',
'PHP_AUTH_PW' => 'kitten',
]);
$client->request('GET', '/en/admin/post/1');
$this->assertResponseIsSuccessful();
}
/**
* This test changes the database contents by editing a blog post. However,
* thanks to the DAMADoctrineTestBundle and its PHPUnit listener, all changes
* to the database are rolled back when this test completes. This means that
* all the application tests begin with the same database contents.
*/
public function testAdminEditPost(): void
{
$newBlogPostTitle = 'Blog Post Title '.mt_rand();
$client = static::createClient([], [
'PHP_AUTH_USER' => 'jane_admin',
'PHP_AUTH_PW' => 'kitten',
]);
$client->request('GET', '/en/admin/post/1/edit');
$client->submitForm('Save changes', [
'post[title]' => $newBlogPostTitle,
]);
$this->assertResponseRedirects('/en/admin/post/1/edit', Response::HTTP_FOUND);
/** @var \App\Entity\Post $post */
$post = self::$container->get(PostRepository::class)->find(1);
$this->assertSame($newBlogPostTitle, $post->getTitle());
}
/**
* This test changes the database contents by deleting a blog post. However,
* thanks to the DAMADoctrineTestBundle and its PHPUnit listener, all changes
* to the database are rolled back when this test completes. This means that
* all the application tests begin with the same database contents.
*/
public function testAdminDeletePost(): void
{
$client = static::createClient([], [
'PHP_AUTH_USER' => 'jane_admin',
'PHP_AUTH_PW' => 'kitten',
]);
$crawler = $client->request('GET', '/en/admin/post/1');
$client->submit($crawler->filter('#delete-form')->form());
$this->assertResponseRedirects('/en/admin/post/', Response::HTTP_FOUND);
$post = self::$container->get(PostRepository::class)->find(1);
$this->assertNull($post);
}
private function generateRandomString(int $length): string
{
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
return mb_substr(str_shuffle(str_repeat($chars, ceil($length / mb_strlen($chars)))), 1, $length);
}
}

View File

@@ -0,0 +1,98 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Controller;
use App\Entity\Post;
use App\Pagination\Paginator;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
/**
* Functional test for the controllers defined inside BlogController.
*
* See https://symfony.com/doc/current/testing.html#functional-tests
*
* Execute the application tests using this command (requires PHPUnit to be installed):
*
* $ cd your-symfony-project/
* $ ./vendor/bin/phpunit
*/
class BlogControllerTest extends WebTestCase
{
public function testIndex(): void
{
$client = static::createClient();
$crawler = $client->request('GET', '/en/blog/');
$this->assertResponseIsSuccessful();
$this->assertCount(
Paginator::PAGE_SIZE,
$crawler->filter('article.post'),
'The homepage displays the right number of posts.'
);
}
public function testRss(): void
{
$client = static::createClient();
$crawler = $client->request('GET', '/en/blog/rss.xml');
$this->assertResponseHeaderSame('Content-Type', 'text/xml; charset=UTF-8');
$this->assertCount(
Paginator::PAGE_SIZE,
$crawler->filter('item'),
'The xml file displays the right number of posts.'
);
}
/**
* This test changes the database contents by creating a new comment. However,
* thanks to the DAMADoctrineTestBundle and its PHPUnit listener, all changes
* to the database are rolled back when this test completes. This means that
* all the application tests begin with the same database contents.
*/
public function testNewComment(): void
{
$client = static::createClient([], [
'PHP_AUTH_USER' => 'john_user',
'PHP_AUTH_PW' => 'kitten',
]);
$client->followRedirects();
// Find first blog post
$crawler = $client->request('GET', '/en/blog/');
$postLink = $crawler->filter('article.post > h2 a')->link();
$client->click($postLink);
$crawler = $client->submitForm('Publish comment', [
'comment[content]' => 'Hi, Symfony!',
]);
$newComment = $crawler->filter('.post-comment')->first()->filter('div > p')->text();
$this->assertSame('Hi, Symfony!', $newComment);
}
public function testAjaxSearch(): void
{
$client = static::createClient();
$client->xmlHttpRequest('GET', '/en/blog/search', ['q' => 'lorem']);
$results = json_decode($client->getResponse()->getContent(), true);
$this->assertResponseHeaderSame('Content-Type', 'application/json');
$this->assertCount(1, $results);
$this->assertSame('Lorem ipsum dolor sit amet consectetur adipiscing elit', $results[0]['title']);
$this->assertSame('Jane Doe', $results[0]['author']);
}
}

View File

@@ -0,0 +1,95 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Controller;
use App\Entity\Post;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;
/**
* Functional test that implements a "smoke test" of all the public and secure
* URLs of the application.
* See https://symfony.com/doc/current/best_practices.html#smoke-test-your-urls.
*
* Execute the application tests using this command (requires PHPUnit to be installed):
*
* $ cd your-symfony-project/
* $ ./vendor/bin/phpunit
*/
class DefaultControllerTest extends WebTestCase
{
/**
* PHPUnit's data providers allow to execute the same tests repeated times
* using a different set of data each time.
* See https://symfony.com/doc/current/testing.html#testing-against-different-sets-of-data.
*
* @dataProvider getPublicUrls
*/
public function testPublicUrls(string $url): void
{
$client = static::createClient();
$client->request('GET', $url);
$this->assertResponseIsSuccessful(sprintf('The %s public URL loads correctly.', $url));
}
/**
* A good practice for tests is to not use the service container, to make
* them more robust. However, in this example we must access to the container
* to get the entity manager and make a database query. The reason is that
* blog post fixtures are randomly generated and there's no guarantee that
* a given blog post slug will be available.
*/
public function testPublicBlogPost(): void
{
$client = static::createClient();
// the service container is always available via the test client
$blogPost = $client->getContainer()->get('doctrine')->getRepository(Post::class)->find(1);
$client->request('GET', sprintf('/en/blog/posts/%s', $blogPost->getSlug()));
$this->assertResponseIsSuccessful();
}
/**
* The application contains a lot of secure URLs which shouldn't be
* publicly accessible. This tests ensures that whenever a user tries to
* access one of those pages, a redirection to the login form is performed.
*
* @dataProvider getSecureUrls
*/
public function testSecureUrls(string $url): void
{
$client = static::createClient();
$client->request('GET', $url);
$this->assertResponseRedirects(
'http://localhost/en/login',
Response::HTTP_FOUND,
sprintf('The %s secure URL redirects to the login form.', $url)
);
}
public function getPublicUrls(): ?\Generator
{
yield ['/'];
yield ['/en/blog/'];
yield ['/en/login'];
}
public function getSecureUrls(): ?\Generator
{
yield ['/en/admin/post/'];
yield ['/en/admin/post/new'];
yield ['/en/admin/post/1'];
yield ['/en/admin/post/1/edit'];
}
}

View File

@@ -0,0 +1,99 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Controller;
use App\Repository\UserRepository;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Response;
/**
* Functional test for the controllers defined inside the UserController used
* for managing the current logged user.
*
* See https://symfony.com/doc/current/testing.html#functional-tests
*
* Whenever you test resources protected by a firewall, consider using the
* technique explained in:
* https://symfony.com/doc/current/testing/http_authentication.html
*
* Execute the application tests using this command (requires PHPUnit to be installed):
*
* $ cd your-symfony-project/
* $ ./vendor/bin/phpunit
*/
class UserControllerTest extends WebTestCase
{
/**
* @dataProvider getUrlsForAnonymousUsers
*/
public function testAccessDeniedForAnonymousUsers(string $httpMethod, string $url): void
{
$client = static::createClient();
$client->request($httpMethod, $url);
$this->assertResponseRedirects(
'http://localhost/en/login',
Response::HTTP_FOUND,
sprintf('The %s secure URL redirects to the login form.', $url)
);
}
public function getUrlsForAnonymousUsers(): ?\Generator
{
yield ['GET', '/en/profile/edit'];
yield ['GET', '/en/profile/change-password'];
}
public function testEditUser(): void
{
$newUserEmail = 'admin_jane@symfony.com';
$client = static::createClient([], [
'PHP_AUTH_USER' => 'jane_admin',
'PHP_AUTH_PW' => 'kitten',
]);
$client->request('GET', '/en/profile/edit');
$client->submitForm('Save changes', [
'user[email]' => $newUserEmail,
]);
$this->assertResponseRedirects('/en/profile/edit', Response::HTTP_FOUND);
/** @var \App\Entity\User $user */
$user = self::$container->get(UserRepository::class)->findOneByEmail($newUserEmail);
$this->assertNotNull($user);
$this->assertSame($newUserEmail, $user->getEmail());
}
public function testChangePassword(): void
{
$newUserPassword = 'new-password';
$client = static::createClient([], [
'PHP_AUTH_USER' => 'jane_admin',
'PHP_AUTH_PW' => 'kitten',
]);
$client->request('GET', '/en/profile/change-password');
$client->submitForm('Save changes', [
'change_password[currentPassword]' => 'kitten',
'change_password[newPassword][first]' => $newUserPassword,
'change_password[newPassword][second]' => $newUserPassword,
]);
$this->assertResponseRedirects(
'/en/logout',
Response::HTTP_FOUND,
'Changing password logout the user.'
);
}
}

View File

@@ -0,0 +1,128 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Form\DataTransformer;
use App\Entity\Tag;
use App\Form\DataTransformer\TagArrayToStringTransformer;
use App\Repository\TagRepository;
use PHPUnit\Framework\TestCase;
/**
* Tests that tags are transformed correctly using the data transformer.
*
* See https://symfony.com/doc/current/testing/database.html
*/
class TagArrayToStringTransformerTest extends TestCase
{
/**
* Ensures that tags are created correctly.
*/
public function testCreateTheRightAmountOfTags(): void
{
$tags = $this->getMockedTransformer()->reverseTransform('Hello, Demo, How');
$this->assertCount(3, $tags);
$this->assertSame('Hello', $tags[0]->getName());
}
/**
* Ensures that empty tags and errors in the number of commas are
* dealt correctly.
*/
public function testCreateTheRightAmountOfTagsWithTooManyCommas(): void
{
$transformer = $this->getMockedTransformer();
$this->assertCount(3, $transformer->reverseTransform('Hello, Demo,, How'));
$this->assertCount(3, $transformer->reverseTransform('Hello, Demo, How,'));
}
/**
* Ensures that leading/trailing spaces are ignored for tag names.
*/
public function testTrimNames(): void
{
$tags = $this->getMockedTransformer()->reverseTransform(' Hello ');
$this->assertSame('Hello', $tags[0]->getName());
}
/**
* Ensures that duplicated tag names are ignored.
*/
public function testDuplicateNames(): void
{
$tags = $this->getMockedTransformer()->reverseTransform('Hello, Hello, Hello');
$this->assertCount(1, $tags);
}
/**
* Ensures that the transformer uses tags already persisted in the database.
*/
public function testUsesAlreadyDefinedTags(): void
{
$persistedTags = [
$this->createTag('Hello'),
$this->createTag('World'),
];
$tags = $this->getMockedTransformer($persistedTags)->reverseTransform('Hello, World, How, Are, You');
$this->assertCount(5, $tags);
$this->assertSame($persistedTags[0], $tags[0]);
$this->assertSame($persistedTags[1], $tags[1]);
}
/**
* Ensures that the transformation from Tag instances to a simple string
* works as expected.
*/
public function testTransform(): void
{
$persistedTags = [
$this->createTag('Hello'),
$this->createTag('World'),
];
$transformed = $this->getMockedTransformer()->transform($persistedTags);
$this->assertSame('Hello,World', $transformed);
}
/**
* This helper method mocks the real TagArrayToStringTransformer class to
* simplify the tests. See https://phpunit.de/manual/current/en/test-doubles.html.
*
* @param array $findByReturnValues The values returned when calling to the findBy() method
*/
private function getMockedTransformer(array $findByReturnValues = []): TagArrayToStringTransformer
{
$tagRepository = $this->getMockBuilder(TagRepository::class)
->disableOriginalConstructor()
->getMock();
$tagRepository->expects($this->any())
->method('findBy')
->willReturn($findByReturnValues);
return new TagArrayToStringTransformer($tagRepository);
}
/**
* This helper method creates a Tag instance for the given tag name.
*/
private function createTag(string $name): Tag
{
$tag = new Tag();
$tag->setName($name);
return $tag;
}
}

View File

@@ -0,0 +1,102 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace App\Tests\Utils;
use App\Utils\Validator;
use PHPUnit\Framework\TestCase;
class ValidatorTest extends TestCase
{
private $validator;
protected function setUp(): void
{
$this->validator = new Validator();
}
public function testValidateUsername(): void
{
$test = 'username';
$this->assertSame($test, $this->validator->validateUsername($test));
}
public function testValidateUsernameEmpty(): void
{
$this->expectException('Exception');
$this->expectExceptionMessage('The username can not be empty.');
$this->validator->validateUsername(null);
}
public function testValidateUsernameInvalid(): void
{
$this->expectException('Exception');
$this->expectExceptionMessage('The username must contain only lowercase latin characters and underscores.');
$this->validator->validateUsername('INVALID');
}
public function testValidatePassword(): void
{
$test = 'password';
$this->assertSame($test, $this->validator->validatePassword($test));
}
public function testValidatePasswordEmpty(): void
{
$this->expectException('Exception');
$this->expectExceptionMessage('The password can not be empty.');
$this->validator->validatePassword(null);
}
public function testValidatePasswordInvalid(): void
{
$this->expectException('Exception');
$this->expectExceptionMessage('The password must be at least 6 characters long.');
$this->validator->validatePassword('12345');
}
public function testValidateEmail(): void
{
$test = '@';
$this->assertSame($test, $this->validator->validateEmail($test));
}
public function testValidateEmailEmpty(): void
{
$this->expectException('Exception');
$this->expectExceptionMessage('The email can not be empty.');
$this->validator->validateEmail(null);
}
public function testValidateEmailInvalid(): void
{
$this->expectException('Exception');
$this->expectExceptionMessage('The email should look like a real email.');
$this->validator->validateEmail('invalid');
}
public function testValidateFullName(): void
{
$test = 'Full Name';
$this->assertSame($test, $this->validator->validateFullName($test));
}
public function testValidateFullNameEmpty()
{
$this->expectException('Exception');
$this->expectExceptionMessage('The full name can not be empty.');
$this->validator->validateFullName(null);
}
}

20
app/tests/bootstrap.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
use Symfony\Component\Dotenv\Dotenv;
require dirname(__DIR__).'/vendor/autoload.php';
if (file_exists(dirname(__DIR__).'/config/bootstrap.php')) {
require dirname(__DIR__).'/config/bootstrap.php';
} elseif (method_exists(Dotenv::class, 'bootEnv')) {
(new Dotenv())->bootEnv(dirname(__DIR__).'/.env');
}