MediaWiki  REL1_22
CSSJanusTest.php
Go to the documentation of this file.
00001 <?php
00010 class CSSJanusTest extends MediaWikiTestCase {
00014     public function testTransform( $cssA, $cssB = null ) {
00015 
00016         if ( $cssB ) {
00017             $transformedA = CSSJanus::transform( $cssA );
00018             $this->assertEquals( $transformedA, $cssB, 'Test A-B transformation' );
00019 
00020             $transformedB = CSSJanus::transform( $cssB );
00021             $this->assertEquals( $transformedB, $cssA, 'Test B-A transformation' );
00022         } else {
00023             // If no B version is provided, it means
00024             // the output should equal the input.
00025             $transformedA = CSSJanus::transform( $cssA );
00026             $this->assertEquals( $transformedA, $cssA, 'Nothing was flipped' );
00027         }
00028     }
00029 
00033     public function testTransformAdvanced( $code, $expectedOutput, $options = array() ) {
00034         $swapLtrRtlInURL = isset( $options['swapLtrRtlInURL'] ) ? $options['swapLtrRtlInURL'] : false;
00035         $swapLeftRightInURL = isset( $options['swapLeftRightInURL'] ) ? $options['swapLeftRightInURL'] : false;
00036 
00037         $flipped = CSSJanus::transform( $code, $swapLtrRtlInURL, $swapLeftRightInURL );
00038 
00039         $this->assertEquals( $expectedOutput, $flipped,
00040             'Test flipping, options: url-ltr-rtl=' . ( $swapLtrRtlInURL ? 'true' : 'false' )
00041                 . ' url-left-right=' . ( $swapLeftRightInURL ? 'true' : 'false' )
00042         );
00043     }
00044 
00049     public function testTransformBroken( $code, $expectedOutput ) {
00050         $flipped = CSSJanus::transform( $code );
00051 
00052         $this->assertEquals( $expectedOutput, $flipped, 'Test flipping' );
00053     }
00054 
00059     public static function provideTransformCases() {
00060         return array(
00061             // Property keys
00062             array(
00063                 '.foo { left: 0; }',
00064                 '.foo { right: 0; }'
00065             ),
00066             // Guard against partial keys
00067             // (CSS currently doesn't have flippable properties
00068             // that contain the direction as part of the key without
00069             // dash separation)
00070             array(
00071                 '.foo { alright: 0; }'
00072             ),
00073             array(
00074                 '.foo { balleft: 0; }'
00075             ),
00076 
00077             // Dashed property keys
00078             array(
00079                 '.foo { padding-left: 0; }',
00080                 '.foo { padding-right: 0; }'
00081             ),
00082             array(
00083                 '.foo { margin-left: 0; }',
00084                 '.foo { margin-right: 0; }'
00085             ),
00086             array(
00087                 '.foo { border-left: 0; }',
00088                 '.foo { border-right: 0; }'
00089             ),
00090 
00091             // Double-dashed property keys
00092             array(
00093                 '.foo { border-left-color: red; }',
00094                 '.foo { border-right-color: red; }'
00095             ),
00096             array(
00097                 // Includes unknown properties?
00098                 '.foo { x-left-y: 0; }',
00099                 '.foo { x-right-y: 0; }'
00100             ),
00101 
00102             // Multi-value properties
00103             array(
00104                 '.foo { padding: 0; }'
00105             ),
00106             array(
00107                 '.foo { padding: 0 1px; }'
00108             ),
00109             array(
00110                 '.foo { padding: 0 1px 2px; }'
00111             ),
00112             array(
00113                 '.foo { padding: 0 1px 2px 3px; }',
00114                 '.foo { padding: 0 3px 2px 1px; }'
00115             ),
00116 
00117             // Shorthand / Four notation
00118             array(
00119                 '.foo { padding: .25em 15px 0pt 0ex; }',
00120                 '.foo { padding: .25em 0ex 0pt 15px; }'
00121             ),
00122             array(
00123                 '.foo { margin: 1px -4px 3px 2px; }',
00124                 '.foo { margin: 1px 2px 3px -4px; }'
00125             ),
00126             array(
00127                 '.foo { padding: 0 15px .25em 0; }',
00128                 '.foo { padding: 0 0 .25em 15px; }'
00129             ),
00130             array(
00131                 '.foo { padding: 1px 4.1grad 3px 2%; }',
00132                 '.foo { padding: 1px 2% 3px 4.1grad; }'
00133             ),
00134             array(
00135                 '.foo { padding: 1px 2px 3px auto; }',
00136                 '.foo { padding: 1px auto 3px 2px; }'
00137             ),
00138             array(
00139                 '.foo { padding: 1px inherit 3px auto; }',
00140                 '.foo { padding: 1px auto 3px inherit; }'
00141             ),
00142             // border-radius assigns different meanings to the values
00143             array(
00144                 '.foo { border-radius: .25em 15px 0pt 0ex; }',
00145                 '.foo { border-radius: 15px .25em 0ex 0pt; }'
00146             ),
00147             array(
00148                 '.foo { border-radius: 0px 0px 5px 5px; }',
00149             ),
00150             // Ensure the rule doesn't break other stuff
00151             array(
00152                 '.foo { x-unknown: a b c d; }'
00153             ),
00154             array(
00155                 '.foo barpx 0 2% { opacity: 0; }'
00156             ),
00157             array(
00158                 '#settings td p strong'
00159             ),
00160             array(
00161                 // Color names
00162                 '.foo { border-color: red green blue white }',
00163                 '.foo { border-color: red white blue green }',
00164             ),
00165             array(
00166                 // Color name, hexdecimal, RGB & RGBA
00167                 '.foo { border-color: red #f00 rgb(255, 0, 0) rgba(255, 0, 0, 0.5) }',
00168                 '.foo { border-color: red rgba(255, 0, 0, 0.5) rgb(255, 0, 0) #f00 }',
00169             ),
00170             array(
00171                 // Color name, hexdecimal, HSL & HSLA
00172                 '.foo { border-color: red #f00 hsl(0, 100%, 50%) hsla(0, 100%, 50%, 0.5) }',
00173                 '.foo { border-color: red hsla(0, 100%, 50%, 0.5) hsl(0, 100%, 50%) #f00 }',
00174             ),
00175             array(
00176                 // Do not mangle 5 or more values
00177                 '.foo { -x-unknown: 1 2 3 4 5; }'
00178             ),
00179             array(
00180                 '.foo { -x-unknown: 1 2 3 4 5 6; }'
00181             ),
00182 
00183             // Shorthand / Three notation
00184             array(
00185                 '.foo { margin: 1em 0 .25em; }'
00186             ),
00187             array(
00188                 '.foo { margin:-1.5em 0 -.75em; }'
00189             ),
00190 
00191             // Shorthand / Two notation
00192             array(
00193                 '.foo { padding: 1px 2px; }'
00194             ),
00195 
00196             // Shorthand / One notation
00197             array(
00198                 '.foo { padding: 1px; }'
00199             ),
00200 
00201             // text-shadow and box-shadow
00202             array(
00203                 '.foo { box-shadow: -6px 3px 8px 5px rgba(0, 0, 0, 0.25); }',
00204                 '.foo { box-shadow: 6px 3px 8px 5px rgba(0, 0, 0, 0.25); }',
00205             ),
00206             array(
00207                 '.foo { box-shadow: inset -6px 3px 8px 5px rgba(0, 0, 0, 0.25); }',
00208                 '.foo { box-shadow: inset 6px 3px 8px 5px rgba(0, 0, 0, 0.25); }',
00209             ),
00210             array(
00211                 '.foo { text-shadow: orange 2px 0; }',
00212                 '.foo { text-shadow: orange -2px 0; }',
00213             ),
00214             array(
00215                 '.foo { text-shadow: 2px 0 orange; }',
00216                 '.foo { text-shadow: -2px 0 orange; }',
00217             ),
00218             array(
00219                 // Don't mangle zeroes
00220                 '.foo { text-shadow: orange 0 2px; }'
00221             ),
00222 
00223             // Direction
00224             // Note: This differs from the Python implementation,
00225             // see also CSSJanus::fixDirection for more info.
00226             array(
00227                 '.foo { direction: ltr; }',
00228                 '.foo { direction: rtl; }'
00229             ),
00230             array(
00231                 '.foo { direction: rtl; }',
00232                 '.foo { direction: ltr; }'
00233             ),
00234             array(
00235                 'input { direction: ltr; }',
00236                 'input { direction: rtl; }'
00237             ),
00238             array(
00239                 'input { direction: rtl; }',
00240                 'input { direction: ltr; }'
00241             ),
00242             array(
00243                 'body { direction: ltr; }',
00244                 'body { direction: rtl; }'
00245             ),
00246             array(
00247                 '.foo, body, input { direction: ltr; }',
00248                 '.foo, body, input { direction: rtl; }'
00249             ),
00250             array(
00251                 'body { padding: 10px; direction: ltr; }',
00252                 'body { padding: 10px; direction: rtl; }'
00253             ),
00254             array(
00255                 'body { direction: ltr } .myClass { direction: ltr }',
00256                 'body { direction: rtl } .myClass { direction: rtl }'
00257             ),
00258 
00259             // Left/right values
00260             array(
00261                 '.foo { float: left; }',
00262                 '.foo { float: right; }'
00263             ),
00264             array(
00265                 '.foo { text-align: left; }',
00266                 '.foo { text-align: right; }'
00267             ),
00268             array(
00269                 '.foo { -x-unknown: left; }',
00270                 '.foo { -x-unknown: right; }'
00271             ),
00272             // Guard against selectors that look flippable
00273             array(
00274                 '.column-left { width: 0; }'
00275             ),
00276             array(
00277                 'a.left { width: 0; }'
00278             ),
00279             array(
00280                 'a.leftification { width: 0; }'
00281             ),
00282             array(
00283                 'a.ltr { width: 0; }'
00284             ),
00285             array(
00286                 # <div class="a-ltr png">
00287                 '.a-ltr.png { width: 0; }'
00288             ),
00289             array(
00290                 # <foo-ltr attr="x">
00291                 'foo-ltr[attr="x"] { width: 0; }'
00292             ),
00293             array(
00294                 'div.left > span.right+span.left { width: 0; }'
00295             ),
00296             array(
00297                 '.thisclass .left .myclass { width: 0; }'
00298             ),
00299             array(
00300                 '.thisclass .left .myclass #myid { width: 0; }'
00301             ),
00302 
00303             // Cursor values (east/west)
00304             array(
00305                 '.foo { cursor: e-resize; }',
00306                 '.foo { cursor: w-resize; }'
00307             ),
00308             array(
00309                 '.foo { cursor: se-resize; }',
00310                 '.foo { cursor: sw-resize; }'
00311             ),
00312             array(
00313                 '.foo { cursor: ne-resize; }',
00314                 '.foo { cursor: nw-resize; }'
00315             ),
00316 
00317             // Background
00318             array(
00319                 '.foo { background-position: top left; }',
00320                 '.foo { background-position: top right; }'
00321             ),
00322             array(
00323                 '.foo { background: url(/foo/bar.png) top left; }',
00324                 '.foo { background: url(/foo/bar.png) top right; }'
00325             ),
00326             array(
00327                 '.foo { background: url(/foo/bar.png) top left no-repeat; }',
00328                 '.foo { background: url(/foo/bar.png) top right no-repeat; }'
00329             ),
00330             array(
00331                 '.foo { background: url(/foo/bar.png) no-repeat top left; }',
00332                 '.foo { background: url(/foo/bar.png) no-repeat top right; }'
00333             ),
00334             array(
00335                 '.foo { background: #fff url(/foo/bar.png) no-repeat top left; }',
00336                 '.foo { background: #fff url(/foo/bar.png) no-repeat top right; }'
00337             ),
00338             array(
00339                 '.foo { background-position: 100% 40%; }',
00340                 '.foo { background-position: 0% 40%; }'
00341             ),
00342             array(
00343                 '.foo { background-position: 23% 0; }',
00344                 '.foo { background-position: 77% 0; }'
00345             ),
00346             array(
00347                 '.foo { background-position: 23% auto; }',
00348                 '.foo { background-position: 77% auto; }'
00349             ),
00350             array(
00351                 '.foo { background-position-x: 23%; }',
00352                 '.foo { background-position-x: 77%; }'
00353             ),
00354             array(
00355                 '.foo { background-position-y: 23%; }',
00356                 '.foo { background-position-y: 23%; }'
00357             ),
00358             array(
00359                 '.foo { background:url(../foo.png) no-repeat 75% 50%; }',
00360                 '.foo { background:url(../foo.png) no-repeat 25% 50%; }'
00361             ),
00362             array(
00363                 '.foo { background: 10% 20% } .bar { background: 40% 30% }',
00364                 '.foo { background: 90% 20% } .bar { background: 60% 30% }'
00365             ),
00366 
00367             // Multiple rules
00368             array(
00369                 'body { direction: rtl; float: right; } .foo { direction: ltr; float: right; }',
00370                 'body { direction: ltr; float: left; } .foo { direction: rtl; float: left; }',
00371             ),
00372 
00373             // Duplicate properties
00374             array(
00375                 '.foo { float: left; float: right; float: left; }',
00376                 '.foo { float: right; float: left; float: right; }',
00377             ),
00378 
00379             // Preserve comments
00380             array(
00381                 '/* left /* right */left: 10px',
00382                 '/* left /* right */right: 10px'
00383             ),
00384             array(
00385                 '/*left*//*left*/left: 10px',
00386                 '/*left*//*left*/right: 10px'
00387             ),
00388             array(
00389                 '/* Going right is cool */ .foo { width: 0 }',
00390             ),
00391             array(
00392                 "/* padding-right 1 2 3 4 */\n#test { width: 0}\n/*right*/"
00393             ),
00394             array(
00395                 "/** Two line comment\n * left\n \*/\n#test {width: 0}"
00396             ),
00397 
00398             // @noflip annotation
00399             array(
00400                 // before selector (single)
00401                 '/* @noflip */ div { float: left; }'
00402             ),
00403             array(
00404                 // before selector (multiple)
00405                 '/* @noflip */ div, .notme { float: left; }'
00406             ),
00407             array(
00408                 // inside selector
00409                 'div, /* @noflip */ .foo { float: left; }'
00410             ),
00411             array(
00412                 // after selector
00413                 'div, .notme /* @noflip */ { float: left; }'
00414             ),
00415             array(
00416                 // before multiple rules
00417                 '/* @noflip */ div { float: left; } .foo { float: left; }',
00418                 '/* @noflip */ div { float: left; } .foo { float: right; }'
00419             ),
00420             array(
00421                 // support parentheses in selector
00422                 '/* @noflip */ .test:not(:first) { margin-right: -0.25em; margin-left: 0.25em; }',
00423                 '/* @noflip */ .test:not(:first) { margin-right: -0.25em; margin-left: 0.25em; }'
00424             ),
00425             array(
00426                 // after multiple rules
00427                 '.foo { float: left; } /* @noflip */ div { float: left; }',
00428                 '.foo { float: right; } /* @noflip */ div { float: left; }'
00429             ),
00430             array(
00431                 // before multiple properties
00432                 'div { /* @noflip */ float: left; text-align: left; }',
00433                 'div { /* @noflip */ float: left; text-align: right; }'
00434             ),
00435             array(
00436                 // after multiple properties
00437                 'div { float: left; /* @noflip */ text-align: left; }',
00438                 'div { float: right; /* @noflip */ text-align: left; }'
00439             ),
00440 
00441             // Guard against css3 stuff
00442             array(
00443                 'background-image: -moz-linear-gradient(#326cc1, #234e8c);'
00444             ),
00445             array(
00446                 'background-image: -webkit-gradient(linear, 100% 0%, 0% 0%, from(#666666), to(#ffffff));'
00447             ),
00448 
00449             // CSS syntax / white-space variations
00450             // spaces, no spaces, tabs, new lines, omitting semi-colons
00451             array(
00452                 ".foo { left: 0; }",
00453                 ".foo { right: 0; }"
00454             ),
00455             array(
00456                 ".foo{ left: 0; }",
00457                 ".foo{ right: 0; }"
00458             ),
00459             array(
00460                 ".foo{ left: 0 }",
00461                 ".foo{ right: 0 }"
00462             ),
00463             array(
00464                 ".foo{left:0 }",
00465                 ".foo{right:0 }"
00466             ),
00467             array(
00468                 ".foo{left:0}",
00469                 ".foo{right:0}"
00470             ),
00471             array(
00472                 ".foo  {  left : 0 ; }",
00473                 ".foo  {  right : 0 ; }"
00474             ),
00475             array(
00476                 ".foo\n  {  left : 0 ; }",
00477                 ".foo\n  {  right : 0 ; }"
00478             ),
00479             array(
00480                 ".foo\n  {  \nleft : 0 ; }",
00481                 ".foo\n  {  \nright : 0 ; }"
00482             ),
00483             array(
00484                 ".foo\n  { \n left : 0 ; }",
00485                 ".foo\n  { \n right : 0 ; }"
00486             ),
00487             array(
00488                 ".foo\n  { \n left\n  : 0; }",
00489                 ".foo\n  { \n right\n  : 0; }"
00490             ),
00491             array(
00492                 ".foo \n  { \n left\n  : 0; }",
00493                 ".foo \n  { \n right\n  : 0; }"
00494             ),
00495             array(
00496                 ".foo\n{\nleft\n:\n0;}",
00497                 ".foo\n{\nright\n:\n0;}"
00498             ),
00499             array(
00500                 ".foo\n.bar {\n\tleft: 0;\n}",
00501                 ".foo\n.bar {\n\tright: 0;\n}"
00502             ),
00503             array(
00504                 ".foo\t{\tleft\t:\t0;}",
00505                 ".foo\t{\tright\t:\t0;}"
00506             ),
00507 
00508             // Guard against partial keys
00509             array(
00510                 '.foo { leftxx: 0; }',
00511                 '.foo { leftxx: 0; }'
00512             ),
00513             array(
00514                 '.foo { rightxx: 0; }',
00515                 '.foo { rightxx: 0; }'
00516             ),
00517         );
00518     }
00519 
00525     public static function provideTransformAdvancedCases() {
00526         $bgPairs = array(
00527             # [ - _ . ] <-> [ left right ltr rtl ]
00528             'foo.jpg' => 'foo.jpg',
00529             'left.jpg' => 'right.jpg',
00530             'ltr.jpg' => 'rtl.jpg',
00531 
00532             'foo-left.png' => 'foo-right.png',
00533             'foo_left.png' => 'foo_right.png',
00534             'foo.left.png' => 'foo.right.png',
00535 
00536             'foo-ltr.png' => 'foo-rtl.png',
00537             'foo_ltr.png' => 'foo_rtl.png',
00538             'foo.ltr.png' => 'foo.rtl.png',
00539 
00540             'left-foo.png' => 'right-foo.png',
00541             'left_foo.png' => 'right_foo.png',
00542             'left.foo.png' => 'right.foo.png',
00543 
00544             'ltr-foo.png' => 'rtl-foo.png',
00545             'ltr_foo.png' => 'rtl_foo.png',
00546             'ltr.foo.png' => 'rtl.foo.png',
00547 
00548             'foo-ltr-left.gif' => 'foo-rtl-right.gif',
00549             'foo_ltr_left.gif' => 'foo_rtl_right.gif',
00550             'foo.ltr.left.gif' => 'foo.rtl.right.gif',
00551             'foo-ltr_left.gif' => 'foo-rtl_right.gif',
00552             'foo_ltr.left.gif' => 'foo_rtl.right.gif',
00553         );
00554         $provider = array();
00555         foreach ( $bgPairs as $left => $right ) {
00556             # By default '-rtl' and '-left' etc. are not touched,
00557             # Only when the appropiate parameter is set.
00558             $provider[] = array(
00559                 ".foo { background: url(images/$left); }",
00560                 ".foo { background: url(images/$left); }"
00561             );
00562             $provider[] = array(
00563                 ".foo { background: url(images/$right); }",
00564                 ".foo { background: url(images/$right); }"
00565             );
00566             $provider[] = array(
00567                 ".foo { background: url(images/$left); }",
00568                 ".foo { background: url(images/$right); }",
00569                 array(
00570                     'swapLtrRtlInURL' => true,
00571                     'swapLeftRightInURL' => true,
00572                 )
00573             );
00574             $provider[] = array(
00575                 ".foo { background: url(images/$right); }",
00576                 ".foo { background: url(images/$left); }",
00577                 array(
00578                     'swapLtrRtlInURL' => true,
00579                     'swapLeftRightInURL' => true,
00580                 )
00581             );
00582         }
00583 
00584         return $provider;
00585     }
00586 
00591     public static function provideTransformBrokenCases() {
00592         return array(
00593             // Guard against selectors that look flippable
00594             array(
00595                 # <foo-left-x attr="x">
00596                 'foo-left-x[attr="x"] { width: 0; }',
00597                 'foo-left-x[attr="x"] { width: 0; }'
00598             ),
00599             array(
00600                 # <div class="foo" data-left="x">
00601                 '.foo[data-left="x"] { width: 0; }',
00602                 '.foo[data-left="x"] { width: 0; }'
00603             ),
00604         );
00605     }
00606 }