r89082 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r89081‎ | r89082 | r89083 >
Date:02:54, 29 May 2011
Author:krinkle
Status:resolved (Comments)
Tags:
Comment:
Adding or adjusting front-end function and variable documentation per our conventions.

Also did some trivial clean up, JSHint fixes and performance optimizations while at it. (per http://www.mediawiki.org/wiki/JavaScript_performance )
* Missing semicolons.
* [mediawiki.js Line 540] Unreachable 'registry' after 'throw'.
* [mediawiki.log.js] Checked for window.console but used console.
* Added callback option to mw.util.toggleToc (forwarded to jQuery.fn.slideUp/Down)
* Made mw.log argument more descriptive (logmsg instead of string)
* Using deepEqual(, true) instead of ok() when asserting true. (ok() is like casting a boolean (or a plain if() statement) to verify that something is not falsy)

Added missing test suites for:
* mw.util.toggleToc (follow-up r88732, Dummy check was introduced in r87898)

/me has been infected by Reedy and will be doing more of this tonight.
Modified paths:
  • /trunk/phase3/resources/mediawiki.util/mediawiki.util.jpegmeta.js (modified) (history)
  • /trunk/phase3/resources/mediawiki.util/mediawiki.util.js (modified) (history)
  • /trunk/phase3/resources/mediawiki/mediawiki.htmlform.js (modified) (history)
  • /trunk/phase3/resources/mediawiki/mediawiki.js (modified) (history)
  • /trunk/phase3/resources/mediawiki/mediawiki.log.js (modified) (history)
  • /trunk/phase3/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js (modified) (history)
  • /trunk/phase3/tests/qunit/suites/resources/jquery/jquery.colorUtil.js (modified) (history)
  • /trunk/phase3/tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js (modified) (history)
  • /trunk/phase3/tests/qunit/suites/resources/jquery/jquery.tabIndex.js (modified) (history)
  • /trunk/phase3/tests/qunit/suites/resources/mediawiki.util/mediawiki.util.js (modified) (history)
  • /trunk/phase3/tests/qunit/suites/resources/mediawiki/mediawiki.js (modified) (history)

Diff [purge]

Index: trunk/phase3/tests/qunit/suites/resources/jquery/jquery.colorUtil.js
@@ -1,11 +1,12 @@
22 module( 'jquery.colorUtil.js' );
33
44 test( '-- Initial check', function(){
5 -
 5+ expect(1);
66 ok( jQuery.colorUtil, 'jQuery.colorUtil defined' );
77 });
88
99 test( 'getRGB', function(){
 10+ expect(18);
1011
1112 equal( typeof jQuery.colorUtil.getRGB(), 'undefined', 'No arguments' );
1213 equal( typeof jQuery.colorUtil.getRGB( '' ), 'undefined', 'Empty string' );
@@ -32,6 +33,8 @@
3334 });
3435
3536 test( 'rgbToHsl', function(){
 37+ expect(4);
 38+
3639 var hsl = jQuery.colorUtil.rgbToHsl( 144, 238, 144 );
3740 var dualDecimals = function(a,b){
3841 return Math.round(a*100)/100;
@@ -45,6 +48,8 @@
4649 });
4750
4851 test( 'hslToRgb', function(){
 52+ expect(4);
 53+
4954 var rgb = jQuery.colorUtil.hslToRgb( 0.3, 0.7, 0.8 );
5055
5156 ok( rgb, 'Basic return evaluation' );
@@ -55,6 +60,7 @@
5661 });
5762
5863 test( 'getColorBrightness', function(){
 64+ expect(2);
5965
6066 var a = jQuery.colorUtil.getColorBrightness( 'red', +0.1 );
6167
Index: trunk/phase3/tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js
@@ -54,5 +54,5 @@
5555 ok( $j.compareObject( { foo: 1 }, { foo: 1 } ), 'compareObject: Two the same objects' );
5656 ok( !$j.compareObject( { bar: true }, { baz: false } ),
5757 'compareObject: Two different objects (false)' );
58 -
 58+
5959 });
\ No newline at end of file
Index: trunk/phase3/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js
@@ -1,7 +1,7 @@
22 module( 'jquery.autoEllipsis.js' );
33
44 test( '-- Initial check', function(){
5 -
 5+ expect(1);
66 ok( jQuery.fn.autoEllipsis, 'jQuery.fn.autoEllipsis defined' );
77 });
88
@@ -21,6 +21,8 @@
2222 }
2323
2424 test( 'Position right', function() {
 25+ expect(3);
 26+
2527 // We need this thing to be visible, so append it to the DOM
2628 var origText = 'This is a really long random string and there is no way it fits in 100 pixels.';
2729 var $wrapper = createWrappedDiv( origText );
@@ -33,7 +35,7 @@
3436
3537 // Check that the text fits by turning on word wrapping
3638 $span.css( 'whiteSpace', 'nowrap' );
37 - ok( $span.width() <= $span.parent().width(), "Text fits (span's width is no larger than its parent's width)" );
 39+ deepEqual( $span.width() <= $span.parent().width(), true, "Text fits (span's width is no larger than its parent's width)" );
3840
3941 // Add one character using scary black magic
4042 var spanText = $span.text();
@@ -42,7 +44,7 @@
4345
4446 // Put this text in the span and verify it doesn't fit
4547 $span.text( spanText );
46 - ok( $span.width() > $span.parent().width(), 'Fit is maximal (adding one character makes it not fit any more)' );
 48+ deepEqual( $span.width() > $span.parent().width(), true, 'Fit is maximal (adding one character makes it not fit any more)' );
4749
4850 // Clean up
4951 $wrapper.remove();
Index: trunk/phase3/tests/qunit/suites/resources/jquery/jquery.tabIndex.js
@@ -1,6 +1,7 @@
22 module( 'jquery.tabIndex.js' );
33
44 test( '-- Initial check', function(){
 5+ expect(2);
56
67 ok( $.fn.firstTabIndex, '$.fn.firstTabIndex defined' );
78 ok( $.fn.lastTabIndex, '$.fn.lastTabIndex defined' );
@@ -8,14 +9,15 @@
910 });
1011
1112 test( 'firstTabIndex', function(){
 13+ expect(2);
1214
1315 var testEnvironment =
14 -'<form>\
15 - <input tabindex="7" />\
16 - <input tabindex="9" />\
17 - <textarea tabindex="2">Foobar</textarea>\
18 - <textarea tabindex="5">Foobar</textarea>\
19 -</form>';
 16+'<form>' +
 17+ '<input tabindex="7" />' +
 18+ '<input tabindex="9" />' +
 19+ '<textarea tabindex="2">Foobar</textarea>' +
 20+ '<textarea tabindex="5">Foobar</textarea>' +
 21+'</form>';
2022 var $testA = $( '<div />' ).html( testEnvironment ).appendTo( 'body' );
2123
2224 deepEqual( $testA.firstTabIndex(), 2, 'First tabindex should be 2 within this context.' );
@@ -29,14 +31,15 @@
3032 });
3133
3234 test( 'lastTabIndex', function(){
 35+ expect(2);
3336
3437 var testEnvironment =
35 -'<form>\
36 - <input tabindex="7" />\
37 - <input tabindex="9" />\
38 - <textarea tabindex="2">Foobar</textarea>\
39 - <textarea tabindex="5">Foobar</textarea>\
40 -</form>';
 38+'<form>' +
 39+ '<input tabindex="7" />' +
 40+ '<input tabindex="9" />' +
 41+ '<textarea tabindex="2">Foobar</textarea>' +
 42+ '<textarea tabindex="5">Foobar</textarea>' +
 43+'</form>';
4144 var $testA = $( '<div />' ).html( testEnvironment ).appendTo( 'body' );
4245
4346 deepEqual( $testA.lastTabIndex(), 9, 'Last tabindex should be 9 within this context.' );
Index: trunk/phase3/tests/qunit/suites/resources/mediawiki.util/mediawiki.util.js
@@ -34,6 +34,7 @@
3535 });
3636
3737 test( 'wikiScript', function(){
 38+ expect(2);
3839
3940 mw.config.set({
4041 'wgScript': '/w/index.php',
@@ -41,29 +42,59 @@
4243 'wgScriptExtension': '.php'
4344 });
4445
45 - equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript.' );
 46+ equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript' );
 47+ deepEqual( mw.util.wikiScript( 'api' ), '/w/api.php' );
4648
4749 });
4850
4951 test( 'addCSS', function(){
 52+ expect(3);
5053
51 - var a = mw.util.addCSS( '#bodyContent { visibility: hidden; }' );
 54+ window.a = mw.util.addCSS( '#bodyContent { visibility: hidden; }' );
5255 ok( a, 'function works' );
5356 deepEqual( a.disabled, false, 'property "disabled" is available and set to false' );
5457
5558 var $b = $('#bodyContent');
56 - equal( $b.css('visibility'), 'hidden', 'Added style properties are in effect.' );
 59+ equal( $b.css('visibility'), 'hidden', 'Added style properties are in effect' );
5760
58 -
5961 });
6062
61 -/**
62 - * @fixme this seems to only test for the existence of the function, and does not confirm that it works
63 - */
6463 test( 'toggleToc', function(){
 64+ expect(3);
6565
66 - ok( mw.util.toggleToc );
 66+ deepEqual( mw.util.toggleToc(), null, 'Return null if there is no table of contents on the page.' );
6767
 68+ var tocHtml =
 69+ '<table id="toc" class="toc"><tr><td>' +
 70+ '<div id="toctitle">' +
 71+ '<h2>Contents</h2>' +
 72+ '<span class="toctoggle">&nbsp;[<a href="#" class="internal" id="togglelink">Hide</a>&nbsp;]</span>' +
 73+ '</div>' +
 74+ '<ul><li></li></ul>' +
 75+ '</td></tr></table>';
 76+ var $toc = $(tocHtml).appendTo( 'body' );
 77+ var $toggleLink = $( '#togglelink' );
 78+
 79+ // Toggle animation is asynchronous
 80+ // QUnit should not finish this test() untill they are all done
 81+ stop();
 82+
 83+ var actionC = function(){
 84+ start();
 85+
 86+ // Clean up
 87+ $toc.remove();
 88+ };
 89+ var actionB = function(){
 90+ deepEqual( mw.util.toggleToc( $toggleLink, actionC ), true, 'Return boolean true if the TOC is now visible.' );
 91+ };
 92+ var actionA = function(){
 93+ deepEqual( mw.util.toggleToc( $toggleLink, actionB ), false, 'Return boolean false if the TOC is now hidden.' );
 94+ };
 95+
 96+ actionA();
 97+
 98+
6899 });
69100
70101 test( 'getParamValue', function(){
@@ -126,21 +157,23 @@
127158 });
128159
129160 test( 'validateEmail', function(){
 161+ expect(6);
130162
131 - deepEqual( mw.util.validateEmail( "" ), null, 'Empty string should return null' );
132 - deepEqual( mw.util.validateEmail( "user@localhost" ), true );
 163+ deepEqual( mw.util.validateEmail( "" ), null, 'Should return null for empty string ' );
 164+ deepEqual( mw.util.validateEmail( "user@localhost" ), true, 'Return true for a valid e-mail address' );
133165
134166 // testEmailWithCommasAreInvalids
135 - deepEqual( mw.util.validateEmail( "user,foo@example.org" ), false, 'Comma' );
136 - deepEqual( mw.util.validateEmail( "userfoo@ex,ample.org" ), false, 'Comma' );
 167+ deepEqual( mw.util.validateEmail( "user,foo@example.org" ), false, 'Emails with commas are invalid' );
 168+ deepEqual( mw.util.validateEmail( "userfoo@ex,ample.org" ), false, 'Emails with commas are invalid' );
137169
138170 // testEmailWithHyphens
139 - deepEqual( mw.util.validateEmail( "user-foo@example.org" ), true, 'Hyphen' );
140 - deepEqual( mw.util.validateEmail( "userfoo@ex-ample.org" ), true, 'Hyphen' );
 171+ deepEqual( mw.util.validateEmail( "user-foo@example.org" ), true, 'Emails may contain a hyphen' );
 172+ deepEqual( mw.util.validateEmail( "userfoo@ex-ample.org" ), true, 'Emails may contain a hyphen' );
141173
142174 });
143175
144176 test( 'isIPv6Address', function(){
 177+ expect(6);
145178
146179 // Based on IPTest.php > IPv6
147180 deepEqual( mw.util.isIPv6Address( "" ), false, 'Empty string is not an IP' );
@@ -153,6 +186,7 @@
154187 });
155188
156189 test( 'isIPv4Address', function(){
 190+ expect(3);
157191
158192 // Based on IPTest.php > IPv4
159193 deepEqual( mw.util.isIPv4Address( "" ), false, 'Empty string is not an IP' );
Index: trunk/phase3/tests/qunit/suites/resources/mediawiki/mediawiki.js
@@ -13,6 +13,7 @@
1414 });
1515
1616 test( 'mw.Map', function(){
 17+ expect(20);
1718
1819 ok( mw.Map, 'mw.Map defined' );
1920 ok( mw.Map.prototype.get, 'get prototype defined' );
@@ -25,53 +26,57 @@
2627 var arry = [];
2728 var nummy = 7;
2829
29 - deepEqual( conf.get( 'inexistantKey' ), null, 'Map.get() returns null if selection was a string and the key was not found.' );
30 - deepEqual( conf.set( 'myKey', 'myValue' ), true, 'Map.set() returns boolean true if a value was set for a valid key string.' );
31 - deepEqual( conf.set( funky, 'Funky' ), false, 'Map.set() returns boolean false if key was invalid (Function).' );
32 - deepEqual( conf.set( arry, 'Arry' ), false, 'Map.set() returns boolean false if key was invalid (Array).' );
33 - deepEqual( conf.set( nummy, 'Nummy' ), false, 'Map.set() returns boolean false if key was invalid (Number).' );
34 - equal( conf.get( 'myKey' ), 'myValue', 'Map.get() returns a single value value correctly.' );
 30+ deepEqual( conf.get( 'inexistantKey' ), null, 'Map.get() returns null if selection was a string and the key was not found' );
 31+ deepEqual( conf.set( 'myKey', 'myValue' ), true, 'Map.set() returns boolean true if a value was set for a valid key string' );
 32+ deepEqual( conf.set( funky, 'Funky' ), false, 'Map.set() returns boolean false if key was invalid (Function)' );
 33+ deepEqual( conf.set( arry, 'Arry' ), false, 'Map.set() returns boolean false if key was invalid (Array)' );
 34+ deepEqual( conf.set( nummy, 'Nummy' ), false, 'Map.set() returns boolean false if key was invalid (Number)' );
 35+ equal( conf.get( 'myKey' ), 'myValue', 'Map.get() returns a single value value correctly' );
3536
3637 var someValues = {
3738 'foo': 'bar',
3839 'lorem': 'ipsum',
3940 'MediaWiki': true
4041 };
41 - deepEqual( conf.set( someValues ), true, 'Map.set() returns boolean true if multiple values were set by passing an object.' );
 42+ deepEqual( conf.set( someValues ), true, 'Map.set() returns boolean true if multiple values were set by passing an object' );
4243 deepEqual( conf.get( ['foo', 'lorem'] ), {
4344 'foo': 'bar',
4445 'lorem': 'ipsum'
45 - }, 'Map.get() returns multiple values correctly as an object.' );
 46+ }, 'Map.get() returns multiple values correctly as an object' );
4647
4748 deepEqual( conf.get( ['foo', 'notExist'] ), {
4849 'foo': 'bar',
4950 'notExist': null
5051 }, 'Map.get() return includes keys that were not found as null values' );
5152
52 - deepEqual( conf.exists( 'foo' ), true, 'Map.exists() returns boolean true if a key exists.' );
53 - deepEqual( conf.exists( 'notExist' ), false, 'Map.exists() returns boolean false if a key does not exists.' );
54 - deepEqual( conf.get() === conf.values, true, 'Map.get() returns the entire values object by reference (if called without arguments).' );
 53+ deepEqual( conf.exists( 'foo' ), true, 'Map.exists() returns boolean true if a key exists' );
 54+ deepEqual( conf.exists( 'notExist' ), false, 'Map.exists() returns boolean false if a key does not exists' );
 55+ deepEqual( conf.get() === conf.values, true, 'Map.get() returns the entire values object by reference (if called without arguments)' );
5556
5657 conf.set( 'globalMapChecker', 'Hi' );
5758
58 - deepEqual( 'globalMapChecker' in window, false, 'new mw.Map() did not store its values in the global window object by default.' );
59 - ok( !window.globalMapChecker, 'new mw.Map() did not store its values in the global window object by default.' );
 59+ deepEqual( 'globalMapChecker' in window, false, 'new mw.Map() did not store its values in the global window object by default' );
 60+ ok( !window.globalMapChecker, 'new mw.Map() did not store its values in the global window object by default' );
6061
6162 var globalConf = new mw.Map( true );
6263 globalConf.set( 'anotherGlobalMapChecker', 'Hello' );
6364
64 - deepEqual( 'anotherGlobalMapChecker' in window, true, 'new mw.Map( true ) did store its values in the global window object.' )
65 - ok( window.anotherGlobalMapChecker, 'new mw.Map( true ) did store its values in the global window object.' )
 65+ deepEqual( 'anotherGlobalMapChecker' in window, true, 'new mw.Map( true ) did store its values in the global window object' );
 66+ ok( window.anotherGlobalMapChecker, 'new mw.Map( true ) did store its values in the global window object' );
6667
6768 // Clean up
6869 delete window.anotherGlobalMapChecker;
6970 });
7071
7172 test( 'mw.config', function(){
 73+ expect(1);
 74+
7275 deepEqual( mw.config instanceof mw.Map, true, 'mw.config instance of mw.Map' );
7376 });
7477
7578 test( 'mw.messages / mw.message / mw.msg', function(){
 79+ expect(18);
 80+
7681 ok( mw.message, 'mw.message defined' );
7782 ok( mw.msg, 'mw.msg defined' );
7883 ok( mw.messages, 'messages defined' );
@@ -80,37 +85,39 @@
8186
8287 var hello = mw.message( 'hello' );
8388
84 - equal( hello.format, 'parse', 'Message property "format" defaults to "parse".' );
 89+ equal( hello.format, 'parse', 'Message property "format" defaults to "parse"' );
8590 deepEqual( hello.map === mw.messages, true, 'Message property "map" defaults to the global instance in mw.messages' );
8691 equal( hello.key, 'hello', 'Message property "key" (currect key)' );
87 - deepEqual( hello.parameters, [], 'Message property "parameters" defaults to an empty array.' );
 92+ deepEqual( hello.parameters, [], 'Message property "parameters" defaults to an empty array' );
8893
8994 // Todo
90 - ok( hello.params, 'Message prototype "params".' );
 95+ ok( hello.params, 'Message prototype "params"' );
9196
9297 hello.format = 'plain';
93 - equal( hello.toString(), 'Hello <b>awesome</b> world', 'Message.toString() returns the message as a string with the current "format".' );
 98+ equal( hello.toString(), 'Hello <b>awesome</b> world', 'Message.toString() returns the message as a string with the current "format"' );
9499
95 - equal( hello.escaped(), 'Hello &lt;b&gt;awesome&lt;/b&gt; world', 'Message.escaped() returns the escaped message.' );
96 - equal( hello.format, 'escaped', 'Message.escaped() correctly updated the "format" property.' );
 100+ equal( hello.escaped(), 'Hello &lt;b&gt;awesome&lt;/b&gt; world', 'Message.escaped() returns the escaped message' );
 101+ equal( hello.format, 'escaped', 'Message.escaped() correctly updated the "format" property' );
97102
98 - hello.parse()
99 - equal( hello.format, 'parse', 'Message.parse() correctly updated the "format" property.' );
 103+ hello.parse();
 104+ equal( hello.format, 'parse', 'Message.parse() correctly updated the "format" property' );
100105
101106 hello.plain();
102 - equal( hello.format, 'plain', 'Message.plain() correctly updated the "format" property.' );
 107+ equal( hello.format, 'plain', 'Message.plain() correctly updated the "format" property' );
103108
104 - deepEqual( hello.exists(), true, 'Message.exists() returns true for existing messages.' );
 109+ deepEqual( hello.exists(), true, 'Message.exists() returns true for existing messages' );
105110
106111 var goodbye = mw.message( 'goodbye' );
107 - deepEqual( goodbye.exists(), false, 'Message.exists() returns false for inexisting messages.' );
 112+ deepEqual( goodbye.exists(), false, 'Message.exists() returns false for inexisting messages' );
108113
109 - equal( goodbye.toString(), '<goodbye>', 'Message.toString() returns <key> if key does not exist.' );
 114+ equal( goodbye.toString(), '<goodbye>', 'Message.toString() returns <key> if key does not exist' );
110115 });
111116
112117 test( 'mw.msg', function(){
 118+ expect(2);
 119+
113120 equal( mw.msg( 'hello' ), 'Hello <b>awesome</b> world', 'Gets message with default options (existing message)' );
114 - equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (inexisting message).' );
 121+ equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (inexisting message)' );
115122 });
116123
117124 test( 'mw.loader', function(){
Index: trunk/phase3/resources/mediawiki.util/mediawiki.util.jpegmeta.js
@@ -723,11 +723,11 @@
724724 };
725725
726726 /* JsJpegMeta ends here */
727 -
 727+
728728 mw.util = $.extend( mw.util || {}, {
729729 jpegmeta: function( fileReaderResult, fileName ) {
730730 return new JpegMeta.JpegFile( fileReaderResult, fileName );
731731 }
732732 } );
733 -
 733+
734734 } )( jQuery, mediaWiki );
\ No newline at end of file
Index: trunk/phase3/resources/mediawiki.util/mediawiki.util.js
@@ -6,6 +6,9 @@
77 mw.util = $.extend( mw.util || {}, {
88
99 /* Initialisation */
 10+ /**
 11+ * @var boolean Wether or not already initialised
 12+ */
1013 'initialised' : false,
1114 'init' : function() {
1215 if ( this.initialised === false ) {
@@ -93,9 +96,11 @@
9497 // Only add it if there is a TOC and there is no toggle added already
9598 if ( $tocContainer.size() && $tocTitle.size() && !$tocToggleLink.size() ) {
9699 var hideTocCookie = $.cookie( 'mw_hidetoc' );
97 - $tocToggleLink = $( '<a href="#" class="internal" id="togglelink">' ).text( mw.msg( 'hidetoc' ) ).click( function(e){
98 - e.preventDefault();
99 - mw.util.toggleToc( $(this) );
 100+ $tocToggleLink = $( '<a href="#" class="internal" id="togglelink">' )
 101+ .text( mw.msg( 'hidetoc' ) )
 102+ .click( function(e){
 103+ e.preventDefault();
 104+ mw.util.toggleToc( $(this) );
100105 } );
101106 $tocTitle.append( $tocToggleLink.wrap( '<span class="toctoggle">' ).parent().prepend( '&nbsp;[' ).append( ']&nbsp;' ) );
102107
@@ -116,7 +121,7 @@
117122 /**
118123 * Encode the string like PHP's rawurlencode
119124 *
120 - * @param str String to be encoded
 125+ * @param str string String to be encoded
121126 */
122127 'rawurlencode' : function( str ) {
123128 str = ( str + '' ).toString();
@@ -130,7 +135,7 @@
131136 * We want / and : to be included as literal characters in our title URLs
132137 * as they otherwise fatally break the title
133138 *
134 - * @param str String to be encoded
 139+ * @param str string String to be encoded
135140 */
136141 'wikiUrlencode' : function( str ) {
137142 return this.rawurlencode( str )
@@ -140,7 +145,7 @@
141146 /**
142147 * Get the link to a page name (relative to wgServer)
143148 *
144 - * @param str Page name to get the link for.
 149+ * @param str string Page name to get the link for.
145150 * @return string Location for a page with name of 'str' or boolean false on error.
146151 */
147152 'wikiGetlink' : function( str ) {
@@ -152,8 +157,8 @@
153158 * Get address to a script in the wiki root.
154159 * For index.php use mw.config.get( 'wgScript' )
155160 *
156 - * @param str Name of script (eg. 'api'), defaults to 'index'
157 - * @return str Address to script (eg. '/w/api.php' )
 161+ * @param str string Name of script (eg. 'api'), defaults to 'index'
 162+ * @return string Address to script (eg. '/w/api.php' )
158163 */
159164 'wikiScript' : function( str ) {
160165 return mw.config.get( 'wgScriptPath' ) + '/' + ( str || 'index' ) + mw.config.get( 'wgScriptExtension' );
@@ -162,8 +167,8 @@
163168 /**
164169 * Append a new style block to the head
165170 *
166 - * @param text String CSS to be appended
167 - * @return CSSStyleSheet object
 171+ * @param text string CSS to be appended
 172+ * @return CSSStyleSheet
168173 */
169174 'addCSS' : function( text ) {
170175 var s = document.createElement( 'style' );
@@ -174,24 +179,27 @@
175180 } else {
176181 s.appendChild( document.createTextNode( text + '' ) ); // Safari sometimes borks on null
177182 }
178 - document.getElementsByTagName("head")[0].appendChild( s );
 183+ document.getElementsByTagName('head')[0].appendChild( s );
179184 return s.sheet || s;
180185 },
181186
182187 /**
183188 * Hide/show the table of contents element
184189 *
185 - * @param $toggleLink jQuery object of the toggle link
186 - * @return String boolean visibility of the toc (true means it's visible)
 190+ * @param $toggleLink jQuery A jQuery object of the toggle link.
 191+ * @param callback function Function to be called after the toggle is
 192+ * completed (including the animation) (optional)
 193+ * @return mixed Boolean visibility of the toc (true if it's visible)
 194+ * or Null if there was no table of contents.
187195 */
188 - 'toggleToc' : function( $toggleLink ) {
 196+ 'toggleToc' : function( $toggleLink, callback ) {
189197 var $tocList = $( '#toc ul:first' );
190198
191199 // This function shouldn't be called if there's no TOC,
192200 // but just in case...
193201 if ( $tocList.size() ) {
194202 if ( $tocList.is( ':hidden' ) ) {
195 - $tocList.slideDown( 'fast' );
 203+ $tocList.slideDown( 'fast', callback );
196204 $toggleLink.text( mw.msg( 'hidetoc' ) );
197205 $.cookie( 'mw_hidetoc', null, {
198206 expires: 30,
@@ -199,7 +207,7 @@
200208 } );
201209 return true;
202210 } else {
203 - $tocList.slideUp( 'fast' );
 211+ $tocList.slideUp( 'fast', callback );
204212 $toggleLink.text( mw.msg( 'showtoc' ) );
205213 $.cookie( 'mw_hidetoc', '1', {
206214 expires: 30,
@@ -208,7 +216,7 @@
209217 return false;
210218 }
211219 } else {
212 - return false;
 220+ return null;
213221 }
214222 },
215223
@@ -216,8 +224,9 @@
217225 * Grab the URL parameter value for the given parameter.
218226 * Returns null if not found.
219227 *
220 - * @param param The parameter name
221 - * @param url URL to search through (optional)
 228+ * @param param string The parameter name.
 229+ * @param url string URL to search through (optional)
 230+ * @return mixed Parameter value or null.
222231 */
223232 'getParamValue' : function( param, url ) {
224233 url = url ? url : document.location.href;
@@ -230,12 +239,17 @@
231240 return null;
232241 },
233242
234 - // Access key prefix.
235 - // Will be re-defined based on browser/operating system detection in
236 - // mw.util.init().
 243+ /**
 244+ * @var string
 245+ * Access key prefix. Will be re-defined based on browser/operating system
 246+ * detection in mw.util.init().
 247+ */
237248 'tooltipAccessKeyPrefix' : 'alt-',
238249
239 - // Regex to match accesskey tooltips
 250+ /**
 251+ * @var RegExp
 252+ * Regex to match accesskey tooltips.
 253+ */
240254 'tooltipAccessKeyRegexp': /\[(ctrl-)?(alt-)?(shift-)?(esc-)?(.)\]$/,
241255
242256 /**
@@ -244,7 +258,7 @@
245259 * otherwise, all the nodes that will probably have accesskeys by
246260 * default are updated.
247261 *
248 - * @param nodeList jQuery object, or array of elements
 262+ * @param nodeList mixed A jQuery object, or array of elements to update.
249263 */
250264 'updateTooltipAccessKeys' : function( nodeList ) {
251265 var $nodes;
@@ -274,8 +288,11 @@
275289 } );
276290 },
277291
278 - // jQuery object that refers to the page-content element
279 - // Populated by init()
 292+ /*
 293+ * @var jQuery
 294+ * A jQuery object that refers to the page-content element
 295+ * Populated by init().
 296+ */
280297 '$content' : null,
281298
282299 /**
@@ -284,8 +301,8 @@
285302 * p-cactions (Content actions), p-personal (Personal tools),
286303 * p-navigation (Navigation), p-tb (Toolbox)
287304 *
288 - * The first three paramters are required, others are optionals. Though
289 - * providing an id and tooltip is recommended.
 305+ * The first three paramters are required, the others are optional and
 306+ * may be null. Though providing an id and tooltip is recommended.
290307 *
291308 * By default the new link will be added to the end of the list. To
292309 * add the link before a given existing item, pass the DOM node
@@ -297,21 +314,21 @@
298315 * 'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', '#t-print'
299316 * )
300317 *
301 - * @param portlet ID of the target portlet ( 'p-cactions' or 'p-personal' etc.)
302 - * @param href Link URL
303 - * @param text Link text
304 - * @param id ID of the new item, should be unique and preferably have
305 - * the appropriate prefix ( 'ca-', 'pt-', 'n-' or 't-' )
306 - * @param tooltip Text to show when hovering over the link, without accesskey suffix
307 - * @param accesskey Access key to activate this link (one character, try
308 - * to avoid conflicts. Use $( '[accesskey=x]' ).get() in the console to
309 - * see if 'x' is already used.
310 - * @param nextnode DOM node or jQuery-selector string of the item that the new
311 - * item should be added before, should be another item in the same
312 - * list, it will be ignored otherwise
 318+ * @param portlet string ID of the target portlet ( 'p-cactions' or 'p-personal' etc.)
 319+ * @param href string Link URL
 320+ * @param text string Link text
 321+ * @param id string ID of the new item, should be unique and preferably have
 322+ * the appropriate prefix ( 'ca-', 'pt-', 'n-' or 't-' )
 323+ * @param tooltip string Text to show when hovering over the link, without accesskey suffix
 324+ * @param accesskey string Access key to activate this link (one character, try
 325+ * to avoid conflicts. Use $( '[accesskey=x]' ).get() in the console to
 326+ * see if 'x' is already used.
 327+ * @param nextnode mixed DOM Node or jQuery-selector string of the item that the new
 328+ * item should be added before, should be another item in the same
 329+ * list, it will be ignored otherwise
313330 *
314 - * @return The DOM node of the new item (a LI element, or A element for
315 - * older skins) or null.
 331+ * @return mixed The DOM Node of the added item (a ListItem or Anchor element,
 332+ * depending on the skin) or null if no element was added to the document.
316333 */
317334 'addPortletLink' : function( portlet, href, text, id, tooltip, accesskey, nextnode ) {
318335
@@ -399,8 +416,8 @@
400417 } else {
401418 $ul.append( $item );
402419 }
403 -
404420
 421+
405422 return $item[0];
406423 }
407424 },
@@ -412,8 +429,8 @@
413430 *
414431 * @param message mixed The DOM-element or HTML-string to be put inside the message box.
415432 * @param className string Used in adding a class; should be different for each call
416 - * to allow CSS/JS to hide different boxes. null = no class used.
417 - * @return boolean True on success, false on failure
 433+ * to allow CSS/JS to hide different boxes. null = no class used.
 434+ * @return boolean True on success, false on failure.
418435 */
419436 'jsMessage' : function( message, className ) {
420437
@@ -457,7 +474,11 @@
458475 * according to HTML5 specification. Please note the specification
459476 * does not validate a domain with one character.
460477 *
461 - * FIXME: should be moved to or replaced by a JavaScript validation module.
 478+ * @todo FIXME: should be moved to or replaced by a JavaScript validation module.
 479+ *
 480+ * @param mailtxt string E-mail address to be validated.
 481+ * @return mixed Null if mailtxt was an empty string, otherwise true/false
 482+ * is determined by validation.
462483 */
463484 'validateEmail' : function( mailtxt ) {
464485 if( mailtxt === '' ) {
@@ -525,14 +546,27 @@
526547 );
527548 return (null !== mailtxt.match( HTML5_email_regexp ) );
528549 },
529 - // Note: borrows from IP::isIPv4
 550+
 551+ /**
 552+ * Note: borrows from IP::isIPv4
 553+ *
 554+ * @param address string
 555+ * @param allowBlock boolean
 556+ * @return boolean
 557+ */
530558 'isIPv4Address' : function( address, allowBlock ) {
531559 var block = allowBlock ? '(?:\\/(?:3[0-2]|[12]?\\d))?' : '';
532560 var RE_IP_BYTE = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|0?[0-9]?[0-9])';
533561 var RE_IP_ADD = '(?:' + RE_IP_BYTE + '\\.){3}' + RE_IP_BYTE;
534562 return address.search( new RegExp( '^' + RE_IP_ADD + block + '$' ) ) != -1;
535563 },
536 - // Note: borrows from IP::isIPv6
 564+ /**
 565+ * Note: borrows from IP::isIPv6
 566+ *
 567+ * @param address string
 568+ * @param allowBlock boolean
 569+ * @return boolean
 570+ */
537571 'isIPv6Address' : function( address, allowBlock ) {
538572 var block = allowBlock ? '(?:\\/(?:12[0-8]|1[01][0-9]|[1-9]?\\d))?' : '';
539573 var RE_IPV6_ADD =
Index: trunk/phase3/resources/mediawiki/mediawiki.htmlform.js
@@ -3,15 +3,27 @@
44 */
55 ( function( $ ) {
66
7 -// Fade or snap depending on argument
 7+/**
 8+ * jQuery plugin to fade or snap to visible state.
 9+ *
 10+ * @param boolean instantToggle (optional)
 11+ * @return jQuery
 12+ */
813 $.fn.goIn = function( instantToggle ) {
9 - if ( typeof instantToggle != 'undefined' && instantToggle === true ) {
 14+ if ( instantToggle !== undefined && instantToggle === true ) {
1015 return $(this).show();
1116 }
1217 return $(this).stop( true, true ).fadeIn();
1318 };
 19+
 20+/**
 21+ * jQuery plugin to fade or snap to hiding state.
 22+ *
 23+ * @param boolean instantToggle (optional)
 24+ * @return jQuery
 25+ */
1426 $.fn.goOut = function( instantToggle ) {
15 - if ( typeof instantToggle != 'undefined' && instantToggle === true ) {
 27+ if ( instantToggle !== undefined && instantToggle === true ) {
1628 return $(this).hide();
1729 }
1830 return $(this).stop( true, true ).fadeOut();
@@ -34,12 +46,12 @@
3547 // Document ready:
3648 $( function() {
3749
38 - // animate the SelectOrOther fields, to only show the text field when
39 - // 'other' is selected
 50+ // Animate the SelectOrOther fields, to only show the text field when
 51+ // 'other' is selected.
4052 $( '.mw-htmlform-select-or-other' ).liveAndTestAtStart( function( instant ) {
4153 var $other = $( '#' + $(this).attr( 'id' ) + '-other' );
4254 $other = $other.add( $other.siblings( 'br' ) );
43 - if ( $(this).val() == 'other' ) {
 55+ if ( $(this).val() === 'other' ) {
4456 $other.goIn( instant );
4557 } else {
4658 $other.goOut( instant );
@@ -49,4 +61,4 @@
5062 });
5163
5264
53 -})( jQuery );
\ No newline at end of file
 65+})( jQuery );
Index: trunk/phase3/resources/mediawiki/mediawiki.js
@@ -210,7 +210,7 @@
211211
212212 /*
213213 * Dummy function which in debug mode can be replaced with a function that
214 - * emulates console.log in console-less environments.
 214+ * emulates console.log in console-less environments.
215215 */
216216 this.log = function() { };
217217
@@ -318,7 +318,8 @@
319319 return $marker;
320320 }
321321 mw.log( 'getMarker> No <meta name="ResourceLoaderDynamicStyles"> found, inserting dynamically.' );
322 - return $marker = $( '<meta>' ).attr( 'name', 'ResourceLoaderDynamicStyles' ).appendTo( 'head' );
 322+ $marker = $( '<meta>' ).attr( 'name', 'ResourceLoaderDynamicStyles' ).appendTo( 'head' );
 323+ return $marker;
323324 }
324325 }
325326
@@ -512,7 +513,7 @@
513514 };
514515 if ( $.isArray( script ) ) {
515516 var done = 0;
516 - if (script.length == 0 ) {
 517+ if ( script.length === 0 ) {
517518 // No scripts in this module? Let's dive out early.
518519 markModuleReady();
519520 }
@@ -534,8 +535,8 @@
535536 if ( window.console && typeof window.console.log === 'function' ) {
536537 console.log( _fn + 'Exception thrown by ' + module + ': ' + e.message );
537538 }
 539+ registry[module].state = 'error';
538540 throw e;
539 - registry[module].state = 'error';
540541 }
541542 }
542543
@@ -674,8 +675,8 @@
675676 !done
676677 && (
677678 !this.readyState
678 - || this.readyState === "loaded"
679 - || this.readyState === "complete"
 679+ || this.readyState === 'loaded'
 680+ || this.readyState === 'complete'
680681 )
681682 ) {
682683 done = true;
@@ -1031,7 +1032,7 @@
10321033 */
10331034 this.version = function() {
10341035 return mediaWiki.loader.getVersion.apply( mediaWiki.loader, arguments );
1035 - }
 1036+ };
10361037
10371038 /**
10381039 * Gets the state of a module
@@ -1120,7 +1121,7 @@
11211122 }
11221123 // Regular open tag
11231124 s += '>';
1124 - if ( typeof contents === 'string') {
 1125+ if ( typeof contents === 'string' ) {
11251126 // Escaped
11261127 s += this.escape( contents );
11271128 } else if ( contents instanceof this.Raw ) {
Index: trunk/phase3/resources/mediawiki/mediawiki.log.js
@@ -13,16 +13,16 @@
1414 *
1515 * @author Michael Dale <mdale@wikimedia.org>
1616 * @author Trevor Parscal <tparscal@wikimedia.org>
17 - * @param {string} string Message to output to console
 17+ * @param logmsg string Message to output to console.
1818 */
19 - mw.log = function( string ) {
 19+ mw.log = function( msg ) {
2020 // Allow log messages to use a configured prefix to identify the source window (ie. frame)
2121 if ( mw.config.exists( 'mw.log.prefix' ) ) {
22 - string = mw.config.get( 'mw.log.prefix' ) + '> ' + string;
 22+ logmsg = mw.config.get( 'mw.log.prefix' ) + '> ' + logmsg;
2323 }
2424 // Try to use an existing console
25 - if ( typeof window.console !== 'undefined' && typeof window.console.log == 'function' ) {
26 - console.log( string );
 25+ if ( window.console !== undefined && $.isFunction( window.console.log ) ) {
 26+ window.console.log( logmsg );
2727 } else {
2828 // Set timestamp
2929 var d = new Date();
@@ -58,7 +58,7 @@
5959 'white-space': 'pre-wrap',
6060 'padding': '0.125em 0.25em'
6161 } )
62 - .text( string )
 62+ .text( logmsg )
6363 .prepend( '<span style="float:right">[' + time + ']</span>' )
6464 );
6565 }

Follow-up revisions

RevisionCommit summaryAuthorDate
r89094Fix mw.log. Broken by variable renaming in r89082hartman12:25, 29 May 2011
r89845Review of unit test suites...krinkle19:55, 10 June 2011

Past revisions this follows-up on

RevisionCommit summaryAuthorDate
r87898Continue implementation QUnit/TestSwarm integration (bug 28915)...krinkle01:34, 12 May 2011
r88732add a @fixme comment about toggleToc test case: doesn't actually test if the ...brion18:35, 24 May 2011

Comments

#Comment by Brion VIBBER (talk | contribs)   22:37, 31 May 2011

deepEqual(cond, true, ...) seems a lot less legible than ok(cond, ...); why change from the clear form to a less clear form?

#Comment by Krinkle (talk | contribs)   22:40, 31 May 2011

because ok() is casting to a boolean (ie. "Hello world", {} and 42) are also "ok".

deepEqual is a strict comparison to (in this case) boolean true.

#Comment by Brion VIBBER (talk | contribs)   22:47, 31 May 2011

Sounds like you're trying to combine two tests here:

  1. the expression returns the expected boolean result (something evaluating to true)
  2. the expression's return value is always a boolean-typed value

For #1 there, ok() is both sufficient and correct.

For #2, you'd also want to test a range of cases to actually cover it correctly. But honestly there's not much need to be doing that test on language constructs like the <= and in operators, is there?

#Comment by Krinkle (talk | contribs)   23:51, 31 May 2011

I see what you mean with combining tests but I don't think it's much of an issue here.

By the way, I didn't know you were referring to the <= and in expressions. There are also tests for mw.Map / "conf" like deepEqual( conf.exists( ... ), true, ..) and deepEqual( conf.set( ..., .... ), true, ..). I thought you were (also) referring to those. Because those indeed need to be made sure to 1) return boolean and, 2) return true/false per expectation.

I think I've covered the range of cases fairly well here by trying to throw different things at it (see here).

The <= and in.. yeah, those won't need much more than simple checks. But I like to use deepEqual (alias same), because those trigger an "Expected", "Results", "Diff" report when they don't match.

ie.:

"Expected: true"
"Results: undefined"

Whereas ok() is really basic and just reports that it wasn't OK.

deepEqual (hence it's name) aside from being a strict comparer, which is faster than loose comparison in JavaScript, in contrary to PHP) also does deep comparisons in case of objects and arrays.

For example

var b = { foo: 1 };
var c = { foo: 1 };

Neither loose or strict comparison will return true between these, because all objects are references in JS. deepEqual, in such case, does a resursive comparison of the members so that objects can be tested as well. Which makes it possible to easily test the object returns from functions like conf.get (mw.Map.prototype.get).

That's kinda summed up (strict, expected/results output, faster than loose, recursiveness) why I tend to use deepEqual by default instead of ok

-- Krinkle

#Comment by Brion VIBBER (talk | contribs)   23:58, 31 May 2011

Sure for cases where you really are checking the returned data type yes that makes sense -- and distinguishing a false from failed-to-return-value is valuable. It's the numerous cases of simple boolean comparisons with only two possible values getting deep-compared against 'true' that seem very strange to me. :)

"OK" vs "not OK" is exactly what we probably want for simple boolean conditions like this; there are no other possible values and seeing that we got 'false' instead of 'not OK' adds no information; it should still be showing the actual text comment that explains the purpose of the comparison, right?

#Comment by Krinkle (talk | contribs)   00:01, 1 June 2011

I agree. For the <= and in assertions a simple ok will do fine and the description/comment is displayed at all times.

#Comment by Brion VIBBER (talk | contribs)   00:09, 1 June 2011

So for an example rather than this:

deepEqual( conf.exists( 'foo' ), true, 'Map.exists() returns boolean true if a key exists' );
deepEqual( conf.exists( 'notExist' ), false, 'Map.exists() returns boolean false if a key does not exists' );
deepEqual( conf.get() === conf.values, true, 'Map.get() returns the entire values object by reference (if called without arguments)' );

I'd consider this:

deepEqual( conf.exists( 'foo' ), true, 'Map.exists() returns boolean true if a key exists' );
deepEqual( conf.exists( 'notExist' ), false, 'Map.exists() returns boolean false if a key does not exists' );
ok( conf.get() === conf.values, 'Map.get() returns the entire values object by reference (if called without arguments)' );

Comparing a variable that should be true or false against the expected value directly sounds perfect, and the comments already clarify that the return type is something being checked on the conf.exists() calls.

When we're just checking the return value of an === comparison, there's no such need; using ok() lets a maintainer see here you're confirming that a condition is as expected.

You might also be able to use strictEqual() here and have it report the differing data if it ever returns the wrong thing, but the docs are a bit vague on what's actually a same-object check behind the scenes.

#Comment by Krinkle (talk | contribs)   01:38, 1 June 2011

I didn't know about strictEqual, it's basically what I was (ab)using deepEqual(foo,bar) for and looks cleaner than ok( foo === bar). Thanks.

#Comment by Krinkle (talk | contribs)   23:23, 11 June 2011

I've given all test suites a review and fixed all of this mess to use the appropiate functions.

See r89845 commit message and diff. Removing "todo".

#Comment by Krinkle (talk | contribs)   23:26, 11 June 2011

deepEqual( conf.get() === conf.values, true, is wrong in so many ways.

  • Comparing the result of a comparison
  • Wrong use of deepEqual (should be strictEqual)

This was also fixed r89845.

Status & tagging log