MediaWiki  REL1_20
HtmlTest.php
Go to the documentation of this file.
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>&#160;' .
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>&#160;' .
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 }