Version 1
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Asset / LibraryDiscoveryIntegrationTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Asset;
4
5 use Drupal\Core\Asset\Exception\InvalidLibrariesExtendSpecificationException;
6 use Drupal\Core\Asset\Exception\InvalidLibrariesOverrideSpecificationException;
7 use Drupal\KernelTests\KernelTestBase;
8
9 /**
10  * Tests the library discovery and library discovery parser.
11  *
12  * @group Render
13  */
14 class LibraryDiscoveryIntegrationTest extends KernelTestBase {
15
16   /**
17    * The library discovery service.
18    *
19    * @var \Drupal\Core\Asset\LibraryDiscoveryInterface
20    */
21   protected $libraryDiscovery;
22
23   /**
24    * {@inheritdoc}
25    */
26   protected function setUp() {
27     parent::setUp();
28
29     $this->container->get('theme_installer')->install(['test_theme', 'classy']);
30     $this->libraryDiscovery = $this->container->get('library.discovery');
31   }
32
33   /**
34    * Tests that hook_library_info is invoked and the cache is cleared.
35    */
36   public function testHookLibraryInfoByTheme() {
37     // Activate test_theme and verify that the library 'kitten' is added using
38     // hook_library_info_alter().
39     $this->activateTheme('test_theme');
40     $this->assertTrue($this->libraryDiscovery->getLibraryByName('test_theme', 'kitten'));
41
42     // Now make classy the active theme and assert that library is not added.
43     $this->activateTheme('classy');
44     $this->assertFalse($this->libraryDiscovery->getLibraryByName('test_theme', 'kitten'));
45   }
46
47   /**
48    * Tests that libraries-override are applied to library definitions.
49    */
50   public function testLibrariesOverride() {
51     // Assert some classy libraries that will be overridden or removed.
52     $this->activateTheme('classy');
53     $this->assertAssetInLibrary('core/themes/classy/css/components/button.css', 'classy', 'base', 'css');
54     $this->assertAssetInLibrary('core/themes/classy/css/components/collapse-processed.css', 'classy', 'base', 'css');
55     $this->assertAssetInLibrary('core/themes/classy/css/components/container-inline.css', 'classy', 'base', 'css');
56     $this->assertAssetInLibrary('core/themes/classy/css/components/details.css', 'classy', 'base', 'css');
57     $this->assertAssetInLibrary('core/themes/classy/css/components/dialog.css', 'classy', 'dialog', 'css');
58
59     // Confirmatory assert on core library to be removed.
60     $this->assertTrue($this->libraryDiscovery->getLibraryByName('core', 'drupal.progress'), 'Confirmatory test on "core/drupal.progress"');
61
62     // Activate test theme that defines libraries overrides.
63     $this->activateTheme('test_theme');
64
65     // Assert that entire library was correctly overridden.
66     $this->assertEqual($this->libraryDiscovery->getLibraryByName('core', 'drupal.collapse'), $this->libraryDiscovery->getLibraryByName('test_theme', 'collapse'), 'Entire library correctly overridden.');
67
68     // Assert that classy library assets were correctly overridden or removed.
69     $this->assertNoAssetInLibrary('core/themes/classy/css/components/button.css', 'classy', 'base', 'css');
70     $this->assertNoAssetInLibrary('core/themes/classy/css/components/collapse-processed.css', 'classy', 'base', 'css');
71     $this->assertNoAssetInLibrary('core/themes/classy/css/components/container-inline.css', 'classy', 'base', 'css');
72     $this->assertNoAssetInLibrary('core/themes/classy/css/components/details.css', 'classy', 'base', 'css');
73     $this->assertNoAssetInLibrary('core/themes/classy/css/components/dialog.css', 'classy', 'dialog', 'css');
74
75     $this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme/css/my-button.css', 'classy', 'base', 'css');
76     $this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme/css/my-collapse-processed.css', 'classy', 'base', 'css');
77     $this->assertAssetInLibrary('themes/my_theme/css/my-container-inline.css', 'classy', 'base', 'css');
78     $this->assertAssetInLibrary('themes/my_theme/css/my-details.css', 'classy', 'base', 'css');
79
80     // Assert that entire library was correctly removed.
81     $this->assertFalse($this->libraryDiscovery->getLibraryByName('core', 'drupal.progress'), 'Entire library correctly removed.');
82
83     // Assert that overridden library asset still retains attributes.
84     $library = $this->libraryDiscovery->getLibraryByName('core', 'jquery');
85     foreach ($library['js'] as $definition) {
86       if ($definition['data'] == 'core/modules/system/tests/themes/test_theme/js/collapse.js') {
87         $this->assertTrue($definition['minified'] && $definition['weight'] == -20, 'Previous attributes retained');
88         break;
89       }
90     }
91   }
92
93   /**
94    * Tests libraries-override on drupalSettings.
95    */
96   public function testLibrariesOverrideDrupalSettings() {
97     // Activate test theme that attempts to override drupalSettings.
98     $this->activateTheme('test_theme_libraries_override_with_drupal_settings');
99
100     // Assert that drupalSettings cannot be overridden and throws an exception.
101     try {
102       $this->libraryDiscovery->getLibraryByName('core', 'drupal.ajax');
103       $this->fail('Throw Exception when trying to override drupalSettings');
104     }
105     catch (InvalidLibrariesOverrideSpecificationException $e) {
106       $expected_message = 'drupalSettings may not be overridden in libraries-override. Trying to override core/drupal.ajax/drupalSettings. Use hook_library_info_alter() instead.';
107       $this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when trying to override drupalSettings');
108     }
109   }
110
111   /**
112    * Tests libraries-override on malformed assets.
113    */
114   public function testLibrariesOverrideMalformedAsset() {
115     // Activate test theme that overrides with a malformed asset.
116     $this->activateTheme('test_theme_libraries_override_with_invalid_asset');
117
118     // Assert that improperly formed asset "specs" throw an exception.
119     try {
120       $this->libraryDiscovery->getLibraryByName('core', 'drupal.dialog');
121       $this->fail('Throw Exception when specifying invalid override');
122     }
123     catch (InvalidLibrariesOverrideSpecificationException $e) {
124       $expected_message = 'Library asset core/drupal.dialog/css is not correctly specified. It should be in the form "extension/library_name/sub_key/path/to/asset.js".';
125       $this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when specifying invalid override');
126     }
127   }
128
129   /**
130    * Tests library assets with other ways for specifying paths.
131    */
132   public function testLibrariesOverrideOtherAssetLibraryNames() {
133     // Activate a test theme that defines libraries overrides on other types of
134     // assets.
135     $this->activateTheme('test_theme');
136
137     // Assert Drupal-relative paths.
138     $this->assertAssetInLibrary('themes/my_theme/css/dropbutton.css', 'core', 'drupal.dropbutton', 'css');
139
140     // Assert stream wrapper paths.
141     $this->assertAssetInLibrary('public://my_css/vertical-tabs.css', 'core', 'drupal.vertical-tabs', 'css');
142
143     // Assert a protocol-relative URI.
144     $this->assertAssetInLibrary('//my-server/my_theme/css/jquery_ui.css', 'core', 'jquery.ui', 'css');
145
146     // Assert an absolute URI.
147     $this->assertAssetInLibrary('http://example.com/my_theme/css/farbtastic.css', 'core', 'jquery.farbtastic', 'css');
148   }
149
150   /**
151    * Tests that base theme libraries-override still apply in sub themes.
152    */
153   public function testBaseThemeLibrariesOverrideInSubTheme() {
154     // Activate a test theme that has subthemes.
155     $this->activateTheme('test_subtheme');
156
157     // Assert that libraries-override specified in the base theme still applies
158     // in the sub theme.
159     $this->assertNoAssetInLibrary('core/misc/dialog/dialog.js', 'core', 'drupal.dialog', 'js');
160     $this->assertAssetInLibrary('core/modules/system/tests/themes/test_basetheme/css/farbtastic.css', 'core', 'jquery.farbtastic', 'css');
161   }
162
163   /**
164    * Tests libraries-extend.
165    */
166   public function testLibrariesExtend() {
167     // Activate classy themes and verify the libraries are not extended.
168     $this->activateTheme('classy');
169     $this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_1.css', 'classy', 'book-navigation', 'css');
170     $this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/js/extend_1.js', 'classy', 'book-navigation', 'js');
171     $this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_2.css', 'classy', 'book-navigation', 'css');
172
173     // Activate the theme that extends the book-navigation library in classy.
174     $this->activateTheme('test_theme_libraries_extend');
175     $this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_1.css', 'classy', 'book-navigation', 'css');
176     $this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/js/extend_1.js', 'classy', 'book-navigation', 'js');
177     $this->assertAssetInLibrary('core/modules/system/tests/themes/test_theme_libraries_extend/css/extend_2.css', 'classy', 'book-navigation', 'css');
178
179     // Activate a sub theme and confirm that it inherits the library assets
180     // extended in the base theme as well as its own.
181     $this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_basetheme/css/base-libraries-extend.css', 'classy', 'base', 'css');
182     $this->assertNoAssetInLibrary('core/modules/system/tests/themes/test_subtheme/css/sub-libraries-extend.css', 'classy', 'base', 'css');
183     $this->activateTheme('test_subtheme');
184     $this->assertAssetInLibrary('core/modules/system/tests/themes/test_basetheme/css/base-libraries-extend.css', 'classy', 'base', 'css');
185     $this->assertAssetInLibrary('core/modules/system/tests/themes/test_subtheme/css/sub-libraries-extend.css', 'classy', 'base', 'css');
186
187     // Activate test theme that extends with a non-existent library. An
188     // exception should be thrown.
189     $this->activateTheme('test_theme_libraries_extend');
190     try {
191       $this->libraryDiscovery->getLibraryByName('core', 'drupal.dialog');
192       $this->fail('Throw Exception when specifying non-existent libraries-extend.');
193     }
194     catch (InvalidLibrariesExtendSpecificationException $e) {
195       $expected_message = 'The specified library "test_theme_libraries_extend/non_existent_library" does not exist.';
196       $this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when specifying non-existent libraries-extend.');
197     }
198
199     // Also, test non-string libraries-extend. An exception should be thrown.
200     $this->container->get('theme_installer')->install(['test_theme']);
201     try {
202       $this->libraryDiscovery->getLibraryByName('test_theme', 'collapse');
203       $this->fail('Throw Exception when specifying non-string libraries-extend.');
204     }
205     catch (InvalidLibrariesExtendSpecificationException $e) {
206       $expected_message = 'The libraries-extend specification for each library must be a list of strings.';
207       $this->assertEqual($e->getMessage(), $expected_message, 'Throw Exception when specifying non-string libraries-extend.');
208     }
209   }
210
211   /**
212    * Activates a specified theme.
213    *
214    * Installs the theme if not already installed and makes it the active theme.
215    *
216    * @param string $theme_name
217    *   The name of the theme to be activated.
218    */
219   protected function activateTheme($theme_name) {
220     $this->container->get('theme_installer')->install([$theme_name]);
221
222     /** @var \Drupal\Core\Theme\ThemeInitializationInterface $theme_initializer */
223     $theme_initializer = $this->container->get('theme.initialization');
224
225     /** @var \Drupal\Core\Theme\ThemeManagerInterface $theme_manager */
226     $theme_manager = $this->container->get('theme.manager');
227
228     $theme_manager->setActiveTheme($theme_initializer->getActiveThemeByName($theme_name));
229
230     $this->libraryDiscovery->clearCachedDefinitions();
231
232     // Assert message.
233     $this->pass(sprintf('Activated theme "%s"', $theme_name));
234   }
235
236   /**
237    * Asserts that the specified asset is in the given library.
238    *
239    * @param string $asset
240    *   The asset file with the path for the file.
241    * @param string $extension
242    *   The extension in which the $library is defined.
243    * @param string $library_name
244    *   Name of the library.
245    * @param mixed $sub_key
246    *   The library sub key where the given asset is defined.
247    * @param string $message
248    *   (optional) A message to display with the assertion.
249    *
250    * @return bool
251    *   TRUE if the specified asset is found in the library.
252    */
253   protected function assertAssetInLibrary($asset, $extension, $library_name, $sub_key, $message = NULL) {
254     if (!isset($message)) {
255       $message = sprintf('Asset %s found in library "%s/%s"', $asset, $extension, $library_name);
256     }
257     $library = $this->libraryDiscovery->getLibraryByName($extension, $library_name);
258     foreach ($library[$sub_key] as $definition) {
259       if ($asset == $definition['data']) {
260         return $this->pass($message);
261       }
262     }
263     return $this->fail($message);
264   }
265
266   /**
267    * Asserts that the specified asset is not in the given library.
268    *
269    * @param string $asset
270    *   The asset file with the path for the file.
271    * @param string $extension
272    *   The extension in which the $library_name is defined.
273    * @param string $library_name
274    *   Name of the library.
275    * @param mixed $sub_key
276    *   The library sub key where the given asset is defined.
277    * @param string $message
278    *   (optional) A message to display with the assertion.
279    *
280    * @return bool
281    *   TRUE if the specified asset is not found in the library.
282    */
283   protected function assertNoAssetInLibrary($asset, $extension, $library_name, $sub_key, $message = NULL) {
284     if (!isset($message)) {
285       $message = sprintf('Asset %s not found in library "%s/%s"', $asset, $extension, $library_name);
286     }
287     $library = $this->libraryDiscovery->getLibraryByName($extension, $library_name);
288     foreach ($library[$sub_key] as $definition) {
289       if ($asset == $definition['data']) {
290         return $this->fail($message);
291       }
292     }
293     return $this->pass($message);
294   }
295
296 }