diff --git a/composer.json b/composer.json
index 9dd0f5a69f..7660b4e276 100644
--- a/composer.json
+++ b/composer.json
@@ -59,6 +59,9 @@
"symfony/web-link": "^5.4|^6.0|^7.0|^8.0",
"vincentlanglet/twig-cs-fixer": "^3.10"
},
+ "conflict": {
+ "symfony/error-handler": "<5.4.35"
+ },
"config": {
"sort-packages": true,
"allow-plugins": {
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 913281fd33..30da8a1348 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -19,7 +19,6 @@
tests/
tests/Controller/PrettyUrls/
- tests/EventListener/
tests/Form/
diff --git a/tests/EventListener/ExceptionListenerTest.php b/tests/EventListener/ExceptionListenerTest.php
index bd519fb758..d87dac48a1 100644
--- a/tests/EventListener/ExceptionListenerTest.php
+++ b/tests/EventListener/ExceptionListenerTest.php
@@ -2,81 +2,120 @@
namespace EasyCorp\Bundle\EasyAdminBundle\Tests\EventListener;
+use EasyCorp\Bundle\EasyAdminBundle\Context\ExceptionContext;
+use EasyCorp\Bundle\EasyAdminBundle\Contracts\Context\AdminContextInterface;
+use EasyCorp\Bundle\EasyAdminBundle\Contracts\Provider\AdminContextProviderInterface;
use EasyCorp\Bundle\EasyAdminBundle\EventListener\ExceptionListener;
-use EasyCorp\Bundle\EasyAdminBundle\Exception\EntityNotFoundException as EasyEntityNotFoundException;
+use EasyCorp\Bundle\EasyAdminBundle\Exception\BaseException;
use PHPUnit\Framework\TestCase;
use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Event\ExceptionEvent;
use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Twig\Environment;
+use Twig\Error\RuntimeError;
+use Twig\Loader\ArrayLoader;
class ExceptionListenerTest extends TestCase
{
- private function getTwig()
+ /**
+ * @dataProvider unhandledException
+ */
+ public function testUnhandledException(bool $kernelDebug, bool $contextIsNull, \Exception $exception): void
{
- $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock();
- $twig->method('render')->willReturn('template content');
+ $contextProvider = $this->createMock(AdminContextProviderInterface::class);
+ if (!$contextIsNull) {
+ $context = $this->createMock(AdminContextInterface::class);
+ $context->method('getTemplatePath')->willReturn('foo');
+ $contextProvider->method('getContext')->willReturn($context);
+ }
- return $twig;
- }
+ $listener = new ExceptionListener(
+ $kernelDebug,
+ $contextProvider,
+ $this->createMock(Environment::class),
+ );
- private function getEventExceptionThatShouldBeCalledOnce($exception)
- {
- $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent')
- ->disableOriginalConstructor()
- ->getMock();
- $event->method('getException')->willReturn($exception);
- $event->method('getRequest')->willReturn(new Request());
- $event->method('getKernel')->willReturn(new TestKernel());
- $event->expects($this->once())->method('setResponse');
-
- return $event;
+ $expectedMessage = $exception->getMessage();
+
+ $listener->onKernelException($exceptionEvent = $this->createExceptionEvent($exception));
+
+ $this->assertSame($expectedMessage, $exceptionEvent->getThrowable()->getMessage());
+ $this->assertNull($exceptionEvent->getResponse());
}
- public function testCatchBaseExceptions(): void
+ public static function unhandledException(): \Generator
{
- $exception = new EasyEntityNotFoundException([
- 'entity_name' => 'Test',
- 'entity_id_name' => 'Test key',
- 'entity_id_value' => 2,
- ]);
- $event = $this->getEventExceptionThatShouldBeCalledOnce($exception);
- $twig = $this->getTwig();
-
- $listener = new ExceptionListener($twig, []);
- $listener->onKernelException($event);
+ yield [true, true, new \Exception()];
+ yield [true, false, new \Exception()];
+ yield [false, true, new \Exception()];
+ yield [false, false, new \Exception()];
+ yield [true, true, new RuntimeError('foo')];
+ yield [true, false, new RuntimeError('foo')];
+ yield [false, true, new RuntimeError('foo')];
+ yield [false, false, new RuntimeError('foo')];
+ yield [true, true, new class(new ExceptionContext('foo')) extends BaseException {}];
+ yield [true, false, new class(new ExceptionContext('foo')) extends BaseException {}];
+ yield [false, true, new class(new ExceptionContext('foo')) extends BaseException {}];
}
- private function getEventExceptionThatShouldNotBeCalled($exception)
+ public function testAppendMessage(): void
{
- $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent')
- ->disableOriginalConstructor()
- ->getMock();
- $event->method('getException')->willReturn($exception);
- $event->method('getRequest')->willReturn(new Request());
- $event->expects($this->never())->method('setResponse');
-
- return $event;
+ $listener = new ExceptionListener(
+ true,
+ $this->createMock(AdminContextProviderInterface::class),
+ $this->createMock(Environment::class),
+ );
+
+ $exception = new RuntimeError('Variable "ea" does not exist.');
+ $listener->onKernelException($exceptionEvent = $this->createExceptionEvent($exception));
+
+ $expectedMessage = <<assertSame($expectedMessage, $exceptionEvent->getThrowable()->getMessage());
+ $this->assertNull($exceptionEvent->getResponse());
}
- public function testShouldNotCatchExceptionsWithSameName(): void
+ public function testResponse(): void
{
- $exception = new EntityNotFoundException();
- $event = $this->getEventExceptionThatShouldNotBeCalled($exception);
- $twig = $this->getTwig();
+ $contextProvider = $this->createMock(AdminContextProviderInterface::class);
+ $context = $this->createMock(AdminContextInterface::class);
+ $context->method('getTemplatePath')->willReturn('@EasyAdmin/exception.html.twig');
+ $contextProvider->method('getContext')->willReturn($context);
+ $listener = new ExceptionListener(
+ false,
+ $contextProvider,
+ new Environment(new ArrayLoader(['@EasyAdmin/exception.html.twig' => '{{ exception.publicMessage }}'])),
+ );
- $listener = new ExceptionListener($twig, []);
- $listener->onKernelException($event);
- }
-}
+ $exception = new class(new ExceptionContext('foo')) extends BaseException {};
-class EntityNotFoundException extends \Exception
-{
-}
+ $expectedStatusCode = $exception->getStatusCode();
-class TestKernel implements HttpKernelInterface
-{
- public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $catch = true): Response
+ $listener->onKernelException($exceptionEvent = $this->createExceptionEvent($exception));
+
+ $this->assertSame($expectedStatusCode, $exceptionEvent->getResponse()->getStatusCode());
+ $this->assertSame('foo', $exceptionEvent->getResponse()->getContent());
+ }
+
+ private function createExceptionEvent(\Exception $exception): ExceptionEvent
{
- return new Response('foo');
+ return new ExceptionEvent(
+ $this->createStub(HttpKernelInterface::class),
+ Request::create('/'),
+ HttpKernelInterface::MAIN_REQUEST,
+ $exception,
+ );
}
}