5 * Contains \Drupal\Tests\Core\Extension\ThemeHandlerTest.
8 namespace Drupal\Tests\Core\Extension;
10 use Drupal\Core\Extension\Extension;
11 use Drupal\Core\Extension\InfoParser;
12 use Drupal\Core\Extension\ThemeHandler;
13 use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
14 use Drupal\Core\State\State;
15 use Drupal\Tests\UnitTestCase;
18 * @coversDefaultClass \Drupal\Core\Extension\ThemeHandler
21 class ThemeHandlerTest extends UnitTestCase {
24 * The mocked info parser.
26 * @var \Drupal\Core\Extension\InfoParserInterface|\PHPUnit_Framework_MockObject_MockObject
28 protected $infoParser;
31 * The mocked state backend.
33 * @var \Drupal\Core\State\StateInterface|\PHPUnit_Framework_MockObject_MockObject
38 * The mocked config factory.
40 * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
42 protected $configFactory;
45 * The mocked module handler.
47 * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
49 protected $moduleHandler;
52 * The extension discovery.
54 * @var \Drupal\Core\Extension\ExtensionDiscovery|\PHPUnit_Framework_MockObject_MockObject
56 protected $extensionDiscovery;
59 * The tested theme handler.
61 * @var \Drupal\Core\Extension\ThemeHandler|\Drupal\Tests\Core\Extension\StubThemeHandler
63 protected $themeHandler;
68 protected function setUp() {
71 $this->configFactory = $this->getConfigFactoryStub([
80 $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
81 $this->state = new State(new KeyValueMemoryFactory());
82 $this->infoParser = $this->getMock('Drupal\Core\Extension\InfoParserInterface');
83 $this->extensionDiscovery = $this->getMockBuilder('Drupal\Core\Extension\ExtensionDiscovery')
84 ->disableOriginalConstructor()
86 $this->themeHandler = new StubThemeHandler($this->root, $this->configFactory, $this->moduleHandler, $this->state, $this->infoParser, $this->extensionDiscovery);
88 $cache_tags_invalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
89 $this->getContainerWithCacheTagsInvalidator($cache_tags_invalidator);
93 * Tests rebuilding the theme data.
95 * @see \Drupal\Core\Extension\ThemeHandler::rebuildThemeData()
97 public function testRebuildThemeData() {
98 $this->extensionDiscovery->expects($this->at(0))
101 ->will($this->returnValue([
102 'seven' => new Extension($this->root, 'theme', $this->root . '/core/themes/seven/seven.info.yml', 'seven.theme'),
104 $this->extensionDiscovery->expects($this->at(1))
106 ->with('theme_engine')
107 ->will($this->returnValue([
108 'twig' => new Extension($this->root, 'theme_engine', $this->root . '/core/themes/engines/twig/twig.info.yml', 'twig.engine'),
110 $this->infoParser->expects($this->once())
112 ->with($this->root . '/core/themes/seven/seven.info.yml')
113 ->will($this->returnCallback(function ($file) {
114 $info_parser = new InfoParser();
115 return $info_parser->parse($file);
117 $this->moduleHandler->expects($this->once())
118 ->method('buildModuleDependencies')
119 ->will($this->returnArgument(0));
121 $this->moduleHandler->expects($this->once())
124 $theme_data = $this->themeHandler->rebuildThemeData();
125 $this->assertCount(1, $theme_data);
126 $info = $theme_data['seven'];
128 // Ensure some basic properties.
129 $this->assertInstanceOf('Drupal\Core\Extension\Extension', $info);
130 $this->assertEquals('seven', $info->getName());
131 $this->assertEquals($this->root . '/core/themes/seven/seven.info.yml', $info->getPathname());
132 $this->assertEquals($this->root . '/core/themes/seven/seven.theme', $info->getExtensionPathname());
133 $this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info->owner);
134 $this->assertEquals('twig', $info->prefix);
136 $this->assertEquals('twig', $info->info['engine']);
137 $this->assertEquals(['seven/global-styling'], $info->info['libraries']);
141 * Tests empty libraries in theme.info.yml file.
143 public function testThemeLibrariesEmpty() {
144 $theme = new Extension($this->root, 'theme', '/core/modules/system/tests/themes/test_theme_libraries_empty', 'test_theme_libraries_empty.info.yml');
146 $this->themeHandler->addTheme($theme);
147 $this->assertTrue(TRUE, 'Empty libraries key in theme.info.yml does not cause PHP warning');
149 catch (\Exception $e) {
150 $this->fail('Empty libraries key in theme.info.yml causes PHP warning.');
155 * Tests rebuild the theme data with theme parents.
157 public function testRebuildThemeDataWithThemeParents() {
158 $this->extensionDiscovery->expects($this->at(0))
161 ->will($this->returnValue([
162 'test_subtheme' => new Extension($this->root, 'theme', $this->root . '/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml', 'test_subtheme.info.yml'),
163 'test_basetheme' => new Extension($this->root, 'theme', $this->root . '/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml', 'test_basetheme.info.yml'),
165 $this->extensionDiscovery->expects($this->at(1))
167 ->with('theme_engine')
168 ->will($this->returnValue([
169 'twig' => new Extension($this->root, 'theme_engine', $this->root . '/core/themes/engines/twig/twig.info.yml', 'twig.engine'),
171 $this->infoParser->expects($this->at(0))
173 ->with($this->root . '/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml')
174 ->will($this->returnCallback(function ($file) {
175 $info_parser = new InfoParser();
176 return $info_parser->parse($file);
178 $this->infoParser->expects($this->at(1))
180 ->with($this->root . '/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml')
181 ->will($this->returnCallback(function ($file) {
182 $info_parser = new InfoParser();
183 return $info_parser->parse($file);
185 $this->moduleHandler->expects($this->once())
186 ->method('buildModuleDependencies')
187 ->will($this->returnArgument(0));
189 $theme_data = $this->themeHandler->rebuildThemeData();
190 $this->assertCount(2, $theme_data);
192 $info_basetheme = $theme_data['test_basetheme'];
193 $info_subtheme = $theme_data['test_subtheme'];
195 // Ensure some basic properties.
196 $this->assertInstanceOf('Drupal\Core\Extension\Extension', $info_basetheme);
197 $this->assertEquals('test_basetheme', $info_basetheme->getName());
198 $this->assertInstanceOf('Drupal\Core\Extension\Extension', $info_subtheme);
199 $this->assertEquals('test_subtheme', $info_subtheme->getName());
201 // Test the parent/child-theme properties.
202 $info_subtheme->info['base theme'] = 'test_basetheme';
203 $info_basetheme->sub_themes = ['test_subtheme'];
205 $this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info_basetheme->owner);
206 $this->assertEquals('twig', $info_basetheme->prefix);
207 $this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info_subtheme->owner);
208 $this->assertEquals('twig', $info_subtheme->prefix);
212 * Tests getting the base themes for a set a defines themes.
214 * @param array $themes
215 * An array of available themes, keyed by the theme name.
216 * @param string $theme
217 * The theme name to find all its base themes.
218 * @param array $expected
219 * The expected base themes.
221 * @dataProvider providerTestGetBaseThemes
223 public function testGetBaseThemes(array $themes, $theme, array $expected) {
224 $base_themes = $this->themeHandler->getBaseThemes($themes, $theme);
225 $this->assertEquals($expected, $base_themes);
229 * Provides test data for testGetBaseThemes.
232 * An array of theme test data.
234 public function providerTestGetBaseThemes() {
237 // Tests a theme without any base theme.
239 $themes['test_1'] = (object) [
245 $data[] = [$themes, 'test_1', []];
247 // Tests a theme with a non existing base theme.
249 $themes['test_1'] = (object) [
253 'base theme' => 'test_2',
256 $data[] = [$themes, 'test_1', ['test_2' => NULL]];
258 // Tests a theme with a single existing base theme.
260 $themes['test_1'] = (object) [
264 'base theme' => 'test_2',
267 $themes['test_2'] = (object) [
273 $data[] = [$themes, 'test_1', ['test_2' => 'test_2']];
275 // Tests a theme with multiple base themes.
277 $themes['test_1'] = (object) [
281 'base theme' => 'test_2',
284 $themes['test_2'] = (object) [
288 'base theme' => 'test_3',
291 $themes['test_3'] = (object) [
300 ['test_2' => 'test_2', 'test_3' => 'test_3'],
309 * Extends the default theme handler to mock some drupal_ methods.
311 class StubThemeHandler extends ThemeHandler {
314 * Whether the CSS cache was cleared.
318 protected $clearedCssCache;
321 * Whether the registry should be rebuilt.
325 protected $registryRebuild;
328 * A list of themes keyed by name.
332 protected $systemList;
337 protected function clearCssCache() {
338 $this->clearedCssCache = TRUE;
344 protected function themeRegistryRebuild() {
345 $this->registryRebuild = TRUE;
351 protected function systemThemeList() {
352 return $this->systemList;
358 protected function systemListReset() {
363 if (!defined('DRUPAL_EXTENSION_NAME_MAX_LENGTH')) {
364 define('DRUPAL_EXTENSION_NAME_MAX_LENGTH', 50);
366 if (!defined('DRUPAL_PHP_FUNCTION_PATTERN')) {
367 define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*');
369 if (!defined('DRUPAL_MINIMUM_PHP')) {
370 define('DRUPAL_MINIMUM_PHP', '5.3.10');