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