You are here

public function ThemeNotUsingClassyLibraryTest::testThemeAccountsForClassyExtensions in Drupal 8

Checks for theme-specific equivalents of all Classy library-extends.

Classy extends several core libraries with its own assets, these are defined in the `libraries-extend:` list in classy.info.yml. Classy adds additional assets to these libraries (e.g. when the `file/drupal.file` library loads, the assets of `classy/file` are loaded as well). For a theme to be properly decoupled from Classy's libraries, these core library extensions must become the responsibility of that theme.

@dataProvider providerTestThemeAccountsForClassyExtensions

Parameters

string $theme: The theme being tested.

string[] $extends_to_skip: Classy library-extends excluded from the test.

File

core/tests/Drupal/KernelTests/Core/Theme/ThemeNotUsingClassyLibraryTest.php, line 220

Class

ThemeNotUsingClassyLibraryTest
Tests that themes do not depend on Classy libraries.

Namespace

Drupal\KernelTests\Core\Theme

Code

public function testThemeAccountsForClassyExtensions($theme, array $extends_to_skip) {
  $theme_path = $this->themeHandler
    ->getTheme($theme)
    ->getPath();

  // Get a list of libraries overridden by the current theme. In a theme's
  // info.yml file, these are the libraries listed in `libraries-override:`.
  // They are libraries altered by the current theme.
  $theme_library_overrides = $this->themeInitialization
    ->getActiveThemeByName($theme)
    ->getLibrariesOverride()[$theme_path] ?? [];

  // Get a list of libraries extended by the current theme. In a theme's
  // info.yml file, these are the libraries listed in `libraries-extend:`.
  // The current theme adds additional files to these libraries.
  $theme_extends = $this->themeHandler
    ->getTheme($theme)->info['libraries-extend'] ?? [];

  // Some Classy libraries extend core libraries (i.e. they are not standalone
  // libraries. Rather, they extend the functionality of existing core
  // libraries). These extensions that were implemented in Classy need to be
  // accounted for in the current theme by either 1) The current theme
  // extending the core library with local copy of the Classy library 2)
  // Overriding the core library altogether.
  // The following iterates through each library extended by Classy to confirm
  // that the current theme accounts for these these extensions.
  foreach ($this->classyLibrariesExtend as $library_extended => $info) {
    if (in_array($library_extended, $extends_to_skip)) {
      continue;
    }
    $extends_core_library = isset($theme_extends[$library_extended]);
    $overrides_core_library = isset($theme_library_overrides[$library_extended]);

    // Every core library extended by Classy must be extended or overridden by
    // the current theme.
    $this
      ->assertTrue($extends_core_library || $overrides_core_library, "{$library_extended} is extended by Classy and should be extended or overridden by {$theme}");

    // If the core library is overridden, confirm that the override does not
    // include any Classy assets.
    if ($overrides_core_library) {
      $overridden_with = $theme_library_overrides[$library_extended];

      // A library override variable can be one of three types:
      // - bool (set to false): this means the override simply prevents the
      //   library from loading.
      // - array: this means some files in the overridden library are changed,
      //   but not necessarily all of them.
      // - string (which is what is being looked for here): this means the
      //   library is replaced with a completely different library.
      $override_replaces_library = gettype($overridden_with) === 'string';
      if ($override_replaces_library) {

        // Make sure the replacement library does not come from Classy.
        $this
          ->assertFalse(substr($overridden_with, 0, 7) === 'classy/', "{$library_extended} is replaced with {$overridden_with}. The replacement should not be a Classy library.");
      }

      // If the override doesn't prevent the core library from loading
      // entirely, and it doesn't replace it with another library, each asset
      // must be checked to confirm it isn't coming from Classy.
      if ($overridden_with !== FALSE && !$override_replaces_library) {
        foreach ([
          'component',
          'layout',
        ] as $category) {
          if (isset($overridden_with['css'][$category])) {
            foreach ($overridden_with['css'][$category] as $css_file) {
              $this
                ->assertFalse(strpos($css_file, 'core/themes/classy/css'), "Override is loading a Classy asset: {$css_file}");
            }
          }
        }
        if (isset($overridden_with['js'])) {
          foreach ($overridden_with['js'] as $js_file) {
            $this
              ->assertFalse(strpos($js_file, 'core/themes/classy/js'), "Override is loading a Classy asset: {$js_file}");
          }
        }
      }
    }

    // If the library is extended, make sure it's not being extended with a
    // Classy library.
    if ($extends_core_library) {
      foreach ($theme_extends[$library_extended] as $library) {
        $this
          ->assertFalse(substr($library, 0, 7) === 'classy/', "{$theme} is extending the core library: {$library_extended} with {$library}. Core libraries should not be extended with a Classy library.");
      }
    }
  }
}