Merge branch 'wip_MDL-46552_m27_memcached' of https://github.com/skodak/moodle into...
[moodle.git] / lib / tests / csslib_test.php
blob5a9f02c5bc704127b9dfcfdf3beb035d5b54789b
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * This file contains the unittests for the css optimiser in csslib.php
20 * @package core_css
21 * @category phpunit
22 * @copyright 2012 Sam Hemelryk
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
26 defined('MOODLE_INTERNAL') || die();
28 global $CFG;
29 require_once($CFG->libdir . '/csslib.php');
32 /**
33 * CSS optimiser test class.
35 * @package core_css
36 * @category phpunit
37 * @copyright 2012 Sam Hemelryk
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
40 class core_csslib_testcase extends advanced_testcase {
42 /**
43 * Returns a CSS optimiser
45 * @return css_optimiser
47 protected function get_optimiser() {
48 return new css_optimiser();
51 /**
52 * Background colour tests.
54 * When testing background styles it is important to understand how the background shorthand works.
55 * background shorthand allows the following styles to be specified in a single "background" declaration:
56 * - background-color
57 * - background-image
58 * - background-repeat
59 * - background-attachment
60 * - background-position
62 * If the background shorthand is used it can contain one or more of those (preferabbly in that order).
63 * Important it does not need to contain all of them.
64 * However even if it doesn't contain values for all styles all of the styles will be affected.
65 * If a style is missed from the shorthand background style but has an existing value in the rule then the existing value
66 * is cleared.
68 * For example:
69 * .test {background: url(\'test.png\');background: bottom right;background:#123456;}
70 * will result in:
71 * .test {background:#123456;}
73 * And:
74 * .test {background-image: url(\'test.png\');background:#123456;}
75 * will result in:
76 * .test {background:#123456;}
78 public function test_background() {
79 $optimiser = new css_optimiser();
81 $cssin = '.test {background-color: #123456;}';
82 $cssout = '.test{background-color:#123456;}';
83 $this->assertSame($cssout, $optimiser->process($cssin));
85 $cssin = '.test {background: #123456;}';
86 $cssout = '.test{background:#123456;}';
87 $this->assertSame($cssout, $optimiser->process($cssin));
89 $cssin = '.test {background-image: url(\'test.png\');}';
90 $cssout = '.test{background-image:url(\'test.png\');}';
91 $this->assertSame($cssout, $optimiser->process($cssin));
93 $cssin = '.test {background: #123456 url(\'test.png\') no-repeat top left;}';
94 $cssout = '.test{background:#123456 url(\'test.png\') no-repeat top left;}';
95 $this->assertSame($cssout, $optimiser->process($cssin));
97 // Check out this for madness, background position and background-repeat have been reversed.
98 $cssin = '.test {background: #123456 url(\'test.png\') center no-repeat;}';
99 $cssout = '.test{background:#123456 url(\'test.png\') no-repeat center;}';
100 $this->assertSame($cssout, $optimiser->process($cssin));
102 $cssin = '.test {background: url(\'test.png\') no-repeat top left;}.test{background-position: bottom right}.test {background-color:#123456;}';
103 $cssout = '.test{background:#123456 url(\'test.png\') no-repeat bottom right;}';
104 $this->assertSame($cssout, $optimiser->process($cssin));
106 $cssin = '.test {background: url( \'test.png\' )}.test{background: bottom right}.test {background:#123456;}';
107 $cssout = '.test{background:#123456;}';
108 $this->assertSame($cssout, $optimiser->process($cssin));
110 $cssin = '.test {background-image: url(\'test.png\');background:#123456;}';
111 $cssout = '.test{background:#123456;}';
112 $this->assertSame($cssout, $optimiser->process($cssin));
114 $cssin = '.test {background-color: #123456;background-repeat: repeat-x; background-position: 100% 0%;}';
115 $cssout = '.test{background-color:#123456;background-repeat:repeat-x;background-position:100% 0%;}';
116 $this->assertSame($cssout, $optimiser->process($cssin));
118 $cssin = '.tree_item.branch {background-image: url([[pix:t/expanded]]);background-position: 0 10%;background-repeat: no-repeat;}
119 .tree_item.branch.navigation_node {background-image:none;padding-left:0;}';
120 $cssout = '.tree_item.branch{background-image:url([[pix:t/expanded]]);background-position:0 10%;background-repeat:no-repeat;} .tree_item.branch.navigation_node{background-image:none;padding-left:0;}';
121 $this->assertSame($cssout, $optimiser->process($cssin));
123 $cssin = '#nextLink{background:url(data:image/gif;base64,AAAA);}';
124 $cssout = '#nextLink{background:url(data:image/gif;base64,AAAA);}';
125 $this->assertSame($cssout, $optimiser->process($cssin));
127 $cssin = '#nextLink{background-image:url(data:image/gif;base64,AAAA);}';
128 $cssout = '#nextLink{background-image:url(data:image/gif;base64,AAAA);}';
129 $this->assertSame($cssout, $optimiser->process($cssin));
131 $cssin = '.test {background: #123456 url(data:image/gif;base64,AAAA) no-repeat top left;}';
132 $cssout = '.test{background:#123456 url(data:image/gif;base64,AAAA) no-repeat top left;}';
133 $this->assertSame($cssout, $optimiser->process($cssin));
135 $cssin = '#test {background-image:none;background-position:right center;background-repeat:no-repeat;}';
136 $cssout = '#test{background-image:none;background-position:right center;background-repeat:no-repeat;}';
137 $this->assertSame($cssout, $optimiser->process($cssin));
139 $cssin = '.test {background: url([[pix:theme|photos]]) no-repeat 50% 50%;background-size: 40px 40px;-webkit-background-size: 40px 40px;}';
140 $cssout = '.test{background:url([[pix:theme|photos]]) no-repeat 50% 50%;background-size:40px 40px;-webkit-background-size:40px 40px;}';
141 $this->assertSame($cssout, $optimiser->process($cssin));
143 $cssin = '.test{background-image: -o-linear-gradient(#3c3c3c, #111);background-image: linear-gradient(#3c3c3c, #111);}';
144 $cssout = '.test{background-image:-o-linear-gradient(#3c3c3c, #111);background-image:linear-gradient(#3c3c3c, #111);}';
145 $this->assertSame($cssout, $optimiser->process($cssin));
147 $cssin = '.test{background-image: -moz-linear-gradient(#3c3c3c, #111);background-image: -webkit-linear-gradient(#3c3c3c, #111);background-image: -o-linear-gradient(#3c3c3c, #111);background-image: linear-gradient(#3c3c3c, #111);background-image: url(/test.png);}';
148 $cssout = '.test{background-image:url(/test.png);background-image:-moz-linear-gradient(#3c3c3c, #111);background-image:-webkit-linear-gradient(#3c3c3c, #111);background-image:-o-linear-gradient(#3c3c3c, #111);background-image:linear-gradient(#3c3c3c, #111);}';
149 $this->assertSame($cssout, $optimiser->process($cssin));
151 $cssin = '.test{background:#CCC; background-image: url(test.png);}';
152 $cssout = '.test{background:#CCC url(test.png);}';
153 $this->assertSame($cssout, $optimiser->process($cssin));
155 $cssin = '.test{background:#CCC; background-image: linear-gradient(#3c3c3c, #111);}';
156 $cssout = '.test{background:#CCC;background-image:linear-gradient(#3c3c3c, #111);}';
157 $this->assertSame($cssout, $optimiser->process($cssin));
159 $cssin = '.test{background:#CCC; background-image: -o-linear-gradient(#3c3c3c, #111);background-image: linear-gradient(#3c3c3c, #111);}';
160 $cssout = '.test{background:#CCC;background-image:-o-linear-gradient(#3c3c3c, #111);background-image:linear-gradient(#3c3c3c, #111);}';
161 $this->assertSame($cssout, $optimiser->process($cssin));
163 $cssin = '#newmessageoverlay{font-weight: normal; border: 1px solid #222; background: #444; color: #ddd; text-shadow: 0 -1px 0px #000; background-image: -moz-linear-gradient(top, #333 0%, #333 5%, #444 15%, #444 60%, #222 100%); background-image: -webkit-gradient(linear, center top, center bottom, color-stop(0, #333), color-stop(5%, #333), color-stop(15%, #444), color-stop(60%, #444), color-stop(1, #222)); -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorStr=\'#333333\', EndColorStr=\'#222222\')"; padding:20px; padding-left: 0px; padding-right: 10px; position: inherit; z-index: 9999; width: 90%; margin-left: auto; margin-right: auto; height: 100%;}';
164 $cssout = '#newmessageoverlay{font-weight:normal;border:1px solid #222;background:#444;color:#DDD;text-shadow:0 -1px 0px #000;-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorStr=\'#333333\', EndColorStr=\'#222222\')";padding:20px 10px 20px 0;position:inherit;z-index:9999;width:90%;margin-left:auto;margin-right:auto;height:100%;background-image:-moz-linear-gradient(top, #333 0%, #333 5%, #444 15%, #444 60%, #222 100%);background-image:-webkit-gradient(linear, center top, center bottom, color-stop(0, #333), color-stop(5%, #333), color-stop(15%, #444), color-stop(60%, #444), color-stop(1, #222));}';
165 $this->assertSame($cssout, $optimiser->process($cssin));
167 $cssin = '.userenrolment {background-color:inherit !important;background: inherit !important;}';
168 $cssout = '.userenrolment{background:inherit !important;}';
169 $this->assertSame($cssout, $optimiser->process($cssin));
171 $cssin = '.userenrolment {background-image:url(test.png) !important;background: inherit !important;}';
172 $cssout = '.userenrolment{background:inherit !important;}';
173 $this->assertSame($cssout, $optimiser->process($cssin));
175 $cssin = '.userenrolment {background: inherit !important;background-image:url(test.png) !important;}';
176 $cssout = '.userenrolment{background:inherit url(test.png) !important;}';
177 $this->assertSame($cssout, $optimiser->process($cssin));
179 $cssin = '.userenrolment {background: inherit !important;background-image:url(test.png);}';
180 $cssout = '.userenrolment{background:inherit !important;}';
181 $this->assertSame($cssout, $optimiser->process($cssin));
183 $css = '#filesskin .yui3-widget-hd{background:#CCC;background:-webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC));background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC);}';
184 $this->assertSame($css, $optimiser->process($css));
186 $css = '.userenrolment{background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC) !important;}';
187 $this->assertSame($css, $optimiser->process($css));
189 $css = '.userenrolment{background:#CCC !important;background:-webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC)) !important;background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC) !important;}';
190 $this->assertSame($css, $optimiser->process($css));
192 $cssin = '.userenrolment{background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC) !important;}.userenrolment {background: #CCCCCC!important;background: -webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC))!important;}';
193 $cssout = '.userenrolment{background:#CCC !important;background:-moz-linear-gradient(top, #FFFFFF, #CCCCCC) !important;background:-webkit-gradient(linear, left top, left bottom, from(#FFFFFF), to(#CCCCCC)) !important;}';
194 $this->assertSame($cssout, $optimiser->process($cssin));
198 * Border tests.
200 public function test_borders() {
201 $optimiser = new css_optimiser();
203 $cssin = '.test {border: 1px solid #654321} .test {border-bottom-color: #123456}';
204 $cssout = '.test{border:1px solid;border-color:#654321 #654321 #123456;}';
205 $this->assertSame($cssout, $optimiser->process($cssin));
207 $cssin = '.one {border:1px solid red;}';
208 $cssout = '.one{border:1px solid #F00;}';
209 $this->assertSame($cssout, $optimiser->process($cssin));
211 $cssin = '.one {border:1px solid;} .one {border:2px dotted #DDD;}';
212 $cssout = '.one{border:2px dotted #DDD;}';
213 $this->assertSame($cssout, $optimiser->process($cssin));
215 $cssin = '.one {border:2px dotted #DDD;}.one {border:1px solid;} ';
216 $cssout = '.one{border:1px solid #DDD;}';
217 $this->assertSame($cssout, $optimiser->process($cssin));
219 $cssin = '.one, .two {border:1px solid red;}';
220 $cssout = ".one, .two{border:1px solid #F00;}";
221 $this->assertSame($cssout, $optimiser->process($cssin));
223 $cssin = '.one, .two {border:0px;}';
224 $cssout = ".one, .two{border-width:0;}";
225 $this->assertSame($cssout, $optimiser->process($cssin));
227 $cssin = '.one, .two {border: thin;}';
228 $cssout = ".one, .two{border-width:thin;}";
229 $this->assertSame($cssout, $optimiser->process($cssin));
231 $cssin = '.one, .two {border: thin solid black;}';
232 $cssout = ".one, .two{border:thin solid #000;}";
233 $this->assertSame($cssout, $optimiser->process($cssin));
235 $cssin = '.one, .two {border-top: 5px solid white;}';
236 $cssout = ".one, .two{border-top:5px solid #FFF;}";
237 $this->assertSame($cssout, $optimiser->process($cssin));
239 $cssin = '.one {border:1px solid red;} .two {border:1px solid red;}';
240 $cssout = ".one, .two{border:1px solid #F00;}";
241 $this->assertSame($cssout, $optimiser->process($cssin));
243 $cssin = '.one {border:1px solid red;width:20px;} .two {border:1px solid red;height:20px;}';
244 $cssout = ".one{border:1px solid #F00;width:20px;} .two{border:1px solid #F00;height:20px;}";
245 $this->assertSame($cssout, $optimiser->process($cssin));
247 $cssin = '.test {border: 1px solid #123456;} .test {border-color: #654321}';
248 $cssout = '.test{border:1px solid #654321;}';
249 $this->assertSame($cssout, $optimiser->process($cssin));
251 $cssin = '.test {border-width: 1px; border-style: solid; border-color: #123456;}';
252 $cssout = '.test{border:1px solid #123456;}';
253 $this->assertSame($cssout, $optimiser->process($cssin));
255 $cssin = '.test {border:1px solid #123456;border-top:2px dotted #654321;}';
256 $cssout = '.test{border:1px solid #123456;border-top:2px dotted #654321;}';
257 $this->assertSame($cssout, $optimiser->process($cssin));
259 $cssin = '.test {border:1px solid #123456;border-left:2px dotted #654321;}';
260 $cssout = '.test{border:1px solid #123456;border-left:2px dotted #654321;}';
261 $this->assertSame($cssout, $optimiser->process($cssin));
263 $cssin = '.test {border-left:2px dotted #654321;border:1px solid #123456;}';
264 $cssout = '.test{border:1px solid #123456;}';
265 $this->assertSame($cssout, $optimiser->process($cssin));
267 $cssin = '.test {border:1px solid;border-top-color:#123456;}';
268 $cssout = '.test{border:1px solid;border-top-color:#123456;}';
269 $this->assertSame($cssout, $optimiser->process($cssin));
271 $cssin = '.test {border:1px solid;border-top-color:#111; border-bottom-color: #222;border-left-color: #333;}';
272 $cssout = '.test{border:1px solid;border-top-color:#111;border-bottom-color:#222;border-left-color:#333;}';
273 $this->assertSame($cssout, $optimiser->process($cssin));
275 $cssin = '.test {border:1px solid;border-top-color:#111; border-bottom-color: #222;border-left-color: #333;border-right-color:#444;}';
276 $cssout = '.test{border:1px solid;border-color:#111 #444 #222 #333;}';
277 $this->assertSame($cssout, $optimiser->process($cssin));
279 $cssin = '.generaltable .cell {border-color:#EEE;} .generaltable .cell {border-width: 1px;border-style: solid;}';
280 $cssout = '.generaltable .cell{border:1px solid #EEE;}';
281 $this->assertSame($cssout, $optimiser->process($cssin));
283 $cssin = '#page-admin-roles-override .rolecap {border:none;border-bottom:1px solid #CECECE;}';
284 $cssout = '#page-admin-roles-override .rolecap{border-top:0;border-right:0;border-bottom:1px solid #CECECE;border-left:0;}';
285 $this->assertSame($cssout, $optimiser->process($cssin));
289 * Test colour styles.
291 public function test_colors() {
292 $optimiser = new css_optimiser();
294 $css = '.css{}';
295 $this->assertSame($css, $optimiser->process($css));
297 $css = '.css{color:#123456;}';
298 $this->assertSame($css, $optimiser->process($css));
300 $css = '#some{color:#123456;}';
301 $this->assertSame($css, $optimiser->process($css));
303 $css = 'div{color:#123456;}';
304 $this->assertSame($css, $optimiser->process($css));
306 $css = 'div.css{color:#123456;}';
307 $this->assertSame($css, $optimiser->process($css));
309 $css = 'div#some{color:#123456;}';
310 $this->assertSame($css, $optimiser->process($css));
312 $css = 'div[type=blah]{color:#123456;}';
313 $this->assertSame($css, $optimiser->process($css));
315 $css = 'div.css[type=blah]{color:#123456;}';
316 $this->assertSame($css, $optimiser->process($css));
318 $css = 'div#some[type=blah]{color:#123456;}';
319 $this->assertSame($css, $optimiser->process($css));
321 $css = '#some.css[type=blah]{color:#123456;}';
322 $this->assertSame($css, $optimiser->process($css));
324 $css = '#some .css[type=blah]{color:#123456;}';
325 $this->assertSame($css, $optimiser->process($css));
327 $cssin = '.one {color:red;} .two {color:#F00;}';
328 $cssout = ".one, .two{color:#F00;}";
329 $this->assertSame($cssout, $optimiser->process($cssin));
331 $cssin = '.one {color:#123;color:#321;}';
332 $cssout = '.one{color:#321;}';
333 $this->assertSame($cssout, $optimiser->process($cssin));
335 $cssin = '.one {color:#123; color : #321 ;}';
336 $cssout = '.one{color:#321;}';
337 $this->assertSame($cssout, $optimiser->process($cssin));
339 $cssin = '.one {color:#123;} .one {color:#321;}';
340 $cssout = '.one{color:#321;}';
341 $this->assertSame($cssout, $optimiser->process($cssin));
343 $cssin = '.one {color:#123 !important;color:#321;}';
344 $cssout = '.one{color:#123 !important;}';
345 $this->assertSame($cssout, $optimiser->process($cssin));
347 $cssin = '.one {color:#123 !important;} .one {color:#321;}';
348 $cssout = '.one{color:#123 !important;}';
349 $this->assertSame($cssout, $optimiser->process($cssin));
351 $cssin = '.one {color:#123!important;} .one {color:#321;}';
352 $cssout = '.one{color:#123 !important;}';
353 $this->assertSame($cssout, $optimiser->process($cssin));
355 $cssin = '.one {color:rgb(255, 128, 1)}';
356 $cssout = '.one{color:rgb(255, 128, 1);}';
357 $this->assertSame($cssout, $optimiser->process($cssin));
359 $cssin = '.one {color:rgba(255, 128, 1, 0.5)}';
360 $cssout = '.one{color:rgba(255, 128, 1, 0.5);}';
361 $this->assertSame($cssout, $optimiser->process($cssin));
363 $cssin = '.one {color:hsl(120, 65%, 75%)}';
364 $cssout = '.one{color:hsl(120, 65%, 75%);}';
365 $this->assertSame($cssout, $optimiser->process($cssin));
367 $cssin = '.one {color:hsla(120,65%,75%,0.5)}';
368 $cssout = '.one{color:hsla(120,65%,75%,0.5);}';
369 $this->assertSame($cssout, $optimiser->process($cssin));
371 // Try some invalid colours to make sure we don't mangle them.
372 $css = 'div#some{color:#1;}';
373 $this->assertSame($css, $optimiser->process($css));
375 $css = 'div#some{color:#12;}';
376 $this->assertSame($css, $optimiser->process($css));
378 $css = 'div#some{color:#1234;}';
379 $this->assertSame($css, $optimiser->process($css));
381 $css = 'div#some{color:#12345;}';
382 $this->assertSame($css, $optimiser->process($css));
386 * Test widths.
388 public function test_widths() {
389 $optimiser = new css_optimiser();
391 $cssin = '.css {width:0}';
392 $cssout = '.css{width:0;}';
393 $this->assertSame($cssout, $optimiser->process($cssin));
395 $cssin = '.css {width:0px}';
396 $cssout = '.css{width:0;}';
397 $this->assertSame($cssout, $optimiser->process($cssin));
399 $cssin = '.css {width:0em}';
400 $cssout = '.css{width:0;}';
401 $this->assertSame($cssout, $optimiser->process($cssin));
403 $cssin = '.css {width:0pt}';
404 $cssout = '.css{width:0;}';
405 $this->assertSame($cssout, $optimiser->process($cssin));
407 $cssin = '.css {width:0mm}';
408 $cssout = '.css{width:0;}';
409 $this->assertSame($cssout, $optimiser->process($cssin));
411 $cssin = '.css {width:100px}';
412 $cssout = '.css{width:100px;}';
413 $this->assertSame($cssout, $optimiser->process($cssin));
417 * Test margin styles.
419 public function test_margins() {
420 $optimiser = new css_optimiser();
422 $cssin = '.one {margin: 1px 2px 3px 4px}';
423 $cssout = '.one{margin:1px 2px 3px 4px;}';
424 $this->assertSame($cssout, $optimiser->process($cssin));
426 $cssin = '.one {margin-top:1px; margin-left:4px; margin-right:2px; margin-bottom: 3px;}';
427 $cssout = '.one{margin:1px 2px 3px 4px;}';
428 $this->assertSame($cssout, $optimiser->process($cssin));
430 $cssin = '.one {margin-top:1px; margin-left:4px;}';
431 $cssout = '.one{margin-top:1px;margin-left:4px;}';
432 $this->assertSame($cssout, $optimiser->process($cssin));
434 $cssin = '.one {margin:1px; margin-left:4px;}';
435 $cssout = '.one{margin:1px 1px 1px 4px;}';
436 $this->assertSame($cssout, $optimiser->process($cssin));
438 $cssin = '.one {margin:1px; margin-bottom:4px;}';
439 $cssout = '.one{margin:1px 1px 4px;}';
440 $this->assertSame($cssout, $optimiser->process($cssin));
442 $cssin = '.one, .two, .one.two, .one .two {margin:0;} .one.two {margin:0 7px;}';
443 $cssout = '.one, .two{margin:0;} .one.two{margin:0 7px;} .one .two{margin:0;}';
444 $this->assertSame($cssout, $optimiser->process($cssin));
446 $cssin = '.block {margin-top: 0px !important;margin-bottom: 0px !important;}';
447 $cssout = '.block{margin-top:0 !important;margin-bottom:0 !important;}';
448 $this->assertSame($cssout, $optimiser->process($cssin));
450 $cssin = '.block {margin: 0px !important;margin-bottom: 3px;}';
451 $cssout = '.block{margin:0 !important;}';
452 $this->assertSame($cssout, $optimiser->process($cssin));
454 $cssin = '.block {margin: 5px;margin-right: 0 !important;}';
455 $cssout = '.block{margin:5px;margin-right:0 !important;}';
456 $this->assertSame($cssout, $optimiser->process($cssin));
460 * Test padding styles.
462 public function test_padding() {
463 $optimiser = new css_optimiser();
465 $cssin = '.one {padding: 1px 2px 3px 4px}';
466 $cssout = '.one{padding:1px 2px 3px 4px;}';
467 $this->assertSame($cssout, $optimiser->process($cssin));
469 $cssin = '.one {padding-top:1px; padding-left:4px; padding-right:2px; padding-bottom: 3px;}';
470 $cssout = '.one{padding:1px 2px 3px 4px;}';
471 $this->assertSame($cssout, $optimiser->process($cssin));
473 $cssin = '.one {padding-top:1px; padding-left:4px;padding-bottom: 3px;}';
474 $cssout = '.one{padding-top:1px;padding-left:4px;padding-bottom:3px;}';
475 $this->assertSame($cssout, $optimiser->process($cssin));
477 $cssin = '.one {padding-top:1px; padding-left:4px;}';
478 $cssout = '.one{padding-top:1px;padding-left:4px;}';
479 $this->assertSame($cssout, $optimiser->process($cssin));
481 $cssin = '.one {padding:1px; padding-left:4px;}';
482 $cssout = '.one{padding:1px 1px 1px 4px;}';
483 $this->assertSame($cssout, $optimiser->process($cssin));
485 $cssin = '.one {padding:1px; padding-bottom:4px;}';
486 $cssout = '.one{padding:1px 1px 4px;}';
487 $this->assertSame($cssout, $optimiser->process($cssin));
489 $cssin = '.one {padding:0 !important;}';
490 $cssout = '.one{padding:0 !important;}';
491 $this->assertSame($cssout, $optimiser->process($cssin));
493 $cssin = '.one {padding:0 !important;}';
494 $cssout = '.one{padding:0 !important;}';
495 $this->assertSame($cssout, $optimiser->process($cssin));
497 $cssin = '.one, .two, .one.two, .one .two {padding:0;} .one.two {padding:0 7px;}';
498 $cssout = '.one, .two{padding:0;} .one.two{padding:0 7px;} .one .two{padding:0;}';
499 $this->assertSame($cssout, $optimiser->process($cssin));
501 $cssin = '.block {padding-top: 0px !important;padding-bottom: 0px !important;}';
502 $cssout = '.block{padding-top:0 !important;padding-bottom:0 !important;}';
503 $this->assertSame($cssout, $optimiser->process($cssin));
505 $cssin = '.block {padding: 0px !important;padding-bottom: 3px;}';
506 $cssout = '.block{padding:0 !important;}';
507 $this->assertSame($cssout, $optimiser->process($cssin));
509 $cssin = '.block {padding: 5px;padding-right: 0 !important;}';
510 $cssout = '.block{padding:5px;padding-right:0 !important;}';
511 $this->assertSame($cssout, $optimiser->process($cssin));
515 * Test cursor optimisations
517 public function test_cursor() {
518 $optimiser = new css_optimiser();
520 // Valid cursor.
521 $cssin = '.one {cursor: pointer;}';
522 $cssout = '.one{cursor:pointer;}';
523 $this->assertSame($cssout, $optimiser->process($cssin));
525 // Invalid cursor but tolerated.
526 $cssin = '.one {cursor: hand;}';
527 $cssout = '.one{cursor:hand;}';
528 $this->assertSame($cssout, $optimiser->process($cssin));
530 // Valid cursor: url relative.
531 $cssin = '.one {cursor: mycursor.png;}';
532 $cssout = '.one{cursor:mycursor.png;}';
533 $this->assertSame($cssout, $optimiser->process($cssin));
535 // Valid cursor: url absolute.
536 $cssin = '.one {cursor: http://local.host/mycursor.png;}';
537 $cssout = '.one{cursor:http://local.host/mycursor.png;}';
538 $this->assertSame($cssout, $optimiser->process($cssin));
542 * Test vertical align optimisations
544 public function test_vertical_align() {
545 $optimiser = new css_optimiser();
547 // Valid vertical aligns.
548 $cssin = '.one {vertical-align: baseline;}';
549 $cssout = '.one{vertical-align:baseline;}';
550 $this->assertSame($cssout, $optimiser->process($cssin));
551 $cssin = '.one {vertical-align: middle;}';
552 $cssout = '.one{vertical-align:middle;}';
553 $this->assertSame($cssout, $optimiser->process($cssin));
554 $cssin = '.one {vertical-align: 0.75em;}';
555 $cssout = '.one{vertical-align:0.75em;}';
556 $this->assertSame($cssout, $optimiser->process($cssin));
557 $cssin = '.one {vertical-align: 50%;}';
558 $cssout = '.one{vertical-align:50%;}';
559 $this->assertSame($cssout, $optimiser->process($cssin));
561 // Invalid but tolerated.
562 $cssin = '.one {vertical-align: center;}';
563 $cssout = '.one{vertical-align:center;}';
564 $this->assertSame($cssout, $optimiser->process($cssin));
568 * Test float optimisations
570 public function test_float() {
571 $optimiser = new css_optimiser();
573 // Valid vertical aligns.
574 $cssin = '.one {float: inherit;}';
575 $cssout = '.one{float:inherit;}';
576 $this->assertSame($cssout, $optimiser->process($cssin));
577 $cssin = '.one {float: left;}';
578 $cssout = '.one{float:left;}';
579 $this->assertSame($cssout, $optimiser->process($cssin));
580 $cssin = '.one {float: right;}';
581 $cssout = '.one{float:right;}';
582 $this->assertSame($cssout, $optimiser->process($cssin));
583 $cssin = '.one {float: none;}';
584 $cssout = '.one{float:none;}';
585 $this->assertSame($cssout, $optimiser->process($cssin));
587 // Invalid but tolerated.
588 $cssin = '.one {float: center;}';
589 $cssout = '.one{float:center;}';
590 $this->assertSame($cssout, $optimiser->process($cssin));
594 * Test some totally invalid CSS optimisation.
596 public function test_invalid_css_handling() {
597 $optimiser = new css_optimiser();
599 $cssin = array(
600 '.one{}',
601 '.one {:}',
602 '.one {;}',
603 '.one {;;;;;}',
604 '.one {:;}',
605 '.one {:;:;:;:::;;;}',
606 '.one {!important}',
607 '.one {:!important}',
608 '.one {:!important;}',
609 '.one {;!important}'
611 $cssout = '.one{}';
612 foreach ($cssin as $css) {
613 $this->assertSame($cssout, $optimiser->process($css));
616 $cssin = array(
617 '.one{background-color:red;}',
618 '.one {background-color:red;} .one {background-color:}',
619 '.one {background-color:red;} .one {background-color;}',
620 '.one {background-color:red;} .one {background-color}',
621 '.one {background-color:red;} .one {background-color:;}',
622 '.one {background-color:red;} .one {:blue;}',
623 '.one {background-color:red;} .one {:#00F}',
625 $cssout = '.one{background-color:#F00;}';
626 foreach ($cssin as $css) {
627 $this->assertSame($cssout, $optimiser->process($css));
630 $cssin = '..one {background-color:color:red}';
631 $cssout = '..one{background-color:color:red;}';
632 $this->assertSame($cssout, $optimiser->process($cssin));
634 $cssin = '#.one {background-color:color:red}';
635 $cssout = '#.one{background-color:color:red;}';
636 $this->assertSame($cssout, $optimiser->process($cssin));
638 $cssin = '##one {background-color:color:red}';
639 $cssout = '##one{background-color:color:red;}';
640 $this->assertSame($cssout, $optimiser->process($cssin));
642 $cssin = '.one {background-color:color:red}';
643 $cssout = '.one{background-color:color:red;}';
644 $this->assertSame($cssout, $optimiser->process($cssin));
646 $cssin = '.one {background-color:red;color;border-color:blue}';
647 $cssout = '.one{background-color:#F00;border-color:#00F;}';
648 $this->assertSame($cssout, $optimiser->process($cssin));
650 $cssin = '{background-color:#123456;color:red;}{color:green;}';
651 $cssout = "{background-color:#123456;color:#008000;}";
652 $this->assertSame($cssout, $optimiser->process($cssin));
654 $cssin = '.one {color:red;} {color:green;} .one {background-color:blue;}';
655 $cssout = ".one{color:#F00;background-color:#00F;} {color:#008000;}";
656 $this->assertSame($cssout, $optimiser->process($cssin));
660 * Try to break some things.
662 public function test_break_things() {
663 $optimiser = new css_optimiser();
665 // Wildcard test.
666 $cssin = '* {color: black;}';
667 $cssout = '*{color:#000;}';
668 $this->assertSame($cssout, $optimiser->process($cssin));
670 // Wildcard test.
671 $cssin = '.one * {color: black;}';
672 $cssout = '.one *{color:#000;}';
673 $this->assertSame($cssout, $optimiser->process($cssin));
675 // Wildcard test.
676 $cssin = '* .one * {color: black;}';
677 $cssout = '* .one *{color:#000;}';
678 $this->assertSame($cssout, $optimiser->process($cssin));
680 // Wildcard test.
681 $cssin = '*,* {color: black;}';
682 $cssout = '*{color:#000;}';
683 $this->assertSame($cssout, $optimiser->process($cssin));
685 // Wildcard test.
686 $cssin = '*, * .one {color: black;}';
687 $cssout = "*, * .one{color:#000;}";
688 $this->assertSame($cssout, $optimiser->process($cssin));
690 // Wildcard test.
691 $cssin = '*, *.one {color: black;}';
692 $cssout = "*, *.one{color:#000;}";
693 $this->assertSame($cssout, $optimiser->process($cssin));
695 // Psedo test.
696 $cssin = '.one:before {color: black;}';
697 $cssout = '.one:before{color:#000;}';
698 $this->assertSame($cssout, $optimiser->process($cssin));
700 // Psedo test.
701 $cssin = '.one:after {color: black;}';
702 $cssout = '.one:after{color:#000;}';
703 $this->assertSame($cssout, $optimiser->process($cssin));
705 // Psedo test.
706 $cssin = '.one:onclick {color: black;}';
707 $cssout = '.one:onclick{color:#000;}';
708 $this->assertSame($cssout, $optimiser->process($cssin));
710 // Test complex CSS rules that don't really exist but mimic other CSS rules.
711 $cssin = '.one {master-of-destruction: explode(\' \', "What madness");}';
712 $cssout = '.one{master-of-destruction:explode(\' \', "What madness");}';
713 $this->assertSame($cssout, $optimiser->process($cssin));
715 // Test some complex IE css... I couldn't even think of a more complext solution
716 // than the CSS they came up with.
717 $cssin = 'a { opacity: 0.5;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50); }';
718 $cssout = 'a{opacity:0.5;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";filter:alpha(opacity=50);}';
719 $this->assertSame($cssout, $optimiser->process($cssin));
723 * A bulk processing test.
725 public function test_bulk_processing() {
726 global $CFG;
727 $cssin = <<<CSS
728 .test .one {
729 margin:5px;
730 border:0;
732 .test .one {
733 margin: 10px;
734 color: red;
737 .test.one {
738 margin: 15px;
741 #test .one {margin: 20px;}
742 #test #one {margin: 25px;}.test #one {margin: 30px;}
743 .test .one { background-color: #123; }
744 .test.one{border:1px solid blue}.test.one{border-color:green;}
746 @media print {
747 #test .one {margin: 35px;}
750 @media print {
751 #test .one {margin: 40px;color: #123456;}
752 #test #one {margin: 45px;}
755 @media print,screen {
756 #test .one {color: #654321;}
759 #test .one,
760 #new.style {color:#000;}
761 CSS;
763 $cssout = <<<CSS
764 .test .one{margin:10px;border-width:0;color:#F00;background-color:#123;}
765 .test.one{margin:15px;border:1px solid #008000;}
766 #test .one{margin:20px;color:#000;}
767 #test #one{margin:25px;}
768 .test #one{margin:30px;}
769 #new.style{color:#000;}
771 @media print {
772 #test .one{margin:40px;color:#123456;}
773 #test #one{margin:45px;}
775 @media print,screen {
776 #test .one{color:#654321;}
778 CSS;
779 $CFG->cssoptimiserpretty = true;
780 $optimiser = new css_optimiser();
781 $this->assertSame($optimiser->process($cssin), $cssout);
782 unset($CFG->cssoptimiserpretty);
786 * Test CSS colour matching.
788 public function test_css_is_colour() {
789 // First lets test hex colours.
790 $this->assertTrue(css_is_colour('#123456'));
791 $this->assertTrue(css_is_colour('#123'));
792 $this->assertTrue(css_is_colour('#ABCDEF'));
793 $this->assertTrue(css_is_colour('#ABC'));
794 $this->assertTrue(css_is_colour('#abcdef'));
795 $this->assertTrue(css_is_colour('#abc'));
796 $this->assertTrue(css_is_colour('#aBcDeF'));
797 $this->assertTrue(css_is_colour('#aBc'));
798 $this->assertTrue(css_is_colour('#1a2Bc3'));
799 $this->assertTrue(css_is_colour('#1Ac'));
801 // Note the following two colour's aren't really colours but browsers process
802 // them still.
803 $this->assertTrue(css_is_colour('#A'));
804 $this->assertTrue(css_is_colour('#12'));
805 // Having four or five characters however are not valid colours and
806 // browsers don't parse them. They need to fail so that broken CSS
807 // stays broken after optimisation.
808 $this->assertFalse(css_is_colour('#1234'));
809 $this->assertFalse(css_is_colour('#12345'));
811 $this->assertFalse(css_is_colour('#BCDEFG'));
812 $this->assertFalse(css_is_colour('#'));
813 $this->assertFalse(css_is_colour('#0000000'));
814 $this->assertFalse(css_is_colour('#132-245'));
815 $this->assertFalse(css_is_colour('#13 23 43'));
816 $this->assertFalse(css_is_colour('123456'));
818 // Next lets test real browser mapped colours.
819 $this->assertTrue(css_is_colour('black'));
820 $this->assertTrue(css_is_colour('blue'));
821 $this->assertTrue(css_is_colour('BLACK'));
822 $this->assertTrue(css_is_colour('Black'));
823 $this->assertTrue(css_is_colour('bLACK'));
824 $this->assertTrue(css_is_colour('mediumaquamarine'));
825 $this->assertTrue(css_is_colour('mediumAquamarine'));
826 $this->assertFalse(css_is_colour('monkey'));
827 $this->assertFalse(css_is_colour(''));
828 $this->assertFalse(css_is_colour('not a colour'));
830 // Next lets test rgb(a) colours.
831 $this->assertTrue(css_is_colour('rgb(255,255,255)'));
832 $this->assertTrue(css_is_colour('rgb(0, 0, 0)'));
833 $this->assertTrue(css_is_colour('RGB (255, 255 , 255)'));
834 $this->assertTrue(css_is_colour('rgba(0,0,0,0)'));
835 $this->assertTrue(css_is_colour('RGBA(255,255,255,1)'));
836 $this->assertTrue(css_is_colour('rgbA(255,255,255,0.5)'));
837 $this->assertFalse(css_is_colour('rgb(-255,-255,-255)'));
838 $this->assertFalse(css_is_colour('rgb(256,-256,256)'));
840 // Now lets test HSL colours.
841 $this->assertTrue(css_is_colour('hsl(0,0%,100%)'));
842 $this->assertTrue(css_is_colour('hsl(180, 0%, 10%)'));
843 $this->assertTrue(css_is_colour('hsl (360, 100% , 95%)'));
845 // Finally test the special values.
846 $this->assertTrue(css_is_colour('inherit'));
850 * Test the css_is_width function.
852 public function test_css_is_width() {
854 $this->assertTrue(css_is_width('0'));
855 $this->assertTrue(css_is_width('0px'));
856 $this->assertTrue(css_is_width('0em'));
857 $this->assertTrue(css_is_width('199px'));
858 $this->assertTrue(css_is_width('199em'));
859 $this->assertTrue(css_is_width('199%'));
860 $this->assertTrue(css_is_width('-1px'));
861 $this->assertTrue(css_is_width('auto'));
862 $this->assertTrue(css_is_width('inherit'));
864 // Valid widths but missing their unit specifier.
865 $this->assertFalse(css_is_width('0.75'));
866 $this->assertFalse(css_is_width('3'));
867 $this->assertFalse(css_is_width('-1'));
869 // Totally invalid widths.
870 $this->assertFalse(css_is_width('-'));
871 $this->assertFalse(css_is_width('bananas'));
872 $this->assertFalse(css_is_width(''));
873 $this->assertFalse(css_is_width('top'));
877 * This function tests some of the broken crazy CSS we have in Moodle.
878 * For each of these things the value needs to be corrected if we can be 100%
879 * certain what is going wrong, Or it needs to be left as is.
881 public function test_broken_css_found_in_moodle() {
882 $optimiser = new css_optimiser();
884 // Notice how things are out of order here but that they get corrected.
885 $cssin = '.test {background:url([[pix:theme|pageheaderbgred]]) top center no-repeat}';
886 $cssout = '.test{background:url([[pix:theme|pageheaderbgred]]) no-repeat top center;}';
887 $this->assertSame($cssout, $optimiser->process($cssin));
889 // Cursor hand isn't valid.
890 $cssin = '.test {cursor: hand;}';
891 $cssout = '.test{cursor:hand;}';
892 $this->assertSame($cssout, $optimiser->process($cssin));
894 // Zoom property isn't valid.
895 $cssin = '.test {zoom: 1;}';
896 $cssout = '.test{zoom:1;}';
897 $this->assertSame($cssout, $optimiser->process($cssin));
899 // Left isn't a valid position property.
900 $cssin = '.test {position: left;}';
901 $cssout = '.test{position:left;}';
902 $this->assertSame($cssout, $optimiser->process($cssin));
904 // The dark red color isn't a valid HTML color but has a standardised
905 // translation of #8B0000.
906 $cssin = '.test {color: darkred;}';
907 $cssout = '.test{color:#8B0000;}';
908 $this->assertSame($cssout, $optimiser->process($cssin));
910 // You can't use argb colours as border colors.
911 $cssin = '.test {border-bottom: 1px solid rgba(0,0,0,0.25);}';
912 $cssout = '.test{border-bottom:1px solid rgba(0,0,0,0.25);}';
913 $this->assertSame($cssout, $optimiser->process($cssin));
915 // Opacity with annoying IE equivalents....
916 $cssin = '.test {opacity: 0.5; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter: alpha(opacity=50);}';
917 $cssout = '.test{opacity:0.5;-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";filter:alpha(opacity=50);}';
918 $this->assertSame($cssout, $optimiser->process($cssin));
922 * Test keyframe declarations.
924 public function test_keyframe_css_animation() {
925 global $CFG;
926 $optimiser = new css_optimiser();
928 $css = '.dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url(\'[[pix:theme|fp/dnd_arrow]]\') no-repeat center;margin-left:-28px;}';
929 $this->assertSame($css, $optimiser->process($css));
931 $css = '@keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}';
932 $this->assertSame($css, $optimiser->process($css));
934 $css = "@keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}\n";
935 $css .= "@-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}\n";
936 $css .= "@-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}";
937 $this->assertSame($css, $optimiser->process($css));
939 $cssin = <<<CSS
940 .test {color:#FFF;}
941 .testtwo {color:#FFF;}
942 @media print {
943 .test {background-color:#FFF;}
945 .dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;margin-left:-28px;}
946 @media print {
947 .test {background-color:#000;}
949 @keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
950 @-moz-keyframes mymove{0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
951 @-webkit-keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
952 @media print {
953 .test {background-color:#333;}
955 .test {color:#888;}
956 .testtwo {color:#888;}
957 CSS;
959 $cssout = <<<CSS
960 .test,
961 .testtwo{color:#888;}
962 .dndupload-arrow{width:56px;height:47px;position:absolute;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;margin-left:-28px;}
964 @media print {
965 .test{background-color:#333;}
967 @keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
968 @-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
969 @-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
970 CSS;
971 $CFG->cssoptimiserpretty = true;
972 $this->assertSame($cssout, $optimiser->process($cssin));
973 unset($CFG->cssoptimiserpretty);
975 $cssin = <<<CSS
976 .dndupload-target {display:none;}
977 .dndsupported .dndupload-ready .dndupload-target {display:block;}
978 .dndupload-uploadinprogress {display:none;text-align:center;}
979 .dndupload-uploading .dndupload-uploadinprogress {display:block;}
980 .dndupload-arrow {background:url('[[pix:theme|fp/dnd_arrow]]') center no-repeat;width:56px;height:47px;position:absolute;margin-left: -28px;/*right:46%;left:46%;*/animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;}
981 @keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}@-moz-keyframes mymove{0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}@-webkit-keyframes mymove {0%{top:10px;} 12%{top:40px;} 30%{top:20px} 65%{top:35px;} 100%{top:9px;}}
984 * Select Dialogue (File Manager only)
986 .filemanager.fp-select .fp-select-loading {display:none;}
987 .filemanager.fp-select.loading .fp-select-loading {display:block;}
988 .filemanager.fp-select.loading form {display:none;}
989 CSS;
991 $cssout = <<<CSS
992 .dndupload-target{display:none;}
993 .dndsupported .dndupload-ready .dndupload-target{display:block;}
994 .dndupload-uploadinprogress{display:none;text-align:center;}
995 .dndupload-uploading .dndupload-uploadinprogress{display:block;}
996 .dndupload-arrow{background:url('[[pix:theme|fp/dnd_arrow]]') no-repeat center;width:56px;height:47px;position:absolute;margin-left:-28px;animation:mymove 5s infinite;-moz-animation:mymove 5s infinite;-webkit-animation:mymove 5s infinite;}
997 .filemanager.fp-select .fp-select-loading{display:none;}
998 .filemanager.fp-select.loading .fp-select-loading{display:block;}
999 .filemanager.fp-select.loading form{display:none;}
1001 @keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
1002 @-moz-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
1003 @-webkit-keyframes mymove {0%{top:10px;}12%{top:40px;}30%{top:20px;}65%{top:35px;}100%{top:9px;}}
1004 CSS;
1005 $CFG->cssoptimiserpretty = true;
1006 $this->assertSame($cssout, $optimiser->process($cssin));
1007 unset($CFG->cssoptimiserpretty);
1011 * Test media declarations.
1013 public function test_media_rules() {
1014 $optimiser = new css_optimiser();
1016 $cssin = "@media print {\n .test{background-color:#333;}\n}";
1017 $cssout = "@media print { .test{background-color:#333;} }";
1018 $this->assertSame($cssout, $optimiser->process($cssin));
1020 $cssin = "@media screen and (min-width:30px) {\n #region-main-box{left: 30px;float: left;}\n}";
1021 $cssout = "@media screen and (min-width:30px) { #region-main-box{left:30px;float:left;} }";
1022 $this->assertSame($cssout, $optimiser->process($cssin));
1024 $cssin = "@media all and (min-width:500px) {\n #region-main-box{left:30px;float:left;}\n}";
1025 $cssout = "@media all and (min-width:500px) { #region-main-box{left:30px;float:left;} }";
1026 $this->assertSame($cssout, $optimiser->process($cssin));
1028 $cssin = "@media (min-width:500px) {\n #region-main-box{left:30px;float:left;}\n}";
1029 $cssout = "@media (min-width:500px) { #region-main-box{left:30px;float:left;} }";
1030 $this->assertSame($cssout, $optimiser->process($cssin));
1032 $cssin = "@media screen and (color), projection and (color) {\n #region-main-box{left:30px;float:left;}\n}";
1033 $cssout = "@media screen and (color),projection and (color) { #region-main-box{left:30px;float:left;} }";
1034 $this->assertSame($cssout, $optimiser->process($cssin));
1036 $cssin = "@media print {\n .test{background-color:#000;}\n}@media print {\n .test{background-color:#FFF;}\n}";
1037 $cssout = "@media print { .test{background-color:#FFF;} }";
1038 $this->assertSame($cssout, $optimiser->process($cssin));
1040 $cssin = "@media screen and (min-width:30px) {\n #region-main-box{background-color:#000;}\n}\n@media screen and (min-width:30px) {\n #region-main-box{background-color:#FFF;}\n}";
1041 $cssout = "@media screen and (min-width:30px) { #region-main-box{background-color:#FFF;} }";
1042 $this->assertSame($cssout, $optimiser->process($cssin));
1044 $cssin = "@media screen and (min-width:30px) {\n #region-main-box{background-color:#000;}\n}\n@media screen and (min-width:31px) {\n #region-main-box{background-color:#FFF;}\n}";
1045 $cssout = "@media screen and (min-width:30px) { #region-main-box{background-color:#000;} }\n@media screen and (min-width:31px) { #region-main-box{background-color:#FFF;} }";
1046 $this->assertSame($cssout, $optimiser->process($cssin));
1048 $cssin = "@media (min-width: 768px) and (max-width: 979px) {\n*{*zoom:1;}}";
1049 $cssout = "@media (min-width: 768px) and (max-width: 979px) { *{*zoom:1;} }";
1050 $this->assertSame($cssout, $optimiser->process($cssin));
1052 $cssin = "#test {min-width:1200px;}@media (min-width: 768px) {#test {min-width: 1024px;}}";
1053 $cssout = "#test{min-width:1200px;} \n@media (min-width: 768px) { #test{min-width:1024px;} }";
1054 $this->assertSame($cssout, $optimiser->process($cssin));
1056 $cssin = "@media(min-width:768px){#page-calender-view .container fluid{min-width:1024px}}.section_add_menus{text-align:right}";
1057 $cssout = ".section_add_menus{text-align:right;} \n@media (min-width:768px) { #page-calender-view .container fluid{min-width:1024px;} }";
1058 $this->assertSame($cssout, $optimiser->process($cssin));
1060 $cssin = "@-ms-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}";
1061 $cssout = "@-ms-keyframes progress-bar-stripes {from{background-position:40px 0;}to{background-position:0 0;}}";
1062 $this->assertSame($cssout, $optimiser->process($cssin));
1066 * Test the ordering of CSS optimisationss
1068 public function test_css_optimisation_ordering() {
1069 $optimiser = $this->get_optimiser();
1071 $css = '.test{display:none;} .dialogue{display:block;} .dialogue-hidden{display:none;}';
1072 $this->assertSame($css, $optimiser->process($css));
1074 $cssin = '.test{display:none;} .dialogue-hidden{display:none;} .dialogue{display:block;}';
1075 $cssout = '.test, .dialogue-hidden{display:none;} .dialogue{display:block;}';
1076 $this->assertSame($cssout, $optimiser->process($cssin));
1080 * Test CSS chunking
1082 public function test_css_chunking() {
1083 // Test with an even number of styles.
1084 $css = 'a{}b{}c{}d{}e{}f{}';
1085 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1086 $this->assertInternalType('array', $chunks);
1087 $this->assertCount(3, $chunks);
1088 $this->assertArrayHasKey(0, $chunks);
1089 $this->assertArrayHasKey(1, $chunks);
1090 $this->assertArrayHasKey(2, $chunks);
1091 $this->assertSame('a{}b{}', $chunks[0]);
1092 $this->assertSame('c{}d{}', $chunks[1]);
1093 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\ne{}f{}", $chunks[2]);
1095 // Test with an odd number of styles.
1096 $css = 'a{}b{}c{}d{}e{}';
1097 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1098 $this->assertInternalType('array', $chunks);
1099 $this->assertCount(3, $chunks);
1100 $this->assertArrayHasKey(0, $chunks);
1101 $this->assertArrayHasKey(1, $chunks);
1102 $this->assertArrayHasKey(2, $chunks);
1103 $this->assertSame('a{}b{}', $chunks[0]);
1104 $this->assertSame('c{}d{}', $chunks[1]);
1105 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\ne{}", $chunks[2]);
1107 // Test well placed commas.
1108 $css = 'a,b{}c,d{}e,f{}';
1109 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1110 $this->assertInternalType('array', $chunks);
1111 $this->assertCount(3, $chunks);
1112 $this->assertArrayHasKey(0, $chunks);
1113 $this->assertArrayHasKey(1, $chunks);
1114 $this->assertArrayHasKey(2, $chunks);
1115 $this->assertSame('a,b{}', $chunks[0]);
1116 $this->assertSame('c,d{}', $chunks[1]);
1117 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\ne,f{}", $chunks[2]);
1119 // Test unfortunately placed commas.
1120 $css = 'a{}b,c{color:red;}d{}e{}f{}';
1121 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1122 $this->assertInternalType('array', $chunks);
1123 $this->assertCount(4, $chunks);
1124 $this->assertArrayHasKey(0, $chunks);
1125 $this->assertArrayHasKey(1, $chunks);
1126 $this->assertArrayHasKey(2, $chunks);
1127 $this->assertArrayHasKey(3, $chunks);
1128 $this->assertSame('a{}', $chunks[0]);
1129 $this->assertSame('b,c{color:red;}', $chunks[1]);
1130 $this->assertSame('d{}e{}', $chunks[2]);
1131 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\n@import url(styles.php?type=test&chunk=3);\nf{}", $chunks[3]);
1133 // Test unfortunate CSS.
1134 $css = 'a,b,c,d,e,f{color:red;}';
1135 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2, 0);
1136 $this->assertInternalType('array', $chunks);
1137 $this->assertCount(1, $chunks);
1138 $this->assertArrayHasKey(0, $chunks);
1139 $this->assertSame('a,b,c,d,e,f{color:red;}', $chunks[0]);
1140 $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 6. Those were ignored.');
1142 // Test to make sure invalid CSS isn't totally ruined.
1143 $css = 'a{},,,e{},';
1144 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1145 // Believe it or not we want to care what comes out here as this will be parsed correctly
1146 // by a browser.
1147 $this->assertInternalType('array', $chunks);
1148 $this->assertCount(3, $chunks);
1149 $this->assertArrayHasKey(0, $chunks);
1150 $this->assertArrayHasKey(1, $chunks);
1151 $this->assertArrayHasKey(2, $chunks);
1152 $this->assertSame('a{}', $chunks[0]);
1153 $this->assertSame(',,,e{}', $chunks[1]);
1154 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\n,", $chunks[2]);
1155 $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 6. Those were ignored.');
1157 // Test utter crap CSS to make sure we don't loop to our deaths.
1158 $css = 'a,b,c,d,e,f';
1159 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1160 $this->assertInternalType('array', $chunks);
1161 $this->assertCount(1, $chunks);
1162 $this->assertArrayHasKey(0, $chunks);
1163 $this->assertSame($css, $chunks[0]);
1164 $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 6. Those were ignored.');
1166 // Test another death situation to make sure we're invincible.
1167 $css = 'a,,,,,e';
1168 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1169 $this->assertInternalType('array', $chunks);
1170 $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 4. Those were ignored.');
1171 // I don't care what the outcome is, I just want to make sure it doesn't die.
1173 // Test media queries.
1174 $css = '@media (min-width: 980px) { .a,.b{} }';
1175 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1176 $this->assertCount(1, $chunks);
1177 $this->assertSame('@media (min-width: 980px) { .a,.b{} }', $chunks[0]);
1179 // Test media queries, with commas.
1180 $css = '.a{} @media (min-width: 700px), handheld and (orientation: landscape) { .b{} }';
1181 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1182 $this->assertCount(1, $chunks);
1183 $this->assertSame($css, $chunks[0]);
1185 // Test special rules.
1186 $css = 'a,b{ background-image: linear-gradient(to bottom, #ffffff, #cccccc);}d,e{}';
1187 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1188 $this->assertCount(2, $chunks);
1189 $this->assertSame('a,b{ background-image: linear-gradient(to bottom, #ffffff, #cccccc);}', $chunks[0]);
1190 $this->assertSame("@import url(styles.php?type=test&chunk=1);\nd,e{}", $chunks[1]);
1192 // Test media queries with too many selectors.
1193 $css = '@media (min-width: 980px) { a,b,c,d{} }';
1194 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1195 $this->assertCount(1, $chunks);
1196 $this->assertSame('@media (min-width: 980px) { a,b,c,d{} }', $chunks[0]);
1197 $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 34. Those were ignored.');
1199 // Complex test.
1200 $css = '@media (a) {b{}} c{} d,e{} f,g,h{} i,j{x:a,b,c} k,l{} @media(x){l,m{ y: a,b,c}} n{}';
1201 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 3);
1202 $this->assertCount(6, $chunks);
1203 $this->assertSame('@media (a) {b{}} c{}', $chunks[0]);
1204 $this->assertSame(' d,e{}', $chunks[1]);
1205 $this->assertSame(' f,g,h{}', $chunks[2]);
1206 $this->assertSame(' i,j{x:a,b,c}', $chunks[3]);
1207 $this->assertSame(' k,l{}', $chunks[4]);
1208 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n@import url(styles.php?type=test&chunk=2);\n@import url(styles.php?type=test&chunk=3);\n@import url(styles.php?type=test&chunk=4);\n@import url(styles.php?type=test&chunk=5);\n @media(x){l,m{ y: a,b,c}} n{}", $chunks[5]);
1210 // Multiple offset errors.
1211 $css = 'a,b,c{} d,e,f{}';
1212 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1213 $this->assertCount(2, $chunks);
1214 $this->assertSame('a,b,c{}', $chunks[0]);
1215 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n d,e,f{}", $chunks[1]);
1216 $this->assertDebuggingCalled('Could not find a safe place to split at offset(s): 6, 14. Those were ignored.');
1218 // Test the split according to IE.
1219 $css = str_repeat('a{}', 4100);
1220 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test');
1221 $this->assertCount(2, $chunks);
1222 $this->assertSame(str_repeat('a{}', 4095), $chunks[0]);
1223 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n" . str_repeat('a{}', 5), $chunks[1]);
1225 // Test strip out comments.
1226 $css = ".a {/** a\nb\nc */} /** a\nb\nc */ .b{} /** .c,.d{} */ e{}";
1227 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1228 $this->assertCount(2, $chunks);
1229 $this->assertSame('.a {} .b{}', $chunks[0]);
1230 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n e{}", $chunks[1]);
1232 // Test something with unicode characters.
1233 $css = 'a,b{} nav a:hover:after { content: "↓"; } b{ color:test;}';
1234 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1235 $this->assertCount(2, $chunks);
1236 $this->assertSame('a,b{}', $chunks[0]);
1237 $this->assertSame("@import url(styles.php?type=test&chunk=1);\n nav a:hover:after { content: \"\"; } b{ color:test;}", $chunks[1]);
1239 // Test that if there is broken CSS with too many close brace symbols,
1240 // media rules after that point are still kept together.
1241 $mediarule = '@media (width=480) {a{}b{}}';
1242 $css = 'c{}}' . $mediarule . 'd{}';
1243 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1244 $this->assertCount(3, $chunks);
1245 $this->assertEquals($mediarule, $chunks[1]);
1247 // Test that this still works even with too many close brace symbols
1248 // inside a media query (note: that broken media query may be split
1249 // after the break, but any following ones should not be).
1250 $brokenmediarule = '@media (width=480) {c{}}d{}}';
1251 $css = $brokenmediarule . 'e{}' . $mediarule . 'f{}';
1252 $chunks = css_chunk_by_selector_count($css, 'styles.php?type=test', 2);
1253 $this->assertCount(4, $chunks);
1254 $this->assertEquals($mediarule, $chunks[2]);
1258 * Test CSS3.
1260 public function test_css3() {
1261 $optimiser = $this->get_optimiser();
1263 $css = '.test > .test{display:inline-block;}';
1264 $this->assertSame($css, $optimiser->process($css));
1266 $css = '*{display:inline-block;}';
1267 $this->assertSame($css, $optimiser->process($css));
1269 $css = 'div > *{display:inline-block;}';
1270 $this->assertSame($css, $optimiser->process($css));
1272 $css = 'div:nth-child(3){display:inline-block;}';
1273 $this->assertSame($css, $optimiser->process($css));
1275 $css = '.test:nth-child(3){display:inline-block;}';
1276 $this->assertSame($css, $optimiser->process($css));
1278 $css = '*:nth-child(3){display:inline-block;}';
1279 $this->assertSame($css, $optimiser->process($css));
1281 $css = '*[id]{display:inline-block;}';
1282 $this->assertSame($css, $optimiser->process($css));
1284 $css = '*[id=blah]{display:inline-block;}';
1285 $this->assertSame($css, $optimiser->process($css));
1287 $css = '*[*id=blah]{display:inline-block;}';
1288 $this->assertSame($css, $optimiser->process($css));
1290 $css = '*[*id=blah_]{display:inline-block;}';
1291 $this->assertSame($css, $optimiser->process($css));
1293 $css = '*[id^=blah*d]{display:inline-block;}';
1294 $this->assertSame($css, $optimiser->process($css));
1296 $css = '.test{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;}';
1297 $this->assertSame($css, $optimiser->process($css));
1299 $css = '#test{box-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.2), 0 1px 2px rgba(0, 0, 0, 0.05);}';
1300 $this->assertSame($css, $optimiser->process($css));
1304 * Test browser hacks here.
1306 public function test_browser_hacks() {
1307 $optimiser = $this->get_optimiser();
1309 $css = '#test{*zoom:1;}';
1310 $this->assertSame($css, $optimiser->process($css));
1312 $css = '.test{width:75%;*width:76%;}';
1313 $this->assertSame($css, $optimiser->process($css));
1315 $css = '#test{*zoom:1;*display:inline;}';
1316 $this->assertSame($css, $optimiser->process($css));
1318 $css = '.test{width:75%;*width:76%;width:76%}';
1319 $this->assertSame('.test{width:76%;*width:76%;}', $optimiser->process($css));
1321 $css = '.test{width:75%;*width:76%;*width:75%}';
1322 $this->assertSame('.test{width:75%;*width:75%;}', $optimiser->process($css));