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