ThemesNotUsingClassyTemplatesTest.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <?php
  2. namespace Drupal\KernelTests\Core\Theme;
  3. use Drupal\Core\Theme\Registry;
  4. use Drupal\KernelTests\KernelTestBase;
  5. /**
  6. * Tests that themes do not depend on Classy templates.
  7. *
  8. * These tests exist to facilitate the process of decoupling themes from
  9. * Classy. The decoupling process includes eliminating the use of Classy
  10. * templates by providing theme-specific versions of templates that would
  11. * otherwise be inherited from Classy.
  12. *
  13. * This test can be removed once the Classy decoupling is complete, and it will
  14. * fail if it is still present when Classy is removed from Drupal core.
  15. *
  16. * @group Theme
  17. */
  18. class ThemesNotUsingClassyTemplatesTest extends KernelTestBase {
  19. /**
  20. * {@inheritdoc}
  21. */
  22. public static $modules = ['system', 'user'];
  23. /**
  24. * The theme handler.
  25. *
  26. * @var \Drupal\Core\Extension\ThemeHandlerInterface
  27. */
  28. protected $themeHandler;
  29. /**
  30. * Templates that are identical in Stable, which means they can be skipped.
  31. *
  32. * In several cases, the templates in Classy are identical to those in
  33. * Stable. This means that a theme would behave identically even if those
  34. * templates were removed from Classy. They are effectively decoupled from
  35. * Classy already as they rely on no functionality unique to Classy.
  36. *
  37. * @var string[]
  38. *
  39. * @see \Drupal\Tests\Core\Theme\ClassyTemplatesIdenticalToStableTest for a
  40. * test that confirms that these templates are identical.
  41. */
  42. protected $templatesSkippableBecauseIdenticalToStable = [
  43. 'file-upload-help',
  44. 'file-widget-multiple',
  45. 'image-formatter',
  46. 'image-style',
  47. 'checkboxes',
  48. 'confirm-form',
  49. 'container',
  50. 'dropbutton-wrapper',
  51. 'field-multiple-value-form',
  52. 'form',
  53. 'input',
  54. 'select',
  55. 'links',
  56. 'menu-local-action',
  57. 'pager',
  58. 'vertical-tabs',
  59. 'views-view-grid',
  60. 'views-view-list',
  61. 'views-view-mapping-test',
  62. 'views-view-opml',
  63. 'views-view-row-opml',
  64. 'views-view-rss',
  65. 'views-view-unformatted',
  66. ];
  67. /**
  68. * {@inheritdoc}
  69. */
  70. protected function setUp() {
  71. parent::setUp();
  72. $this->themeHandler = $this->container->get('theme_handler');
  73. $this->container->get('theme_installer')->install([
  74. 'umami',
  75. 'bartik',
  76. 'seven',
  77. 'claro',
  78. ]);
  79. // Enable all modules so every template is present in the theme registry.
  80. // This makes it possible to check the source of every template and
  81. // determine if they come from Classy.
  82. $this->installAllModules();
  83. }
  84. /**
  85. * Installs all core modules.
  86. */
  87. protected function installAllModules() {
  88. // Enable all core modules.
  89. $all_modules = $this->container->get('extension.list.module')->getList();
  90. $all_modules = array_filter($all_modules, function ($module) {
  91. // Filter contrib, hidden, experimental, already enabled modules, and
  92. // modules in the Testing package.
  93. if ($module->origin !== 'core' || !empty($module->info['hidden']) || $module->status === TRUE || $module->info['package'] === 'Testing' || $module->info['package'] === 'Core (Experimental)') {
  94. return FALSE;
  95. }
  96. return TRUE;
  97. });
  98. $all_modules = array_keys($all_modules);
  99. $module_installer = $this->container->get('module_installer');
  100. $module_installer->install($all_modules);
  101. }
  102. /**
  103. * Ensures that themes are not inheriting templates from Classy.
  104. *
  105. * @param string $theme
  106. * The theme to test.
  107. * @param string[] $templates_to_skip
  108. * Templates that will not be tested.
  109. *
  110. * @dataProvider providerTestThemesTemplatesNotClassy
  111. */
  112. public function testThemesTemplatesNotClassy($theme, array $templates_to_skip) {
  113. // Get every template available to the theme being tested.
  114. $theme_registry = new Registry($this->root, \Drupal::cache(), \Drupal::lock(), \Drupal::moduleHandler(), $this->themeHandler, \Drupal::service('theme.initialization'), $theme);
  115. $theme_registry->setThemeManager(\Drupal::theme());
  116. $theme_registry_full = $theme_registry->get();
  117. // Add views-form-views-form to the skipped templates array. It is
  118. // registered via views_theme() in views.module, but does not represent an
  119. // actual template.
  120. $templates_to_skip[] = 'views-form-views-form';
  121. // Loop through every template available to the current theme, confirm it
  122. // does not come from Classy, does not attach Classy libraries, and does not
  123. // extend or include Classy templates.
  124. foreach ($theme_registry_full as $info) {
  125. if (isset($info['template'])) {
  126. $template_name = $info['template'];
  127. if (in_array($template_name, $templates_to_skip) || in_array($template_name, $this->templatesSkippableBecauseIdenticalToStable)) {
  128. continue;
  129. }
  130. $template_contents = file_get_contents("{$this->root}/{$info['path']}/$template_name.html.twig");
  131. // Confirm template does not come from Classy.
  132. $this->assertFalse($info['theme path'] === 'core/themes/classy', "$theme is inheriting $template_name from Classy.");
  133. // Confirm template does not include or extend Classy templates.
  134. preg_match_all('/(extends|include)\s+(\'|")@classy/', $template_contents, $classy_extend_include_matches);
  135. $this->assertEmpty($classy_extend_include_matches[0], "The template: '$template_name' in the theme: '$theme' includes or extends a Classy template.");
  136. // Confirm template does not attach a Classy library.
  137. preg_match_all('/attach_library\((\'|")classy\/.+(\'|")\)/', $template_contents, $classy_extend_library_matches);
  138. $this->assertEmpty($classy_extend_library_matches[0], "The template: '$template_name' in the theme: '$theme' attaches a Classy library.");
  139. }
  140. }
  141. }
  142. /**
  143. * Data provider for testThemesTemplatesNotClassy().
  144. *
  145. * @return array
  146. * Array of test cases using these keys:
  147. * -'theme-name': The machine name of the theme being tested.
  148. * -'to-skip': Templates that will skipped by the test.
  149. */
  150. public function providerTestThemesTemplatesNotClassy() {
  151. // Each item provides the theme name and an array of templates to skip. The
  152. // templates in the to-skip array are ones that have not yet been decoupled
  153. // from Classy. When a template is properly decoupled from Classy, it can be
  154. // removed from to-skip. If this test passes with an empty to-skip array,
  155. // this is confirmation that the templates are fully decoupled form Classy.
  156. return [
  157. 'umami' => [
  158. 'theme-name' => 'umami',
  159. 'to-skip' => [],
  160. ],
  161. 'seven' => [
  162. 'theme-name' => 'seven',
  163. 'to-skip' => [],
  164. ],
  165. 'claro' => [
  166. 'theme-name' => 'claro',
  167. 'to-skip' => [],
  168. ],
  169. 'bartik' => [
  170. 'theme-name' => 'bartik',
  171. 'to-skip' => [],
  172. ],
  173. ];
  174. }
  175. }