2 * mediaWiki JavaScript library test suite
4 * Available on Special:BlankPage?action=mwutiltest&debug=true
6 * @author Krinkle <krinklemail@gmail.com>
15 // contains either a header or a test
16 // test: [ code, result, contain ] see addTest
17 // header: [ 'HEADER', escapedtitle, id ] see addHead
25 * Adds a row to the test-table
27 * @param code String Code of the test to be executed
28 * @param result String Expected result in 'var (vartype)' form
29 * @param contain String Important part of the result,
30 * if result is different but does contain this it will not return ERROR but PARTIALLY
32 'addTest' : function( code
, result
, contain
) {
36 this.addedTests
.push( [code
, result
, contain
] );
37 this.$table
.append( '<tr class="mw-mwutiltest-test">'
38 + '<td>' + mw
.html
.escape( code
).replace( / /g
, ' ' )
39 + '</td><td>' + mw
.html
.escape( result
).replace( / /g
, ' ' )
40 + '</td><td></td><td>?</td></tr>' );
45 * Adds a heading to the test-table
47 * @param title String Title of the section
49 'addHead' : function( title
) {
53 escapedtitle
= mw
.html
.escape( title
).replace( / /g
, ' ' );
54 this.addedTests
.push( [ 'HEADER', escapedtitle
, mw
.test
.numberOfHeader
++ ] );
55 this.$table
.append( '<tr class="mw-mwutiltest-head" id="mw-mwutiltest-head'+mw
.test
.numberOfHeader
+'"><th colspan="4">' + escapedtitle
+ '</th></tr>' );
60 'initialised' : false,
62 if ( this.initialised
=== false ) {
63 this.initialised
= true;
64 // jQuery document ready
66 if ( mw
.config
.get( 'wgCanonicalSpecialPageName' ) == 'Blankpage'
67 && mw
.util
.getParamValue( 'action' ) === 'mwutiltest' ) {
70 document
.title
= 'mediaWiki JavaScript library test suite - ' + mw
.config
.get( 'wgSiteName' );
71 $( '#firstHeading' ).text( 'mediaWiki JavaScript library test suite' );
72 var skinLinksText
= 'Test in: ',
74 availableSkins
= mw
.config
.get( 'wgAvailableSkins' ),
76 for ( skincode
in availableSkins
) {
77 skinLinks
.push( mw
.html
.element( 'a', {
78 'href': mw
.util
.wikiGetlink( wgPageName
) + '?action=mwutiltest&debug=true&useskin=' + encodeURIComponent( skincode
)
79 }, availableSkins
[skincode
] ) );
81 skinLinksText
+= skinLinks
.join( ' | ' ) + '.';
82 mw
.util
.$content
.html(
83 '<p>Below is a list of tests to confirm proper functionality of the mediaWiki JavaScript library</p>'
84 + '<p>' + skinLinksText
+ '</p>'
86 + '<table id="mw-mwutiltest-table" class="wikitable" style="white-space:break; font-family:monospace,\'Courier New\'; width:100%;">'
87 + '<tr><th>Exec</th><th>Should return</th><th>Does return</th><th>Equal ?</th></tr>'
92 '#mw-mwutiltest-table tr td { padding:0 !important; }' + // Override wikitable padding for <td>
93 '.mw-mwutiltest-head:hover { cursor: pointer; } ' // Header-clicks hide/show the below rows
96 mw
.test
.$table
= $( 'table#mw-mwutiltest-table' );
99 // Try to roughly keep the order similar to the order in the files
100 // or alphabetical (depending on the context)
102 /** Main modules and their aliases **/
103 mw
.test
.addHead( 'Main modules and their aliases' );
105 mw
.test
.addTest( 'typeof mediaWiki',
108 mw
.test
.addTest( 'typeof mw',
111 mw
.test
.addTest( 'typeof jQuery',
112 'function (string)' );
114 mw
.test
.addTest( 'typeof $',
115 'function (string)' );
117 /** Prototype functions added by MediaWiki **/
118 mw
.test
.addHead( 'Prototype functions added by MediaWiki' );
120 mw
.test
.addTest( 'typeof $.trimLeft',
121 'function (string)' );
123 mw
.test
.addTest( '$.trimLeft( " foo bar " )',
124 'foo bar (string)' );
126 mw
.test
.addTest( 'typeof $.trimRight',
127 'function (string)' );
129 mw
.test
.addTest( '$.trimRight( " foo bar " )',
130 ' foo bar (string)' );
132 mw
.test
.addTest( 'typeof $.ucFirst',
133 'function (string)' );
135 mw
.test
.addTest( '$.ucFirst( "mediawiki" )',
136 'Mediawiki (string)' );
138 mw
.test
.addTest( 'typeof $.escapeRE',
139 'function (string)' );
141 mw
.test
.addTest( '$.escapeRE( "<!-- ([{+mW+}]) $^|?>" )',
142 '<!\\-\\- \\(\\[\\{\\+mW\\+\\}\\]\\) \\$\\^\\|\\?> (string)' ); // double escaped
144 mw
.test
.addTest( '$.escapeRE( "ABCDEFGHIJKLMNOPQRSTUVWXYZ" )',
145 'ABCDEFGHIJKLMNOPQRSTUVWXYZ (string)' );
147 mw
.test
.addTest( '$.escapeRE( "abcdefghijklmnopqrstuvwxyz" )',
148 'abcdefghijklmnopqrstuvwxyz (string)' );
150 mw
.test
.addTest( '$.escapeRE( "0123456789" )',
151 '0123456789 (string)' );
153 mw
.test
.addTest( 'typeof $.isEmpty',
154 'function (string)' );
156 mw
.test
.addTest( '$.isEmpty( "string" )',
159 mw
.test
.addTest( '$.isEmpty( "0" )',
162 mw
.test
.addTest( '$.isEmpty([])',
165 mw
.test
.addTest( 'typeof $.compareArray',
166 'function (string)' );
168 mw
.test
.addTest( '$.compareArray( [1, "a", [], [2, "b"] ], [1, "a", [], [2, "b"] ] )',
171 mw
.test
.addTest( '$.compareArray( [1], [2] )',
174 mw
.test
.addTest( 'typeof $.compareObject',
175 'function (string)' );
178 mw
.test
.addHead( 'mediawiki.js' );
180 mw
.test
.addTest( 'mw.config instanceof mw.Map',
183 mw
.test
.addTest( 'mw.config.exists()',
186 mw
.test
.addTest( 'mw.config.exists( "wgSomeName" )',
189 mw
.test
.addTest( 'mw.config.exists( ["wgCanonicalNamespace", "wgTitle"] )',
192 mw
.test
.addTest( 'mw.config.exists( ["wgSomeName", "wgTitle"] )',
195 mw
.test
.addTest( 'mw.config.get( "wgTitle" )',
196 'BlankPage (string)' );
198 mw
.test
.addTest( 'var a = mw.config.get( ["wgTitle"] ); a.wgTitle',
199 'BlankPage (string)' );
201 mw
.test
.addTest( 'typeof mw.html',
204 mw
.test
.addTest( 'mw.html.escape( \'<mw awesome="awesome">\' )',
205 '<mw awesome="awesome"> (string)' );
207 mw
.test
.addTest( 'mw.html.element( "hr" )',
210 mw
.test
.addTest( 'mw.html.element( "img", { "src": "http://mw.org/?title=Main page&action=edit" } )',
211 '<img src="http://mw.org/?title=Main page&action=edit"/> (string)' );
213 mw
.test
.addTest( 'typeof mw.loader',
216 mw
.test
.addTest( 'typeof mw.loader.using',
217 'function (string)' );
219 mw
.test
.addTest( 'typeof mw.Map',
220 'function (string)' );
222 mw
.test
.addTest( 'typeof mw.user',
225 mw
.test
.addTest( 'typeof mw.user.anonymous()',
226 'boolean (string)' );
228 /** mediawiki.util.js **/
229 mw
.test
.addHead( 'mediawiki.util.js' );
231 mw
.test
.addTest( 'typeof mw.util',
234 mw
.test
.addTest( 'typeof mw.util.rawurlencode',
235 'function (string)' );
237 mw
.test
.addTest( 'mw.util.rawurlencode( "Test:A & B/Here" )',
238 'Test%3AA%20%26%20B%2FHere (string)' );
240 mw
.test
.addTest( 'typeof mw.util.wikiUrlencode',
241 'function (string)' );
243 mw
.test
.addTest( 'mw.util.wikiUrlencode( "Test:A & B/Here" )',
244 'Test:A_%26_B/Here (string)' );
246 mw
.test
.addTest( 'typeof mw.util.addCSS',
247 'function (string)' );
249 mw
.test
.addTest( 'var a = mw.util.addCSS( ".plainlinks { color:green; }" ); a.disabled;',
253 mw
.test
.addTest( 'typeof mw.util.wikiGetlink',
254 'function (string)' );
256 mw
.test
.addTest( 'typeof mw.util.getParamValue',
257 'function (string)' );
259 mw
.test
.addTest( 'mw.util.getParamValue( "action" )',
260 'mwutiltest (string)' );
262 mw
.test
.addTest( 'mw.util.getParamValue( "foo", "http://mw.org/?foo=wrong&foo=right#&foo=bad" )',
265 mw
.test
.addTest( 'mw.util.tooltipAccessKeyRegexp.constructor.name',
268 mw
.test
.addTest( 'typeof mw.util.updateTooltipAccessKeys',
269 'function (string)' );
271 mw
.test
.addTest( 'mw.util.$content instanceof jQuery',
274 mw
.test
.addTest( 'mw.util.$content.size()',
277 mw
.test
.addTest( 'typeof mw.util.addPortletLink',
278 'function (string)' );
280 mw
.test
.addTest( 'typeof mw.util.addPortletLink( "p-tb", "http://mediawiki.org/wiki/ResourceLoader", "ResourceLoader", "t-rl", "More info about ResourceLoader on MediaWiki.org ", "l", "#t-specialpages" )',
283 mw
.test
.addTest( 'var a = mw.util.addPortletLink( "p-tb", "http://mediawiki.org/", "MediaWiki.org", "t-mworg", "Go to MediaWiki.org ", "m", "#t-rl" ); $(a).text();',
284 'MediaWiki.org (string)' );
286 mw
.test
.addTest( 'typeof mw.util.jsMessage',
287 'function (string)' );
289 mw
.test
.addTest( 'mw.util.jsMessage( mw.config.get( "wgSiteName" ) + " is <b>Awesome</b>." )',
292 // TODO: Import tests from PHPUnit test suite for user::isValidEmailAddr
293 mw
.test
.addTest( 'mw.util.validateEmail( "" )',
296 mw
.test
.addTest( 'mw.util.validateEmail( "user@localhost" )',
299 // testEmailWithCommasAreInvalids
300 mw
.test
.addTest( 'mw.util.validateEmail( "user,foo@example.org" )',
302 mw
.test
.addTest( 'mw.util.validateEmail( "userfoo@ex,ample.org" )',
304 // testEmailWithHyphens
305 mw
.test
.addTest( 'mw.util.validateEmail( "user-foo@example.org" )',
307 mw
.test
.addTest( 'mw.util.validateEmail( "userfoo@ex-ample.org" )',
310 // From IPTest.php IPv6
311 mw
.test
.addTest( 'mw.util.isIPv6Address( "" )',
313 mw
.test
.addTest( 'mw.util.isIPv6Address( ":fc:100::" )',
315 mw
.test
.addTest( 'mw.util.isIPv6Address( "fc:100::" )',
317 mw
.test
.addTest( 'mw.util.isIPv6Address( "fc:100:a:d:1:e:ac::" )',
319 mw
.test
.addTest( 'mw.util.isIPv6Address( ":::" )',
321 mw
.test
.addTest( 'mw.util.isIPv6Address( "::0:" )',
324 // From IPTest.php IPv4
325 mw
.test
.addTest( 'mw.util.isIPv4Address( "" )',
327 mw
.test
.addTest( 'mw.util.isIPv4Address( "...." )',
329 mw
.test
.addTest( 'mw.util.isIPv4Address( "abc" )',
331 mw
.test
.addTest( 'mw.util.isIPv4Address( "124.24.52" )',
333 mw
.test
.addTest( 'mw.util.isIPv4Address( ".24.52.13" )',
335 mw
.test
.addTest( 'mw.util.isIPv4Address( "124.24.52.13" )',
337 mw
.test
.addTest( 'mw.util.isIPv4Address( "1.24.52.13" )',
339 mw
.test
.addTest( 'mw.util.isIPv4Address( "74.24.52.13/20" )', // Range
341 // @FIXME: The regex that's been in MW JS has never supported ranges but it should
342 // The regex is expected to return false for that reason
345 mw
.test
.addHead( 'jQuery plugins' );
347 mw
.test
.addTest( 'typeof $.client',
350 mw
.test
.addTest( 'typeof $.client.profile',
351 'function (string)' );
353 mw
.test
.addTest( 'var a = $.client.profile(); typeof a.name',
356 mw
.test
.addTest( 'typeof $.fn.makeCollapsible',
357 'function (string)' );
361 mw
.test
.addHead( '*** End of tests ***' );
363 // Run tests and compare results
369 numberOfPartials
= 0,
371 headNumberOfTests
= 0,
372 headNumberOfPasseds
= 0,
373 headNumberOfPartials
= 0,
374 headNumberOfErrors
= 0,
376 previousHeadTitle
= '',
377 $testrows
= mw
.test
.$table
.find( 'tr:has(td)' );
379 $.each( mw
.test
.addedTests
, function( i
, item
) {
382 if( item
[0] == 'HEADER' ) {
384 // update current header with its tests results
385 mw
.test
.$table
.find( 'tr#mw-mwutiltest-head' + numberOfHeaders
+' > th' )
386 .html( previousHeadTitle
+ ' <span style="float:right">('
387 + 'Tests: ' + headNumberOfTests
388 + ' OK: ' + headNumberOfPasseds
389 + ' Partial: ' + headNumberOfPartials
390 + ' Error: ' + headNumberOfErrors
394 // Reset values for the new header;
395 headNumberOfTests
= 0;
396 headNumberOfPasseds
= 0;
397 headNumberOfPartials
= 0;
398 headNumberOfErrors
= 0;
400 previousHeadTitle
= item
[1];
406 shouldreturn
= item
[1];
407 shouldcontain
= item
[2];
411 doesReturn
= eval( exec
);
412 doesReturn
= doesReturn
+ ' (' + typeof doesReturn
+ ')';
413 $thisrow
= $testrows
.eq( i
- numberOfHeaders
); // since headers are rows as well
414 $thisrow
.find( '> td' ).eq(2).html( mw
.html
.escape( doesReturn
).replace(/ /g
, ' ' ) );
416 if ( doesReturn
.indexOf( shouldcontain
) !== -1 ) {
417 if ( doesReturn
== shouldreturn
) {
418 $thisrow
.find( '> td' ).eq(3).css( 'background', '#AFA' ).text( 'OK' );
420 headNumberOfPasseds
++;
422 $thisrow
.find( '> td' ).eq(3).css( 'background', '#FFA' ).html( '<small>PARTIALLY</small>' );
424 headNumberOfPartials
++;
427 $thisrow
.find( '> td' ).eq(3).css( 'background', '#FAA' ).text( 'ERROR' );
429 headNumberOfErrors
++;
433 mw
.test
.$table
.before( '<p><strong>Ran ' + numberOfTests
+ ' tests. ' +
434 numberOfPasseds
+ ' passed test(s). ' + numberOfErrors
+ ' error(s). ' +
435 numberOfPartials
+ ' partially passed test(s). </p>' );
437 // hide all tests. TODO hide only OK?
438 mw
.test
.$table
.find( '.mw-mwutiltest-test' ).hide();
439 // clickable header to show/hide the tests
440 mw
.test
.$table
.find( '.mw-mwutiltest-head' ).click(function() {
441 $(this).nextUntil( '.mw-mwutiltest-head' ).toggle();
451 } )(jQuery
, mediaWiki
);