MediaWiki
REL1_22
|
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 }