Completed
Push — master ( 3fd2dd...aba624 )
by Grégoire
04:19
created

tests/Controller/CRUDControllerTest.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Tests\Controller;
15
16
use PHPUnit\Framework\TestCase;
17
use Psr\Log\LoggerInterface;
18
use Sonata\AdminBundle\Admin\AdminInterface;
19
use Sonata\AdminBundle\Admin\FieldDescriptionCollection;
20
use Sonata\AdminBundle\Admin\Pool;
21
use Sonata\AdminBundle\Controller\CRUDController;
22
use Sonata\AdminBundle\Datagrid\DatagridInterface;
23
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
24
use Sonata\AdminBundle\Exception\LockException;
25
use Sonata\AdminBundle\Exception\ModelManagerException;
26
use Sonata\AdminBundle\Export\Exporter as SonataExporter;
27
use Sonata\AdminBundle\Model\AuditManager;
28
use Sonata\AdminBundle\Model\AuditReaderInterface;
29
use Sonata\AdminBundle\Model\ModelManagerInterface;
30
use Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap;
31
use Sonata\AdminBundle\Security\Handler\AclSecurityHandler;
32
use Sonata\AdminBundle\Templating\TemplateRegistryInterface;
33
use Sonata\AdminBundle\Tests\Fixtures\Controller\BatchAdminController;
34
use Sonata\AdminBundle\Tests\Fixtures\Controller\PreCRUDController;
35
use Sonata\AdminBundle\Util\AdminObjectAclData;
36
use Sonata\AdminBundle\Util\AdminObjectAclManipulator;
37
use Sonata\Exporter\Exporter;
38
use Sonata\Exporter\Source\SourceIteratorInterface;
39
use Sonata\Exporter\Writer\JsonWriter;
40
use Symfony\Bridge\Twig\Extension\FormExtension;
41
use Symfony\Bridge\Twig\Form\TwigRenderer;
42
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
43
use Symfony\Bundle\FrameworkBundle\Templating\DelegatingEngine;
44
use Symfony\Component\DependencyInjection\ContainerInterface;
45
use Symfony\Component\Form\Form;
46
use Symfony\Component\Form\FormRenderer;
47
use Symfony\Component\Form\FormView;
48
use Symfony\Component\HttpFoundation\RedirectResponse;
49
use Symfony\Component\HttpFoundation\Request;
50
use Symfony\Component\HttpFoundation\RequestStack;
51
use Symfony\Component\HttpFoundation\Response;
52
use Symfony\Component\HttpFoundation\Session\Session;
53
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
54
use Symfony\Component\HttpFoundation\StreamedResponse;
55
use Symfony\Component\HttpKernel\Exception\HttpException;
56
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
57
use Symfony\Component\HttpKernel\KernelInterface;
58
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
59
use Symfony\Component\Security\Csrf\CsrfToken;
60
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
61
use Symfony\Component\Translation\TranslatorInterface;
62
63
/**
64
 * Test for CRUDController.
65
 *
66
 * @author Andrej Hudec <[email protected]>
67
 *
68
 * @group legacy
69
 */
70
class CRUDControllerTest extends TestCase
71
{
72
    /**
73
     * @var CRUDController
74
     */
75
    private $controller;
76
77
    /**
78
     * @var Request
79
     */
80
    private $request;
81
82
    /**
83
     * @var AdminInterface
84
     */
85
    private $admin;
86
87
    /**
88
     * @var TemplateRegistryInterface
89
     */
90
    private $templateRegistry;
91
92
    /**
93
     * @var Pool
94
     */
95
    private $pool;
96
97
    /**
98
     * @var array
99
     */
100
    private $parameters;
101
102
    /**
103
     * @var Session
104
     */
105
    private $session;
106
107
    /**
108
     * @var AuditManager
109
     */
110
    private $auditManager;
111
112
    /**
113
     * @var ContainerInterface
114
     */
115
    private $container;
116
117
    /**
118
     * @var AdminObjectAclManipulator
119
     */
120
    private $adminObjectAclManipulator;
121
122
    /**
123
     * @var string
124
     */
125
    private $template;
126
127
    /**
128
     * @var array
129
     */
130
    private $protectedTestedMethods;
131
132
    /**
133
     * @var CsrfTokenManagerInterface
134
     */
135
    private $csrfProvider;
136
137
    /**
138
     * @var KernelInterface
139
     */
140
    private $kernel;
141
142
    /**
143
     * @var TranslatorInterface
144
     */
145
    private $translator;
146
147
    /**
148
     * {@inheritdoc}
149
     */
150
    protected function setUp(): void
151
    {
152
        $this->container = $this->createMock(ContainerInterface::class);
153
154
        $this->request = new Request();
155
        $this->pool = new Pool($this->container, 'title', 'logo.png');
156
        $this->pool->setAdminServiceIds(['foo.admin']);
157
        $this->request->attributes->set('_sonata_admin', 'foo.admin');
158
        $this->admin = $this->getMockBuilder(AdminInterface::class)
159
            ->disableOriginalConstructor()
160
            ->getMock();
161
        $this->translator = $this->createMock(TranslatorInterface::class);
162
        $this->parameters = [];
163
        $this->template = '';
164
165
        $this->templateRegistry = $this->prophesize(TemplateRegistryInterface::class);
166
167
        $templating = $this->getMockBuilder(DelegatingEngine::class)
168
            ->setConstructorArgs([$this->container, []])
169
            ->getMock();
170
171
        $templatingRenderReturnCallback = $this->returnCallback(function (
172
            $view,
173
            array $parameters = [],
174
            Response $response = null
175
        ) {
176
            $this->template = $view;
177
178
            if (null === $response) {
179
                $response = new Response();
180
            }
181
182
            $this->parameters = $parameters;
183
184
            return $response;
185
        });
186
187
        // SF < 3.3.10 BC
188
        $templating->expects($this->any())
189
            ->method('renderResponse')
190
            ->will($templatingRenderReturnCallback);
191
192
        $templating->expects($this->any())
193
            ->method('render')
194
            ->will($templatingRenderReturnCallback);
195
196
        $this->session = new Session(new MockArraySessionStorage());
197
198
        $twig = $this->getMockBuilder(\Twig_Environment::class)
199
            ->disableOriginalConstructor()
200
            ->getMock();
201
202
        $twig->expects($this->any())
203
            ->method('getExtension')
204
            ->will($this->returnCallback(function ($name) {
205
                switch ($name) {
206
                    case FormExtension::class:
207
                        return new FormExtension($this->createMock(TwigRenderer::class));
208
                }
209
            }));
210
211
        $twig->expects($this->any())
212
            ->method('getRuntime')
213
            ->will($this->returnCallback(function ($name) {
214
                switch ($name) {
215
                    case TwigRenderer::class:
216
                        return $this->createMock(TwigRenderer::class);
217
                    case FormRenderer::class:
218
                        return $this->createMock(FormRenderer::class);
219
                }
220
            }));
221
222
        // NEXT_MAJOR : require sonata/exporter ^1.7 and remove conditional
223
        if (class_exists(Exporter::class)) {
224
            $exporter = new Exporter([new JsonWriter('/tmp/sonataadmin/export.json')]);
225
        } else {
226
            $exporter = $this->createMock(SonataExporter::class);
227
228
            $exporter->expects($this->any())
229
                ->method('getResponse')
230
                ->will($this->returnValue(new StreamedResponse()));
231
        }
232
233
        $this->auditManager = $this->getMockBuilder(AuditManager::class)
234
            ->disableOriginalConstructor()
235
            ->getMock();
236
237
        $this->adminObjectAclManipulator = $this->getMockBuilder(AdminObjectAclManipulator::class)
238
            ->disableOriginalConstructor()
239
            ->getMock();
240
241
        $this->csrfProvider = $this->getMockBuilder(CsrfTokenManagerInterface::class)
242
            ->getMock();
243
244
        $this->csrfProvider->expects($this->any())
245
            ->method('getToken')
246
            ->will($this->returnCallback(static function ($intention) {
247
                return new CsrfToken($intention, 'csrf-token-123_'.$intention);
248
            }));
249
250
        $this->csrfProvider->expects($this->any())
251
            ->method('isTokenValid')
252
            ->will($this->returnCallback(static function (CsrfToken $token) {
253
                if ($token->getValue() === 'csrf-token-123_'.$token->getId()) {
254
                    return true;
255
                }
256
257
                return false;
258
            }));
259
260
        $this->logger = $this->createMock(LoggerInterface::class);
261
262
        $requestStack = new RequestStack();
263
        $requestStack->push($this->request);
264
265
        $this->kernel = $this->createMock(KernelInterface::class);
266
267
        $this->container->expects($this->any())
268
            ->method('get')
269
            ->will($this->returnCallback(function ($id) use (
270
                $templating,
271
                $twig,
272
                $exporter,
273
                $requestStack
274
            ) {
275
                switch ($id) {
276
                    case 'sonata.admin.pool':
277
                        return $this->pool;
278
                    case 'request_stack':
279
                        return $requestStack;
280
                    case 'foo.admin':
281
                        return $this->admin;
282
                    case 'foo.admin.template_registry':
283
                        return $this->templateRegistry->reveal();
284
                    case 'templating':
285
                        return $templating;
286
                    case 'twig':
287
                        return $twig;
288
                    case 'session':
289
                        return $this->session;
290
                    case 'sonata.admin.exporter':
291
                        return $exporter;
292
                    case 'sonata.admin.audit.manager':
293
                        return $this->auditManager;
294
                    case 'sonata.admin.object.manipulator.acl.admin':
295
                        return $this->adminObjectAclManipulator;
296
                    case 'security.csrf.token_manager':
297
                        return $this->csrfProvider;
298
                    case 'logger':
299
                        return $this->logger;
300
                    case 'kernel':
301
                        return $this->kernel;
302
                    case 'translator':
303
                        return $this->translator;
304
                }
305
            }));
306
307
        $this->container->expects($this->any())
308
            ->method('has')
309
            ->will($this->returnCallback(function ($id) {
310
                if ('security.csrf.token_manager' === $id && null !== $this->getCsrfProvider()) {
311
                    return true;
312
                }
313
314
                if ('logger' === $id) {
315
                    return true;
316
                }
317
318
                if ('session' === $id) {
319
                    return true;
320
                }
321
322
                if ('templating' === $id) {
323
                    return true;
324
                }
325
326
                if ('translator' === $id) {
327
                    return true;
328
                }
329
330
                return false;
331
            }));
332
333
        $this->container->expects($this->any())
334
            ->method('getParameter')
335
            ->will($this->returnCallback(static function ($name) {
336
                switch ($name) {
337
                    case 'security.role_hierarchy.roles':
338
                       return ['ROLE_SUPER_ADMIN' => ['ROLE_USER', 'ROLE_SONATA_ADMIN', 'ROLE_ADMIN']];
339
                }
340
            }));
341
342
        $this->templateRegistry->getTemplate('ajax')->willReturn('@SonataAdmin/ajax_layout.html.twig');
343
        $this->templateRegistry->getTemplate('layout')->willReturn('@SonataAdmin/standard_layout.html.twig');
344
        $this->templateRegistry->getTemplate('show')->willReturn('@SonataAdmin/CRUD/show.html.twig');
345
        $this->templateRegistry->getTemplate('show_compare')->willReturn('@SonataAdmin/CRUD/show_compare.html.twig');
346
        $this->templateRegistry->getTemplate('edit')->willReturn('@SonataAdmin/CRUD/edit.html.twig');
347
        $this->templateRegistry->getTemplate('dashboard')->willReturn('@SonataAdmin/Core/dashboard.html.twig');
348
        $this->templateRegistry->getTemplate('search')->willReturn('@SonataAdmin/Core/search.html.twig');
349
        $this->templateRegistry->getTemplate('list')->willReturn('@SonataAdmin/CRUD/list.html.twig');
350
        $this->templateRegistry->getTemplate('preview')->willReturn('@SonataAdmin/CRUD/preview.html.twig');
351
        $this->templateRegistry->getTemplate('history')->willReturn('@SonataAdmin/CRUD/history.html.twig');
352
        $this->templateRegistry->getTemplate('acl')->willReturn('@SonataAdmin/CRUD/acl.html.twig');
353
        $this->templateRegistry->getTemplate('delete')->willReturn('@SonataAdmin/CRUD/delete.html.twig');
354
        $this->templateRegistry->getTemplate('batch')->willReturn('@SonataAdmin/CRUD/list__batch.html.twig');
355
        $this->templateRegistry->getTemplate('batch_confirmation')->willReturn('@SonataAdmin/CRUD/batch_confirmation.html.twig');
356
357
        // NEXT_MAJOR: Remove this call
358
        $this->admin->method('getTemplate')->willReturnMap([
359
            ['ajax', '@SonataAdmin/ajax_layout.html.twig'],
360
            ['layout', '@SonataAdmin/standard_layout.html.twig'],
361
            ['show', '@SonataAdmin/CRUD/show.html.twig'],
362
            ['show_compare', '@SonataAdmin/CRUD/show_compare.html.twig'],
363
            ['edit', '@SonataAdmin/CRUD/edit.html.twig'],
364
            ['dashboard', '@SonataAdmin/Core/dashboard.html.twig'],
365
            ['search', '@SonataAdmin/Core/search.html.twig'],
366
            ['list', '@SonataAdmin/CRUD/list.html.twig'],
367
            ['preview', '@SonataAdmin/CRUD/preview.html.twig'],
368
            ['history', '@SonataAdmin/CRUD/history.html.twig'],
369
            ['acl', '@SonataAdmin/CRUD/acl.html.twig'],
370
            ['delete', '@SonataAdmin/CRUD/delete.html.twig'],
371
            ['batch', '@SonataAdmin/CRUD/list__batch.html.twig'],
372
            ['batch_confirmation', '@SonataAdmin/CRUD/batch_confirmation.html.twig'],
373
        ]);
374
375
        $this->admin->expects($this->any())
376
            ->method('getIdParameter')
377
            ->will($this->returnValue('id'));
378
379
        $this->admin->expects($this->any())
380
            ->method('getAccessMapping')
381
            ->will($this->returnValue([]));
382
383
        $this->admin->expects($this->any())
384
            ->method('generateUrl')
385
            ->will(
386
                $this->returnCallback(
387
                    static function ($name, array $parameters = [], $absolute = false) {
388
                        $result = $name;
389
                        if (!empty($parameters)) {
390
                            $result .= '?'.http_build_query($parameters);
391
                        }
392
393
                        return $result;
394
                    }
395
                )
396
            );
397
398
        $this->admin->expects($this->any())
399
            ->method('generateObjectUrl')
400
            ->will(
401
                $this->returnCallback(
402
                    static function ($name, $object, array $parameters = [], $absolute = false) {
403
                        $result = \get_class($object).'_'.$name;
404
                        if (!empty($parameters)) {
405
                            $result .= '?'.http_build_query($parameters);
406
                        }
407
408
                        return $result;
409
                    }
410
                )
411
            );
412
413
        $this->admin->expects($this->any())
414
            ->method('getCode')
415
            ->willReturn('foo.admin');
416
417
        $this->controller = new CRUDController();
418
        $this->controller->setContainer($this->container);
419
420
        // Make some methods public to test them
421
        $testedMethods = [
422
            'renderJson',
423
            'isXmlHttpRequest',
424
            'configure',
425
            'getBaseTemplate',
426
            'redirectTo',
427
            'addFlash',
428
        ];
429
        foreach ($testedMethods as $testedMethod) {
430
            // NEXT_MAJOR: Remove this check and only use CRUDController
431
            if (method_exists(CRUDController::class, $testedMethod)) {
432
                $method = new \ReflectionMethod(CRUDController::class, $testedMethod);
433
            } else {
434
                $method = new \ReflectionMethod(Controller::class, $testedMethod);
435
            }
436
437
            $method->setAccessible(true);
438
            $this->protectedTestedMethods[$testedMethod] = $method;
439
        }
440
    }
441
442
    public function testRenderJson1(): void
443
    {
444
        $data = ['example' => '123', 'foo' => 'bar'];
445
446
        $this->request->headers->set('Content-Type', 'application/x-www-form-urlencoded');
447
        $response = $this->protectedTestedMethods['renderJson']->invoke($this->controller, $data, 200, [], $this->request);
448
449
        $this->assertSame($response->headers->get('Content-Type'), 'application/json');
450
        $this->assertSame(json_encode($data), $response->getContent());
451
    }
452
453
    public function testRenderJson2(): void
454
    {
455
        $data = ['example' => '123', 'foo' => 'bar'];
456
457
        $this->request->headers->set('Content-Type', 'multipart/form-data');
458
        $response = $this->protectedTestedMethods['renderJson']->invoke($this->controller, $data, 200, [], $this->request);
459
460
        $this->assertSame($response->headers->get('Content-Type'), 'application/json');
461
        $this->assertSame(json_encode($data), $response->getContent());
462
    }
463
464
    public function testRenderJsonAjax(): void
465
    {
466
        $data = ['example' => '123', 'foo' => 'bar'];
467
468
        $this->request->attributes->set('_xml_http_request', true);
469
        $this->request->headers->set('Content-Type', 'multipart/form-data');
470
        $response = $this->protectedTestedMethods['renderJson']->invoke($this->controller, $data, 200, [], $this->request);
471
472
        $this->assertSame($response->headers->get('Content-Type'), 'application/json');
473
        $this->assertSame(json_encode($data), $response->getContent());
474
    }
475
476
    public function testIsXmlHttpRequest(): void
477
    {
478
        $this->assertFalse($this->protectedTestedMethods['isXmlHttpRequest']->invoke($this->controller, $this->request));
479
480
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
481
482
        $this->assertTrue($this->protectedTestedMethods['isXmlHttpRequest']->invoke($this->controller, $this->request));
483
484
        $this->request->headers->remove('X-Requested-With');
485
        $this->assertFalse($this->protectedTestedMethods['isXmlHttpRequest']->invoke($this->controller, $this->request));
486
487
        $this->request->attributes->set('_xml_http_request', true);
488
        $this->assertTrue($this->protectedTestedMethods['isXmlHttpRequest']->invoke($this->controller, $this->request));
489
    }
490
491
    public function testConfigure(): void
492
    {
493
        $uniqueId = '';
494
495
        $this->admin->expects($this->once())
496
            ->method('setUniqid')
497
            ->will($this->returnCallback(static function ($uniqid) use (&$uniqueId): void {
498
                $uniqueId = $uniqid;
499
            }));
500
501
        $this->request->query->set('uniqid', 123456);
502
        $this->protectedTestedMethods['configure']->invoke($this->controller);
503
504
        $this->assertSame(123456, $uniqueId);
505
        $this->assertAttributeSame($this->admin, 'admin', $this->controller);
506
    }
507
508
    public function testConfigureChild(): void
509
    {
510
        $uniqueId = '';
511
512
        $this->admin->expects($this->once())
513
            ->method('setUniqid')
514
            ->will($this->returnCallback(static function ($uniqid) use (&$uniqueId): void {
515
                $uniqueId = $uniqid;
516
            }));
517
518
        $this->admin->expects($this->once())
519
            ->method('isChild')
520
            ->will($this->returnValue(true));
521
522
        $adminParent = $this->getMockBuilder(AdminInterface::class)
523
            ->disableOriginalConstructor()
524
            ->getMock();
525
        $this->admin->expects($this->once())
526
            ->method('getParent')
527
            ->will($this->returnValue($adminParent));
528
529
        $this->request->query->set('uniqid', 123456);
530
        $this->protectedTestedMethods['configure']->invoke($this->controller);
531
532
        $this->assertSame(123456, $uniqueId);
533
        $this->assertAttributeInstanceOf(\get_class($adminParent), 'admin', $this->controller);
534
    }
535
536
    public function testConfigureWithException(): void
537
    {
538
        $this->expectException(
539
            \RuntimeException::class,
540
            'There is no `_sonata_admin` defined for the controller `Sonata\AdminBundle\Controller\CRUDController`'
541
        );
542
543
        $this->request->attributes->remove('_sonata_admin');
544
        $this->protectedTestedMethods['configure']->invoke($this->controller);
545
    }
546
547
    public function testConfigureWithException2(): void
548
    {
549
        $this->expectException(
550
            \InvalidArgumentException::class,
551
            'Found service "nonexistent.admin" is not a valid admin service'
552
        );
553
554
        $this->pool->setAdminServiceIds(['nonexistent.admin']);
555
        $this->request->attributes->set('_sonata_admin', 'nonexistent.admin');
556
        $this->protectedTestedMethods['configure']->invoke($this->controller);
557
    }
558
559
    public function testGetBaseTemplate(): void
560
    {
561
        $this->assertSame(
562
            '@SonataAdmin/standard_layout.html.twig',
563
            $this->protectedTestedMethods['getBaseTemplate']->invoke($this->controller, $this->request)
564
        );
565
566
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
567
        $this->assertSame(
568
            '@SonataAdmin/ajax_layout.html.twig',
569
            $this->protectedTestedMethods['getBaseTemplate']->invoke($this->controller, $this->request)
570
        );
571
572
        $this->request->headers->remove('X-Requested-With');
573
        $this->assertSame(
574
            '@SonataAdmin/standard_layout.html.twig',
575
            $this->protectedTestedMethods['getBaseTemplate']->invoke($this->controller, $this->request)
576
        );
577
578
        $this->request->attributes->set('_xml_http_request', true);
579
        $this->assertSame(
580
            '@SonataAdmin/ajax_layout.html.twig',
581
            $this->protectedTestedMethods['getBaseTemplate']->invoke($this->controller, $this->request)
582
        );
583
    }
584
585
    public function testRender(): void
586
    {
587
        $this->parameters = [];
588
        $this->assertInstanceOf(
589
            Response::class,
590
            $this->controller->renderWithExtraParams('@FooAdmin/foo.html.twig', [], null)
591
        );
592
        $this->assertSame($this->admin, $this->parameters['admin']);
593
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
594
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
595
        $this->assertSame('@FooAdmin/foo.html.twig', $this->template);
596
    }
597
598
    public function testRenderWithResponse(): void
599
    {
600
        $this->parameters = [];
601
        $response = $response = new Response();
602
        $response->headers->set('X-foo', 'bar');
603
        $responseResult = $this->controller->renderWithExtraParams('@FooAdmin/foo.html.twig', [], $response);
604
605
        $this->assertSame($response, $responseResult);
606
        $this->assertSame('bar', $responseResult->headers->get('X-foo'));
607
        $this->assertSame($this->admin, $this->parameters['admin']);
608
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
609
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
610
        $this->assertSame('@FooAdmin/foo.html.twig', $this->template);
611
    }
612
613
    public function testRenderCustomParams(): void
614
    {
615
        $this->parameters = [];
616
        $this->assertInstanceOf(
617
            Response::class,
618
            $this->controller->renderWithExtraParams(
619
                '@FooAdmin/foo.html.twig',
620
                ['foo' => 'bar'],
621
                null
622
            )
623
        );
624
        $this->assertSame($this->admin, $this->parameters['admin']);
625
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
626
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
627
        $this->assertSame('bar', $this->parameters['foo']);
628
        $this->assertSame('@FooAdmin/foo.html.twig', $this->template);
629
    }
630
631
    public function testRenderAjax(): void
632
    {
633
        $this->parameters = [];
634
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
635
        $this->assertInstanceOf(
636
            Response::class,
637
            $this->controller->renderWithExtraParams(
638
                '@FooAdmin/foo.html.twig',
639
                ['foo' => 'bar'],
640
                null
641
            )
642
        );
643
        $this->assertSame($this->admin, $this->parameters['admin']);
644
        $this->assertSame('@SonataAdmin/ajax_layout.html.twig', $this->parameters['base_template']);
645
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
646
        $this->assertSame('bar', $this->parameters['foo']);
647
        $this->assertSame('@FooAdmin/foo.html.twig', $this->template);
648
    }
649
650
    public function testListActionAccessDenied(): void
651
    {
652
        $this->expectException(AccessDeniedException::class);
653
654
        $this->admin->expects($this->once())
655
            ->method('checkAccess')
656
            ->with($this->equalTo('list'))
657
            ->will($this->throwException(new AccessDeniedException()));
658
659
        $this->controller->listAction($this->request);
660
    }
661
662
    public function testPreList(): void
663
    {
664
        $this->admin->expects($this->any())
665
            ->method('hasRoute')
666
            ->with($this->equalTo('list'))
667
            ->will($this->returnValue(true));
668
669
        $this->admin->expects($this->once())
670
            ->method('checkAccess')
671
            ->with($this->equalTo('list'))
672
            ->will($this->returnValue(true));
673
674
        $controller = new PreCRUDController();
675
        $controller->setContainer($this->container);
676
677
        $response = $controller->listAction($this->request);
678
        $this->assertInstanceOf(Response::class, $response);
679
        $this->assertSame('preList called', $response->getContent());
680
    }
681
682
    public function testListAction(): void
683
    {
684
        $datagrid = $this->createMock(DatagridInterface::class);
685
686
        $this->admin->expects($this->any())
687
            ->method('hasRoute')
688
            ->with($this->equalTo('list'))
689
            ->will($this->returnValue(true));
690
691
        $this->admin->expects($this->once())
692
            ->method('checkAccess')
693
            ->with($this->equalTo('list'))
694
            ->will($this->returnValue(true));
695
696
        $form = $this->getMockBuilder(Form::class)
697
            ->disableOriginalConstructor()
698
            ->getMock();
699
700
        $form->expects($this->once())
701
            ->method('createView')
702
            ->will($this->returnValue($this->createMock(FormView::class)));
703
704
        $this->admin->expects($this->once())
705
            ->method('getDatagrid')
706
            ->will($this->returnValue($datagrid));
707
708
        $datagrid->expects($this->once())
709
            ->method('getForm')
710
            ->will($this->returnValue($form));
711
712
        $this->parameters = [];
713
        $this->assertInstanceOf(Response::class, $this->controller->listAction($this->request));
714
715
        $this->assertSame($this->admin, $this->parameters['admin']);
716
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
717
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
718
719
        $this->assertSame('list', $this->parameters['action']);
720
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
721
        $this->assertInstanceOf(DatagridInterface::class, $this->parameters['datagrid']);
722
        $this->assertSame('csrf-token-123_sonata.batch', $this->parameters['csrf_token']);
723
        $this->assertSame([], $this->session->getFlashBag()->all());
724
        $this->assertSame('@SonataAdmin/CRUD/list.html.twig', $this->template);
725
    }
726
727
    public function testBatchActionDeleteAccessDenied(): void
728
    {
729
        $this->expectException(AccessDeniedException::class);
730
731
        $this->admin->expects($this->once())
732
            ->method('checkAccess')
733
            ->with($this->equalTo('batchDelete'))
734
            ->will($this->throwException(new AccessDeniedException()));
735
736
        $this->controller->batchActionDelete($this->createMock(ProxyQueryInterface::class));
737
    }
738
739
    public function testBatchActionDelete(): void
740
    {
741
        $modelManager = $this->createMock(ModelManagerInterface::class);
742
743
        $this->admin->expects($this->once())
744
            ->method('checkAccess')
745
            ->with($this->equalTo('batchDelete'))
746
            ->will($this->returnValue(true));
747
748
        $this->admin->expects($this->once())
749
            ->method('getModelManager')
750
            ->will($this->returnValue($modelManager));
751
752
        $this->admin->expects($this->once())
753
            ->method('getFilterParameters')
754
            ->will($this->returnValue(['foo' => 'bar']));
755
756
        $this->expectTranslate('flash_batch_delete_success', [], 'SonataAdminBundle');
757
758
        $result = $this->controller->batchActionDelete($this->createMock(ProxyQueryInterface::class));
759
760
        $this->assertInstanceOf(RedirectResponse::class, $result);
761
        $this->assertSame(['flash_batch_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
762
        $this->assertSame('list?filter%5Bfoo%5D=bar', $result->getTargetUrl());
763
    }
764
765
    public function testBatchActionDeleteWithModelManagerException(): void
766
    {
767
        $modelManager = $this->createMock(ModelManagerInterface::class);
768
        $this->assertLoggerLogsModelManagerException($modelManager, 'batchDelete');
769
770
        $this->admin->expects($this->once())
771
            ->method('getModelManager')
772
            ->will($this->returnValue($modelManager));
773
774
        $this->admin->expects($this->once())
775
            ->method('getFilterParameters')
776
            ->will($this->returnValue(['foo' => 'bar']));
777
778
        $this->expectTranslate('flash_batch_delete_error', [], 'SonataAdminBundle');
779
780
        $result = $this->controller->batchActionDelete($this->createMock(ProxyQueryInterface::class));
781
782
        $this->assertInstanceOf(RedirectResponse::class, $result);
783
        $this->assertSame(['flash_batch_delete_error'], $this->session->getFlashBag()->get('sonata_flash_error'));
784
        $this->assertSame('list?filter%5Bfoo%5D=bar', $result->getTargetUrl());
785
    }
786
787
    public function testBatchActionDeleteWithModelManagerExceptionInDebugMode(): void
788
    {
789
        $modelManager = $this->createMock(ModelManagerInterface::class);
790
        $this->expectException(ModelManagerException::class);
791
792
        $modelManager->expects($this->once())
793
            ->method('batchDelete')
794
            ->will($this->returnCallback(static function (): void {
795
                throw new ModelManagerException();
796
            }));
797
798
        $this->admin->expects($this->once())
799
            ->method('getModelManager')
800
            ->will($this->returnValue($modelManager));
801
802
        $this->kernel->expects($this->once())
803
            ->method('isDebug')
804
            ->will($this->returnValue(true));
805
806
        $this->controller->batchActionDelete($this->createMock(ProxyQueryInterface::class));
807
    }
808
809
    public function testShowActionNotFoundException(): void
810
    {
811
        $this->expectException(NotFoundHttpException::class);
812
813
        $this->admin->expects($this->once())
814
            ->method('getObject')
815
            ->will($this->returnValue(false));
816
817
        $this->controller->showAction(null, $this->request);
818
    }
819
820
    public function testShowActionAccessDenied(): void
821
    {
822
        $this->expectException(AccessDeniedException::class);
823
824
        $this->admin->expects($this->once())
825
            ->method('getObject')
826
            ->will($this->returnValue(new \stdClass()));
827
828
        $this->admin->expects($this->once())
829
            ->method('checkAccess')
830
            ->with($this->equalTo('show'))
831
            ->will($this->throwException(new AccessDeniedException()));
832
833
        $this->controller->showAction(null, $this->request);
834
    }
835
836
    /**
837
     * @group legacy
838
     * @expectedDeprecation Calling this method without implementing "configureShowFields" is not supported since 3.40.0 and will no longer be possible in 4.0
839
     */
840
    public function testShowActionDeprecation(): void
841
    {
842
        $object = new \stdClass();
843
844
        $this->admin->expects($this->once())
845
            ->method('getObject')
846
            ->will($this->returnValue($object));
847
848
        $this->admin->expects($this->once())
849
            ->method('checkAccess')
850
            ->with($this->equalTo('show'))
851
            ->will($this->returnValue(true));
852
853
        $show = $this->createMock(FieldDescriptionCollection::class);
854
855
        $this->admin->expects($this->once())
856
            ->method('getShow')
857
            ->will($this->returnValue($show));
858
859
        $show->expects($this->once())
860
            ->method('getElements')
861
            ->willReturn([]);
862
863
        $show->expects($this->once())
864
            ->method('count')
865
            ->willReturn(0);
866
867
        $this->controller->showAction(null, $this->request);
0 ignored issues
show
The call to CRUDController::showAction() has too many arguments starting with $this->request.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
868
    }
869
870
    public function testPreShow(): void
871
    {
872
        $object = new \stdClass();
873
        $object->foo = 123456;
874
875
        $this->admin->expects($this->once())
876
            ->method('getObject')
877
            ->will($this->returnValue($object));
878
879
        $this->admin->expects($this->once())
880
            ->method('checkAccess')
881
            ->with($this->equalTo('show'))
882
            ->will($this->returnValue(true));
883
884
        $controller = new PreCRUDController();
885
        $controller->setContainer($this->container);
886
887
        $response = $controller->showAction(null, $this->request);
888
        $this->assertInstanceOf(Response::class, $response);
889
        $this->assertSame('preShow called: 123456', $response->getContent());
890
    }
891
892
    public function testShowAction(): void
893
    {
894
        $object = new \stdClass();
895
896
        $this->admin->expects($this->once())
897
            ->method('getObject')
898
            ->will($this->returnValue($object));
899
900
        $this->admin->expects($this->once())
901
            ->method('checkAccess')
902
            ->with($this->equalTo('show'))
903
            ->will($this->returnValue(true));
904
905
        $show = $this->createMock(FieldDescriptionCollection::class);
906
907
        $this->admin->expects($this->once())
908
            ->method('getShow')
909
            ->will($this->returnValue($show));
910
911
        $show->expects($this->once())
912
            ->method('getElements')
913
            ->willReturn(['field' => 'fielddata']);
914
915
        $this->assertInstanceOf(Response::class, $this->controller->showAction(null, $this->request));
916
917
        $this->assertSame($this->admin, $this->parameters['admin']);
918
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
919
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
920
921
        $this->assertSame('show', $this->parameters['action']);
922
        $this->assertInstanceOf(FieldDescriptionCollection::class, $this->parameters['elements']);
923
        $this->assertSame($object, $this->parameters['object']);
924
925
        $this->assertSame([], $this->session->getFlashBag()->all());
926
        $this->assertSame('@SonataAdmin/CRUD/show.html.twig', $this->template);
927
    }
928
929
    /**
930
     * @dataProvider getRedirectToTests
931
     */
932
    public function testRedirectTo($expected, $route, $queryParams, $requestParams, $hasActiveSubclass): void
933
    {
934
        $this->admin->expects($this->any())
935
            ->method('hasActiveSubclass')
936
            ->will($this->returnValue($hasActiveSubclass));
937
938
        $object = new \stdClass();
939
940
        foreach ($queryParams as $key => $value) {
941
            $this->request->query->set($key, $value);
942
        }
943
944
        foreach ($requestParams as $key => $value) {
945
            $this->request->request->set($key, $value);
946
        }
947
948
        $this->admin->expects($this->any())
949
            ->method('hasRoute')
950
            ->with($this->equalTo($route))
951
            ->will($this->returnValue(true));
952
953
        $this->admin->expects($this->any())
954
            ->method('hasAccess')
955
            ->with($this->equalTo($route))
956
            ->will($this->returnValue(true));
957
958
        $response = $this->protectedTestedMethods['redirectTo']->invoke($this->controller, $object, $this->request);
959
        $this->assertInstanceOf(RedirectResponse::class, $response);
960
        $this->assertSame($expected, $response->getTargetUrl());
961
    }
962
963
    public function testRedirectToWithObject(): void
964
    {
965
        $this->admin->expects($this->any())
966
            ->method('hasActiveSubclass')
967
            ->will($this->returnValue(false));
968
969
        $object = new \stdClass();
970
971
        $this->admin->expects($this->at(0))
972
            ->method('hasRoute')
973
            ->with($this->equalTo('edit'))
974
            ->will($this->returnValue(true));
975
976
        $this->admin->expects($this->any())
977
            ->method('hasAccess')
978
            ->with($this->equalTo('edit'), $object)
979
            ->will($this->returnValue(false));
980
981
        $response = $this->protectedTestedMethods['redirectTo']->invoke($this->controller, $object, $this->request);
982
        $this->assertInstanceOf(RedirectResponse::class, $response);
983
        $this->assertSame('list', $response->getTargetUrl());
984
    }
985
986
    public function getRedirectToTests()
987
    {
988
        return [
989
            ['stdClass_edit', 'edit', [], [], false],
990
            ['list', 'list', ['btn_update_and_list' => true], [], false],
991
            ['list', 'list', ['btn_create_and_list' => true], [], false],
992
            ['create', 'create', ['btn_create_and_create' => true], [], false],
993
            ['create?subclass=foo', 'create', ['btn_create_and_create' => true, 'subclass' => 'foo'], [], true],
994
            ['stdClass_edit?_tab=first_tab', 'edit', ['btn_update_and_edit' => true], ['_tab' => 'first_tab'], false],
995
        ];
996
    }
997
998
    public function testDeleteActionNotFoundException(): void
999
    {
1000
        $this->expectException(NotFoundHttpException::class);
1001
1002
        $this->admin->expects($this->once())
1003
            ->method('getObject')
1004
            ->will($this->returnValue(false));
1005
1006
        $this->controller->deleteAction(1, $this->request);
1007
    }
1008
1009
    public function testDeleteActionAccessDenied(): void
1010
    {
1011
        $this->expectException(AccessDeniedException::class);
1012
1013
        $this->admin->expects($this->once())
1014
            ->method('getObject')
1015
            ->will($this->returnValue(new \stdClass()));
1016
1017
        $this->admin->expects($this->once())
1018
            ->method('checkAccess')
1019
            ->with($this->equalTo('delete'))
1020
            ->will($this->throwException(new AccessDeniedException()));
1021
1022
        $this->controller->deleteAction(1, $this->request);
1023
    }
1024
1025
    public function testPreDelete(): void
1026
    {
1027
        $object = new \stdClass();
1028
        $object->foo = 123456;
1029
1030
        $this->admin->expects($this->once())
1031
            ->method('getObject')
1032
            ->will($this->returnValue($object));
1033
1034
        $this->admin->expects($this->once())
1035
            ->method('checkAccess')
1036
            ->with($this->equalTo('delete'))
1037
            ->will($this->returnValue(true));
1038
1039
        $controller = new PreCRUDController();
1040
        $controller->setContainer($this->container);
1041
1042
        $response = $controller->deleteAction(null, $this->request);
1043
        $this->assertInstanceOf(Response::class, $response);
1044
        $this->assertSame('preDelete called: 123456', $response->getContent());
1045
    }
1046
1047
    public function testDeleteAction(): void
1048
    {
1049
        $object = new \stdClass();
1050
1051
        $this->admin->expects($this->once())
1052
            ->method('getObject')
1053
            ->will($this->returnValue($object));
1054
1055
        $this->admin->expects($this->once())
1056
            ->method('checkAccess')
1057
            ->with($this->equalTo('delete'))
1058
            ->will($this->returnValue(true));
1059
1060
        $this->assertInstanceOf(Response::class, $this->controller->deleteAction(1, $this->request));
1061
1062
        $this->assertSame($this->admin, $this->parameters['admin']);
1063
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1064
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1065
1066
        $this->assertSame('delete', $this->parameters['action']);
1067
        $this->assertSame($object, $this->parameters['object']);
1068
        $this->assertSame('csrf-token-123_sonata.delete', $this->parameters['csrf_token']);
1069
1070
        $this->assertSame([], $this->session->getFlashBag()->all());
1071
        $this->assertSame('@SonataAdmin/CRUD/delete.html.twig', $this->template);
1072
    }
1073
1074
    public function testDeleteActionNoCsrfToken(): void
1075
    {
1076
        $this->csrfProvider = null;
1077
1078
        $object = new \stdClass();
1079
1080
        $this->admin->expects($this->once())
1081
            ->method('getObject')
1082
            ->will($this->returnValue($object));
1083
1084
        $this->admin->expects($this->once())
1085
            ->method('checkAccess')
1086
            ->with($this->equalTo('delete'))
1087
            ->will($this->returnValue(true));
1088
1089
        $this->assertInstanceOf(Response::class, $this->controller->deleteAction(1, $this->request));
1090
1091
        $this->assertSame($this->admin, $this->parameters['admin']);
1092
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1093
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1094
1095
        $this->assertSame('delete', $this->parameters['action']);
1096
        $this->assertSame($object, $this->parameters['object']);
1097
        $this->assertFalse($this->parameters['csrf_token']);
1098
1099
        $this->assertSame([], $this->session->getFlashBag()->all());
1100
        $this->assertSame('@SonataAdmin/CRUD/delete.html.twig', $this->template);
1101
    }
1102
1103
    public function testDeleteActionAjaxSuccess1(): void
1104
    {
1105
        $object = new \stdClass();
1106
1107
        $this->admin->expects($this->once())
1108
            ->method('getObject')
1109
            ->will($this->returnValue($object));
1110
1111
        $this->admin->expects($this->once())
1112
            ->method('checkAccess')
1113
            ->with($this->equalTo('delete'))
1114
            ->will($this->returnValue(true));
1115
1116
        $this->request->setMethod('DELETE');
1117
1118
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1119
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1120
1121
        $response = $this->controller->deleteAction(1, $this->request);
1122
1123
        $this->assertInstanceOf(Response::class, $response);
1124
        $this->assertSame(json_encode(['result' => 'ok']), $response->getContent());
1125
        $this->assertSame([], $this->session->getFlashBag()->all());
1126
    }
1127
1128
    public function testDeleteActionAjaxSuccess2(): void
1129
    {
1130
        $object = new \stdClass();
1131
1132
        $this->admin->expects($this->once())
1133
            ->method('getObject')
1134
            ->will($this->returnValue($object));
1135
1136
        $this->admin->expects($this->once())
1137
            ->method('checkAccess')
1138
            ->with($this->equalTo('delete'))
1139
            ->will($this->returnValue(true));
1140
1141
        $this->request->setMethod('POST');
1142
        $this->request->request->set('_method', 'DELETE');
1143
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1144
1145
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1146
1147
        $response = $this->controller->deleteAction(1, $this->request);
1148
1149
        $this->assertInstanceOf(Response::class, $response);
1150
        $this->assertSame(json_encode(['result' => 'ok']), $response->getContent());
1151
        $this->assertSame([], $this->session->getFlashBag()->all());
1152
    }
1153
1154
    public function testDeleteActionAjaxError(): void
1155
    {
1156
        $object = new \stdClass();
1157
1158
        $this->admin->expects($this->once())
1159
            ->method('getObject')
1160
            ->will($this->returnValue($object));
1161
1162
        $this->admin->expects($this->once())
1163
            ->method('checkAccess')
1164
            ->with($this->equalTo('delete'))
1165
            ->will($this->returnValue(true));
1166
1167
        $this->admin->expects($this->any())
1168
            ->method('getClass')
1169
            ->will($this->returnValue('stdClass'));
1170
1171
        $this->assertLoggerLogsModelManagerException($this->admin, 'delete');
1172
1173
        $this->request->setMethod('DELETE');
1174
1175
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1176
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1177
1178
        $response = $this->controller->deleteAction(1, $this->request);
1179
1180
        $this->assertInstanceOf(Response::class, $response);
1181
        $this->assertSame(json_encode(['result' => 'error']), $response->getContent());
1182
        $this->assertSame([], $this->session->getFlashBag()->all());
1183
    }
1184
1185
    public function testDeleteActionWithModelManagerExceptionInDebugMode(): void
1186
    {
1187
        $this->expectException(ModelManagerException::class);
1188
1189
        $object = new \stdClass();
1190
1191
        $this->admin->expects($this->once())
1192
            ->method('getObject')
1193
            ->will($this->returnValue($object));
1194
1195
        $this->admin->expects($this->once())
1196
            ->method('checkAccess')
1197
            ->with($this->equalTo('delete'))
1198
            ->will($this->returnValue(true));
1199
1200
        $this->admin->expects($this->once())
1201
            ->method('delete')
1202
            ->will($this->returnCallback(static function (): void {
1203
                throw new ModelManagerException();
1204
            }));
1205
1206
        $this->kernel->expects($this->once())
1207
            ->method('isDebug')
1208
            ->will($this->returnValue(true));
1209
1210
        $this->request->setMethod('DELETE');
1211
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1212
1213
        $this->controller->deleteAction(1, $this->request);
1214
    }
1215
1216
    /**
1217
     * @dataProvider getToStringValues
1218
     */
1219
    public function testDeleteActionSuccess1($expectedToStringValue, $toStringValue): void
1220
    {
1221
        $object = new \stdClass();
1222
1223
        $this->admin->expects($this->once())
1224
            ->method('getObject')
1225
            ->will($this->returnValue($object));
1226
1227
        $this->admin->expects($this->once())
1228
            ->method('toString')
1229
            ->with($this->equalTo($object))
1230
            ->will($this->returnValue($toStringValue));
1231
1232
        $this->expectTranslate('flash_delete_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1233
1234
        $this->admin->expects($this->once())
1235
            ->method('checkAccess')
1236
            ->with($this->equalTo('delete'))
1237
            ->will($this->returnValue(true));
1238
1239
        $this->request->setMethod('DELETE');
1240
1241
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1242
1243
        $response = $this->controller->deleteAction(1, $this->request);
1244
1245
        $this->assertInstanceOf(RedirectResponse::class, $response);
1246
        $this->assertSame(['flash_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
1247
        $this->assertSame('list', $response->getTargetUrl());
1248
    }
1249
1250
    /**
1251
     * @dataProvider getToStringValues
1252
     */
1253
    public function testDeleteActionSuccess2($expectedToStringValue, $toStringValue): void
1254
    {
1255
        $object = new \stdClass();
1256
1257
        $this->admin->expects($this->once())
1258
            ->method('getObject')
1259
            ->will($this->returnValue($object));
1260
1261
        $this->admin->expects($this->once())
1262
            ->method('checkAccess')
1263
            ->with($this->equalTo('delete'))
1264
            ->will($this->returnValue(true));
1265
1266
        $this->admin->expects($this->once())
1267
            ->method('toString')
1268
            ->with($this->equalTo($object))
1269
            ->will($this->returnValue($toStringValue));
1270
1271
        $this->expectTranslate('flash_delete_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1272
1273
        $this->request->setMethod('POST');
1274
        $this->request->request->set('_method', 'DELETE');
1275
1276
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1277
1278
        $response = $this->controller->deleteAction(1, $this->request);
1279
1280
        $this->assertInstanceOf(RedirectResponse::class, $response);
1281
        $this->assertSame(['flash_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
1282
        $this->assertSame('list', $response->getTargetUrl());
1283
    }
1284
1285
    /**
1286
     * @dataProvider getToStringValues
1287
     */
1288
    public function testDeleteActionSuccessNoCsrfTokenProvider($expectedToStringValue, $toStringValue): void
1289
    {
1290
        $this->csrfProvider = null;
1291
1292
        $object = new \stdClass();
1293
1294
        $this->admin->expects($this->once())
1295
            ->method('getObject')
1296
            ->will($this->returnValue($object));
1297
1298
        $this->admin->expects($this->once())
1299
            ->method('checkAccess')
1300
            ->with($this->equalTo('delete'))
1301
            ->will($this->returnValue(true));
1302
1303
        $this->admin->expects($this->once())
1304
            ->method('toString')
1305
            ->with($this->equalTo($object))
1306
            ->will($this->returnValue($toStringValue));
1307
1308
        $this->expectTranslate('flash_delete_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1309
1310
        $this->request->setMethod('POST');
1311
        $this->request->request->set('_method', 'DELETE');
1312
1313
        $response = $this->controller->deleteAction(1, $this->request);
1314
1315
        $this->assertInstanceOf(RedirectResponse::class, $response);
1316
        $this->assertSame(['flash_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
1317
        $this->assertSame('list', $response->getTargetUrl());
1318
    }
1319
1320
    public function testDeleteActionWrongRequestMethod(): void
1321
    {
1322
        $object = new \stdClass();
1323
1324
        $this->admin->expects($this->once())
1325
            ->method('getObject')
1326
            ->will($this->returnValue($object));
1327
1328
        $this->admin->expects($this->once())
1329
            ->method('checkAccess')
1330
            ->with($this->equalTo('delete'))
1331
            ->will($this->returnValue(true));
1332
1333
        //without POST request parameter "_method" should not be used as real REST method
1334
        $this->request->query->set('_method', 'DELETE');
1335
1336
        $this->assertInstanceOf(Response::class, $this->controller->deleteAction(1, $this->request));
1337
1338
        $this->assertSame($this->admin, $this->parameters['admin']);
1339
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1340
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1341
1342
        $this->assertSame('delete', $this->parameters['action']);
1343
        $this->assertSame($object, $this->parameters['object']);
1344
        $this->assertSame('csrf-token-123_sonata.delete', $this->parameters['csrf_token']);
1345
1346
        $this->assertSame([], $this->session->getFlashBag()->all());
1347
        $this->assertSame('@SonataAdmin/CRUD/delete.html.twig', $this->template);
1348
    }
1349
1350
    /**
1351
     * @dataProvider getToStringValues
1352
     */
1353
    public function testDeleteActionError($expectedToStringValue, $toStringValue): void
1354
    {
1355
        $object = new \stdClass();
1356
1357
        $this->admin->expects($this->once())
1358
            ->method('getObject')
1359
            ->will($this->returnValue($object));
1360
1361
        $this->admin->expects($this->once())
1362
            ->method('checkAccess')
1363
            ->with($this->equalTo('delete'))
1364
            ->will($this->returnValue(true));
1365
1366
        $this->admin->expects($this->once())
1367
            ->method('toString')
1368
            ->with($this->equalTo($object))
1369
            ->will($this->returnValue($toStringValue));
1370
1371
        $this->expectTranslate('flash_delete_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1372
1373
        $this->assertLoggerLogsModelManagerException($this->admin, 'delete');
1374
1375
        $this->request->setMethod('DELETE');
1376
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1377
1378
        $response = $this->controller->deleteAction(1, $this->request);
1379
1380
        $this->assertInstanceOf(RedirectResponse::class, $response);
1381
        $this->assertSame(['flash_delete_error'], $this->session->getFlashBag()->get('sonata_flash_error'));
1382
        $this->assertSame('list', $response->getTargetUrl());
1383
    }
1384
1385
    public function testDeleteActionInvalidCsrfToken(): void
1386
    {
1387
        $object = new \stdClass();
1388
1389
        $this->admin->expects($this->once())
1390
            ->method('getObject')
1391
            ->will($this->returnValue($object));
1392
1393
        $this->admin->expects($this->once())
1394
            ->method('checkAccess')
1395
            ->with($this->equalTo('delete'))
1396
            ->will($this->returnValue(true));
1397
1398
        $this->request->setMethod('POST');
1399
        $this->request->request->set('_method', 'DELETE');
1400
        $this->request->request->set('_sonata_csrf_token', 'CSRF-INVALID');
1401
1402
        try {
1403
            $this->controller->deleteAction(1, $this->request);
1404
        } catch (HttpException $e) {
1405
            $this->assertSame('The csrf token is not valid, CSRF attack?', $e->getMessage());
1406
            $this->assertSame(400, $e->getStatusCode());
1407
        }
1408
    }
1409
1410
    public function testEditActionNotFoundException(): void
1411
    {
1412
        $this->expectException(NotFoundHttpException::class);
1413
1414
        $this->admin->expects($this->once())
1415
            ->method('getObject')
1416
            ->will($this->returnValue(false));
1417
1418
        $this->controller->editAction(null, $this->request);
1419
    }
1420
1421
    public function testEditActionRuntimeException(): void
1422
    {
1423
        $this->expectException(\RuntimeException::class);
1424
1425
        $this->admin->expects($this->once())
1426
            ->method('getObject')
1427
            ->will($this->returnValue(new \stdClass()));
1428
1429
        $this->admin->expects($this->once())
1430
            ->method('checkAccess')
1431
            ->with($this->equalTo('edit'))
1432
            ->will($this->returnValue(true));
1433
1434
        $form = $this->createMock(Form::class);
1435
1436
        $this->admin->expects($this->once())
1437
            ->method('getForm')
1438
            ->will($this->returnValue($form));
1439
1440
        $form->expects($this->once())
1441
            ->method('all')
1442
            ->willReturn([]);
1443
1444
        $this->controller->editAction(null, $this->request);
1445
    }
1446
1447
    public function testEditActionAccessDenied(): void
1448
    {
1449
        $this->expectException(AccessDeniedException::class);
1450
1451
        $this->admin->expects($this->once())
1452
            ->method('getObject')
1453
            ->will($this->returnValue(new \stdClass()));
1454
1455
        $this->admin->expects($this->once())
1456
            ->method('checkAccess')
1457
            ->with($this->equalTo('edit'))
1458
            ->will($this->throwException(new AccessDeniedException()));
1459
1460
        $this->controller->editAction(null, $this->request);
1461
    }
1462
1463
    public function testPreEdit(): void
1464
    {
1465
        $object = new \stdClass();
1466
        $object->foo = 123456;
1467
1468
        $this->admin->expects($this->once())
1469
            ->method('getObject')
1470
            ->will($this->returnValue($object));
1471
1472
        $this->admin->expects($this->once())
1473
            ->method('checkAccess')
1474
            ->with($this->equalTo('edit'))
1475
            ->will($this->returnValue(true));
1476
1477
        $controller = new PreCRUDController();
1478
        $controller->setContainer($this->container);
1479
1480
        $response = $controller->editAction(null, $this->request);
1481
        $this->assertInstanceOf(Response::class, $response);
1482
        $this->assertSame('preEdit called: 123456', $response->getContent());
1483
    }
1484
1485
    public function testEditAction(): void
1486
    {
1487
        $object = new \stdClass();
1488
1489
        $this->admin->expects($this->once())
1490
            ->method('getObject')
1491
            ->will($this->returnValue($object));
1492
1493
        $this->admin->expects($this->once())
1494
            ->method('checkAccess')
1495
            ->with($this->equalTo('edit'))
1496
            ->will($this->returnValue(true));
1497
1498
        $form = $this->createMock(Form::class);
1499
1500
        $this->admin->expects($this->once())
1501
            ->method('getForm')
1502
            ->will($this->returnValue($form));
1503
1504
        $formView = $this->createMock(FormView::class);
1505
1506
        $form->expects($this->any())
1507
            ->method('createView')
1508
            ->will($this->returnValue($formView));
1509
1510
        $form->expects($this->once())
1511
            ->method('all')
1512
            ->willReturn(['field' => 'fielddata']);
1513
1514
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null, $this->request));
1515
1516
        $this->assertSame($this->admin, $this->parameters['admin']);
1517
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1518
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1519
1520
        $this->assertSame('edit', $this->parameters['action']);
1521
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1522
        $this->assertSame($object, $this->parameters['object']);
1523
        $this->assertSame([], $this->session->getFlashBag()->all());
1524
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
1525
    }
1526
1527
    /**
1528
     * @dataProvider getToStringValues
1529
     */
1530
    public function testEditActionSuccess($expectedToStringValue, $toStringValue): void
1531
    {
1532
        $object = new \stdClass();
1533
1534
        $this->admin->expects($this->once())
1535
            ->method('getObject')
1536
            ->will($this->returnValue($object));
1537
1538
        $this->admin->expects($this->once())
1539
            ->method('update')
1540
            ->will($this->returnArgument(0));
1541
1542
        $this->admin->expects($this->once())
1543
            ->method('checkAccess')
1544
            ->with($this->equalTo('edit'));
1545
1546
        $this->admin->expects($this->once())
1547
            ->method('hasRoute')
1548
            ->with($this->equalTo('edit'))
1549
            ->will($this->returnValue(true));
1550
1551
        $this->admin->expects($this->once())
1552
            ->method('hasAccess')
1553
            ->with($this->equalTo('edit'))
1554
            ->will($this->returnValue(true));
1555
1556
        $form = $this->createMock(Form::class);
1557
1558
        $form->expects($this->once())
1559
            ->method('getData')
1560
            ->will($this->returnValue($object));
1561
1562
        $this->admin->expects($this->once())
1563
            ->method('getForm')
1564
            ->will($this->returnValue($form));
1565
1566
        $form->expects($this->once())
1567
            ->method('isSubmitted')
1568
            ->will($this->returnValue(true));
1569
1570
        $form->expects($this->once())
1571
            ->method('isValid')
1572
            ->will($this->returnValue(true));
1573
1574
        $form->expects($this->once())
1575
            ->method('all')
1576
            ->willReturn(['field' => 'fielddata']);
1577
1578
        $this->admin->expects($this->once())
1579
            ->method('toString')
1580
            ->with($this->equalTo($object))
1581
            ->will($this->returnValue($toStringValue));
1582
1583
        $this->expectTranslate('flash_edit_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1584
1585
        $this->request->setMethod('POST');
1586
1587
        $response = $this->controller->editAction(null, $this->request);
1588
1589
        $this->assertInstanceOf(RedirectResponse::class, $response);
1590
        $this->assertSame(['flash_edit_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
1591
        $this->assertSame('stdClass_edit', $response->getTargetUrl());
1592
    }
1593
1594
    /**
1595
     * @dataProvider getToStringValues
1596
     */
1597
    public function testEditActionError($expectedToStringValue, $toStringValue): void
1598
    {
1599
        $object = new \stdClass();
1600
1601
        $this->admin->expects($this->once())
1602
            ->method('getObject')
1603
            ->will($this->returnValue($object));
1604
1605
        $this->admin->expects($this->once())
1606
            ->method('checkAccess')
1607
            ->with($this->equalTo('edit'))
1608
            ->will($this->returnValue(true));
1609
1610
        $form = $this->createMock(Form::class);
1611
1612
        $this->admin->expects($this->once())
1613
            ->method('getForm')
1614
            ->will($this->returnValue($form));
1615
1616
        $form->expects($this->once())
1617
            ->method('isSubmitted')
1618
            ->will($this->returnValue(true));
1619
1620
        $form->expects($this->once())
1621
            ->method('isValid')
1622
            ->will($this->returnValue(false));
1623
1624
        $form->expects($this->once())
1625
            ->method('all')
1626
            ->willReturn(['field' => 'fielddata']);
1627
1628
        $this->admin->expects($this->once())
1629
            ->method('toString')
1630
            ->with($this->equalTo($object))
1631
            ->will($this->returnValue($toStringValue));
1632
1633
        $this->expectTranslate('flash_edit_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1634
1635
        $this->request->setMethod('POST');
1636
1637
        $formView = $this->createMock(FormView::class);
1638
1639
        $form->expects($this->any())
1640
            ->method('createView')
1641
            ->will($this->returnValue($formView));
1642
1643
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null, $this->request));
1644
1645
        $this->assertSame($this->admin, $this->parameters['admin']);
1646
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1647
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1648
1649
        $this->assertSame('edit', $this->parameters['action']);
1650
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1651
        $this->assertSame($object, $this->parameters['object']);
1652
1653
        $this->assertSame(['sonata_flash_error' => ['flash_edit_error']], $this->session->getFlashBag()->all());
1654
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
1655
    }
1656
1657
    public function testEditActionAjaxSuccess(): void
1658
    {
1659
        $object = new \stdClass();
1660
1661
        $this->admin->expects($this->once())
1662
            ->method('getObject')
1663
            ->will($this->returnValue($object));
1664
1665
        $this->admin->expects($this->once())
1666
            ->method('update')
1667
            ->will($this->returnArgument(0));
1668
1669
        $this->admin->expects($this->once())
1670
            ->method('checkAccess')
1671
            ->with($this->equalTo('edit'))
1672
            ->will($this->returnValue(true));
1673
1674
        $form = $this->createMock(Form::class);
1675
1676
        $this->admin->expects($this->once())
1677
            ->method('getForm')
1678
            ->will($this->returnValue($form));
1679
1680
        $form->expects($this->once())
1681
            ->method('isSubmitted')
1682
            ->will($this->returnValue(true));
1683
1684
        $form->expects($this->once())
1685
            ->method('isValid')
1686
            ->will($this->returnValue(true));
1687
1688
        $form->expects($this->once())
1689
            ->method('getData')
1690
            ->will($this->returnValue($object));
1691
1692
        $form->expects($this->once())
1693
            ->method('all')
1694
            ->willReturn(['field' => 'fielddata']);
1695
1696
        $this->admin->expects($this->once())
1697
            ->method('getNormalizedIdentifier')
1698
            ->with($this->equalTo($object))
1699
            ->will($this->returnValue('foo_normalized'));
1700
1701
        $this->admin->expects($this->once())
1702
            ->method('toString')
1703
            ->will($this->returnValue('foo'));
1704
1705
        $this->request->setMethod('POST');
1706
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1707
1708
        $response = $this->controller->editAction(null, $this->request);
1709
1710
        $this->assertInstanceOf(Response::class, $response);
1711
        $this->assertSame(json_encode(['result' => 'ok', 'objectId' => 'foo_normalized', 'objectName' => 'foo']), $response->getContent());
1712
        $this->assertSame([], $this->session->getFlashBag()->all());
1713
    }
1714
1715
    public function testEditActionAjaxError(): void
1716
    {
1717
        $object = new \stdClass();
1718
1719
        $this->admin->expects($this->once())
1720
            ->method('getObject')
1721
            ->will($this->returnValue($object));
1722
1723
        $this->admin->expects($this->once())
1724
            ->method('checkAccess')
1725
            ->with($this->equalTo('edit'))
1726
            ->will($this->returnValue(true));
1727
1728
        $form = $this->createMock(Form::class);
1729
1730
        $this->admin->expects($this->once())
1731
            ->method('getForm')
1732
            ->will($this->returnValue($form));
1733
1734
        $form->expects($this->once())
1735
            ->method('isSubmitted')
1736
            ->will($this->returnValue(true));
1737
1738
        $form->expects($this->once())
1739
            ->method('isValid')
1740
            ->will($this->returnValue(false));
1741
1742
        $form->expects($this->once())
1743
            ->method('all')
1744
            ->willReturn(['field' => 'fielddata']);
1745
1746
        $this->request->setMethod('POST');
1747
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1748
1749
        $formView = $this->createMock(FormView::class);
1750
1751
        $form->expects($this->any())
1752
            ->method('createView')
1753
            ->will($this->returnValue($formView));
1754
1755
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null, $this->request));
1756
1757
        $this->assertSame($this->admin, $this->parameters['admin']);
1758
        $this->assertSame('@SonataAdmin/ajax_layout.html.twig', $this->parameters['base_template']);
1759
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1760
1761
        $this->assertSame('edit', $this->parameters['action']);
1762
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1763
        $this->assertSame($object, $this->parameters['object']);
1764
1765
        $this->assertSame([], $this->session->getFlashBag()->all());
1766
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
1767
    }
1768
1769
    /**
1770
     * @dataProvider getToStringValues
1771
     */
1772
    public function testEditActionWithModelManagerException($expectedToStringValue, $toStringValue): void
1773
    {
1774
        $object = new \stdClass();
1775
1776
        $this->admin->expects($this->once())
1777
            ->method('getObject')
1778
            ->will($this->returnValue($object));
1779
1780
        $this->admin->expects($this->once())
1781
            ->method('checkAccess')
1782
            ->with($this->equalTo('edit'))
1783
            ->will($this->returnValue(true));
1784
1785
        $this->admin->expects($this->any())
1786
            ->method('getClass')
1787
            ->will($this->returnValue('stdClass'));
1788
1789
        $form = $this->createMock(Form::class);
1790
1791
        $this->admin->expects($this->once())
1792
            ->method('getForm')
1793
            ->will($this->returnValue($form));
1794
1795
        $form->expects($this->once())
1796
            ->method('isValid')
1797
            ->will($this->returnValue(true));
1798
1799
        $form->expects($this->once())
1800
            ->method('getData')
1801
            ->will($this->returnValue($object));
1802
1803
        $form->expects($this->once())
1804
            ->method('all')
1805
            ->willReturn(['field' => 'fielddata']);
1806
1807
        $this->admin->expects($this->once())
1808
            ->method('toString')
1809
            ->with($this->equalTo($object))
1810
            ->will($this->returnValue($toStringValue));
1811
1812
        $this->expectTranslate('flash_edit_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1813
1814
        $form->expects($this->once())
1815
            ->method('isSubmitted')
1816
            ->will($this->returnValue(true));
1817
        $this->request->setMethod('POST');
1818
1819
        $formView = $this->createMock(FormView::class);
1820
1821
        $form->expects($this->any())
1822
            ->method('createView')
1823
            ->will($this->returnValue($formView));
1824
1825
        $this->assertLoggerLogsModelManagerException($this->admin, 'update');
1826
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null, $this->request));
1827
1828
        $this->assertSame($this->admin, $this->parameters['admin']);
1829
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1830
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1831
1832
        $this->assertSame('edit', $this->parameters['action']);
1833
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1834
        $this->assertSame($object, $this->parameters['object']);
1835
1836
        $this->assertSame(['sonata_flash_error' => ['flash_edit_error']], $this->session->getFlashBag()->all());
1837
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
1838
    }
1839
1840
    public function testEditActionWithPreview(): void
1841
    {
1842
        $object = new \stdClass();
1843
1844
        $this->admin->expects($this->once())
1845
            ->method('getObject')
1846
            ->will($this->returnValue($object));
1847
1848
        $this->admin->expects($this->once())
1849
            ->method('checkAccess')
1850
            ->with($this->equalTo('edit'))
1851
            ->will($this->returnValue(true));
1852
1853
        $form = $this->createMock(Form::class);
1854
1855
        $this->admin->expects($this->once())
1856
            ->method('getForm')
1857
            ->will($this->returnValue($form));
1858
1859
        $this->admin->expects($this->once())
1860
            ->method('supportsPreviewMode')
1861
            ->will($this->returnValue(true));
1862
1863
        $formView = $this->createMock(FormView::class);
1864
1865
        $form->expects($this->any())
1866
            ->method('createView')
1867
            ->will($this->returnValue($formView));
1868
1869
        $form->expects($this->once())
1870
            ->method('isSubmitted')
1871
            ->will($this->returnValue(true));
1872
1873
        $form->expects($this->once())
1874
            ->method('isValid')
1875
            ->will($this->returnValue(true));
1876
1877
        $form->expects($this->once())
1878
            ->method('all')
1879
            ->willReturn(['field' => 'fielddata']);
1880
1881
        $this->request->setMethod('POST');
1882
        $this->request->request->set('btn_preview', 'Preview');
1883
1884
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null, $this->request));
1885
1886
        $this->assertSame($this->admin, $this->parameters['admin']);
1887
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1888
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1889
1890
        $this->assertSame('edit', $this->parameters['action']);
1891
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1892
        $this->assertSame($object, $this->parameters['object']);
1893
1894
        $this->assertSame([], $this->session->getFlashBag()->all());
1895
        $this->assertSame('@SonataAdmin/CRUD/preview.html.twig', $this->template);
1896
    }
1897
1898
    public function testEditActionWithLockException(): void
1899
    {
1900
        $object = new \stdClass();
1901
        $class = \get_class($object);
1902
1903
        $this->admin->expects($this->any())
1904
            ->method('getObject')
1905
            ->will($this->returnValue($object));
1906
1907
        $this->admin->expects($this->any())
1908
            ->method('checkAccess')
1909
            ->with($this->equalTo('edit'))
1910
            ->will($this->returnValue(true));
1911
1912
        $this->admin->expects($this->any())
1913
            ->method('getClass')
1914
            ->will($this->returnValue($class));
1915
1916
        $form = $this->createMock(Form::class);
1917
1918
        $form->expects($this->any())
1919
            ->method('isValid')
1920
            ->will($this->returnValue(true));
1921
1922
        $form->expects($this->once())
1923
            ->method('getData')
1924
            ->will($this->returnValue($object));
1925
1926
        $form->expects($this->once())
1927
            ->method('all')
1928
            ->willReturn(['field' => 'fielddata']);
1929
1930
        $this->admin->expects($this->any())
1931
            ->method('getForm')
1932
            ->will($this->returnValue($form));
1933
1934
        $form->expects($this->any())
1935
            ->method('isSubmitted')
1936
            ->will($this->returnValue(true));
1937
        $this->request->setMethod('POST');
1938
1939
        $this->admin->expects($this->any())
1940
            ->method('update')
1941
            ->will($this->throwException(new LockException()));
1942
1943
        $this->admin->expects($this->any())
1944
            ->method('toString')
1945
            ->with($this->equalTo($object))
1946
            ->will($this->returnValue($class));
1947
1948
        $formView = $this->createMock(FormView::class);
1949
1950
        $form->expects($this->any())
1951
            ->method('createView')
1952
            ->will($this->returnValue($formView));
1953
1954
        $this->expectTranslate('flash_lock_error', [
1955
            '%name%' => $class,
1956
            '%link_start%' => '<a href="stdClass_edit">',
1957
            '%link_end%' => '</a>',
1958
        ], 'SonataAdminBundle');
1959
1960
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null, $this->request));
1961
    }
1962
1963
    public function testCreateActionAccessDenied(): void
1964
    {
1965
        $this->expectException(AccessDeniedException::class);
1966
1967
        $this->admin->expects($this->once())
1968
            ->method('checkAccess')
1969
            ->with($this->equalTo('create'))
1970
            ->will($this->throwException(new AccessDeniedException()));
1971
1972
        $this->controller->createAction($this->request);
1973
    }
1974
1975
    public function testCreateActionRuntimeException(): void
1976
    {
1977
        $this->expectException(\RuntimeException::class);
1978
1979
        $this->admin->expects($this->once())
1980
            ->method('checkAccess')
1981
            ->with($this->equalTo('create'))
1982
            ->will($this->returnValue(true));
1983
1984
        $this->admin->expects($this->any())
1985
            ->method('getClass')
1986
            ->will($this->returnValue('stdClass'));
1987
1988
        $this->admin->expects($this->once())
1989
            ->method('getNewInstance')
1990
            ->will($this->returnValue(new \stdClass()));
1991
1992
        $form = $this->createMock(Form::class);
1993
1994
        $this->admin->expects($this->once())
1995
            ->method('getForm')
1996
            ->will($this->returnValue($form));
1997
1998
        $form->expects($this->once())
1999
            ->method('all')
2000
            ->willReturn([]);
2001
2002
        $this->controller->createAction($this->request);
2003
    }
2004
2005
    public function testPreCreate(): void
2006
    {
2007
        $object = new \stdClass();
2008
        $object->foo = 123456;
2009
2010
        $this->admin->expects($this->once())
2011
            ->method('checkAccess')
2012
            ->with($this->equalTo('create'))
2013
            ->will($this->returnValue(true));
2014
2015
        $this->admin->expects($this->any())
2016
            ->method('getClass')
2017
            ->will($this->returnValue('stdClass'));
2018
2019
        $this->admin->expects($this->once())
2020
            ->method('getNewInstance')
2021
            ->will($this->returnValue($object));
2022
2023
        $controller = new PreCRUDController();
2024
        $controller->setContainer($this->container);
2025
2026
        $response = $controller->createAction($this->request);
2027
        $this->assertInstanceOf(Response::class, $response);
2028
        $this->assertSame('preCreate called: 123456', $response->getContent());
2029
    }
2030
2031
    public function testCreateAction(): void
2032
    {
2033
        $this->admin->expects($this->once())
2034
            ->method('checkAccess')
2035
            ->with($this->equalTo('create'))
2036
            ->will($this->returnValue(true));
2037
2038
        $object = new \stdClass();
2039
2040
        $this->admin->expects($this->any())
2041
            ->method('getClass')
2042
            ->will($this->returnValue('stdClass'));
2043
2044
        $this->admin->expects($this->once())
2045
            ->method('getNewInstance')
2046
            ->will($this->returnValue($object));
2047
2048
        $form = $this->createMock(Form::class);
2049
2050
        $this->admin->expects($this->once())
2051
            ->method('getForm')
2052
            ->will($this->returnValue($form));
2053
2054
        $form->expects($this->once())
2055
            ->method('all')
2056
            ->willReturn(['field' => 'fielddata']);
2057
2058
        $formView = $this->createMock(FormView::class);
2059
2060
        $form->expects($this->any())
2061
            ->method('createView')
2062
            ->will($this->returnValue($formView));
2063
2064
        $this->assertInstanceOf(Response::class, $this->controller->createAction($this->request));
2065
2066
        $this->assertSame($this->admin, $this->parameters['admin']);
2067
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2068
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2069
2070
        $this->assertSame('create', $this->parameters['action']);
2071
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
2072
        $this->assertSame($object, $this->parameters['object']);
2073
2074
        $this->assertSame([], $this->session->getFlashBag()->all());
2075
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
2076
    }
2077
2078
    /**
2079
     * @dataProvider getToStringValues
2080
     */
2081
    public function testCreateActionSuccess($expectedToStringValue, $toStringValue): void
2082
    {
2083
        $object = new \stdClass();
2084
2085
        $this->admin->expects($this->exactly(2))
2086
            ->method('checkAccess')
2087
            ->will($this->returnCallback(static function ($name, $objectIn = null) use ($object) {
2088
                if ('edit' === $name) {
2089
                    return true;
2090
                }
2091
2092
                if ('create' !== $name) {
2093
                    return false;
2094
                }
2095
2096
                if (null === $objectIn) {
2097
                    return true;
2098
                }
2099
2100
                return $objectIn === $object;
2101
            }));
2102
2103
        $this->admin->expects($this->once())
2104
            ->method('hasRoute')
2105
            ->with($this->equalTo('edit'))
2106
            ->will($this->returnValue(true));
2107
2108
        $this->admin->expects($this->once())
2109
            ->method('hasAccess')
2110
            ->with($this->equalTo('edit'))
2111
            ->will($this->returnValue(true));
2112
2113
        $this->admin->expects($this->once())
2114
            ->method('getNewInstance')
2115
            ->will($this->returnValue($object));
2116
2117
        $this->admin->expects($this->once())
2118
            ->method('create')
2119
            ->will($this->returnArgument(0));
2120
2121
        $form = $this->createMock(Form::class);
2122
2123
        $this->admin->expects($this->any())
2124
            ->method('getClass')
2125
            ->will($this->returnValue('stdClass'));
2126
2127
        $this->admin->expects($this->once())
2128
            ->method('getForm')
2129
            ->will($this->returnValue($form));
2130
2131
        $form->expects($this->once())
2132
            ->method('all')
2133
            ->willReturn(['field' => 'fielddata']);
2134
2135
        $form->expects($this->once())
2136
            ->method('isSubmitted')
2137
            ->will($this->returnValue(true));
2138
2139
        $form->expects($this->once())
2140
            ->method('isValid')
2141
            ->will($this->returnValue(true));
2142
2143
        $form->expects($this->once())
2144
            ->method('getData')
2145
            ->will($this->returnValue($object));
2146
2147
        $this->admin->expects($this->once())
2148
            ->method('toString')
2149
            ->with($this->equalTo($object))
2150
            ->will($this->returnValue($toStringValue));
2151
2152
        $this->expectTranslate('flash_create_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
2153
2154
        $this->request->setMethod('POST');
2155
2156
        $response = $this->controller->createAction($this->request);
2157
2158
        $this->assertInstanceOf(RedirectResponse::class, $response);
2159
        $this->assertSame(['flash_create_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
2160
        $this->assertSame('stdClass_edit', $response->getTargetUrl());
2161
    }
2162
2163
    public function testCreateActionAccessDenied2(): void
2164
    {
2165
        $this->expectException(AccessDeniedException::class);
2166
2167
        $object = new \stdClass();
2168
2169
        $this->admin->expects($this->any())
2170
            ->method('checkAccess')
2171
            ->will($this->returnCallback(static function ($name, $object = null) {
2172
                if ('create' !== $name) {
2173
                    throw new AccessDeniedException();
2174
                }
2175
                if (null === $object) {
2176
                    return true;
2177
                }
2178
2179
                throw new AccessDeniedException();
2180
            }));
2181
2182
        $this->admin->expects($this->once())
2183
            ->method('getNewInstance')
2184
            ->will($this->returnValue($object));
2185
2186
        $form = $this->createMock(Form::class);
2187
2188
        $this->admin->expects($this->any())
2189
            ->method('getClass')
2190
            ->will($this->returnValue('stdClass'));
2191
2192
        $this->admin->expects($this->once())
2193
            ->method('getForm')
2194
            ->will($this->returnValue($form));
2195
2196
        $form->expects($this->once())
2197
            ->method('all')
2198
            ->willReturn(['field' => 'fielddata']);
2199
2200
        $form->expects($this->once())
2201
            ->method('isSubmitted')
2202
            ->will($this->returnValue(true));
2203
2204
        $form->expects($this->once())
2205
            ->method('getData')
2206
            ->will($this->returnValue($object));
2207
2208
        $form->expects($this->once())
2209
            ->method('isValid')
2210
            ->will($this->returnValue(true));
2211
2212
        $this->request->setMethod('POST');
2213
2214
        $this->controller->createAction($this->request);
2215
    }
2216
2217
    /**
2218
     * @dataProvider getToStringValues
2219
     */
2220
    public function testCreateActionError($expectedToStringValue, $toStringValue): void
2221
    {
2222
        $this->admin->expects($this->once())
2223
            ->method('checkAccess')
2224
            ->with($this->equalTo('create'))
2225
            ->will($this->returnValue(true));
2226
2227
        $object = new \stdClass();
2228
2229
        $this->admin->expects($this->any())
2230
            ->method('getClass')
2231
            ->will($this->returnValue('stdClass'));
2232
2233
        $this->admin->expects($this->once())
2234
            ->method('getNewInstance')
2235
            ->will($this->returnValue($object));
2236
2237
        $form = $this->createMock(Form::class);
2238
2239
        $this->admin->expects($this->once())
2240
            ->method('getForm')
2241
            ->will($this->returnValue($form));
2242
2243
        $form->expects($this->once())
2244
            ->method('all')
2245
            ->willReturn(['field' => 'fielddata']);
2246
2247
        $form->expects($this->once())
2248
            ->method('isSubmitted')
2249
            ->will($this->returnValue(true));
2250
2251
        $form->expects($this->once())
2252
            ->method('isValid')
2253
            ->will($this->returnValue(false));
2254
2255
        $this->admin->expects($this->once())
2256
            ->method('toString')
2257
            ->with($this->equalTo($object))
2258
            ->will($this->returnValue($toStringValue));
2259
2260
        $this->expectTranslate('flash_create_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
2261
2262
        $this->request->setMethod('POST');
2263
2264
        $formView = $this->createMock(FormView::class);
2265
2266
        $form->expects($this->any())
2267
            ->method('createView')
2268
            ->will($this->returnValue($formView));
2269
2270
        $this->assertInstanceOf(Response::class, $this->controller->createAction($this->request));
2271
2272
        $this->assertSame($this->admin, $this->parameters['admin']);
2273
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2274
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2275
2276
        $this->assertSame('create', $this->parameters['action']);
2277
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
2278
        $this->assertSame($object, $this->parameters['object']);
2279
2280
        $this->assertSame(['sonata_flash_error' => ['flash_create_error']], $this->session->getFlashBag()->all());
2281
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
2282
    }
2283
2284
    /**
2285
     * @dataProvider getToStringValues
2286
     */
2287
    public function testCreateActionWithModelManagerException($expectedToStringValue, $toStringValue): void
2288
    {
2289
        $this->admin->expects($this->exactly(2))
2290
            ->method('checkAccess')
2291
            ->with($this->equalTo('create'))
2292
            ->will($this->returnValue(true));
2293
2294
        $this->admin->expects($this->any())
2295
            ->method('getClass')
2296
            ->will($this->returnValue('stdClass'));
2297
2298
        $object = new \stdClass();
2299
2300
        $this->admin->expects($this->once())
2301
            ->method('getNewInstance')
2302
            ->will($this->returnValue($object));
2303
2304
        $form = $this->createMock(Form::class);
2305
2306
        $this->admin->expects($this->once())
2307
            ->method('getForm')
2308
            ->will($this->returnValue($form));
2309
2310
        $form->expects($this->once())
2311
            ->method('all')
2312
            ->willReturn(['field' => 'fielddata']);
2313
2314
        $form->expects($this->once())
2315
            ->method('isValid')
2316
            ->will($this->returnValue(true));
2317
2318
        $this->admin->expects($this->once())
2319
            ->method('toString')
2320
            ->with($this->equalTo($object))
2321
            ->will($this->returnValue($toStringValue));
2322
2323
        $this->expectTranslate('flash_create_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
2324
2325
        $form->expects($this->once())
2326
            ->method('isSubmitted')
2327
            ->will($this->returnValue(true));
2328
2329
        $form->expects($this->once())
2330
            ->method('getData')
2331
            ->will($this->returnValue($object));
2332
2333
        $this->request->setMethod('POST');
2334
2335
        $formView = $this->createMock(FormView::class);
2336
2337
        $form->expects($this->any())
2338
            ->method('createView')
2339
            ->will($this->returnValue($formView));
2340
2341
        $this->assertLoggerLogsModelManagerException($this->admin, 'create');
2342
2343
        $this->assertInstanceOf(Response::class, $this->controller->createAction($this->request));
2344
2345
        $this->assertSame($this->admin, $this->parameters['admin']);
2346
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2347
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2348
2349
        $this->assertSame('create', $this->parameters['action']);
2350
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
2351
        $this->assertSame($object, $this->parameters['object']);
2352
2353
        $this->assertSame(['sonata_flash_error' => ['flash_create_error']], $this->session->getFlashBag()->all());
2354
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
2355
    }
2356
2357
    public function testCreateActionAjaxSuccess(): void
2358
    {
2359
        $object = new \stdClass();
2360
2361
        $this->admin->expects($this->exactly(2))
2362
            ->method('checkAccess')
2363
            ->will($this->returnCallback(static function ($name, $objectIn = null) use ($object) {
2364
                if ('create' !== $name) {
2365
                    return false;
2366
                }
2367
2368
                if (null === $objectIn) {
2369
                    return true;
2370
                }
2371
2372
                return $objectIn === $object;
2373
            }));
2374
2375
        $this->admin->expects($this->once())
2376
            ->method('getNewInstance')
2377
            ->will($this->returnValue($object));
2378
2379
        $this->admin->expects($this->once())
2380
            ->method('create')
2381
            ->will($this->returnArgument(0));
2382
2383
        $form = $this->createMock(Form::class);
2384
2385
        $this->admin->expects($this->once())
2386
            ->method('getForm')
2387
            ->will($this->returnValue($form));
2388
2389
        $form->expects($this->once())
2390
            ->method('all')
2391
            ->willReturn(['field' => 'fielddata']);
2392
2393
        $form->expects($this->once())
2394
            ->method('isSubmitted')
2395
            ->will($this->returnValue(true));
2396
2397
        $form->expects($this->once())
2398
            ->method('isValid')
2399
            ->will($this->returnValue(true));
2400
2401
        $form->expects($this->once())
2402
            ->method('getData')
2403
            ->will($this->returnValue($object));
2404
2405
        $this->admin->expects($this->any())
2406
            ->method('getClass')
2407
            ->will($this->returnValue('stdClass'));
2408
2409
        $this->admin->expects($this->once())
2410
            ->method('getNormalizedIdentifier')
2411
            ->with($this->equalTo($object))
2412
            ->will($this->returnValue('foo_normalized'));
2413
2414
        $this->admin->expects($this->once())
2415
            ->method('toString')
2416
            ->will($this->returnValue('foo'));
2417
2418
        $this->request->setMethod('POST');
2419
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
2420
2421
        $response = $this->controller->createAction($this->request);
2422
2423
        $this->assertInstanceOf(Response::class, $response);
2424
        $this->assertSame(json_encode(['result' => 'ok', 'objectId' => 'foo_normalized', 'objectName' => 'foo']), $response->getContent());
2425
        $this->assertSame([], $this->session->getFlashBag()->all());
2426
    }
2427
2428
    public function testCreateActionAjaxError(): void
2429
    {
2430
        $this->admin->expects($this->once())
2431
            ->method('checkAccess')
2432
            ->with($this->equalTo('create'))
2433
            ->will($this->returnValue(true));
2434
2435
        $object = new \stdClass();
2436
2437
        $this->admin->expects($this->once())
2438
            ->method('getNewInstance')
2439
            ->will($this->returnValue($object));
2440
2441
        $form = $this->createMock(Form::class);
2442
2443
        $this->admin->expects($this->any())
2444
            ->method('getClass')
2445
            ->will($this->returnValue('stdClass'));
2446
2447
        $this->admin->expects($this->once())
2448
            ->method('getForm')
2449
            ->will($this->returnValue($form));
2450
2451
        $form->expects($this->once())
2452
            ->method('all')
2453
            ->willReturn(['field' => 'fielddata']);
2454
2455
        $form->expects($this->once())
2456
            ->method('isSubmitted')
2457
            ->will($this->returnValue(true));
2458
2459
        $form->expects($this->once())
2460
            ->method('isValid')
2461
            ->will($this->returnValue(false));
2462
2463
        $this->request->setMethod('POST');
2464
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
2465
2466
        $formView = $this->createMock(FormView::class);
2467
2468
        $form->expects($this->any())
2469
            ->method('createView')
2470
            ->will($this->returnValue($formView));
2471
2472
        $this->assertInstanceOf(Response::class, $this->controller->createAction($this->request));
2473
2474
        $this->assertSame($this->admin, $this->parameters['admin']);
2475
        $this->assertSame('@SonataAdmin/ajax_layout.html.twig', $this->parameters['base_template']);
2476
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2477
2478
        $this->assertSame('create', $this->parameters['action']);
2479
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
2480
        $this->assertSame($object, $this->parameters['object']);
2481
2482
        $this->assertSame([], $this->session->getFlashBag()->all());
2483
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
2484
    }
2485
2486
    public function testCreateActionWithPreview(): void
2487
    {
2488
        $this->admin->expects($this->once())
2489
            ->method('checkAccess')
2490
            ->with($this->equalTo('create'))
2491
            ->will($this->returnValue(true));
2492
2493
        $object = new \stdClass();
2494
2495
        $this->admin->expects($this->once())
2496
            ->method('getNewInstance')
2497
            ->will($this->returnValue($object));
2498
2499
        $form = $this->createMock(Form::class);
2500
2501
        $this->admin->expects($this->any())
2502
            ->method('getClass')
2503
            ->will($this->returnValue('stdClass'));
2504
2505
        $this->admin->expects($this->once())
2506
            ->method('getForm')
2507
            ->will($this->returnValue($form));
2508
2509
        $form->expects($this->once())
2510
            ->method('all')
2511
            ->willReturn(['field' => 'fielddata']);
2512
2513
        $this->admin->expects($this->once())
2514
            ->method('supportsPreviewMode')
2515
            ->will($this->returnValue(true));
2516
2517
        $formView = $this->createMock(FormView::class);
2518
2519
        $form->expects($this->any())
2520
            ->method('createView')
2521
            ->will($this->returnValue($formView));
2522
2523
        $form->expects($this->once())
2524
            ->method('isSubmitted')
2525
            ->will($this->returnValue(true));
2526
2527
        $form->expects($this->once())
2528
            ->method('isValid')
2529
            ->will($this->returnValue(true));
2530
2531
        $this->request->setMethod('POST');
2532
        $this->request->request->set('btn_preview', 'Preview');
2533
2534
        $this->assertInstanceOf(Response::class, $this->controller->createAction($this->request));
2535
2536
        $this->assertSame($this->admin, $this->parameters['admin']);
2537
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2538
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2539
2540
        $this->assertSame('create', $this->parameters['action']);
2541
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
2542
        $this->assertSame($object, $this->parameters['object']);
2543
2544
        $this->assertSame([], $this->session->getFlashBag()->all());
2545
        $this->assertSame('@SonataAdmin/CRUD/preview.html.twig', $this->template);
2546
    }
2547
2548
    public function testExportActionAccessDenied(): void
2549
    {
2550
        $this->expectException(AccessDeniedException::class);
2551
2552
        $this->admin->expects($this->once())
2553
            ->method('checkAccess')
2554
            ->with($this->equalTo('export'))
2555
            ->will($this->throwException(new AccessDeniedException()));
2556
2557
        $this->controller->exportAction($this->request);
2558
    }
2559
2560
    public function testExportActionWrongFormat(): void
2561
    {
2562
        $this->expectException(\RuntimeException::class, 'Export in format `csv` is not allowed for class: `Foo`. Allowed formats are: `json`');
2563
2564
        $this->admin->expects($this->once())
2565
            ->method('checkAccess')
2566
            ->with($this->equalTo('export'))
2567
            ->will($this->returnValue(true));
2568
2569
        $this->admin->expects($this->once())
2570
            ->method('getExportFormats')
2571
            ->will($this->returnValue(['json']));
2572
2573
        $this->admin->expects($this->any())
2574
            ->method('getClass')
2575
            ->will($this->returnValue('Foo'));
2576
2577
        $this->request->query->set('format', 'csv');
2578
2579
        $this->controller->exportAction($this->request);
2580
    }
2581
2582
    public function testExportAction(): void
2583
    {
2584
        $this->admin->expects($this->once())
2585
            ->method('checkAccess')
2586
            ->with($this->equalTo('export'))
2587
            ->will($this->returnValue(true));
2588
2589
        $this->admin->expects($this->once())
2590
            ->method('getExportFormats')
2591
            ->will($this->returnValue(['json']));
2592
2593
        $this->admin->expects($this->once())
2594
            ->method('getClass')
2595
            ->will($this->returnValue(\stdClass::class));
2596
2597
        $dataSourceIterator = $this->createMock(SourceIteratorInterface::class);
2598
2599
        $this->admin->expects($this->once())
2600
            ->method('getDataSourceIterator')
2601
            ->will($this->returnValue($dataSourceIterator));
2602
2603
        $this->request->query->set('format', 'json');
2604
2605
        $response = $this->controller->exportAction($this->request);
2606
        $this->assertInstanceOf(StreamedResponse::class, $response);
2607
        $this->assertSame(200, $response->getStatusCode());
2608
        $this->assertSame([], $this->session->getFlashBag()->all());
2609
    }
2610
2611
    public function testHistoryActionAccessDenied(): void
2612
    {
2613
        $this->expectException(AccessDeniedException::class);
2614
2615
        $this->admin->expects($this->any())
2616
            ->method('getObject')
2617
            ->will($this->returnValue(new \StdClass()));
2618
2619
        $this->admin->expects($this->once())
2620
            ->method('checkAccess')
2621
            ->with($this->equalTo('history'))
2622
            ->will($this->throwException(new AccessDeniedException()));
2623
2624
        $this->controller->historyAction(null, $this->request);
2625
    }
2626
2627
    public function testHistoryActionNotFoundException(): void
2628
    {
2629
        $this->expectException(NotFoundHttpException::class);
2630
2631
        $this->admin->expects($this->once())
2632
            ->method('getObject')
2633
            ->will($this->returnValue(false));
2634
2635
        $this->controller->historyAction(null, $this->request);
2636
    }
2637
2638
    public function testHistoryActionNoReader(): void
2639
    {
2640
        $this->expectException(NotFoundHttpException::class, 'unable to find the audit reader for class : Foo');
2641
2642
        $this->request->query->set('id', 123);
2643
2644
        $this->admin->expects($this->once())
2645
            ->method('checkAccess')
2646
            ->with($this->equalTo('history'))
2647
            ->will($this->returnValue(true));
2648
2649
        $object = new \stdClass();
2650
2651
        $this->admin->expects($this->once())
2652
            ->method('getObject')
2653
            ->will($this->returnValue($object));
2654
2655
        $this->admin->expects($this->any())
2656
            ->method('getClass')
2657
            ->will($this->returnValue('Foo'));
2658
2659
        $this->auditManager->expects($this->once())
2660
            ->method('hasReader')
2661
            ->with($this->equalTo('Foo'))
2662
            ->will($this->returnValue(false));
2663
2664
        $this->controller->historyAction(null, $this->request);
2665
    }
2666
2667
    public function testHistoryAction(): void
2668
    {
2669
        $this->request->query->set('id', 123);
2670
2671
        $this->admin->expects($this->once())
2672
            ->method('checkAccess')
2673
            ->with($this->equalTo('history'))
2674
            ->will($this->returnValue(true));
2675
2676
        $object = new \stdClass();
2677
2678
        $this->admin->expects($this->once())
2679
            ->method('getObject')
2680
            ->will($this->returnValue($object));
2681
2682
        $this->admin->expects($this->any())
2683
            ->method('getClass')
2684
            ->will($this->returnValue('Foo'));
2685
2686
        $this->auditManager->expects($this->once())
2687
            ->method('hasReader')
2688
            ->with($this->equalTo('Foo'))
2689
            ->will($this->returnValue(true));
2690
2691
        $reader = $this->createMock(AuditReaderInterface::class);
2692
2693
        $this->auditManager->expects($this->once())
2694
            ->method('getReader')
2695
            ->with($this->equalTo('Foo'))
2696
            ->will($this->returnValue($reader));
2697
2698
        $reader->expects($this->once())
2699
            ->method('findRevisions')
2700
            ->with($this->equalTo('Foo'), $this->equalTo(123))
2701
            ->will($this->returnValue([]));
2702
2703
        $this->assertInstanceOf(Response::class, $this->controller->historyAction(null, $this->request));
2704
2705
        $this->assertSame($this->admin, $this->parameters['admin']);
2706
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2707
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2708
2709
        $this->assertSame('history', $this->parameters['action']);
2710
        $this->assertSame([], $this->parameters['revisions']);
2711
        $this->assertSame($object, $this->parameters['object']);
2712
2713
        $this->assertSame([], $this->session->getFlashBag()->all());
2714
        $this->assertSame('@SonataAdmin/CRUD/history.html.twig', $this->template);
2715
    }
2716
2717
    public function testAclActionAclNotEnabled(): void
2718
    {
2719
        $this->expectException(NotFoundHttpException::class, 'ACL are not enabled for this admin');
2720
2721
        $this->controller->aclAction(null, $this->request);
2722
    }
2723
2724
    public function testAclActionNotFoundException(): void
2725
    {
2726
        $this->expectException(NotFoundHttpException::class);
2727
2728
        $this->admin->expects($this->once())
2729
            ->method('isAclEnabled')
2730
            ->will($this->returnValue(true));
2731
2732
        $this->admin->expects($this->once())
2733
            ->method('getObject')
2734
            ->will($this->returnValue(false));
2735
2736
        $this->controller->aclAction(null, $this->request);
2737
    }
2738
2739
    public function testAclActionAccessDenied(): void
2740
    {
2741
        $this->expectException(AccessDeniedException::class);
2742
2743
        $this->admin->expects($this->once())
2744
            ->method('isAclEnabled')
2745
            ->will($this->returnValue(true));
2746
2747
        $object = new \stdClass();
2748
2749
        $this->admin->expects($this->once())
2750
            ->method('getObject')
2751
            ->will($this->returnValue($object));
2752
2753
        $this->admin->expects($this->once())
2754
            ->method('checkAccess')
2755
            ->with($this->equalTo('acl'), $this->equalTo($object))
2756
            ->will($this->throwException(new AccessDeniedException()));
2757
2758
        $this->controller->aclAction(null, $this->request);
2759
    }
2760
2761
    public function testAclAction(): void
2762
    {
2763
        $this->request->query->set('id', 123);
2764
2765
        $this->admin->expects($this->once())
2766
            ->method('isAclEnabled')
2767
            ->will($this->returnValue(true));
2768
2769
        $object = new \stdClass();
2770
2771
        $this->admin->expects($this->once())
2772
            ->method('getObject')
2773
            ->will($this->returnValue($object));
2774
2775
        $this->admin->expects($this->any())
2776
            ->method('checkAccess')
2777
            ->will($this->returnValue(true));
2778
2779
        $this->admin->expects($this->any())
2780
            ->method('getSecurityInformation')
2781
            ->will($this->returnValue([]));
2782
2783
        $this->adminObjectAclManipulator->expects($this->once())
2784
            ->method('getMaskBuilderClass')
2785
            ->will($this->returnValue(AdminPermissionMap::class));
2786
2787
        $aclUsersForm = $this->getMockBuilder(Form::class)
2788
            ->disableOriginalConstructor()
2789
            ->getMock();
2790
2791
        $aclUsersForm->expects($this->once())
2792
            ->method('createView')
2793
            ->will($this->returnValue($this->createMock(FormView::class)));
2794
2795
        $aclRolesForm = $this->getMockBuilder(Form::class)
2796
            ->disableOriginalConstructor()
2797
            ->getMock();
2798
2799
        $aclRolesForm->expects($this->once())
2800
            ->method('createView')
2801
            ->will($this->returnValue($this->createMock(FormView::class)));
2802
2803
        $this->adminObjectAclManipulator->expects($this->once())
2804
            ->method('createAclUsersForm')
2805
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2806
            ->will($this->returnValue($aclUsersForm));
2807
2808
        $this->adminObjectAclManipulator->expects($this->once())
2809
            ->method('createAclRolesForm')
2810
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2811
            ->will($this->returnValue($aclRolesForm));
2812
2813
        $aclSecurityHandler = $this->getMockBuilder(AclSecurityHandler::class)
2814
            ->disableOriginalConstructor()
2815
            ->getMock();
2816
2817
        $aclSecurityHandler->expects($this->any())
2818
            ->method('getObjectPermissions')
2819
            ->will($this->returnValue([]));
2820
2821
        $this->admin->expects($this->any())
2822
            ->method('getSecurityHandler')
2823
            ->will($this->returnValue($aclSecurityHandler));
2824
2825
        $this->assertInstanceOf(Response::class, $this->controller->aclAction(null, $this->request));
2826
2827
        $this->assertSame($this->admin, $this->parameters['admin']);
2828
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2829
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2830
2831
        $this->assertSame('acl', $this->parameters['action']);
2832
        $this->assertSame([], $this->parameters['permissions']);
2833
        $this->assertSame($object, $this->parameters['object']);
2834
        $this->assertInstanceOf(\ArrayIterator::class, $this->parameters['users']);
2835
        $this->assertInstanceOf(\ArrayIterator::class, $this->parameters['roles']);
2836
        $this->assertInstanceOf(FormView::class, $this->parameters['aclUsersForm']);
2837
        $this->assertInstanceOf(FormView::class, $this->parameters['aclRolesForm']);
2838
2839
        $this->assertSame([], $this->session->getFlashBag()->all());
2840
        $this->assertSame('@SonataAdmin/CRUD/acl.html.twig', $this->template);
2841
    }
2842
2843
    public function testAclActionInvalidUpdate(): void
2844
    {
2845
        $this->request->query->set('id', 123);
2846
        $this->request->request->set(AdminObjectAclManipulator::ACL_USERS_FORM_NAME, []);
2847
2848
        $this->admin->expects($this->once())
2849
            ->method('isAclEnabled')
2850
            ->will($this->returnValue(true));
2851
2852
        $object = new \stdClass();
2853
2854
        $this->admin->expects($this->once())
2855
            ->method('getObject')
2856
            ->will($this->returnValue($object));
2857
2858
        $this->admin->expects($this->any())
2859
            ->method('checkAccess')
2860
            ->will($this->returnValue(true));
2861
2862
        $this->admin->expects($this->any())
2863
            ->method('getSecurityInformation')
2864
            ->will($this->returnValue([]));
2865
2866
        $this->adminObjectAclManipulator->expects($this->once())
2867
            ->method('getMaskBuilderClass')
2868
            ->will($this->returnValue(AdminPermissionMap::class));
2869
2870
        $aclUsersForm = $this->getMockBuilder(Form::class)
2871
            ->disableOriginalConstructor()
2872
            ->getMock();
2873
2874
        $aclUsersForm->expects($this->once())
2875
            ->method('isValid')
2876
            ->will($this->returnValue(false));
2877
2878
        $aclUsersForm->expects($this->once())
2879
            ->method('createView')
2880
            ->will($this->returnValue($this->createMock(FormView::class)));
2881
2882
        $aclRolesForm = $this->getMockBuilder(Form::class)
2883
            ->disableOriginalConstructor()
2884
            ->getMock();
2885
2886
        $aclRolesForm->expects($this->once())
2887
            ->method('createView')
2888
            ->will($this->returnValue($this->createMock(FormView::class)));
2889
2890
        $this->adminObjectAclManipulator->expects($this->once())
2891
            ->method('createAclUsersForm')
2892
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2893
            ->will($this->returnValue($aclUsersForm));
2894
2895
        $this->adminObjectAclManipulator->expects($this->once())
2896
            ->method('createAclRolesForm')
2897
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2898
            ->will($this->returnValue($aclRolesForm));
2899
2900
        $aclSecurityHandler = $this->getMockBuilder(AclSecurityHandler::class)
2901
            ->disableOriginalConstructor()
2902
            ->getMock();
2903
2904
        $aclSecurityHandler->expects($this->any())
2905
            ->method('getObjectPermissions')
2906
            ->will($this->returnValue([]));
2907
2908
        $this->admin->expects($this->any())
2909
            ->method('getSecurityHandler')
2910
            ->will($this->returnValue($aclSecurityHandler));
2911
2912
        $this->request->setMethod('POST');
2913
2914
        $this->assertInstanceOf(Response::class, $this->controller->aclAction(null, $this->request));
2915
2916
        $this->assertSame($this->admin, $this->parameters['admin']);
2917
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2918
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2919
2920
        $this->assertSame('acl', $this->parameters['action']);
2921
        $this->assertSame([], $this->parameters['permissions']);
2922
        $this->assertSame($object, $this->parameters['object']);
2923
        $this->assertInstanceOf(\ArrayIterator::class, $this->parameters['users']);
2924
        $this->assertInstanceOf(\ArrayIterator::class, $this->parameters['roles']);
2925
        $this->assertInstanceOf(FormView::class, $this->parameters['aclUsersForm']);
2926
        $this->assertInstanceOf(FormView::class, $this->parameters['aclRolesForm']);
2927
2928
        $this->assertSame([], $this->session->getFlashBag()->all());
2929
        $this->assertSame('@SonataAdmin/CRUD/acl.html.twig', $this->template);
2930
    }
2931
2932
    public function testAclActionSuccessfulUpdate(): void
2933
    {
2934
        $this->request->query->set('id', 123);
2935
        $this->request->request->set(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME, []);
2936
2937
        $this->admin->expects($this->once())
2938
            ->method('isAclEnabled')
2939
            ->will($this->returnValue(true));
2940
2941
        $object = new \stdClass();
2942
2943
        $this->admin->expects($this->once())
2944
            ->method('getObject')
2945
            ->will($this->returnValue($object));
2946
2947
        $this->admin->expects($this->any())
2948
            ->method('checkAccess')
2949
            ->will($this->returnValue(true));
2950
2951
        $this->admin->expects($this->any())
2952
            ->method('getSecurityInformation')
2953
            ->will($this->returnValue([]));
2954
2955
        $this->adminObjectAclManipulator->expects($this->once())
2956
            ->method('getMaskBuilderClass')
2957
            ->will($this->returnValue(AdminPermissionMap::class));
2958
2959
        $aclUsersForm = $this->getMockBuilder(Form::class)
2960
            ->disableOriginalConstructor()
2961
            ->getMock();
2962
2963
        $aclUsersForm->expects($this->any())
2964
            ->method('createView')
2965
            ->will($this->returnValue($this->createMock(FormView::class)));
2966
2967
        $aclRolesForm = $this->getMockBuilder(Form::class)
2968
            ->disableOriginalConstructor()
2969
            ->getMock();
2970
2971
        $aclRolesForm->expects($this->any())
2972
            ->method('createView')
2973
            ->will($this->returnValue($this->createMock(FormView::class)));
2974
2975
        $aclRolesForm->expects($this->once())
2976
            ->method('isValid')
2977
            ->will($this->returnValue(true));
2978
2979
        $this->adminObjectAclManipulator->expects($this->once())
2980
            ->method('createAclUsersForm')
2981
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2982
            ->will($this->returnValue($aclUsersForm));
2983
2984
        $this->adminObjectAclManipulator->expects($this->once())
2985
            ->method('createAclRolesForm')
2986
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2987
            ->will($this->returnValue($aclRolesForm));
2988
2989
        $aclSecurityHandler = $this->getMockBuilder(AclSecurityHandler::class)
2990
            ->disableOriginalConstructor()
2991
            ->getMock();
2992
2993
        $aclSecurityHandler->expects($this->any())
2994
            ->method('getObjectPermissions')
2995
            ->will($this->returnValue([]));
2996
2997
        $this->admin->expects($this->any())
2998
            ->method('getSecurityHandler')
2999
            ->will($this->returnValue($aclSecurityHandler));
3000
3001
        $this->expectTranslate('flash_acl_edit_success', [], 'SonataAdminBundle');
3002
3003
        $this->request->setMethod('POST');
3004
3005
        $response = $this->controller->aclAction(null, $this->request);
3006
3007
        $this->assertInstanceOf(RedirectResponse::class, $response);
3008
3009
        $this->assertSame(['flash_acl_edit_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
3010
        $this->assertSame('stdClass_acl', $response->getTargetUrl());
3011
    }
3012
3013
    public function testHistoryViewRevisionActionAccessDenied(): void
3014
    {
3015
        $this->expectException(AccessDeniedException::class);
3016
3017
        $this->admin->expects($this->any())
3018
            ->method('getObject')
3019
            ->will($this->returnValue(new \StdClass()));
3020
3021
        $this->admin->expects($this->once())
3022
            ->method('checkAccess')
3023
            ->with($this->equalTo('historyViewRevision'))
3024
            ->will($this->throwException(new AccessDeniedException()));
3025
3026
        $this->controller->historyViewRevisionAction(null, null, $this->request);
3027
    }
3028
3029
    public function testHistoryViewRevisionActionNotFoundException(): void
3030
    {
3031
        $this->expectException(NotFoundHttpException::class, 'unable to find the object with id: 123');
3032
3033
        $this->request->query->set('id', 123);
3034
3035
        $this->admin->expects($this->once())
3036
            ->method('getObject')
3037
            ->will($this->returnValue(false));
3038
3039
        $this->controller->historyViewRevisionAction(null, null, $this->request);
3040
    }
3041
3042
    public function testHistoryViewRevisionActionNoReader(): void
3043
    {
3044
        $this->expectException(NotFoundHttpException::class, 'unable to find the audit reader for class : Foo');
3045
3046
        $this->request->query->set('id', 123);
3047
3048
        $this->admin->expects($this->once())
3049
            ->method('checkAccess')
3050
            ->with($this->equalTo('historyViewRevision'))
3051
            ->will($this->returnValue(true));
3052
3053
        $object = new \stdClass();
3054
3055
        $this->admin->expects($this->once())
3056
            ->method('getObject')
3057
            ->will($this->returnValue($object));
3058
3059
        $this->admin->expects($this->any())
3060
            ->method('getClass')
3061
            ->will($this->returnValue('Foo'));
3062
3063
        $this->auditManager->expects($this->once())
3064
            ->method('hasReader')
3065
            ->with($this->equalTo('Foo'))
3066
            ->will($this->returnValue(false));
3067
3068
        $this->controller->historyViewRevisionAction(null, null, $this->request);
3069
    }
3070
3071
    public function testHistoryViewRevisionActionNotFoundRevision(): void
3072
    {
3073
        $this->expectException(NotFoundHttpException::class, 'unable to find the targeted object `123` from the revision `456` with classname : `Foo`');
3074
3075
        $this->request->query->set('id', 123);
3076
3077
        $this->admin->expects($this->once())
3078
            ->method('checkAccess')
3079
            ->with($this->equalTo('historyViewRevision'))
3080
            ->will($this->returnValue(true));
3081
3082
        $object = new \stdClass();
3083
3084
        $this->admin->expects($this->once())
3085
            ->method('getObject')
3086
            ->will($this->returnValue($object));
3087
3088
        $this->admin->expects($this->any())
3089
            ->method('getClass')
3090
            ->will($this->returnValue('Foo'));
3091
3092
        $this->auditManager->expects($this->once())
3093
            ->method('hasReader')
3094
            ->with($this->equalTo('Foo'))
3095
            ->will($this->returnValue(true));
3096
3097
        $reader = $this->createMock(AuditReaderInterface::class);
3098
3099
        $this->auditManager->expects($this->once())
3100
            ->method('getReader')
3101
            ->with($this->equalTo('Foo'))
3102
            ->will($this->returnValue($reader));
3103
3104
        $reader->expects($this->once())
3105
            ->method('find')
3106
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3107
            ->will($this->returnValue(null));
3108
3109
        $this->controller->historyViewRevisionAction(123, 456, $this->request);
3110
    }
3111
3112
    public function testHistoryViewRevisionAction(): void
3113
    {
3114
        $this->request->query->set('id', 123);
3115
3116
        $this->admin->expects($this->once())
3117
            ->method('checkAccess')
3118
            ->with($this->equalTo('historyViewRevision'))
3119
            ->will($this->returnValue(true));
3120
3121
        $object = new \stdClass();
3122
3123
        $this->admin->expects($this->once())
3124
            ->method('getObject')
3125
            ->will($this->returnValue($object));
3126
3127
        $this->admin->expects($this->any())
3128
            ->method('getClass')
3129
            ->will($this->returnValue('Foo'));
3130
3131
        $this->auditManager->expects($this->once())
3132
            ->method('hasReader')
3133
            ->with($this->equalTo('Foo'))
3134
            ->will($this->returnValue(true));
3135
3136
        $reader = $this->createMock(AuditReaderInterface::class);
3137
3138
        $this->auditManager->expects($this->once())
3139
            ->method('getReader')
3140
            ->with($this->equalTo('Foo'))
3141
            ->will($this->returnValue($reader));
3142
3143
        $objectRevision = new \stdClass();
3144
        $objectRevision->revision = 456;
3145
3146
        $reader->expects($this->once())
3147
            ->method('find')
3148
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3149
            ->will($this->returnValue($objectRevision));
3150
3151
        $this->admin->expects($this->once())
3152
            ->method('setSubject')
3153
            ->with($this->equalTo($objectRevision))
3154
            ->will($this->returnValue(null));
3155
3156
        $fieldDescriptionCollection = new FieldDescriptionCollection();
3157
        $this->admin->expects($this->once())
3158
            ->method('getShow')
3159
            ->will($this->returnValue($fieldDescriptionCollection));
3160
3161
        $this->assertInstanceOf(Response::class, $this->controller->historyViewRevisionAction(123, 456, $this->request));
3162
3163
        $this->assertSame($this->admin, $this->parameters['admin']);
3164
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
3165
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
3166
3167
        $this->assertSame('show', $this->parameters['action']);
3168
        $this->assertSame($objectRevision, $this->parameters['object']);
3169
        $this->assertSame($fieldDescriptionCollection, $this->parameters['elements']);
3170
3171
        $this->assertSame([], $this->session->getFlashBag()->all());
3172
        $this->assertSame('@SonataAdmin/CRUD/show.html.twig', $this->template);
3173
    }
3174
3175
    public function testHistoryCompareRevisionsActionAccessDenied(): void
3176
    {
3177
        $this->expectException(AccessDeniedException::class);
3178
3179
        $this->admin->expects($this->once())
3180
            ->method('checkAccess')
3181
            ->with($this->equalTo('historyCompareRevisions'))
3182
            ->will($this->throwException(new AccessDeniedException()));
3183
3184
        $this->controller->historyCompareRevisionsAction(null, null, null, $this->request);
3185
    }
3186
3187
    public function testHistoryCompareRevisionsActionNotFoundException(): void
3188
    {
3189
        $this->expectException(NotFoundHttpException::class, 'unable to find the object with id: 123');
3190
3191
        $this->request->query->set('id', 123);
3192
3193
        $this->admin->expects($this->once())
3194
            ->method('checkAccess')
3195
            ->with($this->equalTo('historyCompareRevisions'))
3196
            ->will($this->returnValue(true));
3197
3198
        $this->admin->expects($this->once())
3199
            ->method('getObject')
3200
            ->will($this->returnValue(false));
3201
3202
        $this->controller->historyCompareRevisionsAction(null, null, null, $this->request);
3203
    }
3204
3205
    public function testHistoryCompareRevisionsActionNoReader(): void
3206
    {
3207
        $this->expectException(NotFoundHttpException::class, 'unable to find the audit reader for class : Foo');
3208
3209
        $this->request->query->set('id', 123);
3210
3211
        $this->admin->expects($this->once())
3212
            ->method('checkAccess')
3213
            ->with($this->equalTo('historyCompareRevisions'))
3214
            ->will($this->returnValue(true));
3215
3216
        $object = new \stdClass();
3217
3218
        $this->admin->expects($this->once())
3219
            ->method('getObject')
3220
            ->will($this->returnValue($object));
3221
3222
        $this->admin->expects($this->any())
3223
            ->method('getClass')
3224
            ->will($this->returnValue('Foo'));
3225
3226
        $this->auditManager->expects($this->once())
3227
            ->method('hasReader')
3228
            ->with($this->equalTo('Foo'))
3229
            ->will($this->returnValue(false));
3230
3231
        $this->controller->historyCompareRevisionsAction(null, null, null, $this->request);
3232
    }
3233
3234
    public function testHistoryCompareRevisionsActionNotFoundBaseRevision(): void
3235
    {
3236
        $this->expectException(NotFoundHttpException::class, 'unable to find the targeted object `123` from the revision `456` with classname : `Foo`');
3237
3238
        $this->request->query->set('id', 123);
3239
3240
        $this->admin->expects($this->once())
3241
            ->method('checkAccess')
3242
            ->with($this->equalTo('historyCompareRevisions'))
3243
            ->will($this->returnValue(true));
3244
3245
        $object = new \stdClass();
3246
3247
        $this->admin->expects($this->once())
3248
            ->method('getObject')
3249
            ->will($this->returnValue($object));
3250
3251
        $this->admin->expects($this->any())
3252
            ->method('getClass')
3253
            ->will($this->returnValue('Foo'));
3254
3255
        $this->auditManager->expects($this->once())
3256
            ->method('hasReader')
3257
            ->with($this->equalTo('Foo'))
3258
            ->will($this->returnValue(true));
3259
3260
        $reader = $this->createMock(AuditReaderInterface::class);
3261
3262
        $this->auditManager->expects($this->once())
3263
            ->method('getReader')
3264
            ->with($this->equalTo('Foo'))
3265
            ->will($this->returnValue($reader));
3266
3267
        // once because it will not be found and therefore the second call won't be executed
3268
        $reader->expects($this->once())
3269
            ->method('find')
3270
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3271
            ->will($this->returnValue(null));
3272
3273
        $this->controller->historyCompareRevisionsAction(123, 456, 789, $this->request);
3274
    }
3275
3276
    public function testHistoryCompareRevisionsActionNotFoundCompareRevision(): void
3277
    {
3278
        $this->expectException(NotFoundHttpException::class, 'unable to find the targeted object `123` from the revision `789` with classname : `Foo`');
3279
3280
        $this->request->query->set('id', 123);
3281
3282
        $this->admin->expects($this->once())
3283
            ->method('checkAccess')
3284
            ->with($this->equalTo('historyCompareRevisions'))
3285
            ->will($this->returnValue(true));
3286
3287
        $object = new \stdClass();
3288
3289
        $this->admin->expects($this->once())
3290
            ->method('getObject')
3291
            ->will($this->returnValue($object));
3292
3293
        $this->admin->expects($this->any())
3294
            ->method('getClass')
3295
            ->will($this->returnValue('Foo'));
3296
3297
        $this->auditManager->expects($this->once())
3298
            ->method('hasReader')
3299
            ->with($this->equalTo('Foo'))
3300
            ->will($this->returnValue(true));
3301
3302
        $reader = $this->createMock(AuditReaderInterface::class);
3303
3304
        $this->auditManager->expects($this->once())
3305
            ->method('getReader')
3306
            ->with($this->equalTo('Foo'))
3307
            ->will($this->returnValue($reader));
3308
3309
        $objectRevision = new \stdClass();
3310
        $objectRevision->revision = 456;
3311
3312
        // first call should return, so the second call will throw an exception
3313
        $reader->expects($this->at(0))
3314
            ->method('find')
3315
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3316
            ->will($this->returnValue($objectRevision));
3317
3318
        $reader->expects($this->at(1))
3319
            ->method('find')
3320
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(789))
3321
            ->will($this->returnValue(null));
3322
3323
        $this->controller->historyCompareRevisionsAction(123, 456, 789, $this->request);
3324
    }
3325
3326
    public function testHistoryCompareRevisionsActionAction(): void
3327
    {
3328
        $this->request->query->set('id', 123);
3329
3330
        $this->admin->expects($this->once())
3331
            ->method('checkAccess')
3332
            ->with($this->equalTo('historyCompareRevisions'))
3333
            ->will($this->returnValue(true));
3334
3335
        $object = new \stdClass();
3336
3337
        $this->admin->expects($this->once())
3338
            ->method('getObject')
3339
            ->will($this->returnValue($object));
3340
3341
        $this->admin->expects($this->any())
3342
            ->method('getClass')
3343
            ->will($this->returnValue('Foo'));
3344
3345
        $this->auditManager->expects($this->once())
3346
            ->method('hasReader')
3347
            ->with($this->equalTo('Foo'))
3348
            ->will($this->returnValue(true));
3349
3350
        $reader = $this->createMock(AuditReaderInterface::class);
3351
3352
        $this->auditManager->expects($this->once())
3353
            ->method('getReader')
3354
            ->with($this->equalTo('Foo'))
3355
            ->will($this->returnValue($reader));
3356
3357
        $objectRevision = new \stdClass();
3358
        $objectRevision->revision = 456;
3359
3360
        $compareObjectRevision = new \stdClass();
3361
        $compareObjectRevision->revision = 789;
3362
3363
        $reader->expects($this->at(0))
3364
            ->method('find')
3365
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3366
            ->will($this->returnValue($objectRevision));
3367
3368
        $reader->expects($this->at(1))
3369
            ->method('find')
3370
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(789))
3371
            ->will($this->returnValue($compareObjectRevision));
3372
3373
        $this->admin->expects($this->once())
3374
            ->method('setSubject')
3375
            ->with($this->equalTo($objectRevision))
3376
            ->will($this->returnValue(null));
3377
3378
        $fieldDescriptionCollection = new FieldDescriptionCollection();
3379
        $this->admin->expects($this->once())
3380
            ->method('getShow')
3381
            ->will($this->returnValue($fieldDescriptionCollection));
3382
3383
        $this->assertInstanceOf(Response::class, $this->controller->historyCompareRevisionsAction(123, 456, 789, $this->request));
3384
3385
        $this->assertSame($this->admin, $this->parameters['admin']);
3386
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
3387
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
3388
3389
        $this->assertSame('show', $this->parameters['action']);
3390
        $this->assertSame($objectRevision, $this->parameters['object']);
3391
        $this->assertSame($compareObjectRevision, $this->parameters['object_compare']);
3392
        $this->assertSame($fieldDescriptionCollection, $this->parameters['elements']);
3393
3394
        $this->assertSame([], $this->session->getFlashBag()->all());
3395
        $this->assertSame('@SonataAdmin/CRUD/show_compare.html.twig', $this->template);
3396
    }
3397
3398
    public function testBatchActionWrongMethod(): void
3399
    {
3400
        $this->expectException(NotFoundHttpException::class, 'Invalid request type "GET", POST expected');
3401
3402
        $this->controller->batchAction($this->request);
3403
    }
3404
3405
    public function testBatchActionActionNotDefined(): void
3406
    {
3407
        $this->expectException(\RuntimeException::class, 'The `foo` batch action is not defined');
3408
3409
        $batchActions = [];
3410
3411
        $this->admin->expects($this->once())
3412
            ->method('getBatchActions')
3413
            ->will($this->returnValue($batchActions));
3414
3415
        $this->request->setMethod('POST');
3416
        $this->request->request->set('data', json_encode(['action' => 'foo', 'idx' => ['123', '456'], 'all_elements' => false]));
3417
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3418
3419
        $this->controller->batchAction($this->request);
3420
    }
3421
3422
    public function testBatchActionActionInvalidCsrfToken(): void
3423
    {
3424
        $this->request->setMethod('POST');
3425
        $this->request->request->set('data', json_encode(['action' => 'foo', 'idx' => ['123', '456'], 'all_elements' => false]));
3426
        $this->request->request->set('_sonata_csrf_token', 'CSRF-INVALID');
3427
3428
        try {
3429
            $this->controller->batchAction($this->request);
3430
        } catch (HttpException $e) {
3431
            $this->assertSame('The csrf token is not valid, CSRF attack?', $e->getMessage());
3432
            $this->assertSame(400, $e->getStatusCode());
3433
        }
3434
    }
3435
3436
    public function testBatchActionMethodNotExist(): void
3437
    {
3438
        $this->expectException(\RuntimeException::class, 'A `Sonata\AdminBundle\Controller\CRUDController::batchActionFoo` method must be callable');
3439
3440
        $batchActions = ['foo' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3441
3442
        $this->admin->expects($this->once())
3443
            ->method('getBatchActions')
3444
            ->will($this->returnValue($batchActions));
3445
3446
        $datagrid = $this->createMock(DatagridInterface::class);
3447
        $this->admin->expects($this->once())
3448
            ->method('getDatagrid')
3449
            ->will($this->returnValue($datagrid));
3450
3451
        $this->request->setMethod('POST');
3452
        $this->request->request->set('data', json_encode(['action' => 'foo', 'idx' => ['123', '456'], 'all_elements' => false]));
3453
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3454
3455
        $this->controller->batchAction($this->request);
3456
    }
3457
3458
    public function testBatchActionWithoutConfirmation(): void
3459
    {
3460
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3461
3462
        $this->admin->expects($this->once())
3463
            ->method('getBatchActions')
3464
            ->will($this->returnValue($batchActions));
3465
3466
        $datagrid = $this->createMock(DatagridInterface::class);
3467
3468
        $query = $this->createMock(ProxyQueryInterface::class);
3469
        $datagrid->expects($this->once())
3470
            ->method('getQuery')
3471
            ->will($this->returnValue($query));
3472
3473
        $this->admin->expects($this->once())
3474
            ->method('getDatagrid')
3475
            ->will($this->returnValue($datagrid));
3476
3477
        $modelManager = $this->createMock(ModelManagerInterface::class);
3478
3479
        $this->admin->expects($this->once())
3480
            ->method('checkAccess')
3481
            ->with($this->equalTo('batchDelete'))
3482
            ->will($this->returnValue(true));
3483
3484
        $this->admin->expects($this->any())
3485
            ->method('getModelManager')
3486
            ->will($this->returnValue($modelManager));
3487
3488
        $this->admin->expects($this->any())
3489
            ->method('getClass')
3490
            ->will($this->returnValue('Foo'));
3491
3492
        $modelManager->expects($this->once())
3493
            ->method('addIdentifiersToQuery')
3494
            ->with($this->equalTo('Foo'), $this->equalTo($query), $this->equalTo(['123', '456']))
3495
            ->will($this->returnValue(true));
3496
3497
        $this->expectTranslate('flash_batch_delete_success', [], 'SonataAdminBundle');
3498
3499
        $this->request->setMethod('POST');
3500
        $this->request->request->set('data', json_encode(['action' => 'delete', 'idx' => ['123', '456'], 'all_elements' => false]));
3501
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3502
3503
        $result = $this->controller->batchAction($this->request);
3504
3505
        $this->assertInstanceOf(RedirectResponse::class, $result);
3506
        $this->assertSame(['flash_batch_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
3507
        $this->assertSame('list', $result->getTargetUrl());
3508
    }
3509
3510
    public function testBatchActionWithoutConfirmation2(): void
3511
    {
3512
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3513
3514
        $this->admin->expects($this->once())
3515
            ->method('getBatchActions')
3516
            ->will($this->returnValue($batchActions));
3517
3518
        $datagrid = $this->createMock(DatagridInterface::class);
3519
3520
        $query = $this->createMock(ProxyQueryInterface::class);
3521
        $datagrid->expects($this->once())
3522
            ->method('getQuery')
3523
            ->will($this->returnValue($query));
3524
3525
        $this->admin->expects($this->once())
3526
            ->method('getDatagrid')
3527
            ->will($this->returnValue($datagrid));
3528
3529
        $modelManager = $this->createMock(ModelManagerInterface::class);
3530
3531
        $this->admin->expects($this->once())
3532
            ->method('checkAccess')
3533
            ->with($this->equalTo('batchDelete'))
3534
            ->will($this->returnValue(true));
3535
3536
        $this->admin->expects($this->any())
3537
            ->method('getModelManager')
3538
            ->will($this->returnValue($modelManager));
3539
3540
        $this->admin->expects($this->any())
3541
            ->method('getClass')
3542
            ->will($this->returnValue('Foo'));
3543
3544
        $modelManager->expects($this->once())
3545
            ->method('addIdentifiersToQuery')
3546
            ->with($this->equalTo('Foo'), $this->equalTo($query), $this->equalTo(['123', '456']))
3547
            ->will($this->returnValue(true));
3548
3549
        $this->expectTranslate('flash_batch_delete_success', [], 'SonataAdminBundle');
3550
3551
        $this->request->setMethod('POST');
3552
        $this->request->request->set('action', 'delete');
3553
        $this->request->request->set('idx', ['123', '456']);
3554
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3555
3556
        $result = $this->controller->batchAction($this->request);
3557
3558
        $this->assertInstanceOf(RedirectResponse::class, $result);
3559
        $this->assertSame(['flash_batch_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
3560
        $this->assertSame('list', $result->getTargetUrl());
3561
    }
3562
3563
    public function testBatchActionWithConfirmation(): void
3564
    {
3565
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'translation_domain' => 'FooBarBaz', 'ask_confirmation' => true]];
3566
3567
        $this->admin->expects($this->once())
3568
            ->method('getBatchActions')
3569
            ->will($this->returnValue($batchActions));
3570
3571
        $data = ['action' => 'delete', 'idx' => ['123', '456'], 'all_elements' => false];
3572
3573
        $this->request->setMethod('POST');
3574
        $this->request->request->set('data', json_encode($data));
3575
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3576
3577
        $datagrid = $this->createMock(DatagridInterface::class);
3578
3579
        $this->admin->expects($this->once())
3580
            ->method('getDatagrid')
3581
            ->will($this->returnValue($datagrid));
3582
3583
        $form = $this->getMockBuilder(Form::class)
3584
            ->disableOriginalConstructor()
3585
            ->getMock();
3586
3587
        $form->expects($this->once())
3588
            ->method('createView')
3589
            ->will($this->returnValue($this->createMock(FormView::class)));
3590
3591
        $datagrid->expects($this->once())
3592
            ->method('getForm')
3593
            ->will($this->returnValue($form));
3594
3595
        $this->assertInstanceOf(Response::class, $this->controller->batchAction($this->request));
3596
3597
        $this->assertSame($this->admin, $this->parameters['admin']);
3598
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
3599
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
3600
3601
        $this->assertSame('list', $this->parameters['action']);
3602
        $this->assertSame($datagrid, $this->parameters['datagrid']);
3603
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
3604
        $this->assertSame($data, $this->parameters['data']);
3605
        $this->assertSame('csrf-token-123_sonata.batch', $this->parameters['csrf_token']);
3606
        $this->assertSame('Foo Bar', $this->parameters['action_label']);
3607
3608
        $this->assertSame([], $this->session->getFlashBag()->all());
3609
        $this->assertSame('@SonataAdmin/CRUD/batch_confirmation.html.twig', $this->template);
3610
    }
3611
3612
    public function testBatchActionNonRelevantAction(): void
3613
    {
3614
        $controller = new BatchAdminController();
3615
        $controller->setContainer($this->container);
3616
3617
        $batchActions = ['foo' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3618
3619
        $this->admin->expects($this->once())
3620
            ->method('getBatchActions')
3621
            ->will($this->returnValue($batchActions));
3622
3623
        $datagrid = $this->createMock(DatagridInterface::class);
3624
3625
        $this->admin->expects($this->once())
3626
            ->method('getDatagrid')
3627
            ->will($this->returnValue($datagrid));
3628
3629
        $this->expectTranslate('flash_batch_empty', [], 'SonataAdminBundle');
3630
3631
        $this->request->setMethod('POST');
3632
        $this->request->request->set('action', 'foo');
3633
        $this->request->request->set('idx', ['789']);
3634
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3635
3636
        $result = $controller->batchAction($this->request);
3637
3638
        $this->assertInstanceOf(RedirectResponse::class, $result);
3639
        $this->assertSame(['flash_batch_empty'], $this->session->getFlashBag()->get('sonata_flash_info'));
3640
        $this->assertSame('list', $result->getTargetUrl());
3641
    }
3642
3643
    public function testBatchActionWithCustomConfirmationTemplate(): void
3644
    {
3645
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => true, 'template' => 'custom_template.html.twig']];
3646
3647
        $this->admin->expects($this->once())
3648
            ->method('getBatchActions')
3649
            ->will($this->returnValue($batchActions));
3650
3651
        $data = ['action' => 'delete', 'idx' => ['123', '456'], 'all_elements' => false];
3652
3653
        $this->request->setMethod('POST');
3654
        $this->request->request->set('data', json_encode($data));
3655
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3656
3657
        $datagrid = $this->createMock(DatagridInterface::class);
3658
3659
        $this->admin->expects($this->once())
3660
            ->method('getDatagrid')
3661
            ->will($this->returnValue($datagrid));
3662
3663
        $form = $this->createMock(Form::class);
3664
3665
        $form->expects($this->once())
3666
            ->method('createView')
3667
            ->will($this->returnValue($this->createMock(FormView::class)));
3668
3669
        $datagrid->expects($this->once())
3670
            ->method('getForm')
3671
            ->will($this->returnValue($form));
3672
3673
        $this->controller->batchAction($this->request);
3674
3675
        $this->assertSame('custom_template.html.twig', $this->template);
3676
    }
3677
3678
    public function testBatchActionNonRelevantAction2(): void
3679
    {
3680
        $controller = new BatchAdminController();
3681
        $controller->setContainer($this->container);
3682
3683
        $batchActions = ['foo' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3684
3685
        $this->admin->expects($this->once())
3686
            ->method('getBatchActions')
3687
            ->will($this->returnValue($batchActions));
3688
3689
        $datagrid = $this->createMock(DatagridInterface::class);
3690
3691
        $this->admin->expects($this->once())
3692
            ->method('getDatagrid')
3693
            ->will($this->returnValue($datagrid));
3694
3695
        $this->expectTranslate('flash_foo_error', [], 'SonataAdminBundle');
3696
3697
        $this->request->setMethod('POST');
3698
        $this->request->request->set('action', 'foo');
3699
        $this->request->request->set('idx', ['999']);
3700
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3701
3702
        $result = $controller->batchAction($this->request);
3703
3704
        $this->assertInstanceOf(RedirectResponse::class, $result);
3705
        $this->assertSame(['flash_foo_error'], $this->session->getFlashBag()->get('sonata_flash_info'));
3706
        $this->assertSame('list', $result->getTargetUrl());
3707
    }
3708
3709
    public function testBatchActionNoItems(): void
3710
    {
3711
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => true]];
3712
3713
        $this->admin->expects($this->once())
3714
            ->method('getBatchActions')
3715
            ->will($this->returnValue($batchActions));
3716
3717
        $datagrid = $this->createMock(DatagridInterface::class);
3718
3719
        $this->admin->expects($this->once())
3720
            ->method('getDatagrid')
3721
            ->will($this->returnValue($datagrid));
3722
3723
        $this->expectTranslate('flash_batch_empty', [], 'SonataAdminBundle');
3724
3725
        $this->request->setMethod('POST');
3726
        $this->request->request->set('action', 'delete');
3727
        $this->request->request->set('idx', []);
3728
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3729
3730
        $result = $this->controller->batchAction($this->request);
3731
3732
        $this->assertInstanceOf(RedirectResponse::class, $result);
3733
        $this->assertSame(['flash_batch_empty'], $this->session->getFlashBag()->get('sonata_flash_info'));
3734
        $this->assertSame('list', $result->getTargetUrl());
3735
    }
3736
3737
    public function testBatchActionNoItemsEmptyQuery(): void
3738
    {
3739
        $controller = new BatchAdminController();
3740
        $controller->setContainer($this->container);
3741
3742
        $batchActions = ['bar' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3743
3744
        $this->admin->expects($this->once())
3745
            ->method('getBatchActions')
3746
            ->will($this->returnValue($batchActions));
3747
3748
        $datagrid = $this->createMock(DatagridInterface::class);
3749
3750
        $query = $this->createMock(ProxyQueryInterface::class);
3751
        $datagrid->expects($this->once())
3752
            ->method('getQuery')
3753
            ->will($this->returnValue($query));
3754
3755
        $this->admin->expects($this->once())
3756
            ->method('getDatagrid')
3757
            ->will($this->returnValue($datagrid));
3758
3759
        $modelManager = $this->createMock(ModelManagerInterface::class);
3760
3761
        $this->admin->expects($this->any())
3762
            ->method('getModelManager')
3763
            ->will($this->returnValue($modelManager));
3764
3765
        $this->admin->expects($this->any())
3766
            ->method('getClass')
3767
            ->will($this->returnValue('Foo'));
3768
3769
        $this->request->setMethod('POST');
3770
        $this->request->request->set('action', 'bar');
3771
        $this->request->request->set('idx', []);
3772
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3773
3774
        $this->expectTranslate('flash_batch_no_elements_processed', [], 'SonataAdminBundle');
3775
        $result = $controller->batchAction($this->request);
3776
3777
        $this->assertInstanceOf(Response::class, $result);
3778
        $this->assertRegExp('/Redirecting to list/', $result->getContent());
3779
    }
3780
3781
    public function testBatchActionWithRequesData(): void
3782
    {
3783
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3784
3785
        $this->admin->expects($this->once())
3786
            ->method('getBatchActions')
3787
            ->will($this->returnValue($batchActions));
3788
3789
        $datagrid = $this->createMock(DatagridInterface::class);
3790
3791
        $query = $this->createMock(ProxyQueryInterface::class);
3792
        $datagrid->expects($this->once())
3793
            ->method('getQuery')
3794
            ->will($this->returnValue($query));
3795
3796
        $this->admin->expects($this->once())
3797
            ->method('getDatagrid')
3798
            ->will($this->returnValue($datagrid));
3799
3800
        $modelManager = $this->createMock(ModelManagerInterface::class);
3801
3802
        $this->admin->expects($this->once())
3803
            ->method('checkAccess')
3804
            ->with($this->equalTo('batchDelete'))
3805
            ->will($this->returnValue(true));
3806
3807
        $this->admin->expects($this->any())
3808
            ->method('getModelManager')
3809
            ->will($this->returnValue($modelManager));
3810
3811
        $this->admin->expects($this->any())
3812
            ->method('getClass')
3813
            ->will($this->returnValue('Foo'));
3814
3815
        $modelManager->expects($this->once())
3816
            ->method('addIdentifiersToQuery')
3817
            ->with($this->equalTo('Foo'), $this->equalTo($query), $this->equalTo(['123', '456']))
3818
            ->will($this->returnValue(true));
3819
3820
        $this->expectTranslate('flash_batch_delete_success', [], 'SonataAdminBundle');
3821
3822
        $this->request->setMethod('POST');
3823
        $this->request->request->set('data', json_encode(['action' => 'delete', 'idx' => ['123', '456'], 'all_elements' => false]));
3824
        $this->request->request->set('foo', 'bar');
3825
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3826
3827
        $result = $this->controller->batchAction($this->request);
3828
3829
        $this->assertInstanceOf(RedirectResponse::class, $result);
3830
        $this->assertSame(['flash_batch_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
3831
        $this->assertSame('list', $result->getTargetUrl());
3832
        $this->assertSame('bar', $this->request->request->get('foo'));
3833
    }
3834
3835
    public function testItThrowsWhenCallingAnUndefinedMethod(): void
3836
    {
3837
        $this->expectException(
3838
            \LogicException::class
3839
        );
3840
        $this->expectExceptionMessage(
3841
            'Call to undefined method Sonata\AdminBundle\Controller\CRUDController::doesNotExist'
3842
        );
3843
        $this->controller->doesNotExist();
3844
    }
3845
3846
    /**
3847
     * @expectedDeprecation Method Sonata\AdminBundle\Controller\CRUDController::render has been renamed to Sonata\AdminBundle\Controller\CRUDController::renderWithExtraParams.
3848
     */
3849
    public function testRenderIsDeprecated(): void
3850
    {
3851
        $this->controller->render('toto.html.twig');
3852
    }
3853
3854
    public function getCsrfProvider()
3855
    {
3856
        return $this->csrfProvider;
3857
    }
3858
3859
    public function getToStringValues()
3860
    {
3861
        return [
3862
            ['', ''],
3863
            ['Foo', 'Foo'],
3864
            ['&lt;a href=&quot;http://foo&quot;&gt;Bar&lt;/a&gt;', '<a href="http://foo">Bar</a>'],
3865
            ['&lt;&gt;&amp;&quot;&#039;abcdefghijklmnopqrstuvwxyz*-+.,?_()[]\/', '<>&"\'abcdefghijklmnopqrstuvwxyz*-+.,?_()[]\/'],
3866
        ];
3867
    }
3868
3869
    private function assertLoggerLogsModelManagerException($subject, string $method): void
3870
    {
3871
        $exception = new ModelManagerException(
3872
            $message = 'message',
3873
            1234,
3874
            new \Exception($previousExceptionMessage = 'very useful message')
3875
        );
3876
3877
        $subject->expects($this->once())
3878
            ->method($method)
3879
            ->will($this->returnCallback(static function () use ($exception): void {
3880
                throw $exception;
3881
            }));
3882
3883
        $this->logger->expects($this->once())
3884
            ->method('error')
3885
            ->with($message, [
3886
                'exception' => $exception,
3887
                'previous_exception_message' => $previousExceptionMessage,
3888
            ]);
3889
    }
3890
3891
    private function expectTranslate(
3892
        string $id,
3893
        array $parameters = [],
3894
        ?string $domain = null,
3895
        ?string $locale = null
3896
    ): void {
3897
        $this->translator->expects($this->once())
3898
            ->method('trans')
3899
            ->with($this->equalTo($id), $this->equalTo($parameters), $this->equalTo($domain), $this->equalTo($locale))
3900
            ->will($this->returnValue($id));
3901
    }
3902
}
3903