Skip to content

Commit

Permalink
Improve tests
Browse files Browse the repository at this point in the history
  • Loading branch information
javiereguiluz committed Jan 22, 2025
1 parent f0ae222 commit 8e7a0ec
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 103 deletions.
4 changes: 2 additions & 2 deletions src/Twig/Component/Icon.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
namespace EasyCorp\Bundle\EasyAdminBundle\Twig\Component;

use EasyCorp\Bundle\EasyAdminBundle\Config\Option\IconSet;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Provider\AdminContextProviderInterface;
use EasyCorp\Bundle\EasyAdminBundle\Dto\IconDto;
use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;

class Icon
{
Expand All @@ -13,7 +13,7 @@ class Icon
private ?string $iconSet = null;

public function __construct(
private AdminContextProvider $adminContextProvider,
private AdminContextProviderInterface $adminContextProvider,
) {
}

Expand Down
14 changes: 12 additions & 2 deletions src/Twig/EasyAdminTwigExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,20 @@ public function representAsString($value, string|callable|null $toStringMethod =
}

if (method_exists($value, 'getId')) {
return sprintf('%s #%s', $value::class, $value->getId());
return sprintf(
'%s #%s',
// remove null bytes from class name (this happens in anonymous classes)
str_replace("\0", '', $value::class),
$value->getId()
);
}

return sprintf('%s #%s', $value::class, hash('xxh32', (string) spl_object_id($value)));
return sprintf(
'%s #%s',
// remove null bytes from class name (this happens in anonymous classes)
str_replace("\0", '', $value::class),
hash('xxh32', (string) spl_object_id($value))
);
}

return '';
Expand Down
119 changes: 53 additions & 66 deletions tests/Menu/MenuItemMatcherTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminRouteGenerator;
use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\HttpFoundation\InputBag;
use Symfony\Component\HttpFoundation\Request;

class MenuItemMatcherTest extends KernelTestCase
{
public function testIsSelectedWhenContextIsNull()
{
$request = $this->getRequestMock();
$request = $this->createRequest();

self::bootKernel();
$adminUrlGenerator = self::getContainer()->get(AdminUrlGenerator::class);
Expand All @@ -31,7 +30,7 @@ public function testIsSelectedWhenContextIsNull()

public function testIsSelectedWhenMenuItemIsSection()
{
$request = $this->getRequestMock();
$request = $this->createRequest();

self::bootKernel();
$adminUrlGenerator = self::getContainer()->get(AdminUrlGenerator::class);
Expand All @@ -47,8 +46,8 @@ public function testIsSelectedWhenMenuItemIsSection()

public function testIsSelectedWithCrudControllers()
{
$request = $this->getRequestMock(
getControllerFqcn: 'App\Controller\Admin\SomeController',
$request = $this->createRequest(
crudControllerFqcn: 'App\Controller\Admin\SomeController',
);

self::bootKernel();
Expand All @@ -68,10 +67,10 @@ public function testIsSelectedWithCrudControllers()
$menuItemMatcher->markSelectedMenuItem([$menuItemDto], $request);
$this->assertTrue($menuItemDto->isSelected(), 'The CRUD controller matches');

$request = $this->getRequestMock(
getControllerFqcn: 'App\Controller\Admin\SomeController',
getPrimaryKeyValue: '57',
getCurrentAction: 'edit',
$request = $this->createRequest(
crudControllerFqcn: 'App\Controller\Admin\SomeController',
entityId: '57',
action: 'edit',
);

$menuItemDto = $this->getMenuItemDto(crudControllerFqcn: 'App\Controller\Admin\SomeController', action: 'edit', entityId: '57');
Expand All @@ -82,10 +81,10 @@ public function testIsSelectedWithCrudControllers()
$menuItemMatcher->markSelectedMenuItem([$menuItemDto], $request);
$this->assertFalse($menuItemDto->isSelected(), 'The entity ID of the menu item does not match');

$request = $this->getRequestMock(
getControllerFqcn: 'App\Controller\Admin\SomeController',
getPrimaryKeyValue: '57',
getCurrentAction: 'detail',
$request = $this->createRequest(
crudControllerFqcn: 'App\Controller\Admin\SomeController',
entityId: '57',
action: 'detail',
);

$menuItemDto = $this->getMenuItemDto(crudControllerFqcn: 'App\Controller\Admin\SomeController', action: Crud::PAGE_DETAIL, entityId: '57');
Expand All @@ -99,7 +98,7 @@ public function testIsSelectedWithCrudControllers()

public function testIsSelectedWithRoutes()
{
$request = $this->getRequestMock(
$request = $this->createRequest(
routeName: 'some_route',
);

Expand All @@ -117,7 +116,7 @@ public function testIsSelectedWithRoutes()
$menuItemMatcher->markSelectedMenuItem([$menuItemDto], $request);
$this->assertFalse($menuItemDto->isSelected());

$request = $this->getRequestMock(
$request = $this->createRequest(
routeName: 'some_route',
routeParameters: ['foo1' => 'bar1', 'foo2' => 'bar2'],
);
Expand All @@ -141,8 +140,9 @@ public function testIsSelectedWithRoutes()

public function testIsSelectedWithUrls()
{
$request = $this->getRequestMock(
getUri: 'https://example.com/foo?bar=baz',
$request = $this->createRequest(
requestPath: '/foo',
queryParameters: ['bar' => 'baz'],
);

self::bootKernel();
Expand Down Expand Up @@ -182,7 +182,7 @@ public function testMenuWithDashboardItem()
$this->getMenuItemDto(label: 'item2', routeName: 'item2'),
];

$request = $this->getRequestMock(
$request = $this->createRequest(
routeName: 'item2',
);

Expand All @@ -198,8 +198,8 @@ public function testMenuWithDashboardItem()
public function testComplexMenu()
{
$menuItems = $this->getComplexMenuItems();
$request = $this->getRequestMock(
getControllerFqcn: 'App\Controller\Admin\Controller1',
$request = $this->createRequest(
crudControllerFqcn: 'App\Controller\Admin\Controller1',
);

self::bootKernel();
Expand All @@ -214,9 +214,9 @@ public function testComplexMenu()

unset($menuItems);
$menuItems = $this->getComplexMenuItems();
$request = $this->getRequestMock(
getControllerFqcn: 'App\Controller\Admin\Controller1',
getCurrentAction: 'edit',
$request = $this->createRequest(
crudControllerFqcn: 'App\Controller\Admin\Controller1',
action: 'edit',
// the primary key value is missing on purpose in this example
);
$menuItems = $menuItemMatcher->markSelectedMenuItem($menuItems, $request);
Expand All @@ -225,40 +225,40 @@ public function testComplexMenu()

unset($menuItems);
$menuItems = $this->getComplexMenuItems();
$request = $this->getRequestMock(
getControllerFqcn: 'App\Controller\Admin\Controller1',
getCurrentAction: 'new',
$request = $this->createRequest(
crudControllerFqcn: 'App\Controller\Admin\Controller1',
action: 'new',
);
$menuItems = $menuItemMatcher->markSelectedMenuItem($menuItems, $request);
$this->assertSame('item5', $this->getSelectedMenuItemLabel($menuItems), 'Perfect match: CRUD controller and action');
$this->assertSame('item4', $this->getExpandedMenuItemLabel($menuItems), 'A submenu item is matched, so its parent item must be marked as expanded');

unset($menuItems);
$menuItems = $this->getComplexMenuItems();
$request = $this->getRequestMock(
getControllerFqcn: 'App\Controller\Admin\Controller2',
getCurrentAction: 'new',
$request = $this->createRequest(
crudControllerFqcn: 'App\Controller\Admin\Controller2',
action: 'new',
);
$menuItems = $menuItemMatcher->markSelectedMenuItem($menuItems, $request);
$this->assertSame('item3', $this->getSelectedMenuItemLabel($menuItems), 'Approximate match: controller matches, action doesn\'t match; the item with the INDEX action is selected by default');
$this->assertNull($this->getExpandedMenuItemLabel($menuItems), 'No menu item is marked as expanded');

unset($menuItems);
$menuItems = $this->getComplexMenuItems();
$request = $this->getRequestMock(
getControllerFqcn: 'App\Controller\Admin\Controller2',
getCurrentAction: 'edit',
getPrimaryKeyValue: 'NOT_57',
$request = $this->createRequest(
crudControllerFqcn: 'App\Controller\Admin\Controller2',
action: 'edit',
entityId: 'NOT_57',
);
$menuItems = $menuItemMatcher->markSelectedMenuItem($menuItems, $request);
$this->assertNull($this->getSelectedMenuItemLabel($menuItems), 'No match: controller and action match, but query parameters don\'t');
$this->assertNull($this->getExpandedMenuItemLabel($menuItems), 'No menu item is marked as expanded');

unset($menuItems);
$menuItems = $this->getComplexMenuItems();
$request = $this->getRequestMock(
getControllerFqcn: 'App\Controller\Admin\Controller3',
getCurrentAction: 'new',
$request = $this->createRequest(
crudControllerFqcn: 'App\Controller\Admin\Controller3',
action: 'new',
);
$menuItems = $menuItemMatcher->markSelectedMenuItem($menuItems, $request);
$this->assertSame('item7', $this->getSelectedMenuItemLabel($menuItems), 'Approximate match: only the controller matches; the item with the INDEX action is selected');
Expand Down Expand Up @@ -356,38 +356,25 @@ private function getMenuItemDto(?string $crudControllerFqcn = null, ?string $act
return $menuItemDto;
}

private function getRequestMock(?string $getControllerFqcn = null, ?string $getPrimaryKeyValue = null, ?string $getCurrentAction = null, ?string $routeName = null, ?array $routeParameters = null, ?string $getUri = null): Request
private function createRequest(?string $crudControllerFqcn = null, ?string $entityId = null, ?string $action = null, ?string $routeName = null, ?array $routeParameters = null, ?string $requestPath = null, array $queryParameters = []): Request
{
$queryParameters = [];

if (null !== $getControllerFqcn) {
$queryParameters[EA::CRUD_CONTROLLER_FQCN] = $getControllerFqcn;
}
if (null !== $getCurrentAction) {
$queryParameters[EA::CRUD_ACTION] = $getCurrentAction;
}

if (null !== $getPrimaryKeyValue) {
$queryParameters[EA::ENTITY_ID] = $getPrimaryKeyValue;
}

if (null !== $routeName) {
$queryParameters[EA::ROUTE_NAME] = $routeName;
}
if (null !== $routeParameters) {
$queryParameters[EA::ROUTE_PARAMS] = $routeParameters;
}

$request = $this->getMockBuilder(Request::class)->getMock();
$request->query = new InputBag($queryParameters);
$request->attributes = new InputBag($queryParameters);

if (null !== $getUri) {
$request->method('getUri')->willReturn($getUri);
} else {
$request->method('getUri')->willReturn('/?'.http_build_query($queryParameters));
$queryParameters[EA::CRUD_CONTROLLER_FQCN] = $crudControllerFqcn;
$queryParameters[EA::CRUD_ACTION] = $action;
$queryParameters[EA::ENTITY_ID] = $entityId;
$queryParameters[EA::ROUTE_NAME] = $routeName;
$queryParameters[EA::ROUTE_PARAMS] = $routeParameters;

$queryParameters = array_filter($queryParameters, static fn ($value) => null !== $value);

$serverParameters = [
'HTTPS' => 'On',
'HTTP_HOST' => 'example.com',
'QUERY_STRING' => http_build_query($queryParameters),
];
if (null !== $requestPath) {
$serverParameters['REQUEST_URI'] = '/'.ltrim($requestPath, '/');
}

return $request;
return new Request(query: $queryParameters, attributes: $queryParameters, server: $serverParameters);
}
}
13 changes: 7 additions & 6 deletions tests/Twig/Component/IconTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
namespace EasyCorp\Bundle\EasyAdminBundle\Tests\Twig\Component;

use EasyCorp\Bundle\EasyAdminBundle\Config\Option\IconSet;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Context\AdminContextInterface;
use EasyCorp\Bundle\EasyAdminBundle\Contracts\Provider\AdminContextProviderInterface;
use EasyCorp\Bundle\EasyAdminBundle\Dto\AssetsDto;
use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
use EasyCorp\Bundle\EasyAdminBundle\Twig\Component\Icon;
Expand Down Expand Up @@ -123,11 +124,11 @@ public function testUnknownInternalIcon(): void
*/
private function getAdminContextProviderMock(string $appIconSet)
{
$adminContextProvider = $this->getMockBuilder(AdminContextProvider::class)->disableOriginalConstructor()->getMock();
$adminContext = $this->getMockBuilder(AdminContext::class)->disableOriginalConstructor()->getMock();
$assetsDto = $this->getMockBuilder(AssetsDto::class)->disableOriginalConstructor()->getMock();
$assetsDto->method('getIconSet')->willReturn($appIconSet);
$assetsDto->method('getDefaultIconPrefix')->willReturn(''); // TODO
$adminContextProvider = $this->getMockBuilder(AdminContextProviderInterface::class)->disableOriginalConstructor()->getMock();
$adminContext = $this->getMockBuilder(AdminContextInterface::class)->disableOriginalConstructor()->getMock();
$assetsDto = new AssetsDto();
$assetsDto->setIconSet($appIconSet);
$assetsDto->setDefaultIconPrefix(''); // TODO
$adminContext->method('getAssets')->willReturn($assetsDto);
$adminContextProvider->method('getContext')->willReturn($adminContext);

Expand Down
50 changes: 23 additions & 27 deletions tests/Twig/EasyAdminTwigExtensionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,10 @@

namespace EasyCorp\Bundle\EasyAdminBundle\Tests\Twig;

use EasyCorp\Bundle\EasyAdminBundle\Provider\AdminContextProvider;
use EasyCorp\Bundle\EasyAdminBundle\Twig\EasyAdminTwigExtension;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;
use Symfony\Component\AssetMapper\ImportMap\ImportMapRenderer;
use Symfony\Component\DependencyInjection\ServiceLocator;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Contracts\Translation\TranslatableInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Symfony\UX\Icons\Twig\UXIconRuntime;

class EasyAdminTwigExtensionTest extends KernelTestCase
{
Expand All @@ -19,45 +14,46 @@ class EasyAdminTwigExtensionTest extends KernelTestCase
*/
public function testRepresentAsString($value, $expectedValue, bool $assertRegex = false, string|callable|null $toStringMethod = null): void
{
$translator = $this->getMockBuilder(TranslatorInterface::class)->disableOriginalConstructor()->getMock();
$translator->method('trans')->willReturnCallback(fn ($value) => '*'.$value);
$customTranslator = new class implements TranslatorInterface {
public function trans(string $id, array $parameters = [], ?string $domain = null, ?string $locale = null): string
{
return '*'.$id;
}

public function getLocale(): string
{
return 'en';
}
};

$extension = new EasyAdminTwigExtension(
$this->getMockBuilder(ServiceLocator::class)->disableOriginalConstructor()->getMock(),
$this->getMockBuilder(AdminContextProvider::class)->disableOriginalConstructor()->getMock(),
$this->getMockBuilder(CsrfTokenManagerInterface::class)->disableOriginalConstructor()->getMock(),
$this->getMockBuilder(ImportMapRenderer::class)->disableOriginalConstructor()->getMock(),
$translator,
$this->getMockBuilder(UXIconRuntime::class)->disableOriginalConstructor()->getMock(),
);
$reflectedClass = new \ReflectionClass(EasyAdminTwigExtension::class);
$twigExtensionInstance = $reflectedClass->newInstanceWithoutConstructor();
$property = $reflectedClass->getProperty('translator');
$property->setValue($twigExtensionInstance, $customTranslator);

$result = $extension->representAsString($value, $toStringMethod);
$result = $twigExtensionInstance->representAsString($value, $toStringMethod);

if ($assertRegex) {
$this->assertMatchesRegularExpression($expectedValue, $result);
} else {
$this->assertSame($expectedValue, $result);
}

$this->assertStringNotContainsString("\0", $result, 'The string representation of a value must not contain the null character (which can happen when the original value is an anonymous class object)');
}

public function testRepresentAsStringExcepion()
public function testRepresentAsStringException(): void
{
$this->expectException(\RuntimeException::class);
$this->expectExceptionMessageMatches('/The method "someMethod\(\)" does not exist or is not callable in the value of type "class@anonymous.*"/');

$extension = new EasyAdminTwigExtension(
$this->getMockBuilder(ServiceLocator::class)->disableOriginalConstructor()->getMock(),
$this->getMockBuilder(AdminContextProvider::class)->disableOriginalConstructor()->getMock(),
$this->getMockBuilder(CsrfTokenManagerInterface::class)->disableOriginalConstructor()->getMock(),
$this->getMockBuilder(ImportMapRenderer::class)->disableOriginalConstructor()->getMock(),
$this->getMockBuilder(TranslatorInterface::class)->disableOriginalConstructor()->getMock(),
$this->getMockBuilder(UXIconRuntime::class)->disableOriginalConstructor()->getMock(),
);
$reflectedClass = new \ReflectionClass(EasyAdminTwigExtension::class);
$twigExtensionInstance = $reflectedClass->newInstanceWithoutConstructor();

$extension->representAsString(new class {}, 'someMethod');
$twigExtensionInstance->representAsString(new class {}, 'someMethod');
}

public function provideValuesForRepresentAsString()
public function provideValuesForRepresentAsString(): iterable
{
yield [null, ''];
yield ['foo bar', 'foo bar'];
Expand Down

0 comments on commit 8e7a0ec

Please sign in to comment.