diff --git a/src/AppBundle/DataFixtures/ORM/PostFixtures.php b/src/AppBundle/DataFixtures/ORM/PostFixtures.php index ffc495dee..c7d7d9d41 100644 --- a/src/AppBundle/DataFixtures/ORM/PostFixtures.php +++ b/src/AppBundle/DataFixtures/ORM/PostFixtures.php @@ -13,12 +13,12 @@ use AppBundle\Entity\Comment; use AppBundle\Entity\Post; -use AppBundle\Entity\Tag; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\DataFixtures\DependentFixtureInterface; use Doctrine\Common\Persistence\ObjectManager; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; +use Tests\FixturesTrait; /** * Defines the sample blog posts to load in the database before running the unit @@ -35,6 +35,7 @@ class PostFixtures extends AbstractFixture implements DependentFixtureInterface, ContainerAwareInterface { use ContainerAwareTrait; + use FixturesTrait; /** * {@inheritdoc} @@ -100,124 +101,11 @@ private function getRandomTags($numTags = 0) return $tags; } - $indexes = (array) array_rand(TagFixtures::$names, $numTags); + $indexes = (array) array_rand($this->getTagNames(), $numTags); foreach ($indexes as $index) { $tags[] = $this->getReference('tag-'.$index); } return $tags; } - - private function getPostContent() - { - return <<<'MARKDOWN' -Lorem ipsum dolor sit amet consectetur adipisicing elit, sed do eiusmod tempor -incididunt ut labore et **dolore magna aliqua**: Duis aute irure dolor in -reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. -Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia -deserunt mollit anim id est laborum. - - * Ut enim ad minim veniam - * Quis nostrud exercitation *ullamco laboris* - * Nisi ut aliquip ex ea commodo consequat - -Praesent id fermentum lorem. Ut est lorem, fringilla at accumsan nec, euismod at -nunc. Aenean mattis sollicitudin mattis. Nullam pulvinar vestibulum bibendum. -Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos -himenaeos. Fusce nulla purus, gravida ac interdum ut, blandit eget ex. Duis a -luctus dolor. - -Integer auctor massa maximus nulla scelerisque accumsan. *Aliquam ac malesuada* -ex. Pellentesque tortor magna, vulputate eu vulputate ut, venenatis ac lectus. -Praesent ut lacinia sem. Mauris a lectus eget felis mollis feugiat. Quisque -efficitur, mi ut semper pulvinar, urna urna blandit massa, eget tincidunt augue -nulla vitae est. - -Ut posuere aliquet tincidunt. Aliquam erat volutpat. **Class aptent taciti** -sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi -arcu orci, gravida eget aliquam eu, suscipit et ante. Morbi vulputate metus vel -ipsum finibus, ut dapibus massa feugiat. Vestibulum vel lobortis libero. Sed -tincidunt tellus et viverra scelerisque. Pellentesque tincidunt cursus felis. -Sed in egestas erat. - -Aliquam pulvinar interdum massa, vel ullamcorper ante consectetur eu. Vestibulum -lacinia ac enim vel placerat. Integer pulvinar magna nec dui malesuada, nec -congue nisl dictum. Donec mollis nisl tortor, at congue erat consequat a. Nam -tempus elit porta, blandit elit vel, viverra lorem. Sed sit amet tellus -tincidunt, faucibus nisl in, aliquet libero. -MARKDOWN; - } - - private function getPhrases() - { - return [ - 'Lorem ipsum dolor sit amet consectetur adipiscing elit', - 'Pellentesque vitae velit ex', - 'Mauris dapibus risus quis suscipit vulputate', - 'Eros diam egestas libero eu vulputate risus', - 'In hac habitasse platea dictumst', - 'Morbi tempus commodo mattis', - 'Ut suscipit posuere justo at vulputate', - 'Ut eleifend mauris et risus ultrices egestas', - 'Aliquam sodales odio id eleifend tristique', - 'Urna nisl sollicitudin id varius orci quam id turpis', - 'Nulla porta lobortis ligula vel egestas', - 'Curabitur aliquam euismod dolor non ornare', - 'Sed varius a risus eget aliquam', - 'Nunc viverra elit ac laoreet suscipit', - 'Pellentesque et sapien pulvinar consectetur', - 'Ubi est barbatus nix', - 'Abnobas sunt hilotaes de placidus vita', - 'Ubi est audax amicitia', - 'Eposs sunt solems de superbus fortis', - 'Vae humani generis', - 'Diatrias tolerare tanquam noster caesium', - 'Teres talis saepe tractare de camerarius flavum sensorem', - 'Silva de secundus galatae demitto quadra', - 'Sunt accentores vitare salvus flavum parses', - 'Potus sensim ad ferox abnoba', - 'Sunt seculaes transferre talis camerarius fluctuies', - 'Era brevis ratione est', - 'Sunt torquises imitari velox mirabilis medicinaes', - 'Mineralis persuadere omnes finises desiderium', - 'Bassus fatalis classiss virtualiter transferre de flavum', - ]; - } - - private function getRandomPostTitles() - { - $phrases = $this->getPhrases(); - - // this ensures that the first title is always 'Lorem Ipsum...' - $loremIpsumPhrase = array_shift($phrases); - shuffle($phrases); - array_unshift($phrases, $loremIpsumPhrase); - - return $phrases; - } - - private function getRandomPostSummary($maxLength = 255) - { - $phrases = $this->getPhrases(); - - $numPhrases = mt_rand(6, 12); - shuffle($phrases); - $phrases = array_slice($phrases, 0, $numPhrases - 1); - - while (strlen($summary = implode('. ', $phrases).'.') > $maxLength) { - array_pop($phrases); - } - - return $summary; - } - - private function getRandomCommentContent() - { - $phrases = $this->getPhrases(); - - $numPhrases = mt_rand(2, 15); - shuffle($phrases); - - return implode(' ', array_slice($phrases, 0, $numPhrases - 1)); - } } diff --git a/src/AppBundle/DataFixtures/ORM/TagFixtures.php b/src/AppBundle/DataFixtures/ORM/TagFixtures.php index 50433e9b3..4052e051f 100644 --- a/src/AppBundle/DataFixtures/ORM/TagFixtures.php +++ b/src/AppBundle/DataFixtures/ORM/TagFixtures.php @@ -14,6 +14,7 @@ use AppBundle\Entity\Tag; use Doctrine\Common\DataFixtures\AbstractFixture; use Doctrine\Common\Persistence\ObjectManager; +use Tests\FixturesTrait; /** * Defines the sample blog tags to load in the database before running the unit @@ -27,24 +28,14 @@ */ class TagFixtures extends AbstractFixture { - public static $names = [ - 'lorem', - 'ipsum', - 'consectetur', - 'adipiscing', - 'incididunt', - 'labore', - 'voluptate', - 'dolore', - 'pariatur', - ]; + use FixturesTrait; /** * {@inheritdoc} */ public function load(ObjectManager $manager) { - foreach (self::$names as $index => $name) { + foreach ($this->getTagNames() as $index => $name) { $tag = new Tag(); $tag->setName($name); diff --git a/tests/AppBundle/Controller/Admin/BlogControllerTest.php b/tests/AppBundle/Controller/Admin/BlogControllerTest.php index 0b601401e..a5093b96e 100644 --- a/tests/AppBundle/Controller/Admin/BlogControllerTest.php +++ b/tests/AppBundle/Controller/Admin/BlogControllerTest.php @@ -14,6 +14,8 @@ use AppBundle\Entity\Post; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; use Symfony\Component\HttpFoundation\Response; +use Tests\ControllerTestTrait; +use Tests\FixturesTrait; /** * Functional test for the controllers defined inside the BlogController used @@ -32,37 +34,26 @@ */ class BlogControllerTest extends WebTestCase { + use ControllerTestTrait; + use FixturesTrait; + /** * @dataProvider getUrlsForRegularUsers */ - public function testRegularUsers($httpMethod, $url, $statusCode) + public function testAccessDeniedForRegularUsers($httpMethod, $url) { - $client = static::createClient([], [ - 'PHP_AUTH_USER' => 'john_user', - 'PHP_AUTH_PW' => 'kitten', - ]); + $client = $this->getUserClient(); $client->request($httpMethod, $url); - $this->assertSame($statusCode, $client->getResponse()->getStatusCode()); + $this->assertSame(Response::HTTP_FORBIDDEN, $client->getResponse()->getStatusCode()); } public function getUrlsForRegularUsers() { - yield ['GET', '/en/admin/post/', Response::HTTP_FORBIDDEN]; - yield ['GET', '/en/admin/post/1', Response::HTTP_FORBIDDEN]; - yield ['GET', '/en/admin/post/1/edit', Response::HTTP_FORBIDDEN]; - yield ['POST', '/en/admin/post/1/delete', Response::HTTP_FORBIDDEN]; - } - - /** - * @return \Symfony\Bundle\FrameworkBundle\Client - */ - private function getAdminClient() - { - return static::createClient([], [ - 'PHP_AUTH_USER' => 'jane_admin', - 'PHP_AUTH_PW' => 'kitten', - ]); + 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() @@ -80,21 +71,42 @@ public function testAdminBackendHomePage() } /** - * This test changes the database contents by deleting a blog post. However, + * 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 testAdminDeletePost() + public function testAdminNewPost() { + $postTitle = 'Blog Post Title '.mt_rand(); + $postSummary = $this->getRandomPostSummary(); + $postContent = $this->getPostContent(); + $client = $this->getAdminClient(); - $crawler = $client->request('GET', '/en/admin/post/1'); - $client->submit($crawler->filter('#delete-form')->form()); + $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); $this->assertSame(Response::HTTP_FOUND, $client->getResponse()->getStatusCode()); - $post = $client->getContainer()->get('doctrine')->getRepository(Post::class)->find(1); - $this->assertNull($post); + $post = $client->getContainer()->get('doctrine')->getRepository(Post::class)->findOneBy([ + 'title' => $postTitle, + ]); + $this->assertNotNull($post); + $this->assertSame($postSummary, $post->getSummary()); + $this->assertSame($postContent, $post->getContent()); + } + + public function testAdminShowPost() + { + $client = $this->getAdminClient(); + $client->request('GET', '/en/admin/post/1'); + + $this->assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode()); } /** @@ -120,4 +132,22 @@ public function testAdminEditPost() $post = $client->getContainer()->get('doctrine')->getRepository(Post::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() + { + $client = $this->getAdminClient(); + $crawler = $client->request('GET', '/en/admin/post/1'); + $client->submit($crawler->filter('#delete-form')->form()); + + $this->assertSame(Response::HTTP_FOUND, $client->getResponse()->getStatusCode()); + + $post = $client->getContainer()->get('doctrine')->getRepository(Post::class)->find(1); + $this->assertNull($post); + } } diff --git a/tests/AppBundle/Controller/BlogControllerTest.php b/tests/AppBundle/Controller/BlogControllerTest.php index dd1bba44c..31c95a9fe 100644 --- a/tests/AppBundle/Controller/BlogControllerTest.php +++ b/tests/AppBundle/Controller/BlogControllerTest.php @@ -13,6 +13,9 @@ use AppBundle\Entity\Post; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\HttpFoundation\Response; +use Tests\ControllerTestTrait; +use Tests\FixturesTrait; /** * Functional test for the controllers defined inside BlogController. @@ -26,9 +29,12 @@ */ class BlogControllerTest extends WebTestCase { + use ControllerTestTrait; + use FixturesTrait; + public function testIndex() { - $client = static::createClient(); + $client = $this->getAnonymousClient(); $crawler = $client->request('GET', '/en/blog/'); $this->assertCount( @@ -40,7 +46,7 @@ public function testIndex() public function testRss() { - $client = static::createClient(); + $client = $this->getAnonymousClient(); $crawler = $client->request('GET', '/en/blog/rss.xml'); $this->assertSame( @@ -54,4 +60,35 @@ public function testRss() '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() + { + $client = $this->getUserClient(); + + /** @var Post $post */ + $post = $client->getContainer()->get('doctrine')->getRepository(Post::class)->find(1); + $commentContent = $this->getRandomCommentContent(); + $commentsCount = $post->getComments()->count(); + + $crawler = $client->request('GET', '/en/blog/posts/'.$post->getSlug()); + $form = $crawler->selectButton('Publish comment')->form([ + 'comment[content]' => $commentContent, + ]); + $client->submit($form); + + $this->assertSame(Response::HTTP_FOUND, $client->getResponse()->getStatusCode()); + + $post = $client->getContainer()->get('doctrine')->getRepository(Post::class)->find(1); + // The first one is the last inserted (See descending order of comments association). + $comment = $post->getComments()->first(); + + $this->assertSame($commentsCount + 1, $post->getComments()->count()); + $this->assertSame($commentContent, $comment->getContent()); + } } diff --git a/tests/AppBundle/Controller/DefaultControllerTest.php b/tests/AppBundle/Controller/DefaultControllerTest.php index 10f462dfe..e63e4b93a 100644 --- a/tests/AppBundle/Controller/DefaultControllerTest.php +++ b/tests/AppBundle/Controller/DefaultControllerTest.php @@ -13,6 +13,8 @@ use AppBundle\Entity\Post; use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; +use Symfony\Component\HttpFoundation\Response; +use Tests\ControllerTestTrait; /** * Functional test that implements a "smoke test" of all the public and secure @@ -26,6 +28,8 @@ */ class DefaultControllerTest extends WebTestCase { + use ControllerTestTrait; + /** * PHPUnit's data providers allow to execute the same tests repeated times * using a different set of data each time. @@ -35,11 +39,12 @@ class DefaultControllerTest extends WebTestCase */ public function testPublicUrls($url) { - $client = self::createClient(); + $client = $this->getAnonymousClient(); $client->request('GET', $url); - $this->assertTrue( - $client->getResponse()->isSuccessful(), + $this->assertSame( + Response::HTTP_OK, + $client->getResponse()->getStatusCode(), sprintf('The %s public URL loads correctly.', $url) ); } @@ -54,11 +59,11 @@ public function testPublicUrls($url) public function testPublicBlogPost() { // the service container is always available via the client - $client = self::createClient(); + $client = $this->getAnonymousClient(); $blogPost = $client->getContainer()->get('doctrine')->getRepository(Post::class)->find(1); $client->request('GET', sprintf('/en/blog/posts/%s', $blogPost->getSlug())); - $this->assertTrue($client->getResponse()->isSuccessful()); + $this->assertSame(Response::HTTP_OK, $client->getResponse()->getStatusCode()); } /** @@ -70,14 +75,14 @@ public function testPublicBlogPost() */ public function testSecureUrls($url) { - $client = self::createClient(); + $client = $this->getAnonymousClient(); $client->request('GET', $url); - $this->assertTrue($client->getResponse()->isRedirect()); - + $response = $client->getResponse(); + $this->assertSame(Response::HTTP_FOUND, $response->getStatusCode()); $this->assertSame( 'http://localhost/en/login', - $client->getResponse()->getTargetUrl(), + $response->getTargetUrl(), sprintf('The %s secure URL redirects to the login form.', $url) ); } diff --git a/tests/ControllerTestTrait.php b/tests/ControllerTestTrait.php new file mode 100644 index 000000000..5039f23ff --- /dev/null +++ b/tests/ControllerTestTrait.php @@ -0,0 +1,48 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests; + +/** + * Helper class to create users clients. + */ +trait ControllerTestTrait +{ + /** + * @return \Symfony\Bundle\FrameworkBundle\Client + */ + private function getAdminClient() + { + return self::createClient([], [ + 'PHP_AUTH_USER' => 'jane_admin', + 'PHP_AUTH_PW' => 'kitten', + ]); + } + + /** + * @return \Symfony\Bundle\FrameworkBundle\Client + */ + private function getUserClient() + { + return self::createClient([], [ + 'PHP_AUTH_USER' => 'john_user', + 'PHP_AUTH_PW' => 'kitten', + ]); + } + + /** + * @return \Symfony\Bundle\FrameworkBundle\Client + */ + private function getAnonymousClient() + { + return self::createClient(); + } +} diff --git a/tests/FixturesTrait.php b/tests/FixturesTrait.php new file mode 100644 index 000000000..0f1bbdc8a --- /dev/null +++ b/tests/FixturesTrait.php @@ -0,0 +1,146 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Tests; + +/** + * Helper class to create fixtures contents. + */ +trait FixturesTrait +{ + private function getPostContent() + { + return <<<'MARKDOWN' +Lorem ipsum dolor sit amet consectetur adipisicing elit, sed do eiusmod tempor +incididunt ut labore et **dolore magna aliqua**: Duis aute irure dolor in +reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. +Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia +deserunt mollit anim id est laborum. + + * Ut enim ad minim veniam + * Quis nostrud exercitation *ullamco laboris* + * Nisi ut aliquip ex ea commodo consequat + +Praesent id fermentum lorem. Ut est lorem, fringilla at accumsan nec, euismod at +nunc. Aenean mattis sollicitudin mattis. Nullam pulvinar vestibulum bibendum. +Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos +himenaeos. Fusce nulla purus, gravida ac interdum ut, blandit eget ex. Duis a +luctus dolor. + +Integer auctor massa maximus nulla scelerisque accumsan. *Aliquam ac malesuada* +ex. Pellentesque tortor magna, vulputate eu vulputate ut, venenatis ac lectus. +Praesent ut lacinia sem. Mauris a lectus eget felis mollis feugiat. Quisque +efficitur, mi ut semper pulvinar, urna urna blandit massa, eget tincidunt augue +nulla vitae est. + +Ut posuere aliquet tincidunt. Aliquam erat volutpat. **Class aptent taciti** +sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi +arcu orci, gravida eget aliquam eu, suscipit et ante. Morbi vulputate metus vel +ipsum finibus, ut dapibus massa feugiat. Vestibulum vel lobortis libero. Sed +tincidunt tellus et viverra scelerisque. Pellentesque tincidunt cursus felis. +Sed in egestas erat. + +Aliquam pulvinar interdum massa, vel ullamcorper ante consectetur eu. Vestibulum +lacinia ac enim vel placerat. Integer pulvinar magna nec dui malesuada, nec +congue nisl dictum. Donec mollis nisl tortor, at congue erat consequat a. Nam +tempus elit porta, blandit elit vel, viverra lorem. Sed sit amet tellus +tincidunt, faucibus nisl in, aliquet libero. +MARKDOWN; + } + + private function getPhrases() + { + return [ + 'Lorem ipsum dolor sit amet consectetur adipiscing elit', + 'Pellentesque vitae velit ex', + 'Mauris dapibus risus quis suscipit vulputate', + 'Eros diam egestas libero eu vulputate risus', + 'In hac habitasse platea dictumst', + 'Morbi tempus commodo mattis', + 'Ut suscipit posuere justo at vulputate', + 'Ut eleifend mauris et risus ultrices egestas', + 'Aliquam sodales odio id eleifend tristique', + 'Urna nisl sollicitudin id varius orci quam id turpis', + 'Nulla porta lobortis ligula vel egestas', + 'Curabitur aliquam euismod dolor non ornare', + 'Sed varius a risus eget aliquam', + 'Nunc viverra elit ac laoreet suscipit', + 'Pellentesque et sapien pulvinar consectetur', + 'Ubi est barbatus nix', + 'Abnobas sunt hilotaes de placidus vita', + 'Ubi est audax amicitia', + 'Eposs sunt solems de superbus fortis', + 'Vae humani generis', + 'Diatrias tolerare tanquam noster caesium', + 'Teres talis saepe tractare de camerarius flavum sensorem', + 'Silva de secundus galatae demitto quadra', + 'Sunt accentores vitare salvus flavum parses', + 'Potus sensim ad ferox abnoba', + 'Sunt seculaes transferre talis camerarius fluctuies', + 'Era brevis ratione est', + 'Sunt torquises imitari velox mirabilis medicinaes', + 'Mineralis persuadere omnes finises desiderium', + 'Bassus fatalis classiss virtualiter transferre de flavum', + ]; + } + + private function getTagNames() + { + return [ + 'lorem', + 'ipsum', + 'consectetur', + 'adipiscing', + 'incididunt', + 'labore', + 'voluptate', + 'dolore', + 'pariatur', + ]; + } + + private function getRandomPostTitles() + { + $phrases = $this->getPhrases(); + + // this ensures that the first title is always 'Lorem Ipsum...' + $loremIpsumPhrase = array_shift($phrases); + shuffle($phrases); + array_unshift($phrases, $loremIpsumPhrase); + + return $phrases; + } + + private function getRandomPostSummary($maxLength = 255) + { + $phrases = $this->getPhrases(); + + $numPhrases = mt_rand(6, 12); + shuffle($phrases); + $phrases = array_slice($phrases, 0, $numPhrases - 1); + + while (strlen($summary = implode('. ', $phrases).'.') > $maxLength) { + array_pop($phrases); + } + + return $summary; + } + + private function getRandomCommentContent() + { + $phrases = $this->getPhrases(); + + $numPhrases = mt_rand(2, 15); + shuffle($phrases); + + return implode(' ', array_slice($phrases, 0, $numPhrases - 1)); + } +}