3 namespace Drupal\KernelTests\Core\Theme;
5 use Drupal\Core\Path\CurrentPathStack;
6 use Drupal\Core\Path\PathMatcherInterface;
7 use Drupal\Core\Theme\Registry;
8 use Drupal\Core\Utility\ThemeRegistry;
9 use Drupal\KernelTests\KernelTestBase;
12 * Tests the behavior of the ThemeRegistry class.
16 class RegistryTest extends KernelTestBase {
23 public static $modules = ['theme_test', 'system'];
25 protected $profile = 'testing';
28 * Tests the behavior of the theme registry class.
30 public function testRaceCondition() {
31 // The theme registry is not marked as persistable in case we don't have a
33 \Drupal::request()->setMethod('GET');
34 $cid = 'test_theme_registry';
36 // Directly instantiate the theme registry, this will cause a base cache
37 // entry to be written in __construct().
38 $cache = \Drupal::cache();
39 $lock_backend = \Drupal::lock();
40 $registry = new ThemeRegistry($cid, $cache, $lock_backend, ['theme_registry'], $this->container->get('module_handler')->isLoaded());
42 $this->assertTrue(\Drupal::cache()->get($cid), 'Cache entry was created.');
44 // Trigger a cache miss for an offset.
45 $this->assertTrue($registry->get('theme_test_template_test'), 'Offset was returned correctly from the theme registry.');
46 // This will cause the ThemeRegistry class to write an updated version of
47 // the cache entry when it is destroyed, usually at the end of the request.
48 // Before that happens, manually delete the cache entry we created earlier
49 // so that the new entry is written from scratch.
50 \Drupal::cache()->delete($cid);
52 // Destroy the class so that it triggers a cache write for the offset.
53 $registry->destruct();
55 $this->assertTrue(\Drupal::cache()->get($cid), 'Cache entry was created.');
57 // Create a new instance of the class. Confirm that both the offset
58 // requested previously, and one that has not yet been requested are both
60 $registry = new ThemeRegistry($cid, $cache, $lock_backend, ['theme_registry'], $this->container->get('module_handler')->isLoaded());
61 $this->assertTrue($registry->get('theme_test_template_test'), 'Offset was returned correctly from the theme registry');
62 $this->assertTrue($registry->get('theme_test_template_test_2'), 'Offset was returned correctly from the theme registry');
66 * Tests the theme registry with multiple subthemes.
68 public function testMultipleSubThemes() {
69 $theme_handler = \Drupal::service('theme_handler');
70 $theme_handler->install(['test_basetheme', 'test_subtheme', 'test_subsubtheme']);
72 $registry_subsub_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_subsubtheme');
73 $registry_subsub_theme->setThemeManager(\Drupal::theme());
74 $registry_sub_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_subtheme');
75 $registry_sub_theme->setThemeManager(\Drupal::theme());
76 $registry_base_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_basetheme');
77 $registry_base_theme->setThemeManager(\Drupal::theme());
79 $preprocess_functions = $registry_subsub_theme->get()['theme_test_template_test']['preprocess functions'];
80 $this->assertIdentical([
81 'template_preprocess',
82 'test_basetheme_preprocess_theme_test_template_test',
83 'test_subtheme_preprocess_theme_test_template_test',
84 'test_subsubtheme_preprocess_theme_test_template_test',
85 ], $preprocess_functions);
87 $preprocess_functions = $registry_sub_theme->get()['theme_test_template_test']['preprocess functions'];
88 $this->assertIdentical([
89 'template_preprocess',
90 'test_basetheme_preprocess_theme_test_template_test',
91 'test_subtheme_preprocess_theme_test_template_test',
92 ], $preprocess_functions);
94 $preprocess_functions = $registry_base_theme->get()['theme_test_template_test']['preprocess functions'];
95 $this->assertIdentical([
96 'template_preprocess',
97 'test_basetheme_preprocess_theme_test_template_test',
98 ], $preprocess_functions);
100 $preprocess_functions = $registry_base_theme->get()['theme_test_function_suggestions']['preprocess functions'];
101 $this->assertIdentical([
102 'template_preprocess_theme_test_function_suggestions',
103 'test_basetheme_preprocess_theme_test_function_suggestions',
104 ], $preprocess_functions, "Theme functions don't have template_preprocess but do have template_preprocess_HOOK");
108 * Tests the theme registry with suggestions.
110 public function testSuggestionPreprocessFunctions() {
111 $theme_handler = \Drupal::service('theme_handler');
112 $theme_handler->install(['test_theme']);
114 $registry_theme = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_theme');
115 $registry_theme->setThemeManager(\Drupal::theme());
117 $suggestions = ['__kitten', '__flamingo'];
118 $expected_preprocess_functions = [
119 'template_preprocess',
120 'theme_test_preprocess_theme_test_preprocess_suggestions',
123 $hook = 'theme_test_preprocess_suggestions';
125 $hook .= "$suggestion";
126 $expected_preprocess_functions[] = "test_theme_preprocess_$hook";
127 $preprocess_functions = $registry_theme->get()[$hook]['preprocess functions'];
128 $this->assertIdentical($expected_preprocess_functions, $preprocess_functions, "$hook has correct preprocess functions.");
129 } while ($suggestion = array_shift($suggestions));
131 $expected_preprocess_functions = [
132 'template_preprocess',
133 'theme_test_preprocess_theme_test_preprocess_suggestions',
134 'test_theme_preprocess_theme_test_preprocess_suggestions',
135 'test_theme_preprocess_theme_test_preprocess_suggestions__kitten',
138 $preprocess_functions = $registry_theme->get()['theme_test_preprocess_suggestions__kitten__meerkat']['preprocess functions'];
139 $this->assertIdentical($expected_preprocess_functions, $preprocess_functions, 'Suggestion implemented as a function correctly inherits preprocess functions.');
141 $preprocess_functions = $registry_theme->get()['theme_test_preprocess_suggestions__kitten__bearcat']['preprocess functions'];
142 $this->assertIdentical($expected_preprocess_functions, $preprocess_functions, 'Suggestion implemented as a template correctly inherits preprocess functions.');
144 $this->assertTrue(isset($registry_theme->get()['theme_test_preprocess_suggestions__kitten__meerkat__tarsier__moose']), 'Preprocess function with an unimplemented lower-level suggestion is added to the registry.');
148 * Tests that the theme registry can be altered by themes.
150 public function testThemeRegistryAlterByTheme() {
152 /** @var \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler */
153 $theme_handler = \Drupal::service('theme_handler');
154 $theme_handler->install(['test_theme']);
155 $this->config('system.theme')->set('default', 'test_theme')->save();
157 $registry = new Registry(\Drupal::root(), \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $theme_handler, \Drupal::service('theme.initialization'), 'test_theme');
158 $registry->setThemeManager(\Drupal::theme());
159 $this->assertEqual('value', $registry->get()['theme_test_template_test']['variables']['additional']);
163 * Tests front node theme suggestion generation.
165 public function testThemeSuggestions() {
166 // Mock the current page as the front page.
167 /** @var PathMatcherInterface $path_matcher */
168 $path_matcher = $this->prophesize(PathMatcherInterface::class);
169 $path_matcher->isFrontPage()->willReturn(TRUE);
170 $this->container->set('path.matcher', $path_matcher->reveal());
171 /** @var CurrentPathStack $path_matcher */
172 $path_current = $this->prophesize(CurrentPathStack::class);
173 $path_current->getPath()->willReturn('/node/1');
174 $this->container->set('path.current', $path_current->reveal());
176 // Check suggestions provided through hook_theme_suggestions_html().
177 $suggestions = \Drupal::moduleHandler()->invokeAll('theme_suggestions_html', [[]]);
183 ], $suggestions, 'Found expected html node suggestions.');
185 // Check suggestions provided through hook_theme_suggestions_page().
186 $suggestions = \Drupal::moduleHandler()->invokeAll('theme_suggestions_page', [[]]);
192 ], $suggestions, 'Found expected page node suggestions.');