2 * Allow the test suite to run with other libs or jQuery's.
6 // For checking globals pollution despite auto-created globals in various environments
7 jQuery.each( [ jQuery.expando, "getInterface", "Packages", "java", "netscape" ], function( i, name ) {
8 window[ name ] = window[ name ];
11 // Expose Sizzle for Sizzle's selector tests
12 // We remove Sizzle's globalization in jQuery
13 var Sizzle = Sizzle || jQuery.find;
15 // Allow subprojects to test against their own fixtures
16 var qunitModule = QUnit.module,
17 qunitTest = QUnit.test;
19 function testSubproject( label, url, risTests ) {
20 var sub, fixture, fixtureHTML,
21 fixtureReplaced = false;
23 // Don't let subproject tests jump the gun
24 QUnit.config.reorder = false;
30 // TODO restore parent fixture on teardown to support reordering
31 module = QUnit.module = function( name ) {
34 // Remember subproject-scoped module name
39 return qunitModule.apply( this, args );
41 test = function( name ) {
45 // Prepend subproject-scoped module name to test name
46 args[0] = sub + ": " + name;
48 // Find test function and wrap to require subproject fixture
49 for ( ; i >= 0; i-- ) {
50 if ( originaljQuery.isFunction( args[i] ) ) {
51 args[i] = requireFixture( args[i] );
56 return qunitTest.apply( this, args );
59 // Load tests and fixture from subproject
60 // Test order matters, so we must be synchronous and throw an error on load failure
61 originaljQuery.ajax( url, {
64 error: function( jqXHR, status ) {
65 throw new Error( "Could not load: " + url + " (" + status + ")" );
67 success: function( data, status, jqXHR ) {
68 var page = originaljQuery.parseHTML(
69 // replace html/head with dummy elements so they are represented in the DOM
70 ( data || "" ).replace( /<\/?((!DOCTYPE|html|head)\b.*?)>/gi, "[$1]" ),
75 if ( !page || !page.length ) {
76 this.error( jqXHR, "no data" );
78 page = originaljQuery( page );
80 // Include subproject tests
81 page.filter("script[src]").add( page.find("script[src]") ).each(function() {
82 var src = originaljQuery( this ).attr("src"),
83 html = "<script src='" + url + src + "'></script>";
84 if ( risTests.test( src ) ) {
85 if ( originaljQuery.isReady ) {
86 originaljQuery("head").first().append( html );
88 document.write( html );
93 // Get the fixture, including content outside of #qunit-fixture
94 fixture = page.find("[id='qunit-fixture']");
95 fixtureHTML = fixture.html();
97 while ( fixture.length && !fixture.prevAll("[id='qunit']").length ) {
98 fixture = fixture.parent();
100 fixture = fixture.add( fixture.nextAll() );
104 function requireFixture( fn ) {
106 if ( !fixtureReplaced ) {
107 // Make sure that we retrieved a fixture for the subproject
108 if ( !fixture.length ) {
109 ok( false, "Found subproject fixture" );
113 // Replace the current fixture, including content outside of #qunit-fixture
114 var oldFixture = originaljQuery("#qunit-fixture");
115 while ( oldFixture.length && !oldFixture.prevAll("[id='qunit']").length ) {
116 oldFixture = oldFixture.parent();
118 oldFixture.nextAll().remove();
119 oldFixture.replaceWith( fixture );
121 // WARNING: UNDOCUMENTED INTERFACE
122 QUnit.config.fixture = fixtureHTML;
124 if ( originaljQuery("#qunit-fixture").html() !== fixtureHTML ) {
125 ok( false, "Copied subproject fixture" );
129 fixtureReplaced = true;
132 fn.apply( this, arguments );
137 // Register globals for cleanup and the cleanup code itself
138 // Explanation at http://perfectionkills.com/understanding-delete/#ie_bugs
139 var Globals = (function() {
142 register: function( name ) {
143 globals[ name ] = true;
144 jQuery.globalEval( "var " + name + " = undefined;" );
146 cleanup: function() {
150 for ( name in current ) {
151 jQuery.globalEval( "try { " +
152 "delete " + ( jQuery.support.deleteExpando ? "window['" + name + "']" : name ) +
153 "; } catch( x ) {}" );
159 // Sandbox start for great justice
161 var oldStart = window.start;
162 window.start = function() {
171 // Store the old counts so that we only assert on tests that have actually leaked,
172 // instead of asserting every time a test has leaked sometime in the past
173 var oldCacheLength = 0,
174 oldFragmentsLength = 0,
177 expectedDataKeys = {},
181 ajaxSettings = jQuery.ajaxSettings;
186 ret = Object.keys( o );
198 * @param {jQuery|HTMLElement|Object|Array} elems Target (or array of targets) for jQuery.data.
199 * @param {string} key
201 QUnit.expectJqData = function( elems, key ) {
202 var i, elem, expando;
204 // As of jQuery 2.0, there will be no "cache"-data is
205 // stored and managed completely below the API surface
206 if ( jQuery.cache ) {
207 QUnit.current_testEnvironment.checkJqData = true;
209 if ( elems.jquery && elems.toArray ) {
210 elems = elems.toArray();
212 if ( !jQuery.isArray( elems ) ) {
216 for ( i = 0; i < elems.length; i++ ) {
219 // jQuery.data only stores data for nodes in jQuery.cache,
220 // for other data targets the data is stored in the object itself,
221 // in that case we can't test that target for memory leaks.
222 // But we don't have to since in that case the data will/must will
223 // be available as long as the object is not garbage collected by
224 // the js engine, and when it is, the data will be removed with it.
225 if ( !elem.nodeType ) {
226 // Fixes false positives for dataTests(window), dataTests({}).
230 expando = elem[ jQuery.expando ];
232 if ( expando === undefined ) {
233 // In this case the element exists fine, but
234 // jQuery.data (or internal data) was never (in)directly
236 // Since this method was called it means some data was
237 // expected to be found, but since there is nothing, fail early
238 // (instead of in teardown).
239 notStrictEqual( expando, undefined, "Target for expectJqData must have an expando, for else there can be no data to expect." );
241 if ( expectedDataKeys[expando] ) {
242 expectedDataKeys[expando].push( key );
244 expectedDataKeys[expando] = [ key ];
251 QUnit.config.urlConfig.push( {
253 label: "Always check jQuery.data",
254 tooltip: "Trigger QUnit.expectJqData detection for all tests instead of just the ones that call it"
258 * Ensures that tests have cleaned up properly after themselves. Should be passed as the
259 * teardown function on all modules' lifecycle object.
261 this.moduleTeardown = function() {
263 expectedKeys, actualKeys,
267 // Only look for jQuery data problems if this test actually
268 // provided some information to compare against.
269 if ( QUnit.urlParams.jqdata || this.checkJqData ) {
270 for ( i in jQuery.cache ) {
271 expectedKeys = expectedDataKeys[i];
272 actualKeys = jQuery.cache[i] ? keys( jQuery.cache[i] ) : jQuery.cache[i];
273 if ( !QUnit.equiv( expectedKeys, actualKeys ) ) {
274 deepEqual( actualKeys, expectedKeys, "Expected keys exist in jQuery.cache" );
276 delete jQuery.cache[i];
277 delete expectedDataKeys[i];
279 // In case it was removed from cache before (or never there in the first place)
280 for ( i in expectedDataKeys ) {
281 deepEqual( expectedDataKeys[i], undefined, "No unexpected keys were left in jQuery.cache (#" + i + ")" );
282 delete expectedDataKeys[i];
286 // Reset data register
287 expectedDataKeys = {};
289 // Check for (and clean up, if possible) incomplete animations/requests/etc.
290 if ( jQuery.timers && jQuery.timers.length !== 0 ) {
291 equal( jQuery.timers.length, 0, "No timers are still running" );
292 splice.call( jQuery.timers, 0, jQuery.timers.length );
295 if ( jQuery.active !== undefined && jQuery.active !== oldActive ) {
296 equal( jQuery.active, oldActive, "No AJAX requests are still active" );
297 if ( ajaxTest.abort ) {
298 ajaxTest.abort("active requests");
300 oldActive = jQuery.active;
303 // Allow QUnit.reset to clean up any attached elements before checking for leaks
306 for ( i in jQuery.cache ) {
310 jQuery.fragments = {};
312 for ( i in jQuery.fragments ) {
316 // Because QUnit doesn't have a mechanism for retrieving the number of expected assertions for a test,
317 // if we unconditionally assert any of these, the test will fail with too many assertions :|
318 if ( cacheLength !== oldCacheLength ) {
319 equal( cacheLength, oldCacheLength, "No unit tests leak memory in jQuery.cache" );
320 oldCacheLength = cacheLength;
322 if ( fragmentsLength !== oldFragmentsLength ) {
323 equal( fragmentsLength, oldFragmentsLength, "No unit tests leak memory in jQuery.fragments" );
324 oldFragmentsLength = fragmentsLength;
328 QUnit.done(function() {
329 // Remove our own fixtures outside #qunit-fixture
330 jQuery("#qunit ~ *").remove();
333 // jQuery-specific QUnit.reset
334 QUnit.reset = function() {
336 // Ensure jQuery events and data on the fixture are properly removed
337 jQuery("#qunit-fixture").empty();
339 // Reset internal jQuery state
340 jQuery.event.global = {};
341 if ( ajaxSettings ) {
342 jQuery.ajaxSettings = jQuery.extend( true, {}, ajaxSettings );
344 delete jQuery.ajaxSettings;
350 // Let QUnit reset the fixture
351 reset.apply( this, arguments );
356 * QUnit configuration
358 // Max time for stop() and asyncTest() until it aborts test
359 // and start()'s the next test.
360 QUnit.config.testTimeout = 20 * 1000; // 20 seconds
362 // Enforce an "expect" argument or expect() call in all test bodies.
363 QUnit.config.requireExpects = true;
366 * Load the TestSwarm listener if swarmURL is in the address.
369 var url = window.location.search;
370 url = decodeURIComponent( url.slice( url.indexOf("swarmURL=") + "swarmURL=".length ) );
372 if ( !url || url.indexOf("http") !== 0 ) {
376 document.write("<scr" + "ipt src='http://swarm.jquery.org/js/inject.js?" + (new Date()).getTime() + "'></scr" + "ipt>");