From ca32f08966f1b51fcb19460f0996bb0c4048e6fe Mon Sep 17 00:00:00 2001 From: Pierre Schmitz Date: Sat, 3 Dec 2011 13:29:22 +0100 Subject: Update to MediaWiki 1.18.0 * also update ArchLinux skin to chagnes in MonoBook * Use only css to hide our menu bar when printing --- tests/qunit/.htaccess | 1 + tests/qunit/data/defineTestCallback.js | 4 + tests/qunit/data/testrunner.js | 92 ++++ tests/qunit/data/testwarm.inject.js | 349 +++++++++++++++ tests/qunit/index.html | 90 ++++ .../suites/resources/jquery/jquery.autoEllipsis.js | 58 +++ .../suites/resources/jquery/jquery.byteLength.js | 42 ++ .../suites/resources/jquery/jquery.byteLimit.js | 155 +++++++ .../qunit/suites/resources/jquery/jquery.client.js | 205 +++++++++ .../suites/resources/jquery/jquery.colorUtil.js | 71 +++ .../suites/resources/jquery/jquery.getAttrs.js | 17 + .../suites/resources/jquery/jquery.localize.js | 119 ++++++ .../suites/resources/jquery/jquery.mwPrototypes.js | 56 +++ .../suites/resources/jquery/jquery.tabIndex.js | 50 +++ .../resources/jquery/jquery.tablesorter.test.js | 475 +++++++++++++++++++++ .../mediawiki.special.recentchanges.js | 71 +++ .../qunit/suites/resources/mediawiki/mediawiki.js | 232 ++++++++++ .../resources/mediawiki/mediawiki.jscompat.test.js | 35 ++ .../suites/resources/mediawiki/mediawiki.user.js | 29 ++ .../suites/resources/mediawiki/mediawiki.util.js | 307 +++++++++++++ 20 files changed, 2458 insertions(+) create mode 100644 tests/qunit/.htaccess create mode 100644 tests/qunit/data/defineTestCallback.js create mode 100644 tests/qunit/data/testrunner.js create mode 100644 tests/qunit/data/testwarm.inject.js create mode 100644 tests/qunit/index.html create mode 100644 tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js create mode 100644 tests/qunit/suites/resources/jquery/jquery.byteLength.js create mode 100644 tests/qunit/suites/resources/jquery/jquery.byteLimit.js create mode 100644 tests/qunit/suites/resources/jquery/jquery.client.js create mode 100644 tests/qunit/suites/resources/jquery/jquery.colorUtil.js create mode 100644 tests/qunit/suites/resources/jquery/jquery.getAttrs.js create mode 100644 tests/qunit/suites/resources/jquery/jquery.localize.js create mode 100644 tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js create mode 100644 tests/qunit/suites/resources/jquery/jquery.tabIndex.js create mode 100644 tests/qunit/suites/resources/jquery/jquery.tablesorter.test.js create mode 100644 tests/qunit/suites/resources/mediawiki.special/mediawiki.special.recentchanges.js create mode 100644 tests/qunit/suites/resources/mediawiki/mediawiki.js create mode 100644 tests/qunit/suites/resources/mediawiki/mediawiki.jscompat.test.js create mode 100644 tests/qunit/suites/resources/mediawiki/mediawiki.user.js create mode 100644 tests/qunit/suites/resources/mediawiki/mediawiki.util.js (limited to 'tests/qunit') diff --git a/tests/qunit/.htaccess b/tests/qunit/.htaccess new file mode 100644 index 00000000..605d2f4c --- /dev/null +++ b/tests/qunit/.htaccess @@ -0,0 +1 @@ +Allow from all diff --git a/tests/qunit/data/defineTestCallback.js b/tests/qunit/data/defineTestCallback.js new file mode 100644 index 00000000..6fcd4595 --- /dev/null +++ b/tests/qunit/data/defineTestCallback.js @@ -0,0 +1,4 @@ +window.mw.loader.testCallback = function() { + start(); + ok( true, 'Implementing a module, is the callback timed properly ?'); +}; diff --git a/tests/qunit/data/testrunner.js b/tests/qunit/data/testrunner.js new file mode 100644 index 00000000..dbfe9fad --- /dev/null +++ b/tests/qunit/data/testrunner.js @@ -0,0 +1,92 @@ +( function( $ ) { + +/** + * Add bogus to url to prevent IE crazy caching + * + * @param value {String} a relative path (eg. 'data/defineTestCallback.js' or 'data/test.php?foo=bar') + * @return {String} Such as 'data/defineTestCallback.js?131031765087663960' + */ +QUnit.fixurl = function(value) { + return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random()*100000); +}; + +/** + * Load TestSwarm agent + */ +if ( QUnit.urlParams.swarmURL ) { + document.write(""); +} + +/** + * Load completenesstest + */ +if ( QUnit.urlParams.completenesstest ) { + + // Return true to ignore + var mwTestIgnore = function( val, tester, funcPath ) { + + // Don't record methods of the properties of constructors, + // to avoid getting into a loop (prototype.constructor.prototype..). + // Since we're therefor skipping any injection for + // "new mw.Foo()", manually set it to true here. + if ( val instanceof mw.Map ) { + tester.methodCallTracker['Map'] = true; + return true; + } + + // Don't record methods of the properties of a jQuery object + if ( val instanceof $ ) { + return true; + } + + return false; + }; + + var mwTester = new CompletenessTest( mw, mwTestIgnore ); +} + +/** + * Add-on assertion helpers + */ +// Define the add-ons +var addons = { + + // Expect boolean true + assertTrue: function( actual, message ) { + strictEqual( actual, true, message ); + }, + + // Expect boolean false + assertFalse: function( actual, message ) { + strictEqual( actual, false, message ); + }, + + // Expect numerical value less than X + lt: function( actual, expected, message ) { + QUnit.push( actual < expected, actual, 'less than ' + expected, message ); + }, + + // Expect numerical value less than or equal to X + ltOrEq: function( actual, expected, message ) { + QUnit.push( actual <= expected, actual, 'less than or equal to ' + expected, message ); + }, + + // Expect numerical value greater than X + gt: function( actual, expected, message ) { + QUnit.push( actual > expected, actual, 'greater than ' + expected, message ); + }, + + // Expect numerical value greater than or equal to X + gtOrEq: function( actual, expected, message ) { + QUnit.push( actual >= expected, actual, 'greater than or equal to ' + expected, message ); + }, + + // Backwards compatible with new verions of QUnit + equals: window.equal, + same: window.deepEqual +}; + +$.extend( QUnit, addons ); +$.extend( window, addons ); + +})( jQuery ); diff --git a/tests/qunit/data/testwarm.inject.js b/tests/qunit/data/testwarm.inject.js new file mode 100644 index 00000000..14ee8f93 --- /dev/null +++ b/tests/qunit/data/testwarm.inject.js @@ -0,0 +1,349 @@ +/* + Copyright (c) 2009 John Resig + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + +*/ +(function(){ + + var DEBUG = false; + + var doPost = false; + + try { + doPost = !!window.top.postMessage; + } catch(e){} + + var search = window.location.search, + url, index; + if( ( index = search.indexOf( "swarmURL=" ) ) != -1 ) + url = decodeURIComponent( search.slice( index + 9 ) ); + + if ( !DEBUG && (!url || url.indexOf("http") !== 0) ) { + return; + } + + var submitTimeout = 5; + + var curHeartbeat; + var beatRate = 20; + + // Expose the TestSwarm API + window.TestSwarm = { + submit: submit, + heartbeat: function(){ + if ( curHeartbeat ) { + clearTimeout( curHeartbeat ); + } + + curHeartbeat = setTimeout(function(){ + submit({ fail: -1, total: -1 }); + }, beatRate * 1000); + }, + serialize: function(){ + return trimSerialize(); + } + }; + + // Prevent careless things from executing + window.print = window.confirm = window.alert = window.open = function(){}; + + window.onerror = function(e){ + document.body.appendChild( document.createTextNode( "ERROR: " + e )); + submit({ fail: 0, error: 1, total: 1 }); + return false; + }; + + // QUnit (jQuery) + // http://docs.jquery.com/QUnit + if ( typeof QUnit !== "undefined" ) { + QUnit.done = function(results){ + submit({ + fail: results.failed, + error: 0, + total: results.total + }); + }; + + QUnit.log = window.TestSwarm.heartbeat; + window.TestSwarm.heartbeat(); + + window.TestSwarm.serialize = function(){ + // Clean up the HTML (remove any un-needed test markup) + remove("nothiddendiv"); + remove("loadediframe"); + remove("dl"); + remove("main"); + + // Show any collapsed results + var ol = document.getElementsByTagName("ol"); + for ( var i = 0; i < ol.length; i++ ) { + ol[i].style.display = "block"; + } + + return trimSerialize(); + }; + + // UnitTestJS (Prototype, Scriptaculous) + // http://github.com/tobie/unittest_js/tree/master + } else if ( typeof Test !== "undefined" && Test && Test.Unit && Test.Unit.runners ) { + var total_runners = Test.Unit.runners.length, cur_runners = 0; + var total = 0, fail = 0, error = 0; + + for (var i = 0; i < Test.Unit.runners.length; i++) (function(i){ + var finish = Test.Unit.runners[i].finish; + Test.Unit.runners[i].finish = function(){ + finish.call( this ); + + var results = this.getResult(); + total += results.assertions; + fail += results.failures; + error += results.errors; + + if ( ++cur_runners === total_runners ) { + submit({ + fail: fail, + error: error, + total: total + }); + } + }; + })(i); + + // JSSpec (MooTools) + // http://jania.pe.kr/aw/moin.cgi/JSSpec + } else if ( typeof JSSpec !== "undefined" && JSSpec && JSSpec.Logger ) { + var onRunnerEnd = JSSpec.Logger.prototype.onRunnerEnd; + JSSpec.Logger.prototype.onRunnerEnd = function(){ + onRunnerEnd.call(this); + + // Show any collapsed results + var ul = document.getElementsByTagName("ul"); + for ( var i = 0; i < ul.length; i++ ) { + ul[i].style.display = "block"; + } + + submit({ + fail: JSSpec.runner.getTotalFailures(), + error: JSSpec.runner.getTotalErrors(), + total: JSSpec.runner.totalExamples + }); + }; + + window.TestSwarm.serialize = function(){ + // Show any collapsed results + var ul = document.getElementsByTagName("ul"); + for ( var i = 0; i < ul.length; i++ ) { + ul[i].style.display = "block"; + } + + return trimSerialize(); + }; + + // JSUnit + // http://www.jsunit.net/ + // Note: Injection file must be included before the frames + // are document.write()d into the page. + } else if ( typeof JsUnitTestManager !== "undefined" ) { + var _done = JsUnitTestManager.prototype._done; + JsUnitTestManager.prototype._done = function(){ + _done.call(this); + + submit({ + fail: this.failureCount, + error: this.errorCount, + total: this.totalCount + }); + }; + + window.TestSwarm.serialize = function(){ + return "
" + this.log.join("\n") + "
"; + }; + + // Selenium Core + // http://seleniumhq.org/projects/core/ + } else if ( typeof SeleniumTestResult !== "undefined" && typeof LOG !== "undefined" ) { + // Completely overwrite the postback + SeleniumTestResult.prototype.post = function(){ + submit({ + fail: this.metrics.numCommandFailures, + error: this.metrics.numCommandErrors, + total: this.metrics.numCommandPasses + this.metrics.numCommandFailures + this.metrics.numCommandErrors + }); + }; + + window.TestSwarm.serialize = function(){ + var results = []; + while ( LOG.pendingMessages.length ) { + var msg = LOG.pendingMessages.shift(); + results.push( msg.type + ": " + msg.msg ); + } + + return "
" + results.join("\n") + "
"; + }; + + // Dojo Objective Harness + // http://docs.dojocampus.org/quickstart/doh + } else if ( typeof doh !== "undefined" && doh._report ) { + var _report = doh._report; + doh._report = function(){ + _report.apply(this, arguments); + + submit({ + fail: doh._failureCount, + error: doh._errorCount, + total: doh._testCount + }); + }; + + window.TestSwarm.serialize = function(){ + return "
" + document.getElementById("logBody").innerHTML + "
"; + }; + // Screw.Unit + // git://github.com/nathansobo/screw-unit.git + } else if ( typeof Screw !== "undefined" && typeof jQuery !== 'undefined' && Screw && Screw.Unit ) { + $(Screw).bind("after", function() { + var passed = $('.passed').length; + var failed = $('.failed').length; + submit({ + fail: failed, + error: 0, + total: failed + passed + }); + }); + + $(Screw).bind("loaded", function() { + $('.it') + .bind("passed", window.TestSwarm.heartbeat) + .bind("failed", window.TestSwarm.heartbeat); + window.TestSwarm.heartbeat(); + }); + + window.TestSwarm.serialize = function(){ + return trimSerialize(); + }; + } + + function trimSerialize(doc) { + doc = doc || document; + + var scripts = doc.getElementsByTagName("script"); + while ( scripts.length ) { + remove( scripts[0] ); + } + + var root = window.location.href.replace(/(https?:\/\/.*?)\/.*/, "$1"); + var cur = window.location.href.replace(/[^\/]*$/, ""); + + var links = doc.getElementsByTagName("link"); + for ( var i = 0; i < links.length; i++ ) { + var href = links[i].href; + if ( href.indexOf("/") === 0 ) { + href = root + href; + } else if ( !/^https?:\/\//.test( href ) ) { + href = cur + href; + } + links[i].href = href; + } + + return ("" + doc.documentElement.innerHTML + "") + .replace(/\s+/g, " "); + } + + function remove(elem){ + if ( typeof elem === "string" ) { + elem = document.getElementById( elem ); + } + + if ( elem ) { + elem.parentNode.removeChild( elem ); + } + } + + function submit(params){ + if ( curHeartbeat ) { + clearTimeout( curHeartbeat ); + } + + var paramItems = (url.split("?")[1] || "").split("&"); + + for ( var i = 0; i < paramItems.length; i++ ) { + if ( paramItems[i] ) { + var parts = paramItems[i].split("="); + if ( !params[ parts[0] ] ) { + params[ parts[0] ] = parts[1]; + } + } + } + + if ( !params.state ) { + params.state = "saverun"; + } + + if ( !params.results ) { + params.results = window.TestSwarm.serialize(); + } + + if ( doPost ) { + // Build Query String + var query = ""; + + for ( var i in params ) { + query += ( query ? "&" : "" ) + i + "=" + + encodeURIComponent(params[i]); + } + + if ( DEBUG ) { + alert( query ); + } else { + window.top.postMessage( query, "*" ); + } + + } else { + var form = document.createElement("form"); + form.action = url; + form.method = "POST"; + + for ( var i in params ) { + var input = document.createElement("input"); + input.type = "hidden"; + input.name = i; + input.value = params[i]; + form.appendChild( input ); + } + + if ( DEBUG ) { + alert( form.innerHTML ); + } else { + + // Watch for the result submission timing out + setTimeout(function(){ + submit( params ); + }, submitTimeout * 1000); + + document.body.appendChild( form ); + form.submit(); + } + } + } + +})(); diff --git a/tests/qunit/index.html b/tests/qunit/index.html new file mode 100644 index 00000000..d4bbe7e2 --- /dev/null +++ b/tests/qunit/index.html @@ -0,0 +1,90 @@ + + + + MediaWiki JavaScript Test Suite + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

MediaWiki JavaScript Test Suite

+

+
+

See testing documentation on mediawiki.org

+
+

+
    + + +
    + + diff --git a/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js new file mode 100644 index 00000000..caf5a6f1 --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.autoEllipsis.js @@ -0,0 +1,58 @@ +module( 'jquery.autoEllipsis.js' ); + +test( '-- Initial check', function() { + expect(1); + ok( $.fn.autoEllipsis, 'jQuery.fn.autoEllipsis defined' ); +}); + +function createWrappedDiv( text, width ) { + var $wrapper = $( '
    ' ).css( 'width', width ); + var $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 new file mode 100644 index 00000000..f82fda27 --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.byteLength.js @@ -0,0 +1,42 @@ +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.byteLimit.js b/tests/qunit/suites/resources/jquery/jquery.byteLimit.js new file mode 100644 index 00000000..461ea49b --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.byteLimit.js @@ -0,0 +1,155 @@ +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) ); + } + } +}; +var blti = 0; +/** + * Test factory for $.fn.byteLimit + * + * @param $input {jQuery} jQuery object in an input element + * @param useLimit {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: '', + useLimit: false, + expected: 0, + limit: null + }, options); + var i = blti++; + + test( opt.description, function() { + + opt.$input.appendTo( 'body' ); + + // Simulate pressing keys for each of the sample characters + $.addChars( opt.$input, opt.sample ); + var newVal = opt.$input.val(); + + if ( opt.useLimit ) { + expect(2); + + ltOrEq( $.byteLength( newVal ), opt.limit, 'Prevent keypresses after byteLimit was reached, length never exceeded the limit' ); + equal( $.byteLength( newVal ), opt.expected, 'Not preventing keypresses too early, length has reached the expected length' ); + + } else { + expect(1); + equal( $.byteLength( newVal ), 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: $( '' ) + .attr( { + 'type': 'text' + }), + sample: simpleSample, + useLimit: false, + expected: $.byteLength( simpleSample ) +}); + +byteLimitTest({ + description: 'Limit using the maxlength attribute', + $input: $( '' ) + .attr( { + 'type': 'text', + 'maxlength': '10' + }) + .byteLimit(), + sample: simpleSample, + useLimit: true, + limit: 10, + expected: 10 +}); + +byteLimitTest({ + description: 'Limit using a custom value', + $input: $( '' ) + .attr( { + 'type': 'text' + }) + .byteLimit( 10 ), + sample: simpleSample, + useLimit: true, + limit: 10, + expected: 10 +}); + +byteLimitTest({ + description: 'Limit using a custom value, overriding maxlength attribute', + $input: $( '' ) + .attr( { + 'type': 'text', + 'maxLength': '10' + }) + .byteLimit( 15 ), + sample: simpleSample, + useLimit: true, + limit: 15, + expected: 15 +}); + +byteLimitTest({ + description: 'Limit using a custom value (multibyte)', + $input: $( '' ) + .attr( { + 'type': 'text' + }) + .byteLimit( 14 ), + sample: mbSample, + useLimit: true, + limit: 14, + expected: 14 // (10 x 1-byte char) + (1 x 3-byte char) + (1 x 1-byte char) +}); + +byteLimitTest({ + description: 'Limit using a custom value (multibyte) overlapping a byte', + $input: $( '' ) + .attr( { + 'type': 'text' + }) + .byteLimit( 12 ), + sample: mbSample, + useLimit: true, + limit: 12, + expected: 12 // 10 x 1-byte char. The next 3-byte char exceeds limit of 12, but 2 more 1-byte chars come in after. +}); diff --git a/tests/qunit/suites/resources/jquery/jquery.client.js b/tests/qunit/suites/resources/jquery/jquery.client.js new file mode 100644 index 00000000..50df2928 --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.client.js @@ -0,0 +1,205 @@ +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.colorUtil.js b/tests/qunit/suites/resources/jquery/jquery.colorUtil.js new file mode 100644 index 00000000..93f12b82 --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.colorUtil.js @@ -0,0 +1,71 @@ +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.getAttrs.js b/tests/qunit/suites/resources/jquery/jquery.getAttrs.js new file mode 100644 index 00000000..3d3d01e1 --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.getAttrs.js @@ -0,0 +1,17 @@ +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 = $( '
    ', 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 new file mode 100644 index 00000000..40b58687 --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.localize.js @@ -0,0 +1,119 @@ +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 = '
    '; + $lc = $( html ).localize().find( 'span' ); + + strictEqual( $lc.text(), 'Basic stuff', 'Tag: html:msg' ); + + // Attribute: title-msg + html = '
    '; + $lc = $( html ).localize().find( 'span' ); + + strictEqual( $lc.attr( 'title' ), 'Basic stuff', 'Attribute: title-msg' ); + + // Attribute: alt-msg + html = '
    '; + $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', '' ); + + // 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 = '
    '; + $lc = $( html ).localize().find( 'span' ); + + strictEqual( $lc.text(), mw.msg( 'properfoo' ), 'Content is inserted as text, not as html.' ); + + // Attribute escaping + html = '
    '; + $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 = '
    '; + $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 = '
    '; + $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 = '
    '; + $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 = '
    '; + $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.mwPrototypes.js b/tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js new file mode 100644 index 00000000..bb6d2a1b --- /dev/null +++ b/tests/qunit/suites/resources/jquery/jquery.mwPrototypes.js @@ -0,0 +1,56 @@ +module( 'jquery.mwPrototypes.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( '