* * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Sonata\AdminBundle\Tests\Twig\Extension; use Psr\Log\LoggerInterface; use Sonata\AdminBundle\Admin\AdminInterface; use Sonata\AdminBundle\Admin\FieldDescriptionInterface; use Sonata\AdminBundle\Admin\Pool; use Sonata\AdminBundle\Exception\NoValueException; use Sonata\AdminBundle\Tests\Fixtures\Entity\FooToString; use Sonata\AdminBundle\Twig\Extension\SonataAdminExtension; use Symfony\Bridge\Twig\Extension\RoutingExtension; use Symfony\Bridge\Twig\Extension\TranslationExtension; use Symfony\Bridge\Twig\Tests\Extension\Fixtures\StubFilesystemLoader; use Symfony\Component\Config\FileLocator; use Symfony\Component\Routing\Generator\UrlGenerator; use Symfony\Component\Routing\Loader\XmlFileLoader; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Translation\Loader\XliffFileLoader; use Symfony\Component\Translation\MessageSelector; use Symfony\Component\Translation\Translator; /** * Test for SonataAdminExtension. * * @author Andrej Hudec */ class SonataAdminExtensionTest extends \PHPUnit_Framework_TestCase { /** * @var SonataAdminExtension */ private $twigExtension; /** * @var \Twig_Environment */ private $environment; /** * @var AdminInterface */ private $admin; /** * @var AdminInterface */ private $adminBar; /** * @var FieldDescriptionInterface */ private $fieldDescription; /** * @var \stdClass */ private $object; /** * @var Pool */ private $pool; /** * @var LoggerInterface */ private $logger; /** * @var string[] */ private $xEditableTypeMapping; public function setUp() { date_default_timezone_set('Europe/London'); $container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); $this->pool = new Pool($container, '', ''); $this->pool->setAdminServiceIds(array('sonata_admin_foo_service')); $this->pool->setAdminClasses(array('fooClass' => array('sonata_admin_foo_service'))); $this->logger = $this->getMock('Psr\Log\LoggerInterface'); $this->xEditableTypeMapping = array( 'choice' => 'select', 'boolean' => 'select', 'text' => 'text', 'textarea' => 'textarea', 'html' => 'textarea', 'email' => 'email', 'string' => 'text', 'smallint' => 'text', 'bigint' => 'text', 'integer' => 'number', 'decimal' => 'number', 'currency' => 'number', 'percent' => 'number', 'url' => 'url', ); $this->twigExtension = new SonataAdminExtension($this->pool, $this->logger); $this->twigExtension->setXEditableTypeMapping($this->xEditableTypeMapping); $loader = new StubFilesystemLoader(array( __DIR__.'/../../../Resources/views/CRUD', )); $this->environment = new \Twig_Environment($loader, array( 'strict_variables' => true, 'cache' => false, 'autoescape' => 'html', 'optimizations' => 0, )); $this->environment->addExtension($this->twigExtension); // translation extension $translator = new Translator('en', new MessageSelector()); $translator->addLoader('xlf', new XliffFileLoader()); $translator->addResource( 'xlf', __DIR__.'/../../../Resources/translations/SonataAdminBundle.en.xliff', 'en', 'SonataAdminBundle' ); $this->environment->addExtension(new TranslationExtension($translator)); // routing extension $xmlFileLoader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../../../Resources/config/routing'))); $routeCollection = $xmlFileLoader->load('sonata_admin.xml'); $xmlFileLoader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../../Fixtures/Resources/config/routing'))); $testRouteCollection = $xmlFileLoader->load('routing.xml'); $routeCollection->addCollection($testRouteCollection); $requestContext = new RequestContext(); $urlGenerator = new UrlGenerator($routeCollection, $requestContext); $this->environment->addExtension(new RoutingExtension($urlGenerator)); $this->environment->addExtension(new \Twig_Extensions_Extension_Text()); // initialize object $this->object = new \stdClass(); // initialize admin $this->admin = $this->getMock('Sonata\AdminBundle\Admin\AdminInterface'); $this->admin->expects($this->any()) ->method('getCode') ->will($this->returnValue('xyz')); $this->admin->expects($this->any()) ->method('id') ->with($this->equalTo($this->object)) ->will($this->returnValue(12345)); $this->admin->expects($this->any()) ->method('getNormalizedIdentifier') ->with($this->equalTo($this->object)) ->will($this->returnValue(12345)); $this->admin->expects($this->any()) ->method('trans') ->will($this->returnCallback(function ($id, $parameters = array(), $domain = null) use ($translator) { return $translator->trans($id, $parameters, $domain); })); $this->adminBar = $this->getMock('Sonata\AdminBundle\Admin\AdminInterface'); $this->adminBar->expects($this->any()) ->method('isGranted') ->will($this->returnValue(true)); $this->adminBar->expects($this->any()) ->method('getNormalizedIdentifier') ->with($this->equalTo($this->object)) ->will($this->returnValue(12345)); // for php5.3 BC $admin = $this->admin; $adminBar = $this->adminBar; $container->expects($this->any()) ->method('get') ->will($this->returnCallback(function ($id) use ($admin, $adminBar) { if ($id == 'sonata_admin_foo_service') { return $admin; } elseif ($id == 'sonata_admin_bar_service') { return $adminBar; } return; })); // initialize field description $this->fieldDescription = $this->getMock('Sonata\AdminBundle\Admin\FieldDescriptionInterface'); $this->fieldDescription->expects($this->any()) ->method('getName') ->will($this->returnValue('fd_name')); $this->fieldDescription->expects($this->any()) ->method('getAdmin') ->will($this->returnValue($this->admin)); $this->fieldDescription->expects($this->any()) ->method('getLabel') ->will($this->returnValue('Data')); } /** * @dataProvider getRenderListElementTests */ public function testRenderListElement($expected, $type, $value, array $options) { $this->admin->expects($this->any()) ->method('isGranted') ->will($this->returnValue(true)); $this->admin->expects($this->any()) ->method('getTemplate') ->with($this->equalTo('base_list_field')) ->will($this->returnValue('SonataAdminBundle:CRUD:base_list_field.html.twig')); $this->fieldDescription->expects($this->any()) ->method('getValue') ->will($this->returnValue($value)); $this->fieldDescription->expects($this->any()) ->method('getType') ->will($this->returnValue($type)); $this->fieldDescription->expects($this->any()) ->method('getOptions') ->will($this->returnValue($options)); $this->fieldDescription->expects($this->any()) ->method('getOption') ->will($this->returnCallback(function ($name, $default = null) use ($options) { return isset($options[$name]) ? $options[$name] : $default; })); $this->fieldDescription->expects($this->any()) ->method('getTemplate') ->will($this->returnCallback(function () use ($type) { switch ($type) { case 'string': return 'SonataAdminBundle:CRUD:list_string.html.twig'; case 'boolean': return 'SonataAdminBundle:CRUD:list_boolean.html.twig'; case 'datetime': return 'SonataAdminBundle:CRUD:list_datetime.html.twig'; case 'date': return 'SonataAdminBundle:CRUD:list_date.html.twig'; case 'time': return 'SonataAdminBundle:CRUD:list_time.html.twig'; case 'currency': return 'SonataAdminBundle:CRUD:list_currency.html.twig'; case 'percent': return 'SonataAdminBundle:CRUD:list_percent.html.twig'; case 'email': return 'SonataAdminBundle:CRUD:list_email.html.twig'; case 'choice': return 'SonataAdminBundle:CRUD:list_choice.html.twig'; case 'array': return 'SonataAdminBundle:CRUD:list_array.html.twig'; case 'trans': return 'SonataAdminBundle:CRUD:list_trans.html.twig'; case 'url': return 'SonataAdminBundle:CRUD:list_url.html.twig'; case 'html': return 'SonataAdminBundle:CRUD:list_html.html.twig'; case 'nonexistent': // template doesn`t exist return 'SonataAdminBundle:CRUD:list_nonexistent_template.html.twig'; default: return false; } })); $this->assertSame( $this->removeExtraWhitespace($expected), $this->removeExtraWhitespace($this->twigExtension->renderListElement( $this->environment, $this->object, $this->fieldDescription )) ); } /** * @dataProvider getDeprecatedRenderListElementTests * @group legacy */ public function testDeprecatedRenderListElement($expected, $value, array $options) { $this->admin->expects($this->any()) ->method('isGranted') ->will($this->returnValue(true)); $this->admin->expects($this->any()) ->method('getTemplate') ->with($this->equalTo('base_list_field')) ->will($this->returnValue('SonataAdminBundle:CRUD:base_list_field.html.twig')); $this->fieldDescription->expects($this->any()) ->method('getValue') ->will($this->returnValue($value)); $this->fieldDescription->expects($this->any()) ->method('getType') ->will($this->returnValue('nonexistent')); $this->fieldDescription->expects($this->any()) ->method('getOptions') ->will($this->returnValue($options)); $this->fieldDescription->expects($this->any()) ->method('getOption') ->will($this->returnCallback(function ($name, $default = null) use ($options) { return isset($options[$name]) ? $options[$name] : $default; })); $this->fieldDescription->expects($this->any()) ->method('getTemplate') ->will($this->returnValue('SonataAdminBundle:CRUD:list_nonexistent_template.html.twig')); $this->assertSame( $this->removeExtraWhitespace($expected), $this->removeExtraWhitespace($this->twigExtension->renderListElement( $this->environment, $this->object, $this->fieldDescription )) ); } public function getDeprecatedRenderListElementTests() { return array( array( ' Example ', 'Example', array(), ), array( ' ', null, array(), ), ); } public function getRenderListElementTests() { return array( array( ' Example ', 'string', 'Example', array(), ), array( ' ', 'string', null, array(), ), array( ' Example ', 'text', 'Example', array(), ), array( ' ', 'text', null, array(), ), array( ' Example ', 'textarea', 'Example', array(), ), array( ' ', 'textarea', null, array(), ), 'datetime field' => array( ' December 24, 2013 10:11 ', 'datetime', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array(), ), array( ' December 24, 2013 18:11 ', 'datetime', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')), array('timezone' => 'Asia/Hong_Kong'), ), array( '   ', 'datetime', null, array(), ), array( ' 24.12.2013 10:11:12 ', 'datetime', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array('format' => 'd.m.Y H:i:s'), ), array( '   ', 'datetime', null, array('format' => 'd.m.Y H:i:s'), ), array( ' 24.12.2013 18:11:12 ', 'datetime', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('UTC')), array('format' => 'd.m.Y H:i:s', 'timezone' => 'Asia/Hong_Kong'), ), array( '   ', 'datetime', null, array('format' => 'd.m.Y H:i:s', 'timezone' => 'Asia/Hong_Kong'), ), array( ' December 24, 2013 ', 'date', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array(), ), array( '   ', 'date', null, array(), ), array( ' 24.12.2013 ', 'date', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array('format' => 'd.m.Y'), ), array( '   ', 'date', null, array('format' => 'd.m.Y'), ), array( ' 10:11:12 ', 'time', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array(), ), array( '   ', 'time', null, array(), ), array( ' 10.746135 ', 'number', 10.746135, array(), ), array( ' ', 'number', null, array(), ), array( ' 5678 ', 'integer', 5678, array(), ), array( ' ', 'integer', null, array(), ), array( ' 1074.6135 % ', 'percent', 10.746135, array(), ), array( ' 0 % ', 'percent', null, array(), ), array( ' EUR 10.746135 ', 'currency', 10.746135, array('currency' => 'EUR'), ), array( ' ', 'currency', null, array('currency' => 'EUR'), ), array( ' GBP 51.23456 ', 'currency', 51.23456, array('currency' => 'GBP'), ), array( ' ', 'currency', null, array('currency' => 'GBP'), ), array( '   ', 'email', null, array(), ), array( ' admin@admin.com ', 'email', 'admin@admin.com', array(), ), array( ' admin@admin.com ', 'email', 'admin@admin.com', array('as_string' => false), ), array( ' admin@admin.com ', 'email', 'admin@admin.com', array('as_string' => true), ), array( ' admin@admin.com ', 'email', 'admin@admin.com', array('subject' => 'Main Theme', 'body' => 'Message Body'), ), array( ' admin@admin.com ', 'email', 'admin@admin.com', array('subject' => 'Main Theme'), ), array( ' admin@admin.com ', 'email', 'admin@admin.com', array('body' => 'Message Body'), ), array( ' admin@admin.com ', 'email', 'admin@admin.com', array('as_string' => true, 'subject' => 'Main Theme', 'body' => 'Message Body'), ), array( ' admin@admin.com ', 'email', 'admin@admin.com', array('as_string' => true, 'body' => 'Message Body'), ), array( ' admin@admin.com ', 'email', 'admin@admin.com', array('as_string' => true, 'subject' => 'Main Theme'), ), array( ' [1 => First] [2 => Second] ', 'array', array(1 => 'First', 2 => 'Second'), array(), ), array( ' ', 'array', null, array(), ), array( ' yes ', 'boolean', true, array('editable' => false), ), array( ' no ', 'boolean', false, array('editable' => false), ), array( ' no ', 'boolean', null, array('editable' => false), ), array( <<<'EOT' yes EOT , 'boolean', true, array('editable' => true), ), array( <<<'EOT' no EOT , 'boolean', false, array('editable' => true), ), array( <<<'EOT' no EOT , 'boolean', null, array('editable' => true), ), array( ' Delete ', 'trans', 'action_delete', array('catalogue' => 'SonataAdminBundle'), ), array( ' ', 'trans', null, array('catalogue' => 'SonataAdminBundle'), ), array( ' Delete ', 'trans', 'action_delete', array('format' => '%s', 'catalogue' => 'SonataAdminBundle'), ), array( ' action.action_delete ', 'trans', 'action_delete', array('format' => 'action.%s'), ), array( ' action.action_delete ', 'trans', 'action_delete', array('format' => 'action.%s', 'catalogue' => 'SonataAdminBundle'), ), array( ' Status1 ', 'choice', 'Status1', array(), ), array( ' Status1 ', 'choice', array('Status1'), array('choices' => array(), 'multiple' => true), ), array( ' Alias1 ', 'choice', 'Status1', array('choices' => array('Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3')), ), array( ' ', 'choice', null, array('choices' => array('Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3')), ), array( ' NoValidKeyInChoices ', 'choice', 'NoValidKeyInChoices', array('choices' => array('Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3')), ), array( ' Delete ', 'choice', 'Foo', array('catalogue' => 'SonataAdminBundle', 'choices' => array( 'Foo' => 'action_delete', 'Status2' => 'Alias2', 'Status3' => 'Alias3', )), ), array( ' Alias1, Alias3 ', 'choice', array('Status1', 'Status3'), array('choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( ' Alias1 | Alias3 ', 'choice', array('Status1', 'Status3'), array('choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true, 'delimiter' => ' | '), ), array( ' ', 'choice', null, array('choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( ' NoValidKeyInChoices ', 'choice', array('NoValidKeyInChoices'), array('choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( ' NoValidKeyInChoices, Alias2 ', 'choice', array('NoValidKeyInChoices', 'Status2'), array('choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( ' Delete, Alias3 ', 'choice', array('Foo', 'Status3'), array('catalogue' => 'SonataAdminBundle', 'choices' => array( 'Foo' => 'action_delete', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( ' <b>Alias1</b>, <b>Alias3</b> ', 'choice', array('Status1', 'Status3'), array('choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( <<<'EOT' Status1 EOT , 'choice', 'Status1', array('editable' => true), ), array( <<<'EOT' Alias1 EOT , 'choice', 'Status1', array( 'editable' => true, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), ), ), array( <<<'EOT' EOT , 'choice', null, array( 'editable' => true, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), ), ), array( <<<'EOT' NoValidKeyInChoices EOT , 'choice', 'NoValidKeyInChoices', array( 'editable' => true, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), ), ), array( <<<'EOT' Delete EOT , 'choice', 'Foo', array( 'editable' => true, 'catalogue' => 'SonataAdminBundle', 'choices' => array( 'Foo' => 'action_delete', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), ), ), array( '   ', 'url', null, array(), ), array( '   ', 'url', null, array('url' => 'http://example.com'), ), array( '   ', 'url', null, array('route' => array('name' => 'sonata_admin_foo')), ), array( ' http://example.com ', 'url', 'http://example.com', array(), ), array( ' https://example.com ', 'url', 'https://example.com', array(), ), array( ' example.com ', 'url', 'http://example.com', array('hide_protocol' => true), ), array( ' example.com ', 'url', 'https://example.com', array('hide_protocol' => true), ), array( ' http://example.com ', 'url', 'http://example.com', array('hide_protocol' => false), ), array( ' https://example.com ', 'url', 'https://example.com', array('hide_protocol' => false), ), array( ' Foo ', 'url', 'Foo', array('url' => 'http://example.com'), ), array( ' <b>Foo</b> ', 'url', 'Foo', array('url' => 'http://example.com'), ), array( ' Foo ', 'url', 'Foo', array('route' => array('name' => 'sonata_admin_foo')), ), array( ' Foo ', 'url', 'Foo', array('route' => array('name' => 'sonata_admin_foo', 'absolute' => true)), ), array( ' foo/bar?a=b&c=123456789 ', 'url', 'http://foo/bar?a=b&c=123456789', array('route' => array('name' => 'sonata_admin_foo'), 'hide_protocol' => true, ), ), array( ' foo/bar?a=b&c=123456789 ', 'url', 'http://foo/bar?a=b&c=123456789', array( 'route' => array('name' => 'sonata_admin_foo', 'absolute' => true), 'hide_protocol' => true, ), ), array( ' Foo ', 'url', 'Foo', array( 'route' => array('name' => 'sonata_admin_foo_param', 'parameters' => array('param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'), ), ), ), array( ' Foo ', 'url', 'Foo', array( 'route' => array('name' => 'sonata_admin_foo_param', 'absolute' => true, 'parameters' => array('param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'), ), ), ), array( ' Foo ', 'url', 'Foo', array( 'route' => array('name' => 'sonata_admin_foo_object', 'parameters' => array('param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'), 'identifier_parameter_name' => 'barId', ), ), ), array( ' Foo ', 'url', 'Foo', array( 'route' => array('name' => 'sonata_admin_foo_object', 'absolute' => true, 'parameters' => array('param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'), 'identifier_parameter_name' => 'barId', ), ), ), array( '

Creating a Template for the Field and form

', 'html', '

Creating a Template for the Field and form

', array(), ), array( ' Creating a Template for the Field and form ', 'html', '

Creating a Template for the Field and form

', array('strip' => true), ), array( ' Creating a Template for the Fi... ', 'html', '

Creating a Template for the Field and form

', array('truncate' => true), ), array( ' Creating a... ', 'html', '

Creating a Template for the Field and form

', array('truncate' => array('length' => 10)), ), array( ' Creating a Template for the Field... ', 'html', '

Creating a Template for the Field and form

', array('truncate' => array('preserve' => true)), ), array( ' Creating a Template for the Fi etc. ', 'html', '

Creating a Template for the Field and form

', array('truncate' => array('separator' => ' etc.')), ), array( ' Creating a Template for[...] ', 'html', '

Creating a Template for the Field and form

', array( 'truncate' => array( 'length' => 20, 'preserve' => true, 'separator' => '[...]', ), ), ), ); } /** * @group legacy */ public function testRenderListElementNonExistentTemplate() { $this->admin->expects($this->once()) ->method('getTemplate') ->with($this->equalTo('base_list_field')) ->will($this->returnValue('SonataAdminBundle:CRUD:base_list_field.html.twig')); $this->fieldDescription->expects($this->once()) ->method('getValue') ->will($this->returnValue('Foo')); $this->fieldDescription->expects($this->once()) ->method('getFieldName') ->will($this->returnValue('Foo_name')); $this->fieldDescription->expects($this->exactly(2)) ->method('getType') ->will($this->returnValue('nonexistent')); $this->fieldDescription->expects($this->once()) ->method('getTemplate') ->will($this->returnValue('SonataAdminBundle:CRUD:list_nonexistent_template.html.twig')); $this->logger->expects($this->once()) ->method('warning') ->with(($this->stringStartsWith($this->removeExtraWhitespace( 'An error occured trying to load the template "SonataAdminBundle:CRUD:list_nonexistent_template.html.twig" for the field "Foo_name", the default template "SonataAdminBundle:CRUD:base_list_field.html.twig" was used instead.' )))); $this->twigExtension->renderListElement($this->environment, $this->object, $this->fieldDescription); } /** * @expectedException Twig_Error_Loader * @expectedExceptionMessage Unable to find template "base_list_nonexistent_field.html.twig" * @group legacy */ public function testRenderListElementErrorLoadingTemplate() { $this->admin->expects($this->once()) ->method('getTemplate') ->with($this->equalTo('base_list_field')) ->will($this->returnValue('SonataAdminBundle:CRUD:base_list_nonexistent_field.html.twig')); $this->fieldDescription->expects($this->once()) ->method('getTemplate') ->will($this->returnValue('SonataAdminBundle:CRUD:list_nonexistent_template.html.twig')); $this->twigExtension->renderListElement($this->environment, $this->object, $this->fieldDescription); } /** * @dataProvider getRenderViewElementTests */ public function testRenderViewElement($expected, $type, $value, array $options) { $this->admin->expects($this->any()) ->method('getTemplate') ->will($this->returnValue('SonataAdminBundle:CRUD:base_show_field.html.twig')); $this->fieldDescription->expects($this->any()) ->method('getValue') ->will($this->returnCallback(function () use ($value) { if ($value instanceof NoValueException) { throw $value; } return $value; })); $this->fieldDescription->expects($this->any()) ->method('getType') ->will($this->returnValue($type)); $this->fieldDescription->expects($this->any()) ->method('getOptions') ->will($this->returnValue($options)); $this->fieldDescription->expects($this->any()) ->method('getTemplate') ->will($this->returnCallback(function () use ($type) { switch ($type) { case 'boolean': return 'SonataAdminBundle:CRUD:show_boolean.html.twig'; case 'datetime': return 'SonataAdminBundle:CRUD:show_datetime.html.twig'; case 'date': return 'SonataAdminBundle:CRUD:show_date.html.twig'; case 'time': return 'SonataAdminBundle:CRUD:show_time.html.twig'; case 'currency': return 'SonataAdminBundle:CRUD:show_currency.html.twig'; case 'percent': return 'SonataAdminBundle:CRUD:show_percent.html.twig'; case 'email': return 'SonataAdminBundle:CRUD:show_email.html.twig'; case 'choice': return 'SonataAdminBundle:CRUD:show_choice.html.twig'; case 'array': return 'SonataAdminBundle:CRUD:show_array.html.twig'; case 'trans': return 'SonataAdminBundle:CRUD:show_trans.html.twig'; case 'url': return 'SonataAdminBundle:CRUD:show_url.html.twig'; case 'html': return 'SonataAdminBundle:CRUD:show_html.html.twig'; default: return false; } })); $this->assertSame($expected, trim(preg_replace( '/\s+/', ' ', $this->twigExtension->renderViewElement( $this->environment, $this->fieldDescription, $this->object ) ))); } public function getRenderViewElementTests() { return array( array('Data Example', 'string', 'Example', array('safe' => false)), array('Data Example', 'text', 'Example', array('safe' => false)), array('Data Example', 'textarea', 'Example', array('safe' => false)), array( 'Data December 24, 2013 10:11', 'datetime', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array(), ), array( 'Data 24.12.2013 10:11:12', 'datetime', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array('format' => 'd.m.Y H:i:s'), ), array( 'Data December 24, 2013', 'date', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array(), ), array( 'Data 24.12.2013', 'date', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array('format' => 'd.m.Y'), ), array( 'Data 10:11:12', 'time', new \DateTime('2013-12-24 10:11:12', new \DateTimeZone('Europe/London')), array(), ), array('Data 10.746135', 'number', 10.746135, array('safe' => false)), array('Data 5678', 'integer', 5678, array('safe' => false)), array('Data 1074.6135 % ', 'percent', 10.746135, array()), array('Data EUR 10.746135 ', 'currency', 10.746135, array('currency' => 'EUR')), array('Data GBP 51.23456 ', 'currency', 51.23456, array('currency' => 'GBP')), array( 'Data [1 => First]
[2 => Second] ', 'array', array(1 => 'First', 2 => 'Second'), array('safe' => false), ), array( 'Data [1 => First] [2 => Second] ', 'array', array(1 => 'First', 2 => 'Second'), array('safe' => false, 'inline' => true), ), array( 'Data yes', 'boolean', true, array(), ), array('Data no', 'boolean', false, array()), array( 'Data Delete ', 'trans', 'action_delete', array('safe' => false, 'catalogue' => 'SonataAdminBundle'), ), array('Data Status1', 'choice', 'Status1', array('safe' => false)), array( 'Data Alias1', 'choice', 'Status1', array('safe' => false, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', )), ), array( 'Data NoValidKeyInChoices', 'choice', 'NoValidKeyInChoices', array('safe' => false, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', )), ), array( 'Data Delete', 'choice', 'Foo', array('safe' => false, 'catalogue' => 'SonataAdminBundle', 'choices' => array( 'Foo' => 'action_delete', 'Status2' => 'Alias2', 'Status3' => 'Alias3', )), ), array( 'Data NoValidKeyInChoices', 'choice', array('NoValidKeyInChoices'), array('safe' => false, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( 'Data NoValidKeyInChoices, Alias2', 'choice', array('NoValidKeyInChoices', 'Status2'), array('safe' => false, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( 'Data Alias1, Alias3', 'choice', array('Status1', 'Status3'), array('safe' => false, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( 'Data Alias1 | Alias3', 'choice', array('Status1', 'Status3'), array('safe' => false, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true, 'delimiter' => ' | '), ), array( 'Data Delete, Alias3', 'choice', array('Foo', 'Status3'), array('safe' => false, 'catalogue' => 'SonataAdminBundle', 'choices' => array( 'Foo' => 'action_delete', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( 'Data Alias1, Alias3', 'choice', array('Status1', 'Status3'), array('safe' => true, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( 'Data <b>Alias1</b>, <b>Alias3</b>', 'choice', array('Status1', 'Status3'), array('safe' => false, 'choices' => array( 'Status1' => 'Alias1', 'Status2' => 'Alias2', 'Status3' => 'Alias3', ), 'multiple' => true), ), array( 'Data http://example.com', 'url', 'http://example.com', array('safe' => false), ), array( 'Data https://example.com', 'url', 'https://example.com', array('safe' => false), ), array( 'Data example.com', 'url', 'http://example.com', array('safe' => false, 'hide_protocol' => true), ), array( 'Data example.com', 'url', 'https://example.com', array('safe' => false, 'hide_protocol' => true), ), array( 'Data http://example.com', 'url', 'http://example.com', array('safe' => false, 'hide_protocol' => false), ), array( 'Data https://example.com', 'url', 'https://example.com', array('safe' => false, 'hide_protocol' => false, ), ), array( 'Data Foo', 'url', 'Foo', array('safe' => false, 'url' => 'http://example.com'), ), array( 'Data <b>Foo</b>', 'url', 'Foo', array('safe' => false, 'url' => 'http://example.com'), ), array( 'Data Foo', 'url', 'Foo', array('safe' => true, 'url' => 'http://example.com'), ), array( 'Data Foo', 'url', 'Foo', array('safe' => false, 'route' => array('name' => 'sonata_admin_foo')), ), array( 'Data Foo', 'url', 'Foo', array('safe' => false, 'route' => array( 'name' => 'sonata_admin_foo', 'absolute' => true, )), ), array( 'Data foo/bar?a=b&c=123456789', 'url', 'http://foo/bar?a=b&c=123456789', array( 'safe' => false, 'route' => array('name' => 'sonata_admin_foo'), 'hide_protocol' => true, ), ), array( 'Data foo/bar?a=b&c=123456789', 'url', 'http://foo/bar?a=b&c=123456789', array('safe' => false, 'route' => array( 'name' => 'sonata_admin_foo', 'absolute' => true, ), 'hide_protocol' => true), ), array( 'Data Foo', 'url', 'Foo', array('safe' => false, 'route' => array( 'name' => 'sonata_admin_foo_param', 'parameters' => array('param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl'), )), ), array( 'Data Foo', 'url', 'Foo', array('safe' => false, 'route' => array( 'name' => 'sonata_admin_foo_param', 'absolute' => true, 'parameters' => array( 'param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl', ), )), ), array( 'Data Foo', 'url', 'Foo', array('safe' => false, 'route' => array( 'name' => 'sonata_admin_foo_object', 'parameters' => array( 'param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl', ), 'identifier_parameter_name' => 'barId', )), ), array( 'Data Foo', 'url', 'Foo', array('safe' => false, 'route' => array( 'name' => 'sonata_admin_foo_object', 'absolute' => true, 'parameters' => array( 'param1' => 'abcd', 'param2' => 'efgh', 'param3' => 'ijkl', ), 'identifier_parameter_name' => 'barId', )), ), array( 'Data  ', 'email', null, array(), ), array( 'Data admin@admin.com', 'email', 'admin@admin.com', array(), ), array( 'Data admin@admin.com', 'email', 'admin@admin.com', array('subject' => 'Main Theme', 'body' => 'Message Body'), ), array( 'Data admin@admin.com', 'email', 'admin@admin.com', array('subject' => 'Main Theme'), ), array( 'Data admin@admin.com', 'email', 'admin@admin.com', array('body' => 'Message Body'), ), array( 'Data admin@admin.com', 'email', 'admin@admin.com', array('as_string' => true, 'subject' => 'Main Theme', 'body' => 'Message Body'), ), array( 'Data admin@admin.com', 'email', 'admin@admin.com', array('as_string' => true, 'subject' => 'Main Theme'), ), array( 'Data admin@admin.com', 'email', 'admin@admin.com', array('as_string' => true, 'body' => 'Message Body'), ), array( 'Data admin@admin.com', 'email', 'admin@admin.com', array('as_string' => false), ), array( 'Data admin@admin.com', 'email', 'admin@admin.com', array('as_string' => true), ), array( 'Data

Creating a Template for the Field and form

', 'html', '

Creating a Template for the Field and form

', array(), ), array( 'Data Creating a Template for the Field and form ', 'html', '

Creating a Template for the Field and form

', array('strip' => true), ), array( 'Data Creating a Template for the Fi... ', 'html', '

Creating a Template for the Field and form

', array('truncate' => true), ), array( 'Data Creating a... ', 'html', '

Creating a Template for the Field and form

', array('truncate' => array('length' => 10)), ), array( 'Data Creating a Template for the Field... ', 'html', '

Creating a Template for the Field and form

', array('truncate' => array('preserve' => true)), ), array( 'Data Creating a Template for the Fi etc. ', 'html', '

Creating a Template for the Field and form

', array('truncate' => array('separator' => ' etc.')), ), array( 'Data Creating a Template for[...] ', 'html', '

Creating a Template for the Field and form

', array( 'truncate' => array( 'length' => 20, 'preserve' => true, 'separator' => '[...]', ), ), ), // NoValueException array('Data ', 'string', new NoValueException(), array('safe' => false)), array('Data ', 'text', new NoValueException(), array('safe' => false)), array('Data ', 'textarea', new NoValueException(), array('safe' => false)), array('Data  ', 'datetime', new NoValueException(), array()), array( 'Data  ', 'datetime', new NoValueException(), array('format' => 'd.m.Y H:i:s'), ), array('Data  ', 'date', new NoValueException(), array()), array('Data  ', 'date', new NoValueException(), array('format' => 'd.m.Y')), array('Data  ', 'time', new NoValueException(), array()), array('Data ', 'number', new NoValueException(), array('safe' => false)), array('Data ', 'integer', new NoValueException(), array('safe' => false)), array('Data 0 % ', 'percent', new NoValueException(), array()), array('Data ', 'currency', new NoValueException(), array('currency' => 'EUR')), array('Data ', 'currency', new NoValueException(), array('currency' => 'GBP')), array('Data ', 'array', new NoValueException(), array('safe' => false)), array( 'Data no', 'boolean', new NoValueException(), array(), ), array( 'Data ', 'trans', new NoValueException(), array('safe' => false, 'catalogue' => 'SonataAdminBundle'), ), array( 'Data ', 'choice', new NoValueException(), array('safe' => false, 'choices' => array()), ), array( 'Data ', 'choice', new NoValueException(), array('safe' => false, 'choices' => array(), 'multiple' => true), ), array('Data  ', 'url', new NoValueException(), array()), array( 'Data  ', 'url', new NoValueException(), array('url' => 'http://example.com'), ), array( 'Data  ', 'url', new NoValueException(), array('route' => array('name' => 'sonata_admin_foo')), ), ); } public function testGetValueFromFieldDescription() { $object = new \stdClass(); $fieldDescription = $this->getMock('Sonata\AdminBundle\Admin\FieldDescriptionInterface'); $fieldDescription->expects($this->any()) ->method('getValue') ->will($this->returnValue('test123')); $this->assertSame('test123', $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription)); } public function testGetValueFromFieldDescriptionWithRemoveLoopException() { $object = $this->getMock('\ArrayAccess'); $fieldDescription = $this->getMock('Sonata\AdminBundle\Admin\FieldDescriptionInterface'); try { $this->assertSame( 'anything', $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription, array('loop' => true)) ); } catch (\RuntimeException $e) { $this->assertContains('remove the loop requirement', $e->getMessage()); return; } $this->fail('Failed asserting that exception of type "\RuntimeException" is thrown.'); } public function testGetValueFromFieldDescriptionWithNoValueException() { $object = new \stdClass(); $fieldDescription = $this->getMock('Sonata\AdminBundle\Admin\FieldDescriptionInterface'); $fieldDescription->expects($this->any()) ->method('getValue') ->will($this->returnCallback(function () { throw new NoValueException(); })); $fieldDescription->expects($this->any()) ->method('getAssociationAdmin') ->will($this->returnValue(null)); $this->assertSame(null, $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription)); } public function testGetValueFromFieldDescriptionWithNoValueExceptionNewAdminInstance() { $object = new \stdClass(); $fieldDescription = $this->getMock('Sonata\AdminBundle\Admin\FieldDescriptionInterface'); $fieldDescription->expects($this->any()) ->method('getValue') ->will($this->returnCallback(function () { throw new NoValueException(); })); $fieldDescription->expects($this->any()) ->method('getAssociationAdmin') ->will($this->returnValue($this->admin)); $this->admin->expects($this->once()) ->method('getNewInstance') ->will($this->returnValue('foo')); $this->assertSame('foo', $this->twigExtension->getValueFromFieldDescription($object, $fieldDescription)); } public function testOutput() { $this->fieldDescription->expects($this->any()) ->method('getTemplate') ->will($this->returnValue('SonataAdminBundle:CRUD:base_list_field.html.twig')); $this->fieldDescription->expects($this->any()) ->method('getFieldName') ->will($this->returnValue('fd_name')); $this->environment->disableDebug(); $parameters = array( 'admin' => $this->admin, 'value' => 'foo', 'field_description' => $this->fieldDescription, 'object' => $this->object, ); $template = $this->environment->loadTemplate('SonataAdminBundle:CRUD:base_list_field.html.twig'); $this->assertSame( ' foo ', $this->removeExtraWhitespace($this->twigExtension->output( $this->fieldDescription, $template, $parameters, $this->environment )) ); $this->environment->enableDebug(); $this->assertSame( $this->removeExtraWhitespace(<<<'EOT' foo EOT ), $this->removeExtraWhitespace( $this->twigExtension->output($this->fieldDescription, $template, $parameters, $this->environment) ) ); } public function testRenderRelationElementNoObject() { $this->assertSame('foo', $this->twigExtension->renderRelationElement('foo', $this->fieldDescription)); } public function testRenderRelationElementToString() { $this->fieldDescription->expects($this->exactly(2)) ->method('getOption') ->will($this->returnCallback(function ($value, $default = null) { if ($value == 'associated_property') { return $default; } })); $element = new FooToString(); $this->assertSame('salut', $this->twigExtension->renderRelationElement($element, $this->fieldDescription)); } /** * @group legacy */ public function testDeprecatedRelationElementToString() { $this->fieldDescription->expects($this->exactly(2)) ->method('getOption') ->will($this->returnCallback(function ($value, $default = null) { if ($value == 'associated_tostring') { return '__toString'; } })); $element = new FooToString(); $this->assertSame( 'salut', $this->twigExtension->renderRelationElement($element, $this->fieldDescription) ); } /** * @group legacy */ public function testRenderRelationElementCustomToString() { $this->fieldDescription->expects($this->exactly(2)) ->method('getOption') ->will($this->returnCallback(function ($value, $default = null) { if ($value == 'associated_property') { return $default; } if ($value == 'associated_tostring') { return 'customToString'; } })); $element = $this->getMock('stdClass', array('customToString')); $element->expects($this->any()) ->method('customToString') ->will($this->returnValue('fooBar')); $this->assertSame('fooBar', $this->twigExtension->renderRelationElement($element, $this->fieldDescription)); } /** * @group legacy */ public function testRenderRelationElementMethodNotExist() { $this->fieldDescription->expects($this->exactly(2)) ->method('getOption') ->will($this->returnCallback(function ($value, $default = null) { if ($value == 'associated_tostring') { return 'nonExistedMethod'; } })); $element = new \stdClass(); try { $this->twigExtension->renderRelationElement($element, $this->fieldDescription); } catch (\RuntimeException $e) { $this->assertContains( 'You must define an `associated_property` option or create a `stdClass::__toString', $e->getMessage() ); return; } $this->fail('Failed asserting that exception of type "\RuntimeException" is thrown.'); } public function testRenderRelationElementWithPropertyPath() { $this->fieldDescription->expects($this->exactly(1)) ->method('getOption') ->will($this->returnCallback(function ($value, $default = null) { if ($value == 'associated_property') { return 'foo'; } })); $element = new \stdClass(); $element->foo = 'bar'; $this->assertSame('bar', $this->twigExtension->renderRelationElement($element, $this->fieldDescription)); } public function testRenderRelationElementWithClosure() { $this->fieldDescription->expects($this->exactly(1)) ->method('getOption') ->will($this->returnCallback(function ($value, $default = null) { if ($value == 'associated_property') { return function ($element) { return 'closure '.$element->foo; }; } })); $element = new \stdClass(); $element->foo = 'bar'; $this->assertSame( 'closure bar', $this->twigExtension->renderRelationElement($element, $this->fieldDescription) ); } public function testGetUrlsafeIdentifier() { $entity = new \stdClass(); // set admin to pool $this->pool->setAdminServiceIds(array('sonata_admin_foo_service')); $this->pool->setAdminClasses(array('stdClass' => array('sonata_admin_foo_service'))); $this->admin->expects($this->once()) ->method('getUrlsafeIdentifier') ->with($this->equalTo($entity)) ->will($this->returnValue(1234567)); $this->assertSame(1234567, $this->twigExtension->getUrlsafeIdentifier($entity)); } public function testGetUrlsafeIdentifier_GivenAdmin_Foo() { $entity = new \stdClass(); // set admin to pool $this->pool->setAdminServiceIds(array( 'sonata_admin_foo_service', 'sonata_admin_bar_service', )); $this->pool->setAdminClasses(array('stdClass' => array( 'sonata_admin_foo_service', 'sonata_admin_bar_service', ))); $this->admin->expects($this->once()) ->method('getUrlsafeIdentifier') ->with($this->equalTo($entity)) ->will($this->returnValue(1234567)); $this->adminBar->expects($this->never()) ->method('getUrlsafeIdentifier'); $this->assertSame(1234567, $this->twigExtension->getUrlsafeIdentifier($entity, $this->admin)); } public function testGetUrlsafeIdentifier_GivenAdmin_Bar() { $entity = new \stdClass(); // set admin to pool $this->pool->setAdminServiceIds(array('sonata_admin_foo_service', 'sonata_admin_bar_service')); $this->pool->setAdminClasses(array('stdClass' => array( 'sonata_admin_foo_service', 'sonata_admin_bar_service', ))); $this->admin->expects($this->never()) ->method('getUrlsafeIdentifier'); $this->adminBar->expects($this->once()) ->method('getUrlsafeIdentifier') ->with($this->equalTo($entity)) ->will($this->returnValue(1234567)); $this->assertSame(1234567, $this->twigExtension->getUrlsafeIdentifier($entity, $this->adminBar)); } /** * This method generates url part for Twig layout. Allows to keep BC for PHP 5.3. * * Remove this method for next major release only if PHP 5.3 support will be dropped. * * @param array $url * * @return string */ private function buildTwigLikeUrl($url) { if (defined('PHP_QUERY_RFC3986')) { // add htmlspecialchars because twig add it auto return htmlspecialchars(http_build_query($url, '', '&', PHP_QUERY_RFC3986)); } return htmlspecialchars(http_build_query($url, '', '&')); } private function removeExtraWhitespace($string) { return trim(preg_replace( '/\s+/', ' ', $string )); } }