|
2 | 2 |
|
3 | 3 | namespace EasyCorp\Bundle\EasyAdminBundle\Tests\EventListener; |
4 | 4 |
|
| 5 | +use EasyCorp\Bundle\EasyAdminBundle\Context\ExceptionContext; |
| 6 | +use EasyCorp\Bundle\EasyAdminBundle\Contracts\Context\AdminContextInterface; |
| 7 | +use EasyCorp\Bundle\EasyAdminBundle\Contracts\Provider\AdminContextProviderInterface; |
5 | 8 | use EasyCorp\Bundle\EasyAdminBundle\EventListener\ExceptionListener; |
6 | | -use EasyCorp\Bundle\EasyAdminBundle\Exception\EntityNotFoundException as EasyEntityNotFoundException; |
| 9 | +use EasyCorp\Bundle\EasyAdminBundle\Exception\BaseException; |
7 | 10 | use PHPUnit\Framework\TestCase; |
8 | 11 | use Symfony\Component\HttpFoundation\Request; |
9 | | -use Symfony\Component\HttpFoundation\Response; |
| 12 | +use Symfony\Component\HttpKernel\Event\ExceptionEvent; |
10 | 13 | use Symfony\Component\HttpKernel\HttpKernelInterface; |
| 14 | +use Twig\Environment; |
| 15 | +use Twig\Error\RuntimeError; |
| 16 | +use Twig\Loader\ArrayLoader; |
11 | 17 |
|
12 | 18 | class ExceptionListenerTest extends TestCase |
13 | 19 | { |
14 | | - private function getTwig() |
| 20 | + /** |
| 21 | + * @dataProvider unhandledException |
| 22 | + */ |
| 23 | + public function testUnhandledException(bool $kernelDebug, bool $contextIsNull, \Exception $exception): void |
15 | 24 | { |
16 | | - $twig = $this->getMockBuilder('\Twig_Environment')->disableOriginalConstructor()->getMock(); |
17 | | - $twig->method('render')->willReturn('template content'); |
| 25 | + $contextProvider = $this->createMock(AdminContextProviderInterface::class); |
| 26 | + if (!$contextIsNull) { |
| 27 | + $context = $this->createMock(AdminContextInterface::class); |
| 28 | + $context->method('getTemplatePath')->willReturn('foo'); |
| 29 | + $contextProvider->method('getContext')->willReturn($context); |
| 30 | + } |
18 | 31 |
|
19 | | - return $twig; |
20 | | - } |
| 32 | + $listener = new ExceptionListener( |
| 33 | + $kernelDebug, |
| 34 | + $contextProvider, |
| 35 | + $this->createMock(Environment::class), |
| 36 | + ); |
21 | 37 |
|
22 | | - private function getEventExceptionThatShouldBeCalledOnce($exception) |
23 | | - { |
24 | | - $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent') |
25 | | - ->disableOriginalConstructor() |
26 | | - ->getMock(); |
27 | | - $event->method('getException')->willReturn($exception); |
28 | | - $event->method('getRequest')->willReturn(new Request()); |
29 | | - $event->method('getKernel')->willReturn(new TestKernel()); |
30 | | - $event->expects($this->once())->method('setResponse'); |
31 | | - |
32 | | - return $event; |
| 38 | + $expectedMessage = $exception->getMessage(); |
| 39 | + |
| 40 | + $listener->onKernelException($exceptionEvent = $this->createExceptionEvent($exception)); |
| 41 | + |
| 42 | + $this->assertSame($expectedMessage, $exceptionEvent->getThrowable()->getMessage()); |
| 43 | + $this->assertNull($exceptionEvent->getResponse()); |
33 | 44 | } |
34 | 45 |
|
35 | | - public function testCatchBaseExceptions(): void |
| 46 | + public static function unhandledException(): \Generator |
36 | 47 | { |
37 | | - $exception = new EasyEntityNotFoundException([ |
38 | | - 'entity_name' => 'Test', |
39 | | - 'entity_id_name' => 'Test key', |
40 | | - 'entity_id_value' => 2, |
41 | | - ]); |
42 | | - $event = $this->getEventExceptionThatShouldBeCalledOnce($exception); |
43 | | - $twig = $this->getTwig(); |
44 | | - |
45 | | - $listener = new ExceptionListener($twig, []); |
46 | | - $listener->onKernelException($event); |
| 48 | + yield [true, true, new \Exception()]; |
| 49 | + yield [true, false, new \Exception()]; |
| 50 | + yield [false, true, new \Exception()]; |
| 51 | + yield [false, false, new \Exception()]; |
| 52 | + yield [true, true, new RuntimeError('foo')]; |
| 53 | + yield [true, false, new RuntimeError('foo')]; |
| 54 | + yield [false, true, new RuntimeError('foo')]; |
| 55 | + yield [false, false, new RuntimeError('foo')]; |
| 56 | + yield [true, true, new class(new ExceptionContext('foo')) extends BaseException {}]; |
| 57 | + yield [true, false, new class(new ExceptionContext('foo')) extends BaseException {}]; |
| 58 | + yield [false, true, new class(new ExceptionContext('foo')) extends BaseException {}]; |
47 | 59 | } |
48 | 60 |
|
49 | | - private function getEventExceptionThatShouldNotBeCalled($exception) |
| 61 | + public function testAppendMessage(): void |
50 | 62 | { |
51 | | - $event = $this->getMockBuilder('Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent') |
52 | | - ->disableOriginalConstructor() |
53 | | - ->getMock(); |
54 | | - $event->method('getException')->willReturn($exception); |
55 | | - $event->method('getRequest')->willReturn(new Request()); |
56 | | - $event->expects($this->never())->method('setResponse'); |
57 | | - |
58 | | - return $event; |
| 63 | + $listener = new ExceptionListener( |
| 64 | + true, |
| 65 | + $this->createMock(AdminContextProviderInterface::class), |
| 66 | + $this->createMock(Environment::class), |
| 67 | + ); |
| 68 | + |
| 69 | + $exception = new RuntimeError('Variable "ea" does not exist.'); |
| 70 | + $listener->onKernelException($exceptionEvent = $this->createExceptionEvent($exception)); |
| 71 | + |
| 72 | + $expectedMessage = <<<MESSAGE |
| 73 | +Variable "ea" does not exist. |
| 74 | +
|
| 75 | +The "ea" variable stores the admin context (menu items, actions, fields, etc.) and it's created automatically for requests served by EasyAdmin. |
| 76 | +
|
| 77 | +If you are seeing this error, you are trying to use some EasyAdmin features in a request not served by EasyAdmin. For example, some of your custom actions may be trying to render or extend from one of the templates provided EasyAdmin. |
| 78 | +
|
| 79 | +Your request must meet one of these conditions to be served by EasyAdmin (and to have the "ea" variable defined): |
| 80 | +
|
| 81 | +1) It must be run by a controller that implements DashboardControllerInterface. This is done automatically for all actions and CRUD controllers associated to your dashboard. |
| 82 | +
|
| 83 | +2) It must contain an "eaContext" query string parameter that identifies the Dashboard associated to this request (this parameter is automatically added by EasyAdmin when creating menu items that link to custom Symfony routes). |
| 84 | +MESSAGE; |
| 85 | + |
| 86 | + $this->assertSame($expectedMessage, $exceptionEvent->getThrowable()->getMessage()); |
| 87 | + $this->assertNull($exceptionEvent->getResponse()); |
59 | 88 | } |
60 | 89 |
|
61 | | - public function testShouldNotCatchExceptionsWithSameName(): void |
| 90 | + public function testResponse(): void |
62 | 91 | { |
63 | | - $exception = new EntityNotFoundException(); |
64 | | - $event = $this->getEventExceptionThatShouldNotBeCalled($exception); |
65 | | - $twig = $this->getTwig(); |
| 92 | + $contextProvider = $this->createMock(AdminContextProviderInterface::class); |
| 93 | + $context = $this->createMock(AdminContextInterface::class); |
| 94 | + $context->method('getTemplatePath')->willReturn('@EasyAdmin/exception.html.twig'); |
| 95 | + $contextProvider->method('getContext')->willReturn($context); |
| 96 | + $listener = new ExceptionListener( |
| 97 | + false, |
| 98 | + $contextProvider, |
| 99 | + new Environment(new ArrayLoader(['@EasyAdmin/exception.html.twig' => '{{ exception.publicMessage }}'])), |
| 100 | + ); |
66 | 101 |
|
67 | | - $listener = new ExceptionListener($twig, []); |
68 | | - $listener->onKernelException($event); |
69 | | - } |
70 | | -} |
| 102 | + $exception = new class(new ExceptionContext('foo')) extends BaseException {}; |
71 | 103 |
|
72 | | -class EntityNotFoundException extends \Exception |
73 | | -{ |
74 | | -} |
| 104 | + $expectedStatusCode = $exception->getStatusCode(); |
75 | 105 |
|
76 | | -class TestKernel implements HttpKernelInterface |
77 | | -{ |
78 | | - public function handle(Request $request, int $type = self::MAIN_REQUEST, bool $catch = true): Response |
| 106 | + $listener->onKernelException($exceptionEvent = $this->createExceptionEvent($exception)); |
| 107 | + |
| 108 | + $this->assertSame($expectedStatusCode, $exceptionEvent->getResponse()->getStatusCode()); |
| 109 | + $this->assertSame('foo', $exceptionEvent->getResponse()->getContent()); |
| 110 | + } |
| 111 | + |
| 112 | + private function createExceptionEvent(\Exception $exception): ExceptionEvent |
79 | 113 | { |
80 | | - return new Response('foo'); |
| 114 | + return new ExceptionEvent( |
| 115 | + $this->createStub(HttpKernelInterface::class), |
| 116 | + Request::create('/'), |
| 117 | + HttpKernelInterface::MAIN_REQUEST, |
| 118 | + $exception, |
| 119 | + ); |
81 | 120 | } |
82 | 121 | } |
0 commit comments