-
-
Notifications
You must be signed in to change notification settings - Fork 1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Test : AbstractCrudTestCase definition to use in EasyAdmin Crud testi…
…ng (#5621) * feat : init AbstractCrudTestCase * feat : update CategoryCrudControllerTest using AbstractCrudTestCase * refactor : refactor to use Traits * test : add test for CrudTestUrlGenerationTrait * feat : fix linting issues * test : align testing & linting * feat : setup base traits * feat : assertPageCount * feat : add assert Index pages and entity counts * feat : add Asserts * feat : add action * feat : add dashboard and controller fqcn as input for url generators (#7) * Documentation
- Loading branch information
Showing
19 changed files
with
1,748 additions
and
140 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ Table of Contents | |
actions | ||
security | ||
events | ||
tests | ||
upgrade | ||
|
||
Technical Requirements | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
Tests | ||
====== | ||
|
||
As EasyAdmin is based on Symfony, functionally testing the admin pages can leverage the | ||
`Symfony functional testing workflow`_ extending the :code:`WebTestCase` class. | ||
|
||
But, as EasyAdmin uses specific defined ways of displaying the data in its Crud pages, | ||
a custom test class is provided : :code:`AbstractCrudTestCase`. The | ||
class is based on traits which defines custom asserts and custom helpers. | ||
|
||
|
||
1. `Functional Test Case Example`_ | ||
2. `Url Generation`_ | ||
3. `Asserts`_ | ||
4. `Selector Helpers`_ | ||
|
||
|
||
Functional Test Case Example | ||
------------------------------------------- | ||
|
||
Suppose you have a `Dashboard`_ named :code:`App\\Controller\\Admin\\AppDashboardController` and | ||
a `Category Crud Controller`_ named :code:`App\\Controller\\Admin\\CategoryCrudController`. Here's an | ||
example of a functional test class for that Controller. | ||
|
||
First, your test class need to extend the :code:`AbstractCrudTestCase`. | ||
|
||
.. code-block:: php | ||
# tests/Admin/Controller/CategoryCrudControllerTest.php | ||
namespace App\Tests\Admin\Controller; | ||
use App\Controller\Admin\CategoryCrudController; | ||
use App\Controller\Admin\AppDashboardController | ||
use EasyCorp\Bundle\EasyAdminBundle\Test\AbstractCrudTestCase; | ||
final class CategoryCrudControllerTest extends AbstractCrudTestCase | ||
{ | ||
protected function getControllerFqcn(): string | ||
{ | ||
return CategoryCrudController::class; | ||
} | ||
protected function getDashboardFqcn(): string | ||
{ | ||
return AppDashboardController::class; | ||
} | ||
public function testIndexPage(): void | ||
{ | ||
// no use of security here, up to you to ensure the login in your test in case it's necessary | ||
$this->client->request("GET", $this->generateIndexUrl()); | ||
static::assertResponseIsSuccessful(); | ||
} | ||
} | ||
Url Generation | ||
------------------------ | ||
Used by the :code:`AbstractCrudTestCase`, :code:`CrudTestUrlGeneration` is an url generation trait which helps to generate the specific of | ||
the EasyAdmin urls. | ||
|
||
.. note:: | ||
|
||
The trait can, of course, be used on its own but in that case, the class that is using it needs either: | ||
|
||
- to define the 2 functions :code:`getControllerFqcn` and :code:`getDashboardFqcn` | ||
- to add the DashboardFqcn (class name) and ControllerFqcn (class name) as input to the url generation functions | ||
|
||
Here is the list of the url generation functions that are all providing url based on provided Dashboard | ||
& Controller class names: | ||
|
||
- :code:`getCrudUrl` : is the main function that allows for a complete generation with all possible options. | ||
- :code:`generateIndexUrl` : generates the url for the index page (based on the Dashboard and Controller defined) | ||
- :code:`generateNewFormUrl` : generates the url for the New form page (based on the Dashboard and Controller defined) | ||
- :code:`generateEditFormUrl` : generates the url for the Edit form page of a specific entity (based on the Dashboard and Controller defined and the entity Id) | ||
- :code:`generateDetailUrl` : generates the url for the Detail page of a specific entity (based on the Dashboard and Controller defined and the entity Id) | ||
- :code:`generateFilterRenderUrl` : generates the url to get the rendering of the filters (based on the Dashboard and Controller defined) | ||
|
||
Asserts | ||
------------------------ | ||
Used by the `AbstractCrudTestCase`, are 2 traits filled with specific asserts for EasyAdmin web testing: | ||
|
||
- :code:`CrudTestIndexAsserts`: providing asserts for the index page of EasyAdmin | ||
- :code:`CrudTestFormAsserts` : providing asserts for the form page of EasyAdmin | ||
|
||
.. note:: | ||
|
||
The trait can, of course, be used on its own but in that case, the class that is using it needs both: | ||
|
||
- a class property :code:`client` : instance of :code:`Symfony\\Bundle\\FrameworkBundle\\KernelBrowser` | ||
- a class property :code:`entitytManager` : instance of :code:`Doctrine\\ORM\\EntityManagerInterface` | ||
|
||
|
||
CrudTestIndexAsserts | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
As EasyAdmin uses specific layout, the goal of these Asserts is to ease the way you're testing your EasyAdmin backend by providing specific asserts linked to the Index page. | ||
|
||
The following asserts are existing: | ||
|
||
- :code:`assertIndexFullEntityCount` | ||
- :code:`assertIndexPageEntityCount` | ||
- :code:`assertIndexPagesCount` | ||
- :code:`assertIndexEntityActionExists` | ||
- :code:`assertIndexEntityActionNotExists` | ||
- :code:`assertIndexEntityActionTextSame` | ||
- :code:`assertIndexEntityActionNotTextSame` | ||
- :code:`assertGlobalActionExists` | ||
- :code:`assertGlobalActionNotExists` | ||
- :code:`assertGlobalActionDisplays` | ||
- :code:`assertGlobalActionNotDisplays` | ||
- :code:`assertIndexColumnExists` | ||
- :code:`assertIndexColumnNotExists` | ||
- :code:`assertIndexColumnHeaderContains` | ||
- :code:`assertIndexColumnHeaderNotContains` | ||
|
||
|
||
CrudTestFormAsserts | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
As EasyAdmin uses specific layout, the goal of these Asserts is to ease the way you're testing your EasyAdmin backend by providing specific asserts linked to the **Form** (New or Edit) page. | ||
|
||
The following asserts are existing: | ||
|
||
- :code:`assertFormFieldExists` | ||
- :code:`assertFormFieldNotExists` | ||
- :code:`assertFormFieldHasLabel` | ||
- :code:`assertFormFieldNotHasLabel` | ||
|
||
|
||
Selector Helpers | ||
------------------------ | ||
Used by the Asserts to locate elements, the Trait :code:`CrudTestSelectors` is defining a specific amounts of selector helpers linked to the specificities of EasyAdmin layout. | ||
|
||
.. note:: | ||
|
||
The trait can, of course, be used on its own. It only defines selector strings. | ||
|
||
The following helpers are existing: | ||
|
||
|
||
- :code:`getActionSelector` | ||
- :code:`getGlobalActionSelector` | ||
- :code:`getIndexEntityActionSelector` | ||
- :code:`getIndexEntityRowSelector` | ||
- :code:`getIndexColumnSelector` | ||
- :code:`getIndexHeaderColumnSelector` | ||
- :code:`getIndexHeaderRowSelector` | ||
- :code:`getFormEntity` | ||
- :code:`getEntityFormSelector` | ||
- :code:`getFormFieldIdValue` | ||
- :code:`getFormFieldSelector` | ||
- :code:`getFormFieldLabelSelector` | ||
|
||
|
||
.. _`Symfony functional testing workflow`: https://symfony.com/doc/current/testing.html#application-tests | ||
.. _Dashboard: https://symfony.com/bundles/EasyAdminBundle/4.x/dashboards.html | ||
.. _`Category Crud Controller`: https://symfony.com/bundles/EasyAdminBundle/4.x/crud.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace EasyCorp\Bundle\EasyAdminBundle\Test; | ||
|
||
use Doctrine\ORM\EntityManagerInterface; | ||
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator; | ||
use EasyCorp\Bundle\EasyAdminBundle\Test\Trait\CrudTestActions; | ||
use EasyCorp\Bundle\EasyAdminBundle\Test\Trait\CrudTestIndexAsserts; | ||
use EasyCorp\Bundle\EasyAdminBundle\Test\Trait\CrudTestUrlGeneration; | ||
use Symfony\Bundle\FrameworkBundle\KernelBrowser; | ||
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase; | ||
|
||
abstract class AbstractCrudTestCase extends WebTestCase | ||
{ | ||
use CrudTestActions; | ||
use CrudTestIndexAsserts; | ||
use CrudTestUrlGeneration; | ||
|
||
protected KernelBrowser $client; | ||
protected AdminUrlGenerator $adminUrlGenerator; | ||
protected EntityManagerInterface $entityManager; | ||
|
||
protected function setUp(): void | ||
{ | ||
$this->client = static::createClient(); | ||
$container = static::getContainer(); | ||
$this->entityManager = $container->get(EntityManagerInterface::class); | ||
$this->adminUrlGenerator = $container->get(AdminUrlGenerator::class); | ||
} | ||
|
||
/** | ||
* @return string returns the tested Controller Fqcn | ||
*/ | ||
abstract protected function getControllerFqcn(): string; | ||
|
||
/** | ||
* @return string returns the tested Controller Fqcn | ||
*/ | ||
abstract protected function getDashboardFqcn(): string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace EasyCorp\Bundle\EasyAdminBundle\Test\Exception; | ||
|
||
final class InvalidClassPropertyTypeException extends \Exception | ||
{ | ||
public function __construct(string $propertyName, string $propertyType, int $code = 0, ?\Throwable $previous = null) | ||
{ | ||
$message = sprintf('The test class should have a property named %s of type %s', $propertyName, $propertyType); | ||
|
||
parent::__construct($message, $code, $previous); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace EasyCorp\Bundle\EasyAdminBundle\Test\Exception; | ||
|
||
final class MissingClassMethodException extends \Exception | ||
{ | ||
/** | ||
* @param array<array-key, string> $methodsName | ||
*/ | ||
public function __construct(array $methodsName, int $code = 0, ?\Throwable $previous = null) | ||
{ | ||
$message = 'The class should implement the following methods :'; | ||
|
||
foreach ($methodsName as $key => $methodName) { | ||
$message .= sprintf(' %s', $methodName); | ||
if ($key !== array_key_last($methodsName)) { | ||
$message .= ' and'; | ||
} | ||
} | ||
|
||
parent::__construct($message, $code, $previous); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace EasyCorp\Bundle\EasyAdminBundle\Test\Trait; | ||
|
||
use function PHPUnit\Framework\assertCount; | ||
|
||
trait CrudTestActions | ||
{ | ||
protected function clickOnIndexGlobalAction(string $globalAction): void | ||
{ | ||
$crawler = $this->client->getCrawler(); | ||
$action = $crawler->filter(sprintf('.global-actions .action-%s', $globalAction)); | ||
|
||
assertCount(1, $action, sprintf('There is no action %s in the page', $globalAction)); | ||
|
||
$this->client->click($action->link()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
<?php | ||
|
||
declare(strict_types=1); | ||
|
||
namespace EasyCorp\Bundle\EasyAdminBundle\Test\Trait; | ||
|
||
use PHPUnit\Framework\Constraint\LogicalNot; | ||
use Symfony\Component\DomCrawler\Test\Constraint\CrawlerSelectorTextSame; | ||
|
||
trait CrudTestFormAsserts | ||
{ | ||
use CrudTestSelectors; | ||
|
||
protected function assertFormFieldExists(string $fieldName, ?string $message = null): void | ||
{ | ||
$message ??= sprintf('The field %s is not existing in the form', $fieldName); | ||
|
||
self::assertSelectorExists($this->getFormFieldSelector($fieldName), $message); | ||
} | ||
|
||
protected function assertFormFieldNotExists(string $fieldName, ?string $message = null): void | ||
{ | ||
$message ??= sprintf('The field %s is existing in the form', $fieldName); | ||
|
||
self::assertSelectorNotExists($this->getFormFieldSelector($fieldName), $message); | ||
} | ||
|
||
protected function assertFormFieldHasLabel(string $fieldName, string $label, ?string $message = null): void | ||
{ | ||
$message ??= sprintf('The field %s has not the correct label %s', $fieldName, $label); | ||
|
||
self::assertSelectorExists( | ||
$this->getFormFieldLabelSelector($fieldName), | ||
sprintf('There is no label for the field %s', $fieldName) | ||
); | ||
self::assertSelectorTextSame($this->getFormFieldLabelSelector($fieldName), $label, $message); | ||
} | ||
|
||
protected function assertFormFieldNotHasLabel(string $fieldName, string $label, ?string $message = null): void | ||
{ | ||
$message ??= sprintf('The field %s has the label %s', $fieldName, $label); | ||
|
||
self::assertSelectorExists( | ||
$this->getFormFieldLabelSelector($fieldName), | ||
sprintf('There is no label for the field %s', $fieldName) | ||
); | ||
|
||
self::assertThat( | ||
$this->client->getCrawler(), | ||
new LogicalNot(new CrawlerSelectorTextSame($this->getFormFieldLabelSelector($fieldName), $label)), | ||
$message | ||
); | ||
} | ||
} |
Oops, something went wrong.