[TASK] Removes extra empty lines
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Tests / Unit / Resource / ResourceCompressorTest.php
1 <?php
2 namespace TYPO3\CMS\Core\Tests\Unit\Resource;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Resource\ResourceCompressor;
18
19 /**
20 * Testcase for the ResourceCompressor class
21 */
22 class ResourceCompressorTest extends BaseTestCase
23 {
24 /**
25 * @var ResourceCompressor|\PHPUnit_Framework_MockObject_MockObject|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface
26 */
27 protected $subject;
28
29 /**
30 * Set up the test
31 */
32 protected function setUp()
33 {
34 parent::setUp();
35 $this->subject = $this->getAccessibleMock(ResourceCompressor::class, array('compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory'));
36 }
37
38 /**
39 * @return array
40 */
41 public function cssFixStatementsDataProvider()
42 {
43 return array(
44 'nothing to do - no charset/import/namespace' => array(
45 'body { background: #ffffff; }',
46 'body { background: #ffffff; }'
47 ),
48 'import in front' => array(
49 '@import url(http://www.example.com/css); body { background: #ffffff; }',
50 'LF/* moved by compressor */LF@import url(http://www.example.com/css);LFbody { background: #ffffff; }'
51 ),
52 'import in back, without quotes' => array(
53 'body { background: #ffffff; } @import url(http://www.example.com/css);',
54 'LF/* moved by compressor */LF@import url(http://www.example.com/css);LFbody { background: #ffffff; }'
55 ),
56 'import in back, with double-quotes' => array(
57 'body { background: #ffffff; } @import url("http://www.example.com/css");',
58 'LF/* moved by compressor */LF@import url("http://www.example.com/css");LFbody { background: #ffffff; }'
59 ),
60 'import in back, with single-quotes' => array(
61 'body { background: #ffffff; } @import url(\'http://www.example.com/css\');',
62 'LF/* moved by compressor */LF@import url(\'http://www.example.com/css\');LFbody { background: #ffffff; }'
63 ),
64 'import in middle and back, without quotes' => array(
65 'body { background: #ffffff; } @import url(http://www.example.com/A); div { background: #000; } @import url(http://www.example.com/B);',
66 'LF/* moved by compressor */LF@import url(http://www.example.com/A);LF/* moved by compressor */LF@import url(http://www.example.com/B);LFbody { background: #ffffff; } div { background: #000; }'
67 ),
68 );
69 }
70
71 /**
72 * @test
73 * @dataProvider cssFixStatementsDataProvider
74 * @param string $input
75 * @param string $expected
76 */
77 public function cssFixStatementsMovesStatementsToTopIfNeeded($input, $expected)
78 {
79 $result = $this->subject->_call('cssFixStatements', $input);
80 $resultWithReadableLinefeed = str_replace(LF, 'LF', $result);
81 $this->assertEquals($expected, $resultWithReadableLinefeed);
82 }
83
84 /**
85 * @test
86 */
87 public function compressedCssFileIsFlaggedToNotCompressAgain()
88 {
89 $fileName = 'fooFile.css';
90 $compressedFileName = $fileName . '.gzip';
91 $testFileFixture = array(
92 $fileName => array(
93 'file' => $fileName,
94 'compress' => true,
95 )
96 );
97 $this->subject->expects($this->once())
98 ->method('compressCssFile')
99 ->with($fileName)
100 ->will($this->returnValue($compressedFileName));
101
102 $result = $this->subject->compressCssFiles($testFileFixture);
103
104 $this->assertArrayHasKey($compressedFileName, $result);
105 $this->assertArrayHasKey('compress', $result[$compressedFileName]);
106 $this->assertFalse($result[$compressedFileName]['compress']);
107 }
108
109 /**
110 * @test
111 */
112 public function compressedJsFileIsFlaggedToNotCompressAgain()
113 {
114 $fileName = 'fooFile.js';
115 $compressedFileName = $fileName . '.gzip';
116 $testFileFixture = array(
117 $fileName => array(
118 'file' => $fileName,
119 'compress' => true,
120 )
121 );
122 $this->subject->expects($this->once())
123 ->method('compressJsFile')
124 ->with($fileName)
125 ->will($this->returnValue($compressedFileName));
126
127 $result = $this->subject->compressJsFiles($testFileFixture);
128
129 $this->assertArrayHasKey($compressedFileName, $result);
130 $this->assertArrayHasKey('compress', $result[$compressedFileName]);
131 $this->assertFalse($result[$compressedFileName]['compress']);
132 }
133
134 /**
135 * @test
136 */
137 public function concatenatedCssFileIsFlaggedToNotConcatenateAgain()
138 {
139 $fileName = 'fooFile.css';
140 $concatenatedFileName = 'merged_' . $fileName;
141 $testFileFixture = array(
142 $fileName => array(
143 'file' => $fileName,
144 'excludeFromConcatenation' => false,
145 'media' => 'all',
146 )
147 );
148 $this->subject->expects($this->once())
149 ->method('createMergedCssFile')
150 ->will($this->returnValue($concatenatedFileName));
151 $this->subject->setRelativePath('');
152
153 $result = $this->subject->concatenateCssFiles($testFileFixture);
154
155 $this->assertArrayHasKey($concatenatedFileName, $result);
156 $this->assertArrayHasKey('excludeFromConcatenation', $result[$concatenatedFileName]);
157 $this->assertTrue($result[$concatenatedFileName]['excludeFromConcatenation']);
158 }
159
160 /**
161 * @test
162 */
163 public function concatenatedCssFilesAreSeparatedByMediaType()
164 {
165 $allFileName = 'allFile.css';
166 $screenFileName1 = 'screenFile.css';
167 $screenFileName2 = 'screenFile2.css';
168 $testFileFixture = array(
169 $allFileName => array(
170 'file' => $allFileName,
171 'excludeFromConcatenation' => false,
172 'media' => 'all',
173 ),
174 // use two screen files to check if they are merged into one, even with a different media type
175 $screenFileName1 => array(
176 'file' => $screenFileName1,
177 'excludeFromConcatenation' => false,
178 'media' => 'screen',
179 ),
180 $screenFileName2 => array(
181 'file' => $screenFileName2,
182 'excludeFromConcatenation' => false,
183 'media' => 'screen',
184 ),
185 );
186 $this->subject->expects($this->exactly(2))
187 ->method('createMergedCssFile')
188 ->will($this->onConsecutiveCalls(
189 $this->returnValue('merged_' . $allFileName),
190 $this->returnValue('merged_' . $screenFileName1)
191 ));
192 $this->subject->setRelativePath('');
193
194 $result = $this->subject->concatenateCssFiles($testFileFixture);
195
196 $this->assertEquals(array(
197 'merged_' . $allFileName,
198 'merged_' . $screenFileName1
199 ), array_keys($result));
200 $this->assertEquals('all', $result['merged_' . $allFileName]['media']);
201 $this->assertEquals('screen', $result['merged_' . $screenFileName1]['media']);
202 }
203
204 /**
205 * @test
206 */
207 public function concatenatedCssFilesObeyForceOnTopOption()
208 {
209 $screen1FileName = 'screen1File.css';
210 $screen2FileName = 'screen2File.css';
211 $screen3FileName = 'screen3File.css';
212 $testFileFixture = array(
213 $screen1FileName => array(
214 'file' => $screen1FileName,
215 'excludeFromConcatenation' => false,
216 'media' => 'screen',
217 ),
218 $screen2FileName => array(
219 'file' => $screen2FileName,
220 'excludeFromConcatenation' => false,
221 'media' => 'screen',
222 ),
223 $screen3FileName => array(
224 'file' => $screen3FileName,
225 'excludeFromConcatenation' => false,
226 'forceOnTop' => true,
227 'media' => 'screen',
228 ),
229 );
230 // Replace mocked method getFilenameFromMainDir by passthrough callback
231 $this->subject->expects($this->any())->method('getFilenameFromMainDir')->willReturnArgument(0);
232 $this->subject->expects($this->once())
233 ->method('createMergedCssFile')
234 ->with($this->equalTo(array($screen3FileName, $screen1FileName, $screen2FileName)));
235 $this->subject->setRelativePath('');
236
237 $this->subject->concatenateCssFiles($testFileFixture);
238 }
239
240 /**
241 * @test
242 */
243 public function concatenatedCssFilesObeyExcludeFromConcatenation()
244 {
245 $screen1FileName = 'screen1File.css';
246 $screen2FileName = 'screen2File.css';
247 $screen3FileName = 'screen3File.css';
248 $testFileFixture = array(
249 $screen1FileName => array(
250 'file' => $screen1FileName,
251 'excludeFromConcatenation' => false,
252 'media' => 'screen',
253 ),
254 $screen2FileName => array(
255 'file' => $screen2FileName,
256 'excludeFromConcatenation' => true,
257 'media' => 'screen',
258 ),
259 $screen3FileName => array(
260 'file' => $screen3FileName,
261 'excludeFromConcatenation' => false,
262 'media' => 'screen',
263 ),
264 );
265 $this->subject->expects($this->any())->method('getFilenameFromMainDir')->willReturnArgument(0);
266 $this->subject->expects($this->once())
267 ->method('createMergedCssFile')
268 ->with($this->equalTo(array($screen1FileName, $screen3FileName)))
269 ->will($this->returnValue('merged_screen'));
270 $this->subject->setRelativePath('');
271
272 $result = $this->subject->concatenateCssFiles($testFileFixture);
273 $this->assertEquals(array(
274 $screen2FileName,
275 'merged_screen'
276 ), array_keys($result));
277 $this->assertEquals('screen', $result[$screen2FileName]['media']);
278 $this->assertEquals('screen', $result['merged_screen']['media']);
279 }
280
281 /**
282 * @test
283 */
284 public function concatenatedJsFileIsFlaggedToNotConcatenateAgain()
285 {
286 $fileName = 'fooFile.js';
287 $concatenatedFileName = 'merged_' . $fileName;
288 $testFileFixture = array(
289 $fileName => array(
290 'file' => $fileName,
291 'excludeFromConcatenation' => false,
292 'section' => 'top',
293 )
294 );
295 $this->subject->expects($this->once())
296 ->method('createMergedJsFile')
297 ->will($this->returnValue($concatenatedFileName));
298 $this->subject->setRelativePath('');
299
300 $result = $this->subject->concatenateJsFiles($testFileFixture);
301
302 $this->assertArrayHasKey($concatenatedFileName, $result);
303 $this->assertArrayHasKey('excludeFromConcatenation', $result[$concatenatedFileName]);
304 $this->assertTrue($result[$concatenatedFileName]['excludeFromConcatenation']);
305 }
306
307 /**
308 * @return array
309 */
310 public function calcStatementsDataProvider()
311 {
312 return array(
313 'simple calc' => array(
314 'calc(100% - 3px)',
315 'calc(100% - 3px)',
316 ),
317 'complex calc with parentheses at the beginning' => array(
318 'calc((100%/20) - 2*3px)',
319 'calc((100%/20) - 2*3px)',
320 ),
321 'complex calc with parentheses at the end' => array(
322 'calc(100%/20 - 2*3px - (200px + 3%))',
323 'calc(100%/20 - 2*3px - (200px + 3%))',
324 ),
325 'complex calc with many parentheses' => array(
326 'calc((100%/20) - (2 * (3px - (200px + 3%))))',
327 'calc((100%/20) - (2 * (3px - (200px + 3%))))',
328 ),
329 );
330 }
331
332 /**
333 * @test
334 * @dataProvider calcStatementsDataProvider
335 * @param string $input
336 * @param string $expected
337 */
338 public function calcFunctionMustRetainWhitespaces($input, $expected)
339 {
340 $result = $this->subject->_call('compressCssString', $input);
341 $this->assertSame($expected, trim($result));
342 }
343
344 /**
345 * @return array
346 */
347 public function compressCssFileContentDataProvider()
348 {
349 $path = dirname(__FILE__) . '/ResourceCompressorTest/Fixtures/';
350 return array(
351 // File. Tests:
352 // - Stripped comments and white-space.
353 // - Retain white-space in selectors. (http://drupal.org/node/472820)
354 // - Retain pseudo-selectors. (http://drupal.org/node/460448)
355 0 => array(
356 $path . 'css_input_without_import.css',
357 $path . 'css_input_without_import.css.optimized.css'
358 ),
359 // File. Tests:
360 // - Retain comment hacks.
361 2 => array(
362 $path . 'comment_hacks.css',
363 $path . 'comment_hacks.css.optimized.css'
364 ),/*
365 // File. Tests:
366 // - Any @charset declaration at the beginning of a file should be
367 // removed without breaking subsequent CSS.*/
368 6 => array(
369 $path . 'charset_sameline.css',
370 $path . 'charset.css.optimized.css'
371 ),
372 7 => array(
373 $path . 'charset_newline.css',
374 $path . 'charset.css.optimized.css'
375 ),
376 );
377 }
378
379 /**
380 * Tests optimizing a CSS asset group.
381 *
382 * @test
383 * @dataProvider compressCssFileContentDataProvider
384 * @param string $cssFile
385 * @param string $expected
386 */
387 public function compressCssFileContent($cssFile, $expected)
388 {
389 $cssContent = file_get_contents($cssFile);
390 $compressedCss = $this->subject->_call('compressCssString', $cssContent);
391 // we have to fix relative paths, if we aren't working on a file in our target directory
392 $relativeFilename = str_replace(PATH_site, '', $cssFile);
393 if (strpos($relativeFilename, $this->subject->_get('targetDirectory')) === false) {
394 $filenameRelativeToMainDir = substr($relativeFilename, strlen($this->subject->_get('backPath')));
395 $compressedCss = $this->subject->_call('cssFixRelativeUrlPaths', $compressedCss, dirname($filenameRelativeToMainDir) . '/');
396 }
397 $this->assertEquals(file_get_contents($expected), $compressedCss, 'Group of file CSS assets optimized correctly.');
398 }
399 }