5 * Contains \Drupal\Tests\Core\Extension\ThemeHandlerTest.
8 namespace Drupal\Tests\Core\Extension;
10 use Drupal\Core\Cache\MemoryBackend;
11 use Drupal\Core\Extension\Extension;
12 use Drupal\Core\Extension\InfoParser;
13 use Drupal\Core\Extension\ThemeHandler;
14 use Drupal\Core\KeyValueStore\KeyValueMemoryFactory;
15 use Drupal\Core\Lock\NullLockBackend;
16 use Drupal\Core\State\State;
17 use Drupal\Tests\UnitTestCase;
20 * @coversDefaultClass \Drupal\Core\Extension\ThemeHandler
23 class ThemeHandlerTest extends UnitTestCase {
26 * The mocked info parser.
28 * @var \Drupal\Core\Extension\InfoParserInterface|\PHPUnit_Framework_MockObject_MockObject
30 protected $infoParser;
33 * The mocked state backend.
35 * @var \Drupal\Core\State\StateInterface|\PHPUnit_Framework_MockObject_MockObject
40 * The mocked config factory.
42 * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
44 protected $configFactory;
47 * The mocked module handler.
49 * @var \Drupal\Core\Extension\ModuleHandlerInterface|\PHPUnit_Framework_MockObject_MockObject
51 protected $moduleHandler;
54 * The extension discovery.
56 * @var \Drupal\Core\Extension\ExtensionDiscovery|\PHPUnit_Framework_MockObject_MockObject
58 protected $extensionDiscovery;
61 * The tested theme handler.
63 * @var \Drupal\Core\Extension\ThemeHandler|\Drupal\Tests\Core\Extension\StubThemeHandler
65 protected $themeHandler;
70 protected function setUp() {
73 $this->configFactory = $this->getConfigFactoryStub([
82 $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
83 $this->state = new State(new KeyValueMemoryFactory(), new MemoryBackend('test'), new NullLockBackend());
84 $this->infoParser = $this->getMock('Drupal\Core\Extension\InfoParserInterface');
85 $this->extensionDiscovery = $this->getMockBuilder('Drupal\Core\Extension\ExtensionDiscovery')
86 ->disableOriginalConstructor()
88 $this->themeHandler = new StubThemeHandler($this->root, $this->configFactory, $this->moduleHandler, $this->state, $this->infoParser, $this->extensionDiscovery);
90 $cache_tags_invalidator = $this->getMock('Drupal\Core\Cache\CacheTagsInvalidatorInterface');
91 $this->getContainerWithCacheTagsInvalidator($cache_tags_invalidator);
95 * Tests rebuilding the theme data.
97 * @see \Drupal\Core\Extension\ThemeHandler::rebuildThemeData()
99 public function testRebuildThemeData() {
100 $this->extensionDiscovery->expects($this->at(0))
103 ->will($this->returnValue([
104 'seven' => new Extension($this->root, 'theme', $this->root . '/core/themes/seven/seven.info.yml', 'seven.theme'),
106 $this->extensionDiscovery->expects($this->at(1))
108 ->with('theme_engine')
109 ->will($this->returnValue([
110 'twig' => new Extension($this->root, 'theme_engine', $this->root . '/core/themes/engines/twig/twig.info.yml', 'twig.engine'),
112 $this->infoParser->expects($this->once())
114 ->with($this->root . '/core/themes/seven/seven.info.yml')
115 ->will($this->returnCallback(function ($file) {
116 $info_parser = new InfoParser();
117 return $info_parser->parse($file);
119 $this->moduleHandler->expects($this->once())
120 ->method('buildModuleDependencies')
121 ->will($this->returnArgument(0));
123 $this->moduleHandler->expects($this->once())
126 $theme_data = $this->themeHandler->rebuildThemeData();
127 $this->assertCount(1, $theme_data);
128 $info = $theme_data['seven'];
130 // Ensure some basic properties.
131 $this->assertInstanceOf('Drupal\Core\Extension\Extension', $info);
132 $this->assertEquals('seven', $info->getName());
133 $this->assertEquals($this->root . '/core/themes/seven/seven.info.yml', $info->getPathname());
134 $this->assertEquals($this->root . '/core/themes/seven/seven.theme', $info->getExtensionPathname());
135 $this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info->owner);
136 $this->assertEquals('twig', $info->prefix);
138 $this->assertEquals('twig', $info->info['engine']);
139 $this->assertEquals(['seven/global-styling'], $info->info['libraries']);
143 * Tests empty libraries in theme.info.yml file.
145 public function testThemeLibrariesEmpty() {
146 $theme = new Extension($this->root, 'theme', '/core/modules/system/tests/themes/test_theme_libraries_empty', 'test_theme_libraries_empty.info.yml');
148 $this->themeHandler->addTheme($theme);
149 $this->assertTrue(TRUE, 'Empty libraries key in theme.info.yml does not cause PHP warning');
151 catch (\Exception $e) {
152 $this->fail('Empty libraries key in theme.info.yml causes PHP warning.');
157 * Tests rebuild the theme data with theme parents.
159 public function testRebuildThemeDataWithThemeParents() {
160 $this->extensionDiscovery->expects($this->at(0))
163 ->will($this->returnValue([
164 'test_subtheme' => new Extension($this->root, 'theme', $this->root . '/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml', 'test_subtheme.info.yml'),
165 'test_basetheme' => new Extension($this->root, 'theme', $this->root . '/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml', 'test_basetheme.info.yml'),
167 $this->extensionDiscovery->expects($this->at(1))
169 ->with('theme_engine')
170 ->will($this->returnValue([
171 'twig' => new Extension($this->root, 'theme_engine', $this->root . '/core/themes/engines/twig/twig.info.yml', 'twig.engine'),
173 $this->infoParser->expects($this->at(0))
175 ->with($this->root . '/core/modules/system/tests/themes/test_subtheme/test_subtheme.info.yml')
176 ->will($this->returnCallback(function ($file) {
177 $info_parser = new InfoParser();
178 return $info_parser->parse($file);
180 $this->infoParser->expects($this->at(1))
182 ->with($this->root . '/core/modules/system/tests/themes/test_basetheme/test_basetheme.info.yml')
183 ->will($this->returnCallback(function ($file) {
184 $info_parser = new InfoParser();
185 return $info_parser->parse($file);
187 $this->moduleHandler->expects($this->once())
188 ->method('buildModuleDependencies')
189 ->will($this->returnArgument(0));
191 $theme_data = $this->themeHandler->rebuildThemeData();
192 $this->assertCount(2, $theme_data);
194 $info_basetheme = $theme_data['test_basetheme'];
195 $info_subtheme = $theme_data['test_subtheme'];
197 // Ensure some basic properties.
198 $this->assertInstanceOf('Drupal\Core\Extension\Extension', $info_basetheme);
199 $this->assertEquals('test_basetheme', $info_basetheme->getName());
200 $this->assertInstanceOf('Drupal\Core\Extension\Extension', $info_subtheme);
201 $this->assertEquals('test_subtheme', $info_subtheme->getName());
203 // Test the parent/child-theme properties.
204 $info_subtheme->info['base theme'] = 'test_basetheme';
205 $info_basetheme->sub_themes = ['test_subtheme'];
207 $this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info_basetheme->owner);
208 $this->assertEquals('twig', $info_basetheme->prefix);
209 $this->assertEquals($this->root . '/core/themes/engines/twig/twig.engine', $info_subtheme->owner);
210 $this->assertEquals('twig', $info_subtheme->prefix);
214 * Tests getting the base themes for a set a defines themes.
216 * @param array $themes
217 * An array of available themes, keyed by the theme name.
218 * @param string $theme
219 * The theme name to find all its base themes.
220 * @param array $expected
221 * The expected base themes.
223 * @dataProvider providerTestGetBaseThemes
225 public function testGetBaseThemes(array $themes, $theme, array $expected) {
226 $base_themes = $this->themeHandler->getBaseThemes($themes, $theme);
227 $this->assertEquals($expected, $base_themes);
231 * Provides test data for testGetBaseThemes.
234 * An array of theme test data.
236 public function providerTestGetBaseThemes() {
239 // Tests a theme without any base theme.
241 $themes['test_1'] = (object) [
247 $data[] = [$themes, 'test_1', []];
249 // Tests a theme with a non existing base theme.
251 $themes['test_1'] = (object) [
255 'base theme' => 'test_2',
258 $data[] = [$themes, 'test_1', ['test_2' => NULL]];
260 // Tests a theme with a single existing base theme.
262 $themes['test_1'] = (object) [
266 'base theme' => 'test_2',
269 $themes['test_2'] = (object) [
275 $data[] = [$themes, 'test_1', ['test_2' => 'test_2']];
277 // Tests a theme with multiple base themes.
279 $themes['test_1'] = (object) [
283 'base theme' => 'test_2',
286 $themes['test_2'] = (object) [
290 'base theme' => 'test_3',
293 $themes['test_3'] = (object) [
302 ['test_2' => 'test_2', 'test_3' => 'test_3'],
311 * Extends the default theme handler to mock some drupal_ methods.
313 class StubThemeHandler extends ThemeHandler {
316 * Whether the CSS cache was cleared.
320 protected $clearedCssCache;
323 * Whether the registry should be rebuilt.
327 protected $registryRebuild;
330 * A list of themes keyed by name.
334 protected $systemList;
339 protected function clearCssCache() {
340 $this->clearedCssCache = TRUE;
346 protected function themeRegistryRebuild() {
347 $this->registryRebuild = TRUE;
353 protected function systemThemeList() {
354 return $this->systemList;
360 protected function systemListReset() {
365 if (!defined('DRUPAL_EXTENSION_NAME_MAX_LENGTH')) {
366 define('DRUPAL_EXTENSION_NAME_MAX_LENGTH', 50);
368 if (!defined('DRUPAL_PHP_FUNCTION_PATTERN')) {
369 define('DRUPAL_PHP_FUNCTION_PATTERN', '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*');
371 if (!defined('DRUPAL_MINIMUM_PHP')) {
372 define('DRUPAL_MINIMUM_PHP', '5.3.10');