‪TYPO3CMS  ‪main
ResourceCompressorTest.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
19 
21 use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
22 
23 final class ‪ResourceCompressorTest extends UnitTestCase
24 {
25  protected bool ‪$backupEnvironment = true;
26 
27  public static function ‪cssFixStatementsDataProvider(): array
28  {
29  return [
30  'nothing to do - no charset/import/namespace' => [
31  'body { background: #ffffff; }',
32  'body { background: #ffffff; }',
33  ],
34  'import in front' => [
35  '@import url(http://www.example.com/css); body { background: #ffffff; }',
36  'LF/* moved by compressor */LF@import url(http://www.example.com/css);LF/* moved by compressor */LFbody { background: #ffffff; }',
37  ],
38  'import in back, without quotes' => [
39  'body { background: #ffffff; } @import url(http://www.example.com/css);',
40  'LF/* moved by compressor */LF@import url(http://www.example.com/css);LF/* moved by compressor */LFbody { background: #ffffff; }',
41  ],
42  'import in back, with double-quotes' => [
43  'body { background: #ffffff; } @import url("http://www.example.com/css");',
44  'LF/* moved by compressor */LF@import url("http://www.example.com/css");LF/* moved by compressor */LFbody { background: #ffffff; }',
45  ],
46  'import in back, with single-quotes' => [
47  'body { background: #ffffff; } @import url(\'http://www.example.com/css\');',
48  'LF/* moved by compressor */LF@import url(\'http://www.example.com/css\');LF/* moved by compressor */LFbody { background: #ffffff; }',
49  ],
50  'import in middle and back, without quotes' => [
51  'body { background: #ffffff; } @import url(http://www.example.com/A); div { background: #000; } @import url(http://www.example.com/B);',
52  'LF/* moved by compressor */LF@import url(http://www.example.com/A);@import url(http://www.example.com/B);LF/* moved by compressor */LFbody { background: #ffffff; } div { background: #000; }',
53  ],
54  'charset declaration is unique' => [
55  'body { background: #ffffff; } @charset "UTF-8"; div { background: #000; }; @charset "UTF-8";',
56  '@charset "UTF-8";LF/* moved by compressor */LFbody { background: #ffffff; } div { background: #000; };',
57  ],
58  'order of charset, namespace and import is correct' => [
59  'body { background: #ffffff; } @charset "UTF-8"; div { background: #000; }; @import "file2.css"; @namespace url(http://www.w3.org/1999/xhtml);',
60  '@charset "UTF-8";LF/* moved by compressor */LF@namespace url(http://www.w3.org/1999/xhtml);LF/* moved by compressor */LF@import "file2.css";LF/* moved by compressor */LFbody { background: #ffffff; } div { background: #000; };',
61  ],
62  ];
63  }
64 
69  public function ‪cssFixStatementsMovesStatementsToTopIfNeeded(string $input, string $expected): void
70  {
71  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
72  $subject->_call('initialize');
73  $result = $subject->_call('cssFixStatements', $input);
74  $resultWithReadableLinefeed = str_replace(LF, 'LF', $result);
75  self::assertEquals($expected, $resultWithReadableLinefeed);
76  }
77 
82  {
83  $fileName = 'fooFile.css';
84  $compressedFileName = $fileName . '.gz';
85  $testFileFixture = [
86  $fileName => [
87  'file' => $fileName,
88  'compress' => true,
89  ],
90  ];
91  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
92  $subject->expects(self::once())
93  ->method('compressCssFile')
94  ->with($fileName)
95  ->willReturn($compressedFileName);
96 
97  $result = $subject->compressCssFiles($testFileFixture);
98 
99  self::assertArrayHasKey($compressedFileName, $result);
100  self::assertArrayHasKey('compress', $result[$compressedFileName]);
101  self::assertFalse($result[$compressedFileName]['compress']);
102  }
103 
108  {
109  $fileName = 'fooFile.js';
110  $compressedFileName = $fileName . '.gz';
111  $testFileFixture = [
112  $fileName => [
113  'file' => $fileName,
114  'compress' => true,
115  ],
116  ];
117  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
118  $subject->expects(self::once())
119  ->method('compressJsFile')
120  ->with($fileName)
121  ->willReturn($compressedFileName);
122 
123  $result = $subject->compressJsFiles($testFileFixture);
124 
125  self::assertArrayHasKey($compressedFileName, $result);
126  self::assertArrayHasKey('compress', $result[$compressedFileName]);
127  self::assertFalse($result[$compressedFileName]['compress']);
128  }
129 
134  {
135  $fileName = 'fooFile.css';
136  $concatenatedFileName = 'merged_' . $fileName;
137  $testFileFixture = [
138  $fileName => [
139  'file' => $fileName,
140  'excludeFromConcatenation' => false,
141  'media' => 'all',
142  ],
143  ];
144  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
145  $subject->expects(self::once())
146  ->method('createMergedCssFile')
147  ->willReturn($concatenatedFileName);
148 
149  $result = $subject->concatenateCssFiles($testFileFixture);
150 
151  self::assertArrayHasKey($concatenatedFileName, $result);
152  self::assertArrayHasKey('excludeFromConcatenation', $result[$concatenatedFileName]);
153  self::assertTrue($result[$concatenatedFileName]['excludeFromConcatenation']);
154  }
155 
160  {
161  $allFileName = 'allFile.css';
162  $screenFileName1 = 'screenFile.css';
163  $screenFileName2 = 'screenFile2.css';
164  $testFileFixture = [
165  $allFileName => [
166  'file' => $allFileName,
167  'excludeFromConcatenation' => false,
168  'media' => 'all',
169  ],
170  // use two screen files to check if they are merged into one, even with a different media type
171  $screenFileName1 => [
172  'file' => $screenFileName1,
173  'excludeFromConcatenation' => false,
174  'media' => 'screen',
175  ],
176  $screenFileName2 => [
177  'file' => $screenFileName2,
178  'excludeFromConcatenation' => false,
179  'media' => 'screen',
180  ],
181  ];
182  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
183  $subject->expects(self::exactly(2))
184  ->method('createMergedCssFile')
185  ->will(self::onConsecutiveCalls(
186  self::returnValue('merged_' . $allFileName),
187  self::returnValue('merged_' . $screenFileName1)
188  ));
189 
190  $result = $subject->concatenateCssFiles($testFileFixture);
191 
192  self::assertEquals([
193  'merged_' . $allFileName,
194  'merged_' . $screenFileName1,
195  ], array_keys($result));
196  self::assertEquals('all', $result['merged_' . $allFileName]['media']);
197  self::assertEquals('screen', $result['merged_' . $screenFileName1]['media']);
198  }
199 
204  {
205  $screen1FileName = 'screen1File.css';
206  $screen2FileName = 'screen2File.css';
207  $screen3FileName = 'screen3File.css';
208  $testFileFixture = [
209  $screen1FileName => [
210  'file' => $screen1FileName,
211  'excludeFromConcatenation' => false,
212  'media' => 'screen',
213  ],
214  $screen2FileName => [
215  'file' => $screen2FileName,
216  'excludeFromConcatenation' => false,
217  'media' => 'screen',
218  ],
219  $screen3FileName => [
220  'file' => $screen3FileName,
221  'excludeFromConcatenation' => false,
222  'forceOnTop' => true,
223  'media' => 'screen',
224  ],
225  ];
226  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
227  // Replace mocked method getFilenameFromMainDir by passthrough callback
228  $subject->method('getFilenameFromMainDir')->willReturnArgument(0);
229  $subject->expects(self::once())
230  ->method('createMergedCssFile')
231  ->with(self::equalTo([$screen3FileName, $screen1FileName, $screen2FileName]));
232 
233  $subject->concatenateCssFiles($testFileFixture);
234  }
235 
240  {
241  $screen1FileName = 'screen1File.css';
242  $screen2FileName = 'screen2File.css';
243  $screen3FileName = 'screen3File.css';
244  $testFileFixture = [
245  $screen1FileName => [
246  'file' => $screen1FileName,
247  'excludeFromConcatenation' => false,
248  'media' => 'screen',
249  ],
250  $screen2FileName => [
251  'file' => $screen2FileName,
252  'excludeFromConcatenation' => true,
253  'media' => 'screen',
254  ],
255  $screen3FileName => [
256  'file' => $screen3FileName,
257  'excludeFromConcatenation' => false,
258  'media' => 'screen',
259  ],
260  ];
261  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
262  $subject->method('getFilenameFromMainDir')->willReturnArgument(0);
263  $subject->expects(self::once())
264  ->method('createMergedCssFile')
265  ->with(self::equalTo([$screen1FileName, $screen3FileName]))
266  ->willReturn('merged_screen');
267 
268  $result = $subject->concatenateCssFiles($testFileFixture);
269  self::assertEquals([
270  $screen2FileName,
271  'merged_screen',
272  ], array_keys($result));
273  self::assertEquals('screen', $result[$screen2FileName]['media']);
274  self::assertEquals('screen', $result['merged_screen']['media']);
275  }
276 
281  {
282  $fileName = 'fooFile.js';
283  $concatenatedFileName = 'merged_' . $fileName;
284  $testFileFixture = [
285  $fileName => [
286  'file' => $fileName,
287  'excludeFromConcatenation' => false,
288  'section' => 'top',
289  ],
290  ];
291  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory', 'getJavaScriptFileType']);
292  $subject->method('getJavaScriptFileType')->willReturn('');
293  $subject->expects(self::once())
294  ->method('createMergedJsFile')
295  ->willReturn($concatenatedFileName);
296 
297  $result = $subject->concatenateJsFiles($testFileFixture);
298 
299  self::assertArrayHasKey($concatenatedFileName, $result);
300  self::assertArrayHasKey('excludeFromConcatenation', $result[$concatenatedFileName]);
301  self::assertTrue($result[$concatenatedFileName]['excludeFromConcatenation']);
302  }
303 
304  public static function ‪concatenateJsFileAsyncDataProvider(): array
305  {
306  return [
307  'all files have no async' => [
308  [
309  [
310  'file' => 'file1.js',
311  'excludeFromConcatenation' => false,
312  'section' => 'top',
313  ],
314  [
315  'file' => 'file2.js',
316  'excludeFromConcatenation' => false,
317  'section' => 'top',
318  ],
319  ],
320  false,
321  ],
322  'all files have async false' => [
323  [
324  [
325  'file' => 'file1.js',
326  'excludeFromConcatenation' => false,
327  'section' => 'top',
328  'async' => false,
329  ],
330  [
331  'file' => 'file2.js',
332  'excludeFromConcatenation' => false,
333  'section' => 'top',
334  'async' => false,
335  ],
336  ],
337  false,
338  ],
339  'all files have async true' => [
340  [
341  [
342  'file' => 'file1.js',
343  'excludeFromConcatenation' => false,
344  'section' => 'top',
345  'async' => true,
346  ],
347  [
348  'file' => 'file2.js',
349  'excludeFromConcatenation' => false,
350  'section' => 'top',
351  'async' => true,
352  ],
353  ],
354  true,
355  ],
356  'one file async true and one file async false' => [
357  [
358  [
359  'file' => 'file1.js',
360  'excludeFromConcatenation' => false,
361  'section' => 'top',
362  'async' => true,
363  ],
364  [
365  'file' => 'file2.js',
366  'excludeFromConcatenation' => false,
367  'section' => 'top',
368  'async' => false,
369  ],
370  ],
371  false,
372  ],
373  'one file async true and one file async false but is excluded form concatenation' => [
374  [
375  [
376  'file' => 'file1.js',
377  'excludeFromConcatenation' => false,
378  'section' => 'top',
379  'async' => true,
380  ],
381  [
382  'file' => 'file2.js',
383  'excludeFromConcatenation' => true,
384  'section' => 'top',
385  'async' => false,
386  ],
387  ],
388  true,
389  ],
390  'one file async false and one file async true but is excluded form concatenation' => [
391  [
392  [
393  'file' => 'file1.js',
394  'excludeFromConcatenation' => false,
395  'section' => 'top',
396  'async' => false,
397  ],
398  [
399  'file' => 'file2.js',
400  'excludeFromConcatenation' => true,
401  'section' => 'top',
402  'async' => true,
403  ],
404  ],
405  false,
406  ],
407  ];
408  }
409 
414  public function ‪concatenateJsFileAddsAsyncPropertyIfAllFilesAreAsync(array $input, bool $expected): void
415  {
416  $concatenatedFileName = 'merged_foo.js';
417  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory', 'getJavaScriptFileType']);
418  $subject->method('getJavaScriptFileType')->willReturn('');
419  $subject->expects(self::once())
420  ->method('createMergedJsFile')
421  ->willReturn($concatenatedFileName);
422 
423  $result = $subject->concatenateJsFiles($input);
424 
425  self::assertSame($expected, $result[$concatenatedFileName]['async']);
426  }
427 
428  public static function ‪calcStatementsDataProvider(): array
429  {
430  return [
431  'simple calc' => [
432  'calc(100% - 3px)',
433  'calc(100% - 3px)',
434  ],
435  'complex calc with parentheses at the beginning' => [
436  'calc((100%/20) - 2*3px)',
437  'calc((100%/20) - 2*3px)',
438  ],
439  'complex calc with parentheses at the end' => [
440  'calc(100%/20 - 2*3px - (200px + 3%))',
441  'calc(100%/20 - 2*3px - (200px + 3%))',
442  ],
443  'complex calc with many parentheses' => [
444  'calc((100%/20) - (2 * (3px - (200px + 3%))))',
445  'calc((100%/20) - (2 * (3px - (200px + 3%))))',
446  ],
447  ];
448  }
449 
454  public function ‪calcFunctionMustRetainWhitespaces(string $input, string $expected): void
455  {
456  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
457  $subject->_call('initialize');
458  $result = $subject->_call('compressCssString', $input);
459  self::assertSame($expected, trim($result));
460  }
461 
466  {
467  $fileName = 'fooFile.js';
468  $concatenatedFileName = 'merged_' . $fileName;
469  $testFileFixture = [
470  $fileName => [
471  'file' => $fileName,
472  'nomodule' => true,
473  'section' => 'top',
474  ],
475  ];
476 
477  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
478  $result = $subject->concatenateJsFiles($testFileFixture);
479 
480  self::assertArrayNotHasKey($concatenatedFileName, $result);
481  self::assertTrue($result[$fileName]['nomodule']);
482  }
483 
487  public function ‪deferJavascriptIsNotConcatenated(): void
488  {
489  $fileName = 'fooFile.js';
490  $concatenatedFileName = 'merged_' . $fileName;
491  $testFileFixture = [
492  $fileName => [
493  'file' => $fileName,
494  'defer' => true,
495  'section' => 'top',
496  ],
497  ];
498 
499  $subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
500  $result = $subject->concatenateJsFiles($testFileFixture);
501 
502  self::assertArrayNotHasKey($concatenatedFileName, $result);
503  self::assertTrue($result[$fileName]['defer']);
504  }
505 }
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\calcFunctionMustRetainWhitespaces
‪calcFunctionMustRetainWhitespaces(string $input, string $expected)
Definition: ResourceCompressorTest.php:454
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\$backupEnvironment
‪bool $backupEnvironment
Definition: ResourceCompressorTest.php:25
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\concatenatedCssFilesObeyExcludeFromConcatenation
‪concatenatedCssFilesObeyExcludeFromConcatenation()
Definition: ResourceCompressorTest.php:239
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\concatenatedCssFilesAreSeparatedByMediaType
‪concatenatedCssFilesAreSeparatedByMediaType()
Definition: ResourceCompressorTest.php:159
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\nomoduleJavascriptIsNotConcatenated
‪nomoduleJavascriptIsNotConcatenated()
Definition: ResourceCompressorTest.php:465
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest
Definition: ResourceCompressorTest.php:24
‪TYPO3\CMS\Core\Tests\Unit\Resource
Definition: AbstractFileTest.php:18
‪TYPO3\CMS\Core\Resource\ResourceCompressor
Definition: ResourceCompressor.php:31
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\concatenateJsFileAddsAsyncPropertyIfAllFilesAreAsync
‪concatenateJsFileAddsAsyncPropertyIfAllFilesAreAsync(array $input, bool $expected)
Definition: ResourceCompressorTest.php:414
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\concatenateJsFileIsFlaggedToNotConcatenateAgain
‪concatenateJsFileIsFlaggedToNotConcatenateAgain()
Definition: ResourceCompressorTest.php:280
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\deferJavascriptIsNotConcatenated
‪deferJavascriptIsNotConcatenated()
Definition: ResourceCompressorTest.php:487
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\compressedCssFileIsFlaggedToNotCompressAgain
‪compressedCssFileIsFlaggedToNotCompressAgain()
Definition: ResourceCompressorTest.php:81
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\concatenateJsFileAsyncDataProvider
‪static concatenateJsFileAsyncDataProvider()
Definition: ResourceCompressorTest.php:304
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\calcStatementsDataProvider
‪static calcStatementsDataProvider()
Definition: ResourceCompressorTest.php:428
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\compressedJsFileIsFlaggedToNotCompressAgain
‪compressedJsFileIsFlaggedToNotCompressAgain()
Definition: ResourceCompressorTest.php:107
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\cssFixStatementsDataProvider
‪static cssFixStatementsDataProvider()
Definition: ResourceCompressorTest.php:27
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\concatenatedCssFileIsFlaggedToNotConcatenateAgain
‪concatenatedCssFileIsFlaggedToNotConcatenateAgain()
Definition: ResourceCompressorTest.php:133
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\cssFixStatementsMovesStatementsToTopIfNeeded
‪cssFixStatementsMovesStatementsToTopIfNeeded(string $input, string $expected)
Definition: ResourceCompressorTest.php:69
‪TYPO3\CMS\Core\Tests\Unit\Resource\ResourceCompressorTest\concatenatedCssFilesObeyForceOnTopOption
‪concatenatedCssFilesObeyForceOnTopOption()
Definition: ResourceCompressorTest.php:203