MediaWiki
REL1_20
|
00001 <?php 00004 class HtmlTest extends MediaWikiTestCase { 00005 private static $oldLang; 00006 private static $oldContLang; 00007 private static $oldLanguageCode; 00008 private static $oldNamespaces; 00009 private static $oldHTML5; 00010 00011 public function setUp() { 00012 global $wgLang, $wgContLang, $wgLanguageCode, $wgHTML5; 00013 00014 // Save globals 00015 self::$oldLang = $wgLang; 00016 self::$oldContLang = $wgContLang; 00017 self::$oldNamespaces = $wgContLang->getNamespaces(); 00018 self::$oldLanguageCode = $wgLanguageCode; 00019 self::$oldHTML5 = $wgHTML5; 00020 00021 $wgLanguageCode = 'en'; 00022 $wgContLang = $wgLang = Language::factory( $wgLanguageCode ); 00023 00024 // Hardcode namespaces during test runs, 00025 // so that html output based on existing namespaces 00026 // can be properly evaluated. 00027 $wgContLang->setNamespaces( array( 00028 -2 => 'Media', 00029 -1 => 'Special', 00030 0 => '', 00031 1 => 'Talk', 00032 2 => 'User', 00033 3 => 'User_talk', 00034 4 => 'MyWiki', 00035 5 => 'MyWiki_Talk', 00036 6 => 'File', 00037 7 => 'File_talk', 00038 8 => 'MediaWiki', 00039 9 => 'MediaWiki_talk', 00040 10 => 'Template', 00041 11 => 'Template_talk', 00042 14 => 'Category', 00043 15 => 'Category_talk', 00044 100 => 'Custom', 00045 101 => 'Custom_talk', 00046 ) ); 00047 } 00048 00049 public function tearDown() { 00050 global $wgLang, $wgContLang, $wgLanguageCode, $wgHTML5; 00051 00052 // Restore globals 00053 $wgContLang->setNamespaces( self::$oldNamespaces ); 00054 $wgLang = self::$oldLang; 00055 $wgContLang = self::$oldContLang; 00056 $wgLanguageCode = self::$oldLanguageCode; 00057 $wgHTML5 = self::$oldHTML5; 00058 } 00059 00065 public function enableHTML5() { 00066 global $wgHTML5; 00067 $wgHTML5 = true; 00068 } 00074 public function disableHTML5() { 00075 global $wgHTML5; 00076 $wgHTML5 = false; 00077 } 00078 00079 public function testExpandAttributesSkipsNullAndFalse() { 00080 00081 ### EMPTY ######## 00082 $this->AssertEmpty( 00083 Html::expandAttributes( array( 'foo' => null ) ), 00084 'skip keys with null value' 00085 ); 00086 $this->AssertEmpty( 00087 Html::expandAttributes( array( 'foo' => false ) ), 00088 'skip keys with false value' 00089 ); 00090 $this->AssertNotEmpty( 00091 Html::expandAttributes( array( 'foo' => '' ) ), 00092 'keep keys with an empty string' 00093 ); 00094 } 00095 00096 public function testExpandAttributesForBooleans() { 00097 global $wgHtml5; 00098 $this->AssertEquals( 00099 '', 00100 Html::expandAttributes( array( 'selected' => false ) ), 00101 'Boolean attributes do not generates output when value is false' 00102 ); 00103 $this->AssertEquals( 00104 '', 00105 Html::expandAttributes( array( 'selected' => null ) ), 00106 'Boolean attributes do not generates output when value is null' 00107 ); 00108 00109 $this->AssertEquals( 00110 $wgHtml5 ? ' selected=""' : ' selected="selected"', 00111 Html::expandAttributes( array( 'selected' => true ) ), 00112 'Boolean attributes skip value output' 00113 ); 00114 $this->AssertEquals( 00115 $wgHtml5 ? ' selected=""' : ' selected="selected"', 00116 Html::expandAttributes( array( 'selected' ) ), 00117 'Boolean attributes (ex: selected) do not need a value' 00118 ); 00119 } 00120 00125 public function testExpandAttributesVariousExpansions() { 00126 ### NOT EMPTY #### 00127 $this->AssertEquals( 00128 ' empty_string=""', 00129 Html::expandAttributes( array( 'empty_string' => '' ) ), 00130 'Value with an empty string' 00131 ); 00132 $this->AssertEquals( 00133 ' key="value"', 00134 Html::expandAttributes( array( 'key' => 'value' ) ), 00135 'Value is a string' 00136 ); 00137 $this->AssertEquals( 00138 ' one="1"', 00139 Html::expandAttributes( array( 'one' => 1 ) ), 00140 'Value is a numeric one' 00141 ); 00142 $this->AssertEquals( 00143 ' zero="0"', 00144 Html::expandAttributes( array( 'zero' => 0 ) ), 00145 'Value is a numeric zero' 00146 ); 00147 } 00148 00154 public function testExpandAttributesListValueAttributes() { 00155 ### STRING VALUES 00156 $this->AssertEquals( 00157 ' class="redundant spaces here"', 00158 Html::expandAttributes( array( 'class' => ' redundant spaces here ' ) ), 00159 'Normalization should strip redundant spaces' 00160 ); 00161 $this->AssertEquals( 00162 ' class="foo bar"', 00163 Html::expandAttributes( array( 'class' => 'foo bar foo bar bar' ) ), 00164 'Normalization should remove duplicates in string-lists' 00165 ); 00166 ### "EMPTY" ARRAY VALUES 00167 $this->AssertEquals( 00168 ' class=""', 00169 Html::expandAttributes( array( 'class' => array() ) ), 00170 'Value with an empty array' 00171 ); 00172 $this->AssertEquals( 00173 ' class=""', 00174 Html::expandAttributes( array( 'class' => array( null, '', ' ', ' ' ) ) ), 00175 'Array with null, empty string and spaces' 00176 ); 00177 ### NON-EMPTY ARRAY VALUES 00178 $this->AssertEquals( 00179 ' class="foo bar"', 00180 Html::expandAttributes( array( 'class' => array( 00181 'foo', 00182 'bar', 00183 'foo', 00184 'bar', 00185 'bar', 00186 ) ) ), 00187 'Normalization should remove duplicates in the array' 00188 ); 00189 $this->AssertEquals( 00190 ' class="foo bar"', 00191 Html::expandAttributes( array( 'class' => array( 00192 'foo bar', 00193 'bar foo', 00194 'foo', 00195 'bar bar', 00196 ) ) ), 00197 'Normalization should remove duplicates in string-lists in the array' 00198 ); 00199 } 00200 00205 function testExpandAttributesSpaceSeparatedAttributesWithBoolean() { 00206 $this->assertEquals( 00207 ' class="booltrue one"', 00208 Html::expandAttributes( array( 'class' => array( 00209 'booltrue' => true, 00210 'one' => 1, 00211 00212 # Method use isset() internally, make sure we do discard 00213 # attributes values which have been assigned well known values 00214 'emptystring' => '', 00215 'boolfalse' => false, 00216 'zero' => 0, 00217 'null' => null, 00218 ))) 00219 ); 00220 } 00221 00229 function testValueIsAuthoritativeInSpaceSeparatedAttributesArrays() { 00230 $this->assertEquals( 00231 ' class=""', 00232 Html::expandAttributes( array( 'class' => array( 00233 'GREEN', 00234 'GREEN' => false, 00235 'GREEN', 00236 ))) 00237 ); 00238 } 00239 00240 function testNamespaceSelector() { 00241 $this->assertEquals( 00242 '<select>' . "\n" . 00243 '<option value="0">(Main)</option>' . "\n" . 00244 '<option value="1">Talk</option>' . "\n" . 00245 '<option value="2">User</option>' . "\n" . 00246 '<option value="3">User talk</option>' . "\n" . 00247 '<option value="4">MyWiki</option>' . "\n" . 00248 '<option value="5">MyWiki Talk</option>' . "\n" . 00249 '<option value="6">File</option>' . "\n" . 00250 '<option value="7">File talk</option>' . "\n" . 00251 '<option value="8">MediaWiki</option>' . "\n" . 00252 '<option value="9">MediaWiki talk</option>' . "\n" . 00253 '<option value="10">Template</option>' . "\n" . 00254 '<option value="11">Template talk</option>' . "\n" . 00255 '<option value="14">Category</option>' . "\n" . 00256 '<option value="15">Category talk</option>' . "\n" . 00257 '<option value="100">Custom</option>' . "\n" . 00258 '<option value="101">Custom talk</option>' . "\n" . 00259 '</select>', 00260 Html::namespaceSelector(), 00261 'Basic namespace selector without custom options' 00262 ); 00263 00264 $this->assertEquals( 00265 '<label for="mw-test-namespace">Select a namespace:</label> ' . 00266 '<select id="mw-test-namespace" name="wpNamespace">' . "\n" . 00267 '<option value="all">all</option>' . "\n" . 00268 '<option value="0">(Main)</option>' . "\n" . 00269 '<option value="1">Talk</option>' . "\n" . 00270 '<option value="2" selected="">User</option>' . "\n" . 00271 '<option value="3">User talk</option>' . "\n" . 00272 '<option value="4">MyWiki</option>' . "\n" . 00273 '<option value="5">MyWiki Talk</option>' . "\n" . 00274 '<option value="6">File</option>' . "\n" . 00275 '<option value="7">File talk</option>' . "\n" . 00276 '<option value="8">MediaWiki</option>' . "\n" . 00277 '<option value="9">MediaWiki talk</option>' . "\n" . 00278 '<option value="10">Template</option>' . "\n" . 00279 '<option value="11">Template talk</option>' . "\n" . 00280 '<option value="14">Category</option>' . "\n" . 00281 '<option value="15">Category talk</option>' . "\n" . 00282 '<option value="100">Custom</option>' . "\n" . 00283 '<option value="101">Custom talk</option>' . "\n" . 00284 '</select>', 00285 Html::namespaceSelector( 00286 array( 'selected' => '2', 'all' => 'all', 'label' => 'Select a namespace:' ), 00287 array( 'name' => 'wpNamespace', 'id' => 'mw-test-namespace' ) 00288 ), 00289 'Basic namespace selector with custom values' 00290 ); 00291 00292 $this->assertEquals( 00293 '<label>Select a namespace:</label> ' . 00294 '<select>' . "\n" . 00295 '<option value="0">(Main)</option>' . "\n" . 00296 '<option value="1">Talk</option>' . "\n" . 00297 '<option value="2">User</option>' . "\n" . 00298 '<option value="3">User talk</option>' . "\n" . 00299 '<option value="4">MyWiki</option>' . "\n" . 00300 '<option value="5">MyWiki Talk</option>' . "\n" . 00301 '<option value="6">File</option>' . "\n" . 00302 '<option value="7">File talk</option>' . "\n" . 00303 '<option value="8">MediaWiki</option>' . "\n" . 00304 '<option value="9">MediaWiki talk</option>' . "\n" . 00305 '<option value="10">Template</option>' . "\n" . 00306 '<option value="11">Template talk</option>' . "\n" . 00307 '<option value="14">Category</option>' . "\n" . 00308 '<option value="15">Category talk</option>' . "\n" . 00309 '<option value="100">Custom</option>' . "\n" . 00310 '<option value="101">Custom talk</option>' . "\n" . 00311 '</select>', 00312 Html::namespaceSelector( 00313 array( 'label' => 'Select a namespace:' ) 00314 ), 00315 'Basic namespace selector with a custom label but no id attribtue for the <select>' 00316 ); 00317 } 00318 00319 function testCanFilterOutNamespaces() { 00320 $this->assertEquals( 00321 '<select>' . "\n" . 00322 '<option value="2">User</option>' . "\n" . 00323 '<option value="4">MyWiki</option>' . "\n" . 00324 '<option value="5">MyWiki Talk</option>' . "\n" . 00325 '<option value="6">File</option>' . "\n" . 00326 '<option value="7">File talk</option>' . "\n" . 00327 '<option value="8">MediaWiki</option>' . "\n" . 00328 '<option value="9">MediaWiki talk</option>' . "\n" . 00329 '<option value="10">Template</option>' . "\n" . 00330 '<option value="11">Template talk</option>' . "\n" . 00331 '<option value="14">Category</option>' . "\n" . 00332 '<option value="15">Category talk</option>' . "\n" . 00333 '</select>', 00334 Html::namespaceSelector( 00335 array( 'exclude' => array( 0, 1, 3, 100, 101 ) ) 00336 ), 00337 'Namespace selector namespace filtering.' 00338 ); 00339 } 00340 00341 function testCanDisableANamespaces() { 00342 $this->assertEquals( 00343 '<select>' . "\n" . 00344 '<option disabled="" value="0">(Main)</option>' . "\n" . 00345 '<option disabled="" value="1">Talk</option>' . "\n" . 00346 '<option disabled="" value="2">User</option>' . "\n" . 00347 '<option disabled="" value="3">User talk</option>' . "\n" . 00348 '<option disabled="" value="4">MyWiki</option>' . "\n" . 00349 '<option value="5">MyWiki Talk</option>' . "\n" . 00350 '<option value="6">File</option>' . "\n" . 00351 '<option value="7">File talk</option>' . "\n" . 00352 '<option value="8">MediaWiki</option>' . "\n" . 00353 '<option value="9">MediaWiki talk</option>' . "\n" . 00354 '<option value="10">Template</option>' . "\n" . 00355 '<option value="11">Template talk</option>' . "\n" . 00356 '<option value="14">Category</option>' . "\n" . 00357 '<option value="15">Category talk</option>' . "\n" . 00358 '<option value="100">Custom</option>' . "\n" . 00359 '<option value="101">Custom talk</option>' . "\n" . 00360 '</select>', 00361 Html::namespaceSelector( array( 00362 'disable' => array( 0, 1, 2, 3, 4 ) 00363 ) ), 00364 'Namespace selector namespace disabling' 00365 ); 00366 } 00367 00371 function testHtmlElementAcceptsNewHtml5TypesInHtml5Mode( $HTML5InputType ) { 00372 $this->enableHTML5(); 00373 $this->assertEquals( 00374 '<input type="' . $HTML5InputType . '" />', 00375 HTML::element( 'input', array( 'type' => $HTML5InputType ) ), 00376 'In HTML5, HTML::element() should accept type="' . $HTML5InputType . '"' 00377 ); 00378 } 00379 00384 function providesHtml5InputTypes() { 00385 $types = array( 00386 'datetime', 00387 'datetime-local', 00388 'date', 00389 'month', 00390 'time', 00391 'week', 00392 'number', 00393 'range', 00394 'email', 00395 'url', 00396 'search', 00397 'tel', 00398 'color', 00399 ); 00400 $cases = array(); 00401 foreach( $types as $type ) { 00402 $cases[] = array( $type ); 00403 } 00404 return $cases; 00405 } 00406 00412 function testDropDefaults( $expected, $element, $message = '' ) { 00413 $this->enableHTML5(); 00414 $this->assertEquals( $expected, $element, $message ); 00415 } 00416 00417 function provideElementsWithAttributesHavingDefaultValues() { 00418 # Use cases in a concise format: 00419 # <expected>, <element name>, <array of attributes> [, <message>] 00420 # Will be mapped to Html::element() 00421 $cases = array(); 00422 00423 ### Generic cases, match $attribDefault static array 00424 $cases[] = array( '<area />', 00425 'area', array( 'shape' => 'rect' ) 00426 ); 00427 00428 $cases[] = array( '<button></button>', 00429 'button', array( 'formaction' => 'GET' ) 00430 ); 00431 $cases[] = array( '<button></button>', 00432 'button', array( 'formenctype' => 'application/x-www-form-urlencoded' ) 00433 ); 00434 $cases[] = array( '<button></button>', 00435 'button', array( 'type' => 'submit' ) 00436 ); 00437 00438 $cases[] = array( '<canvas></canvas>', 00439 'canvas', array( 'height' => '150' ) 00440 ); 00441 $cases[] = array( '<canvas></canvas>', 00442 'canvas', array( 'width' => '300' ) 00443 ); 00444 # Also check with numeric values 00445 $cases[] = array( '<canvas></canvas>', 00446 'canvas', array( 'height' => 150 ) 00447 ); 00448 $cases[] = array( '<canvas></canvas>', 00449 'canvas', array( 'width' => 300 ) 00450 ); 00451 00452 $cases[] = array( '<command />', 00453 'command', array( 'type' => 'command' ) 00454 ); 00455 00456 $cases[] = array( '<form></form>', 00457 'form', array( 'action' => 'GET' ) 00458 ); 00459 $cases[] = array( '<form></form>', 00460 'form', array( 'autocomplete' => 'on' ) 00461 ); 00462 $cases[] = array( '<form></form>', 00463 'form', array( 'enctype' => 'application/x-www-form-urlencoded' ) 00464 ); 00465 00466 $cases[] = array( '<input />', 00467 'input', array( 'formaction' => 'GET' ) 00468 ); 00469 $cases[] = array( '<input />', 00470 'input', array( 'type' => 'text' ) 00471 ); 00472 00473 $cases[] = array( '<keygen />', 00474 'keygen', array( 'keytype' => 'rsa' ) 00475 ); 00476 00477 $cases[] = array( '<link />', 00478 'link', array( 'media' => 'all' ) 00479 ); 00480 00481 $cases[] = array( '<menu></menu>', 00482 'menu', array( 'type' => 'list' ) 00483 ); 00484 00485 $cases[] = array( '<script></script>', 00486 'script', array( 'type' => 'text/javascript' ) 00487 ); 00488 00489 $cases[] = array( '<style></style>', 00490 'style', array( 'media' => 'all' ) 00491 ); 00492 $cases[] = array( '<style></style>', 00493 'style', array( 'type' => 'text/css' ) 00494 ); 00495 00496 $cases[] = array( '<textarea></textarea>', 00497 'textarea', array( 'wrap' => 'soft' ) 00498 ); 00499 00500 ### SPECIFIC CASES 00501 00502 # <link type="text/css" /> 00503 $cases[] = array( '<link />', 00504 'link', array( 'type' => 'text/css' ) 00505 ); 00506 00507 # <input /> specific handling 00508 $cases[] = array( '<input type="checkbox" />', 00509 'input', array( 'type' => 'checkbox', 'value' => 'on' ), 00510 'Default value "on" is stripped of checkboxes', 00511 ); 00512 $cases[] = array( '<input type="radio" />', 00513 'input', array( 'type' => 'radio', 'value' => 'on' ), 00514 'Default value "on" is stripped of radio buttons', 00515 ); 00516 $cases[] = array( '<input type="submit" value="Submit" />', 00517 'input', array( 'type' => 'submit', 'value' => 'Submit' ), 00518 'Default value "Submit" is kept on submit buttons (for possible l10n issues)', 00519 ); 00520 $cases[] = array( '<input type="color" />', 00521 'input', array( 'type' => 'color', 'value' => '' ), 00522 ); 00523 $cases[] = array( '<input type="range" />', 00524 'input', array( 'type' => 'range', 'value' => '' ), 00525 ); 00526 00527 # <select /> specifc handling 00528 $cases[] = array( '<select multiple=""></select>', 00529 'select', array( 'size' => '4', 'multiple' => true ), 00530 ); 00531 # .. with numeric value 00532 $cases[] = array( '<select multiple=""></select>', 00533 'select', array( 'size' => 4, 'multiple' => true ), 00534 ); 00535 $cases[] = array( '<select></select>', 00536 'select', array( 'size' => '1', 'multiple' => false ), 00537 ); 00538 # .. with numeric value 00539 $cases[] = array( '<select></select>', 00540 'select', array( 'size' => 1, 'multiple' => false ), 00541 ); 00542 00543 # Passing an array as value 00544 $cases[] = array( '<a class="css-class-one css-class-two"></a>', 00545 'a', array( 'class' => array( 'css-class-one', 'css-class-two' ) ), 00546 "dropDefaults accepts values given as an array" 00547 ); 00548 00549 # FIXME: doDropDefault should remove defaults given in an array 00550 # Expected should be '<a></a>' 00551 $cases[] = array( '<a class=""></a>', 00552 'a', array( 'class' => array( '', '' ) ), 00553 "dropDefaults accepts values given as an array" 00554 ); 00555 00556 00557 # Craft the Html elements 00558 $ret = array(); 00559 foreach( $cases as $case ) { 00560 $ret[] = array( 00561 $case[0], 00562 Html::element( $case[1], $case[2] ) 00563 ); 00564 } 00565 return $ret; 00566 } 00567 00568 public function testFormValidationBlacklist() { 00569 $this->assertEmpty( 00570 Html::expandAttributes( array( 'min' => 1, 'max' => 100, 'pattern' => 'abc', 'required' => true, 'step' => 2 ) ), 00571 'Blacklist form validation attributes.' 00572 ); 00573 $this->assertEquals( 00574 ' step="any"', 00575 Html::expandAttributes( array( 'min' => 1, 'max' => 100, 'pattern' => 'abc', 'required' => true, 'step' => 'any' ) ), 00576 "Allow special case 'step=\"any\"'." 00577 ); 00578 } 00579 00580 }