Completed
Push — 3.x ( 3e834f...38b337 )
by Grégoire
03:36
created

tests/Controller/CRUDControllerTest.php (3 issues)

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\MockObject\MockObject;
17
use PHPUnit\Framework\TestCase;
18
use Psr\Log\LoggerInterface;
19
use Sonata\AdminBundle\Admin\AbstractAdmin;
20
use Sonata\AdminBundle\Admin\BreadcrumbsBuilder;
21
use Sonata\AdminBundle\Admin\FieldDescriptionCollection;
22
use Sonata\AdminBundle\Admin\Pool;
23
use Sonata\AdminBundle\Controller\CRUDController;
24
use Sonata\AdminBundle\Datagrid\DatagridInterface;
25
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
26
use Sonata\AdminBundle\Exception\LockException;
27
use Sonata\AdminBundle\Exception\ModelManagerException;
28
use Sonata\AdminBundle\Export\Exporter as SonataExporter;
29
use Sonata\AdminBundle\Model\AuditManager;
30
use Sonata\AdminBundle\Model\AuditReaderInterface;
31
use Sonata\AdminBundle\Model\ModelManagerInterface;
32
use Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap;
33
use Sonata\AdminBundle\Security\Handler\AclSecurityHandler;
34
use Sonata\AdminBundle\Templating\TemplateRegistryInterface;
35
use Sonata\AdminBundle\Tests\Fixtures\Admin\PostAdmin;
36
use Sonata\AdminBundle\Tests\Fixtures\Controller\BatchAdminController;
37
use Sonata\AdminBundle\Tests\Fixtures\Controller\PreCRUDController;
38
use Sonata\AdminBundle\Util\AdminObjectAclData;
39
use Sonata\AdminBundle\Util\AdminObjectAclManipulator;
40
use Sonata\Exporter\Exporter;
41
use Sonata\Exporter\Source\SourceIteratorInterface;
42
use Sonata\Exporter\Writer\JsonWriter;
43
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
44
use Symfony\Bundle\FrameworkBundle\Templating\DelegatingEngine;
45
use Symfony\Component\DependencyInjection\Container;
46
use Symfony\Component\DependencyInjection\ContainerInterface;
47
use Symfony\Component\Form\Form;
48
use Symfony\Component\Form\FormError;
49
use Symfony\Component\Form\FormRenderer;
50
use Symfony\Component\Form\FormView;
51
use Symfony\Component\HttpFoundation\JsonResponse;
52
use Symfony\Component\HttpFoundation\RedirectResponse;
53
use Symfony\Component\HttpFoundation\Request;
54
use Symfony\Component\HttpFoundation\RequestStack;
55
use Symfony\Component\HttpFoundation\Response;
56
use Symfony\Component\HttpFoundation\Session\Session;
57
use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
58
use Symfony\Component\HttpFoundation\StreamedResponse;
59
use Symfony\Component\HttpKernel\Exception\HttpException;
60
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
61
use Symfony\Component\HttpKernel\KernelInterface;
62
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
63
use Symfony\Component\Security\Csrf\CsrfToken;
64
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
65
use Symfony\Contracts\Translation\TranslatorInterface;
66
use Twig\Environment;
67
68
/**
69
 * Test for CRUDController.
70
 *
71
 * @author Andrej Hudec <[email protected]>
72
 *
73
 * @group legacy
74
 */
75
class CRUDControllerTest extends TestCase
76
{
77
    /**
78
     * @var CRUDController
79
     */
80
    private $controller;
81
82
    /**
83
     * @var Request
84
     */
85
    private $request;
86
87
    /**
88
     * @var AbstractAdmin
89
     */
90
    private $admin;
91
92
    /**
93
     * @var TemplateRegistryInterface
94
     */
95
    private $templateRegistry;
96
97
    /**
98
     * @var Pool
99
     */
100
    private $pool;
101
102
    /**
103
     * @var array
104
     */
105
    private $parameters;
106
107
    /**
108
     * @var Session
109
     */
110
    private $session;
111
112
    /**
113
     * @var AuditManager
114
     */
115
    private $auditManager;
116
117
    /**
118
     * @var ContainerInterface
119
     */
120
    private $container;
121
122
    /**
123
     * @var AdminObjectAclManipulator
124
     */
125
    private $adminObjectAclManipulator;
126
127
    /**
128
     * @var string
129
     */
130
    private $template;
131
132
    /**
133
     * @var array
134
     */
135
    private $protectedTestedMethods;
136
137
    /**
138
     * @var CsrfTokenManagerInterface
139
     */
140
    private $csrfProvider;
141
142
    /**
143
     * @var KernelInterface
144
     */
145
    private $kernel;
146
147
    /**
148
     * @var TranslatorInterface
149
     */
150
    private $translator;
151
152
    /**
153
     * @var LoggerInterface|MockObject
154
     */
155
    private $logger;
156
157
    /**
158
     * {@inheritdoc}
159
     */
160
    protected function setUp(): void
161
    {
162
        $this->container = new Container();
163
        $this->request = new Request();
164
        $this->pool = new Pool($this->container, 'title', 'logo.png');
165
        $this->pool->setAdminServiceIds(['foo.admin']);
166
        $this->request->attributes->set('_sonata_admin', 'foo.admin');
167
        $this->admin = $this->getMockBuilder(AbstractAdmin::class)
168
            ->disableOriginalConstructor()
169
            ->getMock();
170
        $this->translator = $this->createMock(TranslatorInterface::class);
171
        $this->parameters = [];
172
        $this->template = '';
173
174
        $this->templateRegistry = $this->prophesize(TemplateRegistryInterface::class);
175
176
        $templating = $this->createMock(DelegatingEngine::class);
177
178
        $templatingRenderReturnCallback = $this->returnCallback(function (
179
            $view,
180
            array $parameters = [],
181
            ?Response $response = null
182
        ) {
183
            $this->template = $view;
184
185
            if (null === $response) {
186
                $response = new Response();
187
            }
188
189
            $this->parameters = $parameters;
190
191
            return $response;
192
        });
193
194
        $templating
195
            ->method('render')
196
            ->will($templatingRenderReturnCallback);
197
198
        $this->session = new Session(new MockArraySessionStorage());
199
200
        $twig = $this->getMockBuilder(Environment::class)
201
            ->disableOriginalConstructor()
202
            ->getMock();
203
204
        $twig
205
            ->method('getRuntime')
206
            ->willReturn($this->createMock(FormRenderer::class));
207
208
        // NEXT_MAJOR : require sonata/exporter ^1.7 and remove conditional
209
        if (class_exists(Exporter::class)) {
210
            $exporter = new Exporter([new JsonWriter('/tmp/sonataadmin/export.json')]);
211
        } else {
212
            $exporter = $this->createMock(SonataExporter::class);
213
214
            $exporter
215
                ->method('getResponse')
216
                ->willReturn(new StreamedResponse());
217
        }
218
219
        $this->auditManager = $this->getMockBuilder(AuditManager::class)
220
            ->disableOriginalConstructor()
221
            ->getMock();
222
223
        $this->adminObjectAclManipulator = $this->getMockBuilder(AdminObjectAclManipulator::class)
224
            ->disableOriginalConstructor()
225
            ->getMock();
226
227
        $this->csrfProvider = $this->getMockBuilder(CsrfTokenManagerInterface::class)
228
            ->getMock();
229
230
        $this->csrfProvider
231
            ->method('getToken')
232
            ->willReturnCallback(static function (string $intention): CsrfToken {
233
                return new CsrfToken($intention, 'csrf-token-123_'.$intention);
234
            });
235
236
        $this->csrfProvider
237
            ->method('isTokenValid')
238
            ->willReturnCallback(static function (CsrfToken $token): bool {
239
                return $token->getValue() === 'csrf-token-123_'.$token->getId();
240
            });
241
242
        $this->logger = $this->createMock(LoggerInterface::class);
243
244
        $requestStack = new RequestStack();
245
        $requestStack->push($this->request);
246
247
        $this->kernel = $this->createMock(KernelInterface::class);
248
249
        $this->container->set('sonata.admin.pool', $this->pool);
250
        $this->container->set('request_stack', $requestStack);
251
        $this->container->set('foo.admin', $this->admin);
252
        $this->container->set('foo.admin.template_registry', $this->templateRegistry->reveal());
253
        $this->container->set('templating', $templating);
254
        $this->container->set('twig', $twig);
255
        $this->container->set('session', $this->session);
256
        $this->container->set('sonata.admin.exporter', $exporter);
257
        $this->container->set('sonata.admin.audit.manager', $this->auditManager);
258
        $this->container->set('sonata.admin.object.manipulator.acl.admin', $this->adminObjectAclManipulator);
259
        $this->container->set('security.csrf.token_manager', $this->csrfProvider);
260
        $this->container->set('logger', $this->logger);
261
        $this->container->set('kernel', $this->kernel);
262
        $this->container->set('translator', $this->translator);
263
        $this->container->set('sonata.admin.breadcrumbs_builder', new BreadcrumbsBuilder([]));
264
265
        $this->container->setParameter(
266
            'security.role_hierarchy.roles',
267
            ['ROLE_SUPER_ADMIN' => ['ROLE_USER', 'ROLE_SONATA_ADMIN', 'ROLE_ADMIN']]
268
        );
269
        $this->container->setParameter('sonata.admin.security.acl_user_manager', null);
270
271
        $this->templateRegistry->getTemplate('ajax')->willReturn('@SonataAdmin/ajax_layout.html.twig');
272
        $this->templateRegistry->getTemplate('layout')->willReturn('@SonataAdmin/standard_layout.html.twig');
273
        $this->templateRegistry->getTemplate('show')->willReturn('@SonataAdmin/CRUD/show.html.twig');
274
        $this->templateRegistry->getTemplate('show_compare')->willReturn('@SonataAdmin/CRUD/show_compare.html.twig');
275
        $this->templateRegistry->getTemplate('edit')->willReturn('@SonataAdmin/CRUD/edit.html.twig');
276
        $this->templateRegistry->getTemplate('dashboard')->willReturn('@SonataAdmin/Core/dashboard.html.twig');
277
        $this->templateRegistry->getTemplate('search')->willReturn('@SonataAdmin/Core/search.html.twig');
278
        $this->templateRegistry->getTemplate('list')->willReturn('@SonataAdmin/CRUD/list.html.twig');
279
        $this->templateRegistry->getTemplate('preview')->willReturn('@SonataAdmin/CRUD/preview.html.twig');
280
        $this->templateRegistry->getTemplate('history')->willReturn('@SonataAdmin/CRUD/history.html.twig');
281
        $this->templateRegistry->getTemplate('acl')->willReturn('@SonataAdmin/CRUD/acl.html.twig');
282
        $this->templateRegistry->getTemplate('delete')->willReturn('@SonataAdmin/CRUD/delete.html.twig');
283
        $this->templateRegistry->getTemplate('batch')->willReturn('@SonataAdmin/CRUD/list__batch.html.twig');
284
        $this->templateRegistry->getTemplate('batch_confirmation')->willReturn('@SonataAdmin/CRUD/batch_confirmation.html.twig');
285
286
        // NEXT_MAJOR: Remove this call
287
        $this->admin->method('getTemplate')->willReturnMap([
288
            ['ajax', '@SonataAdmin/ajax_layout.html.twig'],
289
            ['layout', '@SonataAdmin/standard_layout.html.twig'],
290
            ['show', '@SonataAdmin/CRUD/show.html.twig'],
291
            ['show_compare', '@SonataAdmin/CRUD/show_compare.html.twig'],
292
            ['edit', '@SonataAdmin/CRUD/edit.html.twig'],
293
            ['dashboard', '@SonataAdmin/Core/dashboard.html.twig'],
294
            ['search', '@SonataAdmin/Core/search.html.twig'],
295
            ['list', '@SonataAdmin/CRUD/list.html.twig'],
296
            ['preview', '@SonataAdmin/CRUD/preview.html.twig'],
297
            ['history', '@SonataAdmin/CRUD/history.html.twig'],
298
            ['acl', '@SonataAdmin/CRUD/acl.html.twig'],
299
            ['delete', '@SonataAdmin/CRUD/delete.html.twig'],
300
            ['batch', '@SonataAdmin/CRUD/list__batch.html.twig'],
301
            ['batch_confirmation', '@SonataAdmin/CRUD/batch_confirmation.html.twig'],
302
        ]);
303
304
        $this->admin
305
            ->method('getIdParameter')
306
            ->willReturn('id');
307
308
        $this->admin
309
            ->method('getAccessMapping')
310
            ->willReturn([]);
311
312
        $this->admin
313
            ->method('generateUrl')
314
            ->willReturnCallback(
315
                static function ($name, array $parameters = []) {
316
                    $result = $name;
317
                    if (!empty($parameters)) {
318
                        $result .= '?'.http_build_query($parameters);
319
                    }
320
321
                    return $result;
322
                }
323
            );
324
325
        $this->admin
326
            ->method('generateObjectUrl')
327
            ->willReturnCallback(
328
                static function (string $name, $object, array $parameters = []): string {
329
                    $result = \get_class($object).'_'.$name;
330
                    if (!empty($parameters)) {
331
                        $result .= '?'.http_build_query($parameters);
332
                    }
333
334
                    return $result;
335
                }
336
            );
337
338
        $this->admin
339
            ->method('getCode')
340
            ->willReturn('foo.admin');
341
342
        $this->controller = new CRUDController();
343
        $this->controller->setContainer($this->container);
344
345
        // Make some methods public to test them
346
        $testedMethods = [
347
            'renderJson',
348
            'isXmlHttpRequest',
349
            'configure',
350
            'getBaseTemplate',
351
            'redirectTo',
352
            'addFlash',
353
        ];
354
        foreach ($testedMethods as $testedMethod) {
355
            // NEXT_MAJOR: Remove this check and only use CRUDController
356
            if (method_exists(CRUDController::class, $testedMethod)) {
357
                $method = new \ReflectionMethod(CRUDController::class, $testedMethod);
358
            } else {
359
                $method = new \ReflectionMethod(Controller::class, $testedMethod);
360
            }
361
362
            $method->setAccessible(true);
363
            $this->protectedTestedMethods[$testedMethod] = $method;
364
        }
365
    }
366
367
    public function testRenderJson1(): void
368
    {
369
        $data = ['example' => '123', 'foo' => 'bar'];
370
371
        $this->request->headers->set('Content-Type', 'application/x-www-form-urlencoded');
372
        $response = $this->protectedTestedMethods['renderJson']->invoke($this->controller, $data, 200, [], $this->request);
373
374
        $this->assertSame($response->headers->get('Content-Type'), 'application/json');
375
        $this->assertSame(json_encode($data), $response->getContent());
376
    }
377
378
    public function testRenderJson2(): void
379
    {
380
        $data = ['example' => '123', 'foo' => 'bar'];
381
382
        $this->request->headers->set('Content-Type', 'multipart/form-data');
383
        $response = $this->protectedTestedMethods['renderJson']->invoke($this->controller, $data, 200, [], $this->request);
384
385
        $this->assertSame($response->headers->get('Content-Type'), 'application/json');
386
        $this->assertSame(json_encode($data), $response->getContent());
387
    }
388
389
    public function testRenderJsonAjax(): void
390
    {
391
        $data = ['example' => '123', 'foo' => 'bar'];
392
393
        $this->request->attributes->set('_xml_http_request', true);
394
        $this->request->headers->set('Content-Type', 'multipart/form-data');
395
        $response = $this->protectedTestedMethods['renderJson']->invoke($this->controller, $data, 200, [], $this->request);
396
397
        $this->assertSame($response->headers->get('Content-Type'), 'application/json');
398
        $this->assertSame(json_encode($data), $response->getContent());
399
    }
400
401
    public function testIsXmlHttpRequest(): void
402
    {
403
        $this->assertFalse($this->protectedTestedMethods['isXmlHttpRequest']->invoke($this->controller, $this->request));
404
405
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
406
407
        $this->assertTrue($this->protectedTestedMethods['isXmlHttpRequest']->invoke($this->controller, $this->request));
408
409
        $this->request->headers->remove('X-Requested-With');
410
        $this->assertFalse($this->protectedTestedMethods['isXmlHttpRequest']->invoke($this->controller, $this->request));
411
412
        $this->request->attributes->set('_xml_http_request', true);
413
        $this->assertTrue($this->protectedTestedMethods['isXmlHttpRequest']->invoke($this->controller, $this->request));
414
    }
415
416
    public function testConfigure(): void
417
    {
418
        $uniqueId = '';
419
420
        $this->admin->expects($this->once())
421
            ->method('setUniqid')
422
            ->willReturnCallback(static function (int $uniqid) use (&$uniqueId): void {
423
                $uniqueId = $uniqid;
424
            });
425
426
        $this->request->query->set('uniqid', 123456);
427
        $this->protectedTestedMethods['configure']->invoke($this->controller);
428
429
        $this->assertSame(123456, $uniqueId);
430
    }
431
432
    public function testConfigureChild(): void
433
    {
434
        $uniqueId = '';
435
436
        $this->admin->expects($this->once())
437
            ->method('setUniqid')
438
            ->willReturnCallback(static function ($uniqid) use (&$uniqueId): void {
439
                $uniqueId = $uniqid;
440
            });
441
442
        $this->admin->expects($this->once())
443
            ->method('isChild')
444
            ->willReturn(true);
445
446
        $adminParent = $this->getMockBuilder(AbstractAdmin::class)
447
            ->disableOriginalConstructor()
448
            ->getMock();
449
        $this->admin->expects($this->once())
450
            ->method('getParent')
451
            ->willReturn($adminParent);
452
453
        $this->request->query->set('uniqid', 123456);
454
        $this->protectedTestedMethods['configure']->invoke($this->controller);
455
456
        $this->assertSame(123456, $uniqueId);
457
    }
458
459
    public function testConfigureWithException(): void
460
    {
461
        $this->expectException(\RuntimeException::class);
462
        $this->expectExceptionMessage(
463
            'There is no `_sonata_admin` defined for the controller `Sonata\AdminBundle\Controller\CRUDController`'
464
        );
465
466
        $this->request->attributes->remove('_sonata_admin');
467
        $this->protectedTestedMethods['configure']->invoke($this->controller);
468
    }
469
470
    public function testConfigureWithException2(): void
471
    {
472
        $this->expectException(\InvalidArgumentException::class);
473
        $this->expectExceptionMessage('You have requested a non-existent service "nonexistent.admin".');
474
475
        $this->pool->setAdminServiceIds(['nonexistent.admin']);
476
        $this->request->attributes->set('_sonata_admin', 'nonexistent.admin');
477
        $this->protectedTestedMethods['configure']->invoke($this->controller);
478
    }
479
480
    public function testGetBaseTemplate(): void
481
    {
482
        $this->assertSame(
483
            '@SonataAdmin/standard_layout.html.twig',
484
            $this->protectedTestedMethods['getBaseTemplate']->invoke($this->controller, $this->request)
485
        );
486
487
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
488
        $this->assertSame(
489
            '@SonataAdmin/ajax_layout.html.twig',
490
            $this->protectedTestedMethods['getBaseTemplate']->invoke($this->controller, $this->request)
491
        );
492
493
        $this->request->headers->remove('X-Requested-With');
494
        $this->assertSame(
495
            '@SonataAdmin/standard_layout.html.twig',
496
            $this->protectedTestedMethods['getBaseTemplate']->invoke($this->controller, $this->request)
497
        );
498
499
        $this->request->attributes->set('_xml_http_request', true);
500
        $this->assertSame(
501
            '@SonataAdmin/ajax_layout.html.twig',
502
            $this->protectedTestedMethods['getBaseTemplate']->invoke($this->controller, $this->request)
503
        );
504
    }
505
506
    public function testRender(): void
507
    {
508
        $this->parameters = [];
509
        $this->assertInstanceOf(
510
            Response::class,
511
            $this->controller->renderWithExtraParams('@FooAdmin/foo.html.twig', [], null)
512
        );
513
        $this->assertSame($this->admin, $this->parameters['admin']);
514
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
515
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
516
        $this->assertSame('@FooAdmin/foo.html.twig', $this->template);
517
    }
518
519
    public function testRenderWithResponse(): void
520
    {
521
        $this->parameters = [];
522
        $response = new Response();
523
        $response->headers->set('X-foo', 'bar');
524
        $responseResult = $this->controller->renderWithExtraParams('@FooAdmin/foo.html.twig', [], $response);
525
526
        $this->assertSame($response, $responseResult);
527
        $this->assertSame('bar', $responseResult->headers->get('X-foo'));
528
        $this->assertSame($this->admin, $this->parameters['admin']);
529
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
530
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
531
        $this->assertSame('@FooAdmin/foo.html.twig', $this->template);
532
    }
533
534
    public function testRenderCustomParams(): void
535
    {
536
        $this->parameters = [];
537
        $this->assertInstanceOf(
538
            Response::class,
539
            $this->controller->renderWithExtraParams(
540
                '@FooAdmin/foo.html.twig',
541
                ['foo' => 'bar'],
542
                null
543
            )
544
        );
545
        $this->assertSame($this->admin, $this->parameters['admin']);
546
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
547
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
548
        $this->assertSame('bar', $this->parameters['foo']);
549
        $this->assertSame('@FooAdmin/foo.html.twig', $this->template);
550
    }
551
552
    public function testRenderAjax(): void
553
    {
554
        $this->parameters = [];
555
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
556
        $this->assertInstanceOf(
557
            Response::class,
558
            $this->controller->renderWithExtraParams(
559
                '@FooAdmin/foo.html.twig',
560
                ['foo' => 'bar'],
561
                null
562
            )
563
        );
564
        $this->assertSame($this->admin, $this->parameters['admin']);
565
        $this->assertSame('@SonataAdmin/ajax_layout.html.twig', $this->parameters['base_template']);
566
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
567
        $this->assertSame('bar', $this->parameters['foo']);
568
        $this->assertSame('@FooAdmin/foo.html.twig', $this->template);
569
    }
570
571
    public function testListActionAccessDenied(): void
572
    {
573
        $this->expectException(AccessDeniedException::class);
574
575
        $this->admin->expects($this->once())
576
            ->method('checkAccess')
577
            ->with($this->equalTo('list'))
578
            ->will($this->throwException(new AccessDeniedException()));
579
580
        $this->controller->listAction();
581
    }
582
583
    public function testPreList(): void
584
    {
585
        $this->admin
586
            ->method('hasRoute')
587
            ->with($this->equalTo('list'))
588
            ->willReturn(true);
589
590
        $this->admin->expects($this->once())
591
            ->method('checkAccess')
592
            ->with($this->equalTo('list'))
593
            ->willReturn(true);
594
595
        $controller = new PreCRUDController();
596
        $controller->setContainer($this->container);
597
598
        $response = $controller->listAction();
599
        $this->assertInstanceOf(Response::class, $response);
600
        $this->assertSame('preList called', $response->getContent());
601
    }
602
603
    public function testListAction(): void
604
    {
605
        $datagrid = $this->createMock(DatagridInterface::class);
606
607
        $this->admin
608
            ->method('hasRoute')
609
            ->with($this->equalTo('list'))
610
            ->willReturn(true);
611
612
        $this->admin->expects($this->once())
613
            ->method('checkAccess')
614
            ->with($this->equalTo('list'))
615
            ->willReturn(true);
616
617
        $form = $this->getMockBuilder(Form::class)
618
            ->disableOriginalConstructor()
619
            ->getMock();
620
621
        $form->expects($this->once())
622
            ->method('createView')
623
            ->willReturn($this->createMock(FormView::class));
624
625
        $this->admin->expects($this->once())
626
            ->method('getDatagrid')
627
            ->willReturn($datagrid);
628
629
        $datagrid->expects($this->once())
630
            ->method('getForm')
631
            ->willReturn($form);
632
633
        $this->parameters = [];
634
        $this->assertInstanceOf(Response::class, $this->controller->listAction());
635
636
        $this->assertSame($this->admin, $this->parameters['admin']);
637
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
638
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
639
640
        $this->assertSame('list', $this->parameters['action']);
641
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
642
        $this->assertInstanceOf(DatagridInterface::class, $this->parameters['datagrid']);
643
        $this->assertSame('csrf-token-123_sonata.batch', $this->parameters['csrf_token']);
644
        $this->assertSame([], $this->session->getFlashBag()->all());
645
        $this->assertSame('@SonataAdmin/CRUD/list.html.twig', $this->template);
646
    }
647
648
    public function testBatchActionDeleteAccessDenied(): void
649
    {
650
        $this->expectException(AccessDeniedException::class);
651
652
        $this->admin->expects($this->once())
653
            ->method('checkAccess')
654
            ->with($this->equalTo('batchDelete'))
655
            ->will($this->throwException(new AccessDeniedException()));
656
657
        $this->controller->batchActionDelete($this->createMock(ProxyQueryInterface::class));
658
    }
659
660
    public function testBatchActionDelete(): void
661
    {
662
        $modelManager = $this->createMock(ModelManagerInterface::class);
663
664
        $this->admin->expects($this->once())
665
            ->method('checkAccess')
666
            ->with($this->equalTo('batchDelete'))
667
            ->willReturn(true);
668
669
        $this->admin->expects($this->once())
670
            ->method('getModelManager')
671
            ->willReturn($modelManager);
672
673
        $this->admin->expects($this->once())
674
            ->method('getFilterParameters')
675
            ->willReturn(['foo' => 'bar']);
676
677
        $this->expectTranslate('flash_batch_delete_success', [], 'SonataAdminBundle');
678
679
        $result = $this->controller->batchActionDelete($this->createMock(ProxyQueryInterface::class));
680
681
        $this->assertInstanceOf(RedirectResponse::class, $result);
682
        $this->assertSame(['flash_batch_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
683
        $this->assertSame('list?filter%5Bfoo%5D=bar', $result->getTargetUrl());
684
    }
685
686
    public function testBatchActionDeleteWithModelManagerException(): void
687
    {
688
        $modelManager = $this->createMock(ModelManagerInterface::class);
689
        $this->assertLoggerLogsModelManagerException($modelManager, 'batchDelete');
690
691
        $this->admin->expects($this->once())
692
            ->method('getModelManager')
693
            ->willReturn($modelManager);
694
695
        $this->admin->expects($this->once())
696
            ->method('getFilterParameters')
697
            ->willReturn(['foo' => 'bar']);
698
699
        $this->expectTranslate('flash_batch_delete_error', [], 'SonataAdminBundle');
700
701
        $result = $this->controller->batchActionDelete($this->createMock(ProxyQueryInterface::class));
702
703
        $this->assertInstanceOf(RedirectResponse::class, $result);
704
        $this->assertSame(['flash_batch_delete_error'], $this->session->getFlashBag()->get('sonata_flash_error'));
705
        $this->assertSame('list?filter%5Bfoo%5D=bar', $result->getTargetUrl());
706
    }
707
708
    public function testBatchActionDeleteWithModelManagerExceptionInDebugMode(): void
709
    {
710
        $modelManager = $this->createMock(ModelManagerInterface::class);
711
        $this->expectException(ModelManagerException::class);
712
713
        $modelManager->expects($this->once())
714
            ->method('batchDelete')
715
            ->willReturnCallback(static function (): void {
716
                throw new ModelManagerException();
717
            });
718
719
        $this->admin->expects($this->once())
720
            ->method('getModelManager')
721
            ->willReturn($modelManager);
722
723
        $this->kernel->expects($this->once())
724
            ->method('isDebug')
725
            ->willReturn(true);
726
727
        $this->controller->batchActionDelete($this->createMock(ProxyQueryInterface::class));
728
    }
729
730
    public function testShowActionNotFoundException(): void
731
    {
732
        $this->expectException(NotFoundHttpException::class);
733
734
        $this->admin->expects($this->once())
735
            ->method('getObject')
736
            ->willReturn(false);
737
738
        $this->controller->showAction(null);
739
    }
740
741
    public function testShowActionAccessDenied(): void
742
    {
743
        $this->expectException(AccessDeniedException::class);
744
745
        $this->admin->expects($this->once())
746
            ->method('getObject')
747
            ->willReturn(new \stdClass());
748
749
        $this->admin->expects($this->once())
750
            ->method('checkAccess')
751
            ->with($this->equalTo('show'))
752
            ->will($this->throwException(new AccessDeniedException()));
753
754
        $this->controller->showAction(null);
755
    }
756
757
    public function testPreShow(): void
758
    {
759
        $object = new \stdClass();
760
        $object->foo = 123456;
761
762
        $this->admin->expects($this->once())
763
            ->method('getObject')
764
            ->willReturn($object);
765
766
        $this->admin->expects($this->once())
767
            ->method('checkAccess')
768
            ->with($this->equalTo('show'))
769
            ->willReturn(true);
770
771
        $controller = new PreCRUDController();
772
        $controller->setContainer($this->container);
773
774
        $response = $controller->showAction(null);
775
        $this->assertInstanceOf(Response::class, $response);
776
        $this->assertSame('preShow called: 123456', $response->getContent());
777
    }
778
779
    public function testShowAction(): void
780
    {
781
        $object = new \stdClass();
782
783
        $this->admin->expects($this->once())
784
            ->method('getObject')
785
            ->willReturn($object);
786
787
        $this->admin->expects($this->once())
788
            ->method('checkAccess')
789
            ->with($this->equalTo('show'))
790
            ->willReturn(true);
791
792
        $show = $this->createMock(FieldDescriptionCollection::class);
793
794
        $this->admin->expects($this->once())
795
            ->method('getShow')
796
            ->willReturn($show);
797
798
        $this->assertInstanceOf(Response::class, $this->controller->showAction(null));
799
800
        $this->assertSame($this->admin, $this->parameters['admin']);
801
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
802
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
803
804
        $this->assertSame('show', $this->parameters['action']);
805
        $this->assertInstanceOf(FieldDescriptionCollection::class, $this->parameters['elements']);
806
        $this->assertSame($object, $this->parameters['object']);
807
808
        $this->assertSame([], $this->session->getFlashBag()->all());
809
        $this->assertSame('@SonataAdmin/CRUD/show.html.twig', $this->template);
810
    }
811
812
    /**
813
     * @dataProvider getRedirectToTests
814
     */
815
    public function testRedirectTo(
816
        string $expected,
817
        string $route,
818
        array $queryParams,
819
        array $requestParams,
820
        bool $hasActiveSubclass
821
    ): void {
822
        $this->admin
823
            ->method('hasActiveSubclass')
824
            ->willReturn($hasActiveSubclass);
825
826
        $object = new \stdClass();
827
828
        foreach ($queryParams as $key => $value) {
829
            $this->request->query->set($key, $value);
830
        }
831
832
        foreach ($requestParams as $key => $value) {
833
            $this->request->request->set($key, $value);
834
        }
835
836
        $this->admin
837
            ->method('hasRoute')
838
            ->with($this->equalTo($route))
839
            ->willReturn(true);
840
841
        $this->admin
842
            ->method('hasAccess')
843
            ->with($this->equalTo($route))
844
            ->willReturn(true);
845
846
        $response = $this->protectedTestedMethods['redirectTo']->invoke($this->controller, $object, $this->request);
847
        $this->assertInstanceOf(RedirectResponse::class, $response);
848
        $this->assertSame($expected, $response->getTargetUrl());
849
    }
850
851
    public function testRedirectToWithObject(): void
852
    {
853
        $this->admin
854
            ->method('hasActiveSubclass')
855
            ->willReturn(false);
856
857
        $object = new \stdClass();
858
859
        $this->admin->expects($this->at(0))
860
            ->method('hasRoute')
861
            ->with($this->equalTo('edit'))
862
            ->willReturn(true);
863
864
        $this->admin
865
            ->method('hasAccess')
866
            ->with($this->equalTo('edit'), $object)
867
            ->willReturn(false);
868
869
        $response = $this->protectedTestedMethods['redirectTo']->invoke($this->controller, $object, $this->request);
870
        $this->assertInstanceOf(RedirectResponse::class, $response);
871
        $this->assertSame('list', $response->getTargetUrl());
872
    }
873
874
    public function getRedirectToTests()
875
    {
876
        return [
877
            ['stdClass_edit', 'edit', [], [], false],
878
            ['list', 'list', ['btn_update_and_list' => true], [], false],
879
            ['list', 'list', ['btn_create_and_list' => true], [], false],
880
            ['create', 'create', ['btn_create_and_create' => true], [], false],
881
            ['create?subclass=foo', 'create', ['btn_create_and_create' => true, 'subclass' => 'foo'], [], true],
882
            ['stdClass_edit?_tab=first_tab', 'edit', ['btn_update_and_edit' => true], ['_tab' => 'first_tab'], false],
883
        ];
884
    }
885
886
    public function testDeleteActionNotFoundException(): void
887
    {
888
        $this->expectException(NotFoundHttpException::class);
889
890
        $this->admin->expects($this->once())
891
            ->method('getObject')
892
            ->willReturn(false);
893
894
        $this->controller->deleteAction(1);
895
    }
896
897
    public function testDeleteActionAccessDenied(): void
898
    {
899
        $this->expectException(AccessDeniedException::class);
900
901
        $this->admin->expects($this->once())
902
            ->method('getObject')
903
            ->willReturn(new \stdClass());
904
905
        $this->admin->expects($this->once())
906
            ->method('checkAccess')
907
            ->with($this->equalTo('delete'))
908
            ->will($this->throwException(new AccessDeniedException()));
909
910
        $this->controller->deleteAction(1);
911
    }
912
913
    public function testPreDelete(): void
914
    {
915
        $object = new \stdClass();
916
        $object->foo = 123456;
917
918
        $this->admin->expects($this->once())
919
            ->method('getObject')
920
            ->willReturn($object);
921
922
        $this->admin->expects($this->once())
923
            ->method('checkAccess')
924
            ->with($this->equalTo('delete'))
925
            ->willReturn(true);
926
927
        $controller = new PreCRUDController();
928
        $controller->setContainer($this->container);
929
930
        $response = $controller->deleteAction(null);
931
        $this->assertInstanceOf(Response::class, $response);
932
        $this->assertSame('preDelete called: 123456', $response->getContent());
933
    }
934
935
    public function testDeleteAction(): void
936
    {
937
        $object = new \stdClass();
938
939
        $this->admin->expects($this->once())
940
            ->method('getObject')
941
            ->willReturn($object);
942
943
        $this->admin->expects($this->once())
944
            ->method('checkAccess')
945
            ->with($this->equalTo('delete'))
946
            ->willReturn(true);
947
948
        $this->assertInstanceOf(Response::class, $this->controller->deleteAction(1));
949
950
        $this->assertSame($this->admin, $this->parameters['admin']);
951
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
952
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
953
954
        $this->assertSame('delete', $this->parameters['action']);
955
        $this->assertSame($object, $this->parameters['object']);
956
        $this->assertSame('csrf-token-123_sonata.delete', $this->parameters['csrf_token']);
957
958
        $this->assertSame([], $this->session->getFlashBag()->all());
959
        $this->assertSame('@SonataAdmin/CRUD/delete.html.twig', $this->template);
960
    }
961
962
    /**
963
     * @group legacy
964
     * @expectedDeprecation Accessing a child that isn't connected to a given parent is deprecated since sonata-project/admin-bundle 3.34 and won't be allowed in 4.0.
965
     */
966
    public function testDeleteActionChildDeprecation(): void
967
    {
968
        $object = new \stdClass();
969
        $object->parent = 'test';
970
971
        $object2 = new \stdClass();
972
973
        $admin = $this->createMock(PostAdmin::class);
974
975
        $admin->expects($this->once())
976
            ->method('getObject')
977
            ->willReturn($object2);
978
979
        $this->admin->expects($this->once())
980
            ->method('getObject')
981
            ->willReturn($object);
982
983
        $this->admin->expects($this->once())
984
            ->method('isChild')
985
            ->willReturn(true);
986
987
        $this->admin->expects($this->once())
988
            ->method('getParent')
989
            ->willReturn($admin);
990
991
        $this->admin->expects($this->exactly(2))
992
            ->method('getParentAssociationMapping')
993
            ->willReturn('parent');
994
995
        $this->controller->deleteAction(1);
996
    }
997
998
    public function testDeleteActionNoParentMappings(): void
999
    {
1000
        $object = new \stdClass();
1001
1002
        $admin = $this->createMock(PostAdmin::class);
1003
1004
        $admin->expects($this->never())
1005
            ->method('getObject');
1006
1007
        $this->admin->expects($this->once())
1008
            ->method('getObject')
1009
            ->willReturn($object);
1010
1011
        $this->admin->expects($this->once())
1012
            ->method('isChild')
1013
            ->willReturn(true);
1014
1015
        $this->admin->expects($this->once())
1016
            ->method('getParentAssociationMapping')
1017
            ->willReturn(false);
1018
1019
        $this->controller->deleteAction(1);
1020
    }
1021
1022
    public function testDeleteActionNoCsrfToken(): void
1023
    {
1024
        $this->container->set('security.csrf.token_manager', null);
1025
1026
        $object = new \stdClass();
1027
1028
        $this->admin->expects($this->once())
1029
            ->method('getObject')
1030
            ->willReturn($object);
1031
1032
        $this->admin->expects($this->once())
1033
            ->method('checkAccess')
1034
            ->with($this->equalTo('delete'))
1035
            ->willReturn(true);
1036
1037
        $this->assertInstanceOf(Response::class, $this->controller->deleteAction(1));
1038
1039
        $this->assertSame($this->admin, $this->parameters['admin']);
1040
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1041
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1042
1043
        $this->assertSame('delete', $this->parameters['action']);
1044
        $this->assertSame($object, $this->parameters['object']);
1045
        $this->assertFalse($this->parameters['csrf_token']);
1046
1047
        $this->assertSame([], $this->session->getFlashBag()->all());
1048
        $this->assertSame('@SonataAdmin/CRUD/delete.html.twig', $this->template);
1049
    }
1050
1051
    public function testDeleteActionAjaxSuccess1(): void
1052
    {
1053
        $object = new \stdClass();
1054
1055
        $this->admin->expects($this->once())
1056
            ->method('getObject')
1057
            ->willReturn($object);
1058
1059
        $this->admin->expects($this->once())
1060
            ->method('checkAccess')
1061
            ->with($this->equalTo('delete'))
1062
            ->willReturn(true);
1063
1064
        $this->request->setMethod(Request::METHOD_DELETE);
1065
1066
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1067
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1068
1069
        $response = $this->controller->deleteAction(1);
1070
1071
        $this->assertInstanceOf(Response::class, $response);
1072
        $this->assertSame(json_encode(['result' => 'ok']), $response->getContent());
1073
        $this->assertSame([], $this->session->getFlashBag()->all());
1074
    }
1075
1076
    public function testDeleteActionAjaxSuccess2(): void
1077
    {
1078
        $object = new \stdClass();
1079
1080
        $this->admin->expects($this->once())
1081
            ->method('getObject')
1082
            ->willReturn($object);
1083
1084
        $this->admin->expects($this->once())
1085
            ->method('checkAccess')
1086
            ->with($this->equalTo('delete'))
1087
            ->willReturn(true);
1088
1089
        $this->request->setMethod(Request::METHOD_POST);
1090
        $this->request->request->set('_method', Request::METHOD_DELETE);
1091
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1092
1093
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1094
1095
        $response = $this->controller->deleteAction(1);
1096
1097
        $this->assertInstanceOf(Response::class, $response);
1098
        $this->assertSame(json_encode(['result' => 'ok']), $response->getContent());
1099
        $this->assertSame([], $this->session->getFlashBag()->all());
1100
    }
1101
1102
    public function testDeleteActionAjaxError(): void
1103
    {
1104
        $object = new \stdClass();
1105
1106
        $this->admin->expects($this->once())
1107
            ->method('getObject')
1108
            ->willReturn($object);
1109
1110
        $this->admin->expects($this->once())
1111
            ->method('checkAccess')
1112
            ->with($this->equalTo('delete'))
1113
            ->willReturn(true);
1114
1115
        $this->admin
1116
            ->method('getClass')
1117
            ->willReturn(\stdClass::class);
1118
1119
        $this->assertLoggerLogsModelManagerException($this->admin, 'delete');
1120
1121
        $this->request->setMethod(Request::METHOD_DELETE);
1122
1123
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1124
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1125
1126
        $response = $this->controller->deleteAction(1);
1127
1128
        $this->assertInstanceOf(Response::class, $response);
1129
        $this->assertSame(json_encode(['result' => 'error']), $response->getContent());
1130
        $this->assertSame([], $this->session->getFlashBag()->all());
1131
    }
1132
1133
    public function testDeleteActionWithModelManagerExceptionInDebugMode(): void
1134
    {
1135
        $this->expectException(ModelManagerException::class);
1136
1137
        $object = new \stdClass();
1138
1139
        $this->admin->expects($this->once())
1140
            ->method('getObject')
1141
            ->willReturn($object);
1142
1143
        $this->admin->expects($this->once())
1144
            ->method('checkAccess')
1145
            ->with($this->equalTo('delete'))
1146
            ->willReturn(true);
1147
1148
        $this->admin->expects($this->once())
1149
            ->method('delete')
1150
            ->willReturnCallback(static function (): void {
1151
                throw new ModelManagerException();
1152
            });
1153
1154
        $this->kernel->expects($this->once())
1155
            ->method('isDebug')
1156
            ->willReturn(true);
1157
1158
        $this->request->setMethod(Request::METHOD_DELETE);
1159
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1160
1161
        $this->controller->deleteAction(1);
1162
    }
1163
1164
    /**
1165
     * @dataProvider getToStringValues
1166
     */
1167
    public function testDeleteActionSuccess1(string $expectedToStringValue, string $toStringValue): void
1168
    {
1169
        $object = new \stdClass();
1170
1171
        $this->admin->expects($this->once())
1172
            ->method('getObject')
1173
            ->willReturn($object);
1174
1175
        $this->admin->expects($this->once())
1176
            ->method('toString')
1177
            ->with($this->equalTo($object))
1178
            ->willReturn($toStringValue);
1179
1180
        $this->expectTranslate('flash_delete_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1181
1182
        $this->admin->expects($this->once())
1183
            ->method('checkAccess')
1184
            ->with($this->equalTo('delete'))
1185
            ->willReturn(true);
1186
1187
        $this->request->setMethod(Request::METHOD_DELETE);
1188
1189
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1190
1191
        $response = $this->controller->deleteAction(1);
1192
1193
        $this->assertInstanceOf(RedirectResponse::class, $response);
1194
        $this->assertSame(['flash_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
1195
        $this->assertSame('list', $response->getTargetUrl());
1196
    }
1197
1198
    /**
1199
     * @dataProvider getToStringValues
1200
     */
1201
    public function testDeleteActionSuccess2(string $expectedToStringValue, string $toStringValue): void
1202
    {
1203
        $object = new \stdClass();
1204
1205
        $this->admin->expects($this->once())
1206
            ->method('getObject')
1207
            ->willReturn($object);
1208
1209
        $this->admin->expects($this->once())
1210
            ->method('checkAccess')
1211
            ->with($this->equalTo('delete'))
1212
            ->willReturn(true);
1213
1214
        $this->admin->expects($this->once())
1215
            ->method('toString')
1216
            ->with($this->equalTo($object))
1217
            ->willReturn($toStringValue);
1218
1219
        $this->expectTranslate('flash_delete_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1220
1221
        $this->request->setMethod(Request::METHOD_POST);
1222
        $this->request->request->set('_method', Request::METHOD_DELETE);
1223
1224
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1225
1226
        $response = $this->controller->deleteAction(1);
1227
1228
        $this->assertInstanceOf(RedirectResponse::class, $response);
1229
        $this->assertSame(['flash_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
1230
        $this->assertSame('list', $response->getTargetUrl());
1231
    }
1232
1233
    /**
1234
     * @dataProvider getToStringValues
1235
     */
1236
    public function testDeleteActionSuccessNoCsrfTokenProvider(string $expectedToStringValue, string $toStringValue): void
1237
    {
1238
        $this->container->set('security.csrf.token_manager', null);
1239
1240
        $object = new \stdClass();
1241
1242
        $this->admin->expects($this->once())
1243
            ->method('getObject')
1244
            ->willReturn($object);
1245
1246
        $this->admin->expects($this->once())
1247
            ->method('checkAccess')
1248
            ->with($this->equalTo('delete'))
1249
            ->willReturn(true);
1250
1251
        $this->admin->expects($this->once())
1252
            ->method('toString')
1253
            ->with($this->equalTo($object))
1254
            ->willReturn($toStringValue);
1255
1256
        $this->expectTranslate('flash_delete_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1257
1258
        $this->request->setMethod(Request::METHOD_POST);
1259
        $this->request->request->set('_method', Request::METHOD_DELETE);
1260
1261
        $response = $this->controller->deleteAction(1);
1262
1263
        $this->assertInstanceOf(RedirectResponse::class, $response);
1264
        $this->assertSame(['flash_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
1265
        $this->assertSame('list', $response->getTargetUrl());
1266
    }
1267
1268
    public function testDeleteActionWrongRequestMethod(): void
1269
    {
1270
        $object = new \stdClass();
1271
1272
        $this->admin->expects($this->once())
1273
            ->method('getObject')
1274
            ->willReturn($object);
1275
1276
        $this->admin->expects($this->once())
1277
            ->method('checkAccess')
1278
            ->with($this->equalTo('delete'))
1279
            ->willReturn(true);
1280
1281
        //without POST request parameter "_method" should not be used as real REST method
1282
        $this->request->query->set('_method', Request::METHOD_DELETE);
1283
1284
        $this->assertInstanceOf(Response::class, $this->controller->deleteAction(1));
1285
1286
        $this->assertSame($this->admin, $this->parameters['admin']);
1287
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1288
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1289
1290
        $this->assertSame('delete', $this->parameters['action']);
1291
        $this->assertSame($object, $this->parameters['object']);
1292
        $this->assertSame('csrf-token-123_sonata.delete', $this->parameters['csrf_token']);
1293
1294
        $this->assertSame([], $this->session->getFlashBag()->all());
1295
        $this->assertSame('@SonataAdmin/CRUD/delete.html.twig', $this->template);
1296
    }
1297
1298
    /**
1299
     * @dataProvider getToStringValues
1300
     */
1301
    public function testDeleteActionError(string $expectedToStringValue, string $toStringValue): void
1302
    {
1303
        $object = new \stdClass();
1304
1305
        $this->admin->expects($this->once())
1306
            ->method('getObject')
1307
            ->willReturn($object);
1308
1309
        $this->admin->expects($this->once())
1310
            ->method('checkAccess')
1311
            ->with($this->equalTo('delete'))
1312
            ->willReturn(true);
1313
1314
        $this->admin->expects($this->once())
1315
            ->method('toString')
1316
            ->with($this->equalTo($object))
1317
            ->willReturn($toStringValue);
1318
1319
        $this->expectTranslate('flash_delete_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1320
1321
        $this->assertLoggerLogsModelManagerException($this->admin, 'delete');
1322
1323
        $this->request->setMethod(Request::METHOD_DELETE);
1324
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.delete');
1325
1326
        $response = $this->controller->deleteAction(1);
1327
1328
        $this->assertInstanceOf(RedirectResponse::class, $response);
1329
        $this->assertSame(['flash_delete_error'], $this->session->getFlashBag()->get('sonata_flash_error'));
1330
        $this->assertSame('list', $response->getTargetUrl());
1331
    }
1332
1333
    public function testDeleteActionInvalidCsrfToken(): void
1334
    {
1335
        $object = new \stdClass();
1336
1337
        $this->admin->expects($this->once())
1338
            ->method('getObject')
1339
            ->willReturn($object);
1340
1341
        $this->admin->expects($this->once())
1342
            ->method('checkAccess')
1343
            ->with($this->equalTo('delete'))
1344
            ->willReturn(true);
1345
1346
        $this->request->setMethod(Request::METHOD_POST);
1347
        $this->request->request->set('_method', Request::METHOD_DELETE);
1348
        $this->request->request->set('_sonata_csrf_token', 'CSRF-INVALID');
1349
1350
        try {
1351
            $this->controller->deleteAction(1);
1352
        } catch (HttpException $e) {
1353
            $this->assertSame('The csrf token is not valid, CSRF attack?', $e->getMessage());
1354
            $this->assertSame(400, $e->getStatusCode());
1355
        }
1356
    }
1357
1358
    public function testEditActionNotFoundException(): void
1359
    {
1360
        $this->expectException(NotFoundHttpException::class);
1361
1362
        $this->admin->expects($this->once())
1363
            ->method('getObject')
1364
            ->willReturn(false);
1365
1366
        $this->controller->editAction(null);
1367
    }
1368
1369
    public function testEditActionAccessDenied(): void
1370
    {
1371
        $this->expectException(AccessDeniedException::class);
1372
1373
        $this->admin->expects($this->once())
1374
            ->method('getObject')
1375
            ->willReturn(new \stdClass());
1376
1377
        $this->admin->expects($this->once())
1378
            ->method('checkAccess')
1379
            ->with($this->equalTo('edit'))
1380
            ->will($this->throwException(new AccessDeniedException()));
1381
1382
        $this->controller->editAction(null);
1383
    }
1384
1385
    public function testPreEdit(): void
1386
    {
1387
        $object = new \stdClass();
1388
        $object->foo = 123456;
1389
1390
        $this->admin->expects($this->once())
1391
            ->method('getObject')
1392
            ->willReturn($object);
1393
1394
        $this->admin->expects($this->once())
1395
            ->method('checkAccess')
1396
            ->with($this->equalTo('edit'))
1397
            ->willReturn(true);
1398
1399
        $controller = new PreCRUDController();
1400
        $controller->setContainer($this->container);
1401
1402
        $response = $controller->editAction(null);
1403
        $this->assertInstanceOf(Response::class, $response);
1404
        $this->assertSame('preEdit called: 123456', $response->getContent());
1405
    }
1406
1407
    public function testEditAction(): void
1408
    {
1409
        $object = new \stdClass();
1410
1411
        $this->admin->expects($this->once())
1412
            ->method('getObject')
1413
            ->willReturn($object);
1414
1415
        $this->admin->expects($this->once())
1416
            ->method('checkAccess')
1417
            ->with($this->equalTo('edit'))
1418
            ->willReturn(true);
1419
1420
        $form = $this->createMock(Form::class);
1421
1422
        $this->admin->expects($this->once())
1423
            ->method('getForm')
1424
            ->willReturn($form);
1425
1426
        $formView = $this->createMock(FormView::class);
1427
1428
        $form
1429
            ->method('createView')
1430
            ->willReturn($formView);
1431
1432
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null));
1433
1434
        $this->assertSame($this->admin, $this->parameters['admin']);
1435
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1436
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1437
1438
        $this->assertSame('edit', $this->parameters['action']);
1439
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1440
        $this->assertSame($object, $this->parameters['object']);
1441
        $this->assertSame([], $this->session->getFlashBag()->all());
1442
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
1443
    }
1444
1445
    /**
1446
     * @dataProvider getToStringValues
1447
     */
1448
    public function testEditActionSuccess(string $expectedToStringValue, string $toStringValue): void
1449
    {
1450
        $object = new \stdClass();
1451
1452
        $this->admin->expects($this->once())
1453
            ->method('getObject')
1454
            ->willReturn($object);
1455
1456
        $this->admin->expects($this->once())
1457
            ->method('update')
1458
            ->willReturnArgument(0);
1459
1460
        $this->admin->expects($this->once())
1461
            ->method('checkAccess')
1462
            ->with($this->equalTo('edit'));
1463
1464
        $this->admin->expects($this->once())
1465
            ->method('hasRoute')
1466
            ->with($this->equalTo('edit'))
1467
            ->willReturn(true);
1468
1469
        $this->admin->expects($this->once())
1470
            ->method('hasAccess')
1471
            ->with($this->equalTo('edit'))
1472
            ->willReturn(true);
1473
1474
        $form = $this->createMock(Form::class);
1475
1476
        $form->expects($this->once())
1477
            ->method('getData')
1478
            ->willReturn($object);
1479
1480
        $this->admin->expects($this->once())
1481
            ->method('getForm')
1482
            ->willReturn($form);
1483
1484
        $form->expects($this->once())
1485
            ->method('isSubmitted')
1486
            ->willReturn(true);
1487
1488
        $form->expects($this->once())
1489
            ->method('isValid')
1490
            ->willReturn(true);
1491
1492
        $this->admin->expects($this->once())
1493
            ->method('toString')
1494
            ->with($this->equalTo($object))
1495
            ->willReturn($toStringValue);
1496
1497
        $this->expectTranslate('flash_edit_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1498
1499
        $this->request->setMethod(Request::METHOD_POST);
1500
1501
        $response = $this->controller->editAction(null);
1502
1503
        $this->assertInstanceOf(RedirectResponse::class, $response);
1504
        $this->assertSame(['flash_edit_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
1505
        $this->assertSame('stdClass_edit', $response->getTargetUrl());
1506
    }
1507
1508
    /**
1509
     * @dataProvider getToStringValues
1510
     */
1511
    public function testEditActionError(string $expectedToStringValue, string $toStringValue): void
1512
    {
1513
        $object = new \stdClass();
1514
1515
        $this->admin->expects($this->once())
1516
            ->method('getObject')
1517
            ->willReturn($object);
1518
1519
        $this->admin->expects($this->once())
1520
            ->method('checkAccess')
1521
            ->with($this->equalTo('edit'))
1522
            ->willReturn(true);
1523
1524
        $form = $this->createMock(Form::class);
1525
1526
        $this->admin->expects($this->once())
1527
            ->method('getForm')
1528
            ->willReturn($form);
1529
1530
        $form->expects($this->once())
1531
            ->method('isSubmitted')
1532
            ->willReturn(true);
1533
1534
        $form->expects($this->once())
1535
            ->method('isValid')
1536
            ->willReturn(false);
1537
1538
        $this->admin->expects($this->once())
1539
            ->method('toString')
1540
            ->with($this->equalTo($object))
1541
            ->willReturn($toStringValue);
1542
1543
        $this->expectTranslate('flash_edit_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1544
1545
        $this->request->setMethod(Request::METHOD_POST);
1546
1547
        $formView = $this->createMock(FormView::class);
1548
1549
        $form
1550
            ->method('createView')
1551
            ->willReturn($formView);
1552
1553
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null));
1554
1555
        $this->assertSame($this->admin, $this->parameters['admin']);
1556
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1557
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1558
1559
        $this->assertSame('edit', $this->parameters['action']);
1560
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1561
        $this->assertSame($object, $this->parameters['object']);
1562
1563
        $this->assertSame(['sonata_flash_error' => ['flash_edit_error']], $this->session->getFlashBag()->all());
1564
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
1565
    }
1566
1567
    public function testEditActionAjaxSuccess(): void
1568
    {
1569
        $object = new \stdClass();
1570
1571
        $this->admin->expects($this->once())
1572
            ->method('getObject')
1573
            ->willReturn($object);
1574
1575
        $this->admin->expects($this->once())
1576
            ->method('update')
1577
            ->willReturnArgument(0);
1578
1579
        $this->admin->expects($this->once())
1580
            ->method('checkAccess')
1581
            ->with($this->equalTo('edit'))
1582
            ->willReturn(true);
1583
1584
        $form = $this->createMock(Form::class);
1585
1586
        $this->admin->expects($this->once())
1587
            ->method('getForm')
1588
            ->willReturn($form);
1589
1590
        $form->expects($this->once())
1591
            ->method('isSubmitted')
1592
            ->willReturn(true);
1593
1594
        $form->expects($this->once())
1595
            ->method('isValid')
1596
            ->willReturn(true);
1597
1598
        $form->expects($this->once())
1599
            ->method('getData')
1600
            ->willReturn($object);
1601
1602
        $this->admin
1603
            ->method('getNormalizedIdentifier')
1604
            ->with($this->equalTo($object))
1605
            ->willReturn('foo_normalized');
1606
1607
        $this->admin->expects($this->once())
1608
            ->method('toString')
1609
            ->willReturn('foo');
1610
1611
        $this->request->setMethod(Request::METHOD_POST);
1612
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1613
1614
        $response = $this->controller->editAction(null);
1615
1616
        $this->assertInstanceOf(Response::class, $response);
1617
        $this->assertSame(json_encode(['result' => 'ok', 'objectId' => 'foo_normalized', 'objectName' => 'foo']), $response->getContent());
1618
        $this->assertSame([], $this->session->getFlashBag()->all());
1619
    }
1620
1621
    public function testEditActionAjaxError(): void
1622
    {
1623
        $object = new \stdClass();
1624
1625
        $this->admin->expects($this->once())
1626
            ->method('getObject')
1627
            ->willReturn($object);
1628
1629
        $this->admin->expects($this->once())
1630
            ->method('checkAccess')
1631
            ->with($this->equalTo('edit'))
1632
            ->willReturn(true);
1633
1634
        $form = $this->createMock(Form::class);
1635
1636
        $this->admin->expects($this->once())
1637
            ->method('getForm')
1638
            ->willReturn($form);
1639
1640
        $form->expects($this->once())
1641
            ->method('isSubmitted')
1642
            ->willReturn(true);
1643
1644
        $form->expects($this->once())
1645
            ->method('isValid')
1646
            ->willReturn(false);
1647
1648
        $formError = $this->createMock(FormError::class);
1649
        $formError->expects($this->atLeastOnce())
1650
            ->method('getMessage')
1651
            ->willReturn('Form error message');
1652
1653
        $form->expects($this->once())
1654
            ->method('getErrors')
1655
            ->with(true)
1656
            ->willReturn([$formError]);
1657
1658
        $this->request->setMethod(Request::METHOD_POST);
1659
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1660
        $this->request->headers->set('Accept', 'application/json');
1661
1662
        $this->assertInstanceOf(JsonResponse::class, $response = $this->controller->editAction(null));
1663
        $this->assertJsonStringEqualsJsonString('{"result":"error","errors":["Form error message"]}', $response->getContent());
1664
    }
1665
1666
    /**
1667
     * @legacy
1668
     * @expectedDeprecation In next major version response will return 406 NOT ACCEPTABLE without `Accept: application/json`
1669
     */
1670
    public function testEditActionAjaxErrorWithoutAcceptApplicationJson(): void
1671
    {
1672
        $object = new \stdClass();
1673
1674
        $this->admin->expects($this->once())
1675
            ->method('getObject')
1676
            ->willReturn($object);
1677
1678
        $this->admin->expects($this->once())
1679
            ->method('checkAccess')
1680
            ->with($this->equalTo('edit'))
1681
            ->willReturn(true);
1682
1683
        $form = $this->createMock(Form::class);
1684
1685
        $this->admin->expects($this->once())
1686
            ->method('getForm')
1687
            ->willReturn($form);
1688
1689
        $form->expects($this->once())
1690
            ->method('isSubmitted')
1691
            ->willReturn(true);
1692
1693
        $form->expects($this->once())
1694
            ->method('isValid')
1695
            ->willReturn(false);
1696
1697
        $this->request->setMethod(Request::METHOD_POST);
1698
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
1699
1700
        $formView = $this->createMock(FormView::class);
1701
        $form
1702
            ->method('createView')
1703
            ->willReturn($formView);
1704
1705
        $this->translator->expects($this->once())
1706
            ->method('trans')
1707
            ->willReturn('flash message');
1708
1709
        $this->assertInstanceOf(Response::class, $response = $this->controller->editAction(null));
1710
        $this->assertSame($this->admin, $this->parameters['admin']);
1711
        $this->assertSame('@SonataAdmin/ajax_layout.html.twig', $this->parameters['base_template']);
1712
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1713
        $this->assertSame('edit', $this->parameters['action']);
1714
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1715
        $this->assertSame($object, $this->parameters['object']);
1716
        $this->assertSame([
1717
            'sonata_flash_error' => [0 => 'flash message'],
1718
        ], $this->session->getFlashBag()->all());
1719
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
1720
    }
1721
1722
    /**
1723
     * @dataProvider getToStringValues
1724
     */
1725
    public function testEditActionWithModelManagerException(string $expectedToStringValue, string $toStringValue): void
1726
    {
1727
        $object = new \stdClass();
1728
1729
        $this->admin->expects($this->once())
1730
            ->method('getObject')
1731
            ->willReturn($object);
1732
1733
        $this->admin->expects($this->once())
1734
            ->method('checkAccess')
1735
            ->with($this->equalTo('edit'))
1736
            ->willReturn(true);
1737
1738
        $this->admin
1739
            ->method('getClass')
1740
            ->willReturn(\stdClass::class);
1741
1742
        $form = $this->createMock(Form::class);
1743
1744
        $this->admin->expects($this->once())
1745
            ->method('getForm')
1746
            ->willReturn($form);
1747
1748
        $form->expects($this->once())
1749
            ->method('isValid')
1750
            ->willReturn(true);
1751
1752
        $form->expects($this->once())
1753
            ->method('getData')
1754
            ->willReturn($object);
1755
1756
        $this->admin->expects($this->once())
1757
            ->method('toString')
1758
            ->with($this->equalTo($object))
1759
            ->willReturn($toStringValue);
1760
1761
        $this->expectTranslate('flash_edit_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
1762
1763
        $form->expects($this->once())
1764
            ->method('isSubmitted')
1765
            ->willReturn(true);
1766
        $this->request->setMethod(Request::METHOD_POST);
1767
1768
        $formView = $this->createMock(FormView::class);
1769
1770
        $form
1771
            ->method('createView')
1772
            ->willReturn($formView);
1773
1774
        $this->assertLoggerLogsModelManagerException($this->admin, 'update');
1775
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null));
1776
1777
        $this->assertSame($this->admin, $this->parameters['admin']);
1778
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1779
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1780
1781
        $this->assertSame('edit', $this->parameters['action']);
1782
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1783
        $this->assertSame($object, $this->parameters['object']);
1784
1785
        $this->assertSame(['sonata_flash_error' => ['flash_edit_error']], $this->session->getFlashBag()->all());
1786
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
1787
    }
1788
1789
    public function testEditActionWithPreview(): void
1790
    {
1791
        $object = new \stdClass();
1792
1793
        $this->admin->expects($this->once())
1794
            ->method('getObject')
1795
            ->willReturn($object);
1796
1797
        $this->admin->expects($this->once())
1798
            ->method('checkAccess')
1799
            ->with($this->equalTo('edit'))
1800
            ->willReturn(true);
1801
1802
        $form = $this->createMock(Form::class);
1803
1804
        $this->admin->expects($this->once())
1805
            ->method('getForm')
1806
            ->willReturn($form);
1807
1808
        $this->admin->expects($this->once())
1809
            ->method('supportsPreviewMode')
1810
            ->willReturn(true);
1811
1812
        $formView = $this->createMock(FormView::class);
1813
1814
        $form
1815
            ->method('createView')
1816
            ->willReturn($formView);
1817
1818
        $form->expects($this->once())
1819
            ->method('isSubmitted')
1820
            ->willReturn(true);
1821
1822
        $form->expects($this->once())
1823
            ->method('isValid')
1824
            ->willReturn(true);
1825
1826
        $this->request->setMethod(Request::METHOD_POST);
1827
        $this->request->request->set('btn_preview', 'Preview');
1828
1829
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null));
1830
1831
        $this->assertSame($this->admin, $this->parameters['admin']);
1832
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1833
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1834
1835
        $this->assertSame('edit', $this->parameters['action']);
1836
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1837
        $this->assertSame($object, $this->parameters['object']);
1838
1839
        $this->assertSame([], $this->session->getFlashBag()->all());
1840
        $this->assertSame('@SonataAdmin/CRUD/preview.html.twig', $this->template);
1841
    }
1842
1843
    public function testEditActionWithLockException(): void
1844
    {
1845
        $object = new \stdClass();
1846
        $class = \get_class($object);
1847
1848
        $this->admin
1849
            ->method('getObject')
1850
            ->willReturn($object);
1851
1852
        $this->admin
1853
            ->method('checkAccess')
1854
            ->with($this->equalTo('edit'))
1855
            ->willReturn(true);
1856
1857
        $this->admin
1858
            ->method('getClass')
1859
            ->willReturn($class);
1860
1861
        $form = $this->createMock(Form::class);
1862
1863
        $form
1864
            ->method('isValid')
1865
            ->willReturn(true);
1866
1867
        $form->expects($this->once())
1868
            ->method('getData')
1869
            ->willReturn($object);
1870
1871
        $this->admin
1872
            ->method('getForm')
1873
            ->willReturn($form);
1874
1875
        $form
1876
            ->method('isSubmitted')
1877
            ->willReturn(true);
1878
        $this->request->setMethod(Request::METHOD_POST);
1879
1880
        $this->admin
1881
            ->method('update')
1882
            ->will($this->throwException(new LockException()));
1883
1884
        $this->admin
1885
            ->method('toString')
1886
            ->with($this->equalTo($object))
1887
            ->willReturn($class);
1888
1889
        $formView = $this->createMock(FormView::class);
1890
1891
        $form
1892
            ->method('createView')
1893
            ->willReturn($formView);
1894
1895
        $this->expectTranslate('flash_lock_error', [
1896
            '%name%' => $class,
1897
            '%link_start%' => '<a href="stdClass_edit">',
1898
            '%link_end%' => '</a>',
1899
        ], 'SonataAdminBundle');
1900
1901
        $this->assertInstanceOf(Response::class, $this->controller->editAction(null));
1902
    }
1903
1904
    public function testCreateActionAccessDenied(): void
1905
    {
1906
        $this->expectException(AccessDeniedException::class);
1907
1908
        $this->admin->expects($this->once())
1909
            ->method('checkAccess')
1910
            ->with($this->equalTo('create'))
1911
            ->will($this->throwException(new AccessDeniedException()));
1912
1913
        $this->controller->createAction();
1914
    }
1915
1916
    public function testPreCreate(): void
1917
    {
1918
        $object = new \stdClass();
1919
        $object->foo = 123456;
1920
1921
        $this->admin->expects($this->once())
1922
            ->method('checkAccess')
1923
            ->with($this->equalTo('create'))
1924
            ->willReturn(true);
1925
1926
        $this->admin
1927
            ->method('getClass')
1928
            ->willReturn(\stdClass::class);
1929
1930
        $this->admin->expects($this->once())
1931
            ->method('getNewInstance')
1932
            ->willReturn($object);
1933
1934
        $controller = new PreCRUDController();
1935
        $controller->setContainer($this->container);
1936
1937
        $response = $controller->createAction();
1938
        $this->assertInstanceOf(Response::class, $response);
1939
        $this->assertSame('preCreate called: 123456', $response->getContent());
1940
    }
1941
1942
    public function testCreateAction(): void
1943
    {
1944
        $this->admin->expects($this->once())
1945
            ->method('checkAccess')
1946
            ->with($this->equalTo('create'))
1947
            ->willReturn(true);
1948
1949
        $object = new \stdClass();
1950
1951
        $this->admin
1952
            ->method('getClass')
1953
            ->willReturn(\stdClass::class);
1954
1955
        $this->admin->expects($this->once())
1956
            ->method('getNewInstance')
1957
            ->willReturn($object);
1958
1959
        $form = $this->createMock(Form::class);
1960
1961
        $this->admin->expects($this->once())
1962
            ->method('getForm')
1963
            ->willReturn($form);
1964
1965
        $formView = $this->createMock(FormView::class);
1966
1967
        $form
1968
            ->method('createView')
1969
            ->willReturn($formView);
1970
1971
        $this->assertInstanceOf(Response::class, $this->controller->createAction());
1972
1973
        $this->assertSame($this->admin, $this->parameters['admin']);
1974
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
1975
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
1976
1977
        $this->assertSame('create', $this->parameters['action']);
1978
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
1979
        $this->assertSame($object, $this->parameters['object']);
1980
1981
        $this->assertSame([], $this->session->getFlashBag()->all());
1982
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
1983
    }
1984
1985
    /**
1986
     * @dataProvider getToStringValues
1987
     */
1988
    public function testCreateActionSuccess(string $expectedToStringValue, string $toStringValue): void
1989
    {
1990
        $object = new \stdClass();
1991
1992
        $this->admin->expects($this->exactly(2))
1993
            ->method('checkAccess')
1994
            ->willReturnCallback(static function (string $name, $objectIn = null) use ($object): bool {
1995
                if ('edit' === $name) {
1996
                    return true;
1997
                }
1998
1999
                if ('create' !== $name) {
2000
                    return false;
2001
                }
2002
2003
                if (null === $objectIn) {
2004
                    return true;
2005
                }
2006
2007
                return $objectIn === $object;
2008
            });
2009
2010
        $this->admin->expects($this->once())
2011
            ->method('hasRoute')
2012
            ->with($this->equalTo('edit'))
2013
            ->willReturn(true);
2014
2015
        $this->admin->expects($this->once())
2016
            ->method('hasAccess')
2017
            ->with($this->equalTo('edit'))
2018
            ->willReturn(true);
2019
2020
        $this->admin->expects($this->once())
2021
            ->method('getNewInstance')
2022
            ->willReturn($object);
2023
2024
        $this->admin->expects($this->once())
2025
            ->method('create')
2026
            ->willReturnArgument(0);
2027
2028
        $form = $this->createMock(Form::class);
2029
2030
        $this->admin
2031
            ->method('getClass')
2032
            ->willReturn(\stdClass::class);
2033
2034
        $this->admin->expects($this->once())
2035
            ->method('getForm')
2036
            ->willReturn($form);
2037
2038
        $form->expects($this->once())
2039
            ->method('isSubmitted')
2040
            ->willReturn(true);
2041
2042
        $form->expects($this->once())
2043
            ->method('isValid')
2044
            ->willReturn(true);
2045
2046
        $form->expects($this->once())
2047
            ->method('getData')
2048
            ->willReturn($object);
2049
2050
        $this->admin->expects($this->once())
2051
            ->method('toString')
2052
            ->with($this->equalTo($object))
2053
            ->willReturn($toStringValue);
2054
2055
        $this->expectTranslate('flash_create_success', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
2056
2057
        $this->request->setMethod(Request::METHOD_POST);
2058
2059
        $response = $this->controller->createAction();
2060
2061
        $this->assertInstanceOf(RedirectResponse::class, $response);
2062
        $this->assertSame(['flash_create_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
2063
        $this->assertSame('stdClass_edit', $response->getTargetUrl());
2064
    }
2065
2066
    public function testCreateActionAccessDenied2(): void
2067
    {
2068
        $this->expectException(AccessDeniedException::class);
2069
2070
        $object = new \stdClass();
2071
2072
        $this->admin
2073
            ->method('checkAccess')
2074
            ->willReturnCallback(static function (string $name, $object = null): bool {
2075
                if ('create' !== $name) {
2076
                    throw new AccessDeniedException();
2077
                }
2078
                if (null === $object) {
2079
                    return true;
2080
                }
2081
2082
                throw new AccessDeniedException();
2083
            });
2084
2085
        $this->admin->expects($this->once())
2086
            ->method('getNewInstance')
2087
            ->willReturn($object);
2088
2089
        $form = $this->createMock(Form::class);
2090
2091
        $this->admin
2092
            ->method('getClass')
2093
            ->willReturn(\stdClass::class);
2094
2095
        $this->admin->expects($this->once())
2096
            ->method('getForm')
2097
            ->willReturn($form);
2098
2099
        $form->expects($this->once())
2100
            ->method('isSubmitted')
2101
            ->willReturn(true);
2102
2103
        $form->expects($this->once())
2104
            ->method('getData')
2105
            ->willReturn($object);
2106
2107
        $form->expects($this->once())
2108
            ->method('isValid')
2109
            ->willReturn(true);
2110
2111
        $this->request->setMethod(Request::METHOD_POST);
2112
2113
        $this->controller->createAction();
2114
    }
2115
2116
    /**
2117
     * @dataProvider getToStringValues
2118
     */
2119
    public function testCreateActionError(string $expectedToStringValue, string $toStringValue): void
2120
    {
2121
        $this->admin->expects($this->once())
2122
            ->method('checkAccess')
2123
            ->with($this->equalTo('create'))
2124
            ->willReturn(true);
2125
2126
        $object = new \stdClass();
2127
2128
        $this->admin
2129
            ->method('getClass')
2130
            ->willReturn(\stdClass::class);
2131
2132
        $this->admin->expects($this->once())
2133
            ->method('getNewInstance')
2134
            ->willReturn($object);
2135
2136
        $form = $this->createMock(Form::class);
2137
2138
        $this->admin->expects($this->once())
2139
            ->method('getForm')
2140
            ->willReturn($form);
2141
2142
        $form->expects($this->once())
2143
            ->method('isSubmitted')
2144
            ->willReturn(true);
2145
2146
        $form->expects($this->once())
2147
            ->method('isValid')
2148
            ->willReturn(false);
2149
2150
        $this->admin->expects($this->once())
2151
            ->method('toString')
2152
            ->with($this->equalTo($object))
2153
            ->willReturn($toStringValue);
2154
2155
        $this->expectTranslate('flash_create_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
2156
2157
        $this->request->setMethod(Request::METHOD_POST);
2158
2159
        $formView = $this->createMock(FormView::class);
2160
2161
        $form
2162
            ->method('createView')
2163
            ->willReturn($formView);
2164
2165
        $this->assertInstanceOf(Response::class, $this->controller->createAction());
2166
2167
        $this->assertSame($this->admin, $this->parameters['admin']);
2168
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2169
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2170
2171
        $this->assertSame('create', $this->parameters['action']);
2172
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
2173
        $this->assertSame($object, $this->parameters['object']);
2174
2175
        $this->assertSame(['sonata_flash_error' => ['flash_create_error']], $this->session->getFlashBag()->all());
2176
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
2177
    }
2178
2179
    /**
2180
     * @dataProvider getToStringValues
2181
     */
2182
    public function testCreateActionWithModelManagerException(string $expectedToStringValue, string $toStringValue): void
2183
    {
2184
        $this->admin->expects($this->exactly(2))
2185
            ->method('checkAccess')
2186
            ->with($this->equalTo('create'))
2187
            ->willReturn(true);
2188
2189
        $this->admin
2190
            ->method('getClass')
2191
            ->willReturn(\stdClass::class);
2192
2193
        $object = new \stdClass();
2194
2195
        $this->admin->expects($this->once())
2196
            ->method('getNewInstance')
2197
            ->willReturn($object);
2198
2199
        $form = $this->createMock(Form::class);
2200
2201
        $this->admin->expects($this->once())
2202
            ->method('getForm')
2203
            ->willReturn($form);
2204
2205
        $form->expects($this->once())
2206
            ->method('isValid')
2207
            ->willReturn(true);
2208
2209
        $this->admin->expects($this->once())
2210
            ->method('toString')
2211
            ->with($this->equalTo($object))
2212
            ->willReturn($toStringValue);
2213
2214
        $this->expectTranslate('flash_create_error', ['%name%' => $expectedToStringValue], 'SonataAdminBundle');
2215
2216
        $form->expects($this->once())
2217
            ->method('isSubmitted')
2218
            ->willReturn(true);
2219
2220
        $form->expects($this->once())
2221
            ->method('getData')
2222
            ->willReturn($object);
2223
2224
        $this->request->setMethod(Request::METHOD_POST);
2225
2226
        $formView = $this->createMock(FormView::class);
2227
2228
        $form
2229
            ->method('createView')
2230
            ->willReturn($formView);
2231
2232
        $this->assertLoggerLogsModelManagerException($this->admin, 'create');
2233
2234
        $this->assertInstanceOf(Response::class, $this->controller->createAction());
2235
2236
        $this->assertSame($this->admin, $this->parameters['admin']);
2237
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2238
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2239
2240
        $this->assertSame('create', $this->parameters['action']);
2241
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
2242
        $this->assertSame($object, $this->parameters['object']);
2243
2244
        $this->assertSame(['sonata_flash_error' => ['flash_create_error']], $this->session->getFlashBag()->all());
2245
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
2246
    }
2247
2248
    public function testCreateActionAjaxSuccess(): void
2249
    {
2250
        $object = new \stdClass();
2251
2252
        $this->admin->expects($this->exactly(2))
2253
            ->method('checkAccess')
2254
            ->willReturnCallback(static function (string $name, $objectIn = null) use ($object): bool {
2255
                if ('create' !== $name) {
2256
                    return false;
2257
                }
2258
2259
                if (null === $objectIn) {
2260
                    return true;
2261
                }
2262
2263
                return $objectIn === $object;
2264
            });
2265
2266
        $this->admin->expects($this->once())
2267
            ->method('getNewInstance')
2268
            ->willReturn($object);
2269
2270
        $this->admin->expects($this->once())
2271
            ->method('create')
2272
            ->willReturnArgument(0);
2273
2274
        $form = $this->createMock(Form::class);
2275
2276
        $this->admin->expects($this->once())
2277
            ->method('getForm')
2278
            ->willReturn($form);
2279
2280
        $form->expects($this->once())
2281
            ->method('isSubmitted')
2282
            ->willReturn(true);
2283
2284
        $form->expects($this->once())
2285
            ->method('isValid')
2286
            ->willReturn(true);
2287
2288
        $form->expects($this->once())
2289
            ->method('getData')
2290
            ->willReturn($object);
2291
2292
        $this->admin
2293
            ->method('getClass')
2294
            ->willReturn(\stdClass::class);
2295
2296
        $this->admin->expects($this->once())
2297
            ->method('getNormalizedIdentifier')
2298
            ->with($this->equalTo($object))
2299
            ->willReturn('foo_normalized');
2300
2301
        $this->admin->expects($this->once())
2302
            ->method('toString')
2303
            ->willReturn('foo');
2304
2305
        $this->request->setMethod(Request::METHOD_POST);
2306
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
2307
2308
        $response = $this->controller->createAction();
2309
2310
        $this->assertInstanceOf(Response::class, $response);
2311
        $this->assertSame(json_encode(['result' => 'ok', 'objectId' => 'foo_normalized', 'objectName' => 'foo']), $response->getContent());
2312
        $this->assertSame([], $this->session->getFlashBag()->all());
2313
    }
2314
2315
    public function testCreateActionAjaxError(): void
2316
    {
2317
        $this->admin->expects($this->once())
2318
            ->method('checkAccess')
2319
            ->with($this->equalTo('create'))
2320
            ->willReturn(true);
2321
2322
        $object = new \stdClass();
2323
2324
        $this->admin->expects($this->once())
2325
            ->method('getNewInstance')
2326
            ->willReturn($object);
2327
2328
        $form = $this->createMock(Form::class);
2329
2330
        $this->admin
2331
            ->method('getClass')
2332
            ->willReturn(\stdClass::class);
2333
2334
        $this->admin->expects($this->once())
2335
            ->method('getForm')
2336
            ->willReturn($form);
2337
2338
        $form->expects($this->once())
2339
            ->method('isSubmitted')
2340
            ->willReturn(true);
2341
2342
        $form->expects($this->once())
2343
            ->method('isValid')
2344
            ->willReturn(false);
2345
2346
        $formError = $this->createMock(FormError::class);
2347
        $formError->expects($this->atLeastOnce())
2348
            ->method('getMessage')
2349
            ->willReturn('Form error message');
2350
2351
        $form->expects($this->once())
2352
            ->method('getErrors')
2353
            ->with(true)
2354
            ->willReturn([$formError]);
2355
2356
        $this->request->setMethod(Request::METHOD_POST);
2357
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
2358
        $this->request->headers->set('Accept', 'application/json');
2359
2360
        $this->assertInstanceOf(JsonResponse::class, $response = $this->controller->createAction());
2361
        $this->assertJsonStringEqualsJsonString('{"result":"error","errors":["Form error message"]}', $response->getContent());
2362
    }
2363
2364
    /**
2365
     * @legacy
2366
     * @expectedDeprecation In next major version response will return 406 NOT ACCEPTABLE without `Accept: application/json`
2367
     */
2368
    public function testCreateActionAjaxErrorWithoutAcceptApplicationJson(): void
2369
    {
2370
        $this->admin->expects($this->once())
2371
            ->method('checkAccess')
2372
            ->with($this->equalTo('create'))
2373
            ->willReturn(true);
2374
2375
        $object = new \stdClass();
2376
2377
        $this->admin->expects($this->once())
2378
            ->method('getNewInstance')
2379
            ->willReturn($object);
2380
2381
        $form = $this->createMock(Form::class);
2382
2383
        $this->admin
2384
            ->method('getClass')
2385
            ->willReturn(\stdClass::class);
2386
2387
        $this->admin->expects($this->once())
2388
            ->method('getForm')
2389
            ->willReturn($form);
2390
2391
        $form->expects($this->once())
2392
            ->method('isSubmitted')
2393
            ->willReturn(true);
2394
2395
        $form->expects($this->once())
2396
            ->method('isValid')
2397
            ->willReturn(false);
2398
2399
        $this->request->setMethod(Request::METHOD_POST);
2400
        $this->request->headers->set('X-Requested-With', 'XMLHttpRequest');
2401
2402
        $formView = $this->createMock(FormView::class);
2403
        $form
2404
            ->method('createView')
2405
            ->willReturn($formView);
2406
2407
        $this->translator->expects($this->once())
2408
            ->method('trans')
2409
            ->willReturn('flash message');
2410
2411
        $this->assertInstanceOf(Response::class, $response = $this->controller->createAction());
2412
        $this->assertSame($this->admin, $this->parameters['admin']);
2413
        $this->assertSame('@SonataAdmin/ajax_layout.html.twig', $this->parameters['base_template']);
2414
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2415
        $this->assertSame('create', $this->parameters['action']);
2416
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
2417
        $this->assertSame($object, $this->parameters['object']);
2418
        $this->assertSame([
2419
            'sonata_flash_error' => [0 => 'flash message'],
2420
        ], $this->session->getFlashBag()->all());
2421
        $this->assertSame('@SonataAdmin/CRUD/edit.html.twig', $this->template);
2422
    }
2423
2424
    public function testCreateActionWithPreview(): void
2425
    {
2426
        $this->admin->expects($this->once())
2427
            ->method('checkAccess')
2428
            ->with($this->equalTo('create'))
2429
            ->willReturn(true);
2430
2431
        $object = new \stdClass();
2432
2433
        $this->admin->expects($this->once())
2434
            ->method('getNewInstance')
2435
            ->willReturn($object);
2436
2437
        $form = $this->createMock(Form::class);
2438
2439
        $this->admin
2440
            ->method('getClass')
2441
            ->willReturn(\stdClass::class);
2442
2443
        $this->admin->expects($this->once())
2444
            ->method('getForm')
2445
            ->willReturn($form);
2446
2447
        $this->admin->expects($this->once())
2448
            ->method('supportsPreviewMode')
2449
            ->willReturn(true);
2450
2451
        $formView = $this->createMock(FormView::class);
2452
2453
        $form
2454
            ->method('createView')
2455
            ->willReturn($formView);
2456
2457
        $form->expects($this->once())
2458
            ->method('isSubmitted')
2459
            ->willReturn(true);
2460
2461
        $form->expects($this->once())
2462
            ->method('isValid')
2463
            ->willReturn(true);
2464
2465
        $this->request->setMethod(Request::METHOD_POST);
2466
        $this->request->request->set('btn_preview', 'Preview');
2467
2468
        $this->assertInstanceOf(Response::class, $this->controller->createAction());
2469
2470
        $this->assertSame($this->admin, $this->parameters['admin']);
2471
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2472
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2473
2474
        $this->assertSame('create', $this->parameters['action']);
2475
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
2476
        $this->assertSame($object, $this->parameters['object']);
2477
2478
        $this->assertSame([], $this->session->getFlashBag()->all());
2479
        $this->assertSame('@SonataAdmin/CRUD/preview.html.twig', $this->template);
2480
    }
2481
2482
    public function testExportActionAccessDenied(): void
2483
    {
2484
        $this->expectException(AccessDeniedException::class);
2485
2486
        $this->admin->expects($this->once())
2487
            ->method('checkAccess')
2488
            ->with($this->equalTo('export'))
2489
            ->will($this->throwException(new AccessDeniedException()));
2490
2491
        $this->controller->exportAction($this->request);
2492
    }
2493
2494
    public function testExportActionWrongFormat(): void
2495
    {
2496
        $this->expectException(\RuntimeException::class);
2497
        $this->expectExceptionMessage(
2498
            'Export in format `csv` is not allowed for class: `Foo`. Allowed formats are: `json`'
2499
        );
2500
2501
        $this->admin->expects($this->once())
2502
            ->method('checkAccess')
2503
            ->with($this->equalTo('export'))
2504
            ->willReturn(true);
2505
2506
        $this->admin->expects($this->once())
2507
            ->method('getExportFormats')
2508
            ->willReturn(['json']);
2509
2510
        $this->admin
2511
            ->method('getClass')
2512
            ->willReturn('Foo');
2513
2514
        $this->request->query->set('format', 'csv');
2515
2516
        $this->controller->exportAction($this->request);
2517
    }
2518
2519
    public function testExportAction(): void
2520
    {
2521
        $this->admin->expects($this->once())
2522
            ->method('checkAccess')
2523
            ->with($this->equalTo('export'))
2524
            ->willReturn(true);
2525
2526
        $this->admin->expects($this->once())
2527
            ->method('getExportFormats')
2528
            ->willReturn(['json']);
2529
2530
        $dataSourceIterator = $this->createMock(SourceIteratorInterface::class);
2531
2532
        $this->admin->expects($this->once())
2533
            ->method('getDataSourceIterator')
2534
            ->willReturn($dataSourceIterator);
2535
2536
        $this->request->query->set('format', 'json');
2537
2538
        $response = $this->controller->exportAction($this->request);
2539
        $this->assertInstanceOf(StreamedResponse::class, $response);
2540
        $this->assertSame(Response::HTTP_OK, $response->getStatusCode());
2541
        $this->assertSame([], $this->session->getFlashBag()->all());
2542
    }
2543
2544
    public function testHistoryActionAccessDenied(): void
2545
    {
2546
        $this->expectException(AccessDeniedException::class);
2547
2548
        $this->admin
2549
            ->method('getObject')
2550
            ->willReturn(new \stdClass());
2551
2552
        $this->admin->expects($this->once())
2553
            ->method('checkAccess')
2554
            ->with($this->equalTo('history'))
2555
            ->will($this->throwException(new AccessDeniedException()));
2556
2557
        $this->controller->historyAction(null);
2558
    }
2559
2560
    public function testHistoryActionNotFoundException(): void
2561
    {
2562
        $this->expectException(NotFoundHttpException::class);
2563
2564
        $this->admin->expects($this->once())
2565
            ->method('getObject')
2566
            ->willReturn(false);
2567
2568
        $this->controller->historyAction(null);
2569
    }
2570
2571
    public function testHistoryActionNoReader(): void
2572
    {
2573
        $this->expectException(NotFoundHttpException::class);
2574
        $this->expectExceptionMessage('unable to find the audit reader for class : Foo');
2575
2576
        $this->request->query->set('id', 123);
2577
2578
        $this->admin->expects($this->once())
2579
            ->method('checkAccess')
2580
            ->with($this->equalTo('history'))
2581
            ->willReturn(true);
2582
2583
        $object = new \stdClass();
2584
2585
        $this->admin->expects($this->once())
2586
            ->method('getObject')
2587
            ->willReturn($object);
2588
2589
        $this->admin
2590
            ->method('getClass')
2591
            ->willReturn('Foo');
2592
2593
        $this->auditManager->expects($this->once())
2594
            ->method('hasReader')
2595
            ->with($this->equalTo('Foo'))
2596
            ->willReturn(false);
2597
2598
        $this->controller->historyAction(null);
2599
    }
2600
2601
    public function testHistoryAction(): void
2602
    {
2603
        $this->request->query->set('id', 123);
2604
2605
        $this->admin->expects($this->once())
2606
            ->method('checkAccess')
2607
            ->with($this->equalTo('history'))
2608
            ->willReturn(true);
2609
2610
        $object = new \stdClass();
2611
2612
        $this->admin->expects($this->once())
2613
            ->method('getObject')
2614
            ->willReturn($object);
2615
2616
        $this->admin
2617
            ->method('getClass')
2618
            ->willReturn('Foo');
2619
2620
        $this->auditManager->expects($this->once())
2621
            ->method('hasReader')
2622
            ->with($this->equalTo('Foo'))
2623
            ->willReturn(true);
2624
2625
        $reader = $this->createMock(AuditReaderInterface::class);
2626
2627
        $this->auditManager->expects($this->once())
2628
            ->method('getReader')
2629
            ->with($this->equalTo('Foo'))
2630
            ->willReturn($reader);
2631
2632
        $reader->expects($this->once())
2633
            ->method('findRevisions')
2634
            ->with($this->equalTo('Foo'), $this->equalTo(123))
2635
            ->willReturn([]);
2636
2637
        $this->assertInstanceOf(Response::class, $this->controller->historyAction(null));
2638
2639
        $this->assertSame($this->admin, $this->parameters['admin']);
2640
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2641
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2642
2643
        $this->assertSame('history', $this->parameters['action']);
2644
        $this->assertSame([], $this->parameters['revisions']);
2645
        $this->assertSame($object, $this->parameters['object']);
2646
2647
        $this->assertSame([], $this->session->getFlashBag()->all());
2648
        $this->assertSame('@SonataAdmin/CRUD/history.html.twig', $this->template);
2649
    }
2650
2651
    public function testAclActionAclNotEnabled(): void
2652
    {
2653
        $this->expectException(NotFoundHttpException::class);
2654
        $this->expectExceptionMessage('ACL are not enabled for this admin');
2655
2656
        $this->controller->aclAction(null);
2657
    }
2658
2659
    public function testAclActionNotFoundException(): void
2660
    {
2661
        $this->expectException(NotFoundHttpException::class);
2662
2663
        $this->admin->expects($this->once())
2664
            ->method('isAclEnabled')
2665
            ->willReturn(true);
2666
2667
        $this->admin->expects($this->once())
2668
            ->method('getObject')
2669
            ->willReturn(false);
2670
2671
        $this->controller->aclAction(null);
2672
    }
2673
2674
    public function testAclActionAccessDenied(): void
2675
    {
2676
        $this->expectException(AccessDeniedException::class);
2677
2678
        $this->admin->expects($this->once())
2679
            ->method('isAclEnabled')
2680
            ->willReturn(true);
2681
2682
        $object = new \stdClass();
2683
2684
        $this->admin->expects($this->once())
2685
            ->method('getObject')
2686
            ->willReturn($object);
2687
2688
        $this->admin->expects($this->once())
2689
            ->method('checkAccess')
2690
            ->with($this->equalTo('acl'), $this->equalTo($object))
2691
            ->will($this->throwException(new AccessDeniedException()));
2692
2693
        $this->controller->aclAction(null);
2694
    }
2695
2696
    public function testAclAction(): void
2697
    {
2698
        $this->request->query->set('id', 123);
2699
2700
        $this->admin->expects($this->once())
0 ignored issues
show
The method expects() does not seem to exist on object<Sonata\AdminBundle\Admin\AbstractAdmin>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2701
            ->method('isAclEnabled')
2702
            ->willReturn(true);
2703
2704
        $object = new \stdClass();
2705
2706
        $this->admin->expects($this->once())
2707
            ->method('getObject')
2708
            ->willReturn($object);
2709
2710
        $this->admin
2711
            ->method('checkAccess')
2712
            ->willReturn(true);
2713
2714
        $this->admin
2715
            ->method('getSecurityInformation')
2716
            ->willReturn([]);
2717
2718
        $this->adminObjectAclManipulator->expects($this->once())
2719
            ->method('getMaskBuilderClass')
2720
            ->willReturn(AdminPermissionMap::class);
2721
2722
        $aclUsersForm = $this->getMockBuilder(Form::class)
2723
            ->disableOriginalConstructor()
2724
            ->getMock();
2725
2726
        $aclUsersForm->expects($this->once())
2727
            ->method('createView')
2728
            ->willReturn($this->createMock(FormView::class));
2729
2730
        $aclRolesForm = $this->getMockBuilder(Form::class)
2731
            ->disableOriginalConstructor()
2732
            ->getMock();
2733
2734
        $aclRolesForm->expects($this->once())
2735
            ->method('createView')
2736
            ->willReturn($this->createMock(FormView::class));
2737
2738
        $this->adminObjectAclManipulator->expects($this->once())
2739
            ->method('createAclUsersForm')
2740
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2741
            ->willReturn($aclUsersForm);
2742
2743
        $this->adminObjectAclManipulator->expects($this->once())
2744
            ->method('createAclRolesForm')
2745
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2746
            ->willReturn($aclRolesForm);
2747
2748
        $aclSecurityHandler = $this->getMockBuilder(AclSecurityHandler::class)
2749
            ->disableOriginalConstructor()
2750
            ->getMock();
2751
2752
        $aclSecurityHandler
2753
            ->method('getObjectPermissions')
2754
            ->willReturn([]);
2755
2756
        $this->admin
2757
            ->method('getSecurityHandler')
2758
            ->willReturn($aclSecurityHandler);
2759
2760
        $this->assertInstanceOf(Response::class, $this->controller->aclAction(null));
2761
2762
        $this->assertSame($this->admin, $this->parameters['admin']);
2763
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2764
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2765
2766
        $this->assertSame('acl', $this->parameters['action']);
2767
        $this->assertSame([], $this->parameters['permissions']);
2768
        $this->assertSame($object, $this->parameters['object']);
2769
        $this->assertInstanceOf(\ArrayIterator::class, $this->parameters['users']);
2770
        $this->assertInstanceOf(\ArrayIterator::class, $this->parameters['roles']);
2771
        $this->assertInstanceOf(FormView::class, $this->parameters['aclUsersForm']);
2772
        $this->assertInstanceOf(FormView::class, $this->parameters['aclRolesForm']);
2773
2774
        $this->assertSame([], $this->session->getFlashBag()->all());
2775
        $this->assertSame('@SonataAdmin/CRUD/acl.html.twig', $this->template);
2776
    }
2777
2778
    public function testAclActionInvalidUpdate(): void
2779
    {
2780
        $this->request->query->set('id', 123);
2781
        $this->request->request->set(AdminObjectAclManipulator::ACL_USERS_FORM_NAME, []);
2782
2783
        $this->admin->expects($this->once())
0 ignored issues
show
The method expects() does not seem to exist on object<Sonata\AdminBundle\Admin\AbstractAdmin>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2784
            ->method('isAclEnabled')
2785
            ->willReturn(true);
2786
2787
        $object = new \stdClass();
2788
2789
        $this->admin->expects($this->once())
2790
            ->method('getObject')
2791
            ->willReturn($object);
2792
2793
        $this->admin
2794
            ->method('checkAccess')
2795
            ->willReturn(true);
2796
2797
        $this->admin
2798
            ->method('getSecurityInformation')
2799
            ->willReturn([]);
2800
2801
        $this->adminObjectAclManipulator->expects($this->once())
2802
            ->method('getMaskBuilderClass')
2803
            ->willReturn(AdminPermissionMap::class);
2804
2805
        $aclUsersForm = $this->getMockBuilder(Form::class)
2806
            ->disableOriginalConstructor()
2807
            ->getMock();
2808
2809
        $aclUsersForm->expects($this->once())
2810
            ->method('isValid')
2811
            ->willReturn(false);
2812
2813
        $aclUsersForm->expects($this->once())
2814
            ->method('createView')
2815
            ->willReturn($this->createMock(FormView::class));
2816
2817
        $aclRolesForm = $this->getMockBuilder(Form::class)
2818
            ->disableOriginalConstructor()
2819
            ->getMock();
2820
2821
        $aclRolesForm->expects($this->once())
2822
            ->method('createView')
2823
            ->willReturn($this->createMock(FormView::class));
2824
2825
        $this->adminObjectAclManipulator->expects($this->once())
2826
            ->method('createAclUsersForm')
2827
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2828
            ->willReturn($aclUsersForm);
2829
2830
        $this->adminObjectAclManipulator->expects($this->once())
2831
            ->method('createAclRolesForm')
2832
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2833
            ->willReturn($aclRolesForm);
2834
2835
        $aclSecurityHandler = $this->getMockBuilder(AclSecurityHandler::class)
2836
            ->disableOriginalConstructor()
2837
            ->getMock();
2838
2839
        $aclSecurityHandler
2840
            ->method('getObjectPermissions')
2841
            ->willReturn([]);
2842
2843
        $this->admin
2844
            ->method('getSecurityHandler')
2845
            ->willReturn($aclSecurityHandler);
2846
2847
        $this->request->setMethod(Request::METHOD_POST);
2848
2849
        $this->assertInstanceOf(Response::class, $this->controller->aclAction(null));
2850
2851
        $this->assertSame($this->admin, $this->parameters['admin']);
2852
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
2853
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
2854
2855
        $this->assertSame('acl', $this->parameters['action']);
2856
        $this->assertSame([], $this->parameters['permissions']);
2857
        $this->assertSame($object, $this->parameters['object']);
2858
        $this->assertInstanceOf(\ArrayIterator::class, $this->parameters['users']);
2859
        $this->assertInstanceOf(\ArrayIterator::class, $this->parameters['roles']);
2860
        $this->assertInstanceOf(FormView::class, $this->parameters['aclUsersForm']);
2861
        $this->assertInstanceOf(FormView::class, $this->parameters['aclRolesForm']);
2862
2863
        $this->assertSame([], $this->session->getFlashBag()->all());
2864
        $this->assertSame('@SonataAdmin/CRUD/acl.html.twig', $this->template);
2865
    }
2866
2867
    public function testAclActionSuccessfulUpdate(): void
2868
    {
2869
        $this->request->query->set('id', 123);
2870
        $this->request->request->set(AdminObjectAclManipulator::ACL_ROLES_FORM_NAME, []);
2871
2872
        $this->admin->expects($this->once())
0 ignored issues
show
The method expects() does not seem to exist on object<Sonata\AdminBundle\Admin\AbstractAdmin>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
2873
            ->method('isAclEnabled')
2874
            ->willReturn(true);
2875
2876
        $object = new \stdClass();
2877
2878
        $this->admin->expects($this->once())
2879
            ->method('getObject')
2880
            ->willReturn($object);
2881
2882
        $this->admin
2883
            ->method('checkAccess')
2884
            ->willReturn(true);
2885
2886
        $this->admin
2887
            ->method('getSecurityInformation')
2888
            ->willReturn([]);
2889
2890
        $this->adminObjectAclManipulator->expects($this->once())
2891
            ->method('getMaskBuilderClass')
2892
            ->willReturn(AdminPermissionMap::class);
2893
2894
        $aclUsersForm = $this->getMockBuilder(Form::class)
2895
            ->disableOriginalConstructor()
2896
            ->getMock();
2897
2898
        $aclUsersForm
2899
            ->method('createView')
2900
            ->willReturn($this->createMock(FormView::class));
2901
2902
        $aclRolesForm = $this->getMockBuilder(Form::class)
2903
            ->disableOriginalConstructor()
2904
            ->getMock();
2905
2906
        $aclRolesForm
2907
            ->method('createView')
2908
            ->willReturn($this->createMock(FormView::class));
2909
2910
        $aclRolesForm->expects($this->once())
2911
            ->method('isValid')
2912
            ->willReturn(true);
2913
2914
        $this->adminObjectAclManipulator->expects($this->once())
2915
            ->method('createAclUsersForm')
2916
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2917
            ->willReturn($aclUsersForm);
2918
2919
        $this->adminObjectAclManipulator->expects($this->once())
2920
            ->method('createAclRolesForm')
2921
            ->with($this->isInstanceOf(AdminObjectAclData::class))
2922
            ->willReturn($aclRolesForm);
2923
2924
        $aclSecurityHandler = $this->getMockBuilder(AclSecurityHandler::class)
2925
            ->disableOriginalConstructor()
2926
            ->getMock();
2927
2928
        $aclSecurityHandler
2929
            ->method('getObjectPermissions')
2930
            ->willReturn([]);
2931
2932
        $this->admin
2933
            ->method('getSecurityHandler')
2934
            ->willReturn($aclSecurityHandler);
2935
2936
        $this->expectTranslate('flash_acl_edit_success', [], 'SonataAdminBundle');
2937
2938
        $this->request->setMethod(Request::METHOD_POST);
2939
2940
        $response = $this->controller->aclAction(null);
2941
2942
        $this->assertInstanceOf(RedirectResponse::class, $response);
2943
2944
        $this->assertSame(['flash_acl_edit_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
2945
        $this->assertSame('stdClass_acl', $response->getTargetUrl());
2946
    }
2947
2948
    public function testHistoryViewRevisionActionAccessDenied(): void
2949
    {
2950
        $this->expectException(AccessDeniedException::class);
2951
2952
        $this->admin
2953
            ->method('getObject')
2954
            ->willReturn(new \stdClass());
2955
2956
        $this->admin->expects($this->once())
2957
            ->method('checkAccess')
2958
            ->with($this->equalTo('historyViewRevision'))
2959
            ->will($this->throwException(new AccessDeniedException()));
2960
2961
        $this->controller->historyViewRevisionAction(null, null);
2962
    }
2963
2964
    public function testHistoryViewRevisionActionNotFoundException(): void
2965
    {
2966
        $this->expectException(NotFoundHttpException::class);
2967
        $this->expectExceptionMessage('unable to find the object with id: 123');
2968
2969
        $this->request->query->set('id', 123);
2970
2971
        $this->admin->expects($this->once())
2972
            ->method('getObject')
2973
            ->willReturn(false);
2974
2975
        $this->controller->historyViewRevisionAction(null, null);
2976
    }
2977
2978
    public function testHistoryViewRevisionActionNoReader(): void
2979
    {
2980
        $this->expectException(NotFoundHttpException::class);
2981
        $this->expectExceptionMessage('unable to find the audit reader for class : Foo');
2982
2983
        $this->request->query->set('id', 123);
2984
2985
        $this->admin->expects($this->once())
2986
            ->method('checkAccess')
2987
            ->with($this->equalTo('historyViewRevision'))
2988
            ->willReturn(true);
2989
2990
        $object = new \stdClass();
2991
2992
        $this->admin->expects($this->once())
2993
            ->method('getObject')
2994
            ->willReturn($object);
2995
2996
        $this->admin
2997
            ->method('getClass')
2998
            ->willReturn('Foo');
2999
3000
        $this->auditManager->expects($this->once())
3001
            ->method('hasReader')
3002
            ->with($this->equalTo('Foo'))
3003
            ->willReturn(false);
3004
3005
        $this->controller->historyViewRevisionAction(null, null);
3006
    }
3007
3008
    public function testHistoryViewRevisionActionNotFoundRevision(): void
3009
    {
3010
        $this->expectException(NotFoundHttpException::class);
3011
        $this->expectExceptionMessage(
3012
            'unable to find the targeted object `123` from the revision `456` with classname : `Foo`'
3013
        );
3014
3015
        $this->request->query->set('id', 123);
3016
3017
        $this->admin->expects($this->once())
3018
            ->method('checkAccess')
3019
            ->with($this->equalTo('historyViewRevision'))
3020
            ->willReturn(true);
3021
3022
        $object = new \stdClass();
3023
3024
        $this->admin->expects($this->once())
3025
            ->method('getObject')
3026
            ->willReturn($object);
3027
3028
        $this->admin
3029
            ->method('getClass')
3030
            ->willReturn('Foo');
3031
3032
        $this->auditManager->expects($this->once())
3033
            ->method('hasReader')
3034
            ->with($this->equalTo('Foo'))
3035
            ->willReturn(true);
3036
3037
        $reader = $this->createMock(AuditReaderInterface::class);
3038
3039
        $this->auditManager->expects($this->once())
3040
            ->method('getReader')
3041
            ->with($this->equalTo('Foo'))
3042
            ->willReturn($reader);
3043
3044
        $reader->expects($this->once())
3045
            ->method('find')
3046
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3047
            ->willReturn(null);
3048
3049
        $this->controller->historyViewRevisionAction(123, 456);
3050
    }
3051
3052
    public function testHistoryViewRevisionAction(): void
3053
    {
3054
        $this->request->query->set('id', 123);
3055
3056
        $this->admin->expects($this->once())
3057
            ->method('checkAccess')
3058
            ->with($this->equalTo('historyViewRevision'))
3059
            ->willReturn(true);
3060
3061
        $object = new \stdClass();
3062
3063
        $this->admin->expects($this->once())
3064
            ->method('getObject')
3065
            ->willReturn($object);
3066
3067
        $this->admin
3068
            ->method('getClass')
3069
            ->willReturn('Foo');
3070
3071
        $this->auditManager->expects($this->once())
3072
            ->method('hasReader')
3073
            ->with($this->equalTo('Foo'))
3074
            ->willReturn(true);
3075
3076
        $reader = $this->createMock(AuditReaderInterface::class);
3077
3078
        $this->auditManager->expects($this->once())
3079
            ->method('getReader')
3080
            ->with($this->equalTo('Foo'))
3081
            ->willReturn($reader);
3082
3083
        $objectRevision = new \stdClass();
3084
        $objectRevision->revision = 456;
3085
3086
        $reader->expects($this->once())
3087
            ->method('find')
3088
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3089
            ->willReturn($objectRevision);
3090
3091
        $this->admin->expects($this->once())
3092
            ->method('setSubject')
3093
            ->with($this->equalTo($objectRevision))
3094
            ->willReturn(null);
3095
3096
        $fieldDescriptionCollection = new FieldDescriptionCollection();
3097
        $this->admin->expects($this->once())
3098
            ->method('getShow')
3099
            ->willReturn($fieldDescriptionCollection);
3100
3101
        $this->assertInstanceOf(Response::class, $this->controller->historyViewRevisionAction(123, 456));
3102
3103
        $this->assertSame($this->admin, $this->parameters['admin']);
3104
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
3105
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
3106
3107
        $this->assertSame('show', $this->parameters['action']);
3108
        $this->assertSame($objectRevision, $this->parameters['object']);
3109
        $this->assertSame($fieldDescriptionCollection, $this->parameters['elements']);
3110
3111
        $this->assertSame([], $this->session->getFlashBag()->all());
3112
        $this->assertSame('@SonataAdmin/CRUD/show.html.twig', $this->template);
3113
    }
3114
3115
    public function testHistoryCompareRevisionsActionAccessDenied(): void
3116
    {
3117
        $this->expectException(AccessDeniedException::class);
3118
3119
        $this->admin->expects($this->once())
3120
            ->method('checkAccess')
3121
            ->with($this->equalTo('historyCompareRevisions'))
3122
            ->will($this->throwException(new AccessDeniedException()));
3123
3124
        $this->controller->historyCompareRevisionsAction(null, null, null);
3125
    }
3126
3127
    public function testHistoryCompareRevisionsActionNotFoundException(): void
3128
    {
3129
        $this->expectException(NotFoundHttpException::class);
3130
        $this->expectExceptionMessage('unable to find the object with id: 123');
3131
3132
        $this->request->query->set('id', 123);
3133
3134
        $this->admin->expects($this->once())
3135
            ->method('checkAccess')
3136
            ->with($this->equalTo('historyCompareRevisions'))
3137
            ->willReturn(true);
3138
3139
        $this->admin->expects($this->once())
3140
            ->method('getObject')
3141
            ->willReturn(false);
3142
3143
        $this->controller->historyCompareRevisionsAction(null, null, null);
3144
    }
3145
3146
    public function testHistoryCompareRevisionsActionNoReader(): void
3147
    {
3148
        $this->expectException(NotFoundHttpException::class);
3149
        $this->expectExceptionMessage('unable to find the audit reader for class : Foo');
3150
3151
        $this->request->query->set('id', 123);
3152
3153
        $this->admin->expects($this->once())
3154
            ->method('checkAccess')
3155
            ->with($this->equalTo('historyCompareRevisions'))
3156
            ->willReturn(true);
3157
3158
        $object = new \stdClass();
3159
3160
        $this->admin->expects($this->once())
3161
            ->method('getObject')
3162
            ->willReturn($object);
3163
3164
        $this->admin
3165
            ->method('getClass')
3166
            ->willReturn('Foo');
3167
3168
        $this->auditManager->expects($this->once())
3169
            ->method('hasReader')
3170
            ->with($this->equalTo('Foo'))
3171
            ->willReturn(false);
3172
3173
        $this->controller->historyCompareRevisionsAction(null, null, null);
3174
    }
3175
3176
    public function testHistoryCompareRevisionsActionNotFoundBaseRevision(): void
3177
    {
3178
        $this->expectException(NotFoundHttpException::class);
3179
        $this->expectExceptionMessage(
3180
            'unable to find the targeted object `123` from the revision `456` with classname : `Foo`'
3181
        );
3182
3183
        $this->request->query->set('id', 123);
3184
3185
        $this->admin->expects($this->once())
3186
            ->method('checkAccess')
3187
            ->with($this->equalTo('historyCompareRevisions'))
3188
            ->willReturn(true);
3189
3190
        $object = new \stdClass();
3191
3192
        $this->admin->expects($this->once())
3193
            ->method('getObject')
3194
            ->willReturn($object);
3195
3196
        $this->admin
3197
            ->method('getClass')
3198
            ->willReturn('Foo');
3199
3200
        $this->auditManager->expects($this->once())
3201
            ->method('hasReader')
3202
            ->with($this->equalTo('Foo'))
3203
            ->willReturn(true);
3204
3205
        $reader = $this->createMock(AuditReaderInterface::class);
3206
3207
        $this->auditManager->expects($this->once())
3208
            ->method('getReader')
3209
            ->with($this->equalTo('Foo'))
3210
            ->willReturn($reader);
3211
3212
        // once because it will not be found and therefore the second call won't be executed
3213
        $reader->expects($this->once())
3214
            ->method('find')
3215
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3216
            ->willReturn(null);
3217
3218
        $this->controller->historyCompareRevisionsAction(123, 456, 789);
3219
    }
3220
3221
    public function testHistoryCompareRevisionsActionNotFoundCompareRevision(): void
3222
    {
3223
        $this->expectException(NotFoundHttpException::class);
3224
        $this->expectExceptionMessage(
3225
            'unable to find the targeted object `123` from the revision `789` with classname : `Foo`'
3226
        );
3227
3228
        $this->request->query->set('id', 123);
3229
3230
        $this->admin->expects($this->once())
3231
            ->method('checkAccess')
3232
            ->with($this->equalTo('historyCompareRevisions'))
3233
            ->willReturn(true);
3234
3235
        $object = new \stdClass();
3236
3237
        $this->admin->expects($this->once())
3238
            ->method('getObject')
3239
            ->willReturn($object);
3240
3241
        $this->admin
3242
            ->method('getClass')
3243
            ->willReturn('Foo');
3244
3245
        $this->auditManager->expects($this->once())
3246
            ->method('hasReader')
3247
            ->with($this->equalTo('Foo'))
3248
            ->willReturn(true);
3249
3250
        $reader = $this->createMock(AuditReaderInterface::class);
3251
3252
        $this->auditManager->expects($this->once())
3253
            ->method('getReader')
3254
            ->with($this->equalTo('Foo'))
3255
            ->willReturn($reader);
3256
3257
        $objectRevision = new \stdClass();
3258
        $objectRevision->revision = 456;
3259
3260
        // first call should return, so the second call will throw an exception
3261
        $reader->expects($this->at(0))
3262
            ->method('find')
3263
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3264
            ->willReturn($objectRevision);
3265
3266
        $reader->expects($this->at(1))
3267
            ->method('find')
3268
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(789))
3269
            ->willReturn(null);
3270
3271
        $this->controller->historyCompareRevisionsAction(123, 456, 789);
3272
    }
3273
3274
    public function testHistoryCompareRevisionsActionAction(): void
3275
    {
3276
        $this->request->query->set('id', 123);
3277
3278
        $this->admin->expects($this->once())
3279
            ->method('checkAccess')
3280
            ->with($this->equalTo('historyCompareRevisions'))
3281
            ->willReturn(true);
3282
3283
        $object = new \stdClass();
3284
3285
        $this->admin->expects($this->once())
3286
            ->method('getObject')
3287
            ->willReturn($object);
3288
3289
        $this->admin
3290
            ->method('getClass')
3291
            ->willReturn('Foo');
3292
3293
        $this->auditManager->expects($this->once())
3294
            ->method('hasReader')
3295
            ->with($this->equalTo('Foo'))
3296
            ->willReturn(true);
3297
3298
        $reader = $this->createMock(AuditReaderInterface::class);
3299
3300
        $this->auditManager->expects($this->once())
3301
            ->method('getReader')
3302
            ->with($this->equalTo('Foo'))
3303
            ->willReturn($reader);
3304
3305
        $objectRevision = new \stdClass();
3306
        $objectRevision->revision = 456;
3307
3308
        $compareObjectRevision = new \stdClass();
3309
        $compareObjectRevision->revision = 789;
3310
3311
        $reader->expects($this->at(0))
3312
            ->method('find')
3313
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(456))
3314
            ->willReturn($objectRevision);
3315
3316
        $reader->expects($this->at(1))
3317
            ->method('find')
3318
            ->with($this->equalTo('Foo'), $this->equalTo(123), $this->equalTo(789))
3319
            ->willReturn($compareObjectRevision);
3320
3321
        $this->admin->expects($this->once())
3322
            ->method('setSubject')
3323
            ->with($this->equalTo($objectRevision))
3324
            ->willReturn(null);
3325
3326
        $fieldDescriptionCollection = new FieldDescriptionCollection();
3327
        $this->admin->expects($this->once())
3328
            ->method('getShow')
3329
            ->willReturn($fieldDescriptionCollection);
3330
3331
        $this->assertInstanceOf(Response::class, $this->controller->historyCompareRevisionsAction(123, 456, 789));
3332
3333
        $this->assertSame($this->admin, $this->parameters['admin']);
3334
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
3335
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
3336
3337
        $this->assertSame('show', $this->parameters['action']);
3338
        $this->assertSame($objectRevision, $this->parameters['object']);
3339
        $this->assertSame($compareObjectRevision, $this->parameters['object_compare']);
3340
        $this->assertSame($fieldDescriptionCollection, $this->parameters['elements']);
3341
3342
        $this->assertSame([], $this->session->getFlashBag()->all());
3343
        $this->assertSame('@SonataAdmin/CRUD/show_compare.html.twig', $this->template);
3344
    }
3345
3346
    public function testBatchActionWrongMethod(): void
3347
    {
3348
        $this->expectException(NotFoundHttpException::class);
3349
        $this->expectExceptionMessage('Invalid request method given "GET", POST expected');
3350
3351
        $this->controller->batchAction();
3352
    }
3353
3354
    /**
3355
     * NEXT_MAJOR: Remove this legacy group.
3356
     *
3357
     * @group legacy
3358
     */
3359
    public function testBatchActionActionNotDefined(): void
3360
    {
3361
        $this->expectException(\RuntimeException::class);
3362
        $this->expectExceptionMessage('The `foo` batch action is not defined');
3363
3364
        $batchActions = [];
3365
3366
        $this->admin->expects($this->once())
3367
            ->method('getBatchActions')
3368
            ->willReturn($batchActions);
3369
3370
        $this->request->setMethod(Request::METHOD_POST);
3371
        $this->request->request->set('data', json_encode(['action' => 'foo', 'idx' => ['123', '456'], 'all_elements' => false]));
3372
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3373
3374
        $this->controller->batchAction();
3375
    }
3376
3377
    public function testBatchActionActionInvalidCsrfToken(): void
3378
    {
3379
        $this->request->setMethod(Request::METHOD_POST);
3380
        $this->request->request->set('data', json_encode(['action' => 'foo', 'idx' => ['123', '456'], 'all_elements' => false]));
3381
        $this->request->request->set('_sonata_csrf_token', 'CSRF-INVALID');
3382
3383
        try {
3384
            $this->controller->batchAction();
3385
        } catch (HttpException $e) {
3386
            $this->assertSame('The csrf token is not valid, CSRF attack?', $e->getMessage());
3387
            $this->assertSame(400, $e->getStatusCode());
3388
        }
3389
    }
3390
3391
    /**
3392
     * NEXT_MAJOR: Remove this legacy group.
3393
     *
3394
     * @group legacy
3395
     */
3396
    public function testBatchActionMethodNotExist(): void
3397
    {
3398
        $this->expectException(\RuntimeException::class);
3399
        $this->expectExceptionMessage(
3400
            'A `Sonata\AdminBundle\Controller\CRUDController::batchActionFoo` method must be callable'
3401
        );
3402
3403
        $batchActions = ['foo' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3404
3405
        $this->admin->expects($this->once())
3406
            ->method('getBatchActions')
3407
            ->willReturn($batchActions);
3408
3409
        $datagrid = $this->createMock(DatagridInterface::class);
3410
        $this->admin->expects($this->once())
3411
            ->method('getDatagrid')
3412
            ->willReturn($datagrid);
3413
3414
        $this->request->setMethod(Request::METHOD_POST);
3415
        $this->request->request->set('data', json_encode(['action' => 'foo', 'idx' => ['123', '456'], 'all_elements' => false]));
3416
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3417
3418
        $this->controller->batchAction();
3419
    }
3420
3421
    /**
3422
     * NEXT_MAJOR: Remove this legacy group.
3423
     *
3424
     * @group legacy
3425
     */
3426
    public function testBatchActionWithoutConfirmation(): void
3427
    {
3428
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3429
3430
        $this->admin->expects($this->once())
3431
            ->method('getBatchActions')
3432
            ->willReturn($batchActions);
3433
3434
        $datagrid = $this->createMock(DatagridInterface::class);
3435
3436
        $query = $this->createMock(ProxyQueryInterface::class);
3437
        $datagrid->expects($this->once())
3438
            ->method('getQuery')
3439
            ->willReturn($query);
3440
3441
        $this->admin->expects($this->once())
3442
            ->method('getDatagrid')
3443
            ->willReturn($datagrid);
3444
3445
        $modelManager = $this->createMock(ModelManagerInterface::class);
3446
3447
        $this->admin->expects($this->once())
3448
            ->method('checkAccess')
3449
            ->with($this->equalTo('batchDelete'))
3450
            ->willReturn(true);
3451
3452
        $this->admin
3453
            ->method('getModelManager')
3454
            ->willReturn($modelManager);
3455
3456
        $this->admin
3457
            ->method('getClass')
3458
            ->willReturn('Foo');
3459
3460
        $modelManager->expects($this->once())
3461
            ->method('addIdentifiersToQuery')
3462
            ->with($this->equalTo('Foo'), $this->equalTo($query), $this->equalTo(['123', '456']))
3463
            ->willReturn(true);
3464
3465
        $this->expectTranslate('flash_batch_delete_success', [], 'SonataAdminBundle');
3466
3467
        $this->request->setMethod(Request::METHOD_POST);
3468
        $this->request->request->set('data', json_encode(['action' => 'delete', 'idx' => ['123', '456'], 'all_elements' => false]));
3469
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3470
3471
        $result = $this->controller->batchAction();
3472
3473
        $this->assertInstanceOf(RedirectResponse::class, $result);
3474
        $this->assertSame(['flash_batch_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
3475
        $this->assertSame('list', $result->getTargetUrl());
3476
    }
3477
3478
    /**
3479
     * NEXT_MAJOR: Remove this legacy group.
3480
     *
3481
     * @group legacy
3482
     */
3483
    public function testBatchActionWithoutConfirmation2(): void
3484
    {
3485
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3486
3487
        $this->admin->expects($this->once())
3488
            ->method('getBatchActions')
3489
            ->willReturn($batchActions);
3490
3491
        $datagrid = $this->createMock(DatagridInterface::class);
3492
3493
        $query = $this->createMock(ProxyQueryInterface::class);
3494
        $datagrid->expects($this->once())
3495
            ->method('getQuery')
3496
            ->willReturn($query);
3497
3498
        $this->admin->expects($this->once())
3499
            ->method('getDatagrid')
3500
            ->willReturn($datagrid);
3501
3502
        $modelManager = $this->createMock(ModelManagerInterface::class);
3503
3504
        $this->admin->expects($this->once())
3505
            ->method('checkAccess')
3506
            ->with($this->equalTo('batchDelete'))
3507
            ->willReturn(true);
3508
3509
        $this->admin
3510
            ->method('getModelManager')
3511
            ->willReturn($modelManager);
3512
3513
        $this->admin
3514
            ->method('getClass')
3515
            ->willReturn('Foo');
3516
3517
        $modelManager->expects($this->once())
3518
            ->method('addIdentifiersToQuery')
3519
            ->with($this->equalTo('Foo'), $this->equalTo($query), $this->equalTo(['123', '456']))
3520
            ->willReturn(true);
3521
3522
        $this->expectTranslate('flash_batch_delete_success', [], 'SonataAdminBundle');
3523
3524
        $this->request->setMethod(Request::METHOD_POST);
3525
        $this->request->request->set('action', 'delete');
3526
        $this->request->request->set('idx', ['123', '456']);
3527
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3528
3529
        $result = $this->controller->batchAction();
3530
3531
        $this->assertInstanceOf(RedirectResponse::class, $result);
3532
        $this->assertSame(['flash_batch_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
3533
        $this->assertSame('list', $result->getTargetUrl());
3534
    }
3535
3536
    /**
3537
     * NEXT_MAJOR: Remove this legacy group.
3538
     *
3539
     * @group legacy
3540
     */
3541
    public function testBatchActionWithConfirmation(): void
3542
    {
3543
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'translation_domain' => 'FooBarBaz', 'ask_confirmation' => true]];
3544
3545
        $this->admin->expects($this->once())
3546
            ->method('getBatchActions')
3547
            ->willReturn($batchActions);
3548
3549
        $data = ['action' => 'delete', 'idx' => ['123', '456'], 'all_elements' => false];
3550
3551
        $this->request->setMethod(Request::METHOD_POST);
3552
        $this->request->request->set('data', json_encode($data));
3553
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3554
3555
        $datagrid = $this->createMock(DatagridInterface::class);
3556
3557
        $this->admin->expects($this->once())
3558
            ->method('getDatagrid')
3559
            ->willReturn($datagrid);
3560
3561
        $form = $this->getMockBuilder(Form::class)
3562
            ->disableOriginalConstructor()
3563
            ->getMock();
3564
3565
        $form->expects($this->once())
3566
            ->method('createView')
3567
            ->willReturn($this->createMock(FormView::class));
3568
3569
        $datagrid->expects($this->once())
3570
            ->method('getForm')
3571
            ->willReturn($form);
3572
3573
        $this->assertInstanceOf(Response::class, $this->controller->batchAction());
3574
3575
        $this->assertSame($this->admin, $this->parameters['admin']);
3576
        $this->assertSame('@SonataAdmin/standard_layout.html.twig', $this->parameters['base_template']);
3577
        $this->assertSame($this->pool, $this->parameters['admin_pool']);
3578
3579
        $this->assertSame('list', $this->parameters['action']);
3580
        $this->assertSame($datagrid, $this->parameters['datagrid']);
3581
        $this->assertInstanceOf(FormView::class, $this->parameters['form']);
3582
        $this->assertSame($data, $this->parameters['data']);
3583
        $this->assertSame('csrf-token-123_sonata.batch', $this->parameters['csrf_token']);
3584
        $this->assertSame('Foo Bar', $this->parameters['action_label']);
3585
3586
        $this->assertSame([], $this->session->getFlashBag()->all());
3587
        $this->assertSame('@SonataAdmin/CRUD/batch_confirmation.html.twig', $this->template);
3588
    }
3589
3590
    /**
3591
     * NEXT_MAJOR: Remove this legacy group.
3592
     *
3593
     * @group legacy
3594
     */
3595
    public function testBatchActionNonRelevantAction(): void
3596
    {
3597
        $controller = new BatchAdminController();
3598
        $controller->setContainer($this->container);
3599
3600
        $batchActions = ['foo' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3601
3602
        $this->admin->expects($this->once())
3603
            ->method('getBatchActions')
3604
            ->willReturn($batchActions);
3605
3606
        $datagrid = $this->createMock(DatagridInterface::class);
3607
3608
        $this->admin->expects($this->once())
3609
            ->method('getDatagrid')
3610
            ->willReturn($datagrid);
3611
3612
        $this->expectTranslate('flash_batch_empty', [], 'SonataAdminBundle');
3613
3614
        $this->request->setMethod(Request::METHOD_POST);
3615
        $this->request->request->set('action', 'foo');
3616
        $this->request->request->set('idx', ['789']);
3617
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3618
3619
        $result = $controller->batchAction();
3620
3621
        $this->assertInstanceOf(RedirectResponse::class, $result);
3622
        $this->assertSame(['flash_batch_empty'], $this->session->getFlashBag()->get('sonata_flash_info'));
3623
        $this->assertSame('list', $result->getTargetUrl());
3624
    }
3625
3626
    public function testBatchActionWithCustomConfirmationTemplate(): void
3627
    {
3628
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => true, 'template' => 'custom_template.html.twig']];
3629
3630
        $this->admin->expects($this->once())
3631
            ->method('getBatchActions')
3632
            ->willReturn($batchActions);
3633
3634
        $data = ['action' => 'delete', 'idx' => ['123', '456'], 'all_elements' => false];
3635
3636
        $this->request->setMethod(Request::METHOD_POST);
3637
        $this->request->request->set('data', json_encode($data));
3638
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3639
3640
        $datagrid = $this->createMock(DatagridInterface::class);
3641
3642
        $this->admin->expects($this->once())
3643
            ->method('getDatagrid')
3644
            ->willReturn($datagrid);
3645
3646
        $form = $this->createMock(Form::class);
3647
3648
        $form->expects($this->once())
3649
            ->method('createView')
3650
            ->willReturn($this->createMock(FormView::class));
3651
3652
        $datagrid->expects($this->once())
3653
            ->method('getForm')
3654
            ->willReturn($form);
3655
3656
        $this->controller->batchAction();
3657
3658
        $this->assertSame('custom_template.html.twig', $this->template);
3659
    }
3660
3661
    /**
3662
     * NEXT_MAJOR: Remove this legacy group.
3663
     *
3664
     * @group legacy
3665
     */
3666
    public function testBatchActionNonRelevantAction2(): void
3667
    {
3668
        $controller = new BatchAdminController();
3669
        $controller->setContainer($this->container);
3670
3671
        $batchActions = ['foo' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3672
3673
        $this->admin->expects($this->once())
3674
            ->method('getBatchActions')
3675
            ->willReturn($batchActions);
3676
3677
        $datagrid = $this->createMock(DatagridInterface::class);
3678
3679
        $this->admin->expects($this->once())
3680
            ->method('getDatagrid')
3681
            ->willReturn($datagrid);
3682
3683
        $this->expectTranslate('flash_foo_error', [], 'SonataAdminBundle');
3684
3685
        $this->request->setMethod(Request::METHOD_POST);
3686
        $this->request->request->set('action', 'foo');
3687
        $this->request->request->set('idx', ['999']);
3688
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3689
3690
        $result = $controller->batchAction();
3691
3692
        $this->assertInstanceOf(RedirectResponse::class, $result);
3693
        $this->assertSame(['flash_foo_error'], $this->session->getFlashBag()->get('sonata_flash_info'));
3694
        $this->assertSame('list', $result->getTargetUrl());
3695
    }
3696
3697
    /**
3698
     * NEXT_MAJOR: Remove this legacy group.
3699
     *
3700
     * @group legacy
3701
     */
3702
    public function testBatchActionNoItems(): void
3703
    {
3704
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => true]];
3705
3706
        $this->admin->expects($this->once())
3707
            ->method('getBatchActions')
3708
            ->willReturn($batchActions);
3709
3710
        $datagrid = $this->createMock(DatagridInterface::class);
3711
3712
        $this->admin->expects($this->once())
3713
            ->method('getDatagrid')
3714
            ->willReturn($datagrid);
3715
3716
        $this->expectTranslate('flash_batch_empty', [], 'SonataAdminBundle');
3717
3718
        $this->request->setMethod(Request::METHOD_POST);
3719
        $this->request->request->set('action', 'delete');
3720
        $this->request->request->set('idx', []);
3721
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3722
3723
        $result = $this->controller->batchAction();
3724
3725
        $this->assertInstanceOf(RedirectResponse::class, $result);
3726
        $this->assertSame(['flash_batch_empty'], $this->session->getFlashBag()->get('sonata_flash_info'));
3727
        $this->assertSame('list', $result->getTargetUrl());
3728
    }
3729
3730
    /**
3731
     * NEXT_MAJOR: Remove this legacy group.
3732
     *
3733
     * @group legacy
3734
     */
3735
    public function testBatchActionNoItemsEmptyQuery(): void
3736
    {
3737
        $controller = new BatchAdminController();
3738
        $controller->setContainer($this->container);
3739
3740
        $batchActions = ['bar' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3741
3742
        $this->admin->expects($this->once())
3743
            ->method('getBatchActions')
3744
            ->willReturn($batchActions);
3745
3746
        $datagrid = $this->createMock(DatagridInterface::class);
3747
3748
        $query = $this->createMock(ProxyQueryInterface::class);
3749
        $datagrid->expects($this->once())
3750
            ->method('getQuery')
3751
            ->willReturn($query);
3752
3753
        $this->admin->expects($this->once())
3754
            ->method('getDatagrid')
3755
            ->willReturn($datagrid);
3756
3757
        $modelManager = $this->createMock(ModelManagerInterface::class);
3758
3759
        $this->admin
3760
            ->method('getModelManager')
3761
            ->willReturn($modelManager);
3762
3763
        $this->admin
3764
            ->method('getClass')
3765
            ->willReturn('Foo');
3766
3767
        $this->request->setMethod(Request::METHOD_POST);
3768
        $this->request->request->set('action', 'bar');
3769
        $this->request->request->set('idx', []);
3770
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3771
3772
        $this->expectTranslate('flash_batch_no_elements_processed', [], 'SonataAdminBundle');
3773
        $result = $controller->batchAction();
3774
3775
        $this->assertInstanceOf(Response::class, $result);
3776
        $this->assertRegExp('/Redirecting to list/', $result->getContent());
3777
    }
3778
3779
    /**
3780
     * NEXT_MAJOR: Remove this legacy group.
3781
     *
3782
     * @group legacy
3783
     */
3784
    public function testBatchActionWithRequesData(): void
3785
    {
3786
        $batchActions = ['delete' => ['label' => 'Foo Bar', 'ask_confirmation' => false]];
3787
3788
        $this->admin->expects($this->once())
3789
            ->method('getBatchActions')
3790
            ->willReturn($batchActions);
3791
3792
        $datagrid = $this->createMock(DatagridInterface::class);
3793
3794
        $query = $this->createMock(ProxyQueryInterface::class);
3795
        $datagrid->expects($this->once())
3796
            ->method('getQuery')
3797
            ->willReturn($query);
3798
3799
        $this->admin->expects($this->once())
3800
            ->method('getDatagrid')
3801
            ->willReturn($datagrid);
3802
3803
        $modelManager = $this->createMock(ModelManagerInterface::class);
3804
3805
        $this->admin->expects($this->once())
3806
            ->method('checkAccess')
3807
            ->with($this->equalTo('batchDelete'))
3808
            ->willReturn(true);
3809
3810
        $this->admin
3811
            ->method('getModelManager')
3812
            ->willReturn($modelManager);
3813
3814
        $this->admin
3815
            ->method('getClass')
3816
            ->willReturn('Foo');
3817
3818
        $modelManager->expects($this->once())
3819
            ->method('addIdentifiersToQuery')
3820
            ->with($this->equalTo('Foo'), $this->equalTo($query), $this->equalTo(['123', '456']))
3821
            ->willReturn(true);
3822
3823
        $this->expectTranslate('flash_batch_delete_success', [], 'SonataAdminBundle');
3824
3825
        $this->request->setMethod(Request::METHOD_POST);
3826
        $this->request->request->set('data', json_encode(['action' => 'delete', 'idx' => ['123', '456'], 'all_elements' => false]));
3827
        $this->request->request->set('foo', 'bar');
3828
        $this->request->request->set('_sonata_csrf_token', 'csrf-token-123_sonata.batch');
3829
3830
        $result = $this->controller->batchAction();
3831
3832
        $this->assertInstanceOf(RedirectResponse::class, $result);
3833
        $this->assertSame(['flash_batch_delete_success'], $this->session->getFlashBag()->get('sonata_flash_success'));
3834
        $this->assertSame('list', $result->getTargetUrl());
3835
        $this->assertSame('bar', $this->request->request->get('foo'));
3836
    }
3837
3838
    /**
3839
     * @expectedDeprecation Method Sonata\AdminBundle\Controller\CRUDController::render has been renamed to Sonata\AdminBundle\Controller\CRUDController::renderWithExtraParams.
3840
     */
3841
    public function testRenderIsDeprecated(): void
3842
    {
3843
        $this->controller->render('toto.html.twig');
3844
    }
3845
3846
    public function getCsrfProvider()
3847
    {
3848
        return $this->csrfProvider;
3849
    }
3850
3851
    public function getToStringValues()
3852
    {
3853
        return [
3854
            ['', ''],
3855
            ['Foo', 'Foo'],
3856
            ['&lt;a href=&quot;http://foo&quot;&gt;Bar&lt;/a&gt;', '<a href="http://foo">Bar</a>'],
3857
            ['&lt;&gt;&amp;&quot;&#039;abcdefghijklmnopqrstuvwxyz*-+.,?_()[]\/', '<>&"\'abcdefghijklmnopqrstuvwxyz*-+.,?_()[]\/'],
3858
        ];
3859
    }
3860
3861
    private function assertLoggerLogsModelManagerException($subject, string $method): void
3862
    {
3863
        $exception = new ModelManagerException(
3864
            $message = 'message',
3865
            1234,
3866
            new \Exception($previousExceptionMessage = 'very useful message')
3867
        );
3868
3869
        $subject->expects($this->once())
3870
            ->method($method)
3871
            ->willReturnCallback(static function () use ($exception): void {
3872
                throw $exception;
3873
            });
3874
3875
        $this->logger->expects($this->once())
3876
            ->method('error')
3877
            ->with($message, [
3878
                'exception' => $exception,
3879
                'previous_exception_message' => $previousExceptionMessage,
3880
            ]);
3881
    }
3882
3883
    private function expectTranslate(
3884
        string $id,
3885
        array $parameters = [],
3886
        ?string $domain = null,
3887
        ?string $locale = null
3888
    ): void {
3889
        $this->translator->expects($this->once())
3890
            ->method('trans')
3891
            ->with($this->equalTo($id), $this->equalTo($parameters), $this->equalTo($domain), $this->equalTo($locale))
3892
            ->willReturn($id);
3893
    }
3894
}
3895