TYPO3 CMS  TYPO3_7-6
ResourceCompressorTest.php
Go to the documentation of this file.
1 <?php
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 
18 
23 {
27  protected $subject;
28 
32  protected function setUp()
33  {
34  parent::setUp();
35  $this->subject = $this->getAccessibleMock(ResourceCompressor::class, ['compressCssFile', 'compressJsFile', 'createMergedCssFile', 'createMergedJsFile', 'getFilenameFromMainDir', 'checkBaseDirectory']);
36  }
37 
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 
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 
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 
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 
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  $this->subject->setRelativePath('');
160 
161  $result = $this->subject->concatenateCssFiles($testFileFixture);
162 
163  $this->assertArrayHasKey($concatenatedFileName, $result);
164  $this->assertArrayHasKey('excludeFromConcatenation', $result[$concatenatedFileName]);
165  $this->assertTrue($result[$concatenatedFileName]['excludeFromConcatenation']);
166  }
167 
172  {
173  $allFileName = 'allFile.css';
174  $screenFileName1 = 'screenFile.css';
175  $screenFileName2 = 'screenFile2.css';
176  $testFileFixture = [
177  $allFileName => [
178  'file' => $allFileName,
179  'excludeFromConcatenation' => false,
180  'media' => 'all',
181  ],
182  // use two screen files to check if they are merged into one, even with a different media type
183  $screenFileName1 => [
184  'file' => $screenFileName1,
185  'excludeFromConcatenation' => false,
186  'media' => 'screen',
187  ],
188  $screenFileName2 => [
189  'file' => $screenFileName2,
190  'excludeFromConcatenation' => false,
191  'media' => 'screen',
192  ],
193  ];
194  $this->subject->expects($this->exactly(2))
195  ->method('createMergedCssFile')
196  ->will($this->onConsecutiveCalls(
197  $this->returnValue('merged_' . $allFileName),
198  $this->returnValue('merged_' . $screenFileName1)
199  ));
200  $this->subject->setRelativePath('');
201 
202  $result = $this->subject->concatenateCssFiles($testFileFixture);
203 
204  $this->assertEquals([
205  'merged_' . $allFileName,
206  'merged_' . $screenFileName1
207  ], array_keys($result));
208  $this->assertEquals('all', $result['merged_' . $allFileName]['media']);
209  $this->assertEquals('screen', $result['merged_' . $screenFileName1]['media']);
210  }
211 
216  {
217  $screen1FileName = 'screen1File.css';
218  $screen2FileName = 'screen2File.css';
219  $screen3FileName = 'screen3File.css';
220  $testFileFixture = [
221  $screen1FileName => [
222  'file' => $screen1FileName,
223  'excludeFromConcatenation' => false,
224  'media' => 'screen',
225  ],
226  $screen2FileName => [
227  'file' => $screen2FileName,
228  'excludeFromConcatenation' => false,
229  'media' => 'screen',
230  ],
231  $screen3FileName => [
232  'file' => $screen3FileName,
233  'excludeFromConcatenation' => false,
234  'forceOnTop' => true,
235  'media' => 'screen',
236  ],
237  ];
238  // Replace mocked method getFilenameFromMainDir by passthrough callback
239  $this->subject->expects($this->any())->method('getFilenameFromMainDir')->willReturnArgument(0);
240  $this->subject->expects($this->once())
241  ->method('createMergedCssFile')
242  ->with($this->equalTo([$screen3FileName, $screen1FileName, $screen2FileName]));
243  $this->subject->setRelativePath('');
244 
245  $this->subject->concatenateCssFiles($testFileFixture);
246  }
247 
252  {
253  $screen1FileName = 'screen1File.css';
254  $screen2FileName = 'screen2File.css';
255  $screen3FileName = 'screen3File.css';
256  $testFileFixture = [
257  $screen1FileName => [
258  'file' => $screen1FileName,
259  'excludeFromConcatenation' => false,
260  'media' => 'screen',
261  ],
262  $screen2FileName => [
263  'file' => $screen2FileName,
264  'excludeFromConcatenation' => true,
265  'media' => 'screen',
266  ],
267  $screen3FileName => [
268  'file' => $screen3FileName,
269  'excludeFromConcatenation' => false,
270  'media' => 'screen',
271  ],
272  ];
273  $this->subject->expects($this->any())->method('getFilenameFromMainDir')->willReturnArgument(0);
274  $this->subject->expects($this->once())
275  ->method('createMergedCssFile')
276  ->with($this->equalTo([$screen1FileName, $screen3FileName]))
277  ->will($this->returnValue('merged_screen'));
278  $this->subject->setRelativePath('');
279 
280  $result = $this->subject->concatenateCssFiles($testFileFixture);
281  $this->assertEquals([
282  $screen2FileName,
283  'merged_screen'
284  ], array_keys($result));
285  $this->assertEquals('screen', $result[$screen2FileName]['media']);
286  $this->assertEquals('screen', $result['merged_screen']['media']);
287  }
288 
293  {
294  $fileName = 'fooFile.js';
295  $concatenatedFileName = 'merged_' . $fileName;
296  $testFileFixture = [
297  $fileName => [
298  'file' => $fileName,
299  'excludeFromConcatenation' => false,
300  'section' => 'top',
301  ]
302  ];
303  $this->subject->expects($this->once())
304  ->method('createMergedJsFile')
305  ->will($this->returnValue($concatenatedFileName));
306  $this->subject->setRelativePath('');
307 
308  $result = $this->subject->concatenateJsFiles($testFileFixture);
309 
310  $this->assertArrayHasKey($concatenatedFileName, $result);
311  $this->assertArrayHasKey('excludeFromConcatenation', $result[$concatenatedFileName]);
312  $this->assertTrue($result[$concatenatedFileName]['excludeFromConcatenation']);
313  }
314 
318  public function calcStatementsDataProvider()
319  {
320  return [
321  'simple calc' => [
322  'calc(100% - 3px)',
323  'calc(100% - 3px)',
324  ],
325  'complex calc with parentheses at the beginning' => [
326  'calc((100%/20) - 2*3px)',
327  'calc((100%/20) - 2*3px)',
328  ],
329  'complex calc with parentheses at the end' => [
330  'calc(100%/20 - 2*3px - (200px + 3%))',
331  'calc(100%/20 - 2*3px - (200px + 3%))',
332  ],
333  'complex calc with many parentheses' => [
334  'calc((100%/20) - (2 * (3px - (200px + 3%))))',
335  'calc((100%/20) - (2 * (3px - (200px + 3%))))',
336  ],
337  ];
338  }
339 
346  public function calcFunctionMustRetainWhitespaces($input, $expected)
347  {
348  $result = $this->subject->_call('compressCssString', $input);
349  $this->assertSame($expected, trim($result));
350  }
351 
356  {
357  $path = dirname(__FILE__) . '/ResourceCompressorTest/Fixtures/';
358  return [
359  // File. Tests:
360  // - Stripped comments and white-space.
361  // - Retain white-space in selectors. (http://drupal.org/node/472820)
362  // - Retain pseudo-selectors. (http://drupal.org/node/460448)
363  0 => [
364  $path . 'css_input_without_import.css',
365  $path . 'css_input_without_import.css.optimized.css'
366  ],
367  // File. Tests:
368  // - Retain comment hacks.
369  2 => [
370  $path . 'comment_hacks.css',
371  $path . 'comment_hacks.css.optimized.css'
372  ], /*
373  // File. Tests:
374  // - Any @charset declaration at the beginning of a file should be
375  // removed without breaking subsequent CSS.*/
376  6 => [
377  $path . 'charset_sameline.css',
378  $path . 'charset.css.optimized.css'
379  ],
380  7 => [
381  $path . 'charset_newline.css',
382  $path . 'charset.css.optimized.css'
383  ],
384  ];
385  }
386 
395  public function compressCssFileContent($cssFile, $expected)
396  {
397  $cssContent = file_get_contents($cssFile);
398  $compressedCss = $this->subject->_call('compressCssString', $cssContent);
399  // we have to fix relative paths, if we aren't working on a file in our target directory
400  $relativeFilename = str_replace(PATH_site, '', $cssFile);
401  if (strpos($relativeFilename, $this->subject->_get('targetDirectory')) === false) {
402  $filenameRelativeToMainDir = substr($relativeFilename, strlen($this->subject->_get('backPath')));
403  $compressedCss = $this->subject->_call('cssFixRelativeUrlPaths', $compressedCss, dirname($filenameRelativeToMainDir) . '/');
404  }
405  $this->assertEquals(file_get_contents($expected), $compressedCss, 'Group of file CSS assets optimized correctly.');
406  }
407 }
getAccessibleMock( $originalClassName, $methods=[], array $arguments=[], $mockClassName='', $callOriginalConstructor=true, $callOriginalClone=true, $callAutoload=true)