ThemeRenderAndAutoescapeTest.php 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. <?php
  2. /**
  3. * @file
  4. * Contains \Drupal\KernelTests\Core\Theme\ThemeRenderAndAutoescapeTest.
  5. */
  6. namespace Drupal\KernelTests\Core\Theme;
  7. use Drupal\Component\Utility\Html;
  8. use Drupal\Core\GeneratedLink;
  9. use Drupal\Core\Link;
  10. use Drupal\Core\Render\RenderContext;
  11. use Drupal\Core\Render\Markup;
  12. use Drupal\Core\Url;
  13. use Drupal\KernelTests\KernelTestBase;
  14. /**
  15. * Tests the theme_render_and_autoescape() function.
  16. *
  17. * @group Theme
  18. */
  19. class ThemeRenderAndAutoescapeTest extends KernelTestBase {
  20. /**
  21. * {@inheritdoc}
  22. */
  23. public static $modules = ['system'];
  24. /**
  25. * {@inheritdoc}
  26. */
  27. protected function setUp() {
  28. parent::setUp();
  29. \Drupal::service('router.builder')->rebuild();
  30. }
  31. /**
  32. * @dataProvider providerTestThemeRenderAndAutoescape
  33. */
  34. public function testThemeRenderAndAutoescape($arg, $expected) {
  35. if (is_array($arg) && isset($arg['#type']) && $arg['#type'] === 'link') {
  36. $arg = Link::createFromRoute($arg['#title'], $arg['#url']);
  37. }
  38. $context = new RenderContext();
  39. // Use a closure here since we need to render with a render context.
  40. $theme_render_and_autoescape = function () use ($arg) {
  41. return theme_render_and_autoescape($arg);
  42. };
  43. /** @var \Drupal\Core\Render\RendererInterface $renderer */
  44. $renderer = \Drupal::service('renderer');
  45. $output = $renderer->executeInRenderContext($context, $theme_render_and_autoescape);
  46. $this->assertEquals($expected, $output);
  47. $this->assertInternalType('string', $output);
  48. }
  49. /**
  50. * Provide test examples.
  51. */
  52. public function providerTestThemeRenderAndAutoescape() {
  53. return [
  54. 'empty string unchanged' => ['', ''],
  55. 'simple string unchanged' => ['ab', 'ab'],
  56. 'int (scalar) cast to string' => [111, '111'],
  57. 'float (scalar) cast to string' => [2.10, '2.10'],
  58. '> is escaped' => ['>', '&gt;'],
  59. 'Markup EM tag is unchanged' => [Markup::create('<em>hi</em>'), '<em>hi</em>'],
  60. 'Markup SCRIPT tag is unchanged' => [Markup::create('<script>alert("hi");</script>'), '<script>alert("hi");</script>'],
  61. 'EM tag in string is escaped' => ['<em>hi</em>', Html::escape('<em>hi</em>')],
  62. 'type link render array is rendered' => [['#type' => 'link', '#title' => 'Text', '#url' => '<none>'], '<a href="">Text</a>'],
  63. 'type markup with EM tags is rendered' => [['#markup' => '<em>hi</em>'], '<em>hi</em>'],
  64. 'SCRIPT tag in string is escaped' => [
  65. '<script>alert(123)</script>',
  66. Html::escape('<script>alert(123)</script>')
  67. ],
  68. 'type plain_text render array EM tag is escaped' => [['#plain_text' => '<em>hi</em>'], Html::escape('<em>hi</em>')],
  69. 'type hidden render array is rendered' => [['#type' => 'hidden', '#name' => 'foo', '#value' => 'bar'], "<input type=\"hidden\" name=\"foo\" value=\"bar\" />\n"],
  70. ];
  71. }
  72. /**
  73. * Ensures invalid content is handled correctly.
  74. */
  75. public function testThemeEscapeAndRenderNotPrintable() {
  76. $this->setExpectedException(\Exception::class);
  77. theme_render_and_autoescape(new NonPrintable());
  78. }
  79. /**
  80. * Ensure cache metadata is bubbled when using theme_render_and_autoescape().
  81. */
  82. public function testBubblingMetadata() {
  83. $link = new GeneratedLink();
  84. $link->setGeneratedLink('<a href="http://example.com"></a>');
  85. $link->addCacheTags(['foo']);
  86. $link->addAttachments(['library' => ['system/base']]);
  87. $context = new RenderContext();
  88. // Use a closure here since we need to render with a render context.
  89. $theme_render_and_autoescape = function () use ($link) {
  90. return theme_render_and_autoescape($link);
  91. };
  92. /** @var \Drupal\Core\Render\RendererInterface $renderer */
  93. $renderer = \Drupal::service('renderer');
  94. $output = $renderer->executeInRenderContext($context, $theme_render_and_autoescape);
  95. $this->assertEquals('<a href="http://example.com"></a>', $output);
  96. /** @var \Drupal\Core\Render\BubbleableMetadata $metadata */
  97. $metadata = $context->pop();
  98. $this->assertEquals(['foo'], $metadata->getCacheTags());
  99. $this->assertEquals(['library' => ['system/base']], $metadata->getAttachments());
  100. }
  101. /**
  102. * Ensure cache metadata is bubbled when using theme_render_and_autoescape().
  103. */
  104. public function testBubblingMetadataWithRenderable() {
  105. $link = new Link('', Url::fromRoute('<current>'));
  106. $context = new RenderContext();
  107. // Use a closure here since we need to render with a render context.
  108. $theme_render_and_autoescape = function () use ($link) {
  109. return theme_render_and_autoescape($link);
  110. };
  111. /** @var \Drupal\Core\Render\RendererInterface $renderer */
  112. $renderer = \Drupal::service('renderer');
  113. $output = $renderer->executeInRenderContext($context, $theme_render_and_autoescape);
  114. $this->assertEquals('<a href="/' . urlencode('<none>') . '"></a>', $output);
  115. /** @var \Drupal\Core\Render\BubbleableMetadata $metadata */
  116. $metadata = $context->pop();
  117. $this->assertEquals(['route'], $metadata->getCacheContexts());
  118. }
  119. }
  120. class NonPrintable {}