Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176
This commit is contained in:
commit
9921556621
13277 changed files with 1459781 additions and 0 deletions
|
@ -0,0 +1,42 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\EnginePhpTemplateTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests theme functions with PHPTemplate.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class EnginePhpTemplateTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('theme_handler')->install(array('test_theme_phptemplate'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a theme's template is overridable based on the 'template' filename.
|
||||
*/
|
||||
function testTemplateOverride() {
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme_phptemplate')
|
||||
->save();
|
||||
$this->drupalGet('theme-test/template-test');
|
||||
$this->assertText('Success: Template overridden with PHPTemplate theme.', 'Template overridden by PHPTemplate file.');
|
||||
}
|
||||
|
||||
}
|
139
core/modules/system/src/Tests/Theme/EngineTwigTest.php
Normal file
139
core/modules/system/src/Tests/Theme/EngineTwigTest.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\EngineTwigTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests Twig-specific theme functionality.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class EngineTwigTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test', 'twig_theme_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('theme_handler')->install(array('test_theme'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the Twig engine handles PHP data correctly.
|
||||
*/
|
||||
function testTwigVariableDataTypes() {
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
$this->drupalGet('twig-theme-test/php-variables');
|
||||
foreach (_test_theme_twig_php_values() as $type => $value) {
|
||||
$this->assertRaw('<li>' . $type . ': ' . $value['expected'] . '</li>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the url and url_generate Twig functions.
|
||||
*/
|
||||
public function testTwigUrlGenerator() {
|
||||
$this->drupalGet('twig-theme-test/url-generator');
|
||||
// Find the absolute URL of the current site.
|
||||
$url_generator = $this->container->get('url_generator');
|
||||
$expected = array(
|
||||
'path (as route) not absolute: ' . $url_generator->generateFromRoute('user.register'),
|
||||
'url (as route) absolute: ' . $url_generator->generateFromRoute('user.register', array(), array('absolute' => TRUE)),
|
||||
'path (as route) not absolute with fragment: ' . $url_generator->generateFromRoute('user.register', array(), array('fragment' => 'bottom')),
|
||||
'url (as route) absolute despite option: ' . $url_generator->generateFromRoute('user.register', array(), array('absolute' => TRUE)),
|
||||
'url (as route) absolute with fragment: ' . $url_generator->generateFromRoute('user.register', array(), array('absolute' => TRUE, 'fragment' => 'bottom')),
|
||||
);
|
||||
|
||||
// Verify that url() has the ability to bubble cacheability metadata:
|
||||
// absolute URLs should bubble the 'url.site' cache context. (This only
|
||||
// needs to test that cacheability metadata is bubbled *at all*; detailed
|
||||
// tests for *which* cacheability metadata is bubbled live elsewhere.)
|
||||
$this->assertCacheContext('url.site');
|
||||
|
||||
// Make sure we got something.
|
||||
$content = $this->getRawContent();
|
||||
$this->assertFalse(empty($content), 'Page content is not empty');
|
||||
foreach ($expected as $string) {
|
||||
$this->assertRaw('<div>' . $string . '</div>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the link_generator Twig functions.
|
||||
*/
|
||||
public function testTwigLinkGenerator() {
|
||||
$this->drupalGet('twig-theme-test/link-generator');
|
||||
|
||||
/** @var \Drupal\Core\Utility\LinkGenerator $link_generator */
|
||||
$link_generator = $this->container->get('link_generator');
|
||||
|
||||
$expected = [
|
||||
'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['absolute' => TRUE])),
|
||||
'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['absolute' => TRUE, 'attributes' => ['foo' => 'bar']])),
|
||||
'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['foo' => 'bar', 'id' => 'kitten']])),
|
||||
'link via the linkgenerator: ' . $link_generator->generate('register', new Url('user.register', [], ['attributes' => ['id' => 'kitten']])),
|
||||
];
|
||||
|
||||
// Verify that link() has the ability to bubble cacheability metadata:
|
||||
// absolute URLs should bubble the 'url.site' cache context. (This only
|
||||
// needs to test that cacheability metadata is bubbled *at all*; detailed
|
||||
// tests for *which* cacheability metadata is bubbled live elsewhere.)
|
||||
$this->assertCacheContext('url.site');
|
||||
|
||||
$content = $this->getRawContent();
|
||||
$this->assertFalse(empty($content), 'Page content is not empty');
|
||||
foreach ($expected as $string) {
|
||||
$this->assertRaw('<div>' . $string . '</div>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the magic url to string Twig functions.
|
||||
*
|
||||
* @see \Drupal\Core\Url
|
||||
*/
|
||||
public function testTwigUrlToString() {
|
||||
$this->drupalGet('twig-theme-test/url-to-string');
|
||||
|
||||
$expected = [
|
||||
'rendered url: ' . Url::fromRoute('user.register')->toString(),
|
||||
];
|
||||
|
||||
$content = $this->getRawContent();
|
||||
$this->assertFalse(empty($content), 'Page content is not empty');
|
||||
foreach ($expected as $string) {
|
||||
$this->assertRaw('<div>' . $string . '</div>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the automatic/magic calling of toString() on objects, if exists.
|
||||
*/
|
||||
public function testTwigFileUrls() {
|
||||
$this->drupalGet('/twig-theme-test/file-url');
|
||||
$filepath = file_create_url('core/modules/system/tests/modules/twig_theme_test/twig_theme_test.js');
|
||||
$this->assertRaw('<div>file_url: ' . $filepath . '</div>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the attach of asset libraries.
|
||||
*/
|
||||
public function testTwigAttachLibrary() {
|
||||
$this->drupalGet('/twig-theme-test/attach-library');
|
||||
$this->assertRaw('ckeditor.js');
|
||||
}
|
||||
|
||||
}
|
149
core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php
Normal file
149
core/modules/system/src/Tests/Theme/EntityFilteringThemeTest.php
Normal file
|
@ -0,0 +1,149 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\EntityFilteringThemeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\comment\Tests\CommentTestTrait;
|
||||
use Drupal\Core\Extension\ExtensionDiscovery;
|
||||
use Drupal\comment\CommentInterface;
|
||||
use Drupal\comment\Plugin\Field\FieldType\CommentItemInterface;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests themed output for each entity type in all available themes to ensure
|
||||
* entity labels are filtered for XSS.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class EntityFilteringThemeTest extends WebTestBase {
|
||||
|
||||
use CommentTestTrait;
|
||||
|
||||
/**
|
||||
* Use the standard profile.
|
||||
*
|
||||
* We test entity theming with the default node, user, comment, and taxonomy
|
||||
* configurations at several paths in the standard profile.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $profile = 'standard';
|
||||
|
||||
/**
|
||||
* A list of all available themes.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\Extension[]
|
||||
*/
|
||||
protected $themes;
|
||||
|
||||
/**
|
||||
* A test user.
|
||||
*
|
||||
* @var \Drupal\user\User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
|
||||
/**
|
||||
* A test node.
|
||||
*
|
||||
* @var \Drupal\node\Node
|
||||
*/
|
||||
protected $node;
|
||||
|
||||
|
||||
/**
|
||||
* A test taxonomy term.
|
||||
*
|
||||
* @var \Drupal\taxonomy\Term
|
||||
*/
|
||||
protected $term;
|
||||
|
||||
|
||||
/**
|
||||
* A test comment.
|
||||
*
|
||||
* @var \Drupal\comment\Comment
|
||||
*/
|
||||
protected $comment;
|
||||
|
||||
/**
|
||||
* A string containing markup and JS.
|
||||
*
|
||||
* @string
|
||||
*/
|
||||
protected $xssLabel = "string with <em>HTML</em> and <script>alert('JS');</script>";
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Install all available non-testing themes.
|
||||
$listing = new ExtensionDiscovery(\Drupal::root());
|
||||
$this->themes = $listing->scan('theme', FALSE);
|
||||
\Drupal::service('theme_handler')->install(array_keys($this->themes));
|
||||
|
||||
// Create a test user.
|
||||
$this->user = $this->drupalCreateUser(array('access content', 'access user profiles'));
|
||||
$this->user->name = $this->xssLabel;
|
||||
$this->user->save();
|
||||
$this->drupalLogin($this->user);
|
||||
|
||||
// Create a test term.
|
||||
$this->term = entity_create('taxonomy_term', array(
|
||||
'name' => $this->xssLabel,
|
||||
'vid' => 1,
|
||||
));
|
||||
$this->term->save();
|
||||
|
||||
// Add a comment field.
|
||||
$this->addDefaultCommentField('node', 'article', 'comment', CommentItemInterface::OPEN);
|
||||
// Create a test node tagged with the test term.
|
||||
$this->node = $this->drupalCreateNode(array(
|
||||
'title' => $this->xssLabel,
|
||||
'type' => 'article',
|
||||
'promote' => NODE_PROMOTED,
|
||||
'field_tags' => array(array('target_id' => $this->term->id())),
|
||||
));
|
||||
|
||||
// Create a test comment on the test node.
|
||||
$this->comment = entity_create('comment', array(
|
||||
'entity_id' => $this->node->id(),
|
||||
'entity_type' => 'node',
|
||||
'field_name' => 'comment',
|
||||
'status' => CommentInterface::PUBLISHED,
|
||||
'subject' => $this->xssLabel,
|
||||
'comment_body' => array($this->randomMachineName()),
|
||||
));
|
||||
$this->comment->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks each themed entity for XSS filtering in available themes.
|
||||
*/
|
||||
function testThemedEntity() {
|
||||
// Check paths where various view modes of the entities are rendered.
|
||||
$paths = array(
|
||||
'user',
|
||||
'node',
|
||||
'node/' . $this->node->id(),
|
||||
'taxonomy/term/' . $this->term->id(),
|
||||
);
|
||||
|
||||
// Check each path in all available themes.
|
||||
foreach ($this->themes as $name => $theme) {
|
||||
$this->config('system.theme')
|
||||
->set('default', $name)
|
||||
->save();
|
||||
foreach ($paths as $path) {
|
||||
$this->drupalGet($path);
|
||||
$this->assertResponse(200);
|
||||
$this->assertNoRaw($this->xssLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
40
core/modules/system/src/Tests/Theme/FastTest.php
Normal file
40
core/modules/system/src/Tests/Theme/FastTest.php
Normal file
|
@ -0,0 +1,40 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\FastTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests autocompletion not loading registry.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class FastTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->account = $this->drupalCreateUser(array('access user profiles'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests access to user autocompletion and verify the correct results.
|
||||
*/
|
||||
function testUserAutocomplete() {
|
||||
$this->drupalLogin($this->account);
|
||||
$this->drupalGet('user/autocomplete', array('query' => array('q' => $this->account->getUsername())));
|
||||
$this->assertRaw($this->account->getUsername());
|
||||
$this->assertNoText('registry initialized', 'The registry was not initialized');
|
||||
}
|
||||
}
|
400
core/modules/system/src/Tests/Theme/FunctionsTest.php
Normal file
400
core/modules/system/src/Tests/Theme/FunctionsTest.php
Normal file
|
@ -0,0 +1,400 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\FunctionsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Session\UserSession;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests for common theme functions.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class FunctionsTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('router_test');
|
||||
|
||||
/**
|
||||
* Tests item-list.html.twig.
|
||||
*/
|
||||
function testItemList() {
|
||||
// Verify that empty items produce no output.
|
||||
$variables = array();
|
||||
$expected = '';
|
||||
$this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback generates no output.');
|
||||
|
||||
// Verify that empty items with title produce no output.
|
||||
$variables = array();
|
||||
$variables['title'] = 'Some title';
|
||||
$expected = '';
|
||||
$this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback with title generates no output.');
|
||||
|
||||
// Verify that empty items produce the empty string.
|
||||
$variables = array();
|
||||
$variables['empty'] = 'No items found.';
|
||||
$expected = '<div class="item-list">No items found.</div>';
|
||||
$this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback generates empty string.');
|
||||
|
||||
// Verify that empty items produce the empty string with title.
|
||||
$variables = array();
|
||||
$variables['title'] = 'Some title';
|
||||
$variables['empty'] = 'No items found.';
|
||||
$expected = '<div class="item-list"><h3>Some title</h3>No items found.</div>';
|
||||
$this->assertThemeOutput('item_list', $variables, $expected, 'Empty %callback generates empty string with title.');
|
||||
|
||||
// Verify that title set to 0 is output.
|
||||
$variables = array();
|
||||
$variables['title'] = 0;
|
||||
$variables['empty'] = 'No items found.';
|
||||
$expected = '<div class="item-list"><h3>0</h3>No items found.</div>';
|
||||
$this->assertThemeOutput('item_list', $variables, $expected, '%callback with title set to 0 generates a title.');
|
||||
|
||||
// Verify that title set to a render array is output.
|
||||
$variables = array();
|
||||
$variables['title'] = array(
|
||||
'#markup' => '<span>Render array</span>',
|
||||
);
|
||||
$variables['empty'] = 'No items found.';
|
||||
$expected = '<div class="item-list"><h3><span>Render array</span></h3>No items found.</div>';
|
||||
$this->assertThemeOutput('item_list', $variables, $expected, '%callback with title set to a render array generates a title.');
|
||||
|
||||
// Verify that empty text is not displayed when there are list items.
|
||||
$variables = array();
|
||||
$variables['title'] = 'Some title';
|
||||
$variables['empty'] = 'No items found.';
|
||||
$variables['items'] = array('Un', 'Deux', 'Trois');
|
||||
$expected = '<div class="item-list"><h3>Some title</h3><ul><li>Un</li><li>Deux</li><li>Trois</li></ul></div>';
|
||||
$this->assertThemeOutput('item_list', $variables, $expected, '%callback does not print empty text when there are list items.');
|
||||
|
||||
// Verify nested item lists.
|
||||
$variables = array();
|
||||
$variables['title'] = 'Some title';
|
||||
$variables['attributes'] = array(
|
||||
'id' => 'parentlist',
|
||||
);
|
||||
$variables['items'] = array(
|
||||
// A plain string value forms an own item.
|
||||
'a',
|
||||
// Items can be fully-fledged render arrays with their own attributes.
|
||||
array(
|
||||
'#wrapper_attributes' => array(
|
||||
'id' => 'item-id-b',
|
||||
),
|
||||
'#markup' => 'b',
|
||||
'childlist' => array(
|
||||
'#theme' => 'item_list',
|
||||
'#attributes' => array('id' => 'blist'),
|
||||
'#list_type' => 'ol',
|
||||
'#items' => array(
|
||||
'ba',
|
||||
array(
|
||||
'#markup' => 'bb',
|
||||
'#wrapper_attributes' => array('class' => array('item-class-bb')),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// However, items can also be child #items.
|
||||
array(
|
||||
'#markup' => 'c',
|
||||
'childlist' => array(
|
||||
'#attributes' => array('id' => 'clist'),
|
||||
'ca',
|
||||
array(
|
||||
'#markup' => 'cb',
|
||||
'#wrapper_attributes' => array('class' => array('item-class-cb')),
|
||||
'children' => array(
|
||||
'cba',
|
||||
'cbb',
|
||||
),
|
||||
),
|
||||
'cc',
|
||||
),
|
||||
),
|
||||
// Use #markup to be able to specify #wrapper_attributes.
|
||||
array(
|
||||
'#markup' => 'd',
|
||||
'#wrapper_attributes' => array('id' => 'item-id-d'),
|
||||
),
|
||||
// An empty item with attributes.
|
||||
array(
|
||||
'#wrapper_attributes' => array('id' => 'item-id-e'),
|
||||
),
|
||||
// Lastly, another plain string item.
|
||||
'f',
|
||||
);
|
||||
|
||||
$inner_b = '<div class="item-list"><ol id="blist">';
|
||||
$inner_b .= '<li>ba</li>';
|
||||
$inner_b .= '<li class="item-class-bb">bb</li>';
|
||||
$inner_b .= '</ol></div>';
|
||||
|
||||
$inner_cb = '<div class="item-list"><ul>';
|
||||
$inner_cb .= '<li>cba</li>';
|
||||
$inner_cb .= '<li>cbb</li>';
|
||||
$inner_cb .= '</ul></div>';
|
||||
|
||||
$inner_c = '<div class="item-list"><ul id="clist">';
|
||||
$inner_c .= '<li>ca</li>';
|
||||
$inner_c .= '<li class="item-class-cb">cb' . $inner_cb . '</li>';
|
||||
$inner_c .= '<li>cc</li>';
|
||||
$inner_c .= '</ul></div>';
|
||||
|
||||
$expected = '<div class="item-list">';
|
||||
$expected .= '<h3>Some title</h3>';
|
||||
$expected .= '<ul id="parentlist">';
|
||||
$expected .= '<li>a</li>';
|
||||
$expected .= '<li id="item-id-b">b' . $inner_b . '</li>';
|
||||
$expected .= '<li>c' . $inner_c . '</li>';
|
||||
$expected .= '<li id="item-id-d">d</li>';
|
||||
$expected .= '<li id="item-id-e"></li>';
|
||||
$expected .= '<li>f</li>';
|
||||
$expected .= '</ul></div>';
|
||||
|
||||
$this->assertThemeOutput('item_list', $variables, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests links.html.twig.
|
||||
*/
|
||||
function testLinks() {
|
||||
// Turn off the query for the _l() function to compare the active
|
||||
// link correctly.
|
||||
$original_query = \Drupal::request()->query->all();
|
||||
\Drupal::request()->query->replace(array());
|
||||
// Verify that empty variables produce no output.
|
||||
$variables = array();
|
||||
$expected = '';
|
||||
$this->assertThemeOutput('links', $variables, $expected, 'Empty %callback generates no output.');
|
||||
|
||||
$variables = array();
|
||||
$variables['heading'] = 'Some title';
|
||||
$expected = '';
|
||||
$this->assertThemeOutput('links', $variables, $expected, 'Empty %callback with heading generates no output.');
|
||||
|
||||
// Verify that a list of links is properly rendered.
|
||||
$variables = array();
|
||||
$variables['attributes'] = array('id' => 'somelinks');
|
||||
$variables['links'] = array(
|
||||
'a link' => array(
|
||||
'title' => 'A <link>',
|
||||
'url' => Url::fromUri('base:a/link'),
|
||||
),
|
||||
'plain text' => array(
|
||||
'title' => 'Plain "text"',
|
||||
),
|
||||
'html text' => array(
|
||||
'title' => SafeMarkup::format('<span class="unescaped">@text</span>', array('@text' => 'potentially unsafe text that <should> be escaped')),
|
||||
),
|
||||
'front page' => array(
|
||||
'title' => 'Front page',
|
||||
'url' => Url::fromRoute('<front>'),
|
||||
),
|
||||
'router-test' => array(
|
||||
'title' => 'Test route',
|
||||
'url' => Url::fromRoute('router_test.1'),
|
||||
),
|
||||
'query-test' => array(
|
||||
'title' => 'Query test route',
|
||||
'url' => Url::fromRoute('router_test.1'),
|
||||
'query' => array(
|
||||
'key' => 'value',
|
||||
)
|
||||
),
|
||||
);
|
||||
|
||||
$expected_links = '';
|
||||
$expected_links .= '<ul id="somelinks">';
|
||||
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . SafeMarkup::checkPlain('A <link>') . '</a></li>';
|
||||
$expected_links .= '<li class="plain-text">' . SafeMarkup::checkPlain('Plain "text"') . '</li>';
|
||||
$expected_links .= '<li class="html-text"><span class="unescaped">' . SafeMarkup::checkPlain('potentially unsafe text that <should> be escaped') . '</span></li>';
|
||||
$expected_links .= '<li class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '">' . SafeMarkup::checkPlain('Front page') . '</a></li>';
|
||||
$expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . SafeMarkup::checkPlain('Test route') . '</a></li>';
|
||||
$query = array('key' => 'value');
|
||||
$expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . SafeMarkup::checkPlain('Query test route') . '</a></li>';
|
||||
$expected_links .= '</ul>';
|
||||
|
||||
// Verify that passing a string as heading works.
|
||||
$variables['heading'] = 'Links heading';
|
||||
$expected_heading = '<h2>Links heading</h2>';
|
||||
$expected = $expected_heading . $expected_links;
|
||||
$this->assertThemeOutput('links', $variables, $expected);
|
||||
|
||||
// Restore the original request's query.
|
||||
\Drupal::request()->query->replace($original_query);
|
||||
|
||||
// Verify that passing an array as heading works (core support).
|
||||
$variables['heading'] = array(
|
||||
'text' => 'Links heading',
|
||||
'level' => 'h3',
|
||||
'attributes' => array('class' => array('heading')),
|
||||
);
|
||||
$expected_heading = '<h3 class="heading">Links heading</h3>';
|
||||
$expected = $expected_heading . $expected_links;
|
||||
$this->assertThemeOutput('links', $variables, $expected);
|
||||
|
||||
// Verify that passing attributes for the heading works.
|
||||
$variables['heading'] = array('text' => 'Links heading', 'level' => 'h3', 'attributes' => array('id' => 'heading'));
|
||||
$expected_heading = '<h3 id="heading">Links heading</h3>';
|
||||
$expected = $expected_heading . $expected_links;
|
||||
$this->assertThemeOutput('links', $variables, $expected);
|
||||
|
||||
// Verify that passing attributes for the links work.
|
||||
$variables['links']['plain text']['attributes'] = array(
|
||||
'class' => array('a/class'),
|
||||
);
|
||||
$expected_links = '';
|
||||
$expected_links .= '<ul id="somelinks">';
|
||||
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . SafeMarkup::checkPlain('A <link>') . '</a></li>';
|
||||
$expected_links .= '<li class="plain-text"><span class="a/class">' . SafeMarkup::checkPlain('Plain "text"') . '</span></li>';
|
||||
$expected_links .= '<li class="html-text"><span class="unescaped">' . SafeMarkup::checkPlain('potentially unsafe text that <should> be escaped') . '</span></li>';
|
||||
$expected_links .= '<li class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '">' . SafeMarkup::checkPlain('Front page') . '</a></li>';
|
||||
$expected_links .= '<li class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . SafeMarkup::checkPlain('Test route') . '</a></li>';
|
||||
$query = array('key' => 'value');
|
||||
$expected_links .= '<li class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '">' . SafeMarkup::checkPlain('Query test route') . '</a></li>';
|
||||
$expected_links .= '</ul>';
|
||||
$expected = $expected_heading . $expected_links;
|
||||
$this->assertThemeOutput('links', $variables, $expected);
|
||||
|
||||
// Verify the data- attributes for setting the "active" class on links.
|
||||
\Drupal::currentUser()->setAccount(new UserSession(array('uid' => 1)));
|
||||
$variables['set_active_class'] = TRUE;
|
||||
$expected_links = '';
|
||||
$expected_links .= '<ul id="somelinks">';
|
||||
$expected_links .= '<li class="a-link"><a href="' . Url::fromUri('base:a/link')->toString() . '">' . SafeMarkup::checkPlain('A <link>') . '</a></li>';
|
||||
$expected_links .= '<li class="plain-text"><span class="a/class">' . SafeMarkup::checkPlain('Plain "text"') . '</span></li>';
|
||||
$expected_links .= '<li class="html-text"><span class="unescaped">' . SafeMarkup::checkPlain('potentially unsafe text that <should> be escaped') . '</span></li>';
|
||||
$expected_links .= '<li data-drupal-link-system-path="<front>" class="front-page"><a href="' . Url::fromRoute('<front>')->toString() . '" data-drupal-link-system-path="<front>">' . SafeMarkup::checkPlain('Front page') . '</a></li>';
|
||||
$expected_links .= '<li data-drupal-link-system-path="router_test/test1" class="router-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '" data-drupal-link-system-path="router_test/test1">' . SafeMarkup::checkPlain('Test route') . '</a></li>';
|
||||
$query = array('key' => 'value');
|
||||
$encoded_query = SafeMarkup::checkPlain(Json::encode($query));
|
||||
$expected_links .= '<li data-drupal-link-query="'.$encoded_query.'" data-drupal-link-system-path="router_test/test1" class="query-test"><a href="' . \Drupal::urlGenerator()->generate('router_test.1', $query) . '" data-drupal-link-query="'.$encoded_query.'" data-drupal-link-system-path="router_test/test1">' . SafeMarkup::checkPlain('Query test route') . '</a></li>';
|
||||
$expected_links .= '</ul>';
|
||||
$expected = $expected_heading . $expected_links;
|
||||
$this->assertThemeOutput('links', $variables, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the use of drupal_pre_render_links() on a nested array of links.
|
||||
*/
|
||||
function testDrupalPreRenderLinks() {
|
||||
// Define the base array to be rendered, containing a variety of different
|
||||
// kinds of links.
|
||||
$base_array = array(
|
||||
'#theme' => 'links',
|
||||
'#pre_render' => array('drupal_pre_render_links'),
|
||||
'#links' => array(
|
||||
'parent_link' => array(
|
||||
'title' => 'Parent link original',
|
||||
'url' => Url::fromRoute('router_test.1'),
|
||||
),
|
||||
),
|
||||
'first_child' => array(
|
||||
'#theme' => 'links',
|
||||
'#links' => array(
|
||||
// This should be rendered if 'first_child' is rendered separately,
|
||||
// but ignored if the parent is being rendered (since it duplicates
|
||||
// one of the parent's links).
|
||||
'parent_link' => array(
|
||||
'title' => 'Parent link copy',
|
||||
'url' => Url::fromRoute('router_test.6'),
|
||||
),
|
||||
// This should always be rendered.
|
||||
'first_child_link' => array(
|
||||
'title' => 'First child link',
|
||||
'url' => Url::fromRoute('router_test.7'),
|
||||
),
|
||||
),
|
||||
),
|
||||
// This should always be rendered as part of the parent.
|
||||
'second_child' => array(
|
||||
'#theme' => 'links',
|
||||
'#links' => array(
|
||||
'second_child_link' => array(
|
||||
'title' => 'Second child link',
|
||||
'url' => Url::fromRoute('router_test.8'),
|
||||
),
|
||||
),
|
||||
),
|
||||
// This should never be rendered, since the user does not have access to
|
||||
// it.
|
||||
'third_child' => array(
|
||||
'#theme' => 'links',
|
||||
'#links' => array(
|
||||
'third_child_link' => array(
|
||||
'title' => 'Third child link',
|
||||
'url' => Url::fromRoute('router_test.9'),
|
||||
),
|
||||
),
|
||||
'#access' => FALSE,
|
||||
),
|
||||
);
|
||||
|
||||
// Start with a fresh copy of the base array, and try rendering the entire
|
||||
// thing. We expect a single <ul> with appropriate links contained within
|
||||
// it.
|
||||
$render_array = $base_array;
|
||||
$html = \Drupal::service('renderer')->renderRoot($render_array);
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadHTML($html);
|
||||
$this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered HTML.');
|
||||
$list_elements = $dom->getElementsByTagName('li');
|
||||
$this->assertEqual($list_elements->length, 3, 'Three "li" tags found in the rendered HTML.');
|
||||
$this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link original', 'First expected link found.');
|
||||
$this->assertEqual($list_elements->item(1)->nodeValue, 'First child link', 'Second expected link found.');
|
||||
$this->assertEqual($list_elements->item(2)->nodeValue, 'Second child link', 'Third expected link found.');
|
||||
$this->assertIdentical(strpos($html, 'Parent link copy'), FALSE, '"Parent link copy" link not found.');
|
||||
$this->assertIdentical(strpos($html, 'Third child link'), FALSE, '"Third child link" link not found.');
|
||||
|
||||
// Now render 'first_child', followed by the rest of the links, and make
|
||||
// sure we get two separate <ul>'s with the appropriate links contained
|
||||
// within each.
|
||||
$render_array = $base_array;
|
||||
$child_html = \Drupal::service('renderer')->renderRoot($render_array['first_child']);
|
||||
$parent_html = \Drupal::service('renderer')->renderRoot($render_array);
|
||||
// First check the child HTML.
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadHTML($child_html);
|
||||
$this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered child HTML.');
|
||||
$list_elements = $dom->getElementsByTagName('li');
|
||||
$this->assertEqual($list_elements->length, 2, 'Two "li" tags found in the rendered child HTML.');
|
||||
$this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link copy', 'First expected link found.');
|
||||
$this->assertEqual($list_elements->item(1)->nodeValue, 'First child link', 'Second expected link found.');
|
||||
// Then check the parent HTML.
|
||||
$dom = new \DOMDocument();
|
||||
$dom->loadHTML($parent_html);
|
||||
$this->assertEqual($dom->getElementsByTagName('ul')->length, 1, 'One "ul" tag found in the rendered parent HTML.');
|
||||
$list_elements = $dom->getElementsByTagName('li');
|
||||
$this->assertEqual($list_elements->length, 2, 'Two "li" tags found in the rendered parent HTML.');
|
||||
$this->assertEqual($list_elements->item(0)->nodeValue, 'Parent link original', 'First expected link found.');
|
||||
$this->assertEqual($list_elements->item(1)->nodeValue, 'Second child link', 'Second expected link found.');
|
||||
$this->assertIdentical(strpos($parent_html, 'First child link'), FALSE, '"First child link" link not found.');
|
||||
$this->assertIdentical(strpos($parent_html, 'Third child link'), FALSE, '"Third child link" link not found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests theme_image().
|
||||
*/
|
||||
function testImage() {
|
||||
// Test that data URIs work with theme_image().
|
||||
$variables = array();
|
||||
$variables['uri'] = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==';
|
||||
$variables['alt'] = 'Data URI image of a red dot';
|
||||
$expected = '<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==" alt="Data URI image of a red dot" />' . "\n";
|
||||
$this->assertThemeOutput('image', $variables, $expected);
|
||||
}
|
||||
|
||||
}
|
36
core/modules/system/src/Tests/Theme/HtmlAttributesTest.php
Normal file
36
core/modules/system/src/Tests/Theme/HtmlAttributesTest.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\HtmlAttributesTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests attributes inserted in the 'html' and 'body' elements on the page.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class HtmlAttributesTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test');
|
||||
|
||||
/**
|
||||
* Tests that attributes in the 'html' and 'body' elements can be altered.
|
||||
*/
|
||||
function testThemeHtmlAttributes() {
|
||||
$this->drupalGet('');
|
||||
$attributes = $this->xpath('/html[@theme_test_html_attribute="theme test html attribute value"]');
|
||||
$this->assertTrue(count($attributes) == 1, "Attribute set in the 'html' element via hook_preprocess_HOOK() found.");
|
||||
$attributes = $this->xpath('/html/body[@theme_test_body_attribute="theme test body attribute value"]');
|
||||
$this->assertTrue(count($attributes) == 1, "Attribute set in the 'body' element via hook_preprocess_HOOK() found.");
|
||||
}
|
||||
}
|
141
core/modules/system/src/Tests/Theme/ImageTest.php
Normal file
141
core/modules/system/src/Tests/Theme/ImageTest.php
Normal file
|
@ -0,0 +1,141 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\ImageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests built-in image theme functions.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class ImageTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/*
|
||||
* The images to test with.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $testImages;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
$this->testImages = array(
|
||||
'/core/misc/druplicon.png',
|
||||
'/core/misc/loading.gif',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an image with the sizes attribute is output correctly.
|
||||
*/
|
||||
function testThemeImageWithSizes() {
|
||||
// Test with multipliers.
|
||||
$sizes = '(max-width: ' . rand(10, 30) . 'em) 100vw, (max-width: ' . rand(30, 50) . 'em) 50vw, 30vw';
|
||||
$image = array(
|
||||
'#theme' => 'image',
|
||||
'#sizes' => $sizes,
|
||||
'#uri' => reset($this->testImages),
|
||||
'#width' => rand(0, 1000) . 'px',
|
||||
'#height' => rand(0, 500) . 'px',
|
||||
'#alt' => $this->randomMachineName(),
|
||||
'#title' => $this->randomMachineName(),
|
||||
);
|
||||
$this->render($image);
|
||||
|
||||
// Make sure sizes is set.
|
||||
$this->assertRaw($sizes, 'Sizes is set correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an image with the src attribute is output correctly.
|
||||
*/
|
||||
function testThemeImageWithSrc() {
|
||||
|
||||
$image = array(
|
||||
'#theme' => 'image',
|
||||
'#uri' => reset($this->testImages),
|
||||
'#width' => rand(0, 1000) . 'px',
|
||||
'#height' => rand(0, 500) . 'px',
|
||||
'#alt' => $this->randomMachineName(),
|
||||
'#title' => $this->randomMachineName(),
|
||||
);
|
||||
$this->render($image);
|
||||
|
||||
// Make sure the src attribute has the correct value.
|
||||
$this->assertRaw(file_create_url($image['#uri']), 'Correct output for an image with the src attribute.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an image with the srcset and multipliers is output correctly.
|
||||
*/
|
||||
function testThemeImageWithSrcsetMultiplier() {
|
||||
// Test with multipliers.
|
||||
$image = array(
|
||||
'#theme' => 'image',
|
||||
'#srcset' => array(
|
||||
array(
|
||||
'uri' => $this->testImages[0],
|
||||
'multiplier' => '1x',
|
||||
),
|
||||
array(
|
||||
'uri' => $this->testImages[1],
|
||||
'multiplier' => '2x',
|
||||
),
|
||||
),
|
||||
'#width' => rand(0, 1000) . 'px',
|
||||
'#height' => rand(0, 500) . 'px',
|
||||
'#alt' => $this->randomMachineName(),
|
||||
'#title' => $this->randomMachineName(),
|
||||
);
|
||||
$this->render($image);
|
||||
|
||||
// Make sure the srcset attribute has the correct value.
|
||||
$this->assertRaw(file_create_url($this->testImages[0]) . ' 1x, ' . file_create_url($this->testImages[1]) . ' 2x', 'Correct output for image with srcset attribute and multipliers.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that an image with the srcset and widths is output correctly.
|
||||
*/
|
||||
function testThemeImageWithSrcsetWidth() {
|
||||
// Test with multipliers.
|
||||
$widths = array(
|
||||
rand(0, 500) . 'w',
|
||||
rand(500, 1000) . 'w',
|
||||
);
|
||||
$image = array(
|
||||
'#theme' => 'image',
|
||||
'#srcset' => array(
|
||||
array(
|
||||
'uri' => $this->testImages[0],
|
||||
'width' => $widths[0],
|
||||
),
|
||||
array(
|
||||
'uri' => $this->testImages[1],
|
||||
'width' => $widths[1],
|
||||
),
|
||||
),
|
||||
'#width' => rand(0, 1000) . 'px',
|
||||
'#height' => rand(0, 500) . 'px',
|
||||
'#alt' => $this->randomMachineName(),
|
||||
'#title' => $this->randomMachineName(),
|
||||
);
|
||||
$this->render($image);
|
||||
|
||||
// Make sure the srcset attribute has the correct value.
|
||||
$this->assertRaw(file_create_url($this->testImages[0]) . ' ' . $widths[0] . ', ' . file_create_url($this->testImages[1]) . ' ' . $widths[1], 'Correct output for image with srcset attribute and width descriptors.');
|
||||
}
|
||||
|
||||
}
|
41
core/modules/system/src/Tests/Theme/MessageTest.php
Normal file
41
core/modules/system/src/Tests/Theme/MessageTest.php
Normal file
|
@ -0,0 +1,41 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\MessageTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests built-in message theme functions.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class MessageTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* Tests setting messages output.
|
||||
*/
|
||||
function testMessages() {
|
||||
// Enable the Classy theme.
|
||||
\Drupal::service('theme_handler')->install(['classy']);
|
||||
$this->config('system.theme')->set('default', 'classy')->save();
|
||||
|
||||
drupal_set_message('An error occurred', 'error');
|
||||
drupal_set_message('But then something nice happened');
|
||||
$messages = array(
|
||||
'#type' => 'status_messages',
|
||||
);
|
||||
$this->render($messages);
|
||||
$this->assertRaw('messages messages--error');
|
||||
$this->assertRaw('messages messages--status');
|
||||
}
|
||||
}
|
119
core/modules/system/src/Tests/Theme/RegistryTest.php
Normal file
119
core/modules/system/src/Tests/Theme/RegistryTest.php
Normal file
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\RegistryTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\Core\Theme\Registry;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
use Drupal\Core\Utility\ThemeRegistry;
|
||||
|
||||
/**
|
||||
* Tests the behavior of the ThemeRegistry class.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class RegistryTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test', 'system');
|
||||
|
||||
protected $profile = 'testing';
|
||||
|
||||
/**
|
||||
* Tests the behavior of the theme registry class.
|
||||
*/
|
||||
function testRaceCondition() {
|
||||
// The theme registry is not marked as persistable in case we don't have a
|
||||
// proper request.
|
||||
\Drupal::request()->setMethod('GET');
|
||||
$cid = 'test_theme_registry';
|
||||
|
||||
// Directly instantiate the theme registry, this will cause a base cache
|
||||
// entry to be written in __construct().
|
||||
$cache = \Drupal::cache();
|
||||
$lock_backend = \Drupal::lock();
|
||||
$registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry'), $this->container->get('module_handler')->isLoaded());
|
||||
|
||||
$this->assertTrue(\Drupal::cache()->get($cid), 'Cache entry was created.');
|
||||
|
||||
// Trigger a cache miss for an offset.
|
||||
$this->assertTrue($registry->get('theme_test_template_test'), 'Offset was returned correctly from the theme registry.');
|
||||
// This will cause the ThemeRegistry class to write an updated version of
|
||||
// the cache entry when it is destroyed, usually at the end of the request.
|
||||
// Before that happens, manually delete the cache entry we created earlier
|
||||
// so that the new entry is written from scratch.
|
||||
\Drupal::cache()->delete($cid);
|
||||
|
||||
// Destroy the class so that it triggers a cache write for the offset.
|
||||
$registry->destruct();
|
||||
|
||||
$this->assertTrue(\Drupal::cache()->get($cid), 'Cache entry was created.');
|
||||
|
||||
// Create a new instance of the class. Confirm that both the offset
|
||||
// requested previously, and one that has not yet been requested are both
|
||||
// available.
|
||||
$registry = new ThemeRegistry($cid, $cache, $lock_backend, array('theme_registry'), $this->container->get('module_handler')->isLoaded());
|
||||
$this->assertTrue($registry->get('theme_test_template_test'), 'Offset was returned correctly from the theme registry');
|
||||
$this->assertTrue($registry->get('theme_test_template_test_2'), 'Offset was returned correctly from the theme registry');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the theme registry with multiple subthemes.
|
||||
*/
|
||||
public function testMultipleSubThemes() {
|
||||
$theme_handler = \Drupal::service('theme_handler');
|
||||
$theme_handler->install(['test_basetheme', 'test_subtheme', 'test_subsubtheme']);
|
||||
|
||||
$registry_subsub_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_subsubtheme');
|
||||
$registry_subsub_theme->setThemeManager(\Drupal::theme());
|
||||
$registry_sub_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_subtheme');
|
||||
$registry_sub_theme->setThemeManager(\Drupal::theme());
|
||||
$registry_base_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_basetheme');
|
||||
$registry_base_theme->setThemeManager(\Drupal::theme());
|
||||
|
||||
$preprocess_functions = $registry_subsub_theme->get()['theme_test_template_test']['preprocess functions'];
|
||||
$this->assertIdentical([
|
||||
'template_preprocess',
|
||||
'test_basetheme_preprocess_theme_test_template_test',
|
||||
'test_subtheme_preprocess_theme_test_template_test',
|
||||
'test_subsubtheme_preprocess_theme_test_template_test',
|
||||
], $preprocess_functions);
|
||||
|
||||
$preprocess_functions = $registry_sub_theme->get()['theme_test_template_test']['preprocess functions'];
|
||||
$this->assertIdentical([
|
||||
'template_preprocess',
|
||||
'test_basetheme_preprocess_theme_test_template_test',
|
||||
'test_subtheme_preprocess_theme_test_template_test',
|
||||
], $preprocess_functions);
|
||||
|
||||
$preprocess_functions = $registry_base_theme->get()['theme_test_template_test']['preprocess functions'];
|
||||
$this->assertIdentical([
|
||||
'template_preprocess',
|
||||
'test_basetheme_preprocess_theme_test_template_test',
|
||||
], $preprocess_functions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the theme registry can be altered by themes.
|
||||
*/
|
||||
public function testThemeRegistryAlterByTheme() {
|
||||
|
||||
/** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
|
||||
$theme_handler = \Drupal::service('theme_handler');
|
||||
$theme_handler->install(['test_theme']);
|
||||
$theme_handler->setDefault('test_theme');
|
||||
|
||||
$registry = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_theme');
|
||||
$registry->setThemeManager(\Drupal::theme());
|
||||
$this->assertEqual('value', $registry->get()['theme_test_template_test']['variables']['additional']);
|
||||
}
|
||||
|
||||
}
|
311
core/modules/system/src/Tests/Theme/TableTest.php
Normal file
311
core/modules/system/src/Tests/Theme/TableTest.php
Normal file
|
@ -0,0 +1,311 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TableTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests built-in table theme functions.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TableTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->installSchema('system', 'router');
|
||||
\Drupal::service('router.builder')->rebuild();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tableheader.js provides 'sticky' table headers, and is included by default.
|
||||
*/
|
||||
function testThemeTableStickyHeaders() {
|
||||
$header = array('one', 'two', 'three');
|
||||
$rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#sticky' => TRUE,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->assertTrue(in_array('core/drupal.tableheader', $table['#attached']['library']), 'tableheader asset library found.');
|
||||
$this->assertRaw('sticky-enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* If $sticky is FALSE, no tableheader.js should be included.
|
||||
*/
|
||||
function testThemeTableNoStickyHeaders() {
|
||||
$header = array('one', 'two', 'three');
|
||||
$rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
|
||||
$attributes = array();
|
||||
$caption = NULL;
|
||||
$colgroups = array();
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#attributes' => $attributes,
|
||||
'#caption' => $caption,
|
||||
'#colgroups' => $colgroups,
|
||||
'#sticky' => FALSE,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->assertFalse(in_array('core/drupal.tableheader', $table['#attached']['library']), 'tableheader asset library not found.');
|
||||
$this->assertNoRaw('sticky-enabled');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the table header is printed correctly even if there are no rows,
|
||||
* and that the empty text is displayed correctly.
|
||||
*/
|
||||
function testThemeTableWithEmptyMessage() {
|
||||
$header = array(
|
||||
'Header 1',
|
||||
array(
|
||||
'data' => 'Header 2',
|
||||
'colspan' => 2,
|
||||
),
|
||||
);
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => array(),
|
||||
'#empty' => 'Empty row.',
|
||||
);
|
||||
|
||||
// Enable the Classy theme.
|
||||
\Drupal::service('theme_handler')->install(['classy']);
|
||||
$this->config('system.theme')->set('default', 'classy')->save();
|
||||
|
||||
$this->render($table);
|
||||
$this->removeWhiteSpace();
|
||||
$this->assertRaw('<thead><tr><th>Header 1</th><th colspan="2">Header 2</th></tr>', 'Table header found.');
|
||||
$this->assertRaw('<tr class="odd"><td colspan="3" class="empty message">Empty row.</td>', 'Colspan on #empty row found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the 'no_striping' option works correctly.
|
||||
*/
|
||||
function testThemeTableWithNoStriping() {
|
||||
$rows = array(
|
||||
array(
|
||||
'data' => array(1),
|
||||
'no_striping' => TRUE,
|
||||
),
|
||||
);
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#rows' => $rows,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->assertNoRaw('class="odd"', 'Odd/even classes were not added because $no_striping = TRUE.');
|
||||
$this->assertNoRaw('no_striping', 'No invalid no_striping HTML attribute was printed.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that the 'footer' option works correctly.
|
||||
*/
|
||||
function testThemeTableFooter() {
|
||||
$footer = array(
|
||||
array(
|
||||
'data' => array(1),
|
||||
),
|
||||
array('Foo'),
|
||||
);
|
||||
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#rows' => array(),
|
||||
'#footer' => $footer,
|
||||
);
|
||||
|
||||
$this->render($table);
|
||||
$this->removeWhiteSpace();
|
||||
$this->assertRaw('<tfoot><tr><td>1</td></tr><tr><td>Foo</td></tr></tfoot>', 'Table footer found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the 'header' option in cells works correctly.
|
||||
*/
|
||||
function testThemeTableHeaderCellOption() {
|
||||
$rows = array(
|
||||
array(
|
||||
array('data' => 1, 'header' => TRUE),
|
||||
array('data' => 1, 'header' => FALSE),
|
||||
array('data' => 1),
|
||||
),
|
||||
);
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#rows' => $rows,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->removeWhiteSpace();
|
||||
$this->assertRaw('<th>1</th><td>1</td><td>1</td>', 'The th and td tags was printed correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the 'responsive-table' class is applied correctly.
|
||||
*/
|
||||
public function testThemeTableResponsive() {
|
||||
$header = array('one', 'two', 'three');
|
||||
$rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#responsive' => TRUE,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->assertRaw('responsive-enabled', 'The responsive-enabled class was printed correctly.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the 'responsive-table' class is not applied without headers.
|
||||
*/
|
||||
public function testThemeTableNotResponsiveHeaders() {
|
||||
$rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#rows' => $rows,
|
||||
'#responsive' => TRUE,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->assertNoRaw('responsive-enabled', 'The responsive-enabled class is not applied without table headers.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that 'responsive-table' class only applied when responsive is TRUE.
|
||||
*/
|
||||
public function testThemeTableNotResponsiveProperty() {
|
||||
$header = array('one', 'two', 'three');
|
||||
$rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#responsive' => FALSE,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->assertNoRaw('responsive-enabled', 'The responsive-enabled class is not applied without the "responsive" property set to TRUE.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests 'priority-medium' and 'priority-low' classes.
|
||||
*/
|
||||
public function testThemeTableResponsivePriority() {
|
||||
$header = array(
|
||||
// Test associative header indices.
|
||||
'associative_key' => array('data' => 1, 'class' => array(RESPONSIVE_PRIORITY_MEDIUM)),
|
||||
// Test non-associative header indices.
|
||||
array('data' => 2, 'class' => array(RESPONSIVE_PRIORITY_LOW)),
|
||||
// Test no responsive priorities.
|
||||
array('data' => 3),
|
||||
);
|
||||
$rows = array(array(4, 5, 6));
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#responsive' => TRUE,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->assertRaw('<th class="priority-medium">1</th>', 'Header 1: the priority-medium class was applied correctly.');
|
||||
$this->assertRaw('<th class="priority-low">2</th>', 'Header 2: the priority-low class was applied correctly.');
|
||||
$this->assertRaw('<th>3</th>', 'Header 3: no priority classes were applied.');
|
||||
$this->assertRaw('<td class="priority-medium">4</td>', 'Cell 1: the priority-medium class was applied correctly.');
|
||||
$this->assertRaw('<td class="priority-low">5</td>', 'Cell 2: the priority-low class was applied correctly.');
|
||||
$this->assertRaw('<td>6</td>', 'Cell 3: no priority classes were applied.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests header elements with a mix of string and render array values.
|
||||
*/
|
||||
public function testThemeTableHeaderRenderArray() {
|
||||
$header = array(
|
||||
array (
|
||||
'data' => array(
|
||||
'#markup' => 'one',
|
||||
),
|
||||
),
|
||||
'two',
|
||||
array (
|
||||
'data' => array(
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'b',
|
||||
'#value' => 'three',
|
||||
),
|
||||
),
|
||||
);
|
||||
$rows = array(array(1,2,3), array(4,5,6), array(7,8,9));
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#responsive' => FALSE,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->removeWhiteSpace();
|
||||
$this->assertRaw('<thead><tr><th>one</th><th>two</th><th><b>three</b></th></tr>', 'Table header found.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests row elements with a mix of string and render array values.
|
||||
*/
|
||||
public function testThemeTableRowRenderArray() {
|
||||
$header = array('one', 'two', 'three');
|
||||
$rows = array(
|
||||
array(
|
||||
'1-one',
|
||||
array(
|
||||
'data' => '1-two'
|
||||
),
|
||||
'1-three',
|
||||
),
|
||||
array(
|
||||
array (
|
||||
'data' => array(
|
||||
'#markup' => '2-one',
|
||||
),
|
||||
),
|
||||
'2-two',
|
||||
array (
|
||||
'data' => array(
|
||||
'#type' => 'html_tag',
|
||||
'#tag' => 'b',
|
||||
'#value' => '2-three',
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
$table = array(
|
||||
'#type' => 'table',
|
||||
'#header' => $header,
|
||||
'#rows' => $rows,
|
||||
'#responsive' => FALSE,
|
||||
);
|
||||
$this->render($table);
|
||||
$this->removeWhiteSpace();
|
||||
$this->assertRaw('<tbody><tr><td>1-one</td><td>1-two</td><td>1-three</td></tr>', 'Table row 1 found.');
|
||||
$this->assertRaw('<tr><td>2-one</td><td>2-two</td><td><b>2-three</b></td></tr></tbody>', 'Table row 2 found.');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\ThemeEarlyInitializationTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests that the theme system can be correctly initialized early in the page
|
||||
* request.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class ThemeEarlyInitializationTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test');
|
||||
|
||||
/**
|
||||
* Test that the theme system can generate output in a request listener.
|
||||
*/
|
||||
function testRequestListener() {
|
||||
$this->drupalGet('theme-test/request-listener');
|
||||
// Verify that themed output generated in the request listener appears.
|
||||
$this->assertRaw('Themed output generated in a KernelEvents::REQUEST listener');
|
||||
// Verify that the default theme's CSS still appears even though the theme
|
||||
// system was initialized early.
|
||||
$this->assertRaw('classy/css/layout.css');
|
||||
}
|
||||
}
|
106
core/modules/system/src/Tests/Theme/ThemeInfoTest.php
Normal file
106
core/modules/system/src/Tests/Theme/ThemeInfoTest.php
Normal file
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\ThemeInfoTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests processing of theme .info.yml properties.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class ThemeInfoTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test');
|
||||
|
||||
/**
|
||||
* The theme handler used in this test for enabling themes.
|
||||
*
|
||||
* @var \Drupal\Core\Extension\ThemeHandler
|
||||
*/
|
||||
protected $themeHandler;
|
||||
|
||||
/**
|
||||
* The theme manager used in this test.
|
||||
*
|
||||
* @var \Drupal\Core\Theme\ThemeManagerInterface
|
||||
*/
|
||||
protected $themeManager;
|
||||
|
||||
/**
|
||||
* The state service used in this test.
|
||||
*
|
||||
* @var \Drupal\Core\State\StateInterface
|
||||
*/
|
||||
protected $state;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
$this->themeHandler = $this->container->get('theme_handler');
|
||||
$this->themeManager = $this->container->get('theme.manager');
|
||||
$this->state = $this->container->get('state');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests stylesheets-remove.
|
||||
*/
|
||||
function testStylesheets() {
|
||||
$this->themeHandler->install(array('test_basetheme', 'test_subtheme'));
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_subtheme')
|
||||
->save();
|
||||
|
||||
$base = drupal_get_path('theme', 'test_basetheme');
|
||||
$sub = drupal_get_path('theme', 'test_subtheme') . '/css';
|
||||
|
||||
// All removals are expected to be based on a file's path and name and
|
||||
// should work nevertheless.
|
||||
$this->drupalGet('theme-test/info/stylesheets');
|
||||
|
||||
$this->assertIdentical(1, count($this->xpath("//link[contains(@href, '$base/base-add.css')]")), "$base/base-add.css found");
|
||||
$this->assertIdentical(0, count($this->xpath("//link[contains(@href, 'base-remove.css')]")), "base-remove.css not found");
|
||||
|
||||
$this->assertIdentical(1, count($this->xpath("//link[contains(@href, '$sub/sub-add.css')]")), "$sub/sub-add.css found");
|
||||
$this->assertIdentical(0, count($this->xpath("//link[contains(@href, 'sub-remove.css')]")), "sub-remove.css not found");
|
||||
$this->assertIdentical(0, count($this->xpath("//link[contains(@href, 'base-add.sub-remove.css')]")), "base-add.sub-remove.css not found");
|
||||
|
||||
// Verify that CSS files with the same name are loaded from both the base theme and subtheme.
|
||||
$this->assertIdentical(1, count($this->xpath("//link[contains(@href, '$base/samename.css')]")), "$base/samename.css found");
|
||||
$this->assertIdentical(1, count($this->xpath("//link[contains(@href, '$sub/samename.css')]")), "$sub/samename.css found");
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that changes to the info file are picked up.
|
||||
*/
|
||||
public function testChanges() {
|
||||
$this->themeHandler->install(array('test_theme'));
|
||||
$this->themeHandler->setDefault('test_theme');
|
||||
$this->themeManager->resetActiveTheme();
|
||||
|
||||
$active_theme = $this->themeManager->getActiveTheme();
|
||||
// Make sure we are not testing the wrong theme.
|
||||
$this->assertEqual('test_theme', $active_theme->getName());
|
||||
$this->assertEqual(['classy/base', 'test_theme/global-styling'], $active_theme->getLibraries());
|
||||
|
||||
// @see theme_test_system_info_alter()
|
||||
$this->state->set('theme_test.modify_info_files', TRUE);
|
||||
drupal_flush_all_caches();
|
||||
$active_theme = $this->themeManager->getActiveTheme();
|
||||
$this->assertEqual(['classy/base', 'test_theme/global-styling', 'core/backbone'], $active_theme->getLibraries());
|
||||
}
|
||||
|
||||
}
|
68
core/modules/system/src/Tests/Theme/ThemeSettingsTest.php
Normal file
68
core/modules/system/src/Tests/Theme/ThemeSettingsTest.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\ThemeSettingsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\Core\Config\InstallStorage;
|
||||
use Drupal\Core\Extension\ExtensionDiscovery;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests theme settings functionality.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class ThemeSettingsTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* List of discovered themes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $availableThemes;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
// Theme settings rely on System module's system.theme.global configuration.
|
||||
$this->installConfig(array('system'));
|
||||
|
||||
if (!isset($this->availableThemes)) {
|
||||
$discovery = new ExtensionDiscovery(\Drupal::root());
|
||||
$this->availableThemes = $discovery->scan('theme');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that $theme.settings are imported and used as default theme settings.
|
||||
*/
|
||||
function testDefaultConfig() {
|
||||
$name = 'test_basetheme';
|
||||
$path = $this->availableThemes[$name]->getPath();
|
||||
$this->assertTrue(file_exists("$path/" . InstallStorage::CONFIG_INSTALL_DIRECTORY . "/$name.settings.yml"));
|
||||
$this->container->get('theme_handler')->install(array($name));
|
||||
$this->assertIdentical(theme_get_setting('base', $name), 'only');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the $theme.settings default config file is optional.
|
||||
*/
|
||||
function testNoDefaultConfig() {
|
||||
$name = 'stark';
|
||||
$path = $this->availableThemes[$name]->getPath();
|
||||
$this->assertFalse(file_exists("$path/" . InstallStorage::CONFIG_INSTALL_DIRECTORY . "/$name.settings.yml"));
|
||||
$this->container->get('theme_handler')->install(array($name));
|
||||
$this->assertNotNull(theme_get_setting('features.favicon', $name));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\ThemeSuggestionsAlterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\Component\Utility\Xss;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests theme suggestion alter hooks.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class ThemeSuggestionsAlterTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('theme_handler')->install(array('test_theme'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that hooks to provide theme suggestions work.
|
||||
*/
|
||||
function testTemplateSuggestions() {
|
||||
$this->drupalGet('theme-test/suggestion-provided');
|
||||
$this->assertText('Template for testing suggestions provided by the module declaring the theme hook.');
|
||||
|
||||
// Install test_theme, it contains a template suggested by theme_test.module
|
||||
// in theme_test_theme_suggestions_theme_test_suggestion_provided().
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
|
||||
$this->drupalGet('theme-test/suggestion-provided');
|
||||
$this->assertText('Template overridden based on suggestion provided by the module declaring the theme hook.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests hook_theme_suggestions_alter().
|
||||
*/
|
||||
function testGeneralSuggestionsAlter() {
|
||||
$this->drupalGet('theme-test/general-suggestion-alter');
|
||||
$this->assertText('Original template for testing hook_theme_suggestions_alter().');
|
||||
|
||||
// Install test_theme and test that themes can alter template suggestions.
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
$this->drupalGet('theme-test/general-suggestion-alter');
|
||||
$this->assertText('Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_alter().');
|
||||
|
||||
// Enable the theme_suggestions_test module to test modules implementing
|
||||
// suggestions alter hooks.
|
||||
\Drupal::service('module_installer')->install(array('theme_suggestions_test'));
|
||||
$this->resetAll();
|
||||
$this->drupalGet('theme-test/general-suggestion-alter');
|
||||
$this->assertText('Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_alter().');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that theme suggestion alter hooks work for templates.
|
||||
*/
|
||||
function testTemplateSuggestionsAlter() {
|
||||
$this->drupalGet('theme-test/suggestion-alter');
|
||||
$this->assertText('Original template for testing hook_theme_suggestions_HOOK_alter().');
|
||||
|
||||
// Install test_theme and test that themes can alter template suggestions.
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
$this->drupalGet('theme-test/suggestion-alter');
|
||||
$this->assertText('Template overridden based on new theme suggestion provided by the test_theme theme via hook_theme_suggestions_HOOK_alter().');
|
||||
|
||||
// Enable the theme_suggestions_test module to test modules implementing
|
||||
// suggestions alter hooks.
|
||||
\Drupal::service('module_installer')->install(array('theme_suggestions_test'));
|
||||
$this->resetAll();
|
||||
$this->drupalGet('theme-test/suggestion-alter');
|
||||
$this->assertText('Template overridden based on new theme suggestion provided by a module via hook_theme_suggestions_HOOK_alter().');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that theme suggestion alter hooks work for specific theme calls.
|
||||
*/
|
||||
function testSpecificSuggestionsAlter() {
|
||||
// Test that the default template is rendered.
|
||||
$this->drupalGet('theme-test/specific-suggestion-alter');
|
||||
$this->assertText('Template for testing specific theme calls.');
|
||||
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
|
||||
// Test a specific theme call similar to '#theme' => 'node__article'.
|
||||
$this->drupalGet('theme-test/specific-suggestion-alter');
|
||||
$this->assertText('Template matching the specific theme call.');
|
||||
$this->assertText('theme_test_specific_suggestions__variant', 'Specific theme call is added to the suggestions array.');
|
||||
|
||||
// Ensure that the base hook is used to determine the suggestion alter hook.
|
||||
\Drupal::service('module_installer')->install(array('theme_suggestions_test'));
|
||||
$this->resetAll();
|
||||
$this->drupalGet('theme-test/specific-suggestion-alter');
|
||||
$this->assertText('Template overridden based on suggestion alter hook determined by the base hook.');
|
||||
$this->assertTrue(strpos($this->getRawContent(), 'theme_test_specific_suggestions__variant') < strpos($this->getRawContent(), 'theme_test_specific_suggestions__variant__foo'), 'Specific theme call is added to the suggestions array before the suggestions alter hook.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that theme suggestion alter hooks work for theme functions.
|
||||
*/
|
||||
function testThemeFunctionSuggestionsAlter() {
|
||||
$this->drupalGet('theme-test/function-suggestion-alter');
|
||||
$this->assertText('Original theme function.');
|
||||
|
||||
// Install test_theme and test that themes can alter theme suggestions.
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
$this->drupalGet('theme-test/function-suggestion-alter');
|
||||
$this->assertText('Theme function overridden based on new theme suggestion provided by the test_theme theme.');
|
||||
|
||||
// Enable the theme_suggestions_test module to test modules implementing
|
||||
// suggestions alter hooks.
|
||||
\Drupal::service('module_installer')->install(array('theme_suggestions_test'));
|
||||
$this->resetAll();
|
||||
$this->drupalGet('theme-test/function-suggestion-alter');
|
||||
$this->assertText('Theme function overridden based on new theme suggestion provided by a module.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that theme suggestion alter hooks work with theme hook includes.
|
||||
*/
|
||||
public function testSuggestionsAlterInclude() {
|
||||
// Check the original theme output.
|
||||
$this->drupalGet('theme-test/suggestion-alter-include');
|
||||
$this->assertText('Original function before altering theme suggestions.');
|
||||
|
||||
// Enable theme_suggestions_test module and make two requests to make sure
|
||||
// the include file is always loaded. The file will always be included for
|
||||
// the first request because the theme registry is being rebuilt.
|
||||
\Drupal::service('module_installer')->install(array('theme_suggestions_test'));
|
||||
$this->resetAll();
|
||||
$this->drupalGet('theme-test/suggestion-alter-include');
|
||||
$this->assertText('Function suggested via suggestion alter hook found in include file.', 'Include file loaded for initial request.');
|
||||
$this->drupalGet('theme-test/suggestion-alter-include');
|
||||
$this->assertText('Function suggested via suggestion alter hook found in include file.', 'Include file loaded for second request.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests execution order of theme suggestion alter hooks.
|
||||
*
|
||||
* hook_theme_suggestions_alter() should fire before
|
||||
* hook_theme_suggestions_HOOK_alter() within an extension (module or theme).
|
||||
*/
|
||||
function testExecutionOrder() {
|
||||
// Install our test theme and module.
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
\Drupal::service('module_installer')->install(array('theme_suggestions_test'));
|
||||
$this->resetAll();
|
||||
|
||||
// Send two requests so that we get all the messages we've set via
|
||||
// drupal_set_message().
|
||||
$this->drupalGet('theme-test/suggestion-alter');
|
||||
// Ensure that the order is first by extension, then for a given extension,
|
||||
// the hook-specific one after the generic one.
|
||||
$expected = array(
|
||||
'theme_suggestions_test_theme_suggestions_alter() executed.',
|
||||
'theme_suggestions_test_theme_suggestions_theme_test_suggestions_alter() executed.',
|
||||
'theme_test_theme_suggestions_alter() executed.',
|
||||
'theme_test_theme_suggestions_theme_test_suggestions_alter() executed.',
|
||||
'test_theme_theme_suggestions_alter() executed.',
|
||||
'test_theme_theme_suggestions_theme_test_suggestions_alter() executed.',
|
||||
);
|
||||
$content = preg_replace('/\s+/', ' ', Xss::filter($this->content, array()));
|
||||
$this->assert(strpos($content, implode(' ', $expected)) !== FALSE, 'Suggestion alter hooks executed in the expected order.');
|
||||
}
|
||||
|
||||
}
|
290
core/modules/system/src/Tests/Theme/ThemeTest.php
Normal file
290
core/modules/system/src/Tests/Theme/ThemeTest.php
Normal file
|
@ -0,0 +1,290 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\ThemeTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\test_theme\ThemeClass;
|
||||
use Symfony\Cmf\Component\Routing\RouteObjectInterface;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Route;
|
||||
|
||||
/**
|
||||
* Tests low-level theme functions.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class ThemeTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test', 'node');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('theme_handler')->install(array('test_theme'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test attribute merging.
|
||||
*
|
||||
* Render arrays that use a render element and templates (and hence call
|
||||
* template_preprocess()) must ensure the attributes at different occasions
|
||||
* are all merged correctly:
|
||||
* - $variables['attributes'] as passed in to _theme()
|
||||
* - the render element's #attributes
|
||||
* - any attributes set in the template's preprocessing function
|
||||
*/
|
||||
function testAttributeMerging() {
|
||||
$theme_test_render_element = array(
|
||||
'elements' => array(
|
||||
'#attributes' => array('data-foo' => 'bar'),
|
||||
),
|
||||
'attributes' => array(
|
||||
'id' => 'bazinga',
|
||||
),
|
||||
);
|
||||
$this->assertThemeOutput('theme_test_render_element', $theme_test_render_element, '<div id="bazinga" data-foo="bar" data-variables-are-preprocessed></div>' . "\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that _theme() returns expected data types.
|
||||
*/
|
||||
function testThemeDataTypes() {
|
||||
// theme_test_false is an implemented theme hook so \Drupal::theme() service should
|
||||
// return a string, even though the theme function itself can return anything.
|
||||
$foos = array('null' => NULL, 'false' => FALSE, 'integer' => 1, 'string' => 'foo');
|
||||
foreach ($foos as $type => $example) {
|
||||
$output = \Drupal::theme()->render('theme_test_foo', array('foo' => $example));
|
||||
$this->assertTrue(is_string($output), format_string('\Drupal::theme() returns a string for data type !type.', array('!type' => $type)));
|
||||
}
|
||||
|
||||
// suggestionnotimplemented is not an implemented theme hook so \Drupal::theme() service
|
||||
// should return FALSE instead of a string.
|
||||
$output = \Drupal::theme()->render(array('suggestionnotimplemented'), array());
|
||||
$this->assertIdentical($output, FALSE, '\Drupal::theme() returns FALSE when a hook suggestion is not implemented.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test function theme_get_suggestions() for SA-CORE-2009-003.
|
||||
*/
|
||||
function testThemeSuggestions() {
|
||||
// Set the front page as something random otherwise the CLI
|
||||
// test runner fails.
|
||||
$this->config('system.site')->set('page.front', '/nobody-home')->save();
|
||||
$args = array('node', '1', 'edit');
|
||||
$suggestions = theme_get_suggestions($args, 'page');
|
||||
$this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1', 'page__node__edit'), 'Found expected node edit page suggestions');
|
||||
// Check attack vectors.
|
||||
$args = array('node', '\\1');
|
||||
$suggestions = theme_get_suggestions($args, 'page');
|
||||
$this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), 'Removed invalid \\ from suggestions');
|
||||
$args = array('node', '1/');
|
||||
$suggestions = theme_get_suggestions($args, 'page');
|
||||
$this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), 'Removed invalid / from suggestions');
|
||||
$args = array('node', "1\0");
|
||||
$suggestions = theme_get_suggestions($args, 'page');
|
||||
$this->assertEqual($suggestions, array('page__node', 'page__node__%', 'page__node__1'), 'Removed invalid \\0 from suggestions');
|
||||
// Define path with hyphens to be used to generate suggestions.
|
||||
$args = array('node', '1', 'hyphen-path');
|
||||
$result = array('page__node', 'page__node__%', 'page__node__1', 'page__node__hyphen_path');
|
||||
$suggestions = theme_get_suggestions($args, 'page');
|
||||
$this->assertEqual($suggestions, $result, 'Found expected page suggestions for paths containing hyphens.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures preprocess functions run even for suggestion implementations.
|
||||
*
|
||||
* The theme hook used by this test has its base preprocess function in a
|
||||
* separate file, so this test also ensures that that file is correctly loaded
|
||||
* when needed.
|
||||
*/
|
||||
function testPreprocessForSuggestions() {
|
||||
// Test with both an unprimed and primed theme registry.
|
||||
drupal_theme_rebuild();
|
||||
for ($i = 0; $i < 2; $i++) {
|
||||
$this->drupalGet('theme-test/suggestion');
|
||||
$this->assertText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the priority of some theme negotiators.
|
||||
*/
|
||||
public function testNegotiatorPriorities() {
|
||||
$this->drupalGet('theme-test/priority');
|
||||
|
||||
// Ensure that the custom theme negotiator was not able to set the theme.
|
||||
|
||||
$this->assertNoText('Theme hook implementor=test_theme_theme_test__suggestion(). Foo=template_preprocess_theme_test', 'Theme hook suggestion ran with data available from a preprocess function for the base hook.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that non-HTML requests never initialize themes.
|
||||
*/
|
||||
public function testThemeOnNonHtmlRequest() {
|
||||
$this->drupalGet('theme-test/non-html');
|
||||
$json = Json::decode($this->getRawContent());
|
||||
$this->assertFalse($json['theme_initialized']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure page-front template suggestion is added when on front page.
|
||||
*/
|
||||
function testFrontPageThemeSuggestion() {
|
||||
// Set the current route to user.login because theme_get_suggestions() will
|
||||
// query it to see if we are on the front page.
|
||||
$request = Request::create('/user/login');
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_NAME, 'user.login');
|
||||
$request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('/user/login'));
|
||||
\Drupal::requestStack()->push($request);
|
||||
$this->config('system.site')->set('page.front', '/user/login')->save();
|
||||
$suggestions = theme_get_suggestions(array('user', 'login'), 'page');
|
||||
// Set it back to not annoy the batch runner.
|
||||
\Drupal::requestStack()->pop();
|
||||
$this->assertTrue(in_array('page__front', $suggestions), 'Front page template was suggested.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a theme's .info.yml file is able to override a module CSS file from being added to the page.
|
||||
*
|
||||
* @see test_theme.info.yml
|
||||
*/
|
||||
function testCSSOverride() {
|
||||
// Reuse the same page as in testPreprocessForSuggestions(). We're testing
|
||||
// what is output to the HTML HEAD based on what is in a theme's .info.yml
|
||||
// file, so it doesn't matter what page we get, as long as it is themed with
|
||||
// the test theme. First we test with CSS aggregation disabled.
|
||||
$config = $this->config('system.performance');
|
||||
$config->set('css.preprocess', 0);
|
||||
$config->save();
|
||||
$this->drupalGet('theme-test/suggestion');
|
||||
$this->assertNoText('system.module.css', 'The theme\'s .info.yml file is able to override a module CSS file from being added to the page.');
|
||||
|
||||
// Also test with aggregation enabled, simply ensuring no PHP errors are
|
||||
// triggered during drupal_build_css_cache() when a source file doesn't
|
||||
// exist. Then allow remaining tests to continue with aggregation disabled
|
||||
// by default.
|
||||
$config->set('css.preprocess', 1);
|
||||
$config->save();
|
||||
$this->drupalGet('theme-test/suggestion');
|
||||
$config->set('css.preprocess', 0);
|
||||
$config->save();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a themes template is overridable based on the 'template' filename.
|
||||
*/
|
||||
function testTemplateOverride() {
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
$this->drupalGet('theme-test/template-test');
|
||||
$this->assertText('Success: Template overridden.', 'Template overridden by defined \'template\' filename.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures a theme template can override a theme function.
|
||||
*/
|
||||
function testFunctionOverride() {
|
||||
$this->drupalGet('theme-test/function-template-overridden');
|
||||
$this->assertText('Success: Template overrides theme function.', 'Theme function overridden by test_theme template.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the listInfo() function.
|
||||
*/
|
||||
function testListThemes() {
|
||||
$theme_handler = $this->container->get('theme_handler');
|
||||
$theme_handler->install(array('test_subtheme'));
|
||||
$themes = $theme_handler->listInfo();
|
||||
|
||||
// Check if ThemeHandlerInterface::listInfo() retrieves enabled themes.
|
||||
$this->assertIdentical(1, $themes['test_theme']->status, 'Installed theme detected');
|
||||
|
||||
// Check if ThemeHandlerInterface::listInfo() returns disabled themes.
|
||||
// Check for base theme and subtheme lists.
|
||||
$base_theme_list = array('test_basetheme' => 'Theme test base theme');
|
||||
$sub_theme_list = array('test_subsubtheme' => 'Theme test subsubtheme', 'test_subtheme' => 'Theme test subtheme');
|
||||
|
||||
$this->assertIdentical($themes['test_basetheme']->sub_themes, $sub_theme_list, 'Base theme\'s object includes list of subthemes.');
|
||||
$this->assertIdentical($themes['test_subtheme']->base_themes, $base_theme_list, 'Subtheme\'s object includes list of base themes.');
|
||||
// Check for theme engine in subtheme.
|
||||
$this->assertIdentical($themes['test_subtheme']->engine, 'twig', 'Subtheme\'s object includes the theme engine.');
|
||||
// Check for theme engine prefix.
|
||||
$this->assertIdentical($themes['test_basetheme']->prefix, 'twig', 'Base theme\'s object includes the theme engine prefix.');
|
||||
$this->assertIdentical($themes['test_subtheme']->prefix, 'twig', 'Subtheme\'s object includes the theme engine prefix.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests child element rendering for 'render element' theme hooks.
|
||||
*/
|
||||
function testDrupalRenderChildren() {
|
||||
$element = array(
|
||||
'#theme' => 'theme_test_render_element_children',
|
||||
'child' => array(
|
||||
'#markup' => 'Foo',
|
||||
),
|
||||
);
|
||||
$this->assertThemeOutput('theme_test_render_element_children', $element, 'Foo', 'drupal_render() avoids #theme recursion loop when rendering a render element.');
|
||||
|
||||
$element = array(
|
||||
'#theme_wrappers' => array('theme_test_render_element_children'),
|
||||
'child' => array(
|
||||
'#markup' => 'Foo',
|
||||
),
|
||||
);
|
||||
$this->assertThemeOutput('theme_test_render_element_children', $element, 'Foo', 'drupal_render() avoids #theme_wrappers recursion loop when rendering a render element.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests theme can provide classes.
|
||||
*/
|
||||
function testClassLoading() {
|
||||
new ThemeClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests drupal_find_theme_templates().
|
||||
*/
|
||||
public function testFindThemeTemplates() {
|
||||
$registry = $this->container->get('theme.registry')->get();
|
||||
$templates = drupal_find_theme_templates($registry, '.html.twig', drupal_get_path('theme', 'test_theme'));
|
||||
$this->assertEqual($templates['node__1']['template'], 'node--1', 'Template node--1.tpl.twig was found in test_theme.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the page variable is not prematurely flattened.
|
||||
*
|
||||
* Some modules check the page array in template_preprocess_html(), so we
|
||||
* ensure that it has not been rendered prematurely.
|
||||
*/
|
||||
function testPreprocessHtml() {
|
||||
$this->drupalGet('');
|
||||
$attributes = $this->xpath('/html/body[@theme_test_page_variable="Page variable is an array."]');
|
||||
$this->assertTrue(count($attributes) == 1, 'In template_preprocess_html(), the page variable is still an array (not rendered yet).');
|
||||
$this->assertText('theme test page bottom markup', 'Modules are able to set the page bottom region.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that region attributes can be manipulated via preprocess functions.
|
||||
*/
|
||||
function testRegionClass() {
|
||||
\Drupal::service('module_installer')->install(array('block', 'theme_region_test'));
|
||||
|
||||
// Place a block.
|
||||
$this->drupalPlaceBlock('system_main_block');
|
||||
$this->drupalGet('');
|
||||
$elements = $this->cssSelect(".region-sidebar-first.new_class");
|
||||
$this->assertEqual(count($elements), 1, 'New class found.');
|
||||
}
|
||||
|
||||
}
|
88
core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
Normal file
88
core/modules/system/src/Tests/Theme/TwigDebugMarkupTest.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigDebugMarkupTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests for Twig debug markup.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TwigDebugMarkupTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test', 'node');
|
||||
|
||||
/**
|
||||
* Tests debug markup added to Twig template output.
|
||||
*/
|
||||
function testTwigDebugMarkup() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
$extension = twig_extension();
|
||||
\Drupal::service('theme_handler')->install(array('test_theme'));
|
||||
$this->config('system.theme')->set('default', 'test_theme')->save();
|
||||
$this->drupalCreateContentType(array('type' => 'page'));
|
||||
// Enable debug, rebuild the service container, and clear all caches.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['debug'] = TRUE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
$this->resetAll();
|
||||
|
||||
$cache = $this->container->get('theme.registry')->get();
|
||||
// Create array of Twig templates.
|
||||
$templates = drupal_find_theme_templates($cache, $extension, drupal_get_path('theme', 'test_theme'));
|
||||
$templates += drupal_find_theme_templates($cache, $extension, drupal_get_path('module', 'node'));
|
||||
|
||||
// Create a node and test different features of the debug markup.
|
||||
$node = $this->drupalCreateNode();
|
||||
$build = node_view($node);
|
||||
$output = $renderer->renderRoot($build);
|
||||
$this->assertTrue(strpos($output, '<!-- THEME DEBUG -->') !== FALSE, 'Twig debug markup found in theme output when debug is enabled.');
|
||||
$this->setRawContent($output);
|
||||
$this->assertTrue(strpos($output, "THEME HOOK: 'node'") !== FALSE, 'Theme call information found.');
|
||||
$this->assertTrue(strpos($output, '* node--1--full' . $extension . PHP_EOL . ' x node--1' . $extension . PHP_EOL . ' * node--page--full' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' * node--full' . $extension . PHP_EOL . ' * node' . $extension) !== FALSE, 'Suggested template files found in order and node ID specific template shown as current template.');
|
||||
$this->assertEscaped('node--<script type="text/javascript">alert(\'yo\');</script>');
|
||||
$template_filename = $templates['node__1']['path'] . '/' . $templates['node__1']['template'] . $extension;
|
||||
$this->assertTrue(strpos($output, "BEGIN OUTPUT from '$template_filename'") !== FALSE, 'Full path to current template file found.');
|
||||
|
||||
// Create another node and make sure the template suggestions shown in the
|
||||
// debug markup are correct.
|
||||
$node2 = $this->drupalCreateNode();
|
||||
$build = node_view($node2);
|
||||
$output = $renderer->renderRoot($build);
|
||||
$this->assertTrue(strpos($output, '* node--2--full' . $extension . PHP_EOL . ' * node--2' . $extension . PHP_EOL . ' * node--page--full' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' * node--full' . $extension . PHP_EOL . ' x node' . $extension) !== FALSE, 'Suggested template files found in order and base template shown as current template.');
|
||||
|
||||
// Create another node and make sure the template suggestions shown in the
|
||||
// debug markup are correct.
|
||||
$node3 = $this->drupalCreateNode();
|
||||
$build = array('#theme' => 'node__foo__bar');
|
||||
$build += node_view($node3);
|
||||
$output = $renderer->renderRoot($build);
|
||||
$this->assertTrue(strpos($output, "THEME HOOK: 'node__foo__bar'") !== FALSE, 'Theme call information found.');
|
||||
$this->assertTrue(strpos($output, '* node--foo--bar' . $extension . PHP_EOL . ' * node--foo' . $extension . PHP_EOL . ' * node--<script type="text/javascript">alert('yo');</script>' . $extension . PHP_EOL . ' * node--3--full' . $extension . PHP_EOL . ' * node--3' . $extension . PHP_EOL . ' * node--page--full' . $extension . PHP_EOL . ' * node--page' . $extension . PHP_EOL . ' * node--full' . $extension . PHP_EOL . ' x node' . $extension) !== FALSE, 'Suggested template files found in order and base template shown as current template.');
|
||||
|
||||
// Disable debug, rebuild the service container, and clear all caches.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['debug'] = FALSE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
$this->resetAll();
|
||||
|
||||
$build = node_view($node);
|
||||
$output = $renderer->renderRoot($build);
|
||||
$this->assertFalse(strpos($output, '<!-- THEME DEBUG -->') !== FALSE, 'Twig debug markup not found in theme output when debug is disabled.');
|
||||
}
|
||||
|
||||
}
|
87
core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php
Normal file
87
core/modules/system/src/Tests/Theme/TwigEnvironmentTest.php
Normal file
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigEnvironmentTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Site\Settings;
|
||||
use Drupal\simpletest\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests the twig environment.
|
||||
*
|
||||
* @see \Drupal\Core\Template\TwigEnvironment
|
||||
* @group Twig
|
||||
*/
|
||||
class TwigEnvironmentTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('system');
|
||||
|
||||
/**
|
||||
* Tests inline templates.
|
||||
*/
|
||||
public function testInlineTemplate() {
|
||||
/** @var \Drupal\Core\Render\RendererInterface $renderer */
|
||||
$renderer = $this->container->get('renderer');
|
||||
/** @var \Drupal\Core\Template\TwigEnvironment $environment */
|
||||
$environment = \Drupal::service('twig');
|
||||
$this->assertEqual($environment->renderInline('test-no-context'), 'test-no-context');
|
||||
$this->assertEqual($environment->renderInline('test-with-context {{ llama }}', array('llama' => 'muuh')), 'test-with-context muuh');
|
||||
|
||||
$element = array();
|
||||
$unsafe_string = '<script>alert(\'Danger! High voltage!\');</script>';
|
||||
$element['test'] = array(
|
||||
'#type' => 'inline_template',
|
||||
'#template' => 'test-with-context {{ unsafe_content }}',
|
||||
'#context' => array('unsafe_content' => $unsafe_string),
|
||||
);
|
||||
$this->assertEqual($renderer->renderRoot($element), 'test-with-context ' . SafeMarkup::checkPlain($unsafe_string));
|
||||
|
||||
// Enable twig_auto_reload and twig_debug.
|
||||
$settings = Settings::getAll();
|
||||
$settings['twig_debug'] = TRUE;
|
||||
$settings['twig_auto_reload'] = TRUE;
|
||||
|
||||
new Settings($settings);
|
||||
$this->container = $this->kernel->rebuildContainer();
|
||||
\Drupal::setContainer($this->container);
|
||||
|
||||
$element = array();
|
||||
$element['test'] = array(
|
||||
'#type' => 'inline_template',
|
||||
'#template' => 'test-with-context {{ llama }}',
|
||||
'#context' => array('llama' => 'muuh'),
|
||||
);
|
||||
$element_copy = $element;
|
||||
// Render it twice so that twig caching is triggered.
|
||||
$this->assertEqual($renderer->renderRoot($element), 'test-with-context muuh');
|
||||
$this->assertEqual($renderer->renderRoot($element_copy), 'test-with-context muuh');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that exceptions are thrown when a template is not found.
|
||||
*/
|
||||
public function testTemplateNotFoundException() {
|
||||
/** @var \Drupal\Core\Template\TwigEnvironment $environment */
|
||||
$environment = \Drupal::service('twig');
|
||||
|
||||
try {
|
||||
$environment->loadTemplate('this-template-does-not-exist.html.twig')->render(array());
|
||||
$this->fail('Did not throw an exception as expected.');
|
||||
}
|
||||
catch (\Twig_Error_Loader $e) {
|
||||
$this->assertTrue(strpos($e->getMessage(), 'Template "this-template-does-not-exist.html.twig" is not defined') === 0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
92
core/modules/system/src/Tests/Theme/TwigExtensionTest.php
Normal file
92
core/modules/system/src/Tests/Theme/TwigExtensionTest.php
Normal file
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigExtensionTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests Twig extensions.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TwigExtensionTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test', 'twig_extension_test');
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('theme_handler')->install(array('test_theme'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the provided Twig extension loads the service appropriately.
|
||||
*/
|
||||
function testTwigExtensionLoaded() {
|
||||
$twigService = \Drupal::service('twig');
|
||||
$ext = $twigService->getExtension('twig_extension_test.test_extension');
|
||||
$this->assertEqual(get_class($ext), 'Drupal\twig_extension_test\TwigExtension\TestExtension', 'TestExtension loaded successfully.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the Twig extension's filter produces expected output.
|
||||
*/
|
||||
function testTwigExtensionFilter() {
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
|
||||
$this->drupalGet('twig-extension-test/filter');
|
||||
$this->assertText('Every plant is not a mineral.', 'Success: String filtered.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that the Twig extension's function produces expected output.
|
||||
*/
|
||||
function testTwigExtensionFunction() {
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme')
|
||||
->save();
|
||||
|
||||
$this->drupalGet('twig-extension-test/function');
|
||||
$this->assertText('THE QUICK BROWN BOX JUMPS OVER THE LAZY DOG 123.', 'Success: Text converted to uppercase.');
|
||||
$this->assertText('the quick brown box jumps over the lazy dog 123.', 'Success: Text converted to lowercase.');
|
||||
$this->assertNoText('The Quick Brown Fox Jumps Over The Lazy Dog 123.', 'Success: No text left behind.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests output of integer and double 0 values of TwigExtension::escapeFilter().
|
||||
*
|
||||
* @see https://www.drupal.org/node/2417733
|
||||
*/
|
||||
public function testsRenderEscapedZeroValue() {
|
||||
/** @var \Drupal\Core\Template\TwigExtension $extension */
|
||||
$extension = \Drupal::service('twig.extension');
|
||||
/** @var \Drupal\Core\Template\TwigEnvironment $twig */
|
||||
$twig = \Drupal::service('twig');
|
||||
$this->assertIdentical($extension->escapeFilter($twig, 0), 0, 'TwigExtension::escapeFilter() returns zero correctly when provided as an integer.');
|
||||
$this->assertIdentical($extension->escapeFilter($twig, 0.0), 0, 'TwigExtension::escapeFilter() returns zero correctly when provided as a double.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests output of integer and double 0 values of TwigExtension->renderVar().
|
||||
*
|
||||
* @see https://www.drupal.org/node/2417733
|
||||
*/
|
||||
public function testsRenderZeroValue() {
|
||||
/** @var \Drupal\Core\Template\TwigExtension $extension */
|
||||
$extension = \Drupal::service('twig.extension');
|
||||
$this->assertIdentical($extension->renderVar(0), 0, 'TwigExtension::renderVar() renders zero correctly when provided as an integer.');
|
||||
$this->assertIdentical($extension->renderVar(0.0), 0, 'TwigExtension::renderVar() renders zero correctly when provided as a double.');
|
||||
}
|
||||
|
||||
}
|
129
core/modules/system/src/Tests/Theme/TwigFilterTest.php
Normal file
129
core/modules/system/src/Tests/Theme/TwigFilterTest.php
Normal file
|
@ -0,0 +1,129 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigFilterTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
|
||||
/**
|
||||
* Tests Drupal's Twig filters.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TwigFilterTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('twig_theme_test');
|
||||
|
||||
/**
|
||||
* Test Twig "without" filter.
|
||||
*/
|
||||
public function testTwigWithoutFilter() {
|
||||
$filter_test = array(
|
||||
'#theme' => 'twig_theme_test_filter',
|
||||
'#quote' => array(
|
||||
'content' => array('#markup' => 'You can only find truth with logic if you have already found truth without it.'),
|
||||
'author' => array('#markup' => 'Gilbert Keith Chesterton'),
|
||||
'date' => array('#markup' => '1874-1936'),
|
||||
),
|
||||
'#attributes' => array(
|
||||
'id' => 'quotes',
|
||||
'checked' => TRUE,
|
||||
'class' => array('red', 'green', 'blue'),
|
||||
),
|
||||
);
|
||||
$rendered = \Drupal::service('renderer')->renderRoot($filter_test);
|
||||
$this->setRawContent($rendered);
|
||||
|
||||
$elements = array(
|
||||
array(
|
||||
'expected' => '<div><strong>No author:</strong> You can only find truth with logic if you have already found truth without it.1874-1936.</div>',
|
||||
'message' => '"No author" was successfully rendered.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><strong>Complete quote after without:</strong> You can only find truth with logic if you have already found truth without it.Gilbert Keith Chesterton1874-1936.</div>',
|
||||
'message' => '"Complete quote after without" was successfully rendered.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><strong>Only author:</strong> Gilbert Keith Chesterton.</div>',
|
||||
'message' => '"Only author:" was successfully rendered.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><strong>No author or date:</strong> You can only find truth with logic if you have already found truth without it..</div>',
|
||||
'message' => '"No author or date" was successfully rendered.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><strong>Only date:</strong> 1874-1936.</div>',
|
||||
'message' => '"Only date" was successfully rendered.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><strong>Complete quote again for good measure:</strong> You can only find truth with logic if you have already found truth without it.Gilbert Keith Chesterton1874-1936.</div>',
|
||||
'message' => '"Complete quote again for good measure" was successfully rendered.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><strong>Marked-up:</strong>
|
||||
<blockquote>
|
||||
<p>You can only find truth with logic if you have already found truth without it.</p>
|
||||
<footer>
|
||||
– <cite><a href="#">Gilbert Keith Chesterton</a> <em>(1874-1936)</em></cite>
|
||||
</footer>
|
||||
</blockquote>',
|
||||
'message' => '"Marked-up quote" was successfully rendered.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><span id="quotes" checked class="red green blue">All attributes:</span></div>',
|
||||
'message' => 'All attributes printed.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><span class="red green blue" id="quotes" checked>Class attributes in front, remainder at the back:</span></div>',
|
||||
'message' => 'Class attributes printed in the front, the rest in the back.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><span id="quotes" checked data-class="red green blue">Class attributes in back, remainder at the front:</span></div>',
|
||||
'message' => 'Class attributes printed in the back, the rest in the front.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><span class="red green blue">Class attributes only:</span></div>',
|
||||
'message' => 'Class attributes only printed.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><span checked id="quotes" class="red green blue">Without boolean attribute.</span></div>',
|
||||
'message' => 'Boolean attribute printed in the front.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><span data-id="quotes" checked class="red green blue">Without string attribute.</span></div>',
|
||||
'message' => 'Without string attribute in the front.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><span checked>Without id and class attributes.</span></div>',
|
||||
'message' => 'Attributes printed without id and class attributes.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><span id="quotes" checked class="red green blue">All attributes again.</span></div>',
|
||||
'message' => 'All attributes printed again.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div id="quotes-here"><span class="gray-like-a-bunny bem__ized--top-feature" id="quotes-here">ID and class. Having the same ID twice is not valid markup but we want to make sure the filter doesn\'t use \Drupal\Component\Utility\Html::getUniqueId().</span></div>',
|
||||
'message' => 'Class and ID filtered.',
|
||||
),
|
||||
array(
|
||||
'expected' => '<div><strong>Rendered author string length:</strong> 24.</div>',
|
||||
'message' => 'Render filter string\'s length.',
|
||||
),
|
||||
);
|
||||
|
||||
foreach ($elements as $element) {
|
||||
$this->assertRaw($element['expected'], $element['message']);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
39
core/modules/system/src/Tests/Theme/TwigLoaderTest.php
Normal file
39
core/modules/system/src/Tests/Theme/TwigLoaderTest.php
Normal file
|
@ -0,0 +1,39 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigLoaderTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests adding Twig loaders.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TwigLoaderTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = ['twig_loader_test'];
|
||||
|
||||
/**
|
||||
* Tests adding an additional twig loader to the loader chain.
|
||||
*/
|
||||
public function testTwigLoaderAddition() {
|
||||
$environment = \Drupal::service('twig');
|
||||
|
||||
$template = $environment->loadTemplate('kittens');
|
||||
$this->assertEqual($template->render(array()), 'kittens', 'Passing "kittens" to the custom Twig loader returns "kittens".');
|
||||
|
||||
$template = $environment->loadTemplate('meow');
|
||||
$this->assertEqual($template->render(array()), 'cats', 'Passing something other than "kittens" to the custom Twig loader returns "cats".');
|
||||
}
|
||||
|
||||
}
|
67
core/modules/system/src/Tests/Theme/TwigNamespaceTest.php
Normal file
67
core/modules/system/src/Tests/Theme/TwigNamespaceTest.php
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigNamespaceTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests Twig namespaces.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TwigNamespaceTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('twig_theme_test', 'twig_namespace_a', 'twig_namespace_b', 'node');
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Template\TwigEnvironment
|
||||
*/
|
||||
protected $twig;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('theme_handler')->install(array('test_theme', 'bartik'));
|
||||
$this->twig = \Drupal::service('twig');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a value is a twig template.
|
||||
*/
|
||||
public function assertTwigTemplate($value, $message = '', $group = 'Other') {
|
||||
$this->assertTrue($value instanceof \Twig_Template, $message, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests template discovery using namespaces.
|
||||
*/
|
||||
public function testTemplateDiscovery() {
|
||||
// Tests resolving namespaced templates in modules.
|
||||
$this->assertTwigTemplate($this->twig->resolveTemplate('@node/node.html.twig'), 'Found node.html.twig in node module.');
|
||||
|
||||
// Tests resolving namespaced templates in themes.
|
||||
$this->assertTwigTemplate($this->twig->resolveTemplate('@bartik/page.html.twig'), 'Found page.html.twig in Bartik theme.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests template extension and includes using namespaces.
|
||||
*/
|
||||
public function testTwigNamespaces() {
|
||||
// Test twig @extends and @include in template files.
|
||||
$test = array('#theme' => 'twig_namespace_test');
|
||||
$this->setRawContent(\Drupal::service('renderer')->renderRoot($test));
|
||||
|
||||
$this->assertText('This line is from twig_namespace_a/templates/test.html.twig');
|
||||
$this->assertText('This line is from twig_namespace_b/templates/test.html.twig');
|
||||
}
|
||||
|
||||
}
|
56
core/modules/system/src/Tests/Theme/TwigRawTest.php
Normal file
56
core/modules/system/src/Tests/Theme/TwigRawTest.php
Normal file
|
@ -0,0 +1,56 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigRawTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests Twig 'raw' filter.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TwigRawTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('twig_theme_test');
|
||||
|
||||
/**
|
||||
* Tests the raw filter inside an autoescape tag.
|
||||
*/
|
||||
public function testAutoescapeRaw() {
|
||||
$test = array(
|
||||
'#theme' => 'twig_raw_test',
|
||||
'#script' => '<script>alert("This alert is real because I will put it through the raw filter!");</script>',
|
||||
);
|
||||
$rendered = \Drupal::service('renderer')->renderRoot($test);
|
||||
$this->setRawContent($rendered);
|
||||
$this->assertRaw('<script>alert("This alert is real because I will put it through the raw filter!");</script>');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests autoescaping of unsafe content.
|
||||
*
|
||||
* This is one of the most important tests in Drupal itself in terms of
|
||||
* security.
|
||||
*/
|
||||
public function testAutoescape() {
|
||||
$script = '<script>alert("This alert is unreal!");</script>';
|
||||
$build = [
|
||||
'#theme' => 'twig_autoescape_test',
|
||||
'#script' => $script,
|
||||
];
|
||||
$rendered = \Drupal::service('renderer')->renderRoot($build);
|
||||
$this->setRawContent($rendered);
|
||||
$this->assertEscaped($script);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigRegistryLoaderTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests Twig registry loader.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TwigRegistryLoaderTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('twig_theme_test', 'block');
|
||||
|
||||
/**
|
||||
* @var \Drupal\Core\Template\TwigEnvironment
|
||||
*/
|
||||
protected $twig;
|
||||
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
\Drupal::service('theme_handler')->install(array('test_theme_twig_registry_loader', 'test_theme_twig_registry_loader_theme', 'test_theme_twig_registry_loader_subtheme'));
|
||||
$this->twig = \Drupal::service('twig');
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if a value is a Twig template.
|
||||
*/
|
||||
public function assertTwigTemplate($value, $message = '', $group = 'Other') {
|
||||
$this->assertTrue($value instanceof \Twig_Template, $message, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests template discovery using the Drupal theme registry.
|
||||
*/
|
||||
public function testTemplateDiscovery() {
|
||||
$this->assertTwigTemplate($this->twig->resolveTemplate('block.html.twig'), 'Found block.html.twig in block module.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests template extension and includes using the Drupal theme registry.
|
||||
*/
|
||||
public function testTwigNamespaces() {
|
||||
// Test the module-provided extend and insert templates.
|
||||
$this->drupalGet('twig-theme-test/registry-loader');
|
||||
$this->assertText('This line is from twig_theme_test/templates/twig-registry-loader-test-extend.html.twig');
|
||||
$this->assertText('This line is from twig_theme_test/templates/twig-registry-loader-test-include.html.twig');
|
||||
|
||||
// Enable a theme that overrides the extend and insert templates to ensure
|
||||
// they are picked up by the registry loader.
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme_twig_registry_loader')
|
||||
->save();
|
||||
$this->drupalGet('twig-theme-test/registry-loader');
|
||||
$this->assertText('This line is from test_theme_twig_registry_loader/templates/twig-registry-loader-test-extend.html.twig');
|
||||
$this->assertText('This line is from test_theme_twig_registry_loader/templates/twig-registry-loader-test-include.html.twig');
|
||||
|
||||
// Enable overriding theme that overrides the extend and insert templates
|
||||
// from the base theme.
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme_twig_registry_loader_theme')
|
||||
->save();
|
||||
$this->drupalGet('twig-theme-test/registry-loader');
|
||||
$this->assertText('This line is from test_theme_twig_registry_loader_theme/templates/twig-registry-loader-test-extend.html.twig');
|
||||
$this->assertText('This line is from test_theme_twig_registry_loader_theme/templates/twig-registry-loader-test-include.html.twig');
|
||||
|
||||
// Enable a subtheme for the theme that doesn't have any overrides to make
|
||||
// sure that templates are being loaded from the first parent which has the
|
||||
// templates.
|
||||
$this->config('system.theme')
|
||||
->set('default', 'test_theme_twig_registry_loader_subtheme')
|
||||
->save();
|
||||
$this->drupalGet('twig-theme-test/registry-loader');
|
||||
$this->assertText('This line is from test_theme_twig_registry_loader_theme/templates/twig-registry-loader-test-extend.html.twig');
|
||||
$this->assertText('This line is from test_theme_twig_registry_loader_theme/templates/twig-registry-loader-test-include.html.twig');
|
||||
}
|
||||
|
||||
}
|
137
core/modules/system/src/Tests/Theme/TwigSettingsTest.php
Normal file
137
core/modules/system/src/Tests/Theme/TwigSettingsTest.php
Normal file
|
@ -0,0 +1,137 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigSettingsTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
use Drupal\Core\PhpStorage\PhpStorageFactory;
|
||||
|
||||
/**
|
||||
* Tests overriding Twig engine settings via settings.php.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TwigSettingsTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array('theme_test');
|
||||
|
||||
/**
|
||||
* Ensures Twig template auto reload setting can be overridden.
|
||||
*/
|
||||
function testTwigAutoReloadOverride() {
|
||||
// Enable auto reload and rebuild the service container.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['auto_reload'] = TRUE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Check isAutoReload() via the Twig service container.
|
||||
$this->assertTrue($this->container->get('twig')->isAutoReload(), 'Automatic reloading of Twig templates enabled.');
|
||||
|
||||
// Disable auto reload and check the service container again.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['auto_reload'] = FALSE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
|
||||
$this->assertFalse($this->container->get('twig')->isAutoReload(), 'Automatic reloading of Twig templates disabled.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures Twig engine debug setting can be overridden.
|
||||
*/
|
||||
function testTwigDebugOverride() {
|
||||
// Enable debug and rebuild the service container.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['debug'] = TRUE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Check isDebug() via the Twig service container.
|
||||
$this->assertTrue($this->container->get('twig')->isDebug(), 'Twig debug enabled.');
|
||||
$this->assertTrue($this->container->get('twig')->isAutoReload(), 'Twig automatic reloading is enabled when debug is enabled.');
|
||||
|
||||
// Override auto reload when debug is enabled.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['auto_reload'] = FALSE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
$this->assertFalse($this->container->get('twig')->isAutoReload(), 'Twig automatic reloading can be disabled when debug is enabled.');
|
||||
|
||||
// Disable debug and check the service container again.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['debug'] = FALSE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
|
||||
$this->assertFalse($this->container->get('twig')->isDebug(), 'Twig debug disabled.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures Twig template cache setting can be overridden.
|
||||
*/
|
||||
function testTwigCacheOverride() {
|
||||
$extension = twig_extension();
|
||||
$theme_handler = $this->container->get('theme_handler');
|
||||
$theme_handler->install(array('test_theme'));
|
||||
$theme_handler->setDefault('test_theme');
|
||||
|
||||
// The registry still works on theme globals, so set them here.
|
||||
\Drupal::theme()->setActiveTheme(\Drupal::service('theme.initialization')->getActiveThemeByName('test_theme'));
|
||||
|
||||
// Reset the theme registry, so that the new theme is used.
|
||||
$this->container->set('theme.registry', NULL);
|
||||
|
||||
// Load array of Twig templates.
|
||||
// reset() is necessary to invalidate caches tagged with 'theme_registry'.
|
||||
$registry = $this->container->get('theme.registry');
|
||||
$registry->reset();
|
||||
$templates = $registry->getRuntime();
|
||||
|
||||
// Get the template filename and the cache filename for
|
||||
// theme_test.template_test.html.twig.
|
||||
$info = $templates->get('theme_test_template_test');
|
||||
$template_filename = $info['path'] . '/' . $info['template'] . $extension;
|
||||
$cache_filename = $this->container->get('twig')->getCacheFilename($template_filename);
|
||||
|
||||
// Navigate to the page and make sure the template gets cached.
|
||||
$this->drupalGet('theme-test/template-test');
|
||||
$this->assertTrue(PhpStorageFactory::get('twig')->exists($cache_filename), 'Cached Twig template found.');
|
||||
|
||||
// Disable the Twig cache and rebuild the service container.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['cache'] = FALSE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
|
||||
// This should return false after rebuilding the service container.
|
||||
$new_cache_filename = $this->container->get('twig')->getCacheFilename($template_filename);
|
||||
$this->assertFalse($new_cache_filename, 'Twig environment does not return cache filename after caching is disabled.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests twig inline templates with auto_reload.
|
||||
*/
|
||||
public function testTwigInlineWithAutoReload() {
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['auto_reload'] = TRUE;
|
||||
$parameters['debug'] = TRUE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
|
||||
$this->drupalGet('theme-test/inline-template-test');
|
||||
$this->assertResponse(200);
|
||||
$this->drupalGet('theme-test/inline-template-test');
|
||||
$this->assertResponse(200);
|
||||
}
|
||||
|
||||
}
|
291
core/modules/system/src/Tests/Theme/TwigTransTest.php
Normal file
291
core/modules/system/src/Tests/Theme/TwigTransTest.php
Normal file
|
@ -0,0 +1,291 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @file
|
||||
* Contains \Drupal\system\Tests\Theme\TwigTransTest.
|
||||
*/
|
||||
|
||||
namespace Drupal\system\Tests\Theme;
|
||||
|
||||
use Drupal\Component\Utility\SafeMarkup;
|
||||
use Drupal\Core\Language\LanguageInterface;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\language\Entity\ConfigurableLanguage;
|
||||
use Drupal\simpletest\WebTestBase;
|
||||
|
||||
/**
|
||||
* Tests Twig "trans" tags.
|
||||
*
|
||||
* @group Theme
|
||||
*/
|
||||
class TwigTransTest extends WebTestBase {
|
||||
|
||||
/**
|
||||
* Modules to enable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public static $modules = array(
|
||||
'theme_test',
|
||||
'twig_theme_test',
|
||||
'locale',
|
||||
'language'
|
||||
);
|
||||
|
||||
/**
|
||||
* An administrative user for testing.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $adminUser;
|
||||
|
||||
/**
|
||||
* Custom languages.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $languages = array(
|
||||
'xx' => 'Lolspeak',
|
||||
'zz' => 'Lolspeak2',
|
||||
);
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp() {
|
||||
parent::setUp();
|
||||
|
||||
// Setup test_theme.
|
||||
\Drupal::service('theme_handler')->install(array('test_theme'));
|
||||
$this->config('system.theme')->set('default', 'test_theme')->save();
|
||||
|
||||
// Create and log in as admin.
|
||||
$this->adminUser = $this->drupalCreateUser(array(
|
||||
'administer languages',
|
||||
'access administration pages',
|
||||
'administer site configuration',
|
||||
'translate interface'
|
||||
));
|
||||
$this->drupalLogin($this->adminUser);
|
||||
|
||||
// Install languages.
|
||||
$this->installLanguages();
|
||||
|
||||
// Assign Lolspeak (xx) to be the default language.
|
||||
$this->config('system.site')->set('default_langcode', 'xx')->save();
|
||||
$this->rebuildContainer();
|
||||
|
||||
// Check that lolspeak is the default language for the site.
|
||||
$this->assertEqual(\Drupal::languageManager()->getDefaultLanguage()->getId(), 'xx', 'Lolspeak is the default language');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test Twig "trans" tags.
|
||||
*/
|
||||
public function testTwigTransTags() {
|
||||
// Run this once without and once with Twig debug because trans can work
|
||||
// differently depending on that setting.
|
||||
$this->drupalGet('twig-theme-test/trans', array('language' => \Drupal::languageManager()->getLanguage('xx')));
|
||||
$this->assertTwigTransTags();
|
||||
|
||||
// Enable debug, rebuild the service container, and clear all caches.
|
||||
$parameters = $this->container->getParameter('twig.config');
|
||||
$parameters['debug'] = TRUE;
|
||||
$this->setContainerParameter('twig.config', $parameters);
|
||||
$this->rebuildContainer();
|
||||
$this->resetAll();
|
||||
|
||||
$this->drupalGet('twig-theme-test/trans', array('language' => \Drupal::languageManager()->getLanguage('xx')));
|
||||
$this->assertTwigTransTags();
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts Twig trans tags.
|
||||
*/
|
||||
protected function assertTwigTransTags() {
|
||||
$this->assertText(
|
||||
'OH HAI SUNZ',
|
||||
'{% trans "Hello sun." %} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'O HAI SUNZZZZZZZ',
|
||||
'{% trans "Hello sun." with {"context": "Lolspeak"} %} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'O HERRO ERRRF.',
|
||||
'{{ "Hello Earth."|trans }} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'OH HAI TEH MUUN',
|
||||
'{% trans %}Hello moon.{% endtrans %} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'O HAI STARRRRR',
|
||||
'{% trans %} with {% plural count = 1 %} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'O HAI 2 STARZZZZ',
|
||||
'{% trans %} with {% plural count = 2 %} was successfully translated.'
|
||||
);
|
||||
|
||||
$this->assertRaw(
|
||||
'ESCAPEE: &"<>',
|
||||
'{{ token }} was successfully translated and prefixed with "@".'
|
||||
);
|
||||
|
||||
$this->assertRaw(
|
||||
'PAS-THRU: &"<>',
|
||||
'{{ token|passthrough }} was successfully translated and prefixed with "!".'
|
||||
);
|
||||
|
||||
$this->assertRaw(
|
||||
'PLAYSHOLDR: <em class="placeholder">&"<></em>',
|
||||
'{{ token|placeholder }} was successfully translated and prefixed with "%".'
|
||||
);
|
||||
|
||||
$this->assertRaw(
|
||||
'DIS complex token HAZ LENGTH OV: 3. IT CONTAYNZ: <em class="placeholder">12345</em> AN &"<>. LETS PAS TEH BAD TEXT THRU: &"<>.',
|
||||
'{{ complex.tokens }} were successfully translated with appropriate prefixes.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'I have context.',
|
||||
'{% trans %} with a context only msgid was excluded from translation.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'I HAZ KONTEX.',
|
||||
'{% trans with {"context": "Lolspeak"} %} was successfully translated with context.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'O HAI NU TXT.',
|
||||
'{% trans with {"langcode": "zz"} %} was successfully translated in specified language.'
|
||||
);
|
||||
|
||||
$this->assertText(
|
||||
'O HAI NU TXTZZZZ.',
|
||||
'{% trans with {"context": "Lolspeak", "langcode": "zz"} %} was successfully translated with context in specified language.'
|
||||
);
|
||||
// Makes sure https://www.drupal.org/node/2489024 doesn't happen without
|
||||
// twig debug.
|
||||
$this->assertNoText(pi(), 'Running php code inside a Twig trans is not possible.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function: install languages.
|
||||
*/
|
||||
protected function installLanguages() {
|
||||
foreach ($this->languages as $langcode => $name) {
|
||||
// Generate custom .po contents for the language.
|
||||
$contents = $this->poFileContents($langcode);
|
||||
if ($contents) {
|
||||
// Add test language for translation testing.
|
||||
$edit = array(
|
||||
'predefined_langcode' => 'custom',
|
||||
'langcode' => $langcode,
|
||||
'label' => $name,
|
||||
'direction' => LanguageInterface::DIRECTION_LTR,
|
||||
);
|
||||
|
||||
// Install the language in Drupal.
|
||||
$this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
|
||||
$this->assertRaw('"edit-languages-' . $langcode . '-weight"', 'Language code found.');
|
||||
|
||||
// Import the custom .po contents for the language.
|
||||
$filename = tempnam('temporary://', "po_") . '.po';
|
||||
file_put_contents($filename, $contents);
|
||||
$options = array(
|
||||
'files[file]' => $filename,
|
||||
'langcode' => $langcode,
|
||||
'customized' => TRUE,
|
||||
);
|
||||
$this->drupalPostForm('admin/config/regional/translate/import', $options, t('Import'));
|
||||
drupal_unlink($filename);
|
||||
}
|
||||
}
|
||||
$this->container->get('language_manager')->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a custom .po file for a specific test language.
|
||||
*
|
||||
* @param string $langcode
|
||||
* The langcode of the specified language.
|
||||
*
|
||||
* @return string|FALSE
|
||||
* The .po contents for the specified language or FALSE if none exists.
|
||||
*/
|
||||
protected function poFileContents($langcode) {
|
||||
if ($langcode === 'xx') {
|
||||
return <<< EOF
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Drupal 8\\n"
|
||||
"MIME-Version: 1.0\\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\\n"
|
||||
"Content-Transfer-Encoding: 8bit\\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
|
||||
|
||||
msgid "Hello sun."
|
||||
msgstr "OH HAI SUNZ"
|
||||
|
||||
msgctxt "Lolspeak"
|
||||
msgid "Hello sun."
|
||||
msgstr "O HAI SUNZZZZZZZ"
|
||||
|
||||
msgid "Hello Earth."
|
||||
msgstr "O HERRO ERRRF."
|
||||
|
||||
msgid "Hello moon."
|
||||
msgstr "OH HAI TEH MUUN"
|
||||
|
||||
msgid "Hello star."
|
||||
msgid_plural "Hello @count stars."
|
||||
msgstr[0] "O HAI STARRRRR"
|
||||
msgstr[1] "O HAI @count STARZZZZ"
|
||||
|
||||
msgid "Escaped: @string"
|
||||
msgstr "ESCAPEE: @string"
|
||||
|
||||
msgid "Pass-through: !string"
|
||||
msgstr "PAS-THRU: !string"
|
||||
|
||||
msgid "Placeholder: %string"
|
||||
msgstr "PLAYSHOLDR: %string"
|
||||
|
||||
msgid "This @token.name has a length of: @count. It contains: %token.numbers and @token.bad_text. Lets pass the bad text through: !token.bad_text."
|
||||
msgstr "DIS @token.name HAZ LENGTH OV: @count. IT CONTAYNZ: %token.numbers AN @token.bad_text. LETS PAS TEH BAD TEXT THRU: !token.bad_text."
|
||||
|
||||
msgctxt "Lolspeak"
|
||||
msgid "I have context."
|
||||
msgstr "I HAZ KONTEX."
|
||||
EOF;
|
||||
}
|
||||
else if ($langcode === 'zz') {
|
||||
return <<< EOF
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Drupal 8\\n"
|
||||
"MIME-Version: 1.0\\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\\n"
|
||||
"Content-Transfer-Encoding: 8bit\\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\\n"
|
||||
|
||||
msgid "Hello new text."
|
||||
msgstr "O HAI NU TXT."
|
||||
|
||||
msgctxt "Lolspeak"
|
||||
msgid "Hello new text."
|
||||
msgstr "O HAI NU TXTZZZZ."
|
||||
EOF;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
}
|
Reference in a new issue