MDL-79059 core: Use full name as alt text for user picture links
[moodle.git] / lib / tests / rtlcss_test.php
blobbc6d327755f30e7bb794ba9b5823cce62a7b0f6f
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 * Tests for the core_rtlcss class.
20 * The core_rtlcss class extends \MoodleHQ\RTLCSS\RTLCSS library which depends on sabberworm/php-css-parser library.
21 * This test verifies that css parsing works as expected should any of the above change.
23 * @package core
24 * @category phpunit
25 * @copyright 2019 Jake Dallimore <jrhdallimore@gmail.com>
26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
28 defined('MOODLE_INTERNAL') || die();
30 use Sabberworm\CSS\Parser;
31 use Sabberworm\CSS\OutputFormat;
33 /**
34 * Class rtlcss_test.
36 class rtlcss_test extends basic_testcase {
37 /**
38 * Data provider.
39 * @return array
41 public function background_image_provider() {
42 return [
43 /* Not supported by MoodleHQ/RTLCSS yet.
45 'should' => 'Should process string map in url (processUrls:true)',
46 'expected' => 'div { background-image: url(images/rtl.png), url(images/right.png);}',
47 'input' => 'div { background-image: url(images/ltr.png), url(images/left.png);}',
48 'reversable' => true,
49 'options' => [ 'processUrls' => true ],
50 'skip' => true
51 ]],
53 'should' => 'Should not negate color value for linear gradient',
54 'expected' => 'div { background-image: linear-gradient(rgba(255, 255, 255, 0.3) 0%, #ff8 100%);}',
55 'input' => 'div { background-image: linear-gradient(rgba(255, 255, 255, 0.3) 0%, #ff8 100%);}',
56 'reversable' => true,
57 'skip' => true
58 ]],
60 'should' => 'Should not negate color value for linear gradient with calc',
61 'expected' => 'div { background-image: linear-gradient(rgba(255, 255, calc((125 * 2) + 5), 0.3) 0%, #ff8 100%);}',
62 'input' => 'div { background-image: linear-gradient(rgba(255, 255, calc((125 * 2) + 5), 0.3) 0%, #ff8 100%);}',
63 'reversable' => true,
64 'skip' => true
65 ]],
67 'should' => 'Should negate angle value for linear gradient',
68 'expected' => 'div { background-image: linear-gradient(13.25deg, rgba(255, 255, 255, .15) 25%, transparent 25%);}',
69 'input' => 'div { background-image: linear-gradient(-13.25deg, rgba(255, 255, 255, .15) 25%, transparent 25%);}',
70 'reversable' => true,
71 'skip' => true
77 /**
78 * Data provider.
79 * @return array
81 public function background_position_provider() {
82 return [
84 'should' => 'Should complement percentage horizontal position ',
85 'expected' => 'div {background-position:100% 75%;}',
86 'input' => 'div {background-position:0 75%;}',
87 'reversable' => false
88 ]],
89 /* Not supported by MoodleHQ/RTLCSS yet.
91 'should' => 'Should complement percentage horizontal position with calc',
92 'expected' => 'div {background-position:calc(100% - (30% + 50px)) 75%;}',
93 'input' => 'div {background-position:calc(30% + 50px) 75%;}',
94 'reversable' => false,
95 'skip' => true
96 ]],
98 'should' => 'Should complement percentage horizontal position ',
99 'expected' => 'div {background-position:81.25% 75%, 10.75% top;}',
100 'input' => 'div {background-position:18.75% 75%, 89.25% top;}',
101 'reversable' => true,
102 'skip' => true
105 'should' => 'Should complement percentage horizontal position with calc',
106 'expected' => 'div {background-position:calc(100% - (30% + 50px)) calc(30% + 50px), 10.75% top;}',
107 'input' => 'div {background-position:calc(30% + 50px) calc(30% + 50px), 89.25% top;}',
108 'reversable' => false,
109 'skip' => true
112 'should' => 'Should swap left with right',
113 'expected' => 'div {background-position:right 75%, left top;}',
114 'input' => 'div {background-position:left 75%, right top;}',
115 'reversable' => true,
116 'skip' => true
119 'should' => 'Should swap left with right wit calc',
120 'expected' => 'div {background-position:right -ms-calc(30% + 50px), left top;}',
121 'input' => 'div {background-position:left -ms-calc(30% + 50px), right top;}',
122 'reversable' => true,
123 'skip' => true
127 'should' => 'Should complement percentage: position-x (treat 0 as 0%)',
128 'expected' => 'div {background-position-x:100%, 0%;}',
129 'input' => 'div {background-position-x:0, 100%;}',
130 'reversable' => false
133 'should' => 'Should complement percentage: position-x',
134 'expected' => 'div {background-position-x:81.75%, 11%;}',
135 'input' => 'div {background-position-x:18.25%, 89%;}',
136 'reversable' => true
138 /* Not supported by MoodleHQ/RTLCSS yet.
140 'should' => 'Should complement percentage with calc: position-x',
141 'expected' => 'div {background-position-x:calc(100% - (30% + 50px)), -webkit-calc(100% - (30% + 50px));}',
142 'input' => 'div {background-position-x:calc(30% + 50px), -webkit-calc(30% + 50px);}',
143 'reversable' => false,
144 'skip' => true
148 'should' => 'Should swap left with right: position-x',
149 'expected' => 'div {background-position-x:right, left;}',
150 'input' => 'div {background-position-x:left, right;}',
151 'reversable' => true
154 'should' => 'Should keep as is: position-x',
155 'expected' => 'div {background-position-x:100px, 0px;}',
156 'input' => 'div {background-position-x:100px, 0px;}',
157 'reversable' => true
161 'should' => 'Should flip when using 3 positions',
162 'expected' => 'div {background-position:center right 1px;}',
163 'input' => 'div {background-position:center left 1px;}',
164 'reversable' => true
167 'should' => 'Should flip when using 4 positions',
168 'expected' => 'div {background-position:center 2px right 1px;}',
169 'input' => 'div {background-position:center 2px left 1px;}',
170 'reversable' => true
173 'should' => 'Should flip when using 4 positions mixed',
174 'expected' => 'div {background-position:right 2px bottom 1px;}',
175 'input' => 'div {background-position:left 2px bottom 1px;}',
176 'reversable' => true
182 * Data provider.
183 * @return array
185 public function background_provider() {
186 return [
188 'should' => 'Should treat 0 as 0%',
189 'expected' => '.banner { background: 100% top url("topbanner.png") #00d repeat-y fixed; }',
190 'input' => '.banner { background: 0 top url("topbanner.png") #00d repeat-y fixed; }',
191 'reversable' => false
194 'should' => 'Should complement percentage horizontal position',
195 'expected' => '.banner { background: 81% top url("topbanner.png") #00d repeat-y fixed; }',
196 'input' => '.banner { background: 19% top url("topbanner.png") #00d repeat-y fixed; }',
197 'reversable' => true
199 /* Not supported by MoodleHQ/RTLCSS yet.
201 'should' => 'Should complement calc horizontal position',
202 'expected' => '.banner { background: calc(100% - (19% + 2px)) top url(topbanner.png) #00d repeat-y fixed; }',
203 'input' => '.banner { background: calc(19% + 2px) top url(topbanner.png) #00d repeat-y fixed; }',
204 'reversable' => false,
205 'skip' => true
209 'should' => 'Should mirror keyword horizontal position',
210 'expected' => '.banner { background: right top url("topbanner.png") #00d repeat-y fixed; }',
211 'input' => '.banner { background: left top url("topbanner.png") #00d repeat-y fixed; }',
212 'reversable' => true
215 'should' => 'Should not process string map in url (default)',
216 'expected' => '.banner { background: 10px top url("ltr-top-right-banner.png") #00d repeat-y fixed; }',
217 'input' => '.banner { background: 10px top url("ltr-top-right-banner.png") #00d repeat-y fixed; }',
218 'reversable' => true
220 /* Not supported by MoodleHQ/RTLCSS yet.
222 'should' => 'Should process string map in url (processUrls:true)',
223 'expected' => '.banner { background: 10px top url(rtl-top-left-banner.png) #00d repeat-y fixed; }',
224 'input' => '.banner { background: 10px top url(ltr-top-right-banner.png) #00d repeat-y fixed; }',
225 'reversable' => true,
226 'options' => [ 'processUrls' => true ],
227 'skip' => true
230 'should' => 'Should process string map in url (processUrls:{decl:true})',
231 'expected' => '.banner { background: 10px top url(rtl-top-left-banner.png) #00d repeat-y fixed; }',
232 'input' => '.banner { background: 10px top url(ltr-top-right-banner.png) #00d repeat-y fixed; }',
233 'reversable' => true,
234 'options' => [ 'processUrls' => [ 'decl' => true ] ],
235 'skip' => true
239 'should' => 'Should not process string map in url (processUrls:{atrule:true})',
240 'expected' => '.banner { background: 10px top url("ltr-top-right-banner.png") #00d repeat-y fixed; }',
241 'input' => '.banner { background: 10px top url("ltr-top-right-banner.png") #00d repeat-y fixed; }',
242 'reversable' => true,
243 'options' => [ 'processUrls' => [ 'atrule' => true ] ]
246 'should' => 'Should not swap bright:bleft, ultra:urtla',
247 'expected' => '.banner { background: 10px top url("ultra/bright.png") #00d repeat-y fixed; }',
248 'input' => '.banner { background: 10px top url("ultra/bright.png") #00d repeat-y fixed; }',
249 'reversable' => true
251 /* Not supported by MoodleHQ/RTLCSS yet.
253 'should' => 'Should swap bright:bleft, ultra:urtla (processUrls: true, greedy)',
254 'expected' => '.banner { background: 10px top url("urtla/bleft.png") #00d repeat-y fixed; }',
255 'input' => '.banner { background: 10px top url("ultra/bright.png") #00d repeat-y fixed; }',
256 'reversable' => true,
257 'options' => [ 'processUrls' => true, 'greedy' => true ],
258 'skip' => true
262 'should' => 'Should not flip hex colors ',
263 'expected' => '.banner { background: #ff0; }',
264 'input' => '.banner { background: #ff0; }',
265 'reversable' => true
271 * Data provider.
272 * @return array
274 public function directives_provider() {
275 return [
277 'should' => 'Should ignore flipping - rule level (default)',
278 'expected' => 'div {left:10px;text-align:right;}',
279 'input' => '/*rtl:ignore*/div { left:10px; text-align:right;}',
280 'reversable' => false
283 'should' => 'Should ignore flipping - rule level (!important comment)',
284 'expected' => 'div {left:10px;text-align:right;}',
285 'input' => '/*!rtl:ignore*/div { left:10px; text-align:right;}',
286 'reversable' => false,
288 // Not supported by MoodleHQ/RTLCSS yet.
289 //[[
290 // 'should' => 'Should ignore flipping - decl. level (default)',
291 // 'expected' => 'div {left:10px;text-align:left;}',
292 // 'input' => 'div {left:10px/*rtl:ignore*/;text-align:right;}',
293 // 'reversable' => false,
294 // 'skip' => true
295 //]],
297 'should' => 'Should add raw css rules',
298 'expected' => 'div {left:10px;text-align:right;} a {display:block;}',
299 'input' => '/*rtl:raw: div { left:10px;text-align:right;}*/ a {display:block;}',
300 'reversable' => false
303 'should' => 'Should add raw css declarations',
304 'expected' => 'div {font-family:"Droid Kufi Arabic";right:10px;text-align:left;}',
305 'input' => 'div { /*rtl:raw: font-family: "Droid Kufi Arabic";*/ left:10px;text-align:right;}',
306 'reversable' => false
309 'should' => 'Should support block-style',
310 'expected' => 'div {left:10px;text-align:right;}',
311 'input' => ' div {/*rtl:begin:ignore*/left:10px;/*rtl:end:ignore*/ text-align:left;}',
312 'reversable' => false
315 'should' => 'Should support none block-style',
316 'expected' => 'div {left:10px;text-align:left;}',
317 'input' => ' /*rtl:ignore*/div {left:10px; text-align:left;}',
318 'reversable' => false
321 'should' => 'Should remove rules (block-style)',
322 'expected' => 'b {float:right;}',
323 'input' => ' /*rtl:begin:remove*/div {left:10px; text-align:left;} a { display:block;} /*rtl:end:remove*/ b{float:left;}',
324 'reversable' => false
327 'should' => 'Should remove rules',
328 'expected' => 'a {display:block;} b {float:right;}',
329 'input' => ' /*rtl:remove*/div {left:10px; text-align:left;} a { display:block;} b{float:left;}',
330 'reversable' => false
333 'should' => 'Should remove declarations',
334 'expected' => 'div {text-align:right;}',
335 'input' => 'div {/*rtl:remove*/left:10px; text-align:left;}',
336 'reversable' => false
339 'should' => 'Should remove declarations (block-style)',
340 'expected' => 'div {display:inline;}',
341 'input' => 'div {/*rtl:begin:remove*/left:10px; text-align:left;/*rtl:end:remove*/ display:inline;}',
342 'reversable' => false
344 // Not supported by MoodleHQ/RTLCSS yet.
345 //[[
346 // 'should' => 'Final/trailing comment ignored bug (block style): note a tag rules are NOT flipped as they should be',
347 // 'expected' => 'div {left:10px;text-align:left;} a {right:10px;}',
348 // 'input' => 'div {/*rtl:begin:ignore*/left:10px; text-align:left;/*rtl:end:ignore*/} a {left:10px;}',
349 // 'reversable' => false,
350 // 'skip' => true
351 //]]
356 * Data provider.
357 * @return array
359 public function properties_provider() {
360 return [
362 'should' => 'Should mirror property name: border-top-right-radius',
363 'expected' => 'div { border-top-left-radius:15px; }',
364 'input' => 'div { border-top-right-radius:15px; }',
365 'reversable' => true
368 'should' => 'Should mirror property name: border-bottom-right-radius',
369 'expected' => 'div { border-bottom-left-radius:15px; }',
370 'input' => 'div { border-bottom-right-radius:15px; }',
371 'reversable' => true
374 'should' => 'Should mirror property name: border-left',
375 'expected' => 'div { border-right:1px solid black; }',
376 'input' => 'div { border-left:1px solid black; }',
377 'reversable' => true
380 'should' => 'Should mirror property name: border-left-color',
381 'expected' => 'div { border-right-color:black; }',
382 'input' => 'div { border-left-color:black; }',
383 'reversable' => true
386 'should' => 'Should mirror property name: border-left-style',
387 'expected' => 'div { border-right-style:solid; }',
388 'input' => 'div { border-left-style:solid; }',
389 'reversable' => true
392 'should' => 'Should mirror property name: border-left-width',
393 'expected' => 'div { border-right-width:1em; }',
394 'input' => 'div { border-left-width:1em; }',
395 'reversable' => true
398 'should' => 'Should mirror property name: left',
399 'expected' => 'div { right:auto; }',
400 'input' => 'div { left:auto; }',
401 'reversable' => true
404 'should' => 'Should mirror property name: margin-left',
405 'expected' => 'div { margin-right:2em; }',
406 'input' => 'div { margin-left:2em; }',
407 'reversable' => true
410 'should' => 'Should mirror property name: padding-left',
411 'expected' => 'div { padding-right:2em; }',
412 'input' => 'div { padding-left:2em; }',
413 'reversable' => true
419 * Data provider.
420 * @return array
422 public function special_provider() {
423 return [
424 /* Not supported by MoodleHQ/RTLCSS yet.
426 'should' => 'Should not negate tokens',
427 'expected' => 'div { box-shadow: rgba(0, 128, 128, .98) inset -5em 1em 0;}',
428 'input' => 'div { box-shadow: rgba(0, 128, 128, .98) inset 5em 1em 0;}',
429 'reversable' => true,
430 'skip' => true,
437 * Data provider.
438 * @return array
440 public function transform_origin_provider() {
441 return [
443 'should' => 'Should mirror (x-offset: 0 means 0%)',
444 'expected' => 'div { transform-origin:100%; }',
445 'input' => 'div { transform-origin:0; }',
446 'reversable' => false
449 'should' => 'Should mirror (x-offset)',
450 'expected' => 'div { transform-origin:90.25%; }',
451 'input' => 'div { transform-origin:9.75%; }',
452 'reversable' => true
454 /* Not supported by MoodleHQ/RTLCSS yet.
456 'should' => 'Should mirror calc (x-offset)',
457 'expected' => 'div { transform-origin: -moz-calc(100% - (((25%/2) * 10px))); }',
458 'input' => 'div { transform-origin: -moz-calc(((25%/2) * 10px)); }',
459 'reversable' => false,
460 'skip' => true
464 'should' => 'Should not mirror (x-offset: not percent, not calc)',
465 'expected' => 'div { transform-origin:10.75px; }',
466 'input' => 'div { transform-origin:10.75px; }',
467 'reversable' => false
470 'should' => 'Should mirror (offset-keyword)',
471 'expected' => 'div { transform-origin:right; }',
472 'input' => 'div { transform-origin:left; }',
473 'reversable' => true
476 'should' => 'Should mirror (x-offset y-offset: 0 means 0%)',
477 'expected' => 'div { transform-origin:100% 0; }',
478 'input' => 'div { transform-origin:0 0; }',
479 'reversable' => false
481 /* Not supported by MoodleHQ/RTLCSS yet.
483 'should' => 'Should mirror with y being calc (x-offset y-offset: 0 means 0%)',
484 'expected' => 'div { transform-origin:100% -webkit-calc(15% * (3/2)); }',
485 'input' => 'div { transform-origin:0 -webkit-calc(15% * (3/2)); }',
486 'reversable' => false,
487 'skip' => true
491 'should' => 'Should mirror percent (x-offset y-offset)',
492 'expected' => 'div { transform-origin:30.25% 10%; }',
493 'input' => 'div { transform-origin:69.75% 10%; }',
494 'reversable' => true
496 /* Not supported by MoodleHQ/RTLCSS yet.
498 'should' => 'Should mirror with x being calc (x-offset y-offset)',
499 'expected' => 'div { transform-origin: -webkit-calc(100% - (15% * (3/2))) 30.25%; }',
500 'input' => 'div { transform-origin: -webkit-calc(15% * (3/2)) 30.25%; }',
501 'reversable' => false,
502 'skip' => true
505 'should' => 'Should mirror with y being calc (x-offset y-offset)',
506 'expected' => 'div { transform-origin:30.25% calc(15% * (3/2)); }',
507 'input' => 'div { transform-origin:69.75% calc(15% * (3/2)); }',
508 'reversable' => true,
509 'skip' => true
513 'should' => 'Should mirror (y-offset x-offset-keyword)',
514 'expected' => 'div { transform-origin:70% right; }',
515 'input' => 'div { transform-origin:70% left; }',
516 'reversable' => true
518 /* Not supported by MoodleHQ/RTLCSS yet.
520 'should' => 'Should mirror with calc (y-offset x-offset-keyword)',
521 'expected' => 'div { transform-origin:-ms-calc(140%/2) right; }',
522 'input' => 'div { transform-origin:-ms-calc(140%/2) left; }',
523 'reversable' => true,
524 'skip' => true
528 'should' => 'Should mirror (x-offset-keyword y-offset)',
529 'expected' => 'div { transform-origin:right 70%; }',
530 'input' => 'div { transform-origin:left 70%; }',
531 'reversable' => true
533 /* Not supported by MoodleHQ/RTLCSS yet.
535 'should' => 'Should mirror with calc (x-offset-keyword y-offset)',
536 'expected' => 'div { transform-origin:right -moz-calc(((140%/2))); }',
537 'input' => 'div { transform-origin:left -moz-calc(((140%/2))); }',
538 'reversable' => true,
539 'skip' => true
543 'should' => 'Should mirror (y-offset-keyword x-offset)',
544 'expected' => 'div { transform-origin:top 30.25%; }',
545 'input' => 'div { transform-origin:top 69.75%; }',
546 'reversable' => true
548 /* Not supported by MoodleHQ/RTLCSS yet.
550 'should' => 'Should not mirror with x being calc (y-offset-keyword x-offset)',
551 'expected' => 'div { transform-origin:top calc(100% - (((140%/2)))); }',
552 'input' => 'div { transform-origin:top calc(((140%/2))); }',
553 'reversable' => false,
554 'skip' => true
558 'should' => 'Should mirror (x-offset-keyword y-offset-keyword)',
559 'expected' => 'div { transform-origin:right top; }',
560 'input' => 'div { transform-origin:left top; }',
561 'reversable' => true
564 'should' => 'Should mirror (y-offset-keyword x-offset-keyword)',
565 'expected' => 'div { transform-origin:top right; }',
566 'input' => 'div { transform-origin:top left; }',
567 'reversable' => true
570 'should' => 'Should mirror (x-offset y-offset z-offset)',
571 'expected' => 'div { transform-origin:80.25% 30% 10%; }',
572 'input' => 'div { transform-origin:19.75% 30% 10%; }',
573 'reversable' => true
575 /* Not supported by MoodleHQ/RTLCSS yet.
577 'should' => 'Should mirror with x being calc (x-offset y-offset z-offset)',
578 'expected' => 'div { transform-origin: calc(100% - (25% * 3 + 20px)) 30% 10%; }',
579 'input' => 'div { transform-origin: calc(25% * 3 + 20px) 30% 10%; }',
580 'reversable' => false,
581 'skip' => true
585 'should' => 'Should mirror (y-offset x-offset-keyword z-offset)',
586 'expected' => 'div { transform-origin:20% right 10%; }',
587 'input' => 'div { transform-origin:20% left 10%; }',
588 'reversable' => true
591 'should' => 'Should mirror (x-offset-keyword y-offset z-offset)',
592 'expected' => 'div { transform-origin:left 20% 10%; }',
593 'input' => 'div { transform-origin:right 20% 10%; }',
594 'reversable' => true
597 'should' => 'Should mirror (x-offset-keyword y-offset-keyword z-offset)',
598 'expected' => 'div { transform-origin:left bottom 10%; }',
599 'input' => 'div { transform-origin:right bottom 10%; }',
600 'reversable' => true
603 'should' => 'Should mirror (y-offset-keyword x-offset-keyword z-offset)',
604 'expected' => 'div { transform-origin:bottom left 10%; }',
605 'input' => 'div { transform-origin:bottom right 10%; }',
606 'reversable' => true
612 * Data provider.
613 * @return array
615 public function transforms_provider() {
616 return [
617 /* Not supported by MoodleHQ/RTLCSS yet.
619 'should' => 'Should mirror transform : matrix',
620 'expected' => 'div { transform: matrix(2, 0.1, 20.75, 2, 2, 2); }',
621 'input' => 'div { transform: matrix(2, -0.1, -20.75, 2, -2, 2); }',
622 'reversable' => true,
623 'skip' => true
626 'should' => 'Should mirror transform (with no digits before dot): matrix',
627 'expected' => 'div { transform: matrix(2, 0.1, 0.75, 2, 2, 2); }',
628 'input' => 'div { transform: matrix(2, -0.1, -.75, 2, -2, 2); }',
629 'reversable' => false,
630 'skip' => true
633 'should' => 'Should mirror transform with calc: matrix',
634 'expected' => 'div { transform: matrix( -moz-calc(((25%/2) * 10px)), calc(-1*(((25%/2) * 10px))), 20.75, 2, 2, 2 ); }',
635 'input' => 'div { transform: matrix( -moz-calc(((25%/2) * 10px)), calc(((25%/2) * 10px)), -20.75, 2, -2, 2 ); }',
636 'reversable' => false,
637 'skip' => true
640 'should' => 'Should mirror transform : matrix3d',
641 'expected' => 'div { transform:matrix3d(0.227114470162179, 0.127248412323519, 0, 0.000811630714323203, 0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, -165, 67, 0, 1); }',
642 'input' => 'div { transform:matrix3d(0.227114470162179, -0.127248412323519, 0, -0.000811630714323203, -0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, 165, 67, 0, 1); }',
643 'reversable' => true,
644 'skip' => true
647 'should' => 'Should mirror transform (with no digits before dot): matrix3d',
648 'expected' => 'div { transform:matrix3d(0.227114470162179, 0.127248412323519, 0, 0.000811630714323203, 0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, -165, 67, 0, 1); }',
649 'input' => 'div { transform:matrix3d(0.227114470162179, -.127248412323519, 0, -0.000811630714323203, -0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, 165, 67, 0, 1); }',
650 'reversable' => false,
651 'skip' => true
654 'should' => 'Should mirror transform with calc : matrix3d',
655 'expected' => 'div { transform:matrix3d(0.227114470162179, 0.127248412323519, 0, 0.000811630714323203, 0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, calc(-1*(((25%/2) * 10px))), 67, 0, 1); }',
656 'input' => 'div { transform:matrix3d(0.227114470162179, -0.127248412323519, 0, -0.000811630714323203, -0.113139853456515, 1.53997196559414, 0, 0.000596368270149729, 0, 0, 1, 0, calc(((25%/2) * 10px)), 67, 0, 1); }',
657 'reversable' => false,
658 'skip' => true
661 'should' => 'Should mirror transform : translate',
662 'expected' => 'div { transform: translate(-10.75px); }',
663 'input' => 'div { transform: translate(10.75px); }',
664 'reversable' => true,
665 'skip' => true
668 'should' => 'Should mirror transform (with no digits before dot): translate',
669 'expected' => 'div { transform: translate(-0.75px); }',
670 'input' => 'div { transform: translate(.75px); }',
671 'reversable' => false,
672 'skip' => true
675 'should' => 'Should mirror transform with calc: translate',
676 'expected' => 'div { transform: translate(-moz-calc(-1*(((25%/2) * 10px)))); }',
677 'input' => 'div { transform: translate(-moz-calc(((25%/2) * 10px))); }',
678 'reversable' => false,
679 'skip' => true
682 'should' => 'Should mirror transform : translateX',
683 'expected' => 'div { transform: translateX(-50.25px); }',
684 'input' => 'div { transform: translateX(50.25px); }',
685 'reversable' => true,
686 'skip' => true
689 'should' => 'Should mirror transform (with no digits before dot): translateX',
690 'expected' => 'div { transform: translateX(-0.25px); }',
691 'input' => 'div { transform: translateX(.25px); }',
692 'reversable' => false,
693 'skip' => true
696 'should' => 'Should mirror transform with calc : translateX',
697 'expected' => 'div { transform: translateX(-ms-calc(-1*(((25%/2) * 10px))))); }',
698 'input' => 'div { transform: translateX(-ms-calc(((25%/2) * 10px)))); }',
699 'reversable' => false,
700 'skip' => true
703 'should' => 'Should mirror transform : translate3d',
704 'expected' => 'div { transform: translate3d(-12.75px, 50%, 3em); }',
705 'input' => 'div { transform: translate3d(12.75px, 50%, 3em); }',
706 'reversable' => true,
707 'skip' => true
710 'should' => 'Should mirror transform (with no digits before dot): translate3d',
711 'expected' => 'div { transform: translate3d(-0.75px, 50%, 3em); }',
712 'input' => 'div { transform: translate3d(.75px, 50%, 3em); }',
713 'reversable' => false,
714 'skip' => true
717 'should' => 'Should mirror transform with calc: translate3d',
718 'expected' => 'div { transform: translate3d(-webkit-calc(-1*(((25%/2) * 10px))))), 50%, calc(((25%/2) * 10px))))); }',
719 'input' => 'div { transform: translate3d(-webkit-calc(((25%/2) * 10px)))), 50%, calc(((25%/2) * 10px))))); }',
720 'reversable' => false,
721 'skip' => true
724 'should' => 'Should mirror transform : rotate',
725 'expected' => 'div { transform: rotate(-20.75deg); }',
726 'input' => 'div { transform: rotate(20.75deg); }',
727 'reversable' => true,
728 'skip' => true
731 'should' => 'Should mirror transform (with no digits before dot): rotate',
732 'expected' => 'div { transform: rotate(-0.75deg); }',
733 'input' => 'div { transform: rotate(.75deg); }',
734 'reversable' => false,
735 'skip' => true
738 'should' => 'Should mirror transform with calc: rotate',
739 'expected' => 'div { transform: rotate(calc(-1*(((25%/2) * 10deg)))); }',
740 'input' => 'div { transform: rotate(calc(((25%/2) * 10deg))); }',
741 'reversable' => false,
742 'skip' => true
745 'should' => 'Should mirror transform : rotate3d',
746 'expected' => 'div { transform: rotate3d(10, -20.15, 10, -45.14deg); }',
747 'input' => 'div { transform: rotate3d(10, 20.15, 10, 45.14deg); }',
748 'reversable' => true,
749 'skip' => true
752 'should' => 'Should mirror transform (with no digits before dot): rotate3d',
753 'expected' => 'div { transform: rotate3d(10, -20, 10, -0.14deg); }',
754 'input' => 'div { transform: rotate3d(10, 20, 10, .14deg); }',
755 'reversable' => false,
756 'skip' => true
759 'should' => 'Should mirror transform with calc: rotate3d',
760 'expected' => 'div { transform: rotate3d(10, -20.15, 10, calc(-1*(((25%/2) * 10deg)))); }',
761 'input' => 'div { transform: rotate3d(10, 20.15, 10, calc(((25%/2) * 10deg))); }',
762 'reversable' => false,
763 'skip' => true
766 'should' => 'Should not mirror transform : rotateX',
767 'expected' => 'div { transform: rotateX(45deg); }',
768 'input' => 'div { transform: rotateX(45deg); }',
769 'reversable' => false,
770 'skip' => true
773 'should' => 'Should not mirror transform with calc: rotateX',
774 'expected' => 'div { transform: rotateX(calc(((25%/2) * 10deg))); }',
775 'input' => 'div { transform: rotateX(calc(((25%/2) * 10deg))); }',
776 'reversable' => false,
777 'skip' => true
780 'should' => 'Should not mirror transform : rotateY',
781 'expected' => 'div { transform: rotateY(45deg); }',
782 'input' => 'div { transform: rotateY(45deg); }',
783 'reversable' => false,
784 'skip' => true
787 'should' => 'Should not mirror transform with calc: rotateY',
788 'expected' => 'div { transform: rotateY(calc(((25%/2) * 10deg))); }',
789 'input' => 'div { transform: rotateY(calc(((25%/2) * 10deg))); }',
790 'reversable' => false,
791 'skip' => true
794 'should' => 'Should mirror transform : rotateZ',
795 'expected' => 'div { transform: rotateZ(-45.75deg); }',
796 'input' => 'div { transform: rotateZ(45.75deg); }',
797 'reversable' => true,
798 'skip' => true
801 'should' => 'Should mirror transform (with no digits before dot): rotateZ',
802 'expected' => 'div { transform: rotateZ(-0.75deg); }',
803 'input' => 'div { transform: rotateZ(.75deg); }',
804 'reversable' => false,
805 'skip' => true
808 'should' => 'Should mirror transform with calc: rotateZ',
809 'expected' => 'div { transform: rotateZ(-ms-calc(-1*(((25%/2) * 10deg)))); }',
810 'input' => 'div { transform: rotateZ(-ms-calc(((25%/2) * 10deg))); }',
811 'reversable' => false,
812 'skip' => true
815 'should' => 'Should mirror transform : skew',
816 'expected' => 'div { transform: skew(-20.25rad,-30deg); }',
817 'input' => 'div { transform: skew(20.25rad,30deg); }',
818 'reversable' => true,
819 'skip' => true
822 'should' => 'Should mirror transform (with no digits before dot): skew',
823 'expected' => 'div { transform: skew(-0.25rad,-30deg); }',
824 'input' => 'div { transform: skew(.25rad,30deg); }',
825 'reversable' => false,
826 'skip' => true
829 'should' => 'Should mirror transform with calc: skew',
830 'expected' => 'div { transform: skew(calc(-1*(((25%/2) * 10rad))),calc(-1*(((25%/2) * 10deg)))); }',
831 'input' => 'div { transform: skew(calc(((25%/2) * 10rad)),calc(((25%/2) * 10deg))); }',
832 'reversable' => false,
833 'skip' => true
836 'should' => 'Should mirror transform : skewX',
837 'expected' => 'div { transform: skewX(-20.75rad); }',
838 'input' => 'div { transform: skewX(20.75rad); }',
839 'reversable' => true,
840 'skip' => true
843 'should' => 'Should mirror transform (with no digits before dot): skewX',
844 'expected' => 'div { transform: skewX(-0.75rad); }',
845 'input' => 'div { transform: skewX(.75rad); }',
846 'reversable' => false,
847 'skip' => true
850 'should' => 'Should mirror transform with calc: skewX',
851 'expected' => 'div { transform: skewX(-moz-calc(-1*(((25%/2) * 10rad)))); }',
852 'input' => 'div { transform: skewX(-moz-calc(((25%/2) * 10rad))); }',
853 'reversable' => false,
854 'skip' => true
857 'should' => 'Should mirror transform : skewY',
858 'expected' => 'div { transform: skewY(-10.75grad); }',
859 'input' => 'div { transform: skewY(10.75grad); }',
860 'reversable' => true,
861 'skip' => true
864 'should' => 'Should mirror transform (with no digits before dot): skewY',
865 'expected' => 'div { transform: skewY(-0.75grad); }',
866 'input' => 'div { transform: skewY(.75grad); }',
867 'reversable' => false,
868 'skip' => true
871 'should' => 'Should mirror transform with calc: skewY',
872 'expected' => 'div { transform: skewY(calc(-1*(((25%/2) * 10grad)))); }',
873 'input' => 'div { transform: skewY(calc(((25%/2) * 10grad))); }',
874 'reversable' => false,
875 'skip' => true
878 'should' => 'Should mirror multiple transforms : translateX translateY Rotate',
879 'expected' => 'div { transform: translateX(-50.25px) translateY(50.25px) rotate(-20.75deg); }',
880 'input' => 'div { transform: translateX(50.25px) translateY(50.25px) rotate(20.75deg); }',
881 'reversable' => true,
882 'skip' => true
885 'should' => 'Should mirror multiple transforms with calc : translateX translateY Rotate',
886 'expected' => 'div { transform: translateX(-ms-calc(-1*(((25%/2) * 10px)))) translateY(-moz-calc(((25%/2) * 10rad))) rotate(calc(-1*(((25%/2) * 10grad)))); }',
887 'input' => 'div { transform: translateX(-ms-calc(((25%/2) * 10px))) translateY(-moz-calc(((25%/2) * 10rad))) rotate(calc(((25%/2) * 10grad))); }',
888 'reversable' => false,
889 'skip' => true
896 * Data provider.
897 * @return array
899 public function values_nsyntax_provider() {
900 return [
902 'should' => 'Should mirror property value: border-radius (4 values)',
903 'expected' => 'div { border-radius: 40.25px 10.5px 10.75px 40.3px; }',
904 'input' => 'div { border-radius: 10.5px 40.25px 40.3px 10.75px; }',
905 'reversable' => true
908 'should' => 'Should mirror property value: border-radius (3 values)',
909 'expected' => 'div { border-radius: 40.75px 10.75px 40.75px 40.3px; }',
910 'input' => 'div { border-radius: 10.75px 40.75px 40.3px; }',
911 'reversable' => false
914 'should' => 'Should mirror property value: border-radius (2 values)',
915 'expected' => 'div { border-radius: 40.25px 10.75px; }',
916 'input' => 'div { border-radius: 10.75px 40.25px; }',
917 'reversable' => true
919 /* Not supported by MoodleHQ/RTLCSS yet.
921 'should' => 'Should mirror property value: border-radius (4 values - double)',
922 'expected' => 'div { border-radius: 40.25px 10.75px .5px 40.75px / .4em 1em 1em 4.5em; }',
923 'input' => 'div { border-radius: 10.75px 40.25px 40.75px .5px / 1em .4em 4.5em 1em; }',
924 'reversable' => true,
925 'skip' => true
928 'should' => 'Should mirror property value: border-radius (3 values - double)',
929 'expected' => 'div { border-radius: .40px 10.5px .40px 40px / 4em 1em 4em 3em; }',
930 'input' => 'div { border-radius: 10.5px .40px 40px / 1em 4em 3em; }',
931 'reversable' => false,
932 'skip' => true
935 'should' => 'Should mirror property value: border-radius (2 values- double)',
936 'expected' => 'div { border-radius: 40px 10px / 2.5em .75em; }',
937 'input' => 'div { border-radius: 10px 40px / .75em 2.5em; }',
938 'reversable' => true,
939 'skip' => true
943 'should' => 'Should mirror property value: border-width',
944 'expected' => 'div { border-width: 1px 4px .3em 2.5em; }',
945 'input' => 'div { border-width: 1px 2.5em .3em 4px; }',
946 'reversable' => true
949 'should' => 'Should mirror property value: border-width (none length)',
950 'expected' => 'div { border-width: thin medium thick none; }',
951 'input' => 'div { border-width: thin none thick medium; }',
952 'reversable' => true
955 'should' => 'Should mirror property value: border-style (4 values)',
956 'expected' => 'div { border-style: none dashed dotted solid; }',
957 'input' => 'div { border-style: none solid dotted dashed; }',
958 'reversable' => true
961 'should' => 'Should mirror property value: border-color (4 values)',
962 'expected' => 'div { border-color: rgba(255, 255, 255, 1) rgb(0, 0, 0) rgb(0, 0, 0) hsla(0, 100%, 50%, 1); }',
963 'input' => 'div { border-color: rgba(255, 255, 255, 1) hsla(0, 100%, 50%, 1) rgb(0, 0, 0) rgb(0, 0, 0); }',
964 'reversable' => true
967 'should' => 'Should not mirror property value: border-color (3 values)',
968 'expected' => 'div { border-color: rgb(0, 0, 0) rgb(0, 0, 0) hsla(0, 100%, 50%, 1); }',
969 'input' => 'div { border-color: #000 rgb(0, 0, 0) hsla(0, 100%, 50%, 1); }',
970 'reversable' => false
973 'should' => 'Should not mirror property value: border-color (2 values)',
974 'expected' => 'div { border-color: rgb(0, 0, 0) hsla(0, 100%, 50%, 1); }',
975 'input' => 'div { border-color: rgb(0, 0, 0) hsla(0, 100%, 50%, 1); }',
976 'reversable' => false
979 'should' => 'Should mirror property value: margin',
980 'expected' => 'div { margin: .1em auto 3.5rem 2px; }',
981 'input' => 'div { margin: .1em 2px 3.5rem auto; }',
982 'reversable' => true
985 'should' => 'Should mirror property value: padding',
986 'expected' => 'div { padding: 1px 4px .3rem 2.5em; }',
987 'input' => 'div { padding: 1px 2.5em .3rem 4px; }',
988 'reversable' => true
990 /* Not supported by MoodleHQ/RTLCSS yet.
992 'should' => 'Should mirror property value: box-shadow',
993 'expected' => 'div { box-shadow: -60px -16px rgba(0, 128, 128, 0.98), -10.25px 5px 5px #ff0, inset -0.5em 1em 0 white; }',
994 'input' => 'div { box-shadow: 60px -16px rgba(0, 128, 128, 0.98), 10.25px 5px 5px #ff0, inset 0.5em 1em 0 white; }',
995 'reversable' => true,
996 'skip' => true
999 'should' => 'Should mirror property value: text-shadow',
1000 'expected' => 'div { text-shadow: -60px -16px rgba(0, 128, 128, 0.98), -10.25px 5px 5px #ff0, inset -0.5em 1em 0 white; }',
1001 'input' => 'div { text-shadow: 60px -16px rgba(0, 128, 128, 0.98), 10.25px 5px 5px #ff0, inset 0.5em 1em 0 white; }',
1002 'reversable' => true,
1003 'skip' => true
1006 'should' => 'Should mirror property value (no digit before the dot): box-shadow, text-shadow',
1007 'expected' => 'div { box-shadow: inset -0.5em 1em 0 white; text-shadow: inset -0.5em 1em 0 white; }',
1008 'input' => 'div { box-shadow: inset .5em 1em 0 white; text-shadow: inset .5em 1em 0 white; }',
1009 'reversable' => false,
1010 'skip' => true
1017 * Data provider.
1018 * @return array
1020 public function values_provider() {
1021 return [
1023 'should' => 'Should mirror property value: clear',
1024 'expected' => 'div { clear:right; }',
1025 'input' => 'div { clear:left; }',
1026 'reversable' => true
1029 'should' => 'Should mirror property value: direction',
1030 'expected' => 'div { direction:ltr; }',
1031 'input' => 'div { direction:rtl; }',
1032 'reversable' => true
1035 'should' => 'Should mirror property value: float',
1036 'expected' => 'div { float:right; }',
1037 'input' => 'div { float:left; }',
1038 'reversable' => true
1041 'should' => 'Should mirror property value: text-align',
1042 'expected' => 'div { text-align:right; }',
1043 'input' => 'div { text-align:left; }',
1044 'reversable' => true
1047 'should' => 'Should mirror property value: cursor nw',
1048 'expected' => 'div { cursor:nw-resize; }',
1049 'input' => 'div { cursor:ne-resize; }',
1050 'reversable' => true
1053 'should' => 'Should mirror property value: cursor sw',
1054 'expected' => 'div { cursor:sw-resize; }',
1055 'input' => 'div { cursor:se-resize; }',
1056 'reversable' => true
1059 'should' => 'Should mirror property value: cursor nesw',
1060 'expected' => 'div { cursor:nesw-resize; }',
1061 'input' => 'div { cursor:nwse-resize; }',
1062 'reversable' => true
1065 'should' => 'Should keep property value as is: cursor ew',
1066 'expected' => 'div { cursor:ew-resize; }',
1067 'input' => 'div { cursor:ew-resize; }',
1068 'reversable' => false
1070 /* Not supported by MoodleHQ/RTLCSS yet.
1072 'should' => 'Should process string map in url: cursor (processUrls: true)',
1073 'expected' => '.foo { cursor: url(right.cur), url(rtl.cur), se-resize, auto }',
1074 'input' => '.foo { cursor: url(left.cur), url(ltr.cur), sw-resize, auto }',
1075 'reversable' => true,
1076 'options' => [ 'processUrls' => true ],
1077 'skip' => true
1081 'should' => 'Should mirror property value: transition',
1082 'expected' => '.foo { transition:right .3s ease .1s,left .3s ease .1s,margin-right .3s ease,margin-left .3s ease,padding-right .3s ease,padding-left .3s ease; }',
1083 'input' => '.foo { transition:left .3s ease .1s,right .3s ease .1s,margin-left .3s ease,margin-right .3s ease,padding-left .3s ease,padding-right .3s ease; }',
1084 'reversable' => true
1087 'should' => 'Should mirror property value: transition-property',
1088 'expected' => '.foo { transition-property:right; }',
1089 'input' => '.foo { transition-property:left; }',
1090 'reversable' => true
1096 * Assert that the provided data flips.
1098 * @param string $expected The expected output.
1099 * @param string $input The input.
1100 * @param string $description The description of the assertion.
1101 * @param OutputFormat $output The output format to use.
1103 protected function assert_flips($expected, $input, $description, $output = null) {
1104 $parser = new Parser($input);
1105 $tree = $parser->parse();
1106 $rtlcss = new core_rtlcss($tree);
1107 $flipped = $rtlcss->flip();
1108 $this->assertEquals($expected, $flipped->render($output), $description);
1112 * Assert data.
1114 * @param array $data With the keys: 'input', 'expected', 'reversable', 'should', and 'skip'.
1115 * @param OutputFormat $output The output format to use.
1117 protected function assert_sample($data, $output = null) {
1118 if (!empty($data['skip'])) {
1119 $this->markTestSkipped('Not yet supported!');
1121 $this->assert_flips($data['expected'], $data['input'], $data['should'], $output);
1122 if (!empty($data['reversable'])) {
1123 $this->assert_flips($data['input'], $data['expected'], $data['should'] . ' (reversed)', $output);
1128 * Test background images.
1129 * @param array $data the provider data.
1130 * @dataProvider background_image_provider
1132 /* Not supported by MoodleHQ/RTLCSS yet.
1133 public function test_background_image($data) {
1134 $output = new OutputFormat();
1135 $this->assert_sample($data, $output);
1140 * Test background position.
1141 * @param array $data the provider data.
1142 * @dataProvider background_position_provider
1144 public function test_background_position($data) {
1145 $output = new OutputFormat();
1146 $output->set('SpaceAfterRuleName', '');
1147 $output->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
1148 $this->assert_sample($data, $output);
1152 * Test background.
1153 * @param array $data the provider data.
1154 * @dataProvider background_provider
1156 public function test_background($data) {
1157 $output = new OutputFormat();
1158 $output->set('SpaceAfterRuleName', ' ');
1159 $output->set('SpaceBeforeRules', ' ');
1160 $output->set('SpaceAfterRules', ' ');
1161 $output->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
1162 $this->assert_sample($data, $output);
1166 * Test directives.
1167 * @param array $data the provider data.
1168 * @dataProvider directives_provider
1170 public function test_directives($data) {
1171 $output = new OutputFormat();
1172 $output->set('SpaceAfterRuleName', '');
1173 $output->set('SpaceBeforeRules', '');
1174 $output->set('SpaceAfterRules', '');
1175 $output->set('SpaceBetweenRules', '');
1176 $output->set('SpaceBetweenBlocks', ' ');
1177 $output->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
1178 $this->assert_sample($data, $output);
1182 * Test properties.
1183 * @param array $data the provider data.
1184 * @dataProvider properties_provider
1186 public function test_properties($data) {
1187 $output = new OutputFormat();
1188 $output->set('SpaceAfterRuleName', '');
1189 $output->set('SpaceBeforeRules', ' ');
1190 $output->set('SpaceAfterRules', ' ');
1191 $output->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
1192 $this->assert_sample($data, $output);
1196 * Test special.
1197 * @param array $data the provider data.
1198 * @dataProvider special_provider
1200 /* Not supported by MoodleHQ/RTLCSS yet.
1201 public function test_special($data) {
1202 $output = new OutputFormat();
1203 $output->set('SpaceBeforeRules', ' ');
1204 $output->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
1205 $this->assert_sample($data, $output);
1210 * Test transform original.
1211 * @param array $data the provider data.
1212 * @dataProvider transform_origin_provider
1214 public function test_transform_origin($data) {
1215 $output = new OutputFormat();
1216 $output->set('SpaceAfterRuleName', '');
1217 $output->set('SpaceBeforeRules', ' ');
1218 $output->set('SpaceAfterRules', ' ');
1219 $output->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
1220 $this->assert_sample($data, $output);
1225 * Test transform.
1226 * @param array $data the provider data.
1227 * @dataProvider transforms_provider
1229 /* Not supported by MoodleHQ/RTLCSS yet.
1230 public function test_transforms($data) {
1231 $output = new OutputFormat();
1232 $output->set('SpaceBeforeRules', ' ');
1233 $output->set('SpaceAfterRules', ' ');
1234 $output->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
1235 $this->assert_sample($data, $output);
1240 * Test values n-syntax.
1241 * @param array $data the provider data.
1242 * @dataProvider values_nsyntax_provider
1244 public function test_values_nsyntax($data) {
1245 $output = new OutputFormat();
1246 $output->set('SpaceBeforeRules', ' ');
1247 $output->set('SpaceAfterRules', ' ');
1248 $output->set('RGBHashNotation', false);
1249 $output->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
1250 $this->assert_sample($data, $output);
1254 * Test values.
1255 * @param array $data the provider data.
1256 * @dataProvider values_provider
1258 public function test_values($data) {
1259 $output = new OutputFormat();
1260 $output->set('SpaceAfterRuleName', '');
1261 $output->set('SpaceBeforeRules', ' ');
1262 $output->set('SpaceAfterRules', ' ');
1263 $output->set('SpaceAfterListArgumentSeparator', array('default' => '', ',' => ' '));
1264 $this->assert_sample($data, $output);