Completed
Push — 3.x ( e95e95...638cd1 )
by Oskar
05:54
created

tests/Twig/Extension/SonataAdminExtensionTest.php (2 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\Twig\Extension;
15
16
use PHPUnit\Framework\TestCase;
17
use Prophecy\Argument;
18
use Psr\Log\LoggerInterface;
19
use Sonata\AdminBundle\Admin\AbstractAdmin;
20
use Sonata\AdminBundle\Admin\AdminInterface;
21
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
22
use Sonata\AdminBundle\Admin\Pool;
23
use Sonata\AdminBundle\Exception\NoValueException;
24
use Sonata\AdminBundle\Templating\TemplateRegistryInterface;
25
use Sonata\AdminBundle\Tests\Fixtures\Entity\FooToString;
26
use Sonata\AdminBundle\Twig\Extension\SonataAdminExtension;
27
use Symfony\Bridge\Twig\AppVariable;
28
use Symfony\Bridge\Twig\Extension\RoutingExtension;
29
use Symfony\Bridge\Twig\Extension\TranslationExtension;
30
use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader;
31
use Symfony\Component\Config\FileLocator;
32
use Symfony\Component\DependencyInjection\ContainerInterface;
33
use Symfony\Component\HttpFoundation\Request;
34
use Symfony\Component\Routing\Generator\UrlGenerator;
35
use Symfony\Component\Routing\Loader\XmlFileLoader;
36
use Symfony\Component\Routing\RequestContext;
37
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
38
use Symfony\Component\Translation\DependencyInjection\TranslationDumperPass;
39
use Symfony\Component\Translation\Loader\XliffFileLoader;
40
use Symfony\Component\Translation\MessageSelector;
41
use Symfony\Component\Translation\Translator;
42
use Symfony\Component\Translation\TranslatorInterface;
43
44
/**
45
 * Test for SonataAdminExtension.
46
 *
47
 * @author Andrej Hudec <[email protected]>
48
 */
49
class SonataAdminExtensionTest extends TestCase
50
{
51
    /**
52
     * @var SonataAdminExtension
53
     */
54
    private $twigExtension;
55
56
    /**
57
     * @var \Twig_Environment
58
     */
59
    private $environment;
60
61
    /**
62
     * @var AdminInterface
63
     */
64
    private $admin;
65
66
    /**
67
     * @var AdminInterface
68
     */
69
    private $adminBar;
70
71
    /**
72
     * @var FieldDescriptionInterface
73
     */
74
    private $fieldDescription;
75
76
    /**
77
     * @var \stdClass
78
     */
79
    private $object;
80
81
    /**
82
     * @var Pool
83
     */
84
    private $pool;
85
86
    /**
87
     * @var LoggerInterface
88
     */
89
    private $logger;
90
91
    /**
92
     * @var string[]
93
     */
94
    private $xEditableTypeMapping;
95
96
    /**
97
     * @var TranslatorInterface
98
     */
99
    private $translator;
100
101
    /**
102
     * @var ContainerInterface
103
     */
104
    private $container;
105
106
    /**
107
     * @var TemplateRegistryInterface
108
     */
109
    private $templateRegistry;
110
111
    /**
112
     * @var AuthorizationCheckerInterface
113
     */
114
    private $securityChecker;
115
116
    public function setUp(): void
117
    {
118
        date_default_timezone_set('Europe/London');
119
120
        $container = $this->getMockForAbstractClass(ContainerInterface::class);
121
122
        $this->pool = new Pool($container, '', '');
123
        $this->pool->setAdminServiceIds(['sonata_admin_foo_service']);
124
        $this->pool->setAdminClasses(['fooClass' => ['sonata_admin_foo_service']]);
125
126
        $this->logger = $this->getMockForAbstractClass(LoggerInterface::class);
127
        $this->xEditableTypeMapping = [
0 ignored issues
show
Documentation Bug introduced by
It seems like array('choice' => 'selec...umber', 'url' => 'url') of type array<string,string,{"ch...tring","url":"string"}> is incompatible with the declared type array<integer,string> of property $xEditableTypeMapping.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
128
            'choice' => 'select',
129
            'boolean' => 'select',
130
            'text' => 'text',
131
            'textarea' => 'textarea',
132
            'html' => 'textarea',
133
            'email' => 'email',
134
            'string' => 'text',
135
            'smallint' => 'text',
136
            'bigint' => 'text',
137
            'integer' => 'number',
138
            'decimal' => 'number',
139
            'currency' => 'number',
140
            'percent' => 'number',
141
            'url' => 'url',
142
        ];
143
144
        // translation extension
145
        $translator = new Translator(
146
            'en',
147
            // NEXT_MAJOR: simplify this when dropping symfony < 3.4
148
            class_exists(TranslationDumperPass::class) ? null : new MessageSelector()
149
        );
150
        $translator->addLoader('xlf', new XliffFileLoader());
151
        $translator->addResource(
152
            'xlf',
153
            __DIR__.'/../../../src/Resources/translations/SonataAdminBundle.en.xliff',
154
            'en',
155
            'SonataAdminBundle'
156
        );
157
158
        $this->translator = $translator;
159
160
        $this->templateRegistry = $this->prophesize(TemplateRegistryInterface::class);
161
        $this->container = $this->prophesize(ContainerInterface::class);
162
        $this->container->get('sonata_admin_foo_service.template_registry')->willReturn($this->templateRegistry->reveal());
163
164
        $this->securityChecker = $this->prophesize(AuthorizationCheckerInterface::class);
165
        $this->securityChecker->isGranted(['foo', 'bar'], null)->willReturn(false);
166
        $this->securityChecker->isGranted(Argument::type('string'), null)->willReturn(true);
167
168
        $this->twigExtension = new SonataAdminExtension(
169
            $this->pool, $this->logger, $this->translator, $this->container->reveal(), $this->securityChecker->reveal()
170
        );
171
        $this->twigExtension->setXEditableTypeMapping($this->xEditableTypeMapping);
172
173
        $request = $this->createMock(Request::class);
174
        $request->expects($this->any())->method('get')->with('_sonata_admin')->willReturn('sonata_admin_foo_service');
175
176
        $loader = new StubFilesystemLoader([
177
            __DIR__.'/../../../src/Resources/views/CRUD',
178
        ]);
179
        $loader->addPath(__DIR__.'/../../../src/Resources/views/', 'SonataAdmin');
180
181
        $this->environment = new \Twig_Environment($loader, [
182
            'strict_variables' => true,
183
            'cache' => false,
184
            'autoescape' => 'html',
185
            'optimizations' => 0,
186
        ]);
187
        $this->environment->addExtension($this->twigExtension);
188
        $this->environment->addExtension(new TranslationExtension($translator));
189
        $this->environment->addExtension(new FakeTemplateRegistryExtension());
190
191
        // routing extension
192
        $xmlFileLoader = new XmlFileLoader(new FileLocator([__DIR__.'/../../../src/Resources/config/routing']));
193
        $routeCollection = $xmlFileLoader->load('sonata_admin.xml');
194
195
        $xmlFileLoader = new XmlFileLoader(new FileLocator([__DIR__.'/../../Fixtures/Resources/config/routing']));
196
        $testRouteCollection = $xmlFileLoader->load('routing.xml');
197
198
        $routeCollection->addCollection($testRouteCollection);
199
        $requestContext = new RequestContext();
200
        $urlGenerator = new UrlGenerator($routeCollection, $requestContext);
201
        $this->environment->addExtension(new RoutingExtension($urlGenerator));
202
        $this->environment->addExtension(new \Twig_Extensions_Extension_Text());
203
204
        // initialize object
205
        $this->object = new \stdClass();
206
207
        // initialize admin
208
        $this->admin = $this->createMock(AbstractAdmin::class);
209
210
        $this->admin->expects($this->any())
211
            ->method('getCode')
212
            ->willReturn('sonata_admin_foo_service');
213
214
        $this->admin->expects($this->any())
215
            ->method('id')
216
            ->with($this->equalTo($this->object))
217
            ->willReturn(12345);
218
219
        $this->admin->expects($this->any())
220
            ->method('getNormalizedIdentifier')
221
            ->with($this->equalTo($this->object))
222
            ->willReturn(12345);
223
224
        $this->admin->expects($this->any())
225
            ->method('trans')
226
            ->willReturnCallback(static function ($id, $parameters = [], $domain = null) use ($translator) {
227
                return $translator->trans($id, $parameters, $domain);
228
            });
229
230
        $this->adminBar = $this->createMock(AbstractAdmin::class);
231
        $this->adminBar->expects($this->any())
232
            ->method('hasAccess')
233
            ->willReturn(true);
234
        $this->adminBar->expects($this->any())
235
            ->method('getNormalizedIdentifier')
236
            ->with($this->equalTo($this->object))
237
            ->willReturn(12345);
238
239
        $container->expects($this->any())
240
            ->method('get')
241
            ->willReturnCallback(function ($id) {
242
                if ('sonata_admin_foo_service' === $id) {
243
                    return $this->admin;
244
                } elseif ('sonata_admin_bar_service' === $id) {
245
                    return $this->adminBar;
246
                }
247
            });
248
249
        // initialize field description
250
        $this->fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
251
252
        $this->fieldDescription->expects($this->any())
253
            ->method('getName')
254
            ->willReturn('fd_name');
255
256
        $this->fieldDescription->expects($this->any())
257
            ->method('getAdmin')
258
            ->willReturn($this->admin);
259
260
        $this->fieldDescription->expects($this->any())
261
            ->method('getLabel')
262
            ->willReturn('Data');
263
    }
264
265
    /**
266
     * @group legacy
267
     * @expectedDeprecation The Sonata\AdminBundle\Admin\AbstractAdmin::getTemplate method is deprecated (since 3.34, will be dropped in 4.0. Use TemplateRegistry services instead).
268
     * @dataProvider getRenderListElementTests
269
     */
270
    public function testRenderListElement($expected, $type, $value, array $options): void
271
    {
272
        $this->admin->expects($this->any())
273
            ->method('getPersistentParameters')
274
            ->willReturn(['context' => 'foo']);
275
276
        $this->admin->expects($this->any())
277
            ->method('hasAccess')
278
            ->willReturn(true);
279
280
        // NEXT_MAJOR: Remove this line
281
        $this->admin->expects($this->any())
282
            ->method('getTemplate')
283
            ->with('base_list_field')
284
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
285
286
        $this->templateRegistry->getTemplate('base_list_field')->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
287
288
        $this->fieldDescription->expects($this->any())
289
            ->method('getValue')
290
            ->willReturn($value);
291
292
        $this->fieldDescription->expects($this->any())
293
            ->method('getType')
294
            ->willReturn($type);
295
296
        $this->fieldDescription->expects($this->any())
297
            ->method('getOptions')
298
            ->willReturn($options);
299
300
        $this->fieldDescription->expects($this->any())
301
            ->method('getOption')
302
            ->willReturnCallback(static function ($name, $default = null) use ($options) {
303
                return $options[$name] ?? $default;
304
            });
305
306
        $this->fieldDescription->expects($this->any())
307
            ->method('getTemplate')
308
            ->willReturnCallback(static function () use ($type) {
309
                switch ($type) {
310
                    case 'string':
311
                        return '@SonataAdmin/CRUD/list_string.html.twig';
312
                    case 'boolean':
313
                        return '@SonataAdmin/CRUD/list_boolean.html.twig';
314
                    case 'datetime':
315
                        return '@SonataAdmin/CRUD/list_datetime.html.twig';
316
                    case 'date':
317
                        return '@SonataAdmin/CRUD/list_date.html.twig';
318
                    case 'time':
319
                        return '@SonataAdmin/CRUD/list_time.html.twig';
320
                    case 'currency':
321
                        return '@SonataAdmin/CRUD/list_currency.html.twig';
322
                    case 'percent':
323
                        return '@SonataAdmin/CRUD/list_percent.html.twig';
324
                    case 'email':
325
                        return '@SonataAdmin/CRUD/list_email.html.twig';
326
                    case 'choice':
327
                        return '@SonataAdmin/CRUD/list_choice.html.twig';
328
                    case 'array':
329
                        return '@SonataAdmin/CRUD/list_array.html.twig';
330
                    case 'trans':
331
                        return '@SonataAdmin/CRUD/list_trans.html.twig';
332
                    case 'url':
333
                        return '@SonataAdmin/CRUD/list_url.html.twig';
334
                    case 'html':
335
                        return '@SonataAdmin/CRUD/list_html.html.twig';
336
                    case 'nonexistent':
337
                        // template doesn`t exist
338
                        return '@SonataAdmin/CRUD/list_nonexistent_template.html.twig';
339
                    default:
340
                        return false;
341
                }
342
            });
343
344
        $this->assertSame(
345
            $this->removeExtraWhitespace($expected),
346
            $this->removeExtraWhitespace($this->twigExtension->renderListElement(
347
                $this->environment,
348
                $this->object,
349
                $this->fieldDescription
350
            ))
351
        );
352
    }
353
354
    /**
355
     * @dataProvider getDeprecatedRenderListElementTests
356
     * @group legacy
357
     */
358
    public function testDeprecatedRenderListElement($expected, $value, array $options): void
359
    {
360
        $this->admin->expects($this->any())
361
            ->method('hasAccess')
362
            ->willReturn(true);
363
364
        // NEXT_MAJOR: Remove this line
365
        $this->admin->expects($this->any())
366
            ->method('getTemplate')
367
            ->with('base_list_field')
368
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
369
370
        $this->templateRegistry->getTemplate('base_list_field')->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
371
372
        $this->fieldDescription->expects($this->any())
373
            ->method('getValue')
374
            ->willReturn($value);
375
376
        $this->fieldDescription->expects($this->any())
377
            ->method('getType')
378
            ->willReturn('nonexistent');
379
380
        $this->fieldDescription->expects($this->any())
381
            ->method('getOptions')
382
            ->willReturn($options);
383
384
        $this->fieldDescription->expects($this->any())
385
            ->method('getOption')
386
            ->willReturnCallback(static function ($name, $default = null) use ($options) {
387
                return $options[$name] ?? $default;
388
            });
389
390
        $this->fieldDescription->expects($this->any())
391
            ->method('getTemplate')
392
            ->willReturn('@SonataAdmin/CRUD/list_nonexistent_template.html.twig');
393
394
        $this->assertSame(
395
            $this->removeExtraWhitespace($expected),
396
            $this->removeExtraWhitespace($this->twigExtension->renderListElement(
397
                $this->environment,
398
                $this->object,
399
                $this->fieldDescription
400
            ))
401
        );
402
    }
403
404
    public function getDeprecatedRenderListElementTests()
405
    {
406
        return [
407
            [
408
                '<td class="sonata-ba-list-field sonata-ba-list-field-nonexistent" objectId="12345"> Example </td>',
409
                'Example',
410
                [],
411
            ],
412
            [
413
                '<td class="sonata-ba-list-field sonata-ba-list-field-nonexistent" objectId="12345"> </td>',
414
                null,
415
                [],
416
            ],
417
        ];
418
    }
419
420
    public function getRenderListElementTests()
421
    {
422
        return [
423
            [
424
                '<td class="sonata-ba-list-field sonata-ba-list-field-string" objectId="12345"> Example </td>',
425
                'string',
426
                'Example',
427
                [],
428
            ],
429
            [
430
                '<td class="sonata-ba-list-field sonata-ba-list-field-string" objectId="12345"> </td>',
431
                'string',
432
                null,
433
                [],
434
            ],
435
            [
436
                '<td class="sonata-ba-list-field sonata-ba-list-field-text" objectId="12345"> Example </td>',
437
                'text',
438
                'Example',
439
                [],
440
            ],
441
            [
442
                '<td class="sonata-ba-list-field sonata-ba-list-field-text" objectId="12345"> </td>',
443
                'text',
444
                null,
445
                [],
446
            ],
447
            [
448
                '<td class="sonata-ba-list-field sonata-ba-list-field-textarea" objectId="12345"> Example </td>',
449
                'textarea',
450
                'Example',
451
                [],
452
            ],
453
            [
454
                '<td class="sonata-ba-list-field sonata-ba-list-field-textarea" objectId="12345"> </td>',
455
                'textarea',
456
                null,
457
                [],
458
            ],
459
            'datetime field' => [
460
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345">
461
                    <time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00">
462
                        December 24, 2013 10:11
463
                    </time>
464
                </td>',
465
                'datetime',
466
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
467
                [],
468
            ],
469
            [
470
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345">
471
                    <time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00">
472
                        December 24, 2013 18:11
473
                    </time>
474
                </td>',
475
                'datetime',
476
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
477
                ['timezone' => 'Asia/Hong_Kong'],
478
            ],
479
            [
480
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345"> &nbsp; </td>',
481
                'datetime',
482
                null,
483
                [],
484
            ],
485
            [
486
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345">
487
                    <time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00">
488
                        24.12.2013 10:11:12
489
                    </time>
490
                </td>',
491
                'datetime',
492
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
493
                ['format' => 'd.m.Y H:i:s'],
494
            ],
495
            [
496
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345"> &nbsp; </td>',
497
                'datetime',
498
                null,
499
                ['format' => 'd.m.Y H:i:s'],
500
            ],
501
            [
502
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345">
503
                    <time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00">
504
                        24.12.2013 18:11:12
505
                    </time>
506
                </td>',
507
                'datetime',
508
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
509
                ['format' => 'd.m.Y H:i:s', 'timezone' => 'Asia/Hong_Kong'],
510
            ],
511
            [
512
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345"> &nbsp; </td>',
513
                'datetime',
514
                null,
515
                ['format' => 'd.m.Y H:i:s', 'timezone' => 'Asia/Hong_Kong'],
516
            ],
517
            [
518
                '<td class="sonata-ba-list-field sonata-ba-list-field-date" objectId="12345">
519
                    <time datetime="2013-12-24" title="2013-12-24">
520
                        December 24, 2013
521
                    </time>
522
                </td>',
523
                'date',
524
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
525
                [],
526
            ],
527
            [
528
                '<td class="sonata-ba-list-field sonata-ba-list-field-date" objectId="12345"> &nbsp; </td>',
529
                'date',
530
                null,
531
                [],
532
            ],
533
            [
534
                '<td class="sonata-ba-list-field sonata-ba-list-field-date" objectId="12345">
535
                    <time datetime="2013-12-24" title="2013-12-24">
536
                        24.12.2013
537
                    </time>
538
                </td>',
539
                'date',
540
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
541
                ['format' => 'd.m.Y'],
542
            ],
543
            [
544
                '<td class="sonata-ba-list-field sonata-ba-list-field-date" objectId="12345"> &nbsp; </td>',
545
                'date',
546
                null,
547
                ['format' => 'd.m.Y'],
548
            ],
549
            [
550
                '<td class="sonata-ba-list-field sonata-ba-list-field-time" objectId="12345">
551
                    <time datetime="10:11:12+00:00" title="10:11:12+00:00">
552
                        10:11:12
553
                    </time>
554
                </td>',
555
                'time',
556
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
557
                [],
558
            ],
559
            [
560
                '<td class="sonata-ba-list-field sonata-ba-list-field-time" objectId="12345">
561
                    <time datetime="10:11:12+00:00" title="10:11:12+00:00">
562
                        18:11:12
563
                    </time>
564
                </td>',
565
                'time',
566
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
567
                ['timezone' => 'Asia/Hong_Kong'],
568
            ],
569
            [
570
                '<td class="sonata-ba-list-field sonata-ba-list-field-time" objectId="12345"> &nbsp; </td>',
571
                'time',
572
                null,
573
                [],
574
            ],
575
            [
576
                '<td class="sonata-ba-list-field sonata-ba-list-field-number" objectId="12345"> 10.746135 </td>',
577
                'number', 10.746135,
578
                [],
579
            ],
580
            [
581
                '<td class="sonata-ba-list-field sonata-ba-list-field-number" objectId="12345"> </td>',
582
                'number',
583
                null,
584
                [],
585
            ],
586
            [
587
                '<td class="sonata-ba-list-field sonata-ba-list-field-integer" objectId="12345"> 5678 </td>',
588
                'integer',
589
                5678,
590
                [],
591
            ],
592
            [
593
                '<td class="sonata-ba-list-field sonata-ba-list-field-integer" objectId="12345"> </td>',
594
                'integer',
595
                null,
596
                [],
597
            ],
598
            [
599
                '<td class="sonata-ba-list-field sonata-ba-list-field-percent" objectId="12345"> 1074.6135 % </td>',
600
                'percent',
601
                10.746135,
602
                [],
603
            ],
604
            [
605
                '<td class="sonata-ba-list-field sonata-ba-list-field-percent" objectId="12345"> 0 % </td>',
606
                'percent',
607
                null,
608
                [],
609
            ],
610
            [
611
                '<td class="sonata-ba-list-field sonata-ba-list-field-currency" objectId="12345"> EUR 10.746135 </td>',
612
                'currency',
613
                10.746135,
614
                ['currency' => 'EUR'],
615
            ],
616
            [
617
                '<td class="sonata-ba-list-field sonata-ba-list-field-currency" objectId="12345"> </td>',
618
                'currency',
619
                null,
620
                ['currency' => 'EUR'],
621
            ],
622
            [
623
                '<td class="sonata-ba-list-field sonata-ba-list-field-currency" objectId="12345"> GBP 51.23456 </td>',
624
                'currency',
625
                51.23456,
626
                ['currency' => 'GBP'],
627
            ],
628
            [
629
                '<td class="sonata-ba-list-field sonata-ba-list-field-currency" objectId="12345"> </td>',
630
                'currency',
631
                null,
632
                ['currency' => 'GBP'],
633
            ],
634
            [
635
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> &nbsp; </td>',
636
                'email',
637
                null,
638
                [],
639
            ],
640
            [
641
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> <a href="mailto:[email protected]">[email protected]</a> </td>',
642
                'email',
643
                '[email protected]',
644
                [],
645
            ],
646
            [
647
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345">
648
                    <a href="mailto:[email protected]">[email protected]</a> </td>',
649
                'email',
650
                '[email protected]',
651
                ['as_string' => false],
652
            ],
653
            [
654
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> [email protected] </td>',
655
                'email',
656
                '[email protected]',
657
                ['as_string' => true],
658
            ],
659
            [
660
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345">
661
                    <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['subject' => 'Main Theme', 'body' => 'Message Body']).'">[email protected]</a>  </td>',
662
                'email',
663
                '[email protected]',
664
                ['subject' => 'Main Theme', 'body' => 'Message Body'],
665
            ],
666
            [
667
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345">
668
                    <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['subject' => 'Main Theme']).'">[email protected]</a>  </td>',
669
                'email',
670
                '[email protected]',
671
                ['subject' => 'Main Theme'],
672
            ],
673
            [
674
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345">
675
                    <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['body' => 'Message Body']).'">[email protected]</a>  </td>',
676
                'email',
677
                '[email protected]',
678
                ['body' => 'Message Body'],
679
            ],
680
            [
681
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> [email protected] </td>',
682
                'email',
683
                '[email protected]',
684
                ['as_string' => true, 'subject' => 'Main Theme', 'body' => 'Message Body'],
685
            ],
686
            [
687
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> [email protected] </td>',
688
                'email',
689
                '[email protected]',
690
                ['as_string' => true, 'body' => 'Message Body'],
691
            ],
692
            [
693
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> [email protected] </td>',
694
                'email',
695
                '[email protected]',
696
                ['as_string' => true, 'subject' => 'Main Theme'],
697
            ],
698
            [
699
                '<td class="sonata-ba-list-field sonata-ba-list-field-array" objectId="12345">
700
                    [1 => First] [2 => Second]
701
                </td>',
702
                'array',
703
                [1 => 'First', 2 => 'Second'],
704
                [],
705
            ],
706
            [
707
                '<td class="sonata-ba-list-field sonata-ba-list-field-array" objectId="12345"> </td>',
708
                'array',
709
                null,
710
                [],
711
            ],
712
            [
713
                '<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
714
                    <span class="label label-success">yes</span>
715
                </td>',
716
                'boolean',
717
                true,
718
                ['editable' => false],
719
            ],
720
            [
721
                '<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
722
                    <span class="label label-danger">no</span>
723
                </td>',
724
                'boolean',
725
                false,
726
                ['editable' => false],
727
            ],
728
            [
729
                '<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
730
                    <span class="label label-danger">no</span>
731
                </td>',
732
                'boolean',
733
                null,
734
                ['editable' => false],
735
            ],
736
            [
737
                <<<'EOT'
738
<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
739
    <span
740
        class="x-editable"
741
        data-type="select"
742
        data-value="1"
743
        data-title="Data"
744
        data-pk="12345"
745
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
746
        data-source="[{value: 0, text: 'no'},{value: 1, text: 'yes'}]"
747
    >
748
        <span class="label label-success">yes</span>
749
    </span>
750
</td>
751
EOT
752
            ,
753
                'boolean',
754
                true,
755
                ['editable' => true],
756
            ],
757
            [
758
                <<<'EOT'
759
<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
760
    <span
761
        class="x-editable"
762
        data-type="select"
763
        data-value="0"
764
        data-title="Data"
765
        data-pk="12345"
766
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
767
        data-source="[{value: 0, text: 'no'},{value: 1, text: 'yes'}]"
768
    >
769
    <span class="label label-danger">no</span> </span>
770
</td>
771
EOT
772
                ,
773
                'boolean',
774
                false,
775
                ['editable' => true],
776
            ],
777
            [
778
                <<<'EOT'
779
<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
780
    <span
781
        class="x-editable"
782
        data-type="select"
783
        data-value="0"
784
        data-title="Data"
785
        data-pk="12345"
786
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
787
        data-source="[{value: 0, text: 'no'},{value: 1, text: 'yes'}]" >
788
        <span class="label label-danger">no</span> </span>
789
</td>
790
EOT
791
                ,
792
                'boolean',
793
                null,
794
                ['editable' => true],
795
            ],
796
            [
797
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345"> Delete </td>',
798
                'trans',
799
                'action_delete',
800
                ['catalogue' => 'SonataAdminBundle'],
801
            ],
802
            [
803
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345"> </td>',
804
                'trans',
805
                null,
806
                ['catalogue' => 'SonataAdminBundle'],
807
            ],
808
            [
809
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345"> Delete </td>',
810
                'trans',
811
                'action_delete',
812
                ['format' => '%s', 'catalogue' => 'SonataAdminBundle'],
813
            ],
814
            [
815
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345">
816
                action.action_delete
817
                </td>',
818
                'trans',
819
                'action_delete',
820
                ['format' => 'action.%s'],
821
            ],
822
            [
823
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345">
824
                action.action_delete
825
                </td>',
826
                'trans',
827
                'action_delete',
828
                ['format' => 'action.%s', 'catalogue' => 'SonataAdminBundle'],
829
            ],
830
            [
831
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Status1 </td>',
832
                'choice',
833
                'Status1',
834
                [],
835
            ],
836
            [
837
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Status1 </td>',
838
                'choice',
839
                ['Status1'],
840
                ['choices' => [], 'multiple' => true],
841
            ],
842
            [
843
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Alias1 </td>',
844
                'choice',
845
                'Status1',
846
                ['choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3']],
847
            ],
848
            [
849
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> </td>',
850
                'choice',
851
                null,
852
                ['choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3']],
853
            ],
854
            [
855
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
856
                NoValidKeyInChoices
857
                </td>',
858
                'choice',
859
                'NoValidKeyInChoices',
860
                ['choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3']],
861
            ],
862
            [
863
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Delete </td>',
864
                'choice',
865
                'Foo',
866
                ['catalogue' => 'SonataAdminBundle', 'choices' => [
867
                    'Foo' => 'action_delete',
868
                    'Status2' => 'Alias2',
869
                    'Status3' => 'Alias3',
870
                ]],
871
            ],
872
            [
873
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Alias1, Alias3 </td>',
874
                'choice',
875
                ['Status1', 'Status3'],
876
                ['choices' => [
877
                    'Status1' => 'Alias1',
878
                    'Status2' => 'Alias2',
879
                    'Status3' => 'Alias3',
880
                ], 'multiple' => true], ],
881
            [
882
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Alias1 | Alias3 </td>',
883
                'choice',
884
                ['Status1', 'Status3'],
885
                ['choices' => [
886
                    'Status1' => 'Alias1',
887
                    'Status2' => 'Alias2',
888
                    'Status3' => 'Alias3',
889
                ], 'multiple' => true, 'delimiter' => ' | '], ],
890
            [
891
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> </td>',
892
                'choice',
893
                null,
894
                ['choices' => [
895
                    'Status1' => 'Alias1',
896
                    'Status2' => 'Alias2',
897
                    'Status3' => 'Alias3',
898
                ], 'multiple' => true],
899
            ],
900
            [
901
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
902
                NoValidKeyInChoices
903
                </td>',
904
                'choice',
905
                ['NoValidKeyInChoices'],
906
                ['choices' => [
907
                    'Status1' => 'Alias1',
908
                    'Status2' => 'Alias2',
909
                    'Status3' => 'Alias3',
910
                ], 'multiple' => true],
911
            ],
912
            [
913
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
914
                NoValidKeyInChoices, Alias2
915
                </td>',
916
                'choice',
917
                ['NoValidKeyInChoices', 'Status2'],
918
                ['choices' => [
919
                    'Status1' => 'Alias1',
920
                    'Status2' => 'Alias2',
921
                    'Status3' => 'Alias3',
922
                ], 'multiple' => true],
923
            ],
924
            [
925
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Delete, Alias3 </td>',
926
                'choice',
927
                ['Foo', 'Status3'],
928
                ['catalogue' => 'SonataAdminBundle', 'choices' => [
929
                    'Foo' => 'action_delete',
930
                    'Status2' => 'Alias2',
931
                    'Status3' => 'Alias3',
932
                ], 'multiple' => true],
933
            ],
934
            [
935
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
936
                &lt;b&gt;Alias1&lt;/b&gt;, &lt;b&gt;Alias3&lt;/b&gt;
937
            </td>',
938
                'choice',
939
                ['Status1', 'Status3'],
940
                ['choices' => [
941
                    'Status1' => '<b>Alias1</b>',
942
                    'Status2' => '<b>Alias2</b>',
943
                    'Status3' => '<b>Alias3</b>',
944
                ], 'multiple' => true], ],
945
            [
946
                <<<'EOT'
947
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
948
    <span
949
        class="x-editable"
950
        data-type="select"
951
        data-value="Status1"
952
        data-title="Data"
953
        data-pk="12345"
954
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
955
        data-source="[]"
956
    >
957
        Status1
958
    </span>
959
</td>
960
EOT
961
                ,
962
                'choice',
963
                'Status1',
964
                ['editable' => true],
965
            ],
966
            [
967
                <<<'EOT'
968
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
969
    <span
970
        class="x-editable"
971
        data-type="select"
972
        data-value="Status1"
973
        data-title="Data"
974
        data-pk="12345"
975
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
976
        data-source="[{&quot;value&quot;:&quot;Status1&quot;,&quot;text&quot;:&quot;Alias1&quot;},{&quot;value&quot;:&quot;Status2&quot;,&quot;text&quot;:&quot;Alias2&quot;},{&quot;value&quot;:&quot;Status3&quot;,&quot;text&quot;:&quot;Alias3&quot;}]" >
977
        Alias1 </span>
978
</td>
979
EOT
980
                ,
981
                'choice',
982
                'Status1',
983
                [
984
                    'editable' => true,
985
                    'choices' => [
986
                        'Status1' => 'Alias1',
987
                        'Status2' => 'Alias2',
988
                        'Status3' => 'Alias3',
989
                    ],
990
                ],
991
            ],
992
            [
993
                <<<'EOT'
994
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
995
    <span
996
        class="x-editable"
997
        data-type="select"
998
        data-value=""
999
        data-title="Data"
1000
        data-pk="12345"
1001
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
1002
        data-source="[{&quot;value&quot;:&quot;Status1&quot;,&quot;text&quot;:&quot;Alias1&quot;},{&quot;value&quot;:&quot;Status2&quot;,&quot;text&quot;:&quot;Alias2&quot;},{&quot;value&quot;:&quot;Status3&quot;,&quot;text&quot;:&quot;Alias3&quot;}]" >
1003
1004
    </span>
1005
</td>
1006
EOT
1007
                ,
1008
                'choice',
1009
                null,
1010
                [
1011
                    'editable' => true,
1012
                    'choices' => [
1013
                        'Status1' => 'Alias1',
1014
                        'Status2' => 'Alias2',
1015
                        'Status3' => 'Alias3',
1016
                    ],
1017
                ],
1018
            ],
1019
            [
1020
                <<<'EOT'
1021
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
1022
    <span
1023
        class="x-editable"
1024
        data-type="select"
1025
        data-value="NoValidKeyInChoices"
1026
        data-title="Data" data-pk="12345"
1027
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
1028
        data-source="[{&quot;value&quot;:&quot;Status1&quot;,&quot;text&quot;:&quot;Alias1&quot;},{&quot;value&quot;:&quot;Status2&quot;,&quot;text&quot;:&quot;Alias2&quot;},{&quot;value&quot;:&quot;Status3&quot;,&quot;text&quot;:&quot;Alias3&quot;}]" >
1029
        NoValidKeyInChoices
1030
    </span>
1031
</td>
1032
EOT
1033
                ,
1034
                'choice',
1035
                'NoValidKeyInChoices',
1036
                [
1037
                    'editable' => true,
1038
                    'choices' => [
1039
                        'Status1' => 'Alias1',
1040
                        'Status2' => 'Alias2',
1041
                        'Status3' => 'Alias3',
1042
                    ],
1043
                ],
1044
            ],
1045
            [
1046
                <<<'EOT'
1047
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
1048
    <span
1049
        class="x-editable"
1050
        data-type="select"
1051
        data-value="Foo"
1052
        data-title="Data"
1053
        data-pk="12345"
1054
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
1055
        data-source="[{&quot;value&quot;:&quot;Foo&quot;,&quot;text&quot;:&quot;Delete&quot;},{&quot;value&quot;:&quot;Status2&quot;,&quot;text&quot;:&quot;Alias2&quot;},{&quot;value&quot;:&quot;Status3&quot;,&quot;text&quot;:&quot;Alias3&quot;}]" >
1056
         Delete
1057
    </span>
1058
</td>
1059
EOT
1060
                ,
1061
                'choice',
1062
                'Foo',
1063
                [
1064
                    'editable' => true,
1065
                    'catalogue' => 'SonataAdminBundle',
1066
                    'choices' => [
1067
                        'Foo' => 'action_delete',
1068
                        'Status2' => 'Alias2',
1069
                        'Status3' => 'Alias3',
1070
                    ],
1071
                ],
1072
            ],
1073
            [
1074
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345"> &nbsp; </td>',
1075
                'url',
1076
                null,
1077
                [],
1078
            ],
1079
            [
1080
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345"> &nbsp; </td>',
1081
                'url',
1082
                null,
1083
                ['url' => 'http://example.com'],
1084
            ],
1085
            [
1086
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345"> &nbsp; </td>',
1087
                'url',
1088
                null,
1089
                ['route' => ['name' => 'sonata_admin_foo']],
1090
            ],
1091
            [
1092
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1093
                <a href="http://example.com">http://example.com</a>
1094
                </td>',
1095
                'url',
1096
                'http://example.com',
1097
                [],
1098
            ],
1099
            [
1100
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1101
                <a href="https://example.com">https://example.com</a>
1102
                </td>',
1103
                'url',
1104
                'https://example.com',
1105
                [],
1106
            ],
1107
            [
1108
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1109
                <a href="https://example.com" target="_blank">https://example.com</a>
1110
                </td>',
1111
                'url',
1112
                'https://example.com',
1113
                ['attributes' => ['target' => '_blank']],
1114
            ],
1115
            [
1116
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1117
                <a href="https://example.com" target="_blank" class="fooLink">https://example.com</a>
1118
                </td>',
1119
                'url',
1120
                'https://example.com',
1121
                ['attributes' => ['target' => '_blank', 'class' => 'fooLink']],
1122
            ],
1123
            [
1124
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1125
                <a href="http://example.com">example.com</a>
1126
                </td>',
1127
                'url',
1128
                'http://example.com',
1129
                ['hide_protocol' => true],
1130
            ],
1131
            [
1132
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1133
                <a href="https://example.com">example.com</a>
1134
                </td>',
1135
                'url',
1136
                'https://example.com',
1137
                ['hide_protocol' => true],
1138
            ],
1139
            [
1140
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1141
                <a href="http://example.com">http://example.com</a>
1142
                </td>',
1143
                'url',
1144
                'http://example.com',
1145
                ['hide_protocol' => false],
1146
            ],
1147
            [
1148
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1149
                <a href="https://example.com">https://example.com</a>
1150
                </td>',
1151
                'url',
1152
                'https://example.com',
1153
                ['hide_protocol' => false],
1154
            ],
1155
            [
1156
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1157
                <a href="http://example.com">Foo</a>
1158
                </td>',
1159
                'url',
1160
                'Foo',
1161
                ['url' => 'http://example.com'],
1162
            ],
1163
            [
1164
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1165
                <a href="http://example.com">&lt;b&gt;Foo&lt;/b&gt;</a>
1166
                </td>',
1167
                'url',
1168
                '<b>Foo</b>',
1169
                ['url' => 'http://example.com'],
1170
            ],
1171
            [
1172
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1173
                <a href="/foo">Foo</a>
1174
                </td>',
1175
                'url',
1176
                'Foo',
1177
                ['route' => ['name' => 'sonata_admin_foo']],
1178
            ],
1179
            [
1180
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1181
                <a href="http://localhost/foo">Foo</a>
1182
                </td>',
1183
                'url',
1184
                'Foo',
1185
                ['route' => ['name' => 'sonata_admin_foo', 'absolute' => true]],
1186
            ],
1187
            [
1188
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1189
                <a href="/foo">foo/bar?a=b&amp;c=123456789</a>
1190
                </td>',
1191
                'url',
1192
                'http://foo/bar?a=b&c=123456789',
1193
                ['route' => ['name' => 'sonata_admin_foo'],
1194
                'hide_protocol' => true, ],
1195
            ],
1196
            [
1197
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1198
                <a href="http://localhost/foo">foo/bar?a=b&amp;c=123456789</a>
1199
                </td>',
1200
                'url',
1201
                'http://foo/bar?a=b&c=123456789',
1202
                [
1203
                    'route' => ['name' => 'sonata_admin_foo', 'absolute' => true],
1204
                    'hide_protocol' => true,
1205
                ],
1206
            ],
1207
            [
1208
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1209
                <a href="/foo/abcd/efgh?param3=ijkl">Foo</a>
1210
                </td>',
1211
                'url',
1212
                'Foo',
1213
                [
1214
                    'route' => ['name' => 'sonata_admin_foo_param',
1215
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'], ],
1216
                ],
1217
            ],
1218
            [
1219
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1220
                <a href="http://localhost/foo/abcd/efgh?param3=ijkl">Foo</a>
1221
                </td>',
1222
                'url',
1223
                'Foo',
1224
                [
1225
                    'route' => ['name' => 'sonata_admin_foo_param',
1226
                    'absolute' => true,
1227
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'], ],
1228
                ],
1229
            ],
1230
            [
1231
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1232
                <a href="/foo/obj/abcd/12345/efgh?param3=ijkl">Foo</a>
1233
                </td>',
1234
                'url',
1235
                'Foo',
1236
                [
1237
                    'route' => ['name' => 'sonata_admin_foo_object',
1238
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'],
1239
                    'identifier_parameter_name' => 'barId', ],
1240
                ],
1241
            ],
1242
            [
1243
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1244
                <a href="http://localhost/foo/obj/abcd/12345/efgh?param3=ijkl">Foo</a>
1245
                </td>',
1246
                'url',
1247
                'Foo',
1248
                [
1249
                    'route' => ['name' => 'sonata_admin_foo_object',
1250
                    'absolute' => true,
1251
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'],
1252
                    'identifier_parameter_name' => 'barId', ],
1253
                ],
1254
            ],
1255
            [
1256
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1257
                <p><strong>Creating a Template for the Field</strong> and form</p>
1258
                </td>',
1259
                'html',
1260
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1261
                [],
1262
            ],
1263
            [
1264
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1265
                Creating a Template for the Field and form
1266
                </td>',
1267
                'html',
1268
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1269
                ['strip' => true],
1270
            ],
1271
            [
1272
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1273
                Creating a Template for the Fi...
1274
                </td>',
1275
                'html',
1276
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1277
                ['truncate' => true],
1278
            ],
1279
            [
1280
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345"> Creating a... </td>',
1281
                'html',
1282
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1283
                ['truncate' => ['length' => 10]],
1284
            ],
1285
            [
1286
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1287
                Creating a Template for the Field...
1288
                </td>',
1289
                'html',
1290
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1291
                ['truncate' => ['preserve' => true]],
1292
            ],
1293
            [
1294
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1295
                Creating a Template for the Fi etc.
1296
                </td>',
1297
                'html',
1298
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1299
                ['truncate' => ['separator' => ' etc.']],
1300
            ],
1301
            [
1302
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1303
                Creating a Template for[...]
1304
                </td>',
1305
                'html',
1306
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1307
                [
1308
                    'truncate' => [
1309
                        'length' => 20,
1310
                        'preserve' => true,
1311
                        'separator' => '[...]',
1312
                    ],
1313
                ],
1314
            ],
1315
1316
            [
1317
                <<<'EOT'
1318
<td class="sonata-ba-list-field sonata-ba-list-field-text" objectId="12345">
1319
<div
1320
    class="sonata-readmore"
1321
    data-readmore-height="40"
1322
    data-readmore-more="Read more"
1323
    data-readmore-less="Close">A very long string</div>
1324
</td>
1325
EOT
1326
                ,
1327
                'text',
1328
                'A very long string',
1329
                [
1330
                    'collapse' => true,
1331
                ],
1332
            ],
1333
            [
1334
                <<<'EOT'
1335
<td class="sonata-ba-list-field sonata-ba-list-field-text" objectId="12345">
1336
<div
1337
    class="sonata-readmore"
1338
    data-readmore-height="10"
1339
    data-readmore-more="More"
1340
    data-readmore-less="Less">A very long string</div>
1341
</td>
1342
EOT
1343
                ,
1344
                'text',
1345
                'A very long string',
1346
                [
1347
                    'collapse' => [
1348
                        'height' => 10,
1349
                        'more' => 'More',
1350
                        'less' => 'Less',
1351
                    ],
1352
                ],
1353
            ],
1354
        ];
1355
    }
1356
1357
    /**
1358
     * @group legacy
1359
     */
1360
    public function testRenderListElementNonExistentTemplate(): void
1361
    {
1362
        // NEXT_MAJOR: Remove this line
1363
        $this->admin->method('getTemplate')
1364
            ->with('base_list_field')
1365
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
1366
1367
        $this->templateRegistry->getTemplate('base_list_field')->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
1368
1369
        $this->fieldDescription->expects($this->once())
1370
            ->method('getValue')
1371
            ->willReturn('Foo');
1372
1373
        $this->fieldDescription->expects($this->once())
1374
            ->method('getFieldName')
1375
            ->willReturn('Foo_name');
1376
1377
        $this->fieldDescription->expects($this->exactly(2))
1378
            ->method('getType')
1379
            ->willReturn('nonexistent');
1380
1381
        $this->fieldDescription->expects($this->once())
1382
            ->method('getTemplate')
1383
            ->willReturn('@SonataAdmin/CRUD/list_nonexistent_template.html.twig');
1384
1385
        $this->logger->expects($this->once())
1386
            ->method('warning')
1387
            ->with(($this->stringStartsWith($this->removeExtraWhitespace(
1388
                'An error occured trying to load the template
1389
                "@SonataAdmin/CRUD/list_nonexistent_template.html.twig"
1390
                for the field "Foo_name", the default template
1391
                    "@SonataAdmin/CRUD/base_list_field.html.twig" was used
1392
                    instead.'
1393
            ))));
1394
1395
        $this->twigExtension->renderListElement($this->environment, $this->object, $this->fieldDescription);
1396
    }
1397
1398
    /**
1399
     * @group                    legacy
1400
     */
1401
    public function testRenderListElementErrorLoadingTemplate(): void
1402
    {
1403
        $this->expectException(\Twig_Error_Loader::class);
1404
        $this->expectExceptionMessage('Unable to find template "@SonataAdmin/CRUD/base_list_nonexistent_field.html.twig"');
1405
1406
        // NEXT_MAJOR: Remove this line
1407
        $this->admin->method('getTemplate')
1408
            ->with('base_list_field')
1409
            ->willReturn('@SonataAdmin/CRUD/base_list_nonexistent_field.html.twig');
1410
1411
        $this->templateRegistry->getTemplate('base_list_field')->willReturn('@SonataAdmin/CRUD/base_list_nonexistent_field.html.twig');
1412
1413
        $this->fieldDescription->expects($this->once())
1414
            ->method('getTemplate')
1415
            ->willReturn('@SonataAdmin/CRUD/list_nonexistent_template.html.twig');
1416
1417
        $this->twigExtension->renderListElement($this->environment, $this->object, $this->fieldDescription);
1418
1419
        $this->templateRegistry->getTemplate('base_list_field')->shouldHaveBeenCalled();
1420
    }
1421
1422
    /**
1423
     * @dataProvider getRenderViewElementTests
1424
     */
1425
    public function testRenderViewElement($expected, $type, $value, array $options): void
1426
    {
1427
        $this->admin->expects($this->any())
1428
            ->method('getTemplate')
1429
            ->willReturn('@SonataAdmin/CRUD/base_show_field.html.twig');
1430
1431
        $this->fieldDescription->expects($this->any())
0 ignored issues
show
The method expects() does not seem to exist on object<Sonata\AdminBundl...ldDescriptionInterface>.

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...
1432
            ->method('getValue')
1433
            ->willReturnCallback(static function () use ($value) {
1434
                if ($value instanceof NoValueException) {
1435
                    throw  $value;
1436
                }
1437
1438
                return $value;
1439
            });
1440
1441
        $this->fieldDescription->expects($this->any())
1442
            ->method('getType')
1443
            ->willReturn($type);
1444
1445
        $this->fieldDescription->expects($this->any())
1446
            ->method('getOptions')
1447
            ->willReturn($options);
1448
1449
        $this->fieldDescription->expects($this->any())
1450
            ->method('getTemplate')
1451
            ->willReturnCallback(static function () use ($type) {
1452
                switch ($type) {
1453
                    case 'boolean':
1454
                        return '@SonataAdmin/CRUD/show_boolean.html.twig';
1455
                    case 'datetime':
1456
                        return '@SonataAdmin/CRUD/show_datetime.html.twig';
1457
                    case 'date':
1458
                        return '@SonataAdmin/CRUD/show_date.html.twig';
1459
                    case 'time':
1460
                        return '@SonataAdmin/CRUD/show_time.html.twig';
1461
                    case 'currency':
1462
                        return '@SonataAdmin/CRUD/show_currency.html.twig';
1463
                    case 'percent':
1464
                        return '@SonataAdmin/CRUD/show_percent.html.twig';
1465
                    case 'email':
1466
                        return '@SonataAdmin/CRUD/show_email.html.twig';
1467
                    case 'choice':
1468
                        return '@SonataAdmin/CRUD/show_choice.html.twig';
1469
                    case 'array':
1470
                        return '@SonataAdmin/CRUD/show_array.html.twig';
1471
                    case 'trans':
1472
                        return '@SonataAdmin/CRUD/show_trans.html.twig';
1473
                    case 'url':
1474
                        return '@SonataAdmin/CRUD/show_url.html.twig';
1475
                    case 'html':
1476
                        return '@SonataAdmin/CRUD/show_html.html.twig';
1477
                    default:
1478
                        return false;
1479
                }
1480
            });
1481
1482
        $this->assertSame(
1483
                $this->removeExtraWhitespace($expected),
1484
                $this->removeExtraWhitespace(
1485
                    $this->twigExtension->renderViewElement(
1486
                        $this->environment,
1487
                        $this->fieldDescription,
1488
                        $this->object
1489
                    )
1490
                )
1491
            );
1492
    }
1493
1494
    public function getRenderViewElementTests()
1495
    {
1496
        return [
1497
            ['<th>Data</th> <td>Example</td>', 'string', 'Example', ['safe' => false]],
1498
            ['<th>Data</th> <td>Example</td>', 'text', 'Example', ['safe' => false]],
1499
            ['<th>Data</th> <td>Example</td>', 'textarea', 'Example', ['safe' => false]],
1500
            [
1501
                '<th>Data</th> <td><time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00"> December 24, 2013 10:11 </time></td>',
1502
                'datetime',
1503
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), [],
1504
            ],
1505
            [
1506
                '<th>Data</th> <td><time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00"> 24.12.2013 10:11:12 </time></td>',
1507
                'datetime',
1508
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
1509
                ['format' => 'd.m.Y H:i:s'],
1510
            ],
1511
            [
1512
                '<th>Data</th> <td><time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00"> December 24, 2013 18:11 </time></td>',
1513
                'datetime',
1514
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
1515
                ['timezone' => 'Asia/Hong_Kong'],
1516
            ],
1517
            [
1518
                '<th>Data</th> <td><time datetime="2013-12-24" title="2013-12-24"> December 24, 2013 </time></td>',
1519
                'date',
1520
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
1521
                [],
1522
            ],
1523
            [
1524
                '<th>Data</th> <td><time datetime="2013-12-24" title="2013-12-24"> 24.12.2013 </time></td>',
1525
                'date',
1526
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
1527
                ['format' => 'd.m.Y'],
1528
            ],
1529
            [
1530
                '<th>Data</th> <td><time datetime="10:11:12+00:00" title="10:11:12+00:00"> 10:11:12 </time></td>',
1531
                'time',
1532
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
1533
                [],
1534
            ],
1535
            [
1536
                '<th>Data</th> <td><time datetime="10:11:12+00:00" title="10:11:12+00:00"> 18:11:12 </time></td>',
1537
                'time',
1538
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
1539
                ['timezone' => 'Asia/Hong_Kong'],
1540
            ],
1541
            ['<th>Data</th> <td>10.746135</td>', 'number', 10.746135, ['safe' => false]],
1542
            ['<th>Data</th> <td>5678</td>', 'integer', 5678, ['safe' => false]],
1543
            ['<th>Data</th> <td> 1074.6135 % </td>', 'percent', 10.746135, []],
1544
            ['<th>Data</th> <td> EUR 10.746135 </td>', 'currency', 10.746135, ['currency' => 'EUR']],
1545
            ['<th>Data</th> <td> GBP 51.23456 </td>', 'currency', 51.23456, ['currency' => 'GBP']],
1546
            [
1547
                '<th>Data</th> <td> [1 => First] <br> [2 => Second] </td>',
1548
                'array',
1549
                [1 => 'First', 2 => 'Second'],
1550
                ['safe' => false],
1551
            ],
1552
            [
1553
                '<th>Data</th> <td> [1 => First] [2 => Second] </td>',
1554
                'array',
1555
                [1 => 'First', 2 => 'Second'],
1556
                ['safe' => false, 'inline' => true],
1557
            ],
1558
            [
1559
                '<th>Data</th> <td><span class="label label-success">yes</span></td>',
1560
                'boolean',
1561
                true,
1562
                [],
1563
            ],
1564
            [
1565
                '<th>Data</th> <td><span class="label label-danger">yes</span></td>',
1566
                'boolean',
1567
                true,
1568
                ['inverse' => true],
1569
            ],
1570
            ['<th>Data</th> <td><span class="label label-danger">no</span></td>', 'boolean', false, []],
1571
            [
1572
                '<th>Data</th> <td><span class="label label-success">no</span></td>',
1573
                'boolean',
1574
                false,
1575
                ['inverse' => true],
1576
            ],
1577
            [
1578
                '<th>Data</th> <td> Delete </td>',
1579
                'trans',
1580
                'action_delete',
1581
                ['safe' => false, 'catalogue' => 'SonataAdminBundle'],
1582
            ],
1583
            ['<th>Data</th> <td>Status1</td>', 'choice', 'Status1', ['safe' => false]],
1584
            [
1585
                '<th>Data</th> <td>Alias1</td>',
1586
                'choice',
1587
                'Status1',
1588
                ['safe' => false, 'choices' => [
1589
                    'Status1' => 'Alias1',
1590
                    'Status2' => 'Alias2',
1591
                    'Status3' => 'Alias3',
1592
                ]],
1593
            ],
1594
            [
1595
                '<th>Data</th> <td>NoValidKeyInChoices</td>',
1596
                'choice',
1597
                'NoValidKeyInChoices',
1598
                ['safe' => false, 'choices' => [
1599
                    'Status1' => 'Alias1',
1600
                    'Status2' => 'Alias2',
1601
                    'Status3' => 'Alias3',
1602
                ]],
1603
            ],
1604
            [
1605
                '<th>Data</th> <td>Delete</td>',
1606
                'choice',
1607
                'Foo',
1608
                ['safe' => false, 'catalogue' => 'SonataAdminBundle', 'choices' => [
1609
                    'Foo' => 'action_delete',
1610
                    'Status2' => 'Alias2',
1611
                    'Status3' => 'Alias3',
1612
                ]],
1613
            ],
1614
            [
1615
                '<th>Data</th> <td>NoValidKeyInChoices</td>',
1616
                'choice',
1617
                ['NoValidKeyInChoices'],
1618
                ['safe' => false, 'choices' => [
1619
                    'Status1' => 'Alias1',
1620
                    'Status2' => 'Alias2',
1621
                    'Status3' => 'Alias3',
1622
                ], 'multiple' => true],
1623
            ],
1624
            [
1625
                '<th>Data</th> <td>NoValidKeyInChoices, Alias2</td>',
1626
                'choice',
1627
                ['NoValidKeyInChoices', 'Status2'],
1628
                ['safe' => false, 'choices' => [
1629
                    'Status1' => 'Alias1',
1630
                    'Status2' => 'Alias2',
1631
                    'Status3' => 'Alias3',
1632
                ], 'multiple' => true],
1633
            ],
1634
            [
1635
                '<th>Data</th> <td>Alias1, Alias3</td>',
1636
                'choice',
1637
                ['Status1', 'Status3'],
1638
                ['safe' => false, 'choices' => [
1639
                    'Status1' => 'Alias1',
1640
                    'Status2' => 'Alias2',
1641
                    'Status3' => 'Alias3',
1642
                ], 'multiple' => true],
1643
            ],
1644
            [
1645
                '<th>Data</th> <td>Alias1 | Alias3</td>',
1646
                'choice',
1647
                ['Status1', 'Status3'], ['safe' => false, 'choices' => [
1648
                    'Status1' => 'Alias1',
1649
                    'Status2' => 'Alias2',
1650
                    'Status3' => 'Alias3',
1651
                ], 'multiple' => true, 'delimiter' => ' | '],
1652
            ],
1653
            [
1654
                '<th>Data</th> <td>Delete, Alias3</td>',
1655
                'choice',
1656
                ['Foo', 'Status3'],
1657
                ['safe' => false, 'catalogue' => 'SonataAdminBundle', 'choices' => [
1658
                    'Foo' => 'action_delete',
1659
                    'Status2' => 'Alias2',
1660
                    'Status3' => 'Alias3',
1661
                ], 'multiple' => true],
1662
            ],
1663
            [
1664
                '<th>Data</th> <td><b>Alias1</b>, <b>Alias3</b></td>',
1665
                'choice',
1666
                ['Status1', 'Status3'],
1667
                ['safe' => true, 'choices' => [
1668
                    'Status1' => '<b>Alias1</b>',
1669
                    'Status2' => '<b>Alias2</b>',
1670
                    'Status3' => '<b>Alias3</b>',
1671
                ], 'multiple' => true],
1672
            ],
1673
            [
1674
                '<th>Data</th> <td>&lt;b&gt;Alias1&lt;/b&gt;, &lt;b&gt;Alias3&lt;/b&gt;</td>',
1675
                'choice',
1676
                ['Status1', 'Status3'],
1677
                ['safe' => false, 'choices' => [
1678
                    'Status1' => '<b>Alias1</b>',
1679
                    'Status2' => '<b>Alias2</b>',
1680
                    'Status3' => '<b>Alias3</b>',
1681
                ], 'multiple' => true],
1682
            ],
1683
            [
1684
                '<th>Data</th> <td><a href="http://example.com">http://example.com</a></td>',
1685
                'url',
1686
                'http://example.com',
1687
                ['safe' => false],
1688
            ],
1689
            [
1690
                '<th>Data</th> <td><a href="http://example.com" target="_blank">http://example.com</a></td>',
1691
                'url',
1692
                'http://example.com',
1693
                ['safe' => false, 'attributes' => ['target' => '_blank']],
1694
            ],
1695
            [
1696
                '<th>Data</th> <td><a href="http://example.com" target="_blank" class="fooLink">http://example.com</a></td>',
1697
                'url',
1698
                'http://example.com',
1699
                ['safe' => false, 'attributes' => ['target' => '_blank', 'class' => 'fooLink']],
1700
            ],
1701
            [
1702
                '<th>Data</th> <td><a href="https://example.com">https://example.com</a></td>',
1703
                'url',
1704
                'https://example.com',
1705
                ['safe' => false],
1706
            ],
1707
            [
1708
                '<th>Data</th> <td><a href="http://example.com">example.com</a></td>',
1709
                'url',
1710
                'http://example.com',
1711
                ['safe' => false, 'hide_protocol' => true],
1712
            ],
1713
            [
1714
                '<th>Data</th> <td><a href="https://example.com">example.com</a></td>',
1715
                'url',
1716
                'https://example.com',
1717
                ['safe' => false, 'hide_protocol' => true],
1718
            ],
1719
            [
1720
                '<th>Data</th> <td><a href="http://example.com">http://example.com</a></td>',
1721
                'url',
1722
                'http://example.com',
1723
                ['safe' => false, 'hide_protocol' => false],
1724
            ],
1725
            [
1726
                '<th>Data</th> <td><a href="https://example.com">https://example.com</a></td>',
1727
                'url',
1728
                'https://example.com',
1729
                ['safe' => false,
1730
                'hide_protocol' => false, ],
1731
            ],
1732
            [
1733
                '<th>Data</th> <td><a href="http://example.com">Foo</a></td>',
1734
                'url',
1735
                'Foo',
1736
                ['safe' => false, 'url' => 'http://example.com'],
1737
            ],
1738
            [
1739
                '<th>Data</th> <td><a href="http://example.com">&lt;b&gt;Foo&lt;/b&gt;</a></td>',
1740
                'url',
1741
                '<b>Foo</b>',
1742
                ['safe' => false, 'url' => 'http://example.com'],
1743
            ],
1744
            [
1745
                '<th>Data</th> <td><a href="http://example.com"><b>Foo</b></a></td>',
1746
                'url',
1747
                '<b>Foo</b>',
1748
                ['safe' => true, 'url' => 'http://example.com'],
1749
            ],
1750
            [
1751
                '<th>Data</th> <td><a href="/foo">Foo</a></td>',
1752
                'url',
1753
                'Foo',
1754
                ['safe' => false, 'route' => ['name' => 'sonata_admin_foo']],
1755
            ],
1756
            [
1757
                '<th>Data</th> <td><a href="http://localhost/foo">Foo</a></td>',
1758
                'url',
1759
                'Foo',
1760
                ['safe' => false, 'route' => [
1761
                    'name' => 'sonata_admin_foo',
1762
                    'absolute' => true,
1763
                ]],
1764
            ],
1765
            [
1766
                '<th>Data</th> <td><a href="/foo">foo/bar?a=b&amp;c=123456789</a></td>',
1767
                'url',
1768
                'http://foo/bar?a=b&c=123456789',
1769
                [
1770
                    'safe' => false,
1771
                    'route' => ['name' => 'sonata_admin_foo'],
1772
                    'hide_protocol' => true,
1773
                ],
1774
            ],
1775
            [
1776
                '<th>Data</th> <td><a href="http://localhost/foo">foo/bar?a=b&amp;c=123456789</a></td>',
1777
                'url',
1778
                'http://foo/bar?a=b&c=123456789',
1779
                ['safe' => false, 'route' => [
1780
                    'name' => 'sonata_admin_foo',
1781
                    'absolute' => true,
1782
                ], 'hide_protocol' => true],
1783
            ],
1784
            [
1785
                '<th>Data</th> <td><a href="/foo/abcd/efgh?param3=ijkl">Foo</a></td>',
1786
                'url',
1787
                'Foo',
1788
                ['safe' => false, 'route' => [
1789
                    'name' => 'sonata_admin_foo_param',
1790
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'],
1791
                ]],
1792
            ],
1793
            [
1794
                '<th>Data</th> <td><a href="http://localhost/foo/abcd/efgh?param3=ijkl">Foo</a></td>',
1795
                'url',
1796
                'Foo',
1797
                ['safe' => false, 'route' => [
1798
                    'name' => 'sonata_admin_foo_param',
1799
                    'absolute' => true,
1800
                    'parameters' => [
1801
                        'param1' => 'abcd',
1802
                        'param2' => 'efgh',
1803
                        'param3' => 'ijkl',
1804
                    ],
1805
                ]],
1806
            ],
1807
            [
1808
                '<th>Data</th> <td><a href="/foo/obj/abcd/12345/efgh?param3=ijkl">Foo</a></td>',
1809
                'url',
1810
                'Foo',
1811
                ['safe' => false, 'route' => [
1812
                    'name' => 'sonata_admin_foo_object',
1813
                    'parameters' => [
1814
                        'param1' => 'abcd',
1815
                        'param2' => 'efgh',
1816
                        'param3' => 'ijkl',
1817
                    ],
1818
                    'identifier_parameter_name' => 'barId',
1819
                ]],
1820
            ],
1821
            [
1822
                '<th>Data</th> <td><a href="http://localhost/foo/obj/abcd/12345/efgh?param3=ijkl">Foo</a></td>',
1823
                'url',
1824
                'Foo',
1825
                ['safe' => false, 'route' => [
1826
                    'name' => 'sonata_admin_foo_object',
1827
                    'absolute' => true,
1828
                    'parameters' => [
1829
                        'param1' => 'abcd',
1830
                        'param2' => 'efgh',
1831
                        'param3' => 'ijkl',
1832
                    ],
1833
                    'identifier_parameter_name' => 'barId',
1834
                ]],
1835
            ],
1836
            [
1837
                '<th>Data</th> <td> &nbsp;</td>',
1838
                'email',
1839
                null,
1840
                [],
1841
            ],
1842
            [
1843
                '<th>Data</th> <td> <a href="mailto:[email protected]">[email protected]</a></td>',
1844
                'email',
1845
                '[email protected]',
1846
                [],
1847
            ],
1848
            [
1849
                '<th>Data</th> <td> <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['subject' => 'Main Theme', 'body' => 'Message Body']).'">[email protected]</a></td>',
1850
                'email',
1851
                '[email protected]',
1852
                ['subject' => 'Main Theme', 'body' => 'Message Body'],
1853
            ],
1854
            [
1855
                '<th>Data</th> <td> <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['subject' => 'Main Theme']).'">[email protected]</a></td>',
1856
                'email',
1857
                '[email protected]',
1858
                ['subject' => 'Main Theme'],
1859
            ],
1860
            [
1861
                '<th>Data</th> <td> <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['body' => 'Message Body']).'">[email protected]</a></td>',
1862
                'email',
1863
                '[email protected]',
1864
                ['body' => 'Message Body'],
1865
            ],
1866
            [
1867
                '<th>Data</th> <td> [email protected]</td>',
1868
                'email',
1869
                '[email protected]',
1870
                ['as_string' => true, 'subject' => 'Main Theme', 'body' => 'Message Body'],
1871
            ],
1872
            [
1873
                '<th>Data</th> <td> [email protected]</td>',
1874
                'email',
1875
                '[email protected]',
1876
                ['as_string' => true, 'subject' => 'Main Theme'],
1877
            ],
1878
            [
1879
                '<th>Data</th> <td> [email protected]</td>',
1880
                'email',
1881
                '[email protected]',
1882
                ['as_string' => true, 'body' => 'Message Body'],
1883
            ],
1884
            [
1885
                '<th>Data</th> <td> <a href="mailto:[email protected]">[email protected]</a></td>',
1886
                'email',
1887
                '[email protected]',
1888
                ['as_string' => false],
1889
            ],
1890
            [
1891
                '<th>Data</th> <td> [email protected]</td>',
1892
                'email',
1893
                '[email protected]',
1894
                ['as_string' => true],
1895
            ],
1896
            [
1897
                '<th>Data</th> <td><p><strong>Creating a Template for the Field</strong> and form</p> </td>',
1898
                'html',
1899
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1900
                [],
1901
            ],
1902
            [
1903
                '<th>Data</th> <td>Creating a Template for the Field and form </td>',
1904
                'html',
1905
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1906
                ['strip' => true],
1907
            ],
1908
            [
1909
                '<th>Data</th> <td> Creating a Template for the Fi... </td>',
1910
                'html',
1911
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1912
                ['truncate' => true],
1913
            ],
1914
            [
1915
                '<th>Data</th> <td> Creating a... </td>',
1916
                'html',
1917
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1918
                ['truncate' => ['length' => 10]],
1919
            ],
1920
            [
1921
                '<th>Data</th> <td> Creating a Template for the Field... </td>',
1922
                'html',
1923
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1924
                ['truncate' => ['preserve' => true]],
1925
            ],
1926
            [
1927
                '<th>Data</th> <td> Creating a Template for the Fi etc. </td>',
1928
                'html',
1929
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1930
                ['truncate' => ['separator' => ' etc.']],
1931
            ],
1932
            [
1933
                '<th>Data</th> <td> Creating a Template for[...] </td>',
1934
                'html',
1935
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1936
                [
1937
                    'truncate' => [
1938
                        'length' => 20,
1939
                        'preserve' => true,
1940
                        'separator' => '[...]',
1941
                    ],
1942
                ],
1943
            ],
1944
1945
            // NoValueException
1946
            ['<th>Data</th> <td></td>', 'string', new NoValueException(), ['safe' => false]],
1947
            ['<th>Data</th> <td></td>', 'text', new NoValueException(), ['safe' => false]],
1948
            ['<th>Data</th> <td></td>', 'textarea', new NoValueException(), ['safe' => false]],
1949
            ['<th>Data</th> <td>&nbsp;</td>', 'datetime', new NoValueException(), []],
1950
            [
1951
                '<th>Data</th> <td>&nbsp;</td>',
1952
                'datetime',
1953
                new NoValueException(),
1954
                ['format' => 'd.m.Y H:i:s'],
1955
            ],
1956
            ['<th>Data</th> <td>&nbsp;</td>', 'date', new NoValueException(), []],
1957
            ['<th>Data</th> <td>&nbsp;</td>', 'date', new NoValueException(), ['format' => 'd.m.Y']],
1958
            ['<th>Data</th> <td>&nbsp;</td>', 'time', new NoValueException(), []],
1959
            ['<th>Data</th> <td></td>', 'number', new NoValueException(), ['safe' => false]],
1960
            ['<th>Data</th> <td></td>', 'integer', new NoValueException(), ['safe' => false]],
1961
            ['<th>Data</th> <td> 0 % </td>', 'percent', new NoValueException(), []],
1962
            ['<th>Data</th> <td> </td>', 'currency', new NoValueException(), ['currency' => 'EUR']],
1963
            ['<th>Data</th> <td> </td>', 'currency', new NoValueException(), ['currency' => 'GBP']],
1964
            ['<th>Data</th> <td> </td>', 'array', new NoValueException(), ['safe' => false]],
1965
            [
1966
                '<th>Data</th> <td><span class="label label-danger">no</span></td>',
1967
                'boolean',
1968
                new NoValueException(),
1969
                [],
1970
            ],
1971
            [
1972
                '<th>Data</th> <td> </td>',
1973
                'trans',
1974
                new NoValueException(),
1975
                ['safe' => false, 'catalogue' => 'SonataAdminBundle'],
1976
            ],
1977
            [
1978
                '<th>Data</th> <td></td>',
1979
                'choice',
1980
                new NoValueException(),
1981
                ['safe' => false, 'choices' => []],
1982
            ],
1983
            [
1984
                '<th>Data</th> <td></td>',
1985
                'choice',
1986
                new NoValueException(),
1987
                ['safe' => false, 'choices' => [], 'multiple' => true],
1988
            ],
1989
            ['<th>Data</th> <td>&nbsp;</td>', 'url', new NoValueException(), []],
1990
            [
1991
                '<th>Data</th> <td>&nbsp;</td>',
1992
                'url',
1993
                new NoValueException(),
1994
                ['url' => 'http://example.com'],
1995
            ],
1996
            [
1997
                '<th>Data</th> <td>&nbsp;</td>',
1998
                'url',
1999
                new NoValueException(),
2000
                ['route' => ['name' => 'sonata_admin_foo']],
2001
            ],
2002
2003
            [
2004
                <<<'EOT'
2005
<th>Data</th> <td><div
2006
        class="sonata-readmore"
2007
        data-readmore-height="40"
2008
        data-readmore-more="Read more"
2009
        data-readmore-less="Close">
2010
            A very long string
2011
</div></td>
2012
EOT
2013
                ,
2014
                'text',
2015
                ' A very long string ',
2016
                [
2017
                    'collapse' => true,
2018
                    'safe' => false,
2019
                ],
2020
            ],
2021
            [
2022
                <<<'EOT'
2023
<th>Data</th> <td><div
2024
        class="sonata-readmore"
2025
        data-readmore-height="10"
2026
        data-readmore-more="More"
2027
        data-readmore-less="Less">
2028
            A very long string
2029
</div></td>
2030
EOT
2031
                ,
2032
                'text',
2033
                ' A very long string ',
2034
                [
2035
                    'collapse' => [
2036
                        'height' => 10,
2037
                        'more' => 'More',
2038
                        'less' => 'Less',
2039
                    ],
2040
                    'safe' => false,
2041
                ],
2042
            ],
2043
        ];
2044
    }
2045
2046
    public function testGetValueFromFieldDescription(): void
2047
    {
2048
        $object = new \stdClass();
2049
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2050
2051
        $fieldDescription->expects($this->any())
2052
            ->method('getValue')
2053
            ->willReturn('test123');
2054
2055
        $this->assertSame('test123', $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription));
2056
    }
2057
2058
    public function testGetValueFromFieldDescriptionWithRemoveLoopException(): void
2059
    {
2060
        $object = $this->createMock(\ArrayAccess::class);
2061
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2062
2063
        $this->expectException(\RuntimeException::class, 'remove the loop requirement');
2064
2065
        $this->assertSame(
2066
            'anything',
2067
            $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription, ['loop' => true])
2068
        );
2069
    }
2070
2071
    public function testGetValueFromFieldDescriptionWithNoValueException(): void
2072
    {
2073
        $object = new \stdClass();
2074
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2075
2076
        $fieldDescription->expects($this->any())
2077
            ->method('getValue')
2078
            ->willReturnCallback(static function (): void {
2079
                throw new NoValueException();
2080
            });
2081
2082
        $fieldDescription->expects($this->any())
2083
            ->method('getAssociationAdmin')
2084
            ->willReturn(null);
2085
2086
        $this->assertNull($this->twigExtension->getValueFromFieldDescription($object, $fieldDescription));
2087
    }
2088
2089
    public function testGetValueFromFieldDescriptionWithNoValueExceptionNewAdminInstance(): void
2090
    {
2091
        $object = new \stdClass();
2092
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2093
2094
        $fieldDescription->expects($this->any())
2095
            ->method('getValue')
2096
            ->willReturnCallback(static function (): void {
2097
                throw new NoValueException();
2098
            });
2099
2100
        $fieldDescription->expects($this->any())
2101
            ->method('getAssociationAdmin')
2102
            ->willReturn($this->admin);
2103
2104
        $this->admin->expects($this->once())
2105
            ->method('getNewInstance')
2106
            ->willReturn('foo');
2107
2108
        $this->assertSame('foo', $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription));
2109
    }
2110
2111
    /**
2112
     * @group legacy
2113
     */
2114
    public function testOutput(): void
2115
    {
2116
        $this->fieldDescription->expects($this->any())
2117
            ->method('getTemplate')
2118
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
2119
2120
        $this->fieldDescription->expects($this->any())
2121
            ->method('getFieldName')
2122
            ->willReturn('fd_name');
2123
2124
        $this->environment->disableDebug();
2125
2126
        $parameters = [
2127
            'admin' => $this->admin,
2128
            'value' => 'foo',
2129
            'field_description' => $this->fieldDescription,
2130
            'object' => $this->object,
2131
        ];
2132
2133
        $template = $this->environment->loadTemplate('@SonataAdmin/CRUD/base_list_field.html.twig');
2134
2135
        $this->assertSame(
2136
            '<td class="sonata-ba-list-field sonata-ba-list-field-" objectId="12345"> foo </td>',
2137
            $this->removeExtraWhitespace($this->twigExtension->output(
2138
                $this->fieldDescription,
2139
                $template,
2140
                $parameters,
2141
                $this->environment
2142
            ))
2143
        );
2144
2145
        $this->environment->enableDebug();
2146
        $this->assertSame(
2147
            $this->removeExtraWhitespace(<<<'EOT'
2148
<!-- START
2149
    fieldName: fd_name
2150
    template: @SonataAdmin/CRUD/base_list_field.html.twig
2151
    compiled template: @SonataAdmin/CRUD/base_list_field.html.twig
2152
-->
2153
    <td class="sonata-ba-list-field sonata-ba-list-field-" objectId="12345"> foo </td>
2154
<!-- END - fieldName: fd_name -->
2155
EOT
2156
            ),
2157
            $this->removeExtraWhitespace(
2158
                $this->twigExtension->output($this->fieldDescription, $template, $parameters, $this->environment)
2159
            )
2160
        );
2161
    }
2162
2163
    /**
2164
     * @group legacy
2165
     * @expectedDeprecation The Sonata\AdminBundle\Admin\AbstractAdmin::getTemplate method is deprecated (since 3.34, will be dropped in 4.0. Use TemplateRegistry services instead).
2166
     */
2167
    public function testRenderWithDebug(): void
2168
    {
2169
        $this->fieldDescription->expects($this->any())
2170
            ->method('getTemplate')
2171
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
2172
2173
        $this->fieldDescription->expects($this->any())
2174
            ->method('getFieldName')
2175
            ->willReturn('fd_name');
2176
2177
        $this->fieldDescription->expects($this->any())
2178
            ->method('getValue')
2179
            ->willReturn('foo');
2180
2181
        $parameters = [
2182
            'admin' => $this->admin,
2183
            'value' => 'foo',
2184
            'field_description' => $this->fieldDescription,
2185
            'object' => $this->object,
2186
        ];
2187
2188
        $this->environment->enableDebug();
2189
2190
        $this->assertSame(
2191
            $this->removeExtraWhitespace(<<<'EOT'
2192
<!-- START
2193
    fieldName: fd_name
2194
    template: @SonataAdmin/CRUD/base_list_field.html.twig
2195
    compiled template: @SonataAdmin/CRUD/base_list_field.html.twig
2196
-->
2197
    <td class="sonata-ba-list-field sonata-ba-list-field-" objectId="12345"> foo </td>
2198
<!-- END - fieldName: fd_name -->
2199
EOT
2200
            ),
2201
            $this->removeExtraWhitespace(
2202
                $this->twigExtension->renderListElement($this->environment, $this->object, $this->fieldDescription, $parameters)
2203
            )
2204
        );
2205
    }
2206
2207
    public function testRenderRelationElementNoObject(): void
2208
    {
2209
        $this->assertSame('foo', $this->twigExtension->renderRelationElement('foo', $this->fieldDescription));
2210
    }
2211
2212
    public function testRenderRelationElementToString(): void
2213
    {
2214
        $this->fieldDescription->expects($this->exactly(2))
2215
            ->method('getOption')
2216
            ->willReturnCallback(static function ($value, $default = null) {
2217
                if ('associated_property' === $value) {
2218
                    return $default;
2219
                }
2220
            });
2221
2222
        $element = new FooToString();
2223
        $this->assertSame('salut', $this->twigExtension->renderRelationElement($element, $this->fieldDescription));
2224
    }
2225
2226
    /**
2227
     * @group legacy
2228
     */
2229
    public function testDeprecatedRelationElementToString(): void
2230
    {
2231
        $this->fieldDescription->expects($this->exactly(2))
2232
            ->method('getOption')
2233
            ->willReturnCallback(static function ($value, $default = null) {
2234
                if ('associated_tostring' === $value) {
2235
                    return '__toString';
2236
                }
2237
            });
2238
2239
        $element = new FooToString();
2240
        $this->assertSame(
2241
            'salut',
2242
            $this->twigExtension->renderRelationElement($element, $this->fieldDescription)
2243
        );
2244
    }
2245
2246
    /**
2247
     * @group legacy
2248
     */
2249
    public function testRenderRelationElementCustomToString(): void
2250
    {
2251
        $this->fieldDescription->expects($this->exactly(2))
2252
            ->method('getOption')
2253
            ->willReturnCallback(static function ($value, $default = null) {
2254
                if ('associated_property' === $value) {
2255
                    return $default;
2256
                }
2257
2258
                if ('associated_tostring' === $value) {
2259
                    return 'customToString';
2260
                }
2261
            });
2262
2263
        $element = $this->getMockBuilder('stdClass')
2264
            ->setMethods(['customToString'])
2265
            ->getMock();
2266
        $element->expects($this->any())
2267
            ->method('customToString')
2268
            ->willReturn('fooBar');
2269
2270
        $this->assertSame('fooBar', $this->twigExtension->renderRelationElement($element, $this->fieldDescription));
2271
    }
2272
2273
    /**
2274
     * @group legacy
2275
     */
2276
    public function testRenderRelationElementMethodNotExist(): void
2277
    {
2278
        $this->fieldDescription->expects($this->exactly(2))
2279
            ->method('getOption')
2280
2281
            ->willReturnCallback(static function ($value, $default = null) {
2282
                if ('associated_tostring' === $value) {
2283
                    return 'nonExistedMethod';
2284
                }
2285
            });
2286
2287
        $element = new \stdClass();
2288
        $this->expectException(\RuntimeException::class, 'You must define an `associated_property` option or create a `stdClass::__toString');
2289
2290
        $this->twigExtension->renderRelationElement($element, $this->fieldDescription);
2291
    }
2292
2293
    public function testRenderRelationElementWithPropertyPath(): void
2294
    {
2295
        $this->fieldDescription->expects($this->exactly(1))
2296
            ->method('getOption')
2297
2298
            ->willReturnCallback(static function ($value, $default = null) {
2299
                if ('associated_property' === $value) {
2300
                    return 'foo';
2301
                }
2302
            });
2303
2304
        $element = new \stdClass();
2305
        $element->foo = 'bar';
2306
2307
        $this->assertSame('bar', $this->twigExtension->renderRelationElement($element, $this->fieldDescription));
2308
    }
2309
2310
    public function testRenderRelationElementWithClosure(): void
2311
    {
2312
        $this->fieldDescription->expects($this->exactly(1))
2313
            ->method('getOption')
2314
2315
            ->willReturnCallback(static function ($value, $default = null) {
2316
                if ('associated_property' === $value) {
2317
                    return static function ($element) {
2318
                        return 'closure '.$element->foo;
2319
                    };
2320
                }
2321
            });
2322
2323
        $element = new \stdClass();
2324
        $element->foo = 'bar';
2325
2326
        $this->assertSame(
2327
            'closure bar',
2328
            $this->twigExtension->renderRelationElement($element, $this->fieldDescription)
2329
        );
2330
    }
2331
2332
    public function testGetUrlsafeIdentifier(): void
2333
    {
2334
        $entity = new \stdClass();
2335
2336
        // set admin to pool
2337
        $this->pool->setAdminServiceIds(['sonata_admin_foo_service']);
2338
        $this->pool->setAdminClasses(['stdClass' => ['sonata_admin_foo_service']]);
2339
2340
        $this->admin->expects($this->once())
2341
            ->method('getUrlsafeIdentifier')
2342
            ->with($this->equalTo($entity))
2343
            ->willReturn(1234567);
2344
2345
        $this->assertSame(1234567, $this->twigExtension->getUrlsafeIdentifier($entity));
2346
    }
2347
2348
    public function testGetUrlsafeIdentifier_GivenAdmin_Foo(): void
2349
    {
2350
        $entity = new \stdClass();
2351
2352
        // set admin to pool
2353
        $this->pool->setAdminServiceIds([
2354
            'sonata_admin_foo_service',
2355
            'sonata_admin_bar_service',
2356
        ]);
2357
        $this->pool->setAdminClasses(['stdClass' => [
2358
            'sonata_admin_foo_service',
2359
            'sonata_admin_bar_service',
2360
        ]]);
2361
2362
        $this->admin->expects($this->once())
2363
            ->method('getUrlsafeIdentifier')
2364
            ->with($this->equalTo($entity))
2365
            ->willReturn(1234567);
2366
2367
        $this->adminBar->expects($this->never())
2368
            ->method('getUrlsafeIdentifier');
2369
2370
        $this->assertSame(1234567, $this->twigExtension->getUrlsafeIdentifier($entity, $this->admin));
2371
    }
2372
2373
    public function testGetUrlsafeIdentifier_GivenAdmin_Bar(): void
2374
    {
2375
        $entity = new \stdClass();
2376
2377
        // set admin to pool
2378
        $this->pool->setAdminServiceIds(['sonata_admin_foo_service', 'sonata_admin_bar_service']);
2379
        $this->pool->setAdminClasses(['stdClass' => [
2380
            'sonata_admin_foo_service',
2381
            'sonata_admin_bar_service',
2382
        ]]);
2383
2384
        $this->admin->expects($this->never())
2385
            ->method('getUrlsafeIdentifier');
2386
2387
        $this->adminBar->expects($this->once())
2388
            ->method('getUrlsafeIdentifier')
2389
            ->with($this->equalTo($entity))
2390
            ->willReturn(1234567);
2391
2392
        $this->assertSame(1234567, $this->twigExtension->getUrlsafeIdentifier($entity, $this->adminBar));
2393
    }
2394
2395
    public function xEditableChoicesProvider()
2396
    {
2397
        return [
2398
            'needs processing' => [
2399
                ['choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2']],
2400
                [
2401
                    ['value' => 'Status1', 'text' => 'Alias1'],
2402
                    ['value' => 'Status2', 'text' => 'Alias2'],
2403
                ],
2404
            ],
2405
            'already processed' => [
2406
                ['choices' => [
2407
                    ['value' => 'Status1', 'text' => 'Alias1'],
2408
                    ['value' => 'Status2', 'text' => 'Alias2'],
2409
                ]],
2410
                [
2411
                    ['value' => 'Status1', 'text' => 'Alias1'],
2412
                    ['value' => 'Status2', 'text' => 'Alias2'],
2413
                ],
2414
            ],
2415
            'not required' => [
2416
                [
2417
                    'required' => false,
2418
                    'choices' => ['' => '', 'Status1' => 'Alias1', 'Status2' => 'Alias2'],
2419
                ],
2420
                [
2421
                    ['value' => '', 'text' => ''],
2422
                    ['value' => 'Status1', 'text' => 'Alias1'],
2423
                    ['value' => 'Status2', 'text' => 'Alias2'],
2424
                ],
2425
            ],
2426
            'not required multiple' => [
2427
                [
2428
                    'required' => false,
2429
                    'multiple' => true,
2430
                    'choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2'],
2431
                ],
2432
                [
2433
                    ['value' => 'Status1', 'text' => 'Alias1'],
2434
                    ['value' => 'Status2', 'text' => 'Alias2'],
2435
                ],
2436
            ],
2437
        ];
2438
    }
2439
2440
    /**
2441
     * @dataProvider xEditablechoicesProvider
2442
     */
2443
    public function testGetXEditableChoicesIsIdempotent(array $options, $expectedChoices): void
2444
    {
2445
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2446
        $fieldDescription->expects($this->any())
2447
            ->method('getOption')
2448
            ->withConsecutive(
2449
                ['choices', []],
2450
                ['catalogue'],
2451
                ['required'],
2452
                ['multiple']
2453
            )
2454
            ->will($this->onConsecutiveCalls(
2455
                $options['choices'],
2456
                'MyCatalogue',
2457
                $options['multiple'] ?? null
2458
            ));
2459
2460
        $this->assertSame($expectedChoices, $this->twigExtension->getXEditableChoices($fieldDescription));
2461
    }
2462
2463
    public function select2LocalesProvider()
2464
    {
2465
        return [
2466
            ['ar', 'ar'],
2467
            ['az', 'az'],
2468
            ['bg', 'bg'],
2469
            ['ca', 'ca'],
2470
            ['cs', 'cs'],
2471
            ['da', 'da'],
2472
            ['de', 'de'],
2473
            ['el', 'el'],
2474
            [null, 'en'],
2475
            ['es', 'es'],
2476
            ['et', 'et'],
2477
            ['eu', 'eu'],
2478
            ['fa', 'fa'],
2479
            ['fi', 'fi'],
2480
            ['fr', 'fr'],
2481
            ['gl', 'gl'],
2482
            ['he', 'he'],
2483
            ['hr', 'hr'],
2484
            ['hu', 'hu'],
2485
            ['id', 'id'],
2486
            ['is', 'is'],
2487
            ['it', 'it'],
2488
            ['ja', 'ja'],
2489
            ['ka', 'ka'],
2490
            ['ko', 'ko'],
2491
            ['lt', 'lt'],
2492
            ['lv', 'lv'],
2493
            ['mk', 'mk'],
2494
            ['ms', 'ms'],
2495
            ['nb', 'nb'],
2496
            ['nl', 'nl'],
2497
            ['pl', 'pl'],
2498
            ['pt-PT', 'pt'],
2499
            ['pt-BR', 'pt-BR'],
2500
            ['pt-PT', 'pt-PT'],
2501
            ['ro', 'ro'],
2502
            ['rs', 'rs'],
2503
            ['ru', 'ru'],
2504
            ['sk', 'sk'],
2505
            ['sv', 'sv'],
2506
            ['th', 'th'],
2507
            ['tr', 'tr'],
2508
            ['ug-CN', 'ug'],
2509
            ['ug-CN', 'ug-CN'],
2510
            ['uk', 'uk'],
2511
            ['vi', 'vi'],
2512
            ['zh-CN', 'zh'],
2513
            ['zh-CN', 'zh-CN'],
2514
            ['zh-TW', 'zh-TW'],
2515
        ];
2516
    }
2517
2518
    /**
2519
     * @dataProvider select2LocalesProvider
2520
     */
2521
    public function testCanonicalizedLocaleForSelect2($expected, $original): void
2522
    {
2523
        $this->assertSame($expected, $this->twigExtension->getCanonicalizedLocaleForSelect2($this->mockExtensionContext($original)));
2524
    }
2525
2526
    public function momentLocalesProvider()
2527
    {
2528
        return [
2529
            ['af', 'af'],
2530
            ['ar-dz', 'ar-dz'],
2531
            ['ar', 'ar'],
2532
            ['ar-ly', 'ar-ly'],
2533
            ['ar-ma', 'ar-ma'],
2534
            ['ar-sa', 'ar-sa'],
2535
            ['ar-tn', 'ar-tn'],
2536
            ['az', 'az'],
2537
            ['be', 'be'],
2538
            ['bg', 'bg'],
2539
            ['bn', 'bn'],
2540
            ['bo', 'bo'],
2541
            ['br', 'br'],
2542
            ['bs', 'bs'],
2543
            ['ca', 'ca'],
2544
            ['cs', 'cs'],
2545
            ['cv', 'cv'],
2546
            ['cy', 'cy'],
2547
            ['da', 'da'],
2548
            ['de-at', 'de-at'],
2549
            ['de', 'de'],
2550
            ['de', 'de-de'],
2551
            ['dv', 'dv'],
2552
            ['el', 'el'],
2553
            [null, 'en'],
2554
            [null, 'en-us'],
2555
            ['en-au', 'en-au'],
2556
            ['en-ca', 'en-ca'],
2557
            ['en-gb', 'en-gb'],
2558
            ['en-ie', 'en-ie'],
2559
            ['en-nz', 'en-nz'],
2560
            ['eo', 'eo'],
2561
            ['es-do', 'es-do'],
2562
            ['es', 'es-ar'],
2563
            ['es', 'es-mx'],
2564
            ['es', 'es'],
2565
            ['et', 'et'],
2566
            ['eu', 'eu'],
2567
            ['fa', 'fa'],
2568
            ['fi', 'fi'],
2569
            ['fo', 'fo'],
2570
            ['fr-ca', 'fr-ca'],
2571
            ['fr-ch', 'fr-ch'],
2572
            ['fr', 'fr-fr'],
2573
            ['fr', 'fr'],
2574
            ['fy', 'fy'],
2575
            ['gd', 'gd'],
2576
            ['gl', 'gl'],
2577
            ['he', 'he'],
2578
            ['hi', 'hi'],
2579
            ['hr', 'hr'],
2580
            ['hu', 'hu'],
2581
            ['hy-am', 'hy-am'],
2582
            ['id', 'id'],
2583
            ['is', 'is'],
2584
            ['it', 'it'],
2585
            ['ja', 'ja'],
2586
            ['jv', 'jv'],
2587
            ['ka', 'ka'],
2588
            ['kk', 'kk'],
2589
            ['km', 'km'],
2590
            ['ko', 'ko'],
2591
            ['ky', 'ky'],
2592
            ['lb', 'lb'],
2593
            ['lo', 'lo'],
2594
            ['lt', 'lt'],
2595
            ['lv', 'lv'],
2596
            ['me', 'me'],
2597
            ['mi', 'mi'],
2598
            ['mk', 'mk'],
2599
            ['ml', 'ml'],
2600
            ['mr', 'mr'],
2601
            ['ms', 'ms'],
2602
            ['ms-my', 'ms-my'],
2603
            ['my', 'my'],
2604
            ['nb', 'nb'],
2605
            ['ne', 'ne'],
2606
            ['nl-be', 'nl-be'],
2607
            ['nl', 'nl'],
2608
            ['nl', 'nl-nl'],
2609
            ['nn', 'nn'],
2610
            ['pa-in', 'pa-in'],
2611
            ['pl', 'pl'],
2612
            ['pt-br', 'pt-br'],
2613
            ['pt', 'pt'],
2614
            ['ro', 'ro'],
2615
            ['ru', 'ru'],
2616
            ['se', 'se'],
2617
            ['si', 'si'],
2618
            ['sk', 'sk'],
2619
            ['sl', 'sl'],
2620
            ['sq', 'sq'],
2621
            ['sr-cyrl', 'sr-cyrl'],
2622
            ['sr', 'sr'],
2623
            ['ss', 'ss'],
2624
            ['sv', 'sv'],
2625
            ['sw', 'sw'],
2626
            ['ta', 'ta'],
2627
            ['te', 'te'],
2628
            ['tet', 'tet'],
2629
            ['th', 'th'],
2630
            ['tlh', 'tlh'],
2631
            ['tl-ph', 'tl-ph'],
2632
            ['tr', 'tr'],
2633
            ['tzl', 'tzl'],
2634
            ['tzm', 'tzm'],
2635
            ['tzm-latn', 'tzm-latn'],
2636
            ['uk', 'uk'],
2637
            ['uz', 'uz'],
2638
            ['vi', 'vi'],
2639
            ['x-pseudo', 'x-pseudo'],
2640
            ['yo', 'yo'],
2641
            ['zh-cn', 'zh-cn'],
2642
            ['zh-hk', 'zh-hk'],
2643
            ['zh-tw', 'zh-tw'],
2644
        ];
2645
    }
2646
2647
    /**
2648
     * @dataProvider momentLocalesProvider
2649
     */
2650
    public function testCanonicalizedLocaleForMoment($expected, $original): void
2651
    {
2652
        $this->assertSame($expected, $this->twigExtension->getCanonicalizedLocaleForMoment($this->mockExtensionContext($original)));
2653
    }
2654
2655
    public function testIsGrantedAffirmative(): void
2656
    {
2657
        $this->assertTrue(
2658
            $this->twigExtension->isGrantedAffirmative(['foo', 'bar'])
2659
        );
2660
        $this->assertTrue($this->twigExtension->isGrantedAffirmative('foo'));
2661
        $this->assertTrue($this->twigExtension->isGrantedAffirmative('bar'));
2662
    }
2663
2664
    /**
2665
     * This method generates url part for Twig layout.
2666
     */
2667
    private function buildTwigLikeUrl(array $url): string
2668
    {
2669
        return htmlspecialchars(http_build_query($url, '', '&', PHP_QUERY_RFC3986));
2670
    }
2671
2672
    private function removeExtraWhitespace($string): string
2673
    {
2674
        return trim(preg_replace(
2675
            '/\s+/',
2676
            ' ',
2677
            $string
2678
        ));
2679
    }
2680
2681
    private function mockExtensionContext($locale): array
2682
    {
2683
        $request = $this->createMock(Request::class);
2684
        $request->method('getLocale')->willReturn($locale);
2685
        $appVariable = $this->createMock(AppVariable::class);
2686
        $appVariable->method('getRequest')->willReturn($request);
2687
2688
        return ['app' => $appVariable];
2689
    }
2690
}
2691