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