Renaming qunit test files to end in ".test.js" (finally!)
authorKrinkle <krinkle@users.mediawiki.org>
Fri, 12 Aug 2011 16:02:03 +0000 (16:02 +0000)
committerKrinkle <krinkle@users.mediawiki.org>
Fri, 12 Aug 2011 16:02:03 +0000 (16:02 +0000)
* There shouldn't be two files with the same name in core, especially not the module and the test suite. Previously postponed due to compatibility with our TestSwarm script, that has been fixed now.

* Had to modify the files in the same commit since the module name is referenced inside the test suite in the module() call, which is the hint for QUnit when filtering is done through the ?filter= parameter.

* As has been added to the conventions, there must be only one module() call per test suite file and it MUST match "filename without .test.js". Otherwise the module will not be submitted to TestSwarm (which glob()'s at the /suites/ directory and extracts test-suite-module-names from the filenames).

32 files changed:
tests/qunit/index.html
tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js [new file with mode: 0644]
tests/qunit/suites/resources/jquery/jquery.byteLength.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.byteLength.test.js [new file with mode: 0644]
tests/qunit/suites/resources/jquery/jquery.byteLimit.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js [new file with mode: 0644]
tests/qunit/suites/resources/jquery/jquery.client.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.client.test.js [new file with mode: 0644]
tests/qunit/suites/resources/jquery/jquery.colorUtil.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js [new file with mode: 0644]
tests/qunit/suites/resources/jquery/jquery.getAttrs.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js [new file with mode: 0644]
tests/qunit/suites/resources/jquery/jquery.localize.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.localize.test.js [new file with mode: 0644]
tests/qunit/suites/resources/jquery/jquery.mwExtension.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js [new file with mode: 0644]
tests/qunit/suites/resources/jquery/jquery.tabIndex.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js [new file with mode: 0644]
tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js
tests/qunit/suites/resources/jquery/jquery.textSelection.js [deleted file]
tests/qunit/suites/resources/jquery/jquery.textSelection.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js [deleted file]
tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.js [deleted file]
tests/qunit/suites/resources/mediawiki/mediawiki.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.title.js [deleted file]
tests/qunit/suites/resources/mediawiki/mediawiki.title.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.user.js [deleted file]
tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js [new file with mode: 0644]
tests/qunit/suites/resources/mediawiki/mediawiki.util.js [deleted file]
tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js [new file with mode: 0644]

index 7eb8fba..e8bb99a 100644 (file)
        <script src="data/testrunner.js"></script>
 
        <!-- QUnit: Load test suites (maintain the same order as above please) -->
-       <script src="suites/resources/mediawiki/mediawiki.js"></script>
-       <script src="suites/resources/mediawiki/mediawiki.user.js"></script>
+       <script src="suites/resources/mediawiki/mediawiki.test.js"></script>
+       <script src="suites/resources/mediawiki/mediawiki.user.test.js"></script>
 
-       <script src="suites/resources/jquery/jquery.client.js"></script>
-       <script src="suites/resources/jquery/jquery.mwExtension.js"></script>
-       <script src="suites/resources/mediawiki/mediawiki.util.js"></script>
+       <script src="suites/resources/jquery/jquery.client.test.js"></script>
+       <script src="suites/resources/jquery/jquery.mwExtension.test.js"></script>
+       <script src="suites/resources/mediawiki/mediawiki.util.test.js"></script>
 
-       <script src="suites/resources/jquery/jquery.autoEllipsis.js"></script>
-       <script src="suites/resources/jquery/jquery.byteLength.js"></script>
-       <script src="suites/resources/jquery/jquery.byteLimit.js"></script>
-       <script src="suites/resources/jquery/jquery.colorUtil.js"></script>
-       <script src="suites/resources/jquery/jquery.getAttrs.js"></script>
-       <script src="suites/resources/jquery/jquery.localize.js"></script>
-       <script src="suites/resources/jquery/jquery.tabIndex.js"></script>
+       <script src="suites/resources/jquery/jquery.autoEllipsis.test.js"></script>
+       <script src="suites/resources/jquery/jquery.byteLength.test.js"></script>
+       <script src="suites/resources/jquery/jquery.byteLimit.test.js"></script>
+       <script src="suites/resources/jquery/jquery.colorUtil.test.js"></script>
+       <script src="suites/resources/jquery/jquery.getAttrs.test.js"></script>
+       <script src="suites/resources/jquery/jquery.localize.test.js"></script>
+       <script src="suites/resources/jquery/jquery.tabIndex.test.js"></script>
        <script src="suites/resources/jquery/jquery.tablesorter.test.js" charset="UTF-8"></script>
-       <script src="suites/resources/jquery/jquery.textSelection.js" charset="UTF-8"></script>
-       <script src="suites/resources/mediawiki/mediawiki.title.js"></script>
-       <script src="suites/resources/mediawiki.special/mediawiki.special.recentchanges.js"></script>
+       <script src="suites/resources/jquery/jquery.textSelection.test.js" charset="UTF-8"></script>
+       <script src="suites/resources/mediawiki/mediawiki.title.test.js"></script>
+       <script src="suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js"></script>
 </head>
 <body>
        <h1 id="qunit-header">MediaWiki JavaScript Test Suite</h1>
diff --git a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js
deleted file mode 100644 (file)
index caf5a6f..0000000
+++ /dev/null
@@ -1,58 +0,0 @@
-module( 'jquery.autoEllipsis.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-       ok( $.fn.autoEllipsis, 'jQuery.fn.autoEllipsis defined' );
-});
-
-function createWrappedDiv( text, width ) {
-       var $wrapper = $( '<div />' ).css( 'width', width );
-       var $div = $( '<div />' ).text( text );
-       $wrapper.append( $div );
-       return $wrapper;
-}
-
-function findDivergenceIndex( a, b ) {
-       var i = 0;
-       while ( i < a.length && i < b.length && a[i] == b[i] ) {
-               i++;
-       }
-       return i;
-}
-
-test( 'Position right', function() {
-       expect(4);
-
-       // We need this thing to be visible, so append it to the DOM
-       var origText = 'This is a really long random string and there is no way it fits in 100 pixels.';
-       var $wrapper = createWrappedDiv( origText, '100px' );
-       $( 'body' ).append( $wrapper );
-       $wrapper.autoEllipsis( { position: 'right' } );
-
-       // Verify that, and only one, span element was created
-       var $span = $wrapper.find( '> span' );
-       strictEqual( $span.length, 1, 'autoEllipsis wrapped the contents in a span element' );
-
-       // Check that the text fits by turning on word wrapping
-       $span.css( 'whiteSpace', 'nowrap' );
-       ltOrEq( $span.width(), $span.parent().width(), "Text fits (making the span 'white-space:nowrap' does not make it wider than its parent)" );
-
-       // Add two characters using scary black magic
-       var spanText = $span.text();
-       var d = findDivergenceIndex( origText, spanText );
-       var spanTextNew = spanText.substr( 0, d ) + origText[d] + origText[d] + '...';
-
-       gt( spanTextNew.length, spanText.length, 'Verify that the new span-length is indeed greater' );
-
-       // Put this text in the span and verify it doesn't fit
-       $span.text( spanTextNew );
-       // In IE6 width works like min-width, allow IE6's width to be "equal to"
-       if ( $.browser.msie && Number( $.browser.version ) == 6 ) {
-               gtOrEq( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more) - IE6: Maybe equal to as well due to width behaving like min-width in IE6' );
-       } else {
-               gt( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more)' );
-       }
-
-       // Clean up
-       $wrapper.remove();
-});
diff --git a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.test.js
new file mode 100644 (file)
index 0000000..690ffb2
--- /dev/null
@@ -0,0 +1,58 @@
+module( 'jquery.autoEllipsis' );
+
+test( '-- Initial check', function() {
+       expect(1);
+       ok( $.fn.autoEllipsis, 'jQuery.fn.autoEllipsis defined' );
+});
+
+function createWrappedDiv( text, width ) {
+       var $wrapper = $( '<div />' ).css( 'width', width );
+       var $div = $( '<div />' ).text( text );
+       $wrapper.append( $div );
+       return $wrapper;
+}
+
+function findDivergenceIndex( a, b ) {
+       var i = 0;
+       while ( i < a.length && i < b.length && a[i] == b[i] ) {
+               i++;
+       }
+       return i;
+}
+
+test( 'Position right', function() {
+       expect(4);
+
+       // We need this thing to be visible, so append it to the DOM
+       var origText = 'This is a really long random string and there is no way it fits in 100 pixels.';
+       var $wrapper = createWrappedDiv( origText, '100px' );
+       $( 'body' ).append( $wrapper );
+       $wrapper.autoEllipsis( { position: 'right' } );
+
+       // Verify that, and only one, span element was created
+       var $span = $wrapper.find( '> span' );
+       strictEqual( $span.length, 1, 'autoEllipsis wrapped the contents in a span element' );
+
+       // Check that the text fits by turning on word wrapping
+       $span.css( 'whiteSpace', 'nowrap' );
+       ltOrEq( $span.width(), $span.parent().width(), "Text fits (making the span 'white-space:nowrap' does not make it wider than its parent)" );
+
+       // Add two characters using scary black magic
+       var spanText = $span.text();
+       var d = findDivergenceIndex( origText, spanText );
+       var spanTextNew = spanText.substr( 0, d ) + origText[d] + origText[d] + '...';
+
+       gt( spanTextNew.length, spanText.length, 'Verify that the new span-length is indeed greater' );
+
+       // Put this text in the span and verify it doesn't fit
+       $span.text( spanTextNew );
+       // In IE6 width works like min-width, allow IE6's width to be "equal to"
+       if ( $.browser.msie && Number( $.browser.version ) == 6 ) {
+               gtOrEq( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more) - IE6: Maybe equal to as well due to width behaving like min-width in IE6' );
+       } else {
+               gt( $span.width(), $span.parent().width(), 'Fit is maximal (adding two characters makes it not fit any more)' );
+       }
+
+       // Clean up
+       $wrapper.remove();
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLength.js b/tests/qunit/suites/resources/jquery/jquery.byteLength.js
deleted file mode 100644 (file)
index f82fda2..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-module( 'jquery.byteLength.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-       ok( $.byteLength, 'jQuery.byteLength defined' );
-} );
-
-test( 'Simple text', function() {
-       expect(5);
-
-       var     azLc = 'abcdefghijklmnopqrstuvwxyz',
-               azUc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
-               num = '0123456789',
-               x = '*',
-               space = '   ';
-
-       equal( $.byteLength( azLc ), 26, 'Lowercase a-z' );
-       equal( $.byteLength( azUc ), 26, 'Uppercase A-Z' );
-       equal( $.byteLength( num ), 10, 'Numbers 0-9' );
-       equal( $.byteLength( x ), 1, 'An asterisk' );
-       equal( $.byteLength( space ), 3, '3 spaces' );
-
-} );
-
-test( 'Special text', window.foo = function() {
-       expect(5);
-
-       // http://en.wikipedia.org/wiki/UTF-8 
-       var     U_0024 = '\u0024',
-               U_00A2 = '\u00A2',
-               U_20AC = '\u20AC',
-               U_024B62 = '\u024B62',
-               // The normal one doesn't display properly, try the below which is the same
-               // according to http://www.fileformat.info/info/unicode/char/24B62/index.htm
-               U_024B62_alt = '\uD852\uDF62';
-
-       strictEqual( $.byteLength( U_0024 ), 1, 'U+0024: 1 byte. \u0024 (dollar sign)' );
-       strictEqual( $.byteLength( U_00A2 ), 2, 'U+00A2: 2 bytes. \u00A2 (cent sign)' );
-       strictEqual( $.byteLength( U_20AC ), 3, 'U+20AC: 3 bytes. \u20AC (euro sign)' );
-       strictEqual( $.byteLength( U_024B62 ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character)' );
-       strictEqual( $.byteLength( U_024B62_alt ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character) - alternative method' );
-} );
diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js b/tests/qunit/suites/resources/jquery/jquery.byteLength.test.js
new file mode 100644 (file)
index 0000000..27913c6
--- /dev/null
@@ -0,0 +1,42 @@
+module( 'jquery.byteLength' );
+
+test( '-- Initial check', function() {
+       expect(1);
+       ok( $.byteLength, 'jQuery.byteLength defined' );
+} );
+
+test( 'Simple text', function() {
+       expect(5);
+
+       var     azLc = 'abcdefghijklmnopqrstuvwxyz',
+               azUc = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
+               num = '0123456789',
+               x = '*',
+               space = '   ';
+
+       equal( $.byteLength( azLc ), 26, 'Lowercase a-z' );
+       equal( $.byteLength( azUc ), 26, 'Uppercase A-Z' );
+       equal( $.byteLength( num ), 10, 'Numbers 0-9' );
+       equal( $.byteLength( x ), 1, 'An asterisk' );
+       equal( $.byteLength( space ), 3, '3 spaces' );
+
+} );
+
+test( 'Special text', window.foo = function() {
+       expect(5);
+
+       // http://en.wikipedia.org/wiki/UTF-8 
+       var     U_0024 = '\u0024',
+               U_00A2 = '\u00A2',
+               U_20AC = '\u20AC',
+               U_024B62 = '\u024B62',
+               // The normal one doesn't display properly, try the below which is the same
+               // according to http://www.fileformat.info/info/unicode/char/24B62/index.htm
+               U_024B62_alt = '\uD852\uDF62';
+
+       strictEqual( $.byteLength( U_0024 ), 1, 'U+0024: 1 byte. \u0024 (dollar sign)' );
+       strictEqual( $.byteLength( U_00A2 ), 2, 'U+00A2: 2 bytes. \u00A2 (cent sign)' );
+       strictEqual( $.byteLength( U_20AC ), 3, 'U+20AC: 3 bytes. \u20AC (euro sign)' );
+       strictEqual( $.byteLength( U_024B62 ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character)' );
+       strictEqual( $.byteLength( U_024B62_alt ), 4, 'U+024B62: 4 bytes. \uD852\uDF62 (a Han character) - alternative method' );
+} );
diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLimit.js b/tests/qunit/suites/resources/jquery/jquery.byteLimit.js
deleted file mode 100644 (file)
index 1d8ca9b..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-module( 'jquery.byteLimit.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-       ok( $.fn.byteLimit, 'jQuery.fn.byteLimit defined' );
-} );
-
-// Basic sendkey-implementation
-$.addChars = function( $input, charstr ) {
-       var len = charstr.length;
-       for ( var i = 0; i < len; i++ ) {
-               // Keep track of the previous value
-               var prevVal = $input.val();
-
-               // Get the key code
-               var code = charstr.charCodeAt(i);
-
-               // Trigger event and undo if prevented
-               var event = new jQuery.Event( 'keypress', { keyCode: code, which: code, charCode: code } );
-               $input.trigger( event );
-               if ( !event.isDefaultPrevented() ) {
-                       $input.val( prevVal + charstr.charAt(i) );
-               }
-       }
-};
-
-/**
- * Test factory for $.fn.byteLimit
- *
- * @param $input {jQuery} jQuery object in an input element
- * @param hasLimit {Boolean} Wether a limit should apply at all
- * @param limit {Number} Limit (if used) otherwise undefined
- * The limit should be less than 20 (the sample data's length)
- */
-var byteLimitTest = function( options ) {
-       var opt = $.extend({
-               description: '',
-               $input: null,
-               sample: '',
-               hasLimit: false,
-               expected: '',
-               limit: null
-       }, options);
-
-       test( opt.description, function() {
-
-               opt.$input.appendTo( 'body' );
-
-               // Simulate pressing keys for each of the sample characters
-               $.addChars( opt.$input, opt.sample );
-               var     rawVal = opt.$input.val(),
-                       fn = opt.$input.data( 'byteLimit-callback' ),
-                       newVal = $.isFunction( fn ) ? fn( rawVal ) : rawVal;
-
-               if ( opt.hasLimit ) {
-                       expect(3);
-
-                       ltOrEq( $.byteLength( newVal ), opt.limit, 'Prevent keypresses after byteLimit was reached, length never exceeded the limit' );
-                       equal( $.byteLength( rawVal ), $.byteLength( opt.expected ), 'Not preventing keypresses too early, length has reached the expected length' );
-                       equal( rawVal, opt.expected, 'New value matches the expected string' );
-
-               } else {
-                       expect(2);
-                       equal( newVal, opt.expected, 'New value matches the expected string' );
-                       equal( $.byteLength( newVal ), $.byteLength( opt.expected ), 'Unlimited scenarios are not affected, expected length reached' );
-               }
-
-               opt.$input.remove();
-       } );
-};
-
-var
-       // Simple sample (20 chars, 20 bytes)
-       simpleSample = '12345678901234567890',
-
-       // 3 bytes (euro-symbol)
-       U_20AC = '\u20AC',
-
-       // Multi-byte sample (22 chars, 26 bytes)
-       mbSample = '1234567890' + U_20AC + '1234567890' + U_20AC;
-
-byteLimitTest({
-       description: 'Plain text input',
-       $input: $( '<input>' )
-               .attr( {
-                       'type': 'text'
-               }),
-       sample: simpleSample,
-       hasLimit: false,
-       expected: simpleSample
-});
-
-byteLimitTest({
-       description: 'Limit using the maxlength attribute',
-       $input: $( '<input>' )
-               .attr( {
-                       'type': 'text',
-                       'maxlength': '10'
-               })
-               .byteLimit(),
-       sample: simpleSample,
-       hasLimit: true,
-       limit: 10,
-       expected: '1234567890'
-});
-
-byteLimitTest({
-       description: 'Limit using a custom value',
-       $input: $( '<input>' )
-               .attr( {
-                       'type': 'text'
-               })
-               .byteLimit( 10 ),
-       sample: simpleSample,
-       hasLimit: true,
-       limit: 10,
-       expected: '1234567890'
-});
-
-byteLimitTest({
-       description: 'Limit using a custom value, overriding maxlength attribute',
-       $input: $( '<input>' )
-               .attr( {
-                       'type': 'text',
-                       'maxLength': '10'
-               })
-               .byteLimit( 15 ),
-       sample: simpleSample,
-       hasLimit: true,
-       limit: 15,
-       expected: '123456789012345'
-});
-
-byteLimitTest({
-       description: 'Limit using a custom value (multibyte)',
-       $input: $( '<input>' )
-               .attr( {
-                       'type': 'text'
-               })
-               .byteLimit( 14 ),
-       sample: mbSample,
-       hasLimit: true,
-       limit: 14,
-       expected: '1234567890' + U_20AC + '1'
-});
-
-byteLimitTest({
-       description: 'Limit using a custom value (multibyte) overlapping a byte',
-       $input: $( '<input>' )
-               .attr( {
-                       'type': 'text'
-               })
-               .byteLimit( 12 ),
-       sample: mbSample,
-       hasLimit: true,
-       limit: 12,
-       expected: '1234567890' + '12'
-});
-
-byteLimitTest({
-       description: 'Pass the limit and a callback as input filter',
-       $input: $( '<input>' )
-               .attr( {
-                       'type': 'text'
-               })
-               .byteLimit( 6, function( val ) {
-                       _titleConfig();
-
-                       // Invalid title
-                       if ( val == '' ) {
-                               return '';
-                       }
-
-                       // Return without namespace prefix
-                       return new mw.Title( '' + val ).getMain();
-               } ),
-       sample: 'User:Sample',
-       hasLimit: true,
-       limit: 6, // 'Sample' length
-       expected: 'User:Sample'
-});
-
-byteLimitTest({
-       description: 'Limit using the maxlength attribute and pass a callback as input filter',
-       $input: $( '<input>' )
-               .attr( {
-                       'type': 'text',
-                       'maxLength': '6'
-               })
-               .byteLimit( function( val ) {
-                       _titleConfig();
-
-                       // Invalid title
-                       if ( val === '' ) {
-                               return '';
-                       }
-
-                       // Return without namespace prefix
-                       return new mw.Title( '' + val ).getMain();
-               } ),
-       sample: 'User:Sample',
-       hasLimit: true,
-       limit: 6, // 'Sample' length
-       expected: 'User:Sample'
-});
diff --git a/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js b/tests/qunit/suites/resources/jquery/jquery.byteLimit.test.js
new file mode 100644 (file)
index 0000000..70343fd
--- /dev/null
@@ -0,0 +1,205 @@
+module( 'jquery.byteLimit' );
+
+test( '-- Initial check', function() {
+       expect(1);
+       ok( $.fn.byteLimit, 'jQuery.fn.byteLimit defined' );
+} );
+
+// Basic sendkey-implementation
+$.addChars = function( $input, charstr ) {
+       var len = charstr.length;
+       for ( var i = 0; i < len; i++ ) {
+               // Keep track of the previous value
+               var prevVal = $input.val();
+
+               // Get the key code
+               var code = charstr.charCodeAt(i);
+
+               // Trigger event and undo if prevented
+               var event = new jQuery.Event( 'keypress', { keyCode: code, which: code, charCode: code } );
+               $input.trigger( event );
+               if ( !event.isDefaultPrevented() ) {
+                       $input.val( prevVal + charstr.charAt(i) );
+               }
+       }
+};
+
+/**
+ * Test factory for $.fn.byteLimit
+ *
+ * @param $input {jQuery} jQuery object in an input element
+ * @param hasLimit {Boolean} Wether a limit should apply at all
+ * @param limit {Number} Limit (if used) otherwise undefined
+ * The limit should be less than 20 (the sample data's length)
+ */
+var byteLimitTest = function( options ) {
+       var opt = $.extend({
+               description: '',
+               $input: null,
+               sample: '',
+               hasLimit: false,
+               expected: '',
+               limit: null
+       }, options);
+
+       test( opt.description, function() {
+
+               opt.$input.appendTo( 'body' );
+
+               // Simulate pressing keys for each of the sample characters
+               $.addChars( opt.$input, opt.sample );
+               var     rawVal = opt.$input.val(),
+                       fn = opt.$input.data( 'byteLimit-callback' ),
+                       newVal = $.isFunction( fn ) ? fn( rawVal ) : rawVal;
+
+               if ( opt.hasLimit ) {
+                       expect(3);
+
+                       ltOrEq( $.byteLength( newVal ), opt.limit, 'Prevent keypresses after byteLimit was reached, length never exceeded the limit' );
+                       equal( $.byteLength( rawVal ), $.byteLength( opt.expected ), 'Not preventing keypresses too early, length has reached the expected length' );
+                       equal( rawVal, opt.expected, 'New value matches the expected string' );
+
+               } else {
+                       expect(2);
+                       equal( newVal, opt.expected, 'New value matches the expected string' );
+                       equal( $.byteLength( newVal ), $.byteLength( opt.expected ), 'Unlimited scenarios are not affected, expected length reached' );
+               }
+
+               opt.$input.remove();
+       } );
+};
+
+var
+       // Simple sample (20 chars, 20 bytes)
+       simpleSample = '12345678901234567890',
+
+       // 3 bytes (euro-symbol)
+       U_20AC = '\u20AC',
+
+       // Multi-byte sample (22 chars, 26 bytes)
+       mbSample = '1234567890' + U_20AC + '1234567890' + U_20AC;
+
+byteLimitTest({
+       description: 'Plain text input',
+       $input: $( '<input>' )
+               .attr( {
+                       'type': 'text'
+               }),
+       sample: simpleSample,
+       hasLimit: false,
+       expected: simpleSample
+});
+
+byteLimitTest({
+       description: 'Limit using the maxlength attribute',
+       $input: $( '<input>' )
+               .attr( {
+                       'type': 'text',
+                       'maxlength': '10'
+               })
+               .byteLimit(),
+       sample: simpleSample,
+       hasLimit: true,
+       limit: 10,
+       expected: '1234567890'
+});
+
+byteLimitTest({
+       description: 'Limit using a custom value',
+       $input: $( '<input>' )
+               .attr( {
+                       'type': 'text'
+               })
+               .byteLimit( 10 ),
+       sample: simpleSample,
+       hasLimit: true,
+       limit: 10,
+       expected: '1234567890'
+});
+
+byteLimitTest({
+       description: 'Limit using a custom value, overriding maxlength attribute',
+       $input: $( '<input>' )
+               .attr( {
+                       'type': 'text',
+                       'maxLength': '10'
+               })
+               .byteLimit( 15 ),
+       sample: simpleSample,
+       hasLimit: true,
+       limit: 15,
+       expected: '123456789012345'
+});
+
+byteLimitTest({
+       description: 'Limit using a custom value (multibyte)',
+       $input: $( '<input>' )
+               .attr( {
+                       'type': 'text'
+               })
+               .byteLimit( 14 ),
+       sample: mbSample,
+       hasLimit: true,
+       limit: 14,
+       expected: '1234567890' + U_20AC + '1'
+});
+
+byteLimitTest({
+       description: 'Limit using a custom value (multibyte) overlapping a byte',
+       $input: $( '<input>' )
+               .attr( {
+                       'type': 'text'
+               })
+               .byteLimit( 12 ),
+       sample: mbSample,
+       hasLimit: true,
+       limit: 12,
+       expected: '1234567890' + '12'
+});
+
+byteLimitTest({
+       description: 'Pass the limit and a callback as input filter',
+       $input: $( '<input>' )
+               .attr( {
+                       'type': 'text'
+               })
+               .byteLimit( 6, function( val ) {
+                       _titleConfig();
+
+                       // Invalid title
+                       if ( val == '' ) {
+                               return '';
+                       }
+
+                       // Return without namespace prefix
+                       return new mw.Title( '' + val ).getMain();
+               } ),
+       sample: 'User:Sample',
+       hasLimit: true,
+       limit: 6, // 'Sample' length
+       expected: 'User:Sample'
+});
+
+byteLimitTest({
+       description: 'Limit using the maxlength attribute and pass a callback as input filter',
+       $input: $( '<input>' )
+               .attr( {
+                       'type': 'text',
+                       'maxLength': '6'
+               })
+               .byteLimit( function( val ) {
+                       _titleConfig();
+
+                       // Invalid title
+                       if ( val === '' ) {
+                               return '';
+                       }
+
+                       // Return without namespace prefix
+                       return new mw.Title( '' + val ).getMain();
+               } ),
+       sample: 'User:Sample',
+       hasLimit: true,
+       limit: 6, // 'Sample' length
+       expected: 'User:Sample'
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.client.js b/tests/qunit/suites/resources/jquery/jquery.client.js
deleted file mode 100644 (file)
index 50df292..0000000
+++ /dev/null
@@ -1,205 +0,0 @@
-module( 'jquery.client.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-       ok( jQuery.client, 'jQuery.client defined' );
-});
-
-test( 'profile userAgent support', function() {
-       expect(8);
-
-       // Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value)
-       // Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/
-       var uas = {
-               // Internet Explorer 6
-               // Internet Explorer 7
-               'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)': {
-                       title: 'Internet Explorer 7',
-                       platform: 'Win32',
-                       profile: {
-                               "name": "msie",
-                               "layout": "trident",
-                               "layoutVersion": "unknown",
-                               "platform": "win",
-                               "version": "7.0",
-                               "versionBase": "7",
-                               "versionNumber": 7
-                       }
-               },
-               // Internet Explorer 8
-               // Internet Explorer 9
-               // Internet Explorer 10
-               // Firefox 2
-               // Firefox 3.5
-               'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19': {
-                       title: 'Firefox 3.5',
-                       platform: 'MacIntel',
-                       profile: {
-                               "name": "firefox",
-                               "layout": "gecko",
-                               "layoutVersion": 20110420,
-                               "platform": "mac",
-                               "version": "3.5.19",
-                               "versionBase": "3",
-                               "versionNumber": 3.5
-                       }
-               },
-               // Firefox 3.6
-               'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110422 Ubuntu/10.10 (maverick) Firefox/3.6.17': {
-                       title: 'Firefox 3.6',
-                       platform: 'Linux i686',
-                       profile: {
-                               "name": "firefox",
-                               "layout": "gecko",
-                               "layoutVersion": 20110422,
-                               "platform": "linux",
-                               "version": "3.6.17",
-                               "versionBase": "3",
-                               "versionNumber": 3.6
-                       }
-               },
-               // Firefox 4
-               'Mozilla/5.0 (Windows NT 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1': {
-                       title: 'Firefox 4',
-                       platform: 'Win32',
-                       profile: {
-                               "name": "firefox",
-                               "layout": "gecko",
-                               "layoutVersion": 20100101,
-                               "platform": "win",
-                               "version": "4.0.1",
-                               "versionBase": "4",
-                               "versionNumber": 4
-                       } 
-               },
-               // Firefox 5
-               // Safari 3
-               // Safari 4
-               'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': {
-                       title: 'Safari 4',
-                       platform: 'MacIntel',
-                       profile: {
-                               "name": "safari",
-                               "layout": "webkit",
-                               "layoutVersion": 531,
-                               "platform": "mac",
-                               "version": "4.0.5",
-                               "versionBase": "4",
-                               "versionNumber": 4
-                       } 
-               },
-               'Mozilla/5.0 (Windows; U; Windows NT 6.0; cs-CZ) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': {
-                       title: 'Safari 4',
-                       platform: 'Win32',
-                       profile: {
-                               "name": "safari",
-                               "layout": "webkit",
-                               "layoutVersion": 533,
-                               "platform": "win",
-                               "version": "4.0.5",
-                               "versionBase": "4",
-                               "versionNumber": 4
-                       }
-               },
-               // Safari 5
-               // Opera 10
-               // Chrome 5
-               // Chrome 6
-               // Chrome 7
-               // Chrome 8
-               // Chrome 9
-               // Chrome 10
-               // Chrome 11
-               // Chrome 12
-               'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30': {
-                       title: 'Chrome 12',
-                       platform: 'MacIntel',
-                       profile: {
-                               "name": "chrome",
-                               "layout": "webkit",
-                               "layoutVersion": 534,
-                               "platform": "mac",
-                               "version": "12.0.742.112",
-                               "versionBase": "12",
-                               "versionNumber": 12
-                       }
-               },
-               'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30': {
-                       title: 'Chrome 12',
-                       platform: 'Linux i686',
-                       profile: {
-                               "name": "chrome",
-                               "layout": "webkit",
-                               "layoutVersion": 534,
-                               "platform": "linux",
-                               "version": "12.0.742.68",
-                               "versionBase": "12",
-                               "versionNumber": 12
-                       }
-               }
-       };
-
-       // Generate a client profile object and compare recursively
-       var uaTest = function( rawUserAgent, data ) {
-               var ret = $.client.profile( {
-                       userAgent: rawUserAgent,
-                       platform: data.platform
-               } );
-               deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
-       };
-
-       // Loop through and run tests
-       $.each( uas, uaTest );
-} );
-
-test( 'profile return validation for current user agent', function() {
-       expect(7);
-       var p = $.client.profile();
-       var unknownOrType = function( val, type, summary ) {
-               return ok( typeof val === type || val === 'unknown', summary );
-       };
-
-       equal( typeof p, 'object', 'profile returns an object' );
-       unknownOrType( p.layout, 'string', 'p.layout is a string (or "unknown")' );
-       unknownOrType( p.layoutVersion, 'number', 'p.layoutVersion is a number (or "unknown")' );
-       unknownOrType( p.platform, 'string', 'p.platform is a string (or "unknown")' );
-       unknownOrType( p.version, 'string', 'p.version is a string (or "unknown")' );
-       unknownOrType( p.versionBase, 'string', 'p.versionBase is a string (or "unknown")' );
-       equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' );
-});
-
-test( 'test', function() {
-       expect(1);
-
-       // Example from WikiEditor
-       var testMap = {
-               'ltr': {
-                       'msie': [['>=', 7]],
-                       'firefox': [['>=', 2]],
-                       'opera': [['>=', 9.6]],
-                       'safari': [['>=', 3]],
-                       'chrome': [['>=', 3]],
-                       'netscape': [['>=', 9]],
-                       'blackberry': false,
-                       'ipod': false,
-                       'iphone': false
-               },
-               'rtl': {
-                       'msie': [['>=', 8]],
-                       'firefox': [['>=', 2]],
-                       'opera': [['>=', 9.6]],
-                       'safari': [['>=', 3]],
-                       'chrome': [['>=', 3]],
-                       'netscape': [['>=', 9]],
-                       'blackberry': false,
-                       'ipod': false,
-                       'iphone': false
-               }
-       };
-       // .test() uses eval, make sure no exceptions are thrown
-       // then do a basic return value type check
-       var testMatch = $.client.test( testMap );
-
-       equal( typeof testMatch, 'boolean', 'test returns a boolean value' );
-
-});
diff --git a/tests/qunit/suites/resources/jquery/jquery.client.test.js b/tests/qunit/suites/resources/jquery/jquery.client.test.js
new file mode 100644 (file)
index 0000000..116a1f0
--- /dev/null
@@ -0,0 +1,205 @@
+module( 'jquery.client' );
+
+test( '-- Initial check', function() {
+       expect(1);
+       ok( jQuery.client, 'jQuery.client defined' );
+});
+
+test( 'profile userAgent support', function() {
+       expect(8);
+
+       // Object keyed by userAgent. Value is an array (human-readable name, client-profile object, navigator.platform value)
+       // Info based on results from http://toolserver.org/~krinkle/testswarm/job/174/
+       var uas = {
+               // Internet Explorer 6
+               // Internet Explorer 7
+               'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)': {
+                       title: 'Internet Explorer 7',
+                       platform: 'Win32',
+                       profile: {
+                               "name": "msie",
+                               "layout": "trident",
+                               "layoutVersion": "unknown",
+                               "platform": "win",
+                               "version": "7.0",
+                               "versionBase": "7",
+                               "versionNumber": 7
+                       }
+               },
+               // Internet Explorer 8
+               // Internet Explorer 9
+               // Internet Explorer 10
+               // Firefox 2
+               // Firefox 3.5
+               'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1.19) Gecko/20110420 Firefox/3.5.19': {
+                       title: 'Firefox 3.5',
+                       platform: 'MacIntel',
+                       profile: {
+                               "name": "firefox",
+                               "layout": "gecko",
+                               "layoutVersion": 20110420,
+                               "platform": "mac",
+                               "version": "3.5.19",
+                               "versionBase": "3",
+                               "versionNumber": 3.5
+                       }
+               },
+               // Firefox 3.6
+               'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.17) Gecko/20110422 Ubuntu/10.10 (maverick) Firefox/3.6.17': {
+                       title: 'Firefox 3.6',
+                       platform: 'Linux i686',
+                       profile: {
+                               "name": "firefox",
+                               "layout": "gecko",
+                               "layoutVersion": 20110422,
+                               "platform": "linux",
+                               "version": "3.6.17",
+                               "versionBase": "3",
+                               "versionNumber": 3.6
+                       }
+               },
+               // Firefox 4
+               'Mozilla/5.0 (Windows NT 6.0; rv:2.0.1) Gecko/20100101 Firefox/4.0.1': {
+                       title: 'Firefox 4',
+                       platform: 'Win32',
+                       profile: {
+                               "name": "firefox",
+                               "layout": "gecko",
+                               "layoutVersion": 20100101,
+                               "platform": "win",
+                               "version": "4.0.1",
+                               "versionBase": "4",
+                               "versionNumber": 4
+                       } 
+               },
+               // Firefox 5
+               // Safari 3
+               // Safari 4
+               'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_7; nl-nl) AppleWebKit/531.22.7 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': {
+                       title: 'Safari 4',
+                       platform: 'MacIntel',
+                       profile: {
+                               "name": "safari",
+                               "layout": "webkit",
+                               "layoutVersion": 531,
+                               "platform": "mac",
+                               "version": "4.0.5",
+                               "versionBase": "4",
+                               "versionNumber": 4
+                       } 
+               },
+               'Mozilla/5.0 (Windows; U; Windows NT 6.0; cs-CZ) AppleWebKit/533.21.1 (KHTML, like Gecko) Version/4.0.5 Safari/531.22.7': {
+                       title: 'Safari 4',
+                       platform: 'Win32',
+                       profile: {
+                               "name": "safari",
+                               "layout": "webkit",
+                               "layoutVersion": 533,
+                               "platform": "win",
+                               "version": "4.0.5",
+                               "versionBase": "4",
+                               "versionNumber": 4
+                       }
+               },
+               // Safari 5
+               // Opera 10
+               // Chrome 5
+               // Chrome 6
+               // Chrome 7
+               // Chrome 8
+               // Chrome 9
+               // Chrome 10
+               // Chrome 11
+               // Chrome 12
+               'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_5_8) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30': {
+                       title: 'Chrome 12',
+                       platform: 'MacIntel',
+                       profile: {
+                               "name": "chrome",
+                               "layout": "webkit",
+                               "layoutVersion": 534,
+                               "platform": "mac",
+                               "version": "12.0.742.112",
+                               "versionBase": "12",
+                               "versionNumber": 12
+                       }
+               },
+               'Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.68 Safari/534.30': {
+                       title: 'Chrome 12',
+                       platform: 'Linux i686',
+                       profile: {
+                               "name": "chrome",
+                               "layout": "webkit",
+                               "layoutVersion": 534,
+                               "platform": "linux",
+                               "version": "12.0.742.68",
+                               "versionBase": "12",
+                               "versionNumber": 12
+                       }
+               }
+       };
+
+       // Generate a client profile object and compare recursively
+       var uaTest = function( rawUserAgent, data ) {
+               var ret = $.client.profile( {
+                       userAgent: rawUserAgent,
+                       platform: data.platform
+               } );
+               deepEqual( ret, data.profile, 'Client profile support check for ' + data.title + ' (' + data.platform + '): ' + rawUserAgent );
+       };
+
+       // Loop through and run tests
+       $.each( uas, uaTest );
+} );
+
+test( 'profile return validation for current user agent', function() {
+       expect(7);
+       var p = $.client.profile();
+       var unknownOrType = function( val, type, summary ) {
+               return ok( typeof val === type || val === 'unknown', summary );
+       };
+
+       equal( typeof p, 'object', 'profile returns an object' );
+       unknownOrType( p.layout, 'string', 'p.layout is a string (or "unknown")' );
+       unknownOrType( p.layoutVersion, 'number', 'p.layoutVersion is a number (or "unknown")' );
+       unknownOrType( p.platform, 'string', 'p.platform is a string (or "unknown")' );
+       unknownOrType( p.version, 'string', 'p.version is a string (or "unknown")' );
+       unknownOrType( p.versionBase, 'string', 'p.versionBase is a string (or "unknown")' );
+       equal( typeof p.versionNumber, 'number', 'p.versionNumber is a number' );
+});
+
+test( 'test', function() {
+       expect(1);
+
+       // Example from WikiEditor
+       var testMap = {
+               'ltr': {
+                       'msie': [['>=', 7]],
+                       'firefox': [['>=', 2]],
+                       'opera': [['>=', 9.6]],
+                       'safari': [['>=', 3]],
+                       'chrome': [['>=', 3]],
+                       'netscape': [['>=', 9]],
+                       'blackberry': false,
+                       'ipod': false,
+                       'iphone': false
+               },
+               'rtl': {
+                       'msie': [['>=', 8]],
+                       'firefox': [['>=', 2]],
+                       'opera': [['>=', 9.6]],
+                       'safari': [['>=', 3]],
+                       'chrome': [['>=', 3]],
+                       'netscape': [['>=', 9]],
+                       'blackberry': false,
+                       'ipod': false,
+                       'iphone': false
+               }
+       };
+       // .test() uses eval, make sure no exceptions are thrown
+       // then do a basic return value type check
+       var testMatch = $.client.test( testMap );
+
+       equal( typeof testMatch, 'boolean', 'test returns a boolean value' );
+
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.colorUtil.js b/tests/qunit/suites/resources/jquery/jquery.colorUtil.js
deleted file mode 100644 (file)
index 93f12b8..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-module( 'jquery.colorUtil.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-       ok( $.colorUtil, '$.colorUtil defined' );
-});
-
-test( 'getRGB', function() {
-       expect(18);
-
-       strictEqual( $.colorUtil.getRGB(), undefined, 'No arguments' );
-       strictEqual( $.colorUtil.getRGB( '' ), undefined, 'Empty string' );
-       deepEqual( $.colorUtil.getRGB( [0, 100, 255] ), [0, 100, 255], 'Parse array of rgb values' );
-       deepEqual( $.colorUtil.getRGB( 'rgb(0,100,255)' ), [0, 100, 255], 'Parse simple rgb string' );
-       deepEqual( $.colorUtil.getRGB( 'rgb(0, 100, 255)' ), [0, 100, 255], 'Parse simple rgb string with spaces' );
-       deepEqual( $.colorUtil.getRGB( 'rgb(0%,20%,40%)' ), [0, 51, 102], 'Parse rgb string with percentages' );
-       deepEqual( $.colorUtil.getRGB( 'rgb(0%, 20%, 40%)' ), [0, 51, 102], 'Parse rgb string with percentages and spaces' );
-       deepEqual( $.colorUtil.getRGB( '#f2ddee' ), [242, 221, 238], 'Hex string: 6 char lowercase' );
-       deepEqual( $.colorUtil.getRGB( '#f2DDEE' ), [242, 221, 238], 'Hex string: 6 char uppercase' );
-       deepEqual( $.colorUtil.getRGB( '#f2DdEe' ), [242, 221, 238], 'Hex string: 6 char mixed' );
-       deepEqual( $.colorUtil.getRGB( '#eee' ), [238, 238, 238], 'Hex string: 3 char lowercase' );
-       deepEqual( $.colorUtil.getRGB( '#EEE' ), [238, 238, 238], 'Hex string: 3 char uppercase' );
-       deepEqual( $.colorUtil.getRGB( '#eEe' ), [238, 238, 238], 'Hex string: 3 char mixed' );
-       deepEqual( $.colorUtil.getRGB( 'rgba(0, 0, 0, 0)' ), [255, 255, 255], 'Zero rgba for Safari 3; Transparent (whitespace)' );
-
-       // Perhaps this is a bug in colorUtil, but it is the current behaviour so, let's keep
-       // track of it, so we will know in case it would ever change.
-       strictEqual( $.colorUtil.getRGB( 'rgba(0,0,0,0)' ), undefined, 'Zero rgba without whitespace' );
-
-       deepEqual( $.colorUtil.getRGB( 'lightGreen' ), [144, 238, 144], 'Color names (lightGreen)' );
-       deepEqual( $.colorUtil.getRGB( 'transparent' ), [255, 255, 255], 'Color names (transparent)' );
-       strictEqual( $.colorUtil.getRGB( 'mediaWiki' ), undefined, 'Inexisting color name' );
-});
-
-test( 'rgbToHsl', function() {
-       expect(1);
-
-       var hsl = $.colorUtil.rgbToHsl( 144, 238, 144 );
-
-       // Cross-browser differences in decimals...
-       // Round to two decimals so they can be more reliably checked.
-       var dualDecimals = function(a,b){
-               return Math.round(a*100)/100;
-       };
-       // Re-create the rgbToHsl return array items, limited to two decimals.
-       var ret = [dualDecimals(hsl[0]), dualDecimals(hsl[1]), dualDecimals(hsl[2])];
-
-       deepEqual( ret, [0.33, 0.73, 0.75], 'rgb(144, 238, 144): hsl(0.33, 0.73, 0.75)' );
-});
-
-test( 'hslToRgb', function() {
-       expect(1);
-
-       var rgb = $.colorUtil.hslToRgb( 0.3, 0.7, 0.8 );
-
-       // Cross-browser differences in decimals...
-       // Re-create the hslToRgb return array items, rounded to whole numbers.
-       var ret = [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])];
-
-       deepEqual( ret ,[183, 240, 168], 'hsl(0.3, 0.7, 0.8): rgb(183, 240, 168)' );
-});
-
-test( 'getColorBrightness', function() {
-       expect(2);
-
-       var a = $.colorUtil.getColorBrightness( 'red', +0.1 );
-       equal( a, 'rgb(255,50,50)', 'Start with named color "red", brighten 10%' );
-
-       var b = $.colorUtil.getColorBrightness( 'rgb(200,50,50)', -0.2 );
-       equal( b, 'rgb(118,29,29)', 'Start with rgb string "rgb(200,50,50)", darken 20%' );
-});
diff --git a/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js b/tests/qunit/suites/resources/jquery/jquery.colorUtil.test.js
new file mode 100644 (file)
index 0000000..88791ca
--- /dev/null
@@ -0,0 +1,71 @@
+module( 'jquery.colorUtil' );
+
+test( '-- Initial check', function() {
+       expect(1);
+       ok( $.colorUtil, '$.colorUtil defined' );
+});
+
+test( 'getRGB', function() {
+       expect(18);
+
+       strictEqual( $.colorUtil.getRGB(), undefined, 'No arguments' );
+       strictEqual( $.colorUtil.getRGB( '' ), undefined, 'Empty string' );
+       deepEqual( $.colorUtil.getRGB( [0, 100, 255] ), [0, 100, 255], 'Parse array of rgb values' );
+       deepEqual( $.colorUtil.getRGB( 'rgb(0,100,255)' ), [0, 100, 255], 'Parse simple rgb string' );
+       deepEqual( $.colorUtil.getRGB( 'rgb(0, 100, 255)' ), [0, 100, 255], 'Parse simple rgb string with spaces' );
+       deepEqual( $.colorUtil.getRGB( 'rgb(0%,20%,40%)' ), [0, 51, 102], 'Parse rgb string with percentages' );
+       deepEqual( $.colorUtil.getRGB( 'rgb(0%, 20%, 40%)' ), [0, 51, 102], 'Parse rgb string with percentages and spaces' );
+       deepEqual( $.colorUtil.getRGB( '#f2ddee' ), [242, 221, 238], 'Hex string: 6 char lowercase' );
+       deepEqual( $.colorUtil.getRGB( '#f2DDEE' ), [242, 221, 238], 'Hex string: 6 char uppercase' );
+       deepEqual( $.colorUtil.getRGB( '#f2DdEe' ), [242, 221, 238], 'Hex string: 6 char mixed' );
+       deepEqual( $.colorUtil.getRGB( '#eee' ), [238, 238, 238], 'Hex string: 3 char lowercase' );
+       deepEqual( $.colorUtil.getRGB( '#EEE' ), [238, 238, 238], 'Hex string: 3 char uppercase' );
+       deepEqual( $.colorUtil.getRGB( '#eEe' ), [238, 238, 238], 'Hex string: 3 char mixed' );
+       deepEqual( $.colorUtil.getRGB( 'rgba(0, 0, 0, 0)' ), [255, 255, 255], 'Zero rgba for Safari 3; Transparent (whitespace)' );
+
+       // Perhaps this is a bug in colorUtil, but it is the current behaviour so, let's keep
+       // track of it, so we will know in case it would ever change.
+       strictEqual( $.colorUtil.getRGB( 'rgba(0,0,0,0)' ), undefined, 'Zero rgba without whitespace' );
+
+       deepEqual( $.colorUtil.getRGB( 'lightGreen' ), [144, 238, 144], 'Color names (lightGreen)' );
+       deepEqual( $.colorUtil.getRGB( 'transparent' ), [255, 255, 255], 'Color names (transparent)' );
+       strictEqual( $.colorUtil.getRGB( 'mediaWiki' ), undefined, 'Inexisting color name' );
+});
+
+test( 'rgbToHsl', function() {
+       expect(1);
+
+       var hsl = $.colorUtil.rgbToHsl( 144, 238, 144 );
+
+       // Cross-browser differences in decimals...
+       // Round to two decimals so they can be more reliably checked.
+       var dualDecimals = function(a,b){
+               return Math.round(a*100)/100;
+       };
+       // Re-create the rgbToHsl return array items, limited to two decimals.
+       var ret = [dualDecimals(hsl[0]), dualDecimals(hsl[1]), dualDecimals(hsl[2])];
+
+       deepEqual( ret, [0.33, 0.73, 0.75], 'rgb(144, 238, 144): hsl(0.33, 0.73, 0.75)' );
+});
+
+test( 'hslToRgb', function() {
+       expect(1);
+
+       var rgb = $.colorUtil.hslToRgb( 0.3, 0.7, 0.8 );
+
+       // Cross-browser differences in decimals...
+       // Re-create the hslToRgb return array items, rounded to whole numbers.
+       var ret = [Math.round(rgb[0]), Math.round(rgb[1]), Math.round(rgb[2])];
+
+       deepEqual( ret ,[183, 240, 168], 'hsl(0.3, 0.7, 0.8): rgb(183, 240, 168)' );
+});
+
+test( 'getColorBrightness', function() {
+       expect(2);
+
+       var a = $.colorUtil.getColorBrightness( 'red', +0.1 );
+       equal( a, 'rgb(255,50,50)', 'Start with named color "red", brighten 10%' );
+
+       var b = $.colorUtil.getColorBrightness( 'rgb(200,50,50)', -0.2 );
+       equal( b, 'rgb(118,29,29)', 'Start with rgb string "rgb(200,50,50)", darken 20%' );
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.getAttrs.js b/tests/qunit/suites/resources/jquery/jquery.getAttrs.js
deleted file mode 100644 (file)
index 3d3d01e..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-module( 'jquery.getAttrs.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-       ok( $.fn.getAttrs, 'jQuery.fn.getAttrs defined' );
-} );
-
-test( 'Check', function() {
-       expect(1);
-       var     attrs = {
-                       foo: 'bar',
-                       'class': 'lorem'
-               },
-               $el = $( '<div>', attrs );
-
-       deepEqual( $el.getAttrs(), attrs, 'getAttrs() return object should match the attributes set, no more, no less' );
-} );
diff --git a/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js b/tests/qunit/suites/resources/jquery/jquery.getAttrs.test.js
new file mode 100644 (file)
index 0000000..5685756
--- /dev/null
@@ -0,0 +1,17 @@
+module( 'jquery.getAttrs' );
+
+test( '-- Initial check', function() {
+       expect(1);
+       ok( $.fn.getAttrs, 'jQuery.fn.getAttrs defined' );
+} );
+
+test( 'Check', function() {
+       expect(1);
+       var     attrs = {
+                       foo: 'bar',
+                       'class': 'lorem'
+               },
+               $el = $( '<div>', attrs );
+
+       deepEqual( $el.getAttrs(), attrs, 'getAttrs() return object should match the attributes set, no more, no less' );
+} );
diff --git a/tests/qunit/suites/resources/jquery/jquery.localize.js b/tests/qunit/suites/resources/jquery/jquery.localize.js
deleted file mode 100644 (file)
index 40b5868..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-module( 'jquery.localize.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-       ok( $.fn.localize, 'jQuery.fn.localize defined' );
-} );
-
-test( 'Handle basic replacements', function() {
-       expect(3);
-
-       var html, $lc;
-       mw.messages.set( 'basic', 'Basic stuff' );
-
-       // Tag: html:msg
-       html = '<div><span><html:msg key="basic" /></span></div>';
-       $lc = $( html ).localize().find( 'span' );
-
-       strictEqual( $lc.text(), 'Basic stuff', 'Tag: html:msg' );
-
-       // Attribute: title-msg
-       html = '<div><span title-msg="basic" /></span></div>';
-       $lc = $( html ).localize().find( 'span' );
-
-       strictEqual( $lc.attr( 'title' ), 'Basic stuff', 'Attribute: title-msg' );
-
-       // Attribute: alt-msg
-       html = '<div><span alt-msg="basic" /></span></div>';
-       $lc = $( html ).localize().find( 'span' );
-
-       strictEqual( $lc.attr( 'alt' ), 'Basic stuff', 'Attribute: alt-msg' );
-} );
-
-test( 'Proper escaping', function() {
-       expect(2);
-
-       var html, $lc;
-       mw.messages.set( 'properfoo', '<proper esc="test">' );
-
-       // This is handled by jQuery inside $.fn.localize, just a simple sanity checked
-       // making sure it is actually using text() and attr() (or something with the same effect)
-
-       // Text escaping
-       html = '<div><span><html:msg key="properfoo" /></span></div>';
-       $lc = $( html ).localize().find( 'span' );
-
-       strictEqual( $lc.text(), mw.msg( 'properfoo' ), 'Content is inserted as text, not as html.' );
-
-       // Attribute escaping
-       html = '<div><span title-msg="properfoo" /></span></div>';
-       $lc = $( html ).localize().find( 'span' );
-
-       strictEqual( $lc.attr( 'title' ), mw.msg( 'properfoo' ), 'Attributes are not inserted raw.' );
-} );
-
-test( 'Options', function() {
-       expect(7);
-
-       mw.messages.set( {
-               'foo-lorem': 'Lorem',
-               'foo-ipsum': 'Ipsum',
-               'foo-bar-title': 'Read more about bars',
-               'foo-bar-label': 'The Bars',
-               'foo-bazz-title': 'Read more about bazz at $1 (last modified: $2)',
-               'foo-bazz-label': 'The Bazz ($1)',
-               'foo-welcome': 'Welcome to $1! (last visit: $2)'
-       } );
-       var html, $lc, attrs, x, sitename = 'Wikipedia';
-
-       // Message key prefix
-       html = '<div><span title-msg="lorem"><html:msg key="ipsum" /></span></div>';
-       $lc = $( html ).localize( {
-               prefix: 'foo-'
-       } ).find( 'span' );
-
-       strictEqual( $lc.attr( 'title' ), 'Lorem', 'Message key prefix - attr' );
-       strictEqual( $lc.text(), 'Ipsum', 'Message key prefix - text' );
-
-       // Variable keys mapping
-       x = 'bar';
-       html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
-       $lc = $( html ).localize( {
-               keys: {
-                       'title': 'foo-' + x + '-title',
-                       'label': 'foo-' + x + '-label'
-               }
-       } ).find( 'span' );
-
-       strictEqual( $lc.attr( 'title' ), 'Read more about bars', 'Variable keys mapping - attr' );
-       strictEqual( $lc.text(), 'The Bars', 'Variable keys mapping - text' );
-
-       // Passing parameteters to mw.msg
-       html = '<div><span><html:msg key="foo-welcome" /></span></div>';
-       $lc = $( html ).localize( {
-               params: {
-                       'foo-welcome': [sitename, 'yesterday']
-               }
-       } ).find( 'span' );
-
-       strictEqual( $lc.text(), 'Welcome to Wikipedia! (last visit: yesterday)', 'Passing parameteters to mw.msg' );
-
-       // Combination of options prefix, params and keys
-       x = 'bazz';
-       html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
-       $lc = $( html ).localize( {
-               prefix: 'foo-',
-               keys: {
-                       'title': x + '-title',
-                       'label': x + '-label'
-               },
-               params: {
-                       'title': [sitename, '3 minutes ago'],
-                       'label': [sitename, '3 minutes ago']
-
-               }
-       } ).find( 'span' );
-
-       strictEqual( $lc.text(), 'The Bazz (Wikipedia)', 'Combination of options prefix, params and keys - text' );
-       strictEqual( $lc.attr( 'title' ), 'Read more about bazz at Wikipedia (last modified: 3 minutes ago)', 'Combination of options prefix, params and keys - attr' );
-} );
diff --git a/tests/qunit/suites/resources/jquery/jquery.localize.test.js b/tests/qunit/suites/resources/jquery/jquery.localize.test.js
new file mode 100644 (file)
index 0000000..c5d8c4d
--- /dev/null
@@ -0,0 +1,119 @@
+module( 'jquery.localize' );
+
+test( '-- Initial check', function() {
+       expect(1);
+       ok( $.fn.localize, 'jQuery.fn.localize defined' );
+} );
+
+test( 'Handle basic replacements', function() {
+       expect(3);
+
+       var html, $lc;
+       mw.messages.set( 'basic', 'Basic stuff' );
+
+       // Tag: html:msg
+       html = '<div><span><html:msg key="basic" /></span></div>';
+       $lc = $( html ).localize().find( 'span' );
+
+       strictEqual( $lc.text(), 'Basic stuff', 'Tag: html:msg' );
+
+       // Attribute: title-msg
+       html = '<div><span title-msg="basic" /></span></div>';
+       $lc = $( html ).localize().find( 'span' );
+
+       strictEqual( $lc.attr( 'title' ), 'Basic stuff', 'Attribute: title-msg' );
+
+       // Attribute: alt-msg
+       html = '<div><span alt-msg="basic" /></span></div>';
+       $lc = $( html ).localize().find( 'span' );
+
+       strictEqual( $lc.attr( 'alt' ), 'Basic stuff', 'Attribute: alt-msg' );
+} );
+
+test( 'Proper escaping', function() {
+       expect(2);
+
+       var html, $lc;
+       mw.messages.set( 'properfoo', '<proper esc="test">' );
+
+       // This is handled by jQuery inside $.fn.localize, just a simple sanity checked
+       // making sure it is actually using text() and attr() (or something with the same effect)
+
+       // Text escaping
+       html = '<div><span><html:msg key="properfoo" /></span></div>';
+       $lc = $( html ).localize().find( 'span' );
+
+       strictEqual( $lc.text(), mw.msg( 'properfoo' ), 'Content is inserted as text, not as html.' );
+
+       // Attribute escaping
+       html = '<div><span title-msg="properfoo" /></span></div>';
+       $lc = $( html ).localize().find( 'span' );
+
+       strictEqual( $lc.attr( 'title' ), mw.msg( 'properfoo' ), 'Attributes are not inserted raw.' );
+} );
+
+test( 'Options', function() {
+       expect(7);
+
+       mw.messages.set( {
+               'foo-lorem': 'Lorem',
+               'foo-ipsum': 'Ipsum',
+               'foo-bar-title': 'Read more about bars',
+               'foo-bar-label': 'The Bars',
+               'foo-bazz-title': 'Read more about bazz at $1 (last modified: $2)',
+               'foo-bazz-label': 'The Bazz ($1)',
+               'foo-welcome': 'Welcome to $1! (last visit: $2)'
+       } );
+       var html, $lc, attrs, x, sitename = 'Wikipedia';
+
+       // Message key prefix
+       html = '<div><span title-msg="lorem"><html:msg key="ipsum" /></span></div>';
+       $lc = $( html ).localize( {
+               prefix: 'foo-'
+       } ).find( 'span' );
+
+       strictEqual( $lc.attr( 'title' ), 'Lorem', 'Message key prefix - attr' );
+       strictEqual( $lc.text(), 'Ipsum', 'Message key prefix - text' );
+
+       // Variable keys mapping
+       x = 'bar';
+       html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
+       $lc = $( html ).localize( {
+               keys: {
+                       'title': 'foo-' + x + '-title',
+                       'label': 'foo-' + x + '-label'
+               }
+       } ).find( 'span' );
+
+       strictEqual( $lc.attr( 'title' ), 'Read more about bars', 'Variable keys mapping - attr' );
+       strictEqual( $lc.text(), 'The Bars', 'Variable keys mapping - text' );
+
+       // Passing parameteters to mw.msg
+       html = '<div><span><html:msg key="foo-welcome" /></span></div>';
+       $lc = $( html ).localize( {
+               params: {
+                       'foo-welcome': [sitename, 'yesterday']
+               }
+       } ).find( 'span' );
+
+       strictEqual( $lc.text(), 'Welcome to Wikipedia! (last visit: yesterday)', 'Passing parameteters to mw.msg' );
+
+       // Combination of options prefix, params and keys
+       x = 'bazz';
+       html = '<div><span title-msg="title"><html:msg key="label" /></span></div>';
+       $lc = $( html ).localize( {
+               prefix: 'foo-',
+               keys: {
+                       'title': x + '-title',
+                       'label': x + '-label'
+               },
+               params: {
+                       'title': [sitename, '3 minutes ago'],
+                       'label': [sitename, '3 minutes ago']
+
+               }
+       } ).find( 'span' );
+
+       strictEqual( $lc.text(), 'The Bazz (Wikipedia)', 'Combination of options prefix, params and keys - text' );
+       strictEqual( $lc.attr( 'title' ), 'Read more about bazz at Wikipedia (last modified: 3 minutes ago)', 'Combination of options prefix, params and keys - attr' );
+} );
diff --git a/tests/qunit/suites/resources/jquery/jquery.mwExtension.js b/tests/qunit/suites/resources/jquery/jquery.mwExtension.js
deleted file mode 100644 (file)
index f68bd58..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-module( 'jquery.mwExtension.js' );
-
-test( 'String functions', function() {
-
-       equal( $.trimLeft( '  foo bar  ' ), 'foo bar  ', 'trimLeft' );
-       equal( $.trimRight( '  foo bar  ' ), '  foo bar', 'trimRight' );
-       equal( $.ucFirst( 'foo'), 'Foo', 'ucFirst' );
-
-       equal( $.escapeRE( '<!-- ([{+mW+}]) $^|?>' ),
-        '<!\\-\\- \\(\\[\\{\\+mW\\+\\}\\]\\) \\$\\^\\|\\?>', 'escapeRE - Escape specials' );
-       equal( $.escapeRE( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ),
-        'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'escapeRE - Leave uppercase alone' );
-       equal( $.escapeRE( 'abcdefghijklmnopqrstuvwxyz' ),
-        'abcdefghijklmnopqrstuvwxyz', 'escapeRE - Leave lowercase alone' );
-       equal( $.escapeRE( '0123456789' ), '0123456789', 'escapeRE - Leave numbers alone' );
-});
-
-test( 'Is functions', function() {
-
-       strictEqual( $.isDomElement( document.getElementById( 'qunit-header' ) ), true,
-        'isDomElement: #qunit-header Node' );
-       strictEqual( $.isDomElement( document.getElementById( 'random-name' ) ), false,
-        'isDomElement: #random-name (null)' );
-       strictEqual( $.isDomElement( document.getElementsByTagName( 'div' ) ), false,
-        'isDomElement: getElementsByTagName Array' );
-       strictEqual( $.isDomElement( document.getElementsByTagName( 'div' )[0] ), true,
-        'isDomElement: getElementsByTagName(..)[0] Node' );
-       strictEqual( $.isDomElement( $( 'div' ) ), false,
-        'isDomElement: jQuery object' );
-       strictEqual( $.isDomElement( $( 'div' ).get(0) ), true,
-        'isDomElement: jQuery object > Get node' );
-       strictEqual( $.isDomElement( document.createElement( 'div' ) ), true,
-        'isDomElement: createElement' );
-       strictEqual( $.isDomElement( { foo: 1 } ), false,
-        'isDomElement: Object' );
-
-       strictEqual( $.isEmpty( 'string' ), false, 'isEmptry: "string"' );
-       strictEqual( $.isEmpty( '0' ), true, 'isEmptry: "0"' );
-       strictEqual( $.isEmpty( [] ), true, 'isEmptry: []' );
-       strictEqual( $.isEmpty( {} ), true, 'isEmptry: {}' );
-
-       // Documented behaviour
-       strictEqual( $.isEmpty( { length: 0 } ), true, 'isEmptry: { length: 0 }' );
-});
-
-test( 'Comparison functions', function() {
-
-       ok( $.compareArray( [0, 'a', [], [2, 'b'] ], [0, "a", [], [2, "b"] ] ),
-        'compareArray: Two deep arrays that are excactly the same' );
-       ok( !$.compareArray( [1], [2] ), 'compareArray: Two different arrays (false)' );
-
-       ok( $.compareObject( {}, {} ), 'compareObject: Two empty objects' );
-       ok( $.compareObject( { foo: 1 }, { foo: 1 } ), 'compareObject: Two the same objects' );
-       ok( !$.compareObject( { bar: true }, { baz: false } ),
-        'compareObject: Two different objects (false)' );
-});
diff --git a/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js b/tests/qunit/suites/resources/jquery/jquery.mwExtension.test.js
new file mode 100644 (file)
index 0000000..09e981e
--- /dev/null
@@ -0,0 +1,56 @@
+module( 'jquery.mwExtension' );
+
+test( 'String functions', function() {
+
+       equal( $.trimLeft( '  foo bar  ' ), 'foo bar  ', 'trimLeft' );
+       equal( $.trimRight( '  foo bar  ' ), '  foo bar', 'trimRight' );
+       equal( $.ucFirst( 'foo'), 'Foo', 'ucFirst' );
+
+       equal( $.escapeRE( '<!-- ([{+mW+}]) $^|?>' ),
+        '<!\\-\\- \\(\\[\\{\\+mW\\+\\}\\]\\) \\$\\^\\|\\?>', 'escapeRE - Escape specials' );
+       equal( $.escapeRE( 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' ),
+        'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'escapeRE - Leave uppercase alone' );
+       equal( $.escapeRE( 'abcdefghijklmnopqrstuvwxyz' ),
+        'abcdefghijklmnopqrstuvwxyz', 'escapeRE - Leave lowercase alone' );
+       equal( $.escapeRE( '0123456789' ), '0123456789', 'escapeRE - Leave numbers alone' );
+});
+
+test( 'Is functions', function() {
+
+       strictEqual( $.isDomElement( document.getElementById( 'qunit-header' ) ), true,
+        'isDomElement: #qunit-header Node' );
+       strictEqual( $.isDomElement( document.getElementById( 'random-name' ) ), false,
+        'isDomElement: #random-name (null)' );
+       strictEqual( $.isDomElement( document.getElementsByTagName( 'div' ) ), false,
+        'isDomElement: getElementsByTagName Array' );
+       strictEqual( $.isDomElement( document.getElementsByTagName( 'div' )[0] ), true,
+        'isDomElement: getElementsByTagName(..)[0] Node' );
+       strictEqual( $.isDomElement( $( 'div' ) ), false,
+        'isDomElement: jQuery object' );
+       strictEqual( $.isDomElement( $( 'div' ).get(0) ), true,
+        'isDomElement: jQuery object > Get node' );
+       strictEqual( $.isDomElement( document.createElement( 'div' ) ), true,
+        'isDomElement: createElement' );
+       strictEqual( $.isDomElement( { foo: 1 } ), false,
+        'isDomElement: Object' );
+
+       strictEqual( $.isEmpty( 'string' ), false, 'isEmptry: "string"' );
+       strictEqual( $.isEmpty( '0' ), true, 'isEmptry: "0"' );
+       strictEqual( $.isEmpty( [] ), true, 'isEmptry: []' );
+       strictEqual( $.isEmpty( {} ), true, 'isEmptry: {}' );
+
+       // Documented behaviour
+       strictEqual( $.isEmpty( { length: 0 } ), true, 'isEmptry: { length: 0 }' );
+});
+
+test( 'Comparison functions', function() {
+
+       ok( $.compareArray( [0, 'a', [], [2, 'b'] ], [0, "a", [], [2, "b"] ] ),
+        'compareArray: Two deep arrays that are excactly the same' );
+       ok( !$.compareArray( [1], [2] ), 'compareArray: Two different arrays (false)' );
+
+       ok( $.compareObject( {}, {} ), 'compareObject: Two empty objects' );
+       ok( $.compareObject( { foo: 1 }, { foo: 1 } ), 'compareObject: Two the same objects' );
+       ok( !$.compareObject( { bar: true }, { baz: false } ),
+        'compareObject: Two different objects (false)' );
+});
diff --git a/tests/qunit/suites/resources/jquery/jquery.tabIndex.js b/tests/qunit/suites/resources/jquery/jquery.tabIndex.js
deleted file mode 100644 (file)
index 1ff81e5..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-module( 'jquery.tabIndex.js' );
-
-test( '-- Initial check', function() {
-       expect(2);
-
-       ok( $.fn.firstTabIndex, '$.fn.firstTabIndex defined' );
-       ok( $.fn.lastTabIndex, '$.fn.lastTabIndex defined' );
-});
-
-test( 'firstTabIndex', function() {
-       expect(2);
-
-       var testEnvironment =
-'<form>' +
-       '<input tabindex="7" />' +
-       '<input tabindex="9" />' +
-       '<textarea tabindex="2">Foobar</textarea>' +
-       '<textarea tabindex="5">Foobar</textarea>' +
-'</form>';
-
-       var $testA = $( '<div>' ).html( testEnvironment ).appendTo( 'body' );
-       strictEqual( $testA.firstTabIndex(), 2, 'First tabindex should be 2 within this context.' );
-
-       var $testB = $( '<div>' );
-       strictEqual( $testB.firstTabIndex(), null, 'Return null if none available.' );
-
-       // Clean up
-       $testA.add( $testB ).remove();
-});
-
-test( 'lastTabIndex', function() {
-       expect(2);
-
-       var testEnvironment =
-'<form>' +
-       '<input tabindex="7" />' +
-       '<input tabindex="9" />' +
-       '<textarea tabindex="2">Foobar</textarea>' +
-       '<textarea tabindex="5">Foobar</textarea>' +
-'</form>';
-
-       var $testA = $( '<div>' ).html( testEnvironment ).appendTo( 'body' );
-       strictEqual( $testA.lastTabIndex(), 9, 'Last tabindex should be 9 within this context.' );
-
-       var $testB = $( '<div>' );
-       strictEqual( $testB.lastTabIndex(), null, 'Return null if none available.' );
-
-       // Clean up
-       $testA.add( $testB ).remove();
-});
diff --git a/tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js b/tests/qunit/suites/resources/jquery/jquery.tabIndex.test.js
new file mode 100644 (file)
index 0000000..7d014f3
--- /dev/null
@@ -0,0 +1,50 @@
+module( 'jquery.tabIndex' );
+
+test( '-- Initial check', function() {
+       expect(2);
+
+       ok( $.fn.firstTabIndex, '$.fn.firstTabIndex defined' );
+       ok( $.fn.lastTabIndex, '$.fn.lastTabIndex defined' );
+});
+
+test( 'firstTabIndex', function() {
+       expect(2);
+
+       var testEnvironment =
+'<form>' +
+       '<input tabindex="7" />' +
+       '<input tabindex="9" />' +
+       '<textarea tabindex="2">Foobar</textarea>' +
+       '<textarea tabindex="5">Foobar</textarea>' +
+'</form>';
+
+       var $testA = $( '<div>' ).html( testEnvironment ).appendTo( 'body' );
+       strictEqual( $testA.firstTabIndex(), 2, 'First tabindex should be 2 within this context.' );
+
+       var $testB = $( '<div>' );
+       strictEqual( $testB.firstTabIndex(), null, 'Return null if none available.' );
+
+       // Clean up
+       $testA.add( $testB ).remove();
+});
+
+test( 'lastTabIndex', function() {
+       expect(2);
+
+       var testEnvironment =
+'<form>' +
+       '<input tabindex="7" />' +
+       '<input tabindex="9" />' +
+       '<textarea tabindex="2">Foobar</textarea>' +
+       '<textarea tabindex="5">Foobar</textarea>' +
+'</form>';
+
+       var $testA = $( '<div>' ).html( testEnvironment ).appendTo( 'body' );
+       strictEqual( $testA.lastTabIndex(), 9, 'Last tabindex should be 9 within this context.' );
+
+       var $testB = $( '<div>' );
+       strictEqual( $testB.lastTabIndex(), null, 'Return null if none available.' );
+
+       // Clean up
+       $testA.add( $testB ).remove();
+});
index b798432..2efd6cd 100644 (file)
@@ -1,6 +1,6 @@
 (function() {
 
-module( 'jquery.tablesorter.test.js' );
+module( 'jquery.tablesorter' );
 
 // setup hack
 mw.config.set('wgMonthNames', window.wgMonthNames = ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']);
diff --git a/tests/qunit/suites/resources/jquery/jquery.textSelection.js b/tests/qunit/suites/resources/jquery/jquery.textSelection.js
deleted file mode 100644 (file)
index c684886..0000000
+++ /dev/null
@@ -1,225 +0,0 @@
-module( 'jquery.textSelection.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-       ok( $.fn.textSelection, 'jQuery.fn.textSelection defined' );
-} );
-
-/**
- * Test factory for $.fn.textSelection('encapsulateText')
- *
- * @param options {object} associative array containing:
- *   description {string}
- *   input {string}
- *   output {string}
- *   start {int} starting char for selection
- *   end {int} ending char for selection
- *   params {object} add'l parameters for $().textSelection('encapsulateText')
- */
-var encapsulateTest = function( options ) {
-       var opt = $.extend({
-               description: '',
-               before: {},
-               after: {},
-               replace: {}
-       }, options);
-
-       opt.before = $.extend({
-               text: '',
-               start: 0,
-               end: 0
-       }, opt.before);
-       opt.after = $.extend({
-               text: '',
-               selected: null
-       }, opt.after);
-
-       test( opt.description, function() {
-               var tests = 1;
-               if (opt.after.selected !== null) {
-                       tests++;
-               }
-               expect(tests);
-
-               var $fixture = $('<div id="qunit-fixture"></div>');
-               var $textarea = $('<textarea>');
-
-               $fixture.append($textarea);
-               $('body').append($fixture);
-
-               //$textarea.textSelection( 'setContents', opt.before.text); // this method is actually missing atm...
-               $textarea.val( opt.before.text ); // won't work with the WikiEditor iframe?
-
-               var     start = opt.before.start,
-                       end = opt.before.end;
-               if ( window.opera ) {
-                       // Compensate for Opera's craziness converting "\n" to "\r\n" and counting that as two chars
-                       var     newLinesBefore = opt.before.text.substring( 0, start ).split( "\n" ).length - 1,
-                               newLinesInside = opt.before.text.substring( start, end ).split( "\n" ).length - 1;
-                       start += newLinesBefore;
-                       end += newLinesBefore + newLinesInside;
-               }
-               $textarea.textSelection( 'setSelection', {
-                       start: start,
-                       end: end
-               });
-
-               $textarea.textSelection( 'encapsulateSelection', opt.replace );
-
-               var text = $textarea.textSelection( 'getContents' ).replace( /\r\n/g, "\n" );
-
-               equal( text, opt.after.text, 'Checking full text after encapsulation' );
-
-               if (opt.after.selected !== null) {
-                       var selected = $textarea.textSelection( 'getSelection' );
-                       equal( selected, opt.after.selected, 'Checking selected text after encapsulation.' );
-               }
-
-       } );
-};
-
-var sig = {
-       'pre': "--~~~~"
-}, bold = {
-       pre: "'''",
-       peri: 'Bold text',
-       post: "'''"
-}, h2 = {
-       'pre': '== ',
-       'peri': 'Heading 2',
-       'post': ' ==',
-       'regex': /^(\s*)(={1,6})(.*?)\2(\s*)$/,
-       'regexReplace': "\$1==\$3==\$4",
-       'ownline': true
-}, ulist = {
-       'pre': "* ",
-       'peri': 'Bulleted list item',
-       'post': "",
-       'ownline': true,
-       'splitlines': true
-};
-
-encapsulateTest({
-       description: "Adding sig to end of text",
-       before: {
-               text: "Wikilove dude! ",
-               start: 15,
-               end: 15
-       },
-       after: {
-               text: "Wikilove dude! --~~~~",
-               selected: ""
-       },
-       replace: sig
-});
-
-encapsulateTest({
-       description: "Adding bold to empty",
-       before: {
-               text: "",
-               start: 0,
-               end: 0
-       },
-       after: {
-               text: "'''Bold text'''",
-               selected: "Bold text" // selected because it's the default
-       },
-       replace: bold
-});
-
-encapsulateTest({
-       description: "Adding bold to existing text",
-       before: {
-               text: "Now is the time for all good men to come to the aid of their country",
-               start: 20,
-               end: 32
-       },
-       after: {
-               text: "Now is the time for '''all good men''' to come to the aid of their country",
-               selected: "" // empty because it's not the default'
-       },
-       replace: bold
-});
-
-encapsulateTest({
-       description: "ownline option: adding new h2",
-       before: {
-               text:"Before\nAfter",
-               start: 7,
-               end: 7
-       },
-       after: {
-               text: "Before\n== Heading 2 ==\nAfter",
-               selected: "Heading 2"
-       },
-       replace: h2
-});
-
-encapsulateTest({
-       description: "ownline option: turn a whole line into new h2",
-       before: {
-               text:"Before\nMy heading\nAfter",
-               start: 7,
-               end: 17
-       },
-       after: {
-               text: "Before\n== My heading ==\nAfter",
-               selected: ""
-       },
-       replace: h2
-});
-
-
-encapsulateTest({
-       description: "ownline option: turn a partial line into new h2",
-       before: {
-               text:"BeforeMy headingAfter",
-               start: 6,
-               end: 16
-       },
-       after: {
-               text: "Before\n== My heading ==\nAfter",
-               selected: ""
-       },
-       replace: h2
-});
-
-
-encapsulateTest({
-       description: "splitlines option: no selection, insert new list item",
-       before: {
-               text: "Before\nAfter",
-               start: 7,
-               end: 7
-       },
-       after: {
-               text: "Before\n* Bulleted list item\nAfter"
-       },
-       replace: ulist
-});
-
-encapsulateTest({
-       description: "splitlines option: single partial line selection, insert new list item",
-       before: {
-               text: "BeforeMy List ItemAfter",
-               start: 6,
-               end: 18
-       },
-       after: {
-               text: "Before\n* My List Item\nAfter"
-       },
-       replace: ulist
-});
-
-encapsulateTest({
-       description: "splitlines option: multiple lines",
-       before: {
-               text: "Before\nFirst\nSecond\nThird\nAfter",
-               start: 7,
-               end: 25
-       },
-       after: {
-               text: "Before\n* First\n* Second\n* Third\nAfter"
-       },
-       replace: ulist
-});
diff --git a/tests/qunit/suites/resources/jquery/jquery.textSelection.test.js b/tests/qunit/suites/resources/jquery/jquery.textSelection.test.js
new file mode 100644 (file)
index 0000000..cea91c8
--- /dev/null
@@ -0,0 +1,225 @@
+module( 'jquery.textSelection' );
+
+test( '-- Initial check', function() {
+       expect(1);
+       ok( $.fn.textSelection, 'jQuery.fn.textSelection defined' );
+} );
+
+/**
+ * Test factory for $.fn.textSelection('encapsulateText')
+ *
+ * @param options {object} associative array containing:
+ *   description {string}
+ *   input {string}
+ *   output {string}
+ *   start {int} starting char for selection
+ *   end {int} ending char for selection
+ *   params {object} add'l parameters for $().textSelection('encapsulateText')
+ */
+var encapsulateTest = function( options ) {
+       var opt = $.extend({
+               description: '',
+               before: {},
+               after: {},
+               replace: {}
+       }, options);
+
+       opt.before = $.extend({
+               text: '',
+               start: 0,
+               end: 0
+       }, opt.before);
+       opt.after = $.extend({
+               text: '',
+               selected: null
+       }, opt.after);
+
+       test( opt.description, function() {
+               var tests = 1;
+               if (opt.after.selected !== null) {
+                       tests++;
+               }
+               expect(tests);
+
+               var $fixture = $('<div id="qunit-fixture"></div>');
+               var $textarea = $('<textarea>');
+
+               $fixture.append($textarea);
+               $('body').append($fixture);
+
+               //$textarea.textSelection( 'setContents', opt.before.text); // this method is actually missing atm...
+               $textarea.val( opt.before.text ); // won't work with the WikiEditor iframe?
+
+               var     start = opt.before.start,
+                       end = opt.before.end;
+               if ( window.opera ) {
+                       // Compensate for Opera's craziness converting "\n" to "\r\n" and counting that as two chars
+                       var     newLinesBefore = opt.before.text.substring( 0, start ).split( "\n" ).length - 1,
+                               newLinesInside = opt.before.text.substring( start, end ).split( "\n" ).length - 1;
+                       start += newLinesBefore;
+                       end += newLinesBefore + newLinesInside;
+               }
+               $textarea.textSelection( 'setSelection', {
+                       start: start,
+                       end: end
+               });
+
+               $textarea.textSelection( 'encapsulateSelection', opt.replace );
+
+               var text = $textarea.textSelection( 'getContents' ).replace( /\r\n/g, "\n" );
+
+               equal( text, opt.after.text, 'Checking full text after encapsulation' );
+
+               if (opt.after.selected !== null) {
+                       var selected = $textarea.textSelection( 'getSelection' );
+                       equal( selected, opt.after.selected, 'Checking selected text after encapsulation.' );
+               }
+
+       } );
+};
+
+var sig = {
+       'pre': "--~~~~"
+}, bold = {
+       pre: "'''",
+       peri: 'Bold text',
+       post: "'''"
+}, h2 = {
+       'pre': '== ',
+       'peri': 'Heading 2',
+       'post': ' ==',
+       'regex': /^(\s*)(={1,6})(.*?)\2(\s*)$/,
+       'regexReplace': "\$1==\$3==\$4",
+       'ownline': true
+}, ulist = {
+       'pre': "* ",
+       'peri': 'Bulleted list item',
+       'post': "",
+       'ownline': true,
+       'splitlines': true
+};
+
+encapsulateTest({
+       description: "Adding sig to end of text",
+       before: {
+               text: "Wikilove dude! ",
+               start: 15,
+               end: 15
+       },
+       after: {
+               text: "Wikilove dude! --~~~~",
+               selected: ""
+       },
+       replace: sig
+});
+
+encapsulateTest({
+       description: "Adding bold to empty",
+       before: {
+               text: "",
+               start: 0,
+               end: 0
+       },
+       after: {
+               text: "'''Bold text'''",
+               selected: "Bold text" // selected because it's the default
+       },
+       replace: bold
+});
+
+encapsulateTest({
+       description: "Adding bold to existing text",
+       before: {
+               text: "Now is the time for all good men to come to the aid of their country",
+               start: 20,
+               end: 32
+       },
+       after: {
+               text: "Now is the time for '''all good men''' to come to the aid of their country",
+               selected: "" // empty because it's not the default'
+       },
+       replace: bold
+});
+
+encapsulateTest({
+       description: "ownline option: adding new h2",
+       before: {
+               text:"Before\nAfter",
+               start: 7,
+               end: 7
+       },
+       after: {
+               text: "Before\n== Heading 2 ==\nAfter",
+               selected: "Heading 2"
+       },
+       replace: h2
+});
+
+encapsulateTest({
+       description: "ownline option: turn a whole line into new h2",
+       before: {
+               text:"Before\nMy heading\nAfter",
+               start: 7,
+               end: 17
+       },
+       after: {
+               text: "Before\n== My heading ==\nAfter",
+               selected: ""
+       },
+       replace: h2
+});
+
+
+encapsulateTest({
+       description: "ownline option: turn a partial line into new h2",
+       before: {
+               text:"BeforeMy headingAfter",
+               start: 6,
+               end: 16
+       },
+       after: {
+               text: "Before\n== My heading ==\nAfter",
+               selected: ""
+       },
+       replace: h2
+});
+
+
+encapsulateTest({
+       description: "splitlines option: no selection, insert new list item",
+       before: {
+               text: "Before\nAfter",
+               start: 7,
+               end: 7
+       },
+       after: {
+               text: "Before\n* Bulleted list item\nAfter"
+       },
+       replace: ulist
+});
+
+encapsulateTest({
+       description: "splitlines option: single partial line selection, insert new list item",
+       before: {
+               text: "BeforeMy List ItemAfter",
+               start: 6,
+               end: 18
+       },
+       after: {
+               text: "Before\n* My List Item\nAfter"
+       },
+       replace: ulist
+});
+
+encapsulateTest({
+       description: "splitlines option: multiple lines",
+       before: {
+               text: "Before\nFirst\nSecond\nThird\nAfter",
+               start: 7,
+               end: 25
+       },
+       after: {
+               text: "Before\n* First\n* Second\n* Third\nAfter"
+       },
+       replace: ulist
+});
diff --git a/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js b/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js
deleted file mode 100644 (file)
index bcc9b96..0000000
+++ /dev/null
@@ -1,71 +0,0 @@
-module( 'mediawiki.special.recentchanges.js' );
-
-test( '-- Initial check', function() {
-       expect( 2 );
-       ok( mw.special.recentchanges.init,
-          'mw.special.recentchanges.init defined'
-         );
-       ok( mw.special.recentchanges.updateCheckboxes,
-          'mw.special.recentchanges.updateCheckboxes defined'
-         );
-       // TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ]
-});
-
-test( '"all" namespace disable checkboxes', function() {
-
-       // from Special:Recentchanges
-       var select =
-       '<select id="namespace" name="namespace" class="namespaceselector">'
-       + '<option value="" selected="selected">all</option>'
-       + '<option value="0">(Main)</option>'
-       + '<option value="1">Talk</option>'
-       + '<option value="2">User</option>'
-       + '<option value="3">User talk</option>'
-       + '<option value="4">ProjectName</option>'
-       + '<option value="5">ProjectName talk</option>'
-       + '</select>'
-       + '<input name="invert" type="checkbox" value="1" id="nsinvert" title="no title" />'
-       + '<label for="nsinvert" title="no title">Invert selection</label>'
-       + '<input name="associated" type="checkbox" value="1" id="nsassociated" title="no title" />'
-       + '<label for="nsassociated" title="no title">Associated namespace</label>'
-       + '<input type="submit" value="Go" />'
-       + '<input type="hidden" value="Special:RecentChanges" name="title" />'
-       ;
-
-       var $env = $( '<div>' ).html( select ).appendTo( 'body' );
-
-       // TODO abstract the double strictEquals
-
-       // At first checkboxes are enabled
-       strictEqual( $( '#nsinvert' ).attr( 'disabled' ), undefined );
-       strictEqual( $( '#nsassociated' ).attr( 'disabled' ), undefined );
-
-       // Initiate the recentchanges module
-       mw.special.recentchanges.init();
-
-       // By default
-       strictEqual( $( '#nsinvert' ).attr( 'disabled' ), 'disabled' );
-       strictEqual( $( '#nsassociated' ).attr( 'disabled' ), 'disabled' );
-
-       // select second option...
-       var $options = $( '#namespace' ).find( 'option' );
-       $options.eq(0).removeAttr( 'selected' );
-       $options.eq(1).attr( 'selected', 'selected' );
-       $( '#namespace' ).change();
-
-       // ... and checkboxes should be enabled again
-       strictEqual( $( '#nsinvert' ).attr( 'disabled' ), undefined );
-       strictEqual( $( '#nsassociated' ).attr( 'disabled' ), undefined );
-
-       // select first option ( 'all' namespace)...
-       $options.eq(1).removeAttr( 'selected' );
-       $options.eq(0).attr( 'selected', 'selected' );;
-       $( '#namespace' ).change();
-       
-       // ... and checkboxes should now be disabled
-       strictEqual( $( '#nsinvert' ).attr( 'disabled' ), 'disabled' );
-       strictEqual( $( '#nsassociated' ).attr( 'disabled' ), 'disabled' );
-
-       // DOM cleanup
-       $env.remove();
-});
diff --git a/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js b/tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.test.js
new file mode 100644 (file)
index 0000000..fca2a80
--- /dev/null
@@ -0,0 +1,71 @@
+module( 'mediawiki.special.recentchanges' );
+
+test( '-- Initial check', function() {
+       expect( 2 );
+       ok( mw.special.recentchanges.init,
+          'mw.special.recentchanges.init defined'
+         );
+       ok( mw.special.recentchanges.updateCheckboxes,
+          'mw.special.recentchanges.updateCheckboxes defined'
+         );
+       // TODO: verify checkboxes == [ 'nsassociated', 'nsinvert' ]
+});
+
+test( '"all" namespace disable checkboxes', function() {
+
+       // from Special:Recentchanges
+       var select =
+       '<select id="namespace" name="namespace" class="namespaceselector">'
+       + '<option value="" selected="selected">all</option>'
+       + '<option value="0">(Main)</option>'
+       + '<option value="1">Talk</option>'
+       + '<option value="2">User</option>'
+       + '<option value="3">User talk</option>'
+       + '<option value="4">ProjectName</option>'
+       + '<option value="5">ProjectName talk</option>'
+       + '</select>'
+       + '<input name="invert" type="checkbox" value="1" id="nsinvert" title="no title" />'
+       + '<label for="nsinvert" title="no title">Invert selection</label>'
+       + '<input name="associated" type="checkbox" value="1" id="nsassociated" title="no title" />'
+       + '<label for="nsassociated" title="no title">Associated namespace</label>'
+       + '<input type="submit" value="Go" />'
+       + '<input type="hidden" value="Special:RecentChanges" name="title" />'
+       ;
+
+       var $env = $( '<div>' ).html( select ).appendTo( 'body' );
+
+       // TODO abstract the double strictEquals
+
+       // At first checkboxes are enabled
+       strictEqual( $( '#nsinvert' ).attr( 'disabled' ), undefined );
+       strictEqual( $( '#nsassociated' ).attr( 'disabled' ), undefined );
+
+       // Initiate the recentchanges module
+       mw.special.recentchanges.init();
+
+       // By default
+       strictEqual( $( '#nsinvert' ).attr( 'disabled' ), 'disabled' );
+       strictEqual( $( '#nsassociated' ).attr( 'disabled' ), 'disabled' );
+
+       // select second option...
+       var $options = $( '#namespace' ).find( 'option' );
+       $options.eq(0).removeAttr( 'selected' );
+       $options.eq(1).attr( 'selected', 'selected' );
+       $( '#namespace' ).change();
+
+       // ... and checkboxes should be enabled again
+       strictEqual( $( '#nsinvert' ).attr( 'disabled' ), undefined );
+       strictEqual( $( '#nsassociated' ).attr( 'disabled' ), undefined );
+
+       // select first option ( 'all' namespace)...
+       $options.eq(1).removeAttr( 'selected' );
+       $options.eq(0).attr( 'selected', 'selected' );;
+       $( '#namespace' ).change();
+       
+       // ... and checkboxes should now be disabled
+       strictEqual( $( '#nsinvert' ).attr( 'disabled' ), 'disabled' );
+       strictEqual( $( '#nsassociated' ).attr( 'disabled' ), 'disabled' );
+
+       // DOM cleanup
+       $env.remove();
+});
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.js b/tests/qunit/suites/resources/mediawiki/mediawiki.js
deleted file mode 100644 (file)
index 15b8377..0000000
+++ /dev/null
@@ -1,228 +0,0 @@
-module( 'mediawiki.js' );
-
-test( '-- Initial check', function() {
-       expect(8);
-
-       ok( window.jQuery, 'jQuery defined' );
-       ok( window.$, '$j defined' );
-       ok( window.$j, '$j defined' );
-       strictEqual( window.$, window.jQuery, '$ alias to jQuery' );
-       strictEqual( window.$j, window.jQuery, '$j alias to jQuery' );
-
-       ok( window.mediaWiki, 'mediaWiki defined' );
-       ok( window.mw, 'mw defined' );
-       strictEqual( window.mw, window.mediaWiki, 'mw alias to mediaWiki' );
-});
-
-test( 'mw.Map', function() {
-       expect(17);
-
-       ok( mw.Map, 'mw.Map defined' );
-
-       var     conf = new mw.Map(),
-               // Dummy variables
-               funky = function() {},
-               arry = [],
-               nummy = 7;
-
-       // Tests for input validation
-       strictEqual( conf.get( 'inexistantKey' ), null, 'Map.get returns null if selection was a string and the key was not found' );
-       strictEqual( conf.set( 'myKey', 'myValue' ), true, 'Map.set returns boolean true if a value was set for a valid key string' );
-       strictEqual( conf.set( funky, 'Funky' ), false, 'Map.set returns boolean false if key was invalid (Function)' );
-       strictEqual( conf.set( arry, 'Arry' ), false, 'Map.set returns boolean false if key was invalid (Array)' );
-       strictEqual( conf.set( nummy, 'Nummy' ), false, 'Map.set returns boolean false if key was invalid (Number)' );
-       equal( conf.get( 'myKey' ), 'myValue', 'Map.get returns a single value value correctly' );
-       strictEqual( conf.get( nummy ), null, 'Map.get ruturns null if selection was invalid (Number)' );
-       strictEqual( conf.get( funky ), null, 'Map.get ruturns null if selection was invalid (Function)' );
-
-       // Multiple values at once
-       var someValues = {
-               'foo': 'bar',
-               'lorem': 'ipsum',
-               'MediaWiki': true
-       };
-       strictEqual( conf.set( someValues ), true, 'Map.set returns boolean true if multiple values were set by passing an object' );
-       deepEqual( conf.get( ['foo', 'lorem'] ), {
-               'foo': 'bar',
-               'lorem': 'ipsum'
-       }, 'Map.get returns multiple values correctly as an object' );
-
-       deepEqual( conf.get( ['foo', 'notExist'] ), {
-               'foo': 'bar',
-               'notExist': null
-       }, 'Map.get return includes keys that were not found as null values' );
-
-       strictEqual( conf.exists( 'foo' ), true, 'Map.exists returns boolean true if a key exists' );
-       strictEqual( conf.exists( 'notExist' ), false, 'Map.exists returns boolean false if a key does not exists' );
-
-       // Interacting with globals and accessing the values object
-       strictEqual( conf.get(), conf.values, 'Map.get returns the entire values object by reference (if called without arguments)' );
-
-       conf.set( 'globalMapChecker', 'Hi' );
-
-       ok( false === 'globalMapChecker' in window, 'new mw.Map did not store its values in the global window object by default' );
-
-       var globalConf = new mw.Map( true );
-       globalConf.set( 'anotherGlobalMapChecker', 'Hello' );
-
-       ok( 'anotherGlobalMapChecker' in window, 'new mw.Map( true ) did store its values in the global window object' );
-
-       // Whitelist this global variable for QUnit's 'noglobal' mode
-       if ( QUnit.config.noglobals ) {
-               QUnit.config.pollution.push( 'anotherGlobalMapChecker' );
-       }
-});
-
-test( 'mw.config', function() {
-       expect(1);
-
-       ok( mw.config instanceof mw.Map, 'mw.config instance of mw.Map' );
-});
-
-test( 'mw.message & mw.messages', function() {
-       expect(16);
-
-       ok( mw.messages, 'messages defined' );
-       ok( mw.messages instanceof mw.Map, 'mw.messages instance of mw.Map' );
-       ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' );
-
-       var hello = mw.message( 'hello' );
-
-       equal( hello.format, 'parse', 'Message property "format" defaults to "parse"' );
-       strictEqual( hello.map, mw.messages, 'Message property "map" defaults to the global instance in mw.messages' );
-       equal( hello.key, 'hello', 'Message property "key" (currect key)' );
-       deepEqual( hello.parameters, [], 'Message property "parameters" defaults to an empty array' );
-
-       // Todo
-       ok( hello.params, 'Message prototype "params"' );
-
-       hello.format = 'plain';
-       equal( hello.toString(), 'Hello <b>awesome</b> world', 'Message.toString returns the message as a string with the current "format"' );
-
-       equal( hello.escaped(), 'Hello &lt;b&gt;awesome&lt;/b&gt; world', 'Message.escaped returns the escaped message' );
-       equal( hello.format, 'escaped', 'Message.escaped correctly updated the "format" property' );
-
-       hello.parse();
-       equal( hello.format, 'parse', 'Message.parse correctly updated the "format" property' );
-
-       hello.plain();
-       equal( hello.format, 'plain', 'Message.plain correctly updated the "format" property' );
-
-       strictEqual( hello.exists(), true, 'Message.exists returns true for existing messages' );
-
-       var goodbye = mw.message( 'goodbye' );
-       strictEqual( goodbye.exists(), false, 'Message.exists returns false for inexisting messages' );
-
-       equal( goodbye.toString(), '<goodbye>', 'Message.toString returns <key> if key does not exist' );
-});
-
-test( 'mw.msg', function() {
-       expect(2);
-
-       equal( mw.msg( 'hello' ), 'Hello <b>awesome</b> world', 'Gets message with default options (existing message)' );
-       equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (inexisting message)' );
-});
-
-test( 'mw.loader', function() {
-       expect(5);
-
-       // Regular expression to extract the path for the QUnit tests
-       // Takes into account that tests could be run from a file:// URL
-       // by excluding the 'index.html' part from the URL
-       var rePath = /(?:[^#\?](?!index.html))*\/?/;
-
-       // Four assertions to test the above regular expression:
-       equal(
-               rePath.exec( 'http://path/to/tests/?foobar' )[0],
-               "http://path/to/tests/",
-               "Extracting path from http URL with query"
-               );
-       equal(
-               rePath.exec( 'http://path/to/tests/#frag' )[0],
-               "http://path/to/tests/",
-               "Extracting path from http URL with fragment"
-               );
-       equal(
-               rePath.exec( 'file://path/to/tests/index.html?foobar' )[0],
-               "file://path/to/tests/",
-               "Extracting path from local URL (file://) with query"
-               );
-       equal(
-               rePath.exec( 'file://path/to/tests/index.html#frag' )[0],
-               "file://path/to/tests/",
-               "Extracting path from local URL (file://) with fragment"
-               );
-
-       // Asynchronous ahead
-       stop(5000);
-
-       // Extract path
-       var tests_path = rePath.exec( location.href );
-
-       mw.loader.implement( 'is.awesome', [QUnit.fixurl( tests_path + 'data/defineTestCallback.js')], {}, {} );
-
-       mw.loader.using( 'is.awesome', function() {
-
-               // /sample/awesome.js declares the "mw.loader.testCallback" function
-               // which contains a call to start() and ok()
-               mw.loader.testCallback();
-               mw.loader.testCallback = undefined;
-
-       }, function() {
-               start();
-               ok( false, 'Error callback fired while implementing "is.awesome" module' );
-       });
-
-});
-
-test( 'mw.loader.bug29107' , function() {
-       expect(2);
-
-       // Message doesn't exist already
-       ok( !mw.messages.exists( 'bug29107' ) );
-
-       // Async! Include a timeout, as failure in this test leads to neither the
-       // success nor failure callbacks getting called.
-       stop(5000);
-
-       mw.loader.implement( 'bug29107.messages-only', [], {}, {'bug29107': 'loaded'} );
-       mw.loader.using( 'bug29107.messages-only', function() {
-               start();
-               ok( mw.messages.exists( 'bug29107' ), 'Bug 29107: messages-only module should implement ok' );
-       }, function() {
-               start();
-               ok( false, 'Error callback fired while implementing "bug29107.messages-only" module' );
-       });
-});
-
-test( 'mw.html', function() {
-       expect(7);
-
-       raises( function(){
-               mw.html.escape();
-       }, TypeError, 'html.escape throws a TypeError if argument given is not a string' );
-
-       equal( mw.html.escape( '<mw awesome="awesome" value=\'test\' />' ),
-               '&lt;mw awesome=&quot;awesome&quot; value=&#039;test&#039; /&gt;', 'html.escape escapes html snippet' );
-
-       equal( mw.html.element(),
-               '<undefined/>', 'html.element Always return a valid html string (even without arguments)' );
-
-       equal( mw.html.element( 'div' ), '<div/>', 'html.element DIV (simple)' );
-
-       equal( mw.html.element( 'div',
-                       { id: 'foobar' } ),
-               '<div id="foobar"/>',
-               'html.element DIV (attribs)' );
-
-       equal( mw.html.element( 'div',
-                       null, 'a' ),
-               '<div>a</div>',
-               'html.element DIV (content)' );
-
-       equal( mw.html.element( 'a',
-                       { href: 'http://mediawiki.org/w/index.php?title=RL&action=history' }, 'a' ),
-               '<a href="http://mediawiki.org/w/index.php?title=RL&amp;action=history">a</a>',
-               'html.element DIV (attribs + content)' );
-
-});
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.test.js
new file mode 100644 (file)
index 0000000..4a87782
--- /dev/null
@@ -0,0 +1,228 @@
+module( 'mediawiki' );
+
+test( '-- Initial check', function() {
+       expect(8);
+
+       ok( window.jQuery, 'jQuery defined' );
+       ok( window.$, '$j defined' );
+       ok( window.$j, '$j defined' );
+       strictEqual( window.$, window.jQuery, '$ alias to jQuery' );
+       strictEqual( window.$j, window.jQuery, '$j alias to jQuery' );
+
+       ok( window.mediaWiki, 'mediaWiki defined' );
+       ok( window.mw, 'mw defined' );
+       strictEqual( window.mw, window.mediaWiki, 'mw alias to mediaWiki' );
+});
+
+test( 'mw.Map', function() {
+       expect(17);
+
+       ok( mw.Map, 'mw.Map defined' );
+
+       var     conf = new mw.Map(),
+               // Dummy variables
+               funky = function() {},
+               arry = [],
+               nummy = 7;
+
+       // Tests for input validation
+       strictEqual( conf.get( 'inexistantKey' ), null, 'Map.get returns null if selection was a string and the key was not found' );
+       strictEqual( conf.set( 'myKey', 'myValue' ), true, 'Map.set returns boolean true if a value was set for a valid key string' );
+       strictEqual( conf.set( funky, 'Funky' ), false, 'Map.set returns boolean false if key was invalid (Function)' );
+       strictEqual( conf.set( arry, 'Arry' ), false, 'Map.set returns boolean false if key was invalid (Array)' );
+       strictEqual( conf.set( nummy, 'Nummy' ), false, 'Map.set returns boolean false if key was invalid (Number)' );
+       equal( conf.get( 'myKey' ), 'myValue', 'Map.get returns a single value value correctly' );
+       strictEqual( conf.get( nummy ), null, 'Map.get ruturns null if selection was invalid (Number)' );
+       strictEqual( conf.get( funky ), null, 'Map.get ruturns null if selection was invalid (Function)' );
+
+       // Multiple values at once
+       var someValues = {
+               'foo': 'bar',
+               'lorem': 'ipsum',
+               'MediaWiki': true
+       };
+       strictEqual( conf.set( someValues ), true, 'Map.set returns boolean true if multiple values were set by passing an object' );
+       deepEqual( conf.get( ['foo', 'lorem'] ), {
+               'foo': 'bar',
+               'lorem': 'ipsum'
+       }, 'Map.get returns multiple values correctly as an object' );
+
+       deepEqual( conf.get( ['foo', 'notExist'] ), {
+               'foo': 'bar',
+               'notExist': null
+       }, 'Map.get return includes keys that were not found as null values' );
+
+       strictEqual( conf.exists( 'foo' ), true, 'Map.exists returns boolean true if a key exists' );
+       strictEqual( conf.exists( 'notExist' ), false, 'Map.exists returns boolean false if a key does not exists' );
+
+       // Interacting with globals and accessing the values object
+       strictEqual( conf.get(), conf.values, 'Map.get returns the entire values object by reference (if called without arguments)' );
+
+       conf.set( 'globalMapChecker', 'Hi' );
+
+       ok( false === 'globalMapChecker' in window, 'new mw.Map did not store its values in the global window object by default' );
+
+       var globalConf = new mw.Map( true );
+       globalConf.set( 'anotherGlobalMapChecker', 'Hello' );
+
+       ok( 'anotherGlobalMapChecker' in window, 'new mw.Map( true ) did store its values in the global window object' );
+
+       // Whitelist this global variable for QUnit's 'noglobal' mode
+       if ( QUnit.config.noglobals ) {
+               QUnit.config.pollution.push( 'anotherGlobalMapChecker' );
+       }
+});
+
+test( 'mw.config', function() {
+       expect(1);
+
+       ok( mw.config instanceof mw.Map, 'mw.config instance of mw.Map' );
+});
+
+test( 'mw.message & mw.messages', function() {
+       expect(16);
+
+       ok( mw.messages, 'messages defined' );
+       ok( mw.messages instanceof mw.Map, 'mw.messages instance of mw.Map' );
+       ok( mw.messages.set( 'hello', 'Hello <b>awesome</b> world' ), 'mw.messages.set: Register' );
+
+       var hello = mw.message( 'hello' );
+
+       equal( hello.format, 'parse', 'Message property "format" defaults to "parse"' );
+       strictEqual( hello.map, mw.messages, 'Message property "map" defaults to the global instance in mw.messages' );
+       equal( hello.key, 'hello', 'Message property "key" (currect key)' );
+       deepEqual( hello.parameters, [], 'Message property "parameters" defaults to an empty array' );
+
+       // Todo
+       ok( hello.params, 'Message prototype "params"' );
+
+       hello.format = 'plain';
+       equal( hello.toString(), 'Hello <b>awesome</b> world', 'Message.toString returns the message as a string with the current "format"' );
+
+       equal( hello.escaped(), 'Hello &lt;b&gt;awesome&lt;/b&gt; world', 'Message.escaped returns the escaped message' );
+       equal( hello.format, 'escaped', 'Message.escaped correctly updated the "format" property' );
+
+       hello.parse();
+       equal( hello.format, 'parse', 'Message.parse correctly updated the "format" property' );
+
+       hello.plain();
+       equal( hello.format, 'plain', 'Message.plain correctly updated the "format" property' );
+
+       strictEqual( hello.exists(), true, 'Message.exists returns true for existing messages' );
+
+       var goodbye = mw.message( 'goodbye' );
+       strictEqual( goodbye.exists(), false, 'Message.exists returns false for inexisting messages' );
+
+       equal( goodbye.toString(), '<goodbye>', 'Message.toString returns <key> if key does not exist' );
+});
+
+test( 'mw.msg', function() {
+       expect(2);
+
+       equal( mw.msg( 'hello' ), 'Hello <b>awesome</b> world', 'Gets message with default options (existing message)' );
+       equal( mw.msg( 'goodbye' ), '<goodbye>', 'Gets message with default options (inexisting message)' );
+});
+
+test( 'mw.loader', function() {
+       expect(5);
+
+       // Regular expression to extract the path for the QUnit tests
+       // Takes into account that tests could be run from a file:// URL
+       // by excluding the 'index.html' part from the URL
+       var rePath = /(?:[^#\?](?!index.html))*\/?/;
+
+       // Four assertions to test the above regular expression:
+       equal(
+               rePath.exec( 'http://path/to/tests/?foobar' )[0],
+               "http://path/to/tests/",
+               "Extracting path from http URL with query"
+               );
+       equal(
+               rePath.exec( 'http://path/to/tests/#frag' )[0],
+               "http://path/to/tests/",
+               "Extracting path from http URL with fragment"
+               );
+       equal(
+               rePath.exec( 'file://path/to/tests/index.html?foobar' )[0],
+               "file://path/to/tests/",
+               "Extracting path from local URL (file://) with query"
+               );
+       equal(
+               rePath.exec( 'file://path/to/tests/index.html#frag' )[0],
+               "file://path/to/tests/",
+               "Extracting path from local URL (file://) with fragment"
+               );
+
+       // Asynchronous ahead
+       stop(5000);
+
+       // Extract path
+       var tests_path = rePath.exec( location.href );
+
+       mw.loader.implement( 'is.awesome', [QUnit.fixurl( tests_path + 'data/defineTestCallback.js')], {}, {} );
+
+       mw.loader.using( 'is.awesome', function() {
+
+               // /sample/awesome.js declares the "mw.loader.testCallback" function
+               // which contains a call to start() and ok()
+               mw.loader.testCallback();
+               mw.loader.testCallback = undefined;
+
+       }, function() {
+               start();
+               ok( false, 'Error callback fired while implementing "is.awesome" module' );
+       });
+
+});
+
+test( 'mw.loader.bug29107' , function() {
+       expect(2);
+
+       // Message doesn't exist already
+       ok( !mw.messages.exists( 'bug29107' ) );
+
+       // Async! Include a timeout, as failure in this test leads to neither the
+       // success nor failure callbacks getting called.
+       stop(5000);
+
+       mw.loader.implement( 'bug29107.messages-only', [], {}, {'bug29107': 'loaded'} );
+       mw.loader.using( 'bug29107.messages-only', function() {
+               start();
+               ok( mw.messages.exists( 'bug29107' ), 'Bug 29107: messages-only module should implement ok' );
+       }, function() {
+               start();
+               ok( false, 'Error callback fired while implementing "bug29107.messages-only" module' );
+       });
+});
+
+test( 'mw.html', function() {
+       expect(7);
+
+       raises( function(){
+               mw.html.escape();
+       }, TypeError, 'html.escape throws a TypeError if argument given is not a string' );
+
+       equal( mw.html.escape( '<mw awesome="awesome" value=\'test\' />' ),
+               '&lt;mw awesome=&quot;awesome&quot; value=&#039;test&#039; /&gt;', 'html.escape escapes html snippet' );
+
+       equal( mw.html.element(),
+               '<undefined/>', 'html.element Always return a valid html string (even without arguments)' );
+
+       equal( mw.html.element( 'div' ), '<div/>', 'html.element DIV (simple)' );
+
+       equal( mw.html.element( 'div',
+                       { id: 'foobar' } ),
+               '<div id="foobar"/>',
+               'html.element DIV (attribs)' );
+
+       equal( mw.html.element( 'div',
+                       null, 'a' ),
+               '<div>a</div>',
+               'html.element DIV (content)' );
+
+       equal( mw.html.element( 'a',
+                       { href: 'http://mediawiki.org/w/index.php?title=RL&action=history' }, 'a' ),
+               '<a href="http://mediawiki.org/w/index.php?title=RL&amp;action=history">a</a>',
+               'html.element DIV (attribs + content)' );
+
+});
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.title.js b/tests/qunit/suites/resources/mediawiki/mediawiki.title.js
deleted file mode 100644 (file)
index c0c0dc0..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-module( 'mediawiki.title.js' );
-
-// mw.Title relies on these three config vars
-// Restore them after each test run
-var _titleConfig = function() {
-
-       mw.config.set({
-               "wgFormattedNamespaces": {
-                       "-2": "Media",
-                       "-1": "Special",
-                       "0": "",
-                       "1": "Talk",
-                       "2": "User",
-                       "3": "User talk",
-                       "4": "Wikipedia",
-                       "5": "Wikipedia talk",
-                       "6": "File",
-                       "7": "File talk",
-                       "8": "MediaWiki",
-                       "9": "MediaWiki talk",
-                       "10": "Template",
-                       "11": "Template talk",
-                       "12": "Help",
-                       "13": "Help talk",
-                       "14": "Category",
-                       "15": "Category talk",
-                       /* testing custom / localized */
-                       "100": "Penguins"
-               },
-               "wgNamespaceIds": {
-                       "media": -2,
-                       "special": -1,
-                       "": 0,
-                       "talk": 1,
-                       "user": 2,
-                       "user_talk": 3,
-                       "wikipedia": 4,
-                       "wikipedia_talk": 5,
-                       "file": 6,
-                       "file_talk": 7,
-                       "mediawiki": 8,
-                       "mediawiki_talk": 9,
-                       "template": 10,
-                       "template_talk": 11,
-                       "help": 12,
-                       "help_talk": 13,
-                       "category": 14,
-                       "category_talk": 15,
-                       "image": 6,
-                       "image_talk": 7,
-                       "project": 4,
-                       "project_talk": 5,
-                       /* testing custom / alias */
-                       "penguins": 100,
-                       "antarctic_waterfowl": 100
-               },
-               "wgCaseSensitiveNamespaces": []
-       });
-};
-
-test( '-- Initial check', function() {
-       expect(1);
-       ok( mw.Title, 'mw.Title defined' );
-});
-
-test( 'Transform between Text and Db', function() {
-       expect(2);
-       _titleConfig();
-
-       var title;
-
-       title = new mw.Title( 'File:quux pif.jpg' );
-       equal( title.getName(), 'Quux_pif' );
-
-       title = new mw.Title( 'File:Glarg_foo_glang.jpg' );
-       equal( title.getNameText(), 'Glarg foo glang' );
-});
-
-test( 'Main text for filename', function() {
-       expect(8);
-       _titleConfig();
-
-       var title = new mw.Title( 'File:foo_bar.JPG' );
-
-       equal( title.getNamespaceId(), 6 );
-       equal( title.getNamespacePrefix(), 'File:' );
-       equal( title.getName(), 'Foo_bar' );
-       equal( title.getNameText(), 'Foo bar' );
-       equal( title.getMain(), 'Foo_bar.jpg' );
-       equal( title.getMainText(), 'Foo bar.jpg' );
-       equal( title.getExtension(), 'jpg' );
-       equal( title.getDotExtension(), '.jpg' );
-});
-
-test( 'Namespace detection and conversion', function() {
-       expect(6);
-       _titleConfig();
-
-       var title;
-
-       title = new mw.Title( 'something.PDF', 6 );
-       equal( title.toString(), 'File:Something.pdf' );
-
-       title = new mw.Title( 'NeilK', 3 );
-       equal( title.toString(), 'User_talk:NeilK' );
-       equal( title.toText(), 'User talk:NeilK' );
-
-       title = new mw.Title( 'Frobisher', 100 );
-       equal( title.toString(), 'Penguins:Frobisher' );
-
-       title = new mw.Title( 'antarctic_waterfowl:flightless_yet_cute.jpg' );
-       equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' );
-
-       title = new mw.Title( 'Penguins:flightless_yet_cute.jpg' );
-       equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' );
-});
-
-test( 'Throw error on invalid title', function() {
-       expect(1);
-       _titleConfig();
-
-       raises(function() {
-               var title = new mw.Title( '' );
-       }, 'Throw error on empty string' );
-});
-
-test( 'Case-sensivity', function() {
-       expect(3);
-       _titleConfig();
-
-       var title;
-
-       // Default config
-       mw.config.set( 'wgCaseSensitiveNamespaces', [] );
-
-       title = new mw.Title( 'article' );
-       equal( title.toString(), 'Article', 'Default config: No sensitive namespaces by default. First-letter becomes uppercase' );
-
-       // $wgCapitalLinks = false;
-       mw.config.set( 'wgCaseSensitiveNamespaces', [0, -2, 1, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15] );
-
-       title = new mw.Title( 'article' );
-       equal( title.toString(), 'article', '$wgCapitalLinks=false: Article namespace is sensitive, first-letter case stays lowercase' );
-
-       title = new mw.Title( 'john', 2 );
-       equal( title.toString(), 'User:John', '$wgCapitalLinks=false: User namespace is insensitive, first-letter becomes uppercase' );
-});
-
-test( 'toString / toText', function() {
-       expect(2);
-       _titleConfig();
-
-       var title = new mw.Title( 'Some random page' );
-
-       equal( title.toString(), title.getPrefixedDb() );
-       equal( title.toText(), title.getPrefixedText() );
-});
-
-test( 'Exists', function() {
-       expect(3);
-       _titleConfig();
-
-       var title;
-
-       // Empty registry, checks default to null
-
-       title = new mw.Title( 'Some random page', 4 );
-       strictEqual( title.exists(), null, 'Return null with empty existance registry' );
-
-       // Basic registry, checks default to boolean
-       mw.Title.exist.set( ['Does_exist', 'User_talk:NeilK', 'Wikipedia:Sandbox_rules'], true );
-       mw.Title.exist.set( ['Does_not_exist', 'User:John', 'Foobar'], false );
-
-       title = new mw.Title( 'Project:Sandbox rules' );
-       assertTrue( title.exists(), 'Return true for page titles marked as existing' );
-       title = new mw.Title( 'Foobar' );
-       assertFalse( title.exists(), 'Return false for page titles marked as inexisting' );
-
-});
-
-test( 'Url', function() {
-       expect(2);
-       _titleConfig();
-
-       var title;
-
-       // Config
-       mw.config.set( 'wgArticlePath', '/wiki/$1' );
-
-       title = new mw.Title( 'Foobar' );
-       equal( title.getUrl(), '/wiki/Foobar', 'Basic functionally, toString passing to wikiGetlink' );
-
-       title = new mw.Title( 'John Doe', 3 );
-       equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
-});
\ No newline at end of file
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.title.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.title.test.js
new file mode 100644 (file)
index 0000000..9d1c517
--- /dev/null
@@ -0,0 +1,195 @@
+module( 'mediawiki.title' );
+
+// mw.Title relies on these three config vars
+// Restore them after each test run
+var _titleConfig = function() {
+
+       mw.config.set({
+               "wgFormattedNamespaces": {
+                       "-2": "Media",
+                       "-1": "Special",
+                       "0": "",
+                       "1": "Talk",
+                       "2": "User",
+                       "3": "User talk",
+                       "4": "Wikipedia",
+                       "5": "Wikipedia talk",
+                       "6": "File",
+                       "7": "File talk",
+                       "8": "MediaWiki",
+                       "9": "MediaWiki talk",
+                       "10": "Template",
+                       "11": "Template talk",
+                       "12": "Help",
+                       "13": "Help talk",
+                       "14": "Category",
+                       "15": "Category talk",
+                       /* testing custom / localized */
+                       "100": "Penguins"
+               },
+               "wgNamespaceIds": {
+                       "media": -2,
+                       "special": -1,
+                       "": 0,
+                       "talk": 1,
+                       "user": 2,
+                       "user_talk": 3,
+                       "wikipedia": 4,
+                       "wikipedia_talk": 5,
+                       "file": 6,
+                       "file_talk": 7,
+                       "mediawiki": 8,
+                       "mediawiki_talk": 9,
+                       "template": 10,
+                       "template_talk": 11,
+                       "help": 12,
+                       "help_talk": 13,
+                       "category": 14,
+                       "category_talk": 15,
+                       "image": 6,
+                       "image_talk": 7,
+                       "project": 4,
+                       "project_talk": 5,
+                       /* testing custom / alias */
+                       "penguins": 100,
+                       "antarctic_waterfowl": 100
+               },
+               "wgCaseSensitiveNamespaces": []
+       });
+};
+
+test( '-- Initial check', function() {
+       expect(1);
+       ok( mw.Title, 'mw.Title defined' );
+});
+
+test( 'Transform between Text and Db', function() {
+       expect(2);
+       _titleConfig();
+
+       var title;
+
+       title = new mw.Title( 'File:quux pif.jpg' );
+       equal( title.getName(), 'Quux_pif' );
+
+       title = new mw.Title( 'File:Glarg_foo_glang.jpg' );
+       equal( title.getNameText(), 'Glarg foo glang' );
+});
+
+test( 'Main text for filename', function() {
+       expect(8);
+       _titleConfig();
+
+       var title = new mw.Title( 'File:foo_bar.JPG' );
+
+       equal( title.getNamespaceId(), 6 );
+       equal( title.getNamespacePrefix(), 'File:' );
+       equal( title.getName(), 'Foo_bar' );
+       equal( title.getNameText(), 'Foo bar' );
+       equal( title.getMain(), 'Foo_bar.jpg' );
+       equal( title.getMainText(), 'Foo bar.jpg' );
+       equal( title.getExtension(), 'jpg' );
+       equal( title.getDotExtension(), '.jpg' );
+});
+
+test( 'Namespace detection and conversion', function() {
+       expect(6);
+       _titleConfig();
+
+       var title;
+
+       title = new mw.Title( 'something.PDF', 6 );
+       equal( title.toString(), 'File:Something.pdf' );
+
+       title = new mw.Title( 'NeilK', 3 );
+       equal( title.toString(), 'User_talk:NeilK' );
+       equal( title.toText(), 'User talk:NeilK' );
+
+       title = new mw.Title( 'Frobisher', 100 );
+       equal( title.toString(), 'Penguins:Frobisher' );
+
+       title = new mw.Title( 'antarctic_waterfowl:flightless_yet_cute.jpg' );
+       equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' );
+
+       title = new mw.Title( 'Penguins:flightless_yet_cute.jpg' );
+       equal( title.toString(), 'Penguins:Flightless_yet_cute.jpg' );
+});
+
+test( 'Throw error on invalid title', function() {
+       expect(1);
+       _titleConfig();
+
+       raises(function() {
+               var title = new mw.Title( '' );
+       }, 'Throw error on empty string' );
+});
+
+test( 'Case-sensivity', function() {
+       expect(3);
+       _titleConfig();
+
+       var title;
+
+       // Default config
+       mw.config.set( 'wgCaseSensitiveNamespaces', [] );
+
+       title = new mw.Title( 'article' );
+       equal( title.toString(), 'Article', 'Default config: No sensitive namespaces by default. First-letter becomes uppercase' );
+
+       // $wgCapitalLinks = false;
+       mw.config.set( 'wgCaseSensitiveNamespaces', [0, -2, 1, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15] );
+
+       title = new mw.Title( 'article' );
+       equal( title.toString(), 'article', '$wgCapitalLinks=false: Article namespace is sensitive, first-letter case stays lowercase' );
+
+       title = new mw.Title( 'john', 2 );
+       equal( title.toString(), 'User:John', '$wgCapitalLinks=false: User namespace is insensitive, first-letter becomes uppercase' );
+});
+
+test( 'toString / toText', function() {
+       expect(2);
+       _titleConfig();
+
+       var title = new mw.Title( 'Some random page' );
+
+       equal( title.toString(), title.getPrefixedDb() );
+       equal( title.toText(), title.getPrefixedText() );
+});
+
+test( 'Exists', function() {
+       expect(3);
+       _titleConfig();
+
+       var title;
+
+       // Empty registry, checks default to null
+
+       title = new mw.Title( 'Some random page', 4 );
+       strictEqual( title.exists(), null, 'Return null with empty existance registry' );
+
+       // Basic registry, checks default to boolean
+       mw.Title.exist.set( ['Does_exist', 'User_talk:NeilK', 'Wikipedia:Sandbox_rules'], true );
+       mw.Title.exist.set( ['Does_not_exist', 'User:John', 'Foobar'], false );
+
+       title = new mw.Title( 'Project:Sandbox rules' );
+       assertTrue( title.exists(), 'Return true for page titles marked as existing' );
+       title = new mw.Title( 'Foobar' );
+       assertFalse( title.exists(), 'Return false for page titles marked as inexisting' );
+
+});
+
+test( 'Url', function() {
+       expect(2);
+       _titleConfig();
+
+       var title;
+
+       // Config
+       mw.config.set( 'wgArticlePath', '/wiki/$1' );
+
+       title = new mw.Title( 'Foobar' );
+       equal( title.getUrl(), '/wiki/Foobar', 'Basic functionally, toString passing to wikiGetlink' );
+
+       title = new mw.Title( 'John Doe', 3 );
+       equal( title.getUrl(), '/wiki/User_talk:John_Doe', 'Escaping in title and namespace for urls' );
+});
\ No newline at end of file
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.user.js b/tests/qunit/suites/resources/mediawiki/mediawiki.user.js
deleted file mode 100644 (file)
index d5c6baa..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-module( 'mediawiki.user.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-
-       ok( mw.user, 'mw.user defined' );
-});
-
-
-test( 'options', function() {
-       expect(1);
-
-       ok( mw.user.options instanceof mw.Map, 'options instance of mw.Map' );
-});
-
-test( 'User login status', function() {
-       expect(5);
-
-       strictEqual( mw.user.name(), null, 'user.name should return null when anonymous' );
-       ok( mw.user.anonymous(), 'user.anonymous should reutrn true when anonymous' );
-
-       // Not part of startUp module
-       mw.config.set( 'wgUserName', 'John' );
-
-       equal( mw.user.name(), 'John', 'user.name returns username when logged-in' );
-       ok( !mw.user.anonymous(), 'user.anonymous returns false when logged-in' );
-
-       equal( mw.user.id(), 'John', 'user.id Returns username when logged-in' );
-});
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.user.test.js
new file mode 100644 (file)
index 0000000..1aa4698
--- /dev/null
@@ -0,0 +1,29 @@
+module( 'mediawiki.user' );
+
+test( '-- Initial check', function() {
+       expect(1);
+
+       ok( mw.user, 'mw.user defined' );
+});
+
+
+test( 'options', function() {
+       expect(1);
+
+       ok( mw.user.options instanceof mw.Map, 'options instance of mw.Map' );
+});
+
+test( 'User login status', function() {
+       expect(5);
+
+       strictEqual( mw.user.name(), null, 'user.name should return null when anonymous' );
+       ok( mw.user.anonymous(), 'user.anonymous should reutrn true when anonymous' );
+
+       // Not part of startUp module
+       mw.config.set( 'wgUserName', 'John' );
+
+       equal( mw.user.name(), 'John', 'user.name returns username when logged-in' );
+       ok( !mw.user.anonymous(), 'user.anonymous returns false when logged-in' );
+
+       equal( mw.user.id(), 'John', 'user.id Returns username when logged-in' );
+});
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.util.js b/tests/qunit/suites/resources/mediawiki/mediawiki.util.js
deleted file mode 100644 (file)
index 4dfb0dc..0000000
+++ /dev/null
@@ -1,301 +0,0 @@
-module( 'mediawiki.util.js' );
-
-test( '-- Initial check', function() {
-       expect(1);
-
-       ok( mw.util, 'mw.util defined' );
-});
-
-test( 'rawurlencode', function() {
-       expect(1);
-
-       equal( mw.util.rawurlencode( 'Test:A & B/Here' ), 'Test%3AA%20%26%20B%2FHere' );
-});
-
-test( 'wikiUrlencode', function() {
-       expect(1);
-
-       equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' );
-});
-
-test( 'wikiGetlink', function() {
-       expect(3);
-
-       // Not part of startUp module
-       mw.config.set( 'wgArticlePath', '/wiki/$1' );
-       mw.config.set( 'wgPageName', 'Foobar' );
-
-       var hrefA = mw.util.wikiGetlink( 'Sandbox' );
-       equal( hrefA, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
-
-       var hrefB = mw.util.wikiGetlink( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
-       equal( hrefB, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_%21_%28test%29/subpage',
-               'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
-
-       var hrefC = mw.util.wikiGetlink();
-       equal( hrefC, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
-});
-
-test( 'wikiScript', function() {
-       expect(2);
-
-       mw.config.set({
-               'wgScript': '/w/index.php',
-               'wgScriptPath': '/w',
-               'wgScriptExtension': '.php'
-       });
-
-       equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript' );
-       equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' );
-
-});
-
-test( 'addCSS', function() {
-       expect(3);
-
-       var $testEl = $( '<div>' ).attr( 'id', 'mw-addcsstest' ).appendTo( 'body' );
-
-       var style = mw.util.addCSS( '#mw-addcsstest { visibility: hidden; }' );
-       equal( typeof style, 'object', 'addCSS returned an object' );
-       strictEqual( style.disabled, false, 'property "disabled" is available and set to false' );
-
-       equal( $testEl.css( 'visibility' ), 'hidden', 'Added style properties are in effect' );
-
-       // Clean up
-       $( style.ownerNode )
-               .add( $testEl )
-               .remove();
-});
-
-test( 'toggleToc', function() {
-       expect(4);
-
-       strictEqual( mw.util.toggleToc(), null, 'Return null if there is no table of contents on the page.' );
-
-       var     tocHtml =
-       '<table id="toc" class="toc"><tr><td>' +
-               '<div id="toctitle">' +
-                       '<h2>Contents</h2>' +
-                       '<span class="toctoggle">&nbsp;[<a href="#" class="internal" id="togglelink">Hide</a>&nbsp;]</span>' +
-               '</div>' +
-               '<ul><li></li></ul>' +
-       '</td></tr></table>',
-               $toc = $(tocHtml).appendTo( 'body' ),
-               $toggleLink = $( '#togglelink' );
-
-       strictEqual( $toggleLink.length, 1, 'Toggle link is appended to the page.' );
-
-       // Toggle animation is asynchronous
-       // QUnit should not finish this test() untill they are all done
-       stop();
-
-       var actionC = function() {
-               start();
-
-               // Clean up
-               $toc.remove();
-       };
-       var actionB = function() {
-               start(); stop();
-               strictEqual( mw.util.toggleToc( $toggleLink, actionC ), true, 'Return boolean true if the TOC is now visible.' );
-       };
-       var actionA = function() {
-               strictEqual( mw.util.toggleToc( $toggleLink, actionB ), false, 'Return boolean false if the TOC is now hidden.' );
-       };
-
-       actionA();
-});
-
-test( 'getParamValue', function() {
-       expect(3);
-
-       var     url1 = 'http://mediawiki.org/?foo=wrong&foo=right#&foo=bad';
-
-       equal( mw.util.getParamValue( 'foo', url1 ), 'right', 'Use latest one, ignore hash' );
-       strictEqual( mw.util.getParamValue( 'bar', url1 ), null, 'Return null when not found' );
-
-       var url2 = 'http://mediawiki.org/#&foo=bad';
-       strictEqual( mw.util.getParamValue( 'foo', url2 ), null, 'Ignore hash if param is not in querystring but in hash (bug 27427)' );
-});
-
-test( 'tooltipAccessKey', function() {
-       expect(3);
-
-       equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'mw.util.tooltipAccessKeyPrefix must be a string' );
-       ok( mw.util.tooltipAccessKeyRegexp instanceof RegExp, 'mw.util.tooltipAccessKeyRegexp instance of RegExp' );
-       ok( mw.util.updateTooltipAccessKeys, 'mw.util.updateTooltipAccessKeys' );
-});
-
-test( '$content', function() {
-       expect(2);
-
-       ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
-       strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
-});
-
-test( 'addPortletLink', function() {
-       expect(7);
-
-       var mwPanel = '<div id="mw-panel" class="noprint">\
-       <h5>Toolbox</h5>\
-       <div class="portlet" id="p-tb">\
-               <ul class="body"></ul>\
-       </div>\
-</div>',
-       vectorTabs = '<div id="p-views" class="vectorTabs">\
-       <h5>Views</h5>\
-       <ul></ul>\
-</div>',
-       $mwPanel = $(mwPanel).appendTo( 'body' ),
-       $vectorTabs = $(vectorTabs).appendTo( 'body' );
-
-       var tbRL = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/wiki/ResourceLoader',
-               'ResourceLoader', 't-rl', 'More info about ResourceLoader on MediaWiki.org ', 'l' );
-
-       ok( $.isDomElement( tbRL ), 'addPortletLink returns a valid DOM Element according to $.isDomElement' );
-
-       var     tbMW = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/',
-                       'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', tbRL ),
-               $tbMW = $( tbMW );
-               
-
-       equal( $tbMW.attr( 'id' ), 't-mworg', 'Link has correct ID set' );
-       equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-tb', 'Link was inserted within correct portlet' );
-       equal( $tbMW.next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing nextnode)' );
-
-       var tbRLDM = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/wiki/RL/DM',
-               'Default modules', 't-rldm', 'List of all default modules ', 'd', '#t-rl' );
-
-       equal( $( tbRLDM ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' );
-
-       var caFoo = mw.util.addPortletLink( 'p-views', '#', 'Foo' );
-
-       strictEqual( $tbMW.find( 'span').length, 0, 'No <span> element should be added for porlets without vectorTabs class.' );
-       strictEqual( $( caFoo ).find( 'span').length, 1, 'A <span> element should be added for porlets with vectorTabs class.' );
-       
-       // Clean up
-       $( [tbRL, tbMW, tbRLDM, caFoo] )
-               .add( $mwPanel )
-               .add( $vectorTabs )
-               .remove();
-});
-
-test( 'jsMessage', function() {
-       expect(1);
-
-       var a = mw.util.jsMessage( "MediaWiki is <b>Awesome</b>." );
-       ok( a, 'Basic checking of return value' );
-
-       // Clean up
-       $( '#mw-js-message' ).remove();
-});
-
-test( 'validateEmail', function() {
-       expect(6);
-
-       strictEqual( mw.util.validateEmail( "" ), null, 'Should return null for empty string ' );
-       strictEqual( mw.util.validateEmail( "user@localhost" ), true, 'Return true for a valid e-mail address' );
-
-       // testEmailWithCommasAreInvalids
-       strictEqual( mw.util.validateEmail( "user,foo@example.org" ), false, 'Emails with commas are invalid' );
-       strictEqual( mw.util.validateEmail( "userfoo@ex,ample.org" ), false, 'Emails with commas are invalid' );
-
-       // testEmailWithHyphens
-       strictEqual( mw.util.validateEmail( "user-foo@example.org" ), true, 'Emails may contain a hyphen' );
-       strictEqual( mw.util.validateEmail( "userfoo@ex-ample.org" ), true, 'Emails may contain a hyphen' );
-});
-
-test( 'isIPv6Address', function() {
-       expect(40);
-
-       // Shortcuts
-       var     assertFalseIPv6 = function( addy, summary ) {
-                       return strictEqual( mw.util.isIPv6Address( addy ), false, summary );
-               },
-               assertTrueIPv6 = function( addy, summary ) {
-                       return strictEqual( mw.util.isIPv6Address( addy ), true, summary );
-               };
-
-       // Based on IPTest.php > testisIPv6
-       assertFalseIPv6( ':fc:100::', 'IPv6 starting with lone ":"' );
-       assertFalseIPv6( 'fc:100:::', 'IPv6 ending with a ":::"' );
-       assertFalseIPv6( 'fc:300', 'IPv6 with only 2 words' );
-       assertFalseIPv6( 'fc:100:300', 'IPv6 with only 3 words' );
-
-       $.each(
-       ['fc:100::',
-       'fc:100:a::',
-       'fc:100:a:d::',
-       'fc:100:a:d:1::',
-       'fc:100:a:d:1:e::',
-       'fc:100:a:d:1:e:ac::'], function( i, addy ){
-               assertTrueIPv6( addy, addy + ' is a valid IP' );
-       });
-
-       assertFalseIPv6( 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"' );
-       assertFalseIPv6( 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"' );
-
-       assertFalseIPv6( ':::' );
-       assertFalseIPv6( '::0:', 'IPv6 ending in a lone ":"' );
-
-       assertTrueIPv6( '::', 'IPv6 zero address' );
-       $.each(
-       ['::0',
-       '::fc',
-       '::fc:100',
-       '::fc:100:a',
-       '::fc:100:a:d',
-       '::fc:100:a:d:1',
-       '::fc:100:a:d:1:e',
-       '::fc:100:a:d:1:e:ac',
-
-       'fc:100:a:d:1:e:ac:0'], function( i, addy ){
-               assertTrueIPv6( addy, addy + ' is a valid IP' );
-       });
-
-       assertFalseIPv6( '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
-       assertFalseIPv6( '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
-
-       assertFalseIPv6( ':fc::100', 'IPv6 starting with lone ":"' );
-       assertFalseIPv6( 'fc::100:', 'IPv6 ending with lone ":"' );
-       assertFalseIPv6( 'fc:::100', 'IPv6 with ":::" in the middle' );
-
-       assertTrueIPv6( 'fc::100', 'IPv6 with "::" and 2 words' );
-       assertTrueIPv6( 'fc::100:a', 'IPv6 with "::" and 3 words' );
-       assertTrueIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' );
-       assertTrueIPv6( 'fc::100:a:d:1', 'IPv6 with "::" and 5 words' );
-       assertTrueIPv6( 'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words' );
-       assertTrueIPv6( 'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words' );
-       assertTrueIPv6( '2001::df', 'IPv6 with "::" and 2 words' );
-       assertTrueIPv6( '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words' );
-       assertTrueIPv6( '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words' );
-
-       assertFalseIPv6( 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
-       assertFalseIPv6( 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
-});
-
-test( 'isIPv4Address', function() {
-       expect(11);
-
-       // Shortcuts
-       var     assertFalseIPv4 = function( addy, summary ) {
-                       return strictEqual( mw.util.isIPv4Address( addy ), false, summary );
-               },
-               assertTrueIPv4 = function( addy, summary ) {
-                       return strictEqual( mw.util.isIPv4Address( addy ), true, summary );
-               };
-
-       // Based on IPTest.php > testisIPv4
-       assertFalseIPv4( false, 'Boolean false is not an IP' );
-       assertFalseIPv4( true, 'Boolean true is not an IP' );
-       assertFalseIPv4( '', 'Empty string is not an IP' );
-       assertFalseIPv4( 'abc', '"abc" is not an IP' );
-       assertFalseIPv4( ':', 'Colon is not an IP' );
-       assertFalseIPv4( '124.24.52', 'IPv4 not enough quads' );
-       assertFalseIPv4( '24.324.52.13', 'IPv4 out of range' );
-       assertFalseIPv4( '.24.52.13', 'IPv4 starts with period' );
-
-       assertTrueIPv4( '124.24.52.13', '124.24.52.134 is a valid IP' );
-       assertTrueIPv4( '1.24.52.13', '1.24.52.13 is a valid IP' );
-       assertFalseIPv4( '74.24.52.13/20', 'IPv4 ranges are not recogzized as valid IPs' );
-});
diff --git a/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js b/tests/qunit/suites/resources/mediawiki/mediawiki.util.test.js
new file mode 100644 (file)
index 0000000..db78f7c
--- /dev/null
@@ -0,0 +1,301 @@
+module( 'mediawiki.util' );
+
+test( '-- Initial check', function() {
+       expect(1);
+
+       ok( mw.util, 'mw.util defined' );
+});
+
+test( 'rawurlencode', function() {
+       expect(1);
+
+       equal( mw.util.rawurlencode( 'Test:A & B/Here' ), 'Test%3AA%20%26%20B%2FHere' );
+});
+
+test( 'wikiUrlencode', function() {
+       expect(1);
+
+       equal( mw.util.wikiUrlencode( 'Test:A & B/Here' ), 'Test:A_%26_B/Here' );
+});
+
+test( 'wikiGetlink', function() {
+       expect(3);
+
+       // Not part of startUp module
+       mw.config.set( 'wgArticlePath', '/wiki/$1' );
+       mw.config.set( 'wgPageName', 'Foobar' );
+
+       var hrefA = mw.util.wikiGetlink( 'Sandbox' );
+       equal( hrefA, '/wiki/Sandbox', 'Simple title; Get link for "Sandbox"' );
+
+       var hrefB = mw.util.wikiGetlink( 'Foo:Sandbox ? 5+5=10 ! (test)/subpage' );
+       equal( hrefB, '/wiki/Foo:Sandbox_%3F_5%2B5%3D10_%21_%28test%29/subpage',
+               'Advanced title; Get link for "Foo:Sandbox ? 5+5=10 ! (test)/subpage"' );
+
+       var hrefC = mw.util.wikiGetlink();
+       equal( hrefC, '/wiki/Foobar', 'Default title; Get link for current page ("Foobar")' );
+});
+
+test( 'wikiScript', function() {
+       expect(2);
+
+       mw.config.set({
+               'wgScript': '/w/index.php',
+               'wgScriptPath': '/w',
+               'wgScriptExtension': '.php'
+       });
+
+       equal( mw.util.wikiScript(), mw.config.get( 'wgScript' ), 'Defaults to index.php and is equal to wgScript' );
+       equal( mw.util.wikiScript( 'api' ), '/w/api.php', 'API path' );
+
+});
+
+test( 'addCSS', function() {
+       expect(3);
+
+       var $testEl = $( '<div>' ).attr( 'id', 'mw-addcsstest' ).appendTo( 'body' );
+
+       var style = mw.util.addCSS( '#mw-addcsstest { visibility: hidden; }' );
+       equal( typeof style, 'object', 'addCSS returned an object' );
+       strictEqual( style.disabled, false, 'property "disabled" is available and set to false' );
+
+       equal( $testEl.css( 'visibility' ), 'hidden', 'Added style properties are in effect' );
+
+       // Clean up
+       $( style.ownerNode )
+               .add( $testEl )
+               .remove();
+});
+
+test( 'toggleToc', function() {
+       expect(4);
+
+       strictEqual( mw.util.toggleToc(), null, 'Return null if there is no table of contents on the page.' );
+
+       var     tocHtml =
+       '<table id="toc" class="toc"><tr><td>' +
+               '<div id="toctitle">' +
+                       '<h2>Contents</h2>' +
+                       '<span class="toctoggle">&nbsp;[<a href="#" class="internal" id="togglelink">Hide</a>&nbsp;]</span>' +
+               '</div>' +
+               '<ul><li></li></ul>' +
+       '</td></tr></table>',
+               $toc = $(tocHtml).appendTo( 'body' ),
+               $toggleLink = $( '#togglelink' );
+
+       strictEqual( $toggleLink.length, 1, 'Toggle link is appended to the page.' );
+
+       // Toggle animation is asynchronous
+       // QUnit should not finish this test() untill they are all done
+       stop();
+
+       var actionC = function() {
+               start();
+
+               // Clean up
+               $toc.remove();
+       };
+       var actionB = function() {
+               start(); stop();
+               strictEqual( mw.util.toggleToc( $toggleLink, actionC ), true, 'Return boolean true if the TOC is now visible.' );
+       };
+       var actionA = function() {
+               strictEqual( mw.util.toggleToc( $toggleLink, actionB ), false, 'Return boolean false if the TOC is now hidden.' );
+       };
+
+       actionA();
+});
+
+test( 'getParamValue', function() {
+       expect(3);
+
+       var     url1 = 'http://mediawiki.org/?foo=wrong&foo=right#&foo=bad';
+
+       equal( mw.util.getParamValue( 'foo', url1 ), 'right', 'Use latest one, ignore hash' );
+       strictEqual( mw.util.getParamValue( 'bar', url1 ), null, 'Return null when not found' );
+
+       var url2 = 'http://mediawiki.org/#&foo=bad';
+       strictEqual( mw.util.getParamValue( 'foo', url2 ), null, 'Ignore hash if param is not in querystring but in hash (bug 27427)' );
+});
+
+test( 'tooltipAccessKey', function() {
+       expect(3);
+
+       equal( typeof mw.util.tooltipAccessKeyPrefix, 'string', 'mw.util.tooltipAccessKeyPrefix must be a string' );
+       ok( mw.util.tooltipAccessKeyRegexp instanceof RegExp, 'mw.util.tooltipAccessKeyRegexp instance of RegExp' );
+       ok( mw.util.updateTooltipAccessKeys, 'mw.util.updateTooltipAccessKeys' );
+});
+
+test( '$content', function() {
+       expect(2);
+
+       ok( mw.util.$content instanceof jQuery, 'mw.util.$content instance of jQuery' );
+       strictEqual( mw.util.$content.length, 1, 'mw.util.$content must have length of 1' );
+});
+
+test( 'addPortletLink', function() {
+       expect(7);
+
+       var mwPanel = '<div id="mw-panel" class="noprint">\
+       <h5>Toolbox</h5>\
+       <div class="portlet" id="p-tb">\
+               <ul class="body"></ul>\
+       </div>\
+</div>',
+       vectorTabs = '<div id="p-views" class="vectorTabs">\
+       <h5>Views</h5>\
+       <ul></ul>\
+</div>',
+       $mwPanel = $(mwPanel).appendTo( 'body' ),
+       $vectorTabs = $(vectorTabs).appendTo( 'body' );
+
+       var tbRL = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/wiki/ResourceLoader',
+               'ResourceLoader', 't-rl', 'More info about ResourceLoader on MediaWiki.org ', 'l' );
+
+       ok( $.isDomElement( tbRL ), 'addPortletLink returns a valid DOM Element according to $.isDomElement' );
+
+       var     tbMW = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/',
+                       'MediaWiki.org', 't-mworg', 'Go to MediaWiki.org ', 'm', tbRL ),
+               $tbMW = $( tbMW );
+               
+
+       equal( $tbMW.attr( 'id' ), 't-mworg', 'Link has correct ID set' );
+       equal( $tbMW.closest( '.portlet' ).attr( 'id' ), 'p-tb', 'Link was inserted within correct portlet' );
+       equal( $tbMW.next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing nextnode)' );
+
+       var tbRLDM = mw.util.addPortletLink( 'p-tb', 'http://mediawiki.org/wiki/RL/DM',
+               'Default modules', 't-rldm', 'List of all default modules ', 'd', '#t-rl' );
+
+       equal( $( tbRLDM ).next().attr( 'id' ), 't-rl', 'Link is in the correct position (by passing CSS selector)' );
+
+       var caFoo = mw.util.addPortletLink( 'p-views', '#', 'Foo' );
+
+       strictEqual( $tbMW.find( 'span').length, 0, 'No <span> element should be added for porlets without vectorTabs class.' );
+       strictEqual( $( caFoo ).find( 'span').length, 1, 'A <span> element should be added for porlets with vectorTabs class.' );
+       
+       // Clean up
+       $( [tbRL, tbMW, tbRLDM, caFoo] )
+               .add( $mwPanel )
+               .add( $vectorTabs )
+               .remove();
+});
+
+test( 'jsMessage', function() {
+       expect(1);
+
+       var a = mw.util.jsMessage( "MediaWiki is <b>Awesome</b>." );
+       ok( a, 'Basic checking of return value' );
+
+       // Clean up
+       $( '#mw-js-message' ).remove();
+});
+
+test( 'validateEmail', function() {
+       expect(6);
+
+       strictEqual( mw.util.validateEmail( "" ), null, 'Should return null for empty string ' );
+       strictEqual( mw.util.validateEmail( "user@localhost" ), true, 'Return true for a valid e-mail address' );
+
+       // testEmailWithCommasAreInvalids
+       strictEqual( mw.util.validateEmail( "user,foo@example.org" ), false, 'Emails with commas are invalid' );
+       strictEqual( mw.util.validateEmail( "userfoo@ex,ample.org" ), false, 'Emails with commas are invalid' );
+
+       // testEmailWithHyphens
+       strictEqual( mw.util.validateEmail( "user-foo@example.org" ), true, 'Emails may contain a hyphen' );
+       strictEqual( mw.util.validateEmail( "userfoo@ex-ample.org" ), true, 'Emails may contain a hyphen' );
+});
+
+test( 'isIPv6Address', function() {
+       expect(40);
+
+       // Shortcuts
+       var     assertFalseIPv6 = function( addy, summary ) {
+                       return strictEqual( mw.util.isIPv6Address( addy ), false, summary );
+               },
+               assertTrueIPv6 = function( addy, summary ) {
+                       return strictEqual( mw.util.isIPv6Address( addy ), true, summary );
+               };
+
+       // Based on IPTest.php > testisIPv6
+       assertFalseIPv6( ':fc:100::', 'IPv6 starting with lone ":"' );
+       assertFalseIPv6( 'fc:100:::', 'IPv6 ending with a ":::"' );
+       assertFalseIPv6( 'fc:300', 'IPv6 with only 2 words' );
+       assertFalseIPv6( 'fc:100:300', 'IPv6 with only 3 words' );
+
+       $.each(
+       ['fc:100::',
+       'fc:100:a::',
+       'fc:100:a:d::',
+       'fc:100:a:d:1::',
+       'fc:100:a:d:1:e::',
+       'fc:100:a:d:1:e:ac::'], function( i, addy ){
+               assertTrueIPv6( addy, addy + ' is a valid IP' );
+       });
+
+       assertFalseIPv6( 'fc:100:a:d:1:e:ac:0::', 'IPv6 with 8 words ending with "::"' );
+       assertFalseIPv6( 'fc:100:a:d:1:e:ac:0:1::', 'IPv6 with 9 words ending with "::"' );
+
+       assertFalseIPv6( ':::' );
+       assertFalseIPv6( '::0:', 'IPv6 ending in a lone ":"' );
+
+       assertTrueIPv6( '::', 'IPv6 zero address' );
+       $.each(
+       ['::0',
+       '::fc',
+       '::fc:100',
+       '::fc:100:a',
+       '::fc:100:a:d',
+       '::fc:100:a:d:1',
+       '::fc:100:a:d:1:e',
+       '::fc:100:a:d:1:e:ac',
+
+       'fc:100:a:d:1:e:ac:0'], function( i, addy ){
+               assertTrueIPv6( addy, addy + ' is a valid IP' );
+       });
+
+       assertFalseIPv6( '::fc:100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
+       assertFalseIPv6( '::fc:100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
+
+       assertFalseIPv6( ':fc::100', 'IPv6 starting with lone ":"' );
+       assertFalseIPv6( 'fc::100:', 'IPv6 ending with lone ":"' );
+       assertFalseIPv6( 'fc:::100', 'IPv6 with ":::" in the middle' );
+
+       assertTrueIPv6( 'fc::100', 'IPv6 with "::" and 2 words' );
+       assertTrueIPv6( 'fc::100:a', 'IPv6 with "::" and 3 words' );
+       assertTrueIPv6( 'fc::100:a:d', 'IPv6 with "::" and 4 words' );
+       assertTrueIPv6( 'fc::100:a:d:1', 'IPv6 with "::" and 5 words' );
+       assertTrueIPv6( 'fc::100:a:d:1:e', 'IPv6 with "::" and 6 words' );
+       assertTrueIPv6( 'fc::100:a:d:1:e:ac', 'IPv6 with "::" and 7 words' );
+       assertTrueIPv6( '2001::df', 'IPv6 with "::" and 2 words' );
+       assertTrueIPv6( '2001:5c0:1400:a::df', 'IPv6 with "::" and 5 words' );
+       assertTrueIPv6( '2001:5c0:1400:a::df:2', 'IPv6 with "::" and 6 words' );
+
+       assertFalseIPv6( 'fc::100:a:d:1:e:ac:0', 'IPv6 with "::" and 8 words' );
+       assertFalseIPv6( 'fc::100:a:d:1:e:ac:0:1', 'IPv6 with 9 words' );
+});
+
+test( 'isIPv4Address', function() {
+       expect(11);
+
+       // Shortcuts
+       var     assertFalseIPv4 = function( addy, summary ) {
+                       return strictEqual( mw.util.isIPv4Address( addy ), false, summary );
+               },
+               assertTrueIPv4 = function( addy, summary ) {
+                       return strictEqual( mw.util.isIPv4Address( addy ), true, summary );
+               };
+
+       // Based on IPTest.php > testisIPv4
+       assertFalseIPv4( false, 'Boolean false is not an IP' );
+       assertFalseIPv4( true, 'Boolean true is not an IP' );
+       assertFalseIPv4( '', 'Empty string is not an IP' );
+       assertFalseIPv4( 'abc', '"abc" is not an IP' );
+       assertFalseIPv4( ':', 'Colon is not an IP' );
+       assertFalseIPv4( '124.24.52', 'IPv4 not enough quads' );
+       assertFalseIPv4( '24.324.52.13', 'IPv4 out of range' );
+       assertFalseIPv4( '.24.52.13', 'IPv4 starts with period' );
+
+       assertTrueIPv4( '124.24.52.13', '124.24.52.134 is a valid IP' );
+       assertTrueIPv4( '1.24.52.13', '1.24.52.13 is a valid IP' );
+       assertFalseIPv4( '74.24.52.13/20', 'IPv4 ranges are not recogzized as valid IPs' );
+});