Completed
Push — 3.x ( b16427...70af05 )
by Grégoire
02:53
created

testConstructThrowsExceptionWithWrongTranslationArgument()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 1
nc 1
nop 0
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\Tests\Fixtures\StubFilesystemLoader;
27
use Sonata\AdminBundle\Twig\Extension\SonataAdminExtension;
28
use Sonata\AdminBundle\Twig\Extension\StringExtension;
29
use Symfony\Bridge\Twig\AppVariable;
30
use Symfony\Bridge\Twig\Extension\RoutingExtension;
31
use Symfony\Bridge\Twig\Extension\TranslationExtension;
32
use Symfony\Component\Config\FileLocator;
33
use Symfony\Component\DependencyInjection\ContainerInterface;
34
use Symfony\Component\HttpFoundation\Request;
35
use Symfony\Component\Routing\Generator\UrlGenerator;
36
use Symfony\Component\Routing\Loader\XmlFileLoader;
37
use Symfony\Component\Routing\RequestContext;
38
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
39
use Symfony\Component\Translation\Loader\XliffFileLoader;
40
use Symfony\Component\Translation\Translator;
41
use Symfony\Component\Translation\TranslatorInterface as LegacyTranslatorInterface;
42
use Symfony\Contracts\Translation\TranslatorInterface;
43
use Twig\Environment;
44
use Twig\Error\LoaderError;
45
use Twig\Extensions\TextExtension;
46
47
/**
48
 * Test for SonataAdminExtension.
49
 *
50
 * @author Andrej Hudec <[email protected]>
51
 */
52
class SonataAdminExtensionTest extends TestCase
53
{
54
    /**
55
     * @var SonataAdminExtension
56
     */
57
    private $twigExtension;
58
59
    /**
60
     * @var Environment
61
     */
62
    private $environment;
63
64
    /**
65
     * @var AdminInterface
66
     */
67
    private $admin;
68
69
    /**
70
     * @var AdminInterface
71
     */
72
    private $adminBar;
73
74
    /**
75
     * @var FieldDescriptionInterface
76
     */
77
    private $fieldDescription;
78
79
    /**
80
     * @var \stdClass
81
     */
82
    private $object;
83
84
    /**
85
     * @var Pool
86
     */
87
    private $pool;
88
89
    /**
90
     * @var LoggerInterface
91
     */
92
    private $logger;
93
94
    /**
95
     * @var string[]
96
     */
97
    private $xEditableTypeMapping;
98
99
    /**
100
     * @var TranslatorInterface
101
     */
102
    private $translator;
103
104
    /**
105
     * @var ContainerInterface
106
     */
107
    private $container;
108
109
    /**
110
     * @var TemplateRegistryInterface
111
     */
112
    private $templateRegistry;
113
114
    /**
115
     * @var AuthorizationCheckerInterface
116
     */
117
    private $securityChecker;
118
119
    public function setUp(): void
120
    {
121
        date_default_timezone_set('Europe/London');
122
123
        $container = $this->createMock(ContainerInterface::class);
124
125
        $this->pool = new Pool($container, '', '');
126
        $this->pool->setAdminServiceIds(['sonata_admin_foo_service']);
127
        $this->pool->setAdminClasses(['fooClass' => ['sonata_admin_foo_service']]);
128
129
        $this->logger = $this->getMockForAbstractClass(LoggerInterface::class);
130
        $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...
131
            'choice' => 'select',
132
            'boolean' => 'select',
133
            'text' => 'text',
134
            'textarea' => 'textarea',
135
            'html' => 'textarea',
136
            'email' => 'email',
137
            'string' => 'text',
138
            'smallint' => 'text',
139
            'bigint' => 'text',
140
            'integer' => 'number',
141
            'decimal' => 'number',
142
            'currency' => 'number',
143
            'percent' => 'number',
144
            'url' => 'url',
145
        ];
146
147
        // translation extension
148
        $translator = new Translator('en');
149
        $translator->addLoader('xlf', new XliffFileLoader());
150
        $translator->addResource(
151
            'xlf',
152
            __DIR__.'/../../../src/Resources/translations/SonataAdminBundle.en.xliff',
153
            'en',
154
            'SonataAdminBundle'
155
        );
156
157
        $this->translator = $translator;
158
159
        $this->templateRegistry = $this->prophesize(TemplateRegistryInterface::class);
160
        $this->container = $this->prophesize(ContainerInterface::class);
161
        $this->container->get('sonata_admin_foo_service.template_registry')->willReturn($this->templateRegistry->reveal());
162
163
        $this->securityChecker = $this->prophesize(AuthorizationCheckerInterface::class);
164
        $this->securityChecker->isGranted(['foo', 'bar'], null)->willReturn(false);
165
        $this->securityChecker->isGranted(Argument::type('string'), null)->willReturn(true);
166
167
        $this->twigExtension = new SonataAdminExtension(
168
            $this->pool,
169
            $this->logger,
170
            $this->translator,
171
            $this->container->reveal(),
172
            $this->securityChecker->reveal()
173
        );
174
        $this->twigExtension->setXEditableTypeMapping($this->xEditableTypeMapping);
175
176
        $request = $this->createMock(Request::class);
177
        $request->method('get')->with('_sonata_admin')->willReturn('sonata_admin_foo_service');
178
179
        $loader = new StubFilesystemLoader([
180
            __DIR__.'/../../../src/Resources/views/CRUD',
181
        ]);
182
        $loader->addPath(__DIR__.'/../../../src/Resources/views/', 'SonataAdmin');
183
184
        $this->environment = new Environment($loader, [
185
            'strict_variables' => true,
186
            'cache' => false,
187
            'autoescape' => 'html',
188
            'optimizations' => 0,
189
        ]);
190
        $this->environment->addExtension($this->twigExtension);
191
        $this->environment->addExtension(new TranslationExtension($translator));
192
        $this->environment->addExtension(new FakeTemplateRegistryExtension());
193
194
        // routing extension
195
        $xmlFileLoader = new XmlFileLoader(new FileLocator([__DIR__.'/../../../src/Resources/config/routing']));
196
        $routeCollection = $xmlFileLoader->load('sonata_admin.xml');
197
198
        $xmlFileLoader = new XmlFileLoader(new FileLocator([__DIR__.'/../../Fixtures/Resources/config/routing']));
199
        $testRouteCollection = $xmlFileLoader->load('routing.xml');
200
201
        $routeCollection->addCollection($testRouteCollection);
0 ignored issues
show
Documentation introduced by
$testRouteCollection is of type object<Symfony\Component\Routing\RouteCollection>, but the function expects a object<self>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
202
        $requestContext = new RequestContext();
203
        $urlGenerator = new UrlGenerator($routeCollection, $requestContext);
204
        $this->environment->addExtension(new RoutingExtension($urlGenerator));
205
        $this->environment->addExtension(new TextExtension());
206
        $this->environment->addExtension(new StringExtension());
207
208
        // initialize object
209
        $this->object = new \stdClass();
210
211
        // initialize admin
212
        $this->admin = $this->createMock(AbstractAdmin::class);
213
214
        $this->admin
215
            ->method('getCode')
216
            ->willReturn('sonata_admin_foo_service');
217
218
        $this->admin
219
            ->method('id')
220
            ->with($this->equalTo($this->object))
221
            ->willReturn(12345);
222
223
        $this->admin
224
            ->method('getNormalizedIdentifier')
225
            ->with($this->equalTo($this->object))
226
            ->willReturn(12345);
227
228
        $this->admin
229
            ->method('trans')
230
            ->willReturnCallback(static function ($id, $parameters = [], $domain = null) use ($translator) {
231
                return $translator->trans($id, $parameters, $domain);
232
            });
233
234
        $this->adminBar = $this->createMock(AbstractAdmin::class);
235
        $this->adminBar
236
            ->method('hasAccess')
237
            ->willReturn(true);
238
        $this->adminBar
239
            ->method('getNormalizedIdentifier')
240
            ->with($this->equalTo($this->object))
241
            ->willReturn(12345);
242
243
        $container
244
            ->method('get')
245
            ->willReturnCallback(function (string $id) {
246
                if ('sonata_admin_foo_service' === $id) {
247
                    return $this->admin;
248
                }
249
250
                if ('sonata_admin_bar_service' === $id) {
251
                    return $this->adminBar;
252
                }
253
            });
254
255
        // initialize field description
256
        $this->fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
257
258
        $this->fieldDescription
259
            ->method('getName')
260
            ->willReturn('fd_name');
261
262
        $this->fieldDescription
263
            ->method('getAdmin')
264
            ->willReturn($this->admin);
265
266
        $this->fieldDescription
267
            ->method('getLabel')
268
            ->willReturn('Data');
269
    }
270
271
    /**
272
     * NEXT_MAJOR: Remove this method.
273
     *
274
     * @group legacy
275
     */
276
    public function testConstructThrowsExceptionWithWrongTranslationArgument(): void
277
    {
278
        $this->expectException(\TypeError::class);
279
280
        new SonataAdminExtension(
281
            $this->pool,
282
            null,
283
            new \stdClass()
284
        );
285
    }
286
287
    /**
288
     * @doesNotPerformAssertions
289
     * @group legacy
290
     */
291
    public function testConstructWithLegacyTranslator(): void
292
    {
293
        new SonataAdminExtension(
294
            $this->pool,
295
            null,
296
            $this->createStub(LegacyTranslatorInterface::class)
297
        );
298
    }
299
300
    /**
301
     * @group legacy
302
     * @expectedDeprecation The Sonata\AdminBundle\Admin\AbstractAdmin::getTemplate method is deprecated (since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead).
303
     * @dataProvider getRenderListElementTests
304
     */
305
    public function testRenderListElement(string $expected, string $type, $value, array $options): void
306
    {
307
        $this->admin
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
308
            ->method('getPersistentParameters')
309
            ->willReturn(['context' => 'foo']);
310
311
        $this->admin
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
312
            ->method('hasAccess')
313
            ->willReturn(true);
314
315
        // NEXT_MAJOR: Remove this line
316
        $this->admin
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
317
            ->method('getTemplate')
318
            ->with('base_list_field')
319
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
320
321
        $this->templateRegistry->getTemplate('base_list_field')->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
0 ignored issues
show
Bug introduced by
The method willReturn cannot be called on $this->templateRegistry-...late('base_list_field') (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
322
323
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
324
            ->method('getValue')
325
            ->willReturn($value);
326
327
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
328
            ->method('getType')
329
            ->willReturn($type);
330
331
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
332
            ->method('getOptions')
333
            ->willReturn($options);
334
335
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
336
            ->method('getOption')
337
            ->willReturnCallback(static function ($name, $default = null) use ($options) {
338
                return $options[$name] ?? $default;
339
            });
340
341
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
342
            ->method('getTemplate')
343
            ->willReturnCallback(static function () use ($type) {
344
                switch ($type) {
345
                    case 'string':
346
                        return '@SonataAdmin/CRUD/list_string.html.twig';
347
                    case 'boolean':
348
                        return '@SonataAdmin/CRUD/list_boolean.html.twig';
349
                    case 'datetime':
350
                        return '@SonataAdmin/CRUD/list_datetime.html.twig';
351
                    case 'date':
352
                        return '@SonataAdmin/CRUD/list_date.html.twig';
353
                    case 'time':
354
                        return '@SonataAdmin/CRUD/list_time.html.twig';
355
                    case 'currency':
356
                        return '@SonataAdmin/CRUD/list_currency.html.twig';
357
                    case 'percent':
358
                        return '@SonataAdmin/CRUD/list_percent.html.twig';
359
                    case 'email':
360
                        return '@SonataAdmin/CRUD/list_email.html.twig';
361
                    case 'choice':
362
                        return '@SonataAdmin/CRUD/list_choice.html.twig';
363
                    case 'array':
364
                        return '@SonataAdmin/CRUD/list_array.html.twig';
365
                    case 'trans':
366
                        return '@SonataAdmin/CRUD/list_trans.html.twig';
367
                    case 'url':
368
                        return '@SonataAdmin/CRUD/list_url.html.twig';
369
                    case 'html':
370
                        return '@SonataAdmin/CRUD/list_html.html.twig';
371
                    case 'nonexistent':
372
                        // template doesn`t exist
373
                        return '@SonataAdmin/CRUD/list_nonexistent_template.html.twig';
374
                    default:
375
                        return false;
376
                }
377
            });
378
379
        $this->assertSame(
380
            $this->removeExtraWhitespace($expected),
381
            $this->removeExtraWhitespace($this->twigExtension->renderListElement(
382
                $this->environment,
383
                $this->object,
384
                $this->fieldDescription
385
            ))
386
        );
387
    }
388
389
    /**
390
     * @dataProvider getDeprecatedRenderListElementTests
391
     * @group legacy
392
     */
393
    public function testDeprecatedRenderListElement(string $expected, ?string $value, array $options): void
394
    {
395
        $this->admin
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
396
            ->method('hasAccess')
397
            ->willReturn(true);
398
399
        // NEXT_MAJOR: Remove this line
400
        $this->admin
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
401
            ->method('getTemplate')
402
            ->with('base_list_field')
403
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
404
405
        $this->templateRegistry->getTemplate('base_list_field')->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
0 ignored issues
show
Bug introduced by
The method willReturn cannot be called on $this->templateRegistry-...late('base_list_field') (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
406
407
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
408
            ->method('getValue')
409
            ->willReturn($value);
410
411
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
412
            ->method('getType')
413
            ->willReturn('nonexistent');
414
415
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
416
            ->method('getOptions')
417
            ->willReturn($options);
418
419
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
420
            ->method('getOption')
421
            ->willReturnCallback(static function ($name, $default = null) use ($options) {
422
                return $options[$name] ?? $default;
423
            });
424
425
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
426
            ->method('getTemplate')
427
            ->willReturn('@SonataAdmin/CRUD/list_nonexistent_template.html.twig');
428
429
        $this->assertSame(
430
            $this->removeExtraWhitespace($expected),
431
            $this->removeExtraWhitespace($this->twigExtension->renderListElement(
432
                $this->environment,
433
                $this->object,
434
                $this->fieldDescription
435
            ))
436
        );
437
    }
438
439
    public function getDeprecatedRenderListElementTests()
440
    {
441
        return [
442
            [
443
                '<td class="sonata-ba-list-field sonata-ba-list-field-nonexistent" objectId="12345"> Example </td>',
444
                'Example',
445
                [],
446
            ],
447
            [
448
                '<td class="sonata-ba-list-field sonata-ba-list-field-nonexistent" objectId="12345"> </td>',
449
                null,
450
                [],
451
            ],
452
        ];
453
    }
454
455
    public function getRenderListElementTests()
456
    {
457
        return [
458
            [
459
                '<td class="sonata-ba-list-field sonata-ba-list-field-string" objectId="12345"> Example </td>',
460
                'string',
461
                'Example',
462
                [],
463
            ],
464
            [
465
                '<td class="sonata-ba-list-field sonata-ba-list-field-string" objectId="12345"> </td>',
466
                'string',
467
                null,
468
                [],
469
            ],
470
            [
471
                '<td class="sonata-ba-list-field sonata-ba-list-field-text" objectId="12345"> Example </td>',
472
                'text',
473
                'Example',
474
                [],
475
            ],
476
            [
477
                '<td class="sonata-ba-list-field sonata-ba-list-field-text" objectId="12345"> </td>',
478
                'text',
479
                null,
480
                [],
481
            ],
482
            [
483
                '<td class="sonata-ba-list-field sonata-ba-list-field-textarea" objectId="12345"> Example </td>',
484
                'textarea',
485
                'Example',
486
                [],
487
            ],
488
            [
489
                '<td class="sonata-ba-list-field sonata-ba-list-field-textarea" objectId="12345"> </td>',
490
                'textarea',
491
                null,
492
                [],
493
            ],
494
            'datetime field' => [
495
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345">
496
                    <time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00">
497
                        December 24, 2013 10:11
498
                    </time>
499
                </td>',
500
                'datetime',
501
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
502
                [],
503
            ],
504
            [
505
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345">
506
                    <time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00">
507
                        December 24, 2013 18:11
508
                    </time>
509
                </td>',
510
                'datetime',
511
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
512
                ['timezone' => 'Asia/Hong_Kong'],
513
            ],
514
            [
515
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345"> &nbsp; </td>',
516
                'datetime',
517
                null,
518
                [],
519
            ],
520
            [
521
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345">
522
                    <time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00">
523
                        24.12.2013 10:11:12
524
                    </time>
525
                </td>',
526
                'datetime',
527
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
528
                ['format' => 'd.m.Y H:i:s'],
529
            ],
530
            [
531
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345"> &nbsp; </td>',
532
                'datetime',
533
                null,
534
                ['format' => 'd.m.Y H:i:s'],
535
            ],
536
            [
537
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345">
538
                    <time datetime="2013-12-24T10:11:12+00:00" title="2013-12-24T10:11:12+00:00">
539
                        24.12.2013 18:11:12
540
                    </time>
541
                </td>',
542
                'datetime',
543
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
544
                ['format' => 'd.m.Y H:i:s', 'timezone' => 'Asia/Hong_Kong'],
545
            ],
546
            [
547
                '<td class="sonata-ba-list-field sonata-ba-list-field-datetime" objectId="12345"> &nbsp; </td>',
548
                'datetime',
549
                null,
550
                ['format' => 'd.m.Y H:i:s', 'timezone' => 'Asia/Hong_Kong'],
551
            ],
552
            [
553
                '<td class="sonata-ba-list-field sonata-ba-list-field-date" objectId="12345">
554
                    <time datetime="2013-12-24" title="2013-12-24">
555
                        December 24, 2013
556
                    </time>
557
                </td>',
558
                'date',
559
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
560
                [],
561
            ],
562
            [
563
                '<td class="sonata-ba-list-field sonata-ba-list-field-date" objectId="12345"> &nbsp; </td>',
564
                'date',
565
                null,
566
                [],
567
            ],
568
            [
569
                '<td class="sonata-ba-list-field sonata-ba-list-field-date" objectId="12345">
570
                    <time datetime="2013-12-24" title="2013-12-24">
571
                        24.12.2013
572
                    </time>
573
                </td>',
574
                'date',
575
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
576
                ['format' => 'd.m.Y'],
577
            ],
578
            [
579
                '<td class="sonata-ba-list-field sonata-ba-list-field-date" objectId="12345"> &nbsp; </td>',
580
                'date',
581
                null,
582
                ['format' => 'd.m.Y'],
583
            ],
584
            [
585
                '<td class="sonata-ba-list-field sonata-ba-list-field-time" objectId="12345">
586
                    <time datetime="10:11:12+00:00" title="10:11:12+00:00">
587
                        10:11:12
588
                    </time>
589
                </td>',
590
                'time',
591
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
592
                [],
593
            ],
594
            [
595
                '<td class="sonata-ba-list-field sonata-ba-list-field-time" objectId="12345">
596
                    <time datetime="10:11:12+00:00" title="10:11:12+00:00">
597
                        18:11:12
598
                    </time>
599
                </td>',
600
                'time',
601
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
602
                ['timezone' => 'Asia/Hong_Kong'],
603
            ],
604
            [
605
                '<td class="sonata-ba-list-field sonata-ba-list-field-time" objectId="12345"> &nbsp; </td>',
606
                'time',
607
                null,
608
                [],
609
            ],
610
            [
611
                '<td class="sonata-ba-list-field sonata-ba-list-field-number" objectId="12345"> 10.746135 </td>',
612
                'number', 10.746135,
613
                [],
614
            ],
615
            [
616
                '<td class="sonata-ba-list-field sonata-ba-list-field-number" objectId="12345"> </td>',
617
                'number',
618
                null,
619
                [],
620
            ],
621
            [
622
                '<td class="sonata-ba-list-field sonata-ba-list-field-integer" objectId="12345"> 5678 </td>',
623
                'integer',
624
                5678,
625
                [],
626
            ],
627
            [
628
                '<td class="sonata-ba-list-field sonata-ba-list-field-integer" objectId="12345"> </td>',
629
                'integer',
630
                null,
631
                [],
632
            ],
633
            [
634
                '<td class="sonata-ba-list-field sonata-ba-list-field-percent" objectId="12345"> 1074.6135 % </td>',
635
                'percent',
636
                10.746135,
637
                [],
638
            ],
639
            [
640
                '<td class="sonata-ba-list-field sonata-ba-list-field-percent" objectId="12345"> 0 % </td>',
641
                'percent',
642
                null,
643
                [],
644
            ],
645
            [
646
                '<td class="sonata-ba-list-field sonata-ba-list-field-currency" objectId="12345"> EUR 10.746135 </td>',
647
                'currency',
648
                10.746135,
649
                ['currency' => 'EUR'],
650
            ],
651
            [
652
                '<td class="sonata-ba-list-field sonata-ba-list-field-currency" objectId="12345"> </td>',
653
                'currency',
654
                null,
655
                ['currency' => 'EUR'],
656
            ],
657
            [
658
                '<td class="sonata-ba-list-field sonata-ba-list-field-currency" objectId="12345"> GBP 51.23456 </td>',
659
                'currency',
660
                51.23456,
661
                ['currency' => 'GBP'],
662
            ],
663
            [
664
                '<td class="sonata-ba-list-field sonata-ba-list-field-currency" objectId="12345"> </td>',
665
                'currency',
666
                null,
667
                ['currency' => 'GBP'],
668
            ],
669
            [
670
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> &nbsp; </td>',
671
                'email',
672
                null,
673
                [],
674
            ],
675
            [
676
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> <a href="mailto:[email protected]">[email protected]</a> </td>',
677
                'email',
678
                '[email protected]',
679
                [],
680
            ],
681
            [
682
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345">
683
                    <a href="mailto:[email protected]">[email protected]</a> </td>',
684
                'email',
685
                '[email protected]',
686
                ['as_string' => false],
687
            ],
688
            [
689
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> [email protected] </td>',
690
                'email',
691
                '[email protected]',
692
                ['as_string' => true],
693
            ],
694
            [
695
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345">
696
                    <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['subject' => 'Main Theme', 'body' => 'Message Body']).'">[email protected]</a>  </td>',
697
                'email',
698
                '[email protected]',
699
                ['subject' => 'Main Theme', 'body' => 'Message Body'],
700
            ],
701
            [
702
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345">
703
                    <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['subject' => 'Main Theme']).'">[email protected]</a>  </td>',
704
                'email',
705
                '[email protected]',
706
                ['subject' => 'Main Theme'],
707
            ],
708
            [
709
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345">
710
                    <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['body' => 'Message Body']).'">[email protected]</a>  </td>',
711
                'email',
712
                '[email protected]',
713
                ['body' => 'Message Body'],
714
            ],
715
            [
716
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> [email protected] </td>',
717
                'email',
718
                '[email protected]',
719
                ['as_string' => true, 'subject' => 'Main Theme', 'body' => 'Message Body'],
720
            ],
721
            [
722
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> [email protected] </td>',
723
                'email',
724
                '[email protected]',
725
                ['as_string' => true, 'body' => 'Message Body'],
726
            ],
727
            [
728
                '<td class="sonata-ba-list-field sonata-ba-list-field-email" objectId="12345"> [email protected] </td>',
729
                'email',
730
                '[email protected]',
731
                ['as_string' => true, 'subject' => 'Main Theme'],
732
            ],
733
            [
734
                '<td class="sonata-ba-list-field sonata-ba-list-field-array" objectId="12345">
735
                    [1 => First] [2 => Second]
736
                </td>',
737
                'array',
738
                [1 => 'First', 2 => 'Second'],
739
                [],
740
            ],
741
            [
742
                '<td class="sonata-ba-list-field sonata-ba-list-field-array" objectId="12345"> </td>',
743
                'array',
744
                null,
745
                [],
746
            ],
747
            [
748
                '<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
749
                    <span class="label label-success">yes</span>
750
                </td>',
751
                'boolean',
752
                true,
753
                ['editable' => false],
754
            ],
755
            [
756
                '<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
757
                    <span class="label label-danger">no</span>
758
                </td>',
759
                'boolean',
760
                false,
761
                ['editable' => false],
762
            ],
763
            [
764
                '<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
765
                    <span class="label label-danger">no</span>
766
                </td>',
767
                'boolean',
768
                null,
769
                ['editable' => false],
770
            ],
771
            [
772
                <<<'EOT'
773
<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
774
    <span
775
        class="x-editable"
776
        data-type="select"
777
        data-value="1"
778
        data-title="Data"
779
        data-pk="12345"
780
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
781
        data-source="[{value: 0, text: 'no'},{value: 1, text: 'yes'}]"
782
    >
783
        <span class="label label-success">yes</span>
784
    </span>
785
</td>
786
EOT
787
            ,
788
                'boolean',
789
                true,
790
                ['editable' => true],
791
            ],
792
            [
793
                <<<'EOT'
794
<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
795
    <span
796
        class="x-editable"
797
        data-type="select"
798
        data-value="0"
799
        data-title="Data"
800
        data-pk="12345"
801
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
802
        data-source="[{value: 0, text: 'no'},{value: 1, text: 'yes'}]"
803
    >
804
    <span class="label label-danger">no</span> </span>
805
</td>
806
EOT
807
                ,
808
                'boolean',
809
                false,
810
                ['editable' => true],
811
            ],
812
            [
813
                <<<'EOT'
814
<td class="sonata-ba-list-field sonata-ba-list-field-boolean" objectId="12345">
815
    <span
816
        class="x-editable"
817
        data-type="select"
818
        data-value="0"
819
        data-title="Data"
820
        data-pk="12345"
821
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
822
        data-source="[{value: 0, text: 'no'},{value: 1, text: 'yes'}]" >
823
        <span class="label label-danger">no</span> </span>
824
</td>
825
EOT
826
                ,
827
                'boolean',
828
                null,
829
                ['editable' => true],
830
            ],
831
            [
832
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345"> Delete </td>',
833
                'trans',
834
                'action_delete',
835
                ['catalogue' => 'SonataAdminBundle'],
836
            ],
837
            [
838
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345"> </td>',
839
                'trans',
840
                null,
841
                ['catalogue' => 'SonataAdminBundle'],
842
            ],
843
            [
844
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345"> Delete </td>',
845
                'trans',
846
                'action_delete',
847
                ['format' => '%s', 'catalogue' => 'SonataAdminBundle'],
848
            ],
849
            [
850
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345">
851
                action.action_delete
852
                </td>',
853
                'trans',
854
                'action_delete',
855
                ['format' => 'action.%s'],
856
            ],
857
            [
858
                '<td class="sonata-ba-list-field sonata-ba-list-field-trans" objectId="12345">
859
                action.action_delete
860
                </td>',
861
                'trans',
862
                'action_delete',
863
                ['format' => 'action.%s', 'catalogue' => 'SonataAdminBundle'],
864
            ],
865
            [
866
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Status1 </td>',
867
                'choice',
868
                'Status1',
869
                [],
870
            ],
871
            [
872
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Status1 </td>',
873
                'choice',
874
                ['Status1'],
875
                ['choices' => [], 'multiple' => true],
876
            ],
877
            [
878
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Alias1 </td>',
879
                'choice',
880
                'Status1',
881
                ['choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3']],
882
            ],
883
            [
884
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> </td>',
885
                'choice',
886
                null,
887
                ['choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3']],
888
            ],
889
            [
890
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
891
                NoValidKeyInChoices
892
                </td>',
893
                'choice',
894
                'NoValidKeyInChoices',
895
                ['choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3']],
896
            ],
897
            [
898
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Delete </td>',
899
                'choice',
900
                'Foo',
901
                ['catalogue' => 'SonataAdminBundle', 'choices' => [
902
                    'Foo' => 'action_delete',
903
                    'Status2' => 'Alias2',
904
                    'Status3' => 'Alias3',
905
                ]],
906
            ],
907
            [
908
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Alias1, Alias3 </td>',
909
                'choice',
910
                ['Status1', 'Status3'],
911
                ['choices' => [
912
                    'Status1' => 'Alias1',
913
                    'Status2' => 'Alias2',
914
                    'Status3' => 'Alias3',
915
                ], 'multiple' => true], ],
916
            [
917
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Alias1 | Alias3 </td>',
918
                'choice',
919
                ['Status1', 'Status3'],
920
                ['choices' => [
921
                    'Status1' => 'Alias1',
922
                    'Status2' => 'Alias2',
923
                    'Status3' => 'Alias3',
924
                ], 'multiple' => true, 'delimiter' => ' | '], ],
925
            [
926
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> </td>',
927
                'choice',
928
                null,
929
                ['choices' => [
930
                    'Status1' => 'Alias1',
931
                    'Status2' => 'Alias2',
932
                    'Status3' => 'Alias3',
933
                ], 'multiple' => true],
934
            ],
935
            [
936
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
937
                NoValidKeyInChoices
938
                </td>',
939
                'choice',
940
                ['NoValidKeyInChoices'],
941
                ['choices' => [
942
                    'Status1' => 'Alias1',
943
                    'Status2' => 'Alias2',
944
                    'Status3' => 'Alias3',
945
                ], 'multiple' => true],
946
            ],
947
            [
948
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
949
                NoValidKeyInChoices, Alias2
950
                </td>',
951
                'choice',
952
                ['NoValidKeyInChoices', 'Status2'],
953
                ['choices' => [
954
                    'Status1' => 'Alias1',
955
                    'Status2' => 'Alias2',
956
                    'Status3' => 'Alias3',
957
                ], 'multiple' => true],
958
            ],
959
            [
960
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345"> Delete, Alias3 </td>',
961
                'choice',
962
                ['Foo', 'Status3'],
963
                ['catalogue' => 'SonataAdminBundle', 'choices' => [
964
                    'Foo' => 'action_delete',
965
                    'Status2' => 'Alias2',
966
                    'Status3' => 'Alias3',
967
                ], 'multiple' => true],
968
            ],
969
            [
970
                '<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
971
                &lt;b&gt;Alias1&lt;/b&gt;, &lt;b&gt;Alias3&lt;/b&gt;
972
            </td>',
973
                'choice',
974
                ['Status1', 'Status3'],
975
                ['choices' => [
976
                    'Status1' => '<b>Alias1</b>',
977
                    'Status2' => '<b>Alias2</b>',
978
                    'Status3' => '<b>Alias3</b>',
979
                ], 'multiple' => true], ],
980
            [
981
                <<<'EOT'
982
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
983
    <span
984
        class="x-editable"
985
        data-type="select"
986
        data-value="Status1"
987
        data-title="Data"
988
        data-pk="12345"
989
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
990
        data-source="[]"
991
    >
992
        Status1
993
    </span>
994
</td>
995
EOT
996
                ,
997
                'choice',
998
                'Status1',
999
                ['editable' => true],
1000
            ],
1001
            [
1002
                <<<'EOT'
1003
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
1004
    <span
1005
        class="x-editable"
1006
        data-type="select"
1007
        data-value="Status1"
1008
        data-title="Data"
1009
        data-pk="12345"
1010
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
1011
        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;}]" >
1012
        Alias1 </span>
1013
</td>
1014
EOT
1015
                ,
1016
                'choice',
1017
                'Status1',
1018
                [
1019
                    'editable' => true,
1020
                    'choices' => [
1021
                        'Status1' => 'Alias1',
1022
                        'Status2' => 'Alias2',
1023
                        'Status3' => 'Alias3',
1024
                    ],
1025
                ],
1026
            ],
1027
            [
1028
                <<<'EOT'
1029
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
1030
    <span
1031
        class="x-editable"
1032
        data-type="select"
1033
        data-value=""
1034
        data-title="Data"
1035
        data-pk="12345"
1036
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
1037
        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;}]" >
1038
1039
    </span>
1040
</td>
1041
EOT
1042
                ,
1043
                'choice',
1044
                null,
1045
                [
1046
                    'editable' => true,
1047
                    'choices' => [
1048
                        'Status1' => 'Alias1',
1049
                        'Status2' => 'Alias2',
1050
                        'Status3' => 'Alias3',
1051
                    ],
1052
                ],
1053
            ],
1054
            [
1055
                <<<'EOT'
1056
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
1057
    <span
1058
        class="x-editable"
1059
        data-type="select"
1060
        data-value="NoValidKeyInChoices"
1061
        data-title="Data" data-pk="12345"
1062
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
1063
        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;}]" >
1064
        NoValidKeyInChoices
1065
    </span>
1066
</td>
1067
EOT
1068
                ,
1069
                'choice',
1070
                'NoValidKeyInChoices',
1071
                [
1072
                    'editable' => true,
1073
                    'choices' => [
1074
                        'Status1' => 'Alias1',
1075
                        'Status2' => 'Alias2',
1076
                        'Status3' => 'Alias3',
1077
                    ],
1078
                ],
1079
            ],
1080
            [
1081
                <<<'EOT'
1082
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
1083
    <span
1084
        class="x-editable"
1085
        data-type="select"
1086
        data-value="Foo"
1087
        data-title="Data"
1088
        data-pk="12345"
1089
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
1090
        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;}]" >
1091
         Delete
1092
    </span>
1093
</td>
1094
EOT
1095
                ,
1096
                'choice',
1097
                'Foo',
1098
                [
1099
                    'editable' => true,
1100
                    'catalogue' => 'SonataAdminBundle',
1101
                    'choices' => [
1102
                        'Foo' => 'action_delete',
1103
                        'Status2' => 'Alias2',
1104
                        'Status3' => 'Alias3',
1105
                    ],
1106
                ],
1107
            ],
1108
            [
1109
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345"> &nbsp; </td>',
1110
                'url',
1111
                null,
1112
                [],
1113
            ],
1114
            [
1115
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345"> &nbsp; </td>',
1116
                'url',
1117
                null,
1118
                ['url' => 'http://example.com'],
1119
            ],
1120
            [
1121
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345"> &nbsp; </td>',
1122
                'url',
1123
                null,
1124
                ['route' => ['name' => 'sonata_admin_foo']],
1125
            ],
1126
            [
1127
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1128
                <a href="http://example.com">http://example.com</a>
1129
                </td>',
1130
                'url',
1131
                'http://example.com',
1132
                [],
1133
            ],
1134
            [
1135
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1136
                <a href="https://example.com">https://example.com</a>
1137
                </td>',
1138
                'url',
1139
                'https://example.com',
1140
                [],
1141
            ],
1142
            [
1143
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1144
                <a href="https://example.com" target="_blank">https://example.com</a>
1145
                </td>',
1146
                'url',
1147
                'https://example.com',
1148
                ['attributes' => ['target' => '_blank']],
1149
            ],
1150
            [
1151
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1152
                <a href="https://example.com" target="_blank" class="fooLink">https://example.com</a>
1153
                </td>',
1154
                'url',
1155
                'https://example.com',
1156
                ['attributes' => ['target' => '_blank', 'class' => 'fooLink']],
1157
            ],
1158
            [
1159
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1160
                <a href="http://example.com">example.com</a>
1161
                </td>',
1162
                'url',
1163
                'http://example.com',
1164
                ['hide_protocol' => true],
1165
            ],
1166
            [
1167
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1168
                <a href="https://example.com">example.com</a>
1169
                </td>',
1170
                'url',
1171
                'https://example.com',
1172
                ['hide_protocol' => true],
1173
            ],
1174
            [
1175
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1176
                <a href="http://example.com">http://example.com</a>
1177
                </td>',
1178
                'url',
1179
                'http://example.com',
1180
                ['hide_protocol' => false],
1181
            ],
1182
            [
1183
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1184
                <a href="https://example.com">https://example.com</a>
1185
                </td>',
1186
                'url',
1187
                'https://example.com',
1188
                ['hide_protocol' => false],
1189
            ],
1190
            [
1191
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1192
                <a href="http://example.com">Foo</a>
1193
                </td>',
1194
                'url',
1195
                'Foo',
1196
                ['url' => 'http://example.com'],
1197
            ],
1198
            [
1199
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1200
                <a href="http://example.com">&lt;b&gt;Foo&lt;/b&gt;</a>
1201
                </td>',
1202
                'url',
1203
                '<b>Foo</b>',
1204
                ['url' => 'http://example.com'],
1205
            ],
1206
            [
1207
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1208
                <a href="/foo">Foo</a>
1209
                </td>',
1210
                'url',
1211
                'Foo',
1212
                ['route' => ['name' => 'sonata_admin_foo']],
1213
            ],
1214
            [
1215
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1216
                <a href="http://localhost/foo">Foo</a>
1217
                </td>',
1218
                'url',
1219
                'Foo',
1220
                ['route' => ['name' => 'sonata_admin_foo', 'absolute' => true]],
1221
            ],
1222
            [
1223
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1224
                <a href="/foo">foo/bar?a=b&amp;c=123456789</a>
1225
                </td>',
1226
                'url',
1227
                'http://foo/bar?a=b&c=123456789',
1228
                ['route' => ['name' => 'sonata_admin_foo'],
1229
                'hide_protocol' => true, ],
1230
            ],
1231
            [
1232
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1233
                <a href="http://localhost/foo">foo/bar?a=b&amp;c=123456789</a>
1234
                </td>',
1235
                'url',
1236
                'http://foo/bar?a=b&c=123456789',
1237
                [
1238
                    'route' => ['name' => 'sonata_admin_foo', 'absolute' => true],
1239
                    'hide_protocol' => true,
1240
                ],
1241
            ],
1242
            [
1243
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1244
                <a href="/foo/abcd/efgh?param3=ijkl">Foo</a>
1245
                </td>',
1246
                'url',
1247
                'Foo',
1248
                [
1249
                    'route' => ['name' => 'sonata_admin_foo_param',
1250
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'], ],
1251
                ],
1252
            ],
1253
            [
1254
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1255
                <a href="http://localhost/foo/abcd/efgh?param3=ijkl">Foo</a>
1256
                </td>',
1257
                'url',
1258
                'Foo',
1259
                [
1260
                    'route' => ['name' => 'sonata_admin_foo_param',
1261
                    'absolute' => true,
1262
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'], ],
1263
                ],
1264
            ],
1265
            [
1266
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1267
                <a href="/foo/obj/abcd/12345/efgh?param3=ijkl">Foo</a>
1268
                </td>',
1269
                'url',
1270
                'Foo',
1271
                [
1272
                    'route' => ['name' => 'sonata_admin_foo_object',
1273
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'],
1274
                    'identifier_parameter_name' => 'barId', ],
1275
                ],
1276
            ],
1277
            [
1278
                '<td class="sonata-ba-list-field sonata-ba-list-field-url" objectId="12345">
1279
                <a href="http://localhost/foo/obj/abcd/12345/efgh?param3=ijkl">Foo</a>
1280
                </td>',
1281
                'url',
1282
                'Foo',
1283
                [
1284
                    'route' => ['name' => 'sonata_admin_foo_object',
1285
                    'absolute' => true,
1286
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'],
1287
                    'identifier_parameter_name' => 'barId', ],
1288
                ],
1289
            ],
1290
            [
1291
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1292
                <p><strong>Creating a Template for the Field</strong> and form</p>
1293
                </td>',
1294
                'html',
1295
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1296
                [],
1297
            ],
1298
            [
1299
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1300
                Creating a Template for the Field and form
1301
                </td>',
1302
                'html',
1303
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1304
                ['strip' => true],
1305
            ],
1306
            [
1307
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1308
                Creating a Template for the...
1309
                </td>',
1310
                'html',
1311
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1312
                ['truncate' => true],
1313
            ],
1314
            [
1315
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345"> Creatin... </td>',
1316
                'html',
1317
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1318
                ['truncate' => ['length' => 10]],
1319
            ],
1320
            [
1321
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1322
                Creating a Template for the Field...
1323
                </td>',
1324
                'html',
1325
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1326
                ['truncate' => ['cut' => false]],
1327
            ],
1328
            [
1329
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1330
                Creating a Template for t etc.
1331
                </td>',
1332
                'html',
1333
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1334
                ['truncate' => ['ellipsis' => ' etc.']],
1335
            ],
1336
            [
1337
                '<td class="sonata-ba-list-field sonata-ba-list-field-html" objectId="12345">
1338
                Creating a Template[...]
1339
                </td>',
1340
                'html',
1341
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1342
                [
1343
                    'truncate' => [
1344
                        'length' => 20,
1345
                        'cut' => false,
1346
                        'ellipsis' => '[...]',
1347
                    ],
1348
                ],
1349
            ],
1350
1351
            [
1352
                <<<'EOT'
1353
<td class="sonata-ba-list-field sonata-ba-list-field-text" objectId="12345">
1354
<div
1355
    class="sonata-readmore"
1356
    data-readmore-height="40"
1357
    data-readmore-more="Read more"
1358
    data-readmore-less="Close">A very long string</div>
1359
</td>
1360
EOT
1361
                ,
1362
                'text',
1363
                'A very long string',
1364
                [
1365
                    'collapse' => true,
1366
                ],
1367
            ],
1368
            [
1369
                <<<'EOT'
1370
<td class="sonata-ba-list-field sonata-ba-list-field-text" objectId="12345">
1371
<div
1372
    class="sonata-readmore"
1373
    data-readmore-height="10"
1374
    data-readmore-more="More"
1375
    data-readmore-less="Less">A very long string</div>
1376
</td>
1377
EOT
1378
                ,
1379
                'text',
1380
                'A very long string',
1381
                [
1382
                    'collapse' => [
1383
                        'height' => 10,
1384
                        'more' => 'More',
1385
                        'less' => 'Less',
1386
                    ],
1387
                ],
1388
            ],
1389
            [
1390
                <<<'EOT'
1391
<td class="sonata-ba-list-field sonata-ba-list-field-choice" objectId="12345">
1392
    <span
1393
        class="x-editable"
1394
        data-type="checklist"
1395
        data-value="[&quot;Status1&quot;,&quot;Status2&quot;]"
1396
        data-title="Data"
1397
        data-pk="12345"
1398
        data-url="/core/set-object-field-value?context=list&amp;field=fd_name&amp;objectId=12345&amp;code=sonata_admin_foo_service"
1399
        data-source="[{&quot;value&quot;:&quot;Status1&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;}]" >
1400
         Delete, Alias2
1401
    </span>
1402
</td>
1403
EOT
1404
                ,
1405
                'choice',
1406
                [
1407
                    'Status1',
1408
                    'Status2',
1409
                ],
1410
                [
1411
                    'editable' => true,
1412
                    'multiple' => true,
1413
                    'catalogue' => 'SonataAdminBundle',
1414
                    'choices' => [
1415
                        'Status1' => 'action_delete',
1416
                        'Status2' => 'Alias2',
1417
                        'Status3' => 'Alias3',
1418
                    ],
1419
                ],
1420
            ],
1421
        ];
1422
    }
1423
1424
    /**
1425
     * @group legacy
1426
     */
1427
    public function testRenderListElementNonExistentTemplate(): void
1428
    {
1429
        // NEXT_MAJOR: Remove this line
1430
        $this->admin->method('getTemplate')
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
1431
            ->with('base_list_field')
1432
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
1433
1434
        $this->templateRegistry->getTemplate('base_list_field')->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
0 ignored issues
show
Bug introduced by
The method willReturn cannot be called on $this->templateRegistry-...late('base_list_field') (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1435
1436
        $this->fieldDescription->expects($this->once())
0 ignored issues
show
Bug introduced by
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...
1437
            ->method('getValue')
1438
            ->willReturn('Foo');
1439
1440
        $this->fieldDescription->expects($this->once())
0 ignored issues
show
Bug introduced by
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...
1441
            ->method('getFieldName')
1442
            ->willReturn('Foo_name');
1443
1444
        $this->fieldDescription->expects($this->exactly(2))
0 ignored issues
show
Bug introduced by
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...
1445
            ->method('getType')
1446
            ->willReturn('nonexistent');
1447
1448
        $this->fieldDescription->expects($this->once())
0 ignored issues
show
Bug introduced by
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...
1449
            ->method('getTemplate')
1450
            ->willReturn('@SonataAdmin/CRUD/list_nonexistent_template.html.twig');
1451
1452
        $this->logger->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Psr\Log\LoggerInterface>.

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...
1453
            ->method('warning')
1454
            ->with(($this->stringStartsWith($this->removeExtraWhitespace(
1455
                'An error occured trying to load the template
1456
                "@SonataAdmin/CRUD/list_nonexistent_template.html.twig"
1457
                for the field "Foo_name", the default template
1458
                    "@SonataAdmin/CRUD/base_list_field.html.twig" was used
1459
                    instead.'
1460
            ))));
1461
1462
        $this->twigExtension->renderListElement($this->environment, $this->object, $this->fieldDescription);
1463
    }
1464
1465
    /**
1466
     * @group                    legacy
1467
     */
1468
    public function testRenderListElementErrorLoadingTemplate(): void
1469
    {
1470
        $this->expectException(LoaderError::class);
1471
        $this->expectExceptionMessage('Unable to find template "@SonataAdmin/CRUD/base_list_nonexistent_field.html.twig"');
1472
1473
        // NEXT_MAJOR: Remove this line
1474
        $this->admin->method('getTemplate')
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
1475
            ->with('base_list_field')
1476
            ->willReturn('@SonataAdmin/CRUD/base_list_nonexistent_field.html.twig');
1477
1478
        $this->templateRegistry->getTemplate('base_list_field')->willReturn('@SonataAdmin/CRUD/base_list_nonexistent_field.html.twig');
0 ignored issues
show
Bug introduced by
The method willReturn cannot be called on $this->templateRegistry-...late('base_list_field') (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1479
1480
        $this->fieldDescription->expects($this->once())
0 ignored issues
show
Bug introduced by
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...
1481
            ->method('getTemplate')
1482
            ->willReturn('@SonataAdmin/CRUD/list_nonexistent_template.html.twig');
1483
1484
        $this->twigExtension->renderListElement($this->environment, $this->object, $this->fieldDescription);
1485
1486
        $this->templateRegistry->getTemplate('base_list_field')->shouldHaveBeenCalled();
0 ignored issues
show
Bug introduced by
The method shouldHaveBeenCalled cannot be called on $this->templateRegistry-...late('base_list_field') (of type string).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
1487
    }
1488
1489
    /**
1490
     * @dataProvider getRenderViewElementTests
1491
     */
1492
    public function testRenderViewElement(string $expected, string $type, $value, array $options): void
1493
    {
1494
        $this->admin
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
1495
            ->method('getTemplate')
1496
            ->willReturn('@SonataAdmin/CRUD/base_show_field.html.twig');
1497
1498
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
1499
            ->method('getValue')
1500
            ->willReturnCallback(static function () use ($value) {
1501
                if ($value instanceof NoValueException) {
1502
                    throw  $value;
1503
                }
1504
1505
                return $value;
1506
            });
1507
1508
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
1509
            ->method('getType')
1510
            ->willReturn($type);
1511
1512
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
1513
            ->method('getOptions')
1514
            ->willReturn($options);
1515
1516
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
1517
            ->method('getTemplate')
1518
            ->willReturnCallback(static function () use ($type) {
1519
                switch ($type) {
1520
                    case 'boolean':
1521
                        return '@SonataAdmin/CRUD/show_boolean.html.twig';
1522
                    case 'datetime':
1523
                        return '@SonataAdmin/CRUD/show_datetime.html.twig';
1524
                    case 'date':
1525
                        return '@SonataAdmin/CRUD/show_date.html.twig';
1526
                    case 'time':
1527
                        return '@SonataAdmin/CRUD/show_time.html.twig';
1528
                    case 'currency':
1529
                        return '@SonataAdmin/CRUD/show_currency.html.twig';
1530
                    case 'percent':
1531
                        return '@SonataAdmin/CRUD/show_percent.html.twig';
1532
                    case 'email':
1533
                        return '@SonataAdmin/CRUD/show_email.html.twig';
1534
                    case 'choice':
1535
                        return '@SonataAdmin/CRUD/show_choice.html.twig';
1536
                    case 'array':
1537
                        return '@SonataAdmin/CRUD/show_array.html.twig';
1538
                    case 'trans':
1539
                        return '@SonataAdmin/CRUD/show_trans.html.twig';
1540
                    case 'url':
1541
                        return '@SonataAdmin/CRUD/show_url.html.twig';
1542
                    case 'html':
1543
                        return '@SonataAdmin/CRUD/show_html.html.twig';
1544
                    default:
1545
                        return false;
1546
                }
1547
            });
1548
1549
        $this->assertSame(
1550
            $this->removeExtraWhitespace($expected),
1551
            $this->removeExtraWhitespace(
1552
                $this->twigExtension->renderViewElement(
1553
                    $this->environment,
1554
                    $this->fieldDescription,
1555
                    $this->object
1556
                )
1557
            )
1558
        );
1559
    }
1560
1561
    public function getRenderViewElementTests()
1562
    {
1563
        return [
1564
            ['<th>Data</th> <td>Example</td>', 'string', 'Example', ['safe' => false]],
1565
            ['<th>Data</th> <td>Example</td>', 'text', 'Example', ['safe' => false]],
1566
            ['<th>Data</th> <td>Example</td>', 'textarea', 'Example', ['safe' => false]],
1567
            [
1568
                '<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>',
1569
                'datetime',
1570
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), [],
1571
            ],
1572
            [
1573
                '<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>',
1574
                'datetime',
1575
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
1576
                ['format' => 'd.m.Y H:i:s'],
1577
            ],
1578
            [
1579
                '<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>',
1580
                'datetime',
1581
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
1582
                ['timezone' => 'Asia/Hong_Kong'],
1583
            ],
1584
            [
1585
                '<th>Data</th> <td><time datetime="2013-12-24" title="2013-12-24"> December 24, 2013 </time></td>',
1586
                'date',
1587
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
1588
                [],
1589
            ],
1590
            [
1591
                '<th>Data</th> <td><time datetime="2013-12-24" title="2013-12-24"> 24.12.2013 </time></td>',
1592
                'date',
1593
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
1594
                ['format' => 'd.m.Y'],
1595
            ],
1596
            [
1597
                '<th>Data</th> <td><time datetime="10:11:12+00:00" title="10:11:12+00:00"> 10:11:12 </time></td>',
1598
                'time',
1599
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')),
1600
                [],
1601
            ],
1602
            [
1603
                '<th>Data</th> <td><time datetime="10:11:12+00:00" title="10:11:12+00:00"> 18:11:12 </time></td>',
1604
                'time',
1605
                new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')),
1606
                ['timezone' => 'Asia/Hong_Kong'],
1607
            ],
1608
            ['<th>Data</th> <td>10.746135</td>', 'number', 10.746135, ['safe' => false]],
1609
            ['<th>Data</th> <td>5678</td>', 'integer', 5678, ['safe' => false]],
1610
            ['<th>Data</th> <td> 1074.6135 % </td>', 'percent', 10.746135, []],
1611
            ['<th>Data</th> <td> EUR 10.746135 </td>', 'currency', 10.746135, ['currency' => 'EUR']],
1612
            ['<th>Data</th> <td> GBP 51.23456 </td>', 'currency', 51.23456, ['currency' => 'GBP']],
1613
            [
1614
                '<th>Data</th> <td> [1 => First] <br> [2 => Second] </td>',
1615
                'array',
1616
                [1 => 'First', 2 => 'Second'],
1617
                ['safe' => false],
1618
            ],
1619
            [
1620
                '<th>Data</th> <td> [1 => First] [2 => Second] </td>',
1621
                'array',
1622
                [1 => 'First', 2 => 'Second'],
1623
                ['safe' => false, 'inline' => true],
1624
            ],
1625
            [
1626
                '<th>Data</th> <td><span class="label label-success">yes</span></td>',
1627
                'boolean',
1628
                true,
1629
                [],
1630
            ],
1631
            [
1632
                '<th>Data</th> <td><span class="label label-danger">yes</span></td>',
1633
                'boolean',
1634
                true,
1635
                ['inverse' => true],
1636
            ],
1637
            ['<th>Data</th> <td><span class="label label-danger">no</span></td>', 'boolean', false, []],
1638
            [
1639
                '<th>Data</th> <td><span class="label label-success">no</span></td>',
1640
                'boolean',
1641
                false,
1642
                ['inverse' => true],
1643
            ],
1644
            [
1645
                '<th>Data</th> <td> Delete </td>',
1646
                'trans',
1647
                'action_delete',
1648
                ['safe' => false, 'catalogue' => 'SonataAdminBundle'],
1649
            ],
1650
            [
1651
                '<th>Data</th> <td> Delete </td>',
1652
                'trans',
1653
                'delete',
1654
                ['safe' => false, 'catalogue' => 'SonataAdminBundle', 'format' => 'action_%s'],
1655
            ],
1656
            ['<th>Data</th> <td>Status1</td>', 'choice', 'Status1', ['safe' => false]],
1657
            [
1658
                '<th>Data</th> <td>Alias1</td>',
1659
                'choice',
1660
                'Status1',
1661
                ['safe' => false, 'choices' => [
1662
                    'Status1' => 'Alias1',
1663
                    'Status2' => 'Alias2',
1664
                    'Status3' => 'Alias3',
1665
                ]],
1666
            ],
1667
            [
1668
                '<th>Data</th> <td>NoValidKeyInChoices</td>',
1669
                'choice',
1670
                'NoValidKeyInChoices',
1671
                ['safe' => false, 'choices' => [
1672
                    'Status1' => 'Alias1',
1673
                    'Status2' => 'Alias2',
1674
                    'Status3' => 'Alias3',
1675
                ]],
1676
            ],
1677
            [
1678
                '<th>Data</th> <td>Delete</td>',
1679
                'choice',
1680
                'Foo',
1681
                ['safe' => false, 'catalogue' => 'SonataAdminBundle', 'choices' => [
1682
                    'Foo' => 'action_delete',
1683
                    'Status2' => 'Alias2',
1684
                    'Status3' => 'Alias3',
1685
                ]],
1686
            ],
1687
            [
1688
                '<th>Data</th> <td>NoValidKeyInChoices</td>',
1689
                'choice',
1690
                ['NoValidKeyInChoices'],
1691
                ['safe' => false, 'choices' => [
1692
                    'Status1' => 'Alias1',
1693
                    'Status2' => 'Alias2',
1694
                    'Status3' => 'Alias3',
1695
                ], 'multiple' => true],
1696
            ],
1697
            [
1698
                '<th>Data</th> <td>NoValidKeyInChoices, Alias2</td>',
1699
                'choice',
1700
                ['NoValidKeyInChoices', 'Status2'],
1701
                ['safe' => false, 'choices' => [
1702
                    'Status1' => 'Alias1',
1703
                    'Status2' => 'Alias2',
1704
                    'Status3' => 'Alias3',
1705
                ], 'multiple' => true],
1706
            ],
1707
            [
1708
                '<th>Data</th> <td>Alias1, Alias3</td>',
1709
                'choice',
1710
                ['Status1', 'Status3'],
1711
                ['safe' => false, 'choices' => [
1712
                    'Status1' => 'Alias1',
1713
                    'Status2' => 'Alias2',
1714
                    'Status3' => 'Alias3',
1715
                ], 'multiple' => true],
1716
            ],
1717
            [
1718
                '<th>Data</th> <td>Alias1 | Alias3</td>',
1719
                'choice',
1720
                ['Status1', 'Status3'], ['safe' => false, 'choices' => [
1721
                    'Status1' => 'Alias1',
1722
                    'Status2' => 'Alias2',
1723
                    'Status3' => 'Alias3',
1724
                ], 'multiple' => true, 'delimiter' => ' | '],
1725
            ],
1726
            [
1727
                '<th>Data</th> <td>Delete, Alias3</td>',
1728
                'choice',
1729
                ['Foo', 'Status3'],
1730
                ['safe' => false, 'catalogue' => 'SonataAdminBundle', 'choices' => [
1731
                    'Foo' => 'action_delete',
1732
                    'Status2' => 'Alias2',
1733
                    'Status3' => 'Alias3',
1734
                ], 'multiple' => true],
1735
            ],
1736
            [
1737
                '<th>Data</th> <td><b>Alias1</b>, <b>Alias3</b></td>',
1738
                'choice',
1739
                ['Status1', 'Status3'],
1740
                ['safe' => true, 'choices' => [
1741
                    'Status1' => '<b>Alias1</b>',
1742
                    'Status2' => '<b>Alias2</b>',
1743
                    'Status3' => '<b>Alias3</b>',
1744
                ], 'multiple' => true],
1745
            ],
1746
            [
1747
                '<th>Data</th> <td>&lt;b&gt;Alias1&lt;/b&gt;, &lt;b&gt;Alias3&lt;/b&gt;</td>',
1748
                'choice',
1749
                ['Status1', 'Status3'],
1750
                ['safe' => false, 'choices' => [
1751
                    'Status1' => '<b>Alias1</b>',
1752
                    'Status2' => '<b>Alias2</b>',
1753
                    'Status3' => '<b>Alias3</b>',
1754
                ], 'multiple' => true],
1755
            ],
1756
            [
1757
                '<th>Data</th> <td><a href="http://example.com">http://example.com</a></td>',
1758
                'url',
1759
                'http://example.com',
1760
                ['safe' => false],
1761
            ],
1762
            [
1763
                '<th>Data</th> <td><a href="http://example.com" target="_blank">http://example.com</a></td>',
1764
                'url',
1765
                'http://example.com',
1766
                ['safe' => false, 'attributes' => ['target' => '_blank']],
1767
            ],
1768
            [
1769
                '<th>Data</th> <td><a href="http://example.com" target="_blank" class="fooLink">http://example.com</a></td>',
1770
                'url',
1771
                'http://example.com',
1772
                ['safe' => false, 'attributes' => ['target' => '_blank', 'class' => 'fooLink']],
1773
            ],
1774
            [
1775
                '<th>Data</th> <td><a href="https://example.com">https://example.com</a></td>',
1776
                'url',
1777
                'https://example.com',
1778
                ['safe' => false],
1779
            ],
1780
            [
1781
                '<th>Data</th> <td><a href="http://example.com">example.com</a></td>',
1782
                'url',
1783
                'http://example.com',
1784
                ['safe' => false, 'hide_protocol' => true],
1785
            ],
1786
            [
1787
                '<th>Data</th> <td><a href="https://example.com">example.com</a></td>',
1788
                'url',
1789
                'https://example.com',
1790
                ['safe' => false, 'hide_protocol' => true],
1791
            ],
1792
            [
1793
                '<th>Data</th> <td><a href="http://example.com">http://example.com</a></td>',
1794
                'url',
1795
                'http://example.com',
1796
                ['safe' => false, 'hide_protocol' => false],
1797
            ],
1798
            [
1799
                '<th>Data</th> <td><a href="https://example.com">https://example.com</a></td>',
1800
                'url',
1801
                'https://example.com',
1802
                ['safe' => false,
1803
                'hide_protocol' => false, ],
1804
            ],
1805
            [
1806
                '<th>Data</th> <td><a href="http://example.com">Foo</a></td>',
1807
                'url',
1808
                'Foo',
1809
                ['safe' => false, 'url' => 'http://example.com'],
1810
            ],
1811
            [
1812
                '<th>Data</th> <td><a href="http://example.com">&lt;b&gt;Foo&lt;/b&gt;</a></td>',
1813
                'url',
1814
                '<b>Foo</b>',
1815
                ['safe' => false, 'url' => 'http://example.com'],
1816
            ],
1817
            [
1818
                '<th>Data</th> <td><a href="http://example.com"><b>Foo</b></a></td>',
1819
                'url',
1820
                '<b>Foo</b>',
1821
                ['safe' => true, 'url' => 'http://example.com'],
1822
            ],
1823
            [
1824
                '<th>Data</th> <td><a href="/foo">Foo</a></td>',
1825
                'url',
1826
                'Foo',
1827
                ['safe' => false, 'route' => ['name' => 'sonata_admin_foo']],
1828
            ],
1829
            [
1830
                '<th>Data</th> <td><a href="http://localhost/foo">Foo</a></td>',
1831
                'url',
1832
                'Foo',
1833
                ['safe' => false, 'route' => [
1834
                    'name' => 'sonata_admin_foo',
1835
                    'absolute' => true,
1836
                ]],
1837
            ],
1838
            [
1839
                '<th>Data</th> <td><a href="/foo">foo/bar?a=b&amp;c=123456789</a></td>',
1840
                'url',
1841
                'http://foo/bar?a=b&c=123456789',
1842
                [
1843
                    'safe' => false,
1844
                    'route' => ['name' => 'sonata_admin_foo'],
1845
                    'hide_protocol' => true,
1846
                ],
1847
            ],
1848
            [
1849
                '<th>Data</th> <td><a href="http://localhost/foo">foo/bar?a=b&amp;c=123456789</a></td>',
1850
                'url',
1851
                'http://foo/bar?a=b&c=123456789',
1852
                ['safe' => false, 'route' => [
1853
                    'name' => 'sonata_admin_foo',
1854
                    'absolute' => true,
1855
                ], 'hide_protocol' => true],
1856
            ],
1857
            [
1858
                '<th>Data</th> <td><a href="/foo/abcd/efgh?param3=ijkl">Foo</a></td>',
1859
                'url',
1860
                'Foo',
1861
                ['safe' => false, 'route' => [
1862
                    'name' => 'sonata_admin_foo_param',
1863
                    'parameters' => ['param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'],
1864
                ]],
1865
            ],
1866
            [
1867
                '<th>Data</th> <td><a href="http://localhost/foo/abcd/efgh?param3=ijkl">Foo</a></td>',
1868
                'url',
1869
                'Foo',
1870
                ['safe' => false, 'route' => [
1871
                    'name' => 'sonata_admin_foo_param',
1872
                    'absolute' => true,
1873
                    'parameters' => [
1874
                        'param1' => 'abcd',
1875
                        'param2' => 'efgh',
1876
                        'param3' => 'ijkl',
1877
                    ],
1878
                ]],
1879
            ],
1880
            [
1881
                '<th>Data</th> <td><a href="/foo/obj/abcd/12345/efgh?param3=ijkl">Foo</a></td>',
1882
                'url',
1883
                'Foo',
1884
                ['safe' => false, 'route' => [
1885
                    'name' => 'sonata_admin_foo_object',
1886
                    'parameters' => [
1887
                        'param1' => 'abcd',
1888
                        'param2' => 'efgh',
1889
                        'param3' => 'ijkl',
1890
                    ],
1891
                    'identifier_parameter_name' => 'barId',
1892
                ]],
1893
            ],
1894
            [
1895
                '<th>Data</th> <td><a href="http://localhost/foo/obj/abcd/12345/efgh?param3=ijkl">Foo</a></td>',
1896
                'url',
1897
                'Foo',
1898
                ['safe' => false, 'route' => [
1899
                    'name' => 'sonata_admin_foo_object',
1900
                    'absolute' => true,
1901
                    'parameters' => [
1902
                        'param1' => 'abcd',
1903
                        'param2' => 'efgh',
1904
                        'param3' => 'ijkl',
1905
                    ],
1906
                    'identifier_parameter_name' => 'barId',
1907
                ]],
1908
            ],
1909
            [
1910
                '<th>Data</th> <td> &nbsp;</td>',
1911
                'email',
1912
                null,
1913
                [],
1914
            ],
1915
            [
1916
                '<th>Data</th> <td> <a href="mailto:[email protected]">[email protected]</a></td>',
1917
                'email',
1918
                '[email protected]',
1919
                [],
1920
            ],
1921
            [
1922
                '<th>Data</th> <td> <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['subject' => 'Main Theme', 'body' => 'Message Body']).'">[email protected]</a></td>',
1923
                'email',
1924
                '[email protected]',
1925
                ['subject' => 'Main Theme', 'body' => 'Message Body'],
1926
            ],
1927
            [
1928
                '<th>Data</th> <td> <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['subject' => 'Main Theme']).'">[email protected]</a></td>',
1929
                'email',
1930
                '[email protected]',
1931
                ['subject' => 'Main Theme'],
1932
            ],
1933
            [
1934
                '<th>Data</th> <td> <a href="mailto:[email protected]?'.$this->buildTwigLikeUrl(['body' => 'Message Body']).'">[email protected]</a></td>',
1935
                'email',
1936
                '[email protected]',
1937
                ['body' => 'Message Body'],
1938
            ],
1939
            [
1940
                '<th>Data</th> <td> [email protected]</td>',
1941
                'email',
1942
                '[email protected]',
1943
                ['as_string' => true, 'subject' => 'Main Theme', 'body' => 'Message Body'],
1944
            ],
1945
            [
1946
                '<th>Data</th> <td> [email protected]</td>',
1947
                'email',
1948
                '[email protected]',
1949
                ['as_string' => true, 'subject' => 'Main Theme'],
1950
            ],
1951
            [
1952
                '<th>Data</th> <td> [email protected]</td>',
1953
                'email',
1954
                '[email protected]',
1955
                ['as_string' => true, 'body' => 'Message Body'],
1956
            ],
1957
            [
1958
                '<th>Data</th> <td> <a href="mailto:[email protected]">[email protected]</a></td>',
1959
                'email',
1960
                '[email protected]',
1961
                ['as_string' => false],
1962
            ],
1963
            [
1964
                '<th>Data</th> <td> [email protected]</td>',
1965
                'email',
1966
                '[email protected]',
1967
                ['as_string' => true],
1968
            ],
1969
            [
1970
                '<th>Data</th> <td><p><strong>Creating a Template for the Field</strong> and form</p> </td>',
1971
                'html',
1972
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1973
                [],
1974
            ],
1975
            [
1976
                '<th>Data</th> <td>Creating a Template for the Field and form </td>',
1977
                'html',
1978
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1979
                ['strip' => true],
1980
            ],
1981
            [
1982
                '<th>Data</th> <td> Creating a Template for the... </td>',
1983
                'html',
1984
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1985
                ['truncate' => true],
1986
            ],
1987
            [
1988
                '<th>Data</th> <td> Creatin... </td>',
1989
                'html',
1990
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1991
                ['truncate' => ['length' => 10]],
1992
            ],
1993
            [
1994
                '<th>Data</th> <td> Creating a Template for the Field... </td>',
1995
                'html',
1996
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
1997
                ['truncate' => ['cut' => false]],
1998
            ],
1999
            [
2000
                '<th>Data</th> <td> Creating a Template for t etc. </td>',
2001
                'html',
2002
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
2003
                ['truncate' => ['ellipsis' => ' etc.']],
2004
            ],
2005
            [
2006
                '<th>Data</th> <td> Creating a Template[...] </td>',
2007
                'html',
2008
                '<p><strong>Creating a Template for the Field</strong> and form</p>',
2009
                [
2010
                    'truncate' => [
2011
                        'length' => 20,
2012
                        'cut' => false,
2013
                        'ellipsis' => '[...]',
2014
                    ],
2015
                ],
2016
            ],
2017
2018
            // NoValueException
2019
            ['<th>Data</th> <td></td>', 'string', new NoValueException(), ['safe' => false]],
2020
            ['<th>Data</th> <td></td>', 'text', new NoValueException(), ['safe' => false]],
2021
            ['<th>Data</th> <td></td>', 'textarea', new NoValueException(), ['safe' => false]],
2022
            ['<th>Data</th> <td>&nbsp;</td>', 'datetime', new NoValueException(), []],
2023
            [
2024
                '<th>Data</th> <td>&nbsp;</td>',
2025
                'datetime',
2026
                new NoValueException(),
2027
                ['format' => 'd.m.Y H:i:s'],
2028
            ],
2029
            ['<th>Data</th> <td>&nbsp;</td>', 'date', new NoValueException(), []],
2030
            ['<th>Data</th> <td>&nbsp;</td>', 'date', new NoValueException(), ['format' => 'd.m.Y']],
2031
            ['<th>Data</th> <td>&nbsp;</td>', 'time', new NoValueException(), []],
2032
            ['<th>Data</th> <td></td>', 'number', new NoValueException(), ['safe' => false]],
2033
            ['<th>Data</th> <td></td>', 'integer', new NoValueException(), ['safe' => false]],
2034
            ['<th>Data</th> <td> 0 % </td>', 'percent', new NoValueException(), []],
2035
            ['<th>Data</th> <td> </td>', 'currency', new NoValueException(), ['currency' => 'EUR']],
2036
            ['<th>Data</th> <td> </td>', 'currency', new NoValueException(), ['currency' => 'GBP']],
2037
            ['<th>Data</th> <td> </td>', 'array', new NoValueException(), ['safe' => false]],
2038
            [
2039
                '<th>Data</th> <td><span class="label label-danger">no</span></td>',
2040
                'boolean',
2041
                new NoValueException(),
2042
                [],
2043
            ],
2044
            [
2045
                '<th>Data</th> <td> </td>',
2046
                'trans',
2047
                new NoValueException(),
2048
                ['safe' => false, 'catalogue' => 'SonataAdminBundle'],
2049
            ],
2050
            [
2051
                '<th>Data</th> <td></td>',
2052
                'choice',
2053
                new NoValueException(),
2054
                ['safe' => false, 'choices' => []],
2055
            ],
2056
            [
2057
                '<th>Data</th> <td></td>',
2058
                'choice',
2059
                new NoValueException(),
2060
                ['safe' => false, 'choices' => [], 'multiple' => true],
2061
            ],
2062
            ['<th>Data</th> <td>&nbsp;</td>', 'url', new NoValueException(), []],
2063
            [
2064
                '<th>Data</th> <td>&nbsp;</td>',
2065
                'url',
2066
                new NoValueException(),
2067
                ['url' => 'http://example.com'],
2068
            ],
2069
            [
2070
                '<th>Data</th> <td>&nbsp;</td>',
2071
                'url',
2072
                new NoValueException(),
2073
                ['route' => ['name' => 'sonata_admin_foo']],
2074
            ],
2075
2076
            [
2077
                <<<'EOT'
2078
<th>Data</th> <td><div
2079
        class="sonata-readmore"
2080
        data-readmore-height="40"
2081
        data-readmore-more="Read more"
2082
        data-readmore-less="Close">
2083
            A very long string
2084
</div></td>
2085
EOT
2086
                ,
2087
                'text',
2088
                ' A very long string ',
2089
                [
2090
                    'collapse' => true,
2091
                    'safe' => false,
2092
                ],
2093
            ],
2094
            [
2095
                <<<'EOT'
2096
<th>Data</th> <td><div
2097
        class="sonata-readmore"
2098
        data-readmore-height="10"
2099
        data-readmore-more="More"
2100
        data-readmore-less="Less">
2101
            A very long string
2102
</div></td>
2103
EOT
2104
                ,
2105
                'text',
2106
                ' A very long string ',
2107
                [
2108
                    'collapse' => [
2109
                        'height' => 10,
2110
                        'more' => 'More',
2111
                        'less' => 'Less',
2112
                    ],
2113
                    'safe' => false,
2114
                ],
2115
            ],
2116
        ];
2117
    }
2118
2119
    /**
2120
     * NEXT_MAJOR: Remove this method.
2121
     *
2122
     * @group legacy
2123
     *
2124
     * @dataProvider getDeprecatedTextExtensionItems
2125
     *
2126
     * @expectedDeprecation The "truncate.preserve" option is deprecated since sonata-project/admin-bundle 3.x, to be removed in 4.0. Use "truncate.cut" instead. ("@SonataAdmin/CRUD/show_html.html.twig" at line %d).
2127
     *
2128
     * @expectedDeprecation The "truncate.separator" option is deprecated since sonata-project/admin-bundle 3.x, to be removed in 4.0. Use "truncate.ellipsis" instead. ("@SonataAdmin/CRUD/show_html.html.twig" at line %d).
2129
     */
2130
    public function testDeprecatedTextExtension(string $expected, string $type, $value, array $options): void
2131
    {
2132
        $loader = new StubFilesystemLoader([
2133
            __DIR__.'/../../../src/Resources/views/CRUD',
2134
        ]);
2135
        $loader->addPath(__DIR__.'/../../../src/Resources/views/', 'SonataAdmin');
2136
        $environment = new Environment($loader, [
2137
            'strict_variables' => true,
2138
            'cache' => false,
2139
            'autoescape' => 'html',
2140
            'optimizations' => 0,
2141
        ]);
2142
        $environment->addExtension($this->twigExtension);
2143
        $environment->addExtension(new TranslationExtension($this->translator));
2144
        $environment->addExtension(new StringExtension(new TextExtension()));
2145
2146
        $this->admin
0 ignored issues
show
Bug introduced by
The method method() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
2147
            ->method('getTemplate')
2148
            ->willReturn('@SonataAdmin/CRUD/base_show_field.html.twig');
2149
2150
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
2151
            ->method('getValue')
2152
            ->willReturn($value);
2153
2154
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
2155
            ->method('getType')
2156
            ->willReturn($type);
2157
2158
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
2159
            ->method('getOptions')
2160
            ->willReturn($options);
2161
2162
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
2163
            ->method('getTemplate')
2164
            ->willReturn('@SonataAdmin/CRUD/show_html.html.twig');
2165
2166
        $this->assertSame(
2167
            $this->removeExtraWhitespace($expected),
2168
            $this->removeExtraWhitespace(
2169
                $this->twigExtension->renderViewElement(
2170
                    $environment,
2171
                    $this->fieldDescription,
2172
                    $this->object
2173
                )
2174
            )
2175
        );
2176
    }
2177
2178
    /**
2179
     * NEXT_MAJOR: Remove this method.
2180
     */
2181
    public function getDeprecatedTextExtensionItems(): iterable
2182
    {
2183
        yield 'default_separator' => [
2184
            '<th>Data</th> <td> Creating a Template for the Field... </td>',
2185
            'html',
2186
            '<p><strong>Creating a Template for the Field</strong> and form</p>',
2187
            ['truncate' => ['preserve' => true, 'separator' => '...']],
2188
        ];
2189
2190
        yield 'custom_length' => [
2191
            '<th>Data</th> <td> Creating a Template for[...] </td>',
2192
            'html',
2193
            '<p><strong>Creating a Template for the Field</strong> and form</p>',
2194
            [
2195
                'truncate' => [
2196
                    'length' => 20,
2197
                    'preserve' => true,
2198
                    'separator' => '[...]',
2199
                ],
2200
            ],
2201
        ];
2202
    }
2203
2204
    public function testGetValueFromFieldDescription(): void
2205
    {
2206
        $object = new \stdClass();
2207
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2208
2209
        $fieldDescription
2210
            ->method('getValue')
2211
            ->willReturn('test123');
2212
2213
        $this->assertSame('test123', $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription));
2214
    }
2215
2216
    public function testGetValueFromFieldDescriptionWithRemoveLoopException(): void
2217
    {
2218
        $object = $this->createMock(\ArrayAccess::class);
2219
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2220
2221
        $this->expectException(\RuntimeException::class);
2222
        $this->expectExceptionMessage('remove the loop requirement');
2223
2224
        $this->assertSame(
2225
            'anything',
2226
            $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription, ['loop' => true])
2227
        );
2228
    }
2229
2230
    public function testGetValueFromFieldDescriptionWithNoValueException(): void
2231
    {
2232
        $object = new \stdClass();
2233
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2234
2235
        $fieldDescription
2236
            ->method('getValue')
2237
            ->willReturnCallback(static function (): void {
2238
                throw new NoValueException();
2239
            });
2240
2241
        $fieldDescription
2242
            ->method('getAssociationAdmin')
2243
            ->willReturn(null);
2244
2245
        $this->assertNull($this->twigExtension->getValueFromFieldDescription($object, $fieldDescription));
2246
    }
2247
2248
    public function testGetValueFromFieldDescriptionWithNoValueExceptionNewAdminInstance(): void
2249
    {
2250
        $object = new \stdClass();
2251
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2252
2253
        $fieldDescription
2254
            ->method('getValue')
2255
            ->willReturnCallback(static function (): void {
2256
                throw new NoValueException();
2257
            });
2258
2259
        $fieldDescription
2260
            ->method('getAssociationAdmin')
2261
            ->willReturn($this->admin);
2262
2263
        $this->admin->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
2264
            ->method('getNewInstance')
2265
            ->willReturn('foo');
2266
2267
        $this->assertSame('foo', $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription));
2268
    }
2269
2270
    /**
2271
     * @group legacy
2272
     */
2273
    public function testOutput(): void
2274
    {
2275
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
2276
            ->method('getTemplate')
2277
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
2278
2279
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
2280
            ->method('getFieldName')
2281
            ->willReturn('fd_name');
2282
2283
        $this->environment->disableDebug();
2284
2285
        $parameters = [
2286
            'admin' => $this->admin,
2287
            'value' => 'foo',
2288
            'field_description' => $this->fieldDescription,
2289
            'object' => $this->object,
2290
        ];
2291
2292
        $template = $this->environment->loadTemplate('@SonataAdmin/CRUD/base_list_field.html.twig');
2293
2294
        $this->assertSame(
2295
            '<td class="sonata-ba-list-field sonata-ba-list-field-" objectId="12345"> foo </td>',
2296
            $this->removeExtraWhitespace($this->twigExtension->output(
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Twig\...dminExtension::output() has been deprecated with message: since sonata-project/admin-bundle 3.33, to be removed in 4.0. Use render instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
2297
                $this->fieldDescription,
2298
                $template,
2299
                $parameters,
2300
                $this->environment
2301
            ))
2302
        );
2303
2304
        $this->environment->enableDebug();
2305
        $this->assertSame(
2306
            $this->removeExtraWhitespace(
2307
                <<<'EOT'
2308
<!-- START
2309
    fieldName: fd_name
2310
    template: @SonataAdmin/CRUD/base_list_field.html.twig
2311
    compiled template: @SonataAdmin/CRUD/base_list_field.html.twig
2312
-->
2313
    <td class="sonata-ba-list-field sonata-ba-list-field-" objectId="12345"> foo </td>
2314
<!-- END - fieldName: fd_name -->
2315
EOT
2316
            ),
2317
            $this->removeExtraWhitespace(
2318
                $this->twigExtension->output($this->fieldDescription, $template, $parameters, $this->environment)
0 ignored issues
show
Deprecated Code introduced by
The method Sonata\AdminBundle\Twig\...dminExtension::output() has been deprecated with message: since sonata-project/admin-bundle 3.33, to be removed in 4.0. Use render instead

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
2319
            )
2320
        );
2321
    }
2322
2323
    /**
2324
     * @group legacy
2325
     * @expectedDeprecation The Sonata\AdminBundle\Admin\AbstractAdmin::getTemplate method is deprecated (since sonata-project/admin-bundle 3.34, will be dropped in 4.0. Use TemplateRegistry services instead).
2326
     */
2327
    public function testRenderWithDebug(): void
2328
    {
2329
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
2330
            ->method('getTemplate')
2331
            ->willReturn('@SonataAdmin/CRUD/base_list_field.html.twig');
2332
2333
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
2334
            ->method('getFieldName')
2335
            ->willReturn('fd_name');
2336
2337
        $this->fieldDescription
0 ignored issues
show
Bug introduced by
The method method() 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...
2338
            ->method('getValue')
2339
            ->willReturn('foo');
2340
2341
        $parameters = [
2342
            'admin' => $this->admin,
2343
            'value' => 'foo',
2344
            'field_description' => $this->fieldDescription,
2345
            'object' => $this->object,
2346
        ];
2347
2348
        $this->environment->enableDebug();
2349
2350
        $this->assertSame(
2351
            $this->removeExtraWhitespace(
2352
                <<<'EOT'
2353
<!-- START
2354
    fieldName: fd_name
2355
    template: @SonataAdmin/CRUD/base_list_field.html.twig
2356
    compiled template: @SonataAdmin/CRUD/base_list_field.html.twig
2357
-->
2358
    <td class="sonata-ba-list-field sonata-ba-list-field-" objectId="12345"> foo </td>
2359
<!-- END - fieldName: fd_name -->
2360
EOT
2361
            ),
2362
            $this->removeExtraWhitespace(
2363
                $this->twigExtension->renderListElement($this->environment, $this->object, $this->fieldDescription, $parameters)
2364
            )
2365
        );
2366
    }
2367
2368
    public function testRenderRelationElementNoObject(): void
2369
    {
2370
        $this->assertSame('foo', $this->twigExtension->renderRelationElement('foo', $this->fieldDescription));
2371
    }
2372
2373
    public function testRenderRelationElementToString(): void
2374
    {
2375
        $this->fieldDescription->expects($this->exactly(2))
0 ignored issues
show
Bug introduced by
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...
2376
            ->method('getOption')
2377
            ->willReturnCallback(static function ($value, $default = null) {
2378
                if ('associated_property' === $value) {
2379
                    return $default;
2380
                }
2381
            });
2382
2383
        $element = new FooToString();
2384
        $this->assertSame('salut', $this->twigExtension->renderRelationElement($element, $this->fieldDescription));
2385
    }
2386
2387
    /**
2388
     * @group legacy
2389
     */
2390
    public function testDeprecatedRelationElementToString(): void
2391
    {
2392
        $this->fieldDescription->expects($this->exactly(2))
0 ignored issues
show
Bug introduced by
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...
2393
            ->method('getOption')
2394
            ->willReturnCallback(static function ($value, $default = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $default is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2395
                if ('associated_tostring' === $value) {
2396
                    return '__toString';
2397
                }
2398
            });
2399
2400
        $element = new FooToString();
2401
        $this->assertSame(
2402
            'salut',
2403
            $this->twigExtension->renderRelationElement($element, $this->fieldDescription)
2404
        );
2405
    }
2406
2407
    /**
2408
     * @group legacy
2409
     */
2410
    public function testRenderRelationElementCustomToString(): void
2411
    {
2412
        $this->fieldDescription->expects($this->exactly(2))
0 ignored issues
show
Bug introduced by
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...
2413
            ->method('getOption')
2414
            ->willReturnCallback(static function ($value, $default = null) {
2415
                if ('associated_property' === $value) {
2416
                    return $default;
2417
                }
2418
2419
                if ('associated_tostring' === $value) {
2420
                    return 'customToString';
2421
                }
2422
            });
2423
2424
        $element = $this->getMockBuilder('stdClass')
2425
            ->setMethods(['customToString'])
2426
            ->getMock();
2427
        $element
2428
            ->method('customToString')
2429
            ->willReturn('fooBar');
2430
2431
        $this->assertSame('fooBar', $this->twigExtension->renderRelationElement($element, $this->fieldDescription));
2432
    }
2433
2434
    /**
2435
     * @group legacy
2436
     */
2437
    public function testRenderRelationElementMethodNotExist(): void
2438
    {
2439
        $this->fieldDescription->expects($this->exactly(2))
0 ignored issues
show
Bug introduced by
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...
2440
            ->method('getOption')
2441
2442
            ->willReturnCallback(static function ($value, $default = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $default is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2443
                if ('associated_tostring' === $value) {
2444
                    return 'nonExistedMethod';
2445
                }
2446
            });
2447
2448
        $element = new \stdClass();
2449
        $this->expectException(\RuntimeException::class);
2450
        $this->expectExceptionMessage('You must define an `associated_property` option or create a `stdClass::__toString');
2451
2452
        $this->twigExtension->renderRelationElement($element, $this->fieldDescription);
2453
    }
2454
2455
    public function testRenderRelationElementWithPropertyPath(): void
2456
    {
2457
        $this->fieldDescription->expects($this->once())
0 ignored issues
show
Bug introduced by
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...
2458
            ->method('getOption')
2459
2460
            ->willReturnCallback(static function ($value, $default = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $default is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2461
                if ('associated_property' === $value) {
2462
                    return 'foo';
2463
                }
2464
            });
2465
2466
        $element = new \stdClass();
2467
        $element->foo = 'bar';
2468
2469
        $this->assertSame('bar', $this->twigExtension->renderRelationElement($element, $this->fieldDescription));
2470
    }
2471
2472
    public function testRenderRelationElementWithClosure(): void
2473
    {
2474
        $this->fieldDescription->expects($this->once())
0 ignored issues
show
Bug introduced by
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...
2475
            ->method('getOption')
2476
2477
            ->willReturnCallback(static function ($value, $default = null) {
0 ignored issues
show
Unused Code introduced by
The parameter $default is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
2478
                if ('associated_property' === $value) {
2479
                    return static function ($element): string {
2480
                        return 'closure '.$element->foo;
2481
                    };
2482
                }
2483
            });
2484
2485
        $element = new \stdClass();
2486
        $element->foo = 'bar';
2487
2488
        $this->assertSame(
2489
            'closure bar',
2490
            $this->twigExtension->renderRelationElement($element, $this->fieldDescription)
2491
        );
2492
    }
2493
2494
    public function testGetUrlsafeIdentifier(): void
2495
    {
2496
        $entity = new \stdClass();
2497
2498
        // set admin to pool
2499
        $this->pool->setAdminServiceIds(['sonata_admin_foo_service']);
2500
        $this->pool->setAdminClasses(['stdClass' => ['sonata_admin_foo_service']]);
2501
2502
        $this->admin->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
2503
            ->method('getUrlsafeIdentifier')
2504
            ->with($this->equalTo($entity))
2505
            ->willReturn(1234567);
2506
2507
        $this->assertSame(1234567, $this->twigExtension->getUrlsafeIdentifier($entity));
2508
    }
2509
2510
    public function testGetUrlsafeIdentifier_GivenAdmin_Foo(): void
2511
    {
2512
        $entity = new \stdClass();
2513
2514
        // set admin to pool
2515
        $this->pool->setAdminServiceIds([
2516
            'sonata_admin_foo_service',
2517
            'sonata_admin_bar_service',
2518
        ]);
2519
        $this->pool->setAdminClasses(['stdClass' => [
2520
            'sonata_admin_foo_service',
2521
            'sonata_admin_bar_service',
2522
        ]]);
2523
2524
        $this->admin->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
2525
            ->method('getUrlsafeIdentifier')
2526
            ->with($this->equalTo($entity))
2527
            ->willReturn(1234567);
2528
2529
        $this->adminBar->expects($this->never())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
2530
            ->method('getUrlsafeIdentifier');
2531
2532
        $this->assertSame(1234567, $this->twigExtension->getUrlsafeIdentifier($entity, $this->admin));
2533
    }
2534
2535
    public function testGetUrlsafeIdentifier_GivenAdmin_Bar(): void
2536
    {
2537
        $entity = new \stdClass();
2538
2539
        // set admin to pool
2540
        $this->pool->setAdminServiceIds(['sonata_admin_foo_service', 'sonata_admin_bar_service']);
2541
        $this->pool->setAdminClasses(['stdClass' => [
2542
            'sonata_admin_foo_service',
2543
            'sonata_admin_bar_service',
2544
        ]]);
2545
2546
        $this->admin->expects($this->never())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
2547
            ->method('getUrlsafeIdentifier');
2548
2549
        $this->adminBar->expects($this->once())
0 ignored issues
show
Bug introduced by
The method expects() does not seem to exist on object<Sonata\AdminBundle\Admin\AdminInterface>.

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...
2550
            ->method('getUrlsafeIdentifier')
2551
            ->with($this->equalTo($entity))
2552
            ->willReturn(1234567);
2553
2554
        $this->assertSame(1234567, $this->twigExtension->getUrlsafeIdentifier($entity, $this->adminBar));
2555
    }
2556
2557
    public function xEditableChoicesProvider()
2558
    {
2559
        return [
2560
            'needs processing' => [
2561
                ['choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2']],
2562
                [
2563
                    ['value' => 'Status1', 'text' => 'Alias1'],
2564
                    ['value' => 'Status2', 'text' => 'Alias2'],
2565
                ],
2566
            ],
2567
            'already processed' => [
2568
                ['choices' => [
2569
                    ['value' => 'Status1', 'text' => 'Alias1'],
2570
                    ['value' => 'Status2', 'text' => 'Alias2'],
2571
                ]],
2572
                [
2573
                    ['value' => 'Status1', 'text' => 'Alias1'],
2574
                    ['value' => 'Status2', 'text' => 'Alias2'],
2575
                ],
2576
            ],
2577
            'not required' => [
2578
                [
2579
                    'required' => false,
2580
                    'choices' => ['' => '', 'Status1' => 'Alias1', 'Status2' => 'Alias2'],
2581
                ],
2582
                [
2583
                    ['value' => '', 'text' => ''],
2584
                    ['value' => 'Status1', 'text' => 'Alias1'],
2585
                    ['value' => 'Status2', 'text' => 'Alias2'],
2586
                ],
2587
            ],
2588
            'not required multiple' => [
2589
                [
2590
                    'required' => false,
2591
                    'multiple' => true,
2592
                    'choices' => ['Status1' => 'Alias1', 'Status2' => 'Alias2'],
2593
                ],
2594
                [
2595
                    ['value' => 'Status1', 'text' => 'Alias1'],
2596
                    ['value' => 'Status2', 'text' => 'Alias2'],
2597
                ],
2598
            ],
2599
        ];
2600
    }
2601
2602
    /**
2603
     * @dataProvider xEditablechoicesProvider
2604
     */
2605
    public function testGetXEditableChoicesIsIdempotent(array $options, array $expectedChoices): void
2606
    {
2607
        $fieldDescription = $this->getMockForAbstractClass(FieldDescriptionInterface::class);
2608
        $fieldDescription
2609
            ->method('getOption')
2610
            ->withConsecutive(
2611
                ['choices', []],
2612
                ['catalogue'],
2613
                ['required'],
2614
                ['multiple']
2615
            )
2616
            ->will($this->onConsecutiveCalls(
2617
                $options['choices'],
2618
                'MyCatalogue',
2619
                $options['multiple'] ?? null
2620
            ));
2621
2622
        $this->assertSame($expectedChoices, $this->twigExtension->getXEditableChoices($fieldDescription));
2623
    }
2624
2625
    public function select2LocalesProvider()
2626
    {
2627
        return [
2628
            ['ar', 'ar'],
2629
            ['az', 'az'],
2630
            ['bg', 'bg'],
2631
            ['ca', 'ca'],
2632
            ['cs', 'cs'],
2633
            ['da', 'da'],
2634
            ['de', 'de'],
2635
            ['el', 'el'],
2636
            [null, 'en'],
2637
            ['es', 'es'],
2638
            ['et', 'et'],
2639
            ['eu', 'eu'],
2640
            ['fa', 'fa'],
2641
            ['fi', 'fi'],
2642
            ['fr', 'fr'],
2643
            ['gl', 'gl'],
2644
            ['he', 'he'],
2645
            ['hr', 'hr'],
2646
            ['hu', 'hu'],
2647
            ['id', 'id'],
2648
            ['is', 'is'],
2649
            ['it', 'it'],
2650
            ['ja', 'ja'],
2651
            ['ka', 'ka'],
2652
            ['ko', 'ko'],
2653
            ['lt', 'lt'],
2654
            ['lv', 'lv'],
2655
            ['mk', 'mk'],
2656
            ['ms', 'ms'],
2657
            ['nb', 'nb'],
2658
            ['nl', 'nl'],
2659
            ['pl', 'pl'],
2660
            ['pt-PT', 'pt'],
2661
            ['pt-BR', 'pt-BR'],
2662
            ['pt-PT', 'pt-PT'],
2663
            ['ro', 'ro'],
2664
            ['rs', 'rs'],
2665
            ['ru', 'ru'],
2666
            ['sk', 'sk'],
2667
            ['sv', 'sv'],
2668
            ['th', 'th'],
2669
            ['tr', 'tr'],
2670
            ['ug-CN', 'ug'],
2671
            ['ug-CN', 'ug-CN'],
2672
            ['uk', 'uk'],
2673
            ['vi', 'vi'],
2674
            ['zh-CN', 'zh'],
2675
            ['zh-CN', 'zh-CN'],
2676
            ['zh-TW', 'zh-TW'],
2677
        ];
2678
    }
2679
2680
    /**
2681
     * @dataProvider select2LocalesProvider
2682
     */
2683
    public function testCanonicalizedLocaleForSelect2(?string $expected, string $original): void
2684
    {
2685
        $this->assertSame($expected, $this->twigExtension->getCanonicalizedLocaleForSelect2($this->mockExtensionContext($original)));
2686
    }
2687
2688
    public function momentLocalesProvider(): array
2689
    {
2690
        return [
2691
            ['af', 'af'],
2692
            ['ar-dz', 'ar-dz'],
2693
            ['ar', 'ar'],
2694
            ['ar-ly', 'ar-ly'],
2695
            ['ar-ma', 'ar-ma'],
2696
            ['ar-sa', 'ar-sa'],
2697
            ['ar-tn', 'ar-tn'],
2698
            ['az', 'az'],
2699
            ['be', 'be'],
2700
            ['bg', 'bg'],
2701
            ['bn', 'bn'],
2702
            ['bo', 'bo'],
2703
            ['br', 'br'],
2704
            ['bs', 'bs'],
2705
            ['ca', 'ca'],
2706
            ['cs', 'cs'],
2707
            ['cv', 'cv'],
2708
            ['cy', 'cy'],
2709
            ['da', 'da'],
2710
            ['de-at', 'de-at'],
2711
            ['de', 'de'],
2712
            ['de', 'de-de'],
2713
            ['dv', 'dv'],
2714
            ['el', 'el'],
2715
            [null, 'en'],
2716
            [null, 'en-us'],
2717
            ['en-au', 'en-au'],
2718
            ['en-ca', 'en-ca'],
2719
            ['en-gb', 'en-gb'],
2720
            ['en-ie', 'en-ie'],
2721
            ['en-nz', 'en-nz'],
2722
            ['eo', 'eo'],
2723
            ['es-do', 'es-do'],
2724
            ['es', 'es-ar'],
2725
            ['es', 'es-mx'],
2726
            ['es', 'es'],
2727
            ['et', 'et'],
2728
            ['eu', 'eu'],
2729
            ['fa', 'fa'],
2730
            ['fi', 'fi'],
2731
            ['fo', 'fo'],
2732
            ['fr-ca', 'fr-ca'],
2733
            ['fr-ch', 'fr-ch'],
2734
            ['fr', 'fr-fr'],
2735
            ['fr', 'fr'],
2736
            ['fy', 'fy'],
2737
            ['gd', 'gd'],
2738
            ['gl', 'gl'],
2739
            ['he', 'he'],
2740
            ['hi', 'hi'],
2741
            ['hr', 'hr'],
2742
            ['hu', 'hu'],
2743
            ['hy-am', 'hy-am'],
2744
            ['id', 'id'],
2745
            ['is', 'is'],
2746
            ['it', 'it'],
2747
            ['ja', 'ja'],
2748
            ['jv', 'jv'],
2749
            ['ka', 'ka'],
2750
            ['kk', 'kk'],
2751
            ['km', 'km'],
2752
            ['ko', 'ko'],
2753
            ['ky', 'ky'],
2754
            ['lb', 'lb'],
2755
            ['lo', 'lo'],
2756
            ['lt', 'lt'],
2757
            ['lv', 'lv'],
2758
            ['me', 'me'],
2759
            ['mi', 'mi'],
2760
            ['mk', 'mk'],
2761
            ['ml', 'ml'],
2762
            ['mr', 'mr'],
2763
            ['ms', 'ms'],
2764
            ['ms-my', 'ms-my'],
2765
            ['my', 'my'],
2766
            ['nb', 'nb'],
2767
            ['ne', 'ne'],
2768
            ['nl-be', 'nl-be'],
2769
            ['nl', 'nl'],
2770
            ['nl', 'nl-nl'],
2771
            ['nn', 'nn'],
2772
            ['pa-in', 'pa-in'],
2773
            ['pl', 'pl'],
2774
            ['pt-br', 'pt-br'],
2775
            ['pt', 'pt'],
2776
            ['ro', 'ro'],
2777
            ['ru', 'ru'],
2778
            ['se', 'se'],
2779
            ['si', 'si'],
2780
            ['sk', 'sk'],
2781
            ['sl', 'sl'],
2782
            ['sq', 'sq'],
2783
            ['sr-cyrl', 'sr-cyrl'],
2784
            ['sr', 'sr'],
2785
            ['ss', 'ss'],
2786
            ['sv', 'sv'],
2787
            ['sw', 'sw'],
2788
            ['ta', 'ta'],
2789
            ['te', 'te'],
2790
            ['tet', 'tet'],
2791
            ['th', 'th'],
2792
            ['tlh', 'tlh'],
2793
            ['tl-ph', 'tl-ph'],
2794
            ['tr', 'tr'],
2795
            ['tzl', 'tzl'],
2796
            ['tzm', 'tzm'],
2797
            ['tzm-latn', 'tzm-latn'],
2798
            ['uk', 'uk'],
2799
            ['uz', 'uz'],
2800
            ['vi', 'vi'],
2801
            ['x-pseudo', 'x-pseudo'],
2802
            ['yo', 'yo'],
2803
            ['zh-cn', 'zh-cn'],
2804
            ['zh-hk', 'zh-hk'],
2805
            ['zh-tw', 'zh-tw'],
2806
        ];
2807
    }
2808
2809
    /**
2810
     * @dataProvider momentLocalesProvider
2811
     */
2812
    public function testCanonicalizedLocaleForMoment(?string $expected, string $original): void
2813
    {
2814
        $this->assertSame($expected, $this->twigExtension->getCanonicalizedLocaleForMoment($this->mockExtensionContext($original)));
2815
    }
2816
2817
    public function testIsGrantedAffirmative(): void
2818
    {
2819
        $this->assertTrue(
2820
            $this->twigExtension->isGrantedAffirmative(['foo', 'bar'])
2821
        );
2822
        $this->assertTrue($this->twigExtension->isGrantedAffirmative('foo'));
2823
        $this->assertTrue($this->twigExtension->isGrantedAffirmative('bar'));
2824
    }
2825
2826
    /**
2827
     * This method generates url part for Twig layout.
2828
     */
2829
    private function buildTwigLikeUrl(array $url): string
2830
    {
2831
        return htmlspecialchars(http_build_query($url, '', '&', PHP_QUERY_RFC3986));
2832
    }
2833
2834
    private function removeExtraWhitespace(string $string): string
2835
    {
2836
        return trim(preg_replace(
2837
            '/\s+/',
2838
            ' ',
2839
            $string
2840
        ));
2841
    }
2842
2843
    private function mockExtensionContext(string $locale): array
2844
    {
2845
        $request = $this->createMock(Request::class);
2846
        $request->method('getLocale')->willReturn($locale);
2847
        $appVariable = $this->createMock(AppVariable::class);
2848
        $appVariable->method('getRequest')->willReturn($request);
2849
2850
        return ['app' => $appVariable];
2851
    }
2852
}
2853