Commit 2f94a274 authored by catch's avatar catch
Browse files

Issue #3535330 by joelpittet, benabaird: Assets paths in CSS no longer...

Issue #3535330 by joelpittet, benabaird: Assets paths in CSS no longer rewritten when aggregation is enabled

(cherry picked from commit ca25750b)
parent 9271a55f
Loading
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -149,8 +149,12 @@ public function optimizeGroup(array $group): string {
      }
      $current_license = $css_asset['license'];

      // Append this file if already minified; otherwise optimize it.
      if (isset($css_asset['minified']) && $css_asset['minified']) {
      // Only external minified files can skip optimization.
      // Local files (even if minified) must be processed to ensure resource
      // paths are rewritten relative to the cached aggregated file location.
      $is_minified = isset($css_asset['minified']) && $css_asset['minified'];
      $is_external = isset($css_asset['type']) && $css_asset['type'] === 'external';
      if ($is_minified && $is_external) {
        $data .= file_get_contents($css_asset['data']);
      }
      else {
+107 −1
Original line number Diff line number Diff line
@@ -7,6 +7,7 @@
use Drupal\Component\Datetime\TimeInterface;
use Drupal\Core\Asset\AssetCollectionGrouperInterface;
use Drupal\Core\Asset\AssetOptimizerInterface;
use Drupal\Core\Asset\CssOptimizer;
use Drupal\Core\Asset\LibraryDependencyResolverInterface;
use Drupal\Core\Asset\CssCollectionOptimizerLazy;
use Drupal\Core\Config\ConfigFactoryInterface;
@@ -15,6 +16,7 @@
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Theme\ThemeManagerInterface;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\MockObject\MockObject;
use Symfony\Component\HttpFoundation\RequestStack;

/**
@@ -24,6 +26,31 @@
 */
class CssCollectionOptimizerLazyUnitTest extends UnitTestCase {

  /**
   * A CSS asset optimizer.
   */
  protected CssOptimizer $optimizer;

  /**
   * The file URL generator mock.
   */
  protected FileUrlGeneratorInterface|MockObject $fileUrlGenerator;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->fileUrlGenerator = $this->createMock(FileUrlGeneratorInterface::class);
    $this->fileUrlGenerator->expects($this->any())
      ->method('generateString')
      ->with($this->isString())
      ->willReturnCallback(function ($uri) {
        return 'generated-relative-url:' . $uri;
      });
    $this->optimizer = new CssOptimizer($this->fileUrlGenerator);
  }

  /**
   * Tests that CSS imports with strange letters do not destroy the CSS output.
   */
@@ -157,6 +184,8 @@ public function testCssLicenseAggregation(): void {
  public function testExternalMinifiedCssAssetOptimizationIsSkipped(): void {
    $mock_grouper = $this->createMock(AssetCollectionGrouperInterface::class);
    $mock_optimizer = $this->createMock(AssetOptimizerInterface::class);

    // The expectation is to never call optimize on minified external assets.
    $mock_optimizer->expects($this->never())->method('optimize');

    $optimizer = new CssCollectionOptimizerLazy(
@@ -171,11 +200,12 @@ public function testExternalMinifiedCssAssetOptimizationIsSkipped(): void {
      $this->createMock(TimeInterface::class),
      $this->createMock(LanguageManagerInterface::class)
    );

    $optimizer->optimizeGroup([
      'items' => [
        [
          'type' => 'external',
          'data' => __DIR__ . '/css_test_files/css_external.optimized.aggregated.css',
          'data' => 'core/tests/Drupal/Tests/Core/Asset/css_test_files/css_external.optimized.aggregated.css',
          'license' => FALSE,
          'preprocess' => TRUE,
          'minified' => TRUE,
@@ -184,4 +214,80 @@ public function testExternalMinifiedCssAssetOptimizationIsSkipped(): void {
    ]);
  }

  /**
   * Test that local minified CSS assets still trigger optimization.
   *
   * This ensures that local minified assets are optimized to correct relative
   * paths.
   */
  public function testLocalMinifiedCssAssetOptimizationIsNotSkipped(): void {
    $mock_grouper = $this->createMock(AssetCollectionGrouperInterface::class);
    $mock_optimizer = $this->createMock(AssetOptimizerInterface::class);
    $mock_optimizer->expects($this->once())->method('optimize');

    $optimizer = new CssCollectionOptimizerLazy(
      $mock_grouper,
      $mock_optimizer,
      $this->createMock(ThemeManagerInterface::class),
      $this->createMock(LibraryDependencyResolverInterface::class),
      new RequestStack(),
      $this->createMock(FileSystemInterface::class),
      $this->createMock(ConfigFactoryInterface::class),
      $this->createMock(FileUrlGeneratorInterface::class),
      $this->createMock(TimeInterface::class),
      $this->createMock(LanguageManagerInterface::class)
    );

    $optimizer->optimizeGroup([
      'items' => [
        [
          'type' => 'file',
          'data' => 'core/tests/Drupal/Tests/Core/Asset/css_test_files/css_input_with_import.css',
          'license' => FALSE,
          'preprocess' => TRUE,
          'minified' => TRUE,
        ],
      ],
    ]);
  }

  /**
   * Test that relative paths in local minified CSS files are updated.
   *
   * This ensures that local minified assets have their relative paths correctly
   * rewritten during optimization.
   */
  public function testRelativePathsInLocalMinifiedCssAssets(): void {
    $mock_grouper = $this->createMock(AssetCollectionGrouperInterface::class);

    $optimizer = new CssCollectionOptimizerLazy(
      $mock_grouper,
      $this->optimizer,
      $this->createMock(ThemeManagerInterface::class),
      $this->createMock(LibraryDependencyResolverInterface::class),
      new RequestStack(),
      $this->createMock(FileSystemInterface::class),
      $this->createMock(ConfigFactoryInterface::class),
      $this->createMock(FileUrlGeneratorInterface::class),
      $this->createMock(TimeInterface::class),
      $this->createMock(LanguageManagerInterface::class)
    );

    $result = $optimizer->optimizeGroup([
      'items' => [
        [
          'type' => 'file',
          'data' => 'core/tests/Drupal/Tests/Core/Asset/css_test_files/css_minified_with_relative_paths.css',
          'media' => 'all',
          'license' => FALSE,
          'preprocess' => TRUE,
          'minified' => TRUE,
        ],
      ],
    ]);

    $expected = file_get_contents(__DIR__ . '/css_test_files/css_minified_with_relative_paths.css.optimized.css');
    self::assertEquals($expected, $result, 'Relative paths in local minified CSS assets are correctly replaced.');
  }

}
+6 −0
Original line number Diff line number Diff line
/* This is a minified CSS file with relative paths */
body{margin:0;padding:0;background:#edf5fa url(../images/background.png) repeat-x;font:76%/170% Verdana,sans-serif;color:#494949;}
.icon{background-image:url(../images/icon.png);}
.logo{background:url(images/logo.png) no-repeat center center;}
.banner{background-image:url('./images/banner.jpg');}
.data-uri{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}
+1 −0
Original line number Diff line number Diff line
body{margin:0;padding:0;background:#edf5fa url(generated-relative-url:core/tests/Drupal/Tests/Core/Asset/images/background.png) repeat-x;font:76%/170% Verdana,sans-serif;color:#494949;}.icon{background-image:url(generated-relative-url:core/tests/Drupal/Tests/Core/Asset/images/icon.png);}.logo{background:url(generated-relative-url:core/tests/Drupal/Tests/Core/Asset/css_test_files/images/logo.png) no-repeat center center;}.banner{background-image:url(generated-relative-url:core/tests/Drupal/Tests/Core/Asset/css_test_files/./images/banner.jpg);}.data-uri{background-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");}