[TASK] Update CSS compressor
[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 parent::setUp();
34 $this->subject = $this->getAccessibleMock(ResourceCompressor::class, array('compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory'));
35 }
36
37 /**
38 * @return array
39 */
40 public function cssFixStatementsDataProvider() {
41 return array(
42 'nothing to do - no charset/import/namespace' => array(
43 'body { background: #ffffff; }',
44 'body { background: #ffffff; }'
45 ),
46 'import in front' => array(
47 '@import url(http://www.example.com/css); body { background: #ffffff; }',
48 'LF/* moved by compressor */LF@import url(http://www.example.com/css);LFbody { background: #ffffff; }'
49 ),
50 'import in back, without quotes' => array(
51 'body { background: #ffffff; } @import url(http://www.example.com/css);',
52 'LF/* moved by compressor */LF@import url(http://www.example.com/css);LFbody { background: #ffffff; }'
53 ),
54 'import in back, with double-quotes' => array(
55 'body { background: #ffffff; } @import url("http://www.example.com/css");',
56 'LF/* moved by compressor */LF@import url("http://www.example.com/css");LFbody { background: #ffffff; }'
57 ),
58 'import in back, with single-quotes' => array(
59 'body { background: #ffffff; } @import url(\'http://www.example.com/css\');',
60 'LF/* moved by compressor */LF@import url(\'http://www.example.com/css\');LFbody { background: #ffffff; }'
61 ),
62 'import in middle and back, without quotes' => array(
63 'body { background: #ffffff; } @import url(http://www.example.com/A); div { background: #000; } @import url(http://www.example.com/B);',
64 '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; }'
65 ),
66 );
67 }
68
69 /**
70 * @test
71 * @dataProvider cssFixStatementsDataProvider
72 * @param string $input
73 * @param string $expected
74 */
75 public function cssFixStatementsMovesStatementsToTopIfNeeded($input, $expected) {
76 $result = $this->subject->_call('cssFixStatements', $input);
77 $resultWithReadableLinefeed = str_replace(LF, 'LF', $result);
78 $this->assertEquals($expected, $resultWithReadableLinefeed);
79 }
80
81 /**
82 * @test
83 */
84 public function compressedCssFileIsFlaggedToNotCompressAgain() {
85 $fileName = 'fooFile.css';
86 $compressedFileName = $fileName . '.gzip';
87 $testFileFixture = array(
88 $fileName => array(
89 'file' => $fileName,
90 'compress' => TRUE,
91 )
92 );
93 $this->subject->expects($this->once())
94 ->method('compressCssFile')
95 ->with($fileName)
96 ->will($this->returnValue($compressedFileName));
97
98 $result = $this->subject->compressCssFiles($testFileFixture);
99
100 $this->assertArrayHasKey($compressedFileName, $result);
101 $this->assertArrayHasKey('compress', $result[$compressedFileName]);
102 $this->assertFalse($result[$compressedFileName]['compress']);
103 }
104
105 /**
106 * @test
107 */
108 public function compressedJsFileIsFlaggedToNotCompressAgain() {
109 $fileName = 'fooFile.js';
110 $compressedFileName = $fileName . '.gzip';
111 $testFileFixture = array(
112 $fileName => array(
113 'file' => $fileName,
114 'compress' => TRUE,
115 )
116 );
117 $this->subject->expects($this->once())
118 ->method('compressJsFile')
119 ->with($fileName)
120 ->will($this->returnValue($compressedFileName));
121
122 $result = $this->subject->compressJsFiles($testFileFixture);
123
124 $this->assertArrayHasKey($compressedFileName, $result);
125 $this->assertArrayHasKey('compress', $result[$compressedFileName]);
126 $this->assertFalse($result[$compressedFileName]['compress']);
127 }
128
129
130 /**
131 * @test
132 */
133 public function concatenatedCssFileIsFlaggedToNotConcatenateAgain() {
134 $fileName = 'fooFile.css';
135 $concatenatedFileName = 'merged_' . $fileName;
136 $testFileFixture = array(
137 $fileName => array(
138 'file' => $fileName,
139 'excludeFromConcatenation' => FALSE,
140 'media' => 'all',
141 )
142 );
143 $this->subject->expects($this->once())
144 ->method('createMergedCssFile')
145 ->will($this->returnValue($concatenatedFileName));
146 $this->subject->setRelativePath('');
147
148 $result = $this->subject->concatenateCssFiles($testFileFixture);
149
150 $this->assertArrayHasKey($concatenatedFileName, $result);
151 $this->assertArrayHasKey('excludeFromConcatenation', $result[$concatenatedFileName]);
152 $this->assertTrue($result[$concatenatedFileName]['excludeFromConcatenation']);
153 }
154
155 /**
156 * @test
157 */
158 public function concatenatedJsFileIsFlaggedToNotConcatenateAgain() {
159 $fileName = 'fooFile.js';
160 $concatenatedFileName = 'merged_' . $fileName;
161 $testFileFixture = array(
162 $fileName => array(
163 'file' => $fileName,
164 'excludeFromConcatenation' => FALSE,
165 'section' => 'top',
166 )
167 );
168 $this->subject->expects($this->once())
169 ->method('createMergedJsFile')
170 ->will($this->returnValue($concatenatedFileName));
171 $this->subject->setRelativePath('');
172
173 $result = $this->subject->concatenateJsFiles($testFileFixture);
174
175 $this->assertArrayHasKey($concatenatedFileName, $result);
176 $this->assertArrayHasKey('excludeFromConcatenation', $result[$concatenatedFileName]);
177 $this->assertTrue($result[$concatenatedFileName]['excludeFromConcatenation']);
178 }
179
180 /**
181 * @return array
182 */
183 public function calcStatementsDataProvider() {
184 return array(
185 'simple calc' => array(
186 'calc(100% - 3px)',
187 'calc(100% - 3px)',
188 ),
189 'complex calc with parentheses at the beginning' => array(
190 'calc((100%/20) - 2*3px)',
191 'calc((100%/20) - 2*3px)',
192 ),
193 'complex calc with parentheses at the end' => array(
194 'calc(100%/20 - 2*3px - (200px + 3%))',
195 'calc(100%/20 - 2*3px - (200px + 3%))',
196 ),
197 'complex calc with many parentheses' => array(
198 'calc((100%/20) - (2 * (3px - (200px + 3%))))',
199 'calc((100%/20) - (2 * (3px - (200px + 3%))))',
200 ),
201 );
202 }
203
204 /**
205 * @test
206 * @dataProvider calcStatementsDataProvider
207 * @param string $input
208 * @param string $expected
209 */
210 public function calcFunctionMustRetainWhitespaces($input, $expected) {
211 $result = $this->subject->_call('compressCssString', $input);
212 $this->assertSame($expected, trim($result));
213 }
214
215 /**
216 * @return array
217 */
218 public function compressCssFileContentDataProvider() {
219 $path = dirname(__FILE__) . '/ResourceCompressorTest/Fixtures/';
220 return array(
221 // File. Tests:
222 // - Stripped comments and white-space.
223 // - Retain white-space in selectors. (http://drupal.org/node/472820)
224 // - Retain pseudo-selectors. (http://drupal.org/node/460448)
225 0 => array(
226 $path . 'css_input_without_import.css',
227 $path . 'css_input_without_import.css.optimized.css'
228 ),
229 // File. Tests:
230 // - Retain comment hacks.
231 2 => array(
232 $path . 'comment_hacks.css',
233 $path . 'comment_hacks.css.optimized.css'
234 ),/*
235 // File. Tests:
236 // - Any @charset declaration at the beginning of a file should be
237 // removed without breaking subsequent CSS.*/
238 6 => array(
239 $path . 'charset_sameline.css',
240 $path . 'charset.css.optimized.css'
241 ),
242 7 => array(
243 $path . 'charset_newline.css',
244 $path . 'charset.css.optimized.css'
245 ),
246 );
247 }
248
249 /**
250 * Tests optimizing a CSS asset group.
251 *
252 * @test
253 * @dataProvider compressCssFileContentDataProvider
254 * @param string $cssFile
255 * @param string $expected
256 */
257 function compressCssFileContent($cssFile, $expected) {
258 $cssContent = file_get_contents($cssFile);
259 $compressedCss = $this->subject->_call('compressCssString', $cssContent);
260 // we have to fix relative paths, if we aren't working on a file in our target directory
261 $relativeFilename = str_replace(PATH_site, '', $cssFile);
262 if (strpos($relativeFilename, $this->subject->_get('targetDirectory')) === FALSE) {
263 $filenameRelativeToMainDir = substr($relativeFilename, strlen($this->subject->_get('backPath')));
264 $compressedCss = $this->subject->_call('cssFixRelativeUrlPaths', $compressedCss, dirname($filenameRelativeToMainDir) . '/');
265 }
266 $this->assertEquals(file_get_contents($expected), $compressedCss, 'Group of file CSS assets optimized correctly.');
267 }
268 }