diff options
Diffstat (limited to 'test/data/testrunner.js')
-rw-r--r-- | test/data/testrunner.js | 469 |
1 files changed, 235 insertions, 234 deletions
diff --git a/test/data/testrunner.js b/test/data/testrunner.js index c766e13ca..3f1a85a03 100644 --- a/test/data/testrunner.js +++ b/test/data/testrunner.js @@ -1,7 +1,236 @@ +define(function() { // Allow subprojects to test against their own fixtures -var qunitModule = QUnit.module, - qunitTest = QUnit.test; +var oldStart = window.start, + qunitModule = QUnit.module, + qunitTest = QUnit.test, + // Store the old counts so that we only assert on tests that have actually leaked, + // instead of asserting every time a test has leaked sometime in the past + oldCacheLength = 0, + oldFragmentsLength = 0, + oldActive = 0, + + expectedDataKeys = {}, + + splice = [].splice, + reset = QUnit.reset, + ajaxSettings = jQuery.ajaxSettings; + + +/** + * QUnit configuration + */ + +// Max time for stop() and asyncTest() until it aborts test +// and start()'s the next test. +QUnit.config.testTimeout = 20 * 1000; // 20 seconds + +// Enforce an "expect" argument or expect() call in all test bodies. +QUnit.config.requireExpects = true; + +/** + * QUnit hooks + */ + +// Sandbox start for great justice +window.start = function() { + oldStart(); +}; + +function keys(o) { + var ret, key; + if ( Object.keys ) { + ret = Object.keys( o ); + } else { + ret = []; + for ( key in o ) { + ret.push( key ); + } + } + ret.sort(); + return ret; +} + +/** + * @param {jQuery|HTMLElement|Object|Array} elems Target (or array of targets) for jQuery.data. + * @param {string} key + */ +QUnit.expectJqData = function( elems, key ) { + var i, elem, expando; + + // As of jQuery 2.0, there will be no "cache"-data is + // stored and managed completely below the API surface + if ( jQuery.cache ) { + QUnit.current_testEnvironment.checkJqData = true; + + if ( elems.jquery && elems.toArray ) { + elems = elems.toArray(); + } + if ( !supportjQuery.isArray( elems ) ) { + elems = [ elems ]; + } + + for ( i = 0; i < elems.length; i++ ) { + elem = elems[i]; + + // jQuery.data only stores data for nodes in jQuery.cache, + // for other data targets the data is stored in the object itself, + // in that case we can't test that target for memory leaks. + // But we don't have to since in that case the data will/must will + // be available as long as the object is not garbage collected by + // the js engine, and when it is, the data will be removed with it. + if ( !elem.nodeType ) { + // Fixes false positives for dataTests(window), dataTests({}). + continue; + } + + expando = elem[ jQuery.expando ]; + + if ( expando === undefined ) { + // In this case the element exists fine, but + // jQuery.data (or internal data) was never (in)directly + // called. + // Since this method was called it means some data was + // expected to be found, but since there is nothing, fail early + // (instead of in teardown). + notStrictEqual( expando, undefined, "Target for expectJqData must have an expando, for else there can be no data to expect." ); + } else { + if ( expectedDataKeys[expando] ) { + expectedDataKeys[expando].push( key ); + } else { + expectedDataKeys[expando] = [ key ]; + } + } + } + } + +}; +QUnit.config.urlConfig.push({ + id: "jqdata", + label: "Always check jQuery.data", + tooltip: "Trigger QUnit.expectJqData detection for all tests instead of just the ones that call it" +}); + +/** + * Ensures that tests have cleaned up properly after themselves. Should be passed as the + * teardown function on all modules' lifecycle object. + */ +window.moduleTeardown = function() { + var i, + expectedKeys, actualKeys, + fragmentsLength = 0, + cacheLength = 0; + + // Only look for jQuery data problems if this test actually + // provided some information to compare against. + if ( QUnit.urlParams.jqdata || this.checkJqData ) { + for ( i in jQuery.cache ) { + expectedKeys = expectedDataKeys[i]; + actualKeys = jQuery.cache[i] ? keys( jQuery.cache[i] ) : jQuery.cache[i]; + if ( !QUnit.equiv( expectedKeys, actualKeys ) ) { + deepEqual( actualKeys, expectedKeys, "Expected keys exist in jQuery.cache" ); + } + delete jQuery.cache[i]; + delete expectedDataKeys[i]; + } + // In case it was removed from cache before (or never there in the first place) + for ( i in expectedDataKeys ) { + deepEqual( expectedDataKeys[i], undefined, "No unexpected keys were left in jQuery.cache (#" + i + ")" ); + delete expectedDataKeys[i]; + } + } + + // Reset data register + expectedDataKeys = {}; + + // Check for (and clean up, if possible) incomplete animations/requests/etc. + if ( jQuery.timers && jQuery.timers.length !== 0 ) { + equal( jQuery.timers.length, 0, "No timers are still running" ); + splice.call( jQuery.timers, 0, jQuery.timers.length ); + jQuery.fx.stop(); + } + if ( jQuery.active !== undefined && jQuery.active !== oldActive ) { + equal( jQuery.active, oldActive, "No AJAX requests are still active" ); + if ( ajaxTest.abort ) { + ajaxTest.abort("active requests"); + } + oldActive = jQuery.active; + } + + // Allow QUnit.reset to clean up any attached elements before checking for leaks + QUnit.reset(); + + for ( i in jQuery.cache ) { + ++cacheLength; + } + + jQuery.fragments = {}; + + for ( i in jQuery.fragments ) { + ++fragmentsLength; + } + + // Because QUnit doesn't have a mechanism for retrieving the number of expected assertions for a test, + // if we unconditionally assert any of these, the test will fail with too many assertions :| + if ( cacheLength !== oldCacheLength ) { + equal( cacheLength, oldCacheLength, "No unit tests leak memory in jQuery.cache" ); + oldCacheLength = cacheLength; + } + if ( fragmentsLength !== oldFragmentsLength ) { + equal( fragmentsLength, oldFragmentsLength, "No unit tests leak memory in jQuery.fragments" ); + oldFragmentsLength = fragmentsLength; + } +}; + +QUnit.done(function() { + // Remove our own fixtures outside #qunit-fixture + supportjQuery("#qunit ~ *").remove(); +}); + +// jQuery-specific QUnit.reset +QUnit.reset = function() { + + // Ensure jQuery events and data on the fixture are properly removed + jQuery("#qunit-fixture").empty(); + // ...even if the jQuery under test has a broken .empty() + supportjQuery("#qunit-fixture").empty(); + + // Reset internal jQuery state + jQuery.event.global = {}; + if ( ajaxSettings ) { + jQuery.ajaxSettings = jQuery.extend( true, {}, ajaxSettings ); + } else { + delete jQuery.ajaxSettings; + } + + // Cleanup globals + Globals.cleanup(); + + // Let QUnit reset the fixture + reset.apply( this, arguments ); +}; + +// Register globals for cleanup and the cleanup code itself +// Explanation at http://perfectionkills.com/understanding-delete/#ie_bugs +window.Globals = (function() { + var globals = {}; + return { + register: function( name ) { + globals[ name ] = true; + supportjQuery.globalEval( "var " + name + " = undefined;" ); + }, + cleanup: function() { + var name, + current = globals; + globals = {}; + for ( name in current ) { + supportjQuery.globalEval( "try { " + + "delete " + ( supportjQuery.support.deleteExpando ? "window['" + name + "']" : name ) + + "; } catch( x ) {}" ); + } + } + }; +})(); /** * Test a subproject with its own fixture @@ -9,7 +238,7 @@ var qunitModule = QUnit.module, * @param {String} url Test folder location * @param {RegExp} risTests To filter script sources */ -this.testSubproject = function( label, url, risTests, complete ) { +function testSubproject( label, url, risTests, complete ) { var sub, fixture, fixtureHTML, fixtureReplaced = false; @@ -131,236 +360,8 @@ this.testSubproject = function( label, url, risTests, complete ) { fn.apply( this, arguments ); }; } -}; - -// Register globals for cleanup and the cleanup code itself -// Explanation at http://perfectionkills.com/understanding-delete/#ie_bugs -this.Globals = (function() { - var globals = {}; - return { - register: function( name ) { - globals[ name ] = true; - supportjQuery.globalEval( "var " + name + " = undefined;" ); - }, - cleanup: function() { - var name, - current = globals; - globals = {}; - for ( name in current ) { - supportjQuery.globalEval( "try { " + - "delete " + ( supportjQuery.support.deleteExpando ? "window['" + name + "']" : name ) + - "; } catch( x ) {}" ); - } - } - }; -})(); - - -/** - * QUnit hooks - */ - -// Sandbox start for great justice -(function() { - var oldStart = window.start; - window.start = function() { - oldStart(); - }; -})(); - -(function() { - // Store the old counts so that we only assert on tests that have actually leaked, - // instead of asserting every time a test has leaked sometime in the past - var oldCacheLength = 0, - oldFragmentsLength = 0, - oldActive = 0, - - expectedDataKeys = {}, - - splice = [].splice, - reset = QUnit.reset, - ajaxSettings = jQuery.ajaxSettings; - - function keys(o) { - var ret, key; - if ( Object.keys ) { - ret = Object.keys( o ); - } else { - ret = []; - for ( key in o ) { - ret.push( key ); - } - } - ret.sort(); - return ret; - } - - /** - * @param {jQuery|HTMLElement|Object|Array} elems Target (or array of targets) for jQuery.data. - * @param {string} key - */ - QUnit.expectJqData = function( elems, key ) { - var i, elem, expando; - - // As of jQuery 2.0, there will be no "cache"-data is - // stored and managed completely below the API surface - if ( jQuery.cache ) { - QUnit.current_testEnvironment.checkJqData = true; - - if ( elems.jquery && elems.toArray ) { - elems = elems.toArray(); - } - if ( !supportjQuery.isArray( elems ) ) { - elems = [ elems ]; - } - - for ( i = 0; i < elems.length; i++ ) { - elem = elems[i]; - - // jQuery.data only stores data for nodes in jQuery.cache, - // for other data targets the data is stored in the object itself, - // in that case we can't test that target for memory leaks. - // But we don't have to since in that case the data will/must will - // be available as long as the object is not garbage collected by - // the js engine, and when it is, the data will be removed with it. - if ( !elem.nodeType ) { - // Fixes false positives for dataTests(window), dataTests({}). - continue; - } - - expando = elem[ jQuery.expando ]; - - if ( expando === undefined ) { - // In this case the element exists fine, but - // jQuery.data (or internal data) was never (in)directly - // called. - // Since this method was called it means some data was - // expected to be found, but since there is nothing, fail early - // (instead of in teardown). - notStrictEqual( expando, undefined, "Target for expectJqData must have an expando, for else there can be no data to expect." ); - } else { - if ( expectedDataKeys[expando] ) { - expectedDataKeys[expando].push( key ); - } else { - expectedDataKeys[expando] = [ key ]; - } - } - } - } - - }; - QUnit.config.urlConfig.push({ - id: "jqdata", - label: "Always check jQuery.data", - tooltip: "Trigger QUnit.expectJqData detection for all tests instead of just the ones that call it" - }); - - /** - * Ensures that tests have cleaned up properly after themselves. Should be passed as the - * teardown function on all modules' lifecycle object. - */ - this.moduleTeardown = function() { - var i, - expectedKeys, actualKeys, - fragmentsLength = 0, - cacheLength = 0; - - // Only look for jQuery data problems if this test actually - // provided some information to compare against. - if ( QUnit.urlParams.jqdata || this.checkJqData ) { - for ( i in jQuery.cache ) { - expectedKeys = expectedDataKeys[i]; - actualKeys = jQuery.cache[i] ? keys( jQuery.cache[i] ) : jQuery.cache[i]; - if ( !QUnit.equiv( expectedKeys, actualKeys ) ) { - deepEqual( actualKeys, expectedKeys, "Expected keys exist in jQuery.cache" ); - } - delete jQuery.cache[i]; - delete expectedDataKeys[i]; - } - // In case it was removed from cache before (or never there in the first place) - for ( i in expectedDataKeys ) { - deepEqual( expectedDataKeys[i], undefined, "No unexpected keys were left in jQuery.cache (#" + i + ")" ); - delete expectedDataKeys[i]; - } - } - - // Reset data register - expectedDataKeys = {}; - - // Check for (and clean up, if possible) incomplete animations/requests/etc. - if ( jQuery.timers && jQuery.timers.length !== 0 ) { - equal( jQuery.timers.length, 0, "No timers are still running" ); - splice.call( jQuery.timers, 0, jQuery.timers.length ); - jQuery.fx.stop(); - } - if ( jQuery.active !== undefined && jQuery.active !== oldActive ) { - equal( jQuery.active, oldActive, "No AJAX requests are still active" ); - if ( ajaxTest.abort ) { - ajaxTest.abort("active requests"); - } - oldActive = jQuery.active; - } - - // Allow QUnit.reset to clean up any attached elements before checking for leaks - QUnit.reset(); - - for ( i in jQuery.cache ) { - ++cacheLength; - } - - jQuery.fragments = {}; - - for ( i in jQuery.fragments ) { - ++fragmentsLength; - } - - // Because QUnit doesn't have a mechanism for retrieving the number of expected assertions for a test, - // if we unconditionally assert any of these, the test will fail with too many assertions :| - if ( cacheLength !== oldCacheLength ) { - equal( cacheLength, oldCacheLength, "No unit tests leak memory in jQuery.cache" ); - oldCacheLength = cacheLength; - } - if ( fragmentsLength !== oldFragmentsLength ) { - equal( fragmentsLength, oldFragmentsLength, "No unit tests leak memory in jQuery.fragments" ); - oldFragmentsLength = fragmentsLength; - } - }; - - QUnit.done(function() { - // Remove our own fixtures outside #qunit-fixture - supportjQuery("#qunit ~ *").remove(); - }); - - // jQuery-specific QUnit.reset - QUnit.reset = function() { - - // Ensure jQuery events and data on the fixture are properly removed - jQuery("#qunit-fixture").empty(); - // ...even if the jQuery under test has a broken .empty() - supportjQuery("#qunit-fixture").empty(); +} - // Reset internal jQuery state - jQuery.event.global = {}; - if ( ajaxSettings ) { - jQuery.ajaxSettings = jQuery.extend( true, {}, ajaxSettings ); - } else { - delete jQuery.ajaxSettings; - } - - // Cleanup globals - Globals.cleanup(); +return testSubproject; - // Let QUnit reset the fixture - reset.apply( this, arguments ); - }; -})(); - -/** - * QUnit configuration - */ -// Max time for stop() and asyncTest() until it aborts test -// and start()'s the next test. -QUnit.config.testTimeout = 20 * 1000; // 20 seconds - -// Enforce an "expect" argument or expect() call in all test bodies. -QUnit.config.requireExpects = true; +}); |