diff --git a/core/modules/system/src/Tests/Asset/LibraryCompletenessTest.php b/core/modules/system/src/Tests/Asset/LibraryCompletenessTest.php index ab29e85..48a879b 100644 --- a/core/modules/system/src/Tests/Asset/LibraryCompletenessTest.php +++ b/core/modules/system/src/Tests/Asset/LibraryCompletenessTest.php @@ -17,6 +17,27 @@ class LibraryCompletenessTest extends KernelTestBase { /** + * The theme handler. + * + * @var \Drupal\Core\Extension\ThemeHandlerInterface + */ + protected $themeHandler; + + /** + * The theme initialization. + * + * @var \Drupal\Core\Theme\ThemeInitializationInterface + */ + protected $themeInitialization; + + /** + * The theme manager. + * + * @var \Drupal\Core\Theme\ThemeManagerInterface + */ + protected $themeManager; + + /** * The library discovery service. * * @var \Drupal\Core\Asset\LibraryDiscoveryInterface @@ -31,13 +52,42 @@ class LibraryCompletenessTest extends KernelTestBase { protected $allModules; /** + * A list of all core themes. + * + * We hardcode this because test themes don't use a 'package' or 'hidden' key + * so we don't have a good way of filtering to only get "real" themes. + * + * @var string[] + */ + protected $allThemes = [ + 'bartik', + 'classy', + 'seven', + 'stable', + 'stark', + ]; + + /** + * A list of all paths that have been checked. + * + * @var array[] + */ + protected $pathsChecked; + + /** * {@inheritdoc} */ public function setUp() { parent::setUp(); + $this->themeHandler = $this->container->get('theme_handler'); + $this->themeInitialization = $this->container->get('theme.initialization'); + $this->themeManager = $this->container->get('theme.manager'); $this->libraryDiscovery = $this->container->get('library.discovery'); + // Install all core themes. + $this->container->get('theme_installer')->install($this->allThemes); + // Enable all core modules. $all_modules = system_rebuild_module_data(); $all_modules = array_filter($all_modules, function ($module) { @@ -49,19 +99,38 @@ public function setUp() { return TRUE; }); $this->allModules = array_keys($all_modules); - asort($this->allModules); + sort($this->allModules); $this->enableModules($this->allModules); } /** - * Ensures that all core library files exist. + * Ensures that all core module and theme library files exist. */ public function testCoreLibraryCompleteness() { - $all_libraries['core'] = $this->libraryDiscovery->getLibrariesByExtension('core'); - $all_libraries += $this->getAllLibraries(); + // First verify all libraries with no active theme. + $this->verifyLibraryFilesExist($this->getAllLibraries()); + + // Then verify all libraries for each core theme. This may seem like + // overkill but themes can override and extend other extension's libraries + // and these changes are only applied for the active theme. + foreach ($this->allThemes as $theme) { + $this->themeManager->setActiveTheme($this->themeInitialization->getActiveThemeByName($theme)); + $this->libraryDiscovery->clearCachedDefinitions(); + $this->verifyLibraryFilesExist($this->getAllLibraries()); + } + } + + /** + * Checks that all the library files exist. + * + * @param array[] + * An array of library definitions, keyed by extension, then by library, and + * so on. + */ + protected function verifyLibraryFilesExist($library_definitions) { $root = \Drupal::root(); - foreach ($all_libraries as $extension => $libraries) { + foreach ($library_definitions as $extension => $libraries) { foreach ($libraries as $library_name => $library) { $libraries_to_skip = [ // Locale has a "dummy" library that does not actually exist. @@ -75,7 +144,12 @@ public function testCoreLibraryCompleteness() { foreach (['css', 'js'] as $asset_type) { foreach ($library[$asset_type] as $asset) { $file = $asset['data']; - $this->assertTrue(is_file($root . '/' . $file), "$file file referenced from the $extension/$library_name library exists."); + $path = $root . '/' . $file; + // Only check and assert each file path once. + if (!isset($this->pathsChecked[$path])) { + $this->assertTrue(is_file($path), "$file file referenced from the $extension/$library_name library exists."); + $this->pathsChecked[$path] = TRUE; + } } } } @@ -83,21 +157,34 @@ public function testCoreLibraryCompleteness() { } /** - * Gets all libraries for all installed modules. + * Gets all libraries for core and all installed modules. * * @return \Drupal\Core\Extension\Extension[] */ protected function getAllLibraries() { $modules = \Drupal::moduleHandler()->getModuleList(); + $extensions = $modules; $module_list = array_keys($modules); - asort($module_list); + sort($module_list); $this->assertEqual($this->allModules, $module_list, 'All core modules are installed.'); + $themes = $this->themeHandler->listInfo(); + $extensions += $themes; + $theme_list = array_keys($themes); + sort($theme_list); + sort($this->allThemes); + $this->assertEqual($this->allThemes, $theme_list, 'All core themes are installed.'); + + $libraries['core'] = $this->libraryDiscovery->getLibrariesByExtension('core'); + $root = \Drupal::root(); - foreach ($modules as $module_name => $module) { - $library_file = $module->getPath() . '/' . $module_name . '.libraries.yml'; + // By merging $modules and $themes into $extensions we're assuming that a + // module and theme don't share names. If they did, their libraries would + // conflict anyway. + foreach ($extensions as $extension_name => $extension) { + $library_file = $extension->getPath() . '/' . $extension_name . '.libraries.yml'; if (is_file($root . '/' . $library_file)) { - $libraries[$module_name] = $this->libraryDiscovery->getLibrariesByExtension($module_name); + $libraries[$extension_name] = $this->libraryDiscovery->getLibrariesByExtension($extension_name); } } return $libraries;