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