class TwigExtensionTest

Same name in this branch
  1. 8.9.x core/modules/system/tests/src/Functional/Theme/TwigExtensionTest.php \Drupal\Tests\system\Functional\Theme\TwigExtensionTest
Same name in other branches
  1. 9 core/modules/system/tests/src/Functional/Theme/TwigExtensionTest.php \Drupal\Tests\system\Functional\Theme\TwigExtensionTest
  2. 9 core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php \Drupal\Tests\Core\Template\TwigExtensionTest
  3. 10 core/modules/system/tests/src/Functional/Theme/TwigExtensionTest.php \Drupal\Tests\system\Functional\Theme\TwigExtensionTest
  4. 10 core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php \Drupal\Tests\Core\Template\TwigExtensionTest
  5. 11.x core/modules/system/tests/src/Functional/Theme/TwigExtensionTest.php \Drupal\Tests\system\Functional\Theme\TwigExtensionTest
  6. 11.x core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php \Drupal\Tests\Core\Template\TwigExtensionTest

Tests the twig extension.

@group Template

@coversDefaultClass \Drupal\Core\Template\TwigExtension

Hierarchy

Expanded class hierarchy of TwigExtensionTest

File

core/tests/Drupal/Tests/Core/Template/TwigExtensionTest.php, line 21

Namespace

Drupal\Tests\Core\Template
View source
class TwigExtensionTest extends UnitTestCase {
    
    /**
     * The renderer.
     *
     * @var \Drupal\Core\Render\RendererInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $renderer;
    
    /**
     * The url generator.
     *
     * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $urlGenerator;
    
    /**
     * The theme manager.
     *
     * @var \Drupal\Core\Theme\ThemeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $themeManager;
    
    /**
     * The date formatter.
     *
     * @var \Drupal\Core\Datetime\DateFormatterInterface|\PHPUnit\Framework\MockObject\MockObject
     */
    protected $dateFormatter;
    
    /**
     * The system under test.
     *
     * @var \Drupal\Core\Template\TwigExtension
     */
    protected $systemUnderTest;
    
    /**
     * {@inheritdoc}
     */
    public function setUp() {
        parent::setUp();
        $this->renderer = $this->createMock('\\Drupal\\Core\\Render\\RendererInterface');
        $this->urlGenerator = $this->createMock('\\Drupal\\Core\\Routing\\UrlGeneratorInterface');
        $this->themeManager = $this->createMock('\\Drupal\\Core\\Theme\\ThemeManagerInterface');
        $this->dateFormatter = $this->createMock('\\Drupal\\Core\\Datetime\\DateFormatterInterface');
        $this->systemUnderTest = new TwigExtension($this->renderer, $this->urlGenerator, $this->themeManager, $this->dateFormatter);
    }
    
    /**
     * Tests the escaping
     *
     * @dataProvider providerTestEscaping
     */
    public function testEscaping($template, $expected) {
        $loader = new \Twig_Loader_Filesystem();
        $twig = new \Twig_Environment($loader, [
            'debug' => TRUE,
            'cache' => FALSE,
            'autoescape' => 'html',
            'optimizations' => 0,
        ]);
        $twig->addExtension($this->systemUnderTest);
        $name = '__string_template_test__';
        $nodes = $twig->parse($twig->tokenize(new \Twig_Source($template, $name)));
        $this->assertSame($expected, $nodes->getNode('body')
            ->getNode(0)
            ->getNode('expr') instanceof \Twig_Node_Expression_Filter);
    }
    
    /**
     * Provides tests data for testEscaping
     *
     * @return array
     *   An array of test data each containing of a twig template string and
     *   a boolean expecting whether the path will be safe.
     */
    public function providerTestEscaping() {
        return [
            [
                '{{ path("foo") }}',
                FALSE,
            ],
            [
                '{{ path("foo", {}) }}',
                FALSE,
            ],
            [
                '{{ path("foo", { foo: "foo" }) }}',
                FALSE,
            ],
            [
                '{{ path("foo", foo) }}',
                TRUE,
            ],
            [
                '{{ path("foo", { foo: foo }) }}',
                TRUE,
            ],
            [
                '{{ path("foo", { foo: ["foo", "bar"] }) }}',
                TRUE,
            ],
            [
                '{{ path("foo", { foo: "foo", bar: "bar" }) }}',
                TRUE,
            ],
            [
                '{{ path(name = "foo", parameters = {}) }}',
                FALSE,
            ],
            [
                '{{ path(name = "foo", parameters = { foo: "foo" }) }}',
                FALSE,
            ],
            [
                '{{ path(name = "foo", parameters = foo) }}',
                TRUE,
            ],
            [
                '{{ path(name = "foo", parameters = { foo: ["foo", "bar"] }) }}',
                TRUE,
            ],
            [
                '{{ path(name = "foo", parameters = { foo: foo }) }}',
                TRUE,
            ],
            [
                '{{ path(name = "foo", parameters = { foo: "foo", bar: "bar" }) }}',
                TRUE,
            ],
        ];
    }
    
    /**
     * Tests the active_theme function.
     */
    public function testActiveTheme() {
        $active_theme = $this->getMockBuilder('\\Drupal\\Core\\Theme\\ActiveTheme')
            ->disableOriginalConstructor()
            ->getMock();
        $active_theme->expects($this->once())
            ->method('getName')
            ->willReturn('test_theme');
        $this->themeManager
            ->expects($this->once())
            ->method('getActiveTheme')
            ->willReturn($active_theme);
        $loader = new StringLoader();
        $twig = new \Twig_Environment($loader);
        $twig->addExtension($this->systemUnderTest);
        $result = $twig->render('{{ active_theme() }}');
        $this->assertEquals('test_theme', $result);
    }
    
    /**
     * Tests the format_date filter.
     */
    public function testFormatDate() {
        $this->dateFormatter
            ->expects($this->exactly(1))
            ->method('format')
            ->will($this->returnCallback(function ($timestamp) {
            return date('Y-m-d', $timestamp);
        }));
        $loader = new StringLoader();
        $twig = new \Twig_Environment($loader);
        $twig->addExtension($this->systemUnderTest);
        $timestamp = strtotime('1978-11-19');
        $result = $twig->render('{{ time|format_date("html_date") }}', [
            'time' => $timestamp,
        ]);
        $this->assertEquals('1978-11-19', $result);
    }
    
    /**
     * Tests the active_theme_path function.
     */
    public function testActiveThemePath() {
        $active_theme = $this->getMockBuilder('\\Drupal\\Core\\Theme\\ActiveTheme')
            ->disableOriginalConstructor()
            ->getMock();
        $active_theme->expects($this->once())
            ->method('getPath')
            ->willReturn('foo/bar');
        $this->themeManager
            ->expects($this->once())
            ->method('getActiveTheme')
            ->willReturn($active_theme);
        $loader = new StringLoader();
        $twig = new \Twig_Environment($loader);
        $twig->addExtension($this->systemUnderTest);
        $result = $twig->render('{{ active_theme_path() }}');
        $this->assertEquals('foo/bar', $result);
    }
    
    /**
     * Tests the escaping of objects implementing MarkupInterface.
     *
     * @covers ::escapeFilter
     */
    public function testSafeStringEscaping() {
        $loader = new \Twig_Loader_Filesystem();
        $twig = new \Twig_Environment($loader, [
            'debug' => TRUE,
            'cache' => FALSE,
            'autoescape' => 'html',
            'optimizations' => 0,
        ]);
        // By default, TwigExtension will attempt to cast objects to strings.
        // Ensure objects that implement MarkupInterface are unchanged.
        $safe_string = $this->createMock('\\Drupal\\Component\\Render\\MarkupInterface');
        $this->assertSame($safe_string, $this->systemUnderTest
            ->escapeFilter($twig, $safe_string, 'html', 'UTF-8', TRUE));
        // Ensure objects that do not implement MarkupInterface are escaped.
        $string_object = new TwigExtensionTestString("<script>alert('here');</script>");
        $this->assertSame('&lt;script&gt;alert(&#039;here&#039;);&lt;/script&gt;', $this->systemUnderTest
            ->escapeFilter($twig, $string_object, 'html', 'UTF-8', TRUE));
    }
    
    /**
     * @covers ::safeJoin
     */
    public function testSafeJoin() {
        $this->renderer
            ->expects($this->any())
            ->method('render')
            ->with([
            '#markup' => '<strong>will be rendered</strong>',
            '#printed' => FALSE,
        ])
            ->willReturn('<strong>will be rendered</strong>');
        $twig_environment = $this->prophesize(TwigEnvironment::class)
            ->reveal();
        // Simulate t().
        $markup = $this->prophesize(TranslatableMarkup::class);
        $markup->__toString()
            ->willReturn('<em>will be markup</em>');
        $markup = $markup->reveal();
        $items = [
            '<em>will be escaped</em>',
            $markup,
            [
                '#markup' => '<strong>will be rendered</strong>',
            ],
        ];
        $result = $this->systemUnderTest
            ->safeJoin($twig_environment, $items, '<br/>');
        $this->assertEquals('&lt;em&gt;will be escaped&lt;/em&gt;<br/><em>will be markup</em><br/><strong>will be rendered</strong>', $result);
        // Ensure safe_join Twig filter supports Traversable variables.
        $items = new \ArrayObject([
            '<em>will be escaped</em>',
            $markup,
            [
                '#markup' => '<strong>will be rendered</strong>',
            ],
        ]);
        $result = $this->systemUnderTest
            ->safeJoin($twig_environment, $items, ', ');
        $this->assertEquals('&lt;em&gt;will be escaped&lt;/em&gt;, <em>will be markup</em>, <strong>will be rendered</strong>', $result);
        // Ensure safe_join Twig filter supports empty variables.
        $items = NULL;
        $result = $this->systemUnderTest
            ->safeJoin($twig_environment, $items, '<br>');
        $this->assertEmpty($result);
    }
    
    /**
     * @dataProvider providerTestRenderVar
     */
    public function testRenderVar($result, $input) {
        $this->renderer
            ->expects($this->any())
            ->method('render')
            ->with($result += [
            '#printed' => FALSE,
        ])
            ->willReturn('Rendered output');
        $this->assertEquals('Rendered output', $this->systemUnderTest
            ->renderVar($input));
    }
    public function providerTestRenderVar() {
        $data = [];
        $renderable = $this->prophesize(RenderableInterface::class);
        $render_array = [
            '#type' => 'test',
            '#var' => 'giraffe',
        ];
        $renderable->toRenderable()
            ->willReturn($render_array);
        $data['renderable'] = [
            $render_array,
            $renderable->reveal(),
        ];
        return $data;
    }
    
    /**
     * @covers ::escapeFilter
     * @covers ::bubbleArgMetadata
     */
    public function testEscapeWithGeneratedLink() {
        $loader = new \Twig_Loader_Filesystem();
        $twig = new \Twig_Environment($loader, [
            'debug' => TRUE,
            'cache' => FALSE,
            'autoescape' => 'html',
            'optimizations' => 0,
        ]);
        $twig->addExtension($this->systemUnderTest);
        $link = new GeneratedLink();
        $link->setGeneratedLink('<a href="http://example.com"></a>');
        $link->addCacheTags([
            'foo',
        ]);
        $link->addAttachments([
            'library' => [
                'system/base',
            ],
        ]);
        $this->renderer
            ->expects($this->atLeastOnce())
            ->method('render')
            ->with([
            "#cache" => [
                "contexts" => [],
                "tags" => [
                    "foo",
                ],
                "max-age" => -1,
            ],
            "#attached" => [
                'library' => [
                    'system/base',
                ],
            ],
        ]);
        $result = $this->systemUnderTest
            ->escapeFilter($twig, $link, 'html', NULL, TRUE);
        $this->assertEquals('<a href="http://example.com"></a>', $result);
    }
    
    /**
     * @covers ::renderVar
     * @covers ::bubbleArgMetadata
     */
    public function testRenderVarWithGeneratedLink() {
        $link = new GeneratedLink();
        $link->setGeneratedLink('<a href="http://example.com"></a>');
        $link->addCacheTags([
            'foo',
        ]);
        $link->addAttachments([
            'library' => [
                'system/base',
            ],
        ]);
        $this->renderer
            ->expects($this->atLeastOnce())
            ->method('render')
            ->with([
            "#cache" => [
                "contexts" => [],
                "tags" => [
                    "foo",
                ],
                "max-age" => -1,
            ],
            "#attached" => [
                'library' => [
                    'system/base',
                ],
            ],
        ]);
        $result = $this->systemUnderTest
            ->renderVar($link);
        $this->assertEquals('<a href="http://example.com"></a>', $result);
    }
    
    /**
     * Tests creating attributes within a Twig template.
     *
     * @covers ::createAttribute
     */
    public function testCreateAttribute() {
        $name = '__string_template_test_1__';
        $loader = new \Twig_Loader_Array([
            $name => "{% for iteration in iterations %}<div{{ create_attribute(iteration) }}></div>{% endfor %}",
        ]);
        $twig = new \Twig_Environment($loader);
        $twig->addExtension($this->systemUnderTest);
        $iterations = [
            [
                'class' => [
                    'kittens',
                ],
                'data-toggle' => 'modal',
                'data-lang' => 'es',
            ],
            [
                'id' => 'puppies',
                'data-value' => 'foo',
                'data-lang' => 'en',
            ],
            [],
        ];
        $result = $twig->render($name, [
            'iterations' => $iterations,
        ]);
        $expected = '<div class="kittens" data-toggle="modal" data-lang="es"></div><div id="puppies" data-value="foo" data-lang="en"></div><div></div>';
        $this->assertEquals($expected, $result);
        // Test default creation of empty attribute object and using its method.
        $name = '__string_template_test_2__';
        $loader = new \Twig_Loader_Array([
            $name => "<div{{ create_attribute().addClass('meow') }}></div>",
        ]);
        $twig->setLoader($loader);
        $result = $twig->render($name);
        $expected = '<div class="meow"></div>';
        $this->assertEquals($expected, $result);
    }
    
    /**
     * @covers ::getLink
     */
    public function testLinkWithOverriddenAttributes() {
        $url = Url::fromRoute('<front>', [], [
            'attributes' => [
                'class' => [
                    'foo',
                ],
            ],
        ]);
        $build = $this->systemUnderTest
            ->getLink('test', $url, [
            'class' => [
                'bar',
            ],
        ]);
        $this->assertEquals([
            'foo',
            'bar',
        ], $build['#url']->getOption('attributes')['class']);
    }

}

Members

Title Sort descending Deprecated Modifiers Object type Summary Overriden Title Overrides
PhpunitCompatibilityTrait::getMock Deprecated public function Returns a mock object for the specified class using the available method.
PhpunitCompatibilityTrait::setExpectedException Deprecated public function Compatibility layer for PHPUnit 6 to support PHPUnit 4 code.
TwigExtensionTest::$dateFormatter protected property The date formatter.
TwigExtensionTest::$renderer protected property The renderer.
TwigExtensionTest::$systemUnderTest protected property The system under test.
TwigExtensionTest::$themeManager protected property The theme manager.
TwigExtensionTest::$urlGenerator protected property The url generator.
TwigExtensionTest::providerTestEscaping public function Provides tests data for testEscaping
TwigExtensionTest::providerTestRenderVar public function
TwigExtensionTest::setUp public function Overrides UnitTestCase::setUp
TwigExtensionTest::testActiveTheme public function Tests the active_theme function.
TwigExtensionTest::testActiveThemePath public function Tests the active_theme_path function.
TwigExtensionTest::testCreateAttribute public function Tests creating attributes within a Twig template.
TwigExtensionTest::testEscapeWithGeneratedLink public function @covers ::escapeFilter
@covers ::bubbleArgMetadata
TwigExtensionTest::testEscaping public function Tests the escaping
TwigExtensionTest::testFormatDate public function Tests the format_date filter.
TwigExtensionTest::testLinkWithOverriddenAttributes public function @covers ::getLink
TwigExtensionTest::testRenderVar public function @dataProvider providerTestRenderVar
TwigExtensionTest::testRenderVarWithGeneratedLink public function @covers ::renderVar
@covers ::bubbleArgMetadata
TwigExtensionTest::testSafeJoin public function @covers ::safeJoin
TwigExtensionTest::testSafeStringEscaping public function Tests the escaping of objects implementing MarkupInterface.
UnitTestCase::$randomGenerator protected property The random generator.
UnitTestCase::$root protected property The app root. 1
UnitTestCase::assertArrayEquals protected function Asserts if two arrays are equal by sorting them first.
UnitTestCase::getBlockMockWithMachineName Deprecated protected function Mocks a block with a block plugin. 1
UnitTestCase::getClassResolverStub protected function Returns a stub class resolver.
UnitTestCase::getConfigFactoryStub public function Returns a stub config factory that behaves according to the passed array.
UnitTestCase::getConfigStorageStub public function Returns a stub config storage that returns the supplied configuration.
UnitTestCase::getContainerWithCacheTagsInvalidator protected function Sets up a container with a cache tags invalidator.
UnitTestCase::getRandomGenerator protected function Gets the random generator for the utility methods.
UnitTestCase::getStringTranslationStub public function Returns a stub translation manager that just returns the passed string.
UnitTestCase::randomMachineName public function Generates a unique random string containing letters and numbers.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.