Index: branches/JSTesting/tests/qunit/data/testrunner.js |
— | — | @@ -11,6 +11,11 @@ |
12 | 12 | }; |
13 | 13 | |
14 | 14 | /** |
| 15 | + * Configuration |
| 16 | + */ |
| 17 | +QUnit.config.testTimeout = 5000; |
| 18 | + |
| 19 | +/** |
15 | 20 | * Load TestSwarm agent |
16 | 21 | */ |
17 | 22 | if ( QUnit.urlParams.swarmURL ) { |
Index: branches/JSTesting/tests/qunit/suites/resources/mediawiki/mediawiki.test.js |
— | — | @@ -131,7 +131,7 @@ |
132 | 132 | expect(1); |
133 | 133 | |
134 | 134 | // Asynchronous ahead |
135 | | - stop(5000); |
| 135 | + stop(); |
136 | 136 | |
137 | 137 | mw.loader.implement( 'is.awesome', [QUnit.fixurl( mw.config.get( 'wgScriptPath' ) + '/tests/qunit/data/defineTestCallback.js' )], {}, {} ); |
138 | 138 | |
— | — | @@ -155,9 +155,9 @@ |
156 | 156 | // Message doesn't exist already |
157 | 157 | ok( !mw.messages.exists( 'bug29107' ) ); |
158 | 158 | |
159 | | - // Async! Include a timeout, as failure in this test leads to neither the |
160 | | - // success nor failure callbacks getting called. |
161 | | - stop(5000); |
| 159 | + // Async! Failure in this test may lead to neither the success nor error callbacks getting called. |
| 160 | + // Due to QUnit's timeout feauture we won't hang here forever if this happends. |
| 161 | + stop(); |
162 | 162 | |
163 | 163 | mw.loader.implement( 'bug29107.messages-only', [], {}, {'bug29107': 'loaded'} ); |
164 | 164 | mw.loader.using( 'bug29107.messages-only', function() { |
— | — | @@ -180,9 +180,8 @@ |
181 | 181 | base = ('//' + loc.hostname + loc.pathname).replace(/\/[^\/]*$/, ''), |
182 | 182 | target = base + '/data/qunitOkCall.js?' + (new Date()).getTime(); |
183 | 183 | |
184 | | - // Async! Include a timeout, as failure in this test leads to neither the |
185 | | - // success nor failure callbacks getting called. |
186 | | - stop(5000); |
| 184 | + // Async! |
| 185 | + stop(); |
187 | 186 | mw.loader.load( target ); |
188 | 187 | }); |
189 | 188 | |
Index: branches/JSTesting/resources/jquery/jquery.qunit.js |
— | — | @@ -1,5 +1,5 @@ |
2 | 2 | /** |
3 | | - * QUnit - A JavaScript Unit Testing Framework |
| 3 | + * QUnit 1.2.0pre - A JavaScript Unit Testing Framework |
4 | 4 | * |
5 | 5 | * http://docs.jquery.com/QUnit |
6 | 6 | * |
— | — | @@ -21,7 +21,9 @@ |
22 | 22 | })() |
23 | 23 | }; |
24 | 24 | |
25 | | -var testId = 0; |
| 25 | +var testId = 0, |
| 26 | + toString = Object.prototype.toString, |
| 27 | + hasOwn = Object.prototype.hasOwnProperty; |
26 | 28 | |
27 | 29 | var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { |
28 | 30 | this.name = name; |
— | — | @@ -48,7 +50,7 @@ |
49 | 51 | setup: function() { |
50 | 52 | if (this.module != config.previousModule) { |
51 | 53 | if ( config.previousModule ) { |
52 | | - QUnit.moduleDone( { |
| 54 | + runLoggingCallbacks('moduleDone', QUnit, { |
53 | 55 | name: config.previousModule, |
54 | 56 | failed: config.moduleStats.bad, |
55 | 57 | passed: config.moduleStats.all - config.moduleStats.bad, |
— | — | @@ -57,7 +59,7 @@ |
58 | 60 | } |
59 | 61 | config.previousModule = this.module; |
60 | 62 | config.moduleStats = { all: 0, bad: 0 }; |
61 | | - QUnit.moduleStart( { |
| 63 | + runLoggingCallbacks( 'moduleStart', QUnit, { |
62 | 64 | name: this.module |
63 | 65 | } ); |
64 | 66 | } |
— | — | @@ -71,9 +73,10 @@ |
72 | 74 | extend(this.testEnvironment, this.testEnvironmentArg); |
73 | 75 | } |
74 | 76 | |
75 | | - QUnit.testStart( { |
76 | | - name: this.testName |
77 | | - } ); |
| 77 | + runLoggingCallbacks( 'testStart', QUnit, { |
| 78 | + name: this.testName, |
| 79 | + module: this.module |
| 80 | + }); |
78 | 81 | |
79 | 82 | // allow utility functions to access the current test environment |
80 | 83 | // TODO why?? |
— | — | @@ -90,6 +93,7 @@ |
91 | 94 | } |
92 | 95 | }, |
93 | 96 | run: function() { |
| 97 | + config.current = this; |
94 | 98 | if ( this.async ) { |
95 | 99 | QUnit.stop(); |
96 | 100 | } |
— | — | @@ -108,11 +112,12 @@ |
109 | 113 | |
110 | 114 | // Restart the tests if they're blocking |
111 | 115 | if ( config.blocking ) { |
112 | | - start(); |
| 116 | + QUnit.start(); |
113 | 117 | } |
114 | 118 | } |
115 | 119 | }, |
116 | 120 | teardown: function() { |
| 121 | + config.current = this; |
117 | 122 | try { |
118 | 123 | this.testEnvironment.teardown.call(this.testEnvironment); |
119 | 124 | checkPollution(); |
— | — | @@ -121,7 +126,8 @@ |
122 | 127 | } |
123 | 128 | }, |
124 | 129 | finish: function() { |
125 | | - if ( this.expected && this.expected != this.assertions.length ) { |
| 130 | + config.current = this; |
| 131 | + if ( this.expected != null && this.expected != this.assertions.length ) { |
126 | 132 | QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); |
127 | 133 | } |
128 | 134 | |
— | — | @@ -210,8 +216,9 @@ |
211 | 217 | fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); |
212 | 218 | } |
213 | 219 | |
214 | | - QUnit.testDone( { |
| 220 | + runLoggingCallbacks( 'testDone', QUnit, { |
215 | 221 | name: this.testName, |
| 222 | + module: this.module, |
216 | 223 | failed: bad, |
217 | 224 | passed: this.assertions.length - bad, |
218 | 225 | total: this.assertions.length |
— | — | @@ -243,7 +250,7 @@ |
244 | 251 | if (bad) { |
245 | 252 | run(); |
246 | 253 | } else { |
247 | | - synchronize(run); |
| 254 | + synchronize(run, true); |
248 | 255 | }; |
249 | 256 | } |
250 | 257 | |
— | — | @@ -260,7 +267,7 @@ |
261 | 268 | asyncTest: function(testName, expected, callback) { |
262 | 269 | if ( arguments.length === 2 ) { |
263 | 270 | callback = expected; |
264 | | - expected = 0; |
| 271 | + expected = null; |
265 | 272 | } |
266 | 273 | |
267 | 274 | QUnit.test(testName, expected, callback, true); |
— | — | @@ -310,8 +317,8 @@ |
311 | 318 | result: a, |
312 | 319 | message: msg |
313 | 320 | }; |
314 | | - msg = escapeHtml(msg); |
315 | | - QUnit.log(details); |
| 321 | + msg = escapeInnerText(msg); |
| 322 | + runLoggingCallbacks( 'log', QUnit, details ); |
316 | 323 | config.current.assertions.push({ |
317 | 324 | result: a, |
318 | 325 | message: msg |
— | — | @@ -387,8 +394,8 @@ |
388 | 395 | QUnit.ok(ok, message); |
389 | 396 | }, |
390 | 397 | |
391 | | - start: function() { |
392 | | - config.semaphore--; |
| 398 | + start: function(count) { |
| 399 | + config.semaphore -= count || 1; |
393 | 400 | if (config.semaphore > 0) { |
394 | 401 | // don't start until equal number of stop-calls |
395 | 402 | return; |
— | — | @@ -408,28 +415,38 @@ |
409 | 416 | } |
410 | 417 | |
411 | 418 | config.blocking = false; |
412 | | - process(); |
| 419 | + process(true); |
413 | 420 | }, 13); |
414 | 421 | } else { |
415 | 422 | config.blocking = false; |
416 | | - process(); |
| 423 | + process(true); |
417 | 424 | } |
418 | 425 | }, |
419 | 426 | |
420 | | - stop: function(timeout) { |
421 | | - config.semaphore++; |
| 427 | + stop: function(count) { |
| 428 | + config.semaphore += count || 1; |
422 | 429 | config.blocking = true; |
423 | 430 | |
424 | | - if ( timeout && defined.setTimeout ) { |
| 431 | + if ( config.testTimeout && defined.setTimeout ) { |
425 | 432 | clearTimeout(config.timeout); |
426 | 433 | config.timeout = window.setTimeout(function() { |
427 | 434 | QUnit.ok( false, "Test timed out" ); |
| 435 | + config.semaphore = 1; |
428 | 436 | QUnit.start(); |
429 | | - }, timeout); |
| 437 | + }, config.testTimeout); |
430 | 438 | } |
431 | 439 | } |
432 | 440 | }; |
433 | 441 | |
| 442 | +//We want access to the constructor's prototype |
| 443 | +(function() { |
| 444 | + function F(){}; |
| 445 | + F.prototype = QUnit; |
| 446 | + QUnit = new F(); |
| 447 | + //Make F QUnit's constructor so that we can add to the prototype later |
| 448 | + QUnit.constructor = F; |
| 449 | +})(); |
| 450 | + |
434 | 451 | // Backwards compatibility, deprecated |
435 | 452 | QUnit.equals = QUnit.equal; |
436 | 453 | QUnit.same = QUnit.deepEqual; |
— | — | @@ -453,7 +470,16 @@ |
454 | 471 | // by default, modify document.title when suite is done |
455 | 472 | altertitle: true, |
456 | 473 | |
457 | | - urlConfig: ['noglobals', 'notrycatch'] |
| 474 | + urlConfig: ['noglobals', 'notrycatch'], |
| 475 | + |
| 476 | + //logging callback queues |
| 477 | + begin: [], |
| 478 | + done: [], |
| 479 | + log: [], |
| 480 | + testStart: [], |
| 481 | + testDone: [], |
| 482 | + moduleStart: [], |
| 483 | + moduleDone: [] |
458 | 484 | }; |
459 | 485 | |
460 | 486 | // Load paramaters |
— | — | @@ -586,8 +612,7 @@ |
587 | 613 | return "null"; |
588 | 614 | } |
589 | 615 | |
590 | | - var type = Object.prototype.toString.call( obj ) |
591 | | - .match(/^\[object\s(.*)\]$/)[1] || ''; |
| 616 | + var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || ''; |
592 | 617 | |
593 | 618 | switch (type) { |
594 | 619 | case 'Number': |
— | — | @@ -618,10 +643,10 @@ |
619 | 644 | expected: expected |
620 | 645 | }; |
621 | 646 | |
622 | | - message = escapeHtml(message) || (result ? "okay" : "failed"); |
| 647 | + message = escapeInnerText(message) || (result ? "okay" : "failed"); |
623 | 648 | message = '<span class="test-message">' + message + "</span>"; |
624 | | - expected = escapeHtml(QUnit.jsDump.parse(expected)); |
625 | | - actual = escapeHtml(QUnit.jsDump.parse(actual)); |
| 649 | + expected = escapeInnerText(QUnit.jsDump.parse(expected)); |
| 650 | + actual = escapeInnerText(QUnit.jsDump.parse(actual)); |
626 | 651 | var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>'; |
627 | 652 | if (actual != expected) { |
628 | 653 | output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>'; |
— | — | @@ -631,12 +656,12 @@ |
632 | 657 | var source = sourceFromStacktrace(); |
633 | 658 | if (source) { |
634 | 659 | details.source = source; |
635 | | - output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeHtml(source) + '</pre></td></tr>'; |
| 660 | + output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>'; |
636 | 661 | } |
637 | 662 | } |
638 | 663 | output += "</table>"; |
639 | 664 | |
640 | | - QUnit.log(details); |
| 665 | + runLoggingCallbacks( 'log', QUnit, details ); |
641 | 666 | |
642 | 667 | config.current.assertions.push({ |
643 | 668 | result: !!result, |
— | — | @@ -649,6 +674,9 @@ |
650 | 675 | var querystring = "?", |
651 | 676 | key; |
652 | 677 | for ( key in params ) { |
| 678 | + if ( !hasOwn.call( params, key ) ) { |
| 679 | + continue; |
| 680 | + } |
653 | 681 | querystring += encodeURIComponent( key ) + "=" + |
654 | 682 | encodeURIComponent( params[ key ] ) + "&"; |
655 | 683 | } |
— | — | @@ -657,23 +685,28 @@ |
658 | 686 | |
659 | 687 | extend: extend, |
660 | 688 | id: id, |
661 | | - addEvent: addEvent, |
| 689 | + addEvent: addEvent |
| 690 | +}); |
662 | 691 | |
| 692 | +//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later |
| 693 | +//Doing this allows us to tell if the following methods have been overwritten on the actual |
| 694 | +//QUnit object, which is a deprecated way of using the callbacks. |
| 695 | +extend(QUnit.constructor.prototype, { |
663 | 696 | // Logging callbacks; all receive a single argument with the listed properties |
664 | 697 | // run test/logs.html for any related changes |
665 | | - begin: function() {}, |
| 698 | + begin: registerLoggingCallback('begin'), |
666 | 699 | // done: { failed, passed, total, runtime } |
667 | | - done: function() {}, |
| 700 | + done: registerLoggingCallback('done'), |
668 | 701 | // log: { result, actual, expected, message } |
669 | | - log: function() {}, |
| 702 | + log: registerLoggingCallback('log'), |
670 | 703 | // testStart: { name } |
671 | | - testStart: function() {}, |
| 704 | + testStart: registerLoggingCallback('testStart'), |
672 | 705 | // testDone: { name, failed, passed, total } |
673 | | - testDone: function() {}, |
| 706 | + testDone: registerLoggingCallback('testDone'), |
674 | 707 | // moduleStart: { name } |
675 | | - moduleStart: function() {}, |
| 708 | + moduleStart: registerLoggingCallback('moduleStart'), |
676 | 709 | // moduleDone: { name, failed, passed, total } |
677 | | - moduleDone: function() {} |
| 710 | + moduleDone: registerLoggingCallback('moduleDone') |
678 | 711 | }); |
679 | 712 | |
680 | 713 | if ( typeof document === "undefined" || document.readyState === "complete" ) { |
— | — | @@ -681,7 +714,7 @@ |
682 | 715 | } |
683 | 716 | |
684 | 717 | QUnit.load = function() { |
685 | | - QUnit.begin({}); |
| 718 | + runLoggingCallbacks( 'begin', QUnit, {} ); |
686 | 719 | |
687 | 720 | // Initialize the config, saving the execution queue |
688 | 721 | var oldconfig = extend({}, config); |
— | — | @@ -756,12 +789,23 @@ |
757 | 790 | |
758 | 791 | addEvent(window, "load", QUnit.load); |
759 | 792 | |
| 793 | +// addEvent(window, "error") gives us a useless event object |
| 794 | +window.onerror = function( message, file, line ) { |
| 795 | + if ( QUnit.config.current ) { |
| 796 | + ok( false, message + ", " + file + ":" + line ); |
| 797 | + } else { |
| 798 | + test( "global failure", function() { |
| 799 | + ok( false, message + ", " + file + ":" + line ); |
| 800 | + }); |
| 801 | + } |
| 802 | +}; |
| 803 | + |
760 | 804 | function done() { |
761 | 805 | config.autorun = true; |
762 | 806 | |
763 | 807 | // Log the last module results |
764 | 808 | if ( config.currentModule ) { |
765 | | - QUnit.moduleDone( { |
| 809 | + runLoggingCallbacks( 'moduleDone', QUnit, { |
766 | 810 | name: config.currentModule, |
767 | 811 | failed: config.moduleStats.bad, |
768 | 812 | passed: config.moduleStats.all - config.moduleStats.bad, |
— | — | @@ -803,7 +847,7 @@ |
804 | 848 | ].join(" "); |
805 | 849 | } |
806 | 850 | |
807 | | - QUnit.done( { |
| 851 | + runLoggingCallbacks( 'done', QUnit, { |
808 | 852 | failed: config.stats.bad, |
809 | 853 | passed: passed, |
810 | 854 | total: config.stats.all, |
— | — | @@ -855,16 +899,14 @@ |
856 | 900 | } |
857 | 901 | } |
858 | 902 | |
859 | | -function escapeHtml(s) { |
| 903 | +function escapeInnerText(s) { |
860 | 904 | if (!s) { |
861 | 905 | return ""; |
862 | 906 | } |
863 | 907 | s = s + ""; |
864 | | - return s.replace(/[\&"<>\\]/g, function(s) { |
| 908 | + return s.replace(/[\&<>]/g, function(s) { |
865 | 909 | switch(s) { |
866 | 910 | case "&": return "&"; |
867 | | - case "\\": return "\\\\"; |
868 | | - case '"': return '\"'; |
869 | 911 | case "<": return "<"; |
870 | 912 | case ">": return ">"; |
871 | 913 | default: return s; |
— | — | @@ -872,26 +914,30 @@ |
873 | 915 | }); |
874 | 916 | } |
875 | 917 | |
876 | | -function synchronize( callback ) { |
| 918 | +function synchronize( callback, last ) { |
877 | 919 | config.queue.push( callback ); |
878 | 920 | |
879 | 921 | if ( config.autorun && !config.blocking ) { |
880 | | - process(); |
| 922 | + process(last); |
881 | 923 | } |
882 | 924 | } |
883 | 925 | |
884 | | -function process() { |
885 | | - var start = (new Date()).getTime(); |
| 926 | +function process( last ) { |
| 927 | + var start = new Date().getTime(); |
| 928 | + config.depth = config.depth ? config.depth + 1 : 1; |
886 | 929 | |
887 | 930 | while ( config.queue.length && !config.blocking ) { |
888 | | - if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { |
| 931 | + if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { |
889 | 932 | config.queue.shift()(); |
890 | 933 | } else { |
891 | | - window.setTimeout( process, 13 ); |
| 934 | + window.setTimeout( function(){ |
| 935 | + process( last ); |
| 936 | + }, 13 ); |
892 | 937 | break; |
893 | 938 | } |
894 | 939 | } |
895 | | - if (!config.blocking && !config.queue.length) { |
| 940 | + config.depth--; |
| 941 | + if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { |
896 | 942 | done(); |
897 | 943 | } |
898 | 944 | } |
— | — | @@ -901,6 +947,9 @@ |
902 | 948 | |
903 | 949 | if ( config.noglobals ) { |
904 | 950 | for ( var key in window ) { |
| 951 | + if ( !hasOwn.call( window, key ) ) { |
| 952 | + continue; |
| 953 | + } |
905 | 954 | config.pollution.push( key ); |
906 | 955 | } |
907 | 956 | } |
— | — | @@ -951,7 +1000,9 @@ |
952 | 1001 | for ( var prop in b ) { |
953 | 1002 | if ( b[prop] === undefined ) { |
954 | 1003 | delete a[prop]; |
955 | | - } else { |
| 1004 | + |
| 1005 | + // Avoid "Member not found" error in IE8 caused by setting window.constructor |
| 1006 | + } else if ( prop !== "constructor" || a !== window ) { |
956 | 1007 | a[prop] = b[prop]; |
957 | 1008 | } |
958 | 1009 | } |
— | — | @@ -974,9 +1025,27 @@ |
975 | 1026 | document.getElementById( name ); |
976 | 1027 | } |
977 | 1028 | |
| 1029 | +function registerLoggingCallback(key){ |
| 1030 | + return function(callback){ |
| 1031 | + config[key].push( callback ); |
| 1032 | + }; |
| 1033 | +} |
| 1034 | + |
| 1035 | +// Supports deprecated method of completely overwriting logging callbacks |
| 1036 | +function runLoggingCallbacks(key, scope, args) { |
| 1037 | + //debugger; |
| 1038 | + var callbacks; |
| 1039 | + if ( QUnit.hasOwnProperty(key) ) { |
| 1040 | + QUnit[key].call(scope, args); |
| 1041 | + } else { |
| 1042 | + callbacks = config[key]; |
| 1043 | + for( var i = 0; i < callbacks.length; i++ ) { |
| 1044 | + callbacks[i].call( scope, args ); |
| 1045 | + } |
| 1046 | + } |
| 1047 | +} |
| 1048 | + |
978 | 1049 | // Test for equality any JavaScript type. |
979 | | -// Discussions and reference: http://philrathe.com/articles/equiv |
980 | | -// Test suites: http://philrathe.com/tests/equiv |
981 | 1050 | // Author: Philippe Rathé <prathe@gmail.com> |
982 | 1051 | QUnit.equiv = function () { |
983 | 1052 | |
— | — | @@ -1226,7 +1295,12 @@ |
1227 | 1296 | type = "document"; |
1228 | 1297 | } else if (obj.nodeType) { |
1229 | 1298 | type = "node"; |
1230 | | - } else if (typeof obj === "object" && typeof obj.length === "number" && obj.length >= 0) { |
| 1299 | + } else if ( |
| 1300 | + // native arrays |
| 1301 | + toString.call( obj ) === "[object Array]" || |
| 1302 | + // NodeList objects |
| 1303 | + ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) |
| 1304 | + ) { |
1231 | 1305 | type = "array"; |
1232 | 1306 | } else { |
1233 | 1307 | type = typeof obj; |
— | — | @@ -1408,6 +1482,9 @@ |
1409 | 1483 | } |
1410 | 1484 | |
1411 | 1485 | for (var i in ns) { |
| 1486 | + if ( !hasOwn.call( ns, i ) ) { |
| 1487 | + continue; |
| 1488 | + } |
1412 | 1489 | if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { |
1413 | 1490 | n[ns[i].rows[0]] = { |
1414 | 1491 | text: n[ns[i].rows[0]], |
Index: branches/JSTesting/resources/jquery/jquery.qunit.css |
— | — | @@ -1,5 +1,5 @@ |
2 | 2 | /** |
3 | | - * QUnit - A JavaScript Unit Testing Framework |
| 3 | + * QUnit 1.2.0pre - A JavaScript Unit Testing Framework |
4 | 4 | * |
5 | 5 | * http://docs.jquery.com/QUnit |
6 | 6 | * |