MediaWiki  REL1_21
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                         'Attribute values are always quoted (wgWellFormedXml): Empty string'
00169                 );
00170                 $this->assertEquals(
00171                         ' key="value"',
00172                         Html::expandAttributes( array( 'key' => 'value' ) ),
00173                         'Attribute values are always quoted (wgWellFormedXml): Simple string'
00174                 );
00175                 $this->assertEquals(
00176                         ' one="1"',
00177                         Html::expandAttributes( array( 'one' => 1 ) ),
00178                         'Attribute values are always quoted (wgWellFormedXml): Number 1'
00179                 );
00180                 $this->assertEquals(
00181                         ' zero="0"',
00182                         Html::expandAttributes( array( 'zero' => 0 ) ),
00183                         'Attribute 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 type=submit></button>',
00465                         'button', array( 'formaction' => 'GET' )
00466                 );
00467                 $cases[] = array( '<button type=submit></button>',
00468                         'button', array( 'formenctype' => 'application/x-www-form-urlencoded' )
00469                 );
00470 
00471                 $cases[] = array( '<canvas></canvas>',
00472                         'canvas', array( 'height' => '150' )
00473                 );
00474                 $cases[] = array( '<canvas></canvas>',
00475                         'canvas', array( 'width' => '300' )
00476                 );
00477                 # Also check with numeric values
00478                 $cases[] = array( '<canvas></canvas>',
00479                         'canvas', array( 'height' => 150 )
00480                 );
00481                 $cases[] = array( '<canvas></canvas>',
00482                         'canvas', array( 'width' => 300 )
00483                 );
00484 
00485                 $cases[] = array( '<command>',
00486                         'command', array( 'type' => 'command' )
00487                 );
00488 
00489                 $cases[] = array( '<form></form>',
00490                         'form', array( 'action' => 'GET' )
00491                 );
00492                 $cases[] = array( '<form></form>',
00493                         'form', array( 'autocomplete' => 'on' )
00494                 );
00495                 $cases[] = array( '<form></form>',
00496                         'form', array( 'enctype' => 'application/x-www-form-urlencoded' )
00497                 );
00498 
00499                 $cases[] = array( '<input>',
00500                         'input', array( 'formaction' => 'GET' )
00501                 );
00502                 $cases[] = array( '<input>',
00503                         'input', array( 'type' => 'text' )
00504                 );
00505 
00506                 $cases[] = array( '<keygen>',
00507                         'keygen', array( 'keytype' => 'rsa' )
00508                 );
00509 
00510                 $cases[] = array( '<link>',
00511                         'link', array( 'media' => 'all' )
00512                 );
00513 
00514                 $cases[] = array( '<menu></menu>',
00515                         'menu', array( 'type' => 'list' )
00516                 );
00517 
00518                 $cases[] = array( '<script></script>',
00519                         'script', array( 'type' => 'text/javascript' )
00520                 );
00521 
00522                 $cases[] = array( '<style></style>',
00523                         'style', array( 'media' => 'all' )
00524                 );
00525                 $cases[] = array( '<style></style>',
00526                         'style', array( 'type' => 'text/css' )
00527                 );
00528 
00529                 $cases[] = array( '<textarea></textarea>',
00530                         'textarea', array( 'wrap' => 'soft' )
00531                 );
00532 
00533                 ### SPECIFIC CASES
00534 
00535                 # <link type="text/css">
00536                 $cases[] = array( '<link>',
00537                         'link', array( 'type' => 'text/css' )
00538                 );
00539 
00540                 # <input> specific handling
00541                 $cases[] = array( '<input type=checkbox>',
00542                         'input', array( 'type' => 'checkbox', 'value' => 'on' ),
00543                         'Default value "on" is stripped of checkboxes',
00544                 );
00545                 $cases[] = array( '<input type=radio>',
00546                         'input', array( 'type' => 'radio', 'value' => 'on' ),
00547                         'Default value "on" is stripped of radio buttons',
00548                 );
00549                 $cases[] = array( '<input type=submit value=Submit>',
00550                         'input', array( 'type' => 'submit', 'value' => 'Submit' ),
00551                         'Default value "Submit" is kept on submit buttons (for possible l10n issues)',
00552                 );
00553                 $cases[] = array( '<input type=color>',
00554                         'input', array( 'type' => 'color', 'value' => '' ),
00555                 );
00556                 $cases[] = array( '<input type=range>',
00557                         'input', array( 'type' => 'range', 'value' => '' ),
00558                 );
00559 
00560                 # <button> specific handling
00561                 # see remarks on http://msdn.microsoft.com/en-us/library/ie/ms535211%28v=vs.85%29.aspx
00562                 $cases[] = array( '<button type=submit></button>',
00563                         'button', array( 'type' => 'submit' ),
00564                         'According to standard the default type is "submit". Depending on compatibility mode IE might use "button", instead.',
00565                 );
00566 
00567                 # <select> specifc handling
00568                 $cases[] = array( '<select multiple></select>',
00569                         'select', array( 'size' => '4', 'multiple' => true ),
00570                 );
00571                 # .. with numeric value
00572                 $cases[] = array( '<select multiple></select>',
00573                         'select', array( 'size' => 4, 'multiple' => true ),
00574                 );
00575                 $cases[] = array( '<select></select>',
00576                         'select', array( 'size' => '1', 'multiple' => false ),
00577                 );
00578                 # .. with numeric value
00579                 $cases[] = array( '<select></select>',
00580                         'select', array( 'size' => 1, 'multiple' => false ),
00581                 );
00582 
00583                 # Passing an array as value
00584                 $cases[] = array( '<a class="css-class-one css-class-two"></a>',
00585                         'a', array( 'class' => array( 'css-class-one', 'css-class-two' ) ),
00586                         "dropDefaults accepts values given as an array"
00587                 );
00588 
00589                 # FIXME: doDropDefault should remove defaults given in an array
00590                 # Expected should be '<a></a>'
00591                 $cases[] = array( '<a class=""></a>',
00592                         'a', array( 'class' => array( '', '' ) ),
00593                         "dropDefaults accepts values given as an array"
00594                 );
00595 
00596                 # Craft the Html elements
00597                 $ret = array();
00598                 foreach ( $cases as $case ) {
00599                         $ret[] = array(
00600                                 $case[0],
00601                                 $case[1], $case[2],
00602                                 isset( $case[3] ) ? $case[3] : ''
00603                         );
00604                 }
00605                 return $ret;
00606         }
00607 
00608         public function testFormValidationBlacklist() {
00609                 $this->assertEmpty(
00610                         Html::expandAttributes( array( 'min' => 1, 'max' => 100, 'pattern' => 'abc', 'required' => true, 'step' => 2 ) ),
00611                         'Blacklist form validation attributes.'
00612                 );
00613                 $this->assertEquals(
00614                         ' step=any',
00615                         Html::expandAttributes( array( 'min' => 1, 'max' => 100, 'pattern' => 'abc', 'required' => true, 'step' => 'any' ) ),
00616                         'Allow special case "step=any".'
00617                 );
00618         }
00619 
00620 }