UNPKG

cron-ui

Version:

CronUI provides a simple easy-to-use form for users to specify recurrent events, translated to cron strings.

1,563 lines (1,275 loc) 156 kB
/*! * QUnit 1.23.0 * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * * Date: 2016-03-25T19:37Z */ ( function( global ) { var QUnit = {}; var Date = global.Date; var now = Date.now || function() { return new Date().getTime(); }; var setTimeout = global.setTimeout; var clearTimeout = global.clearTimeout; // Store a local window from the global to allow direct references. var window = global.window; var defined = { document: window && window.document !== undefined, setTimeout: setTimeout !== undefined, sessionStorage: ( function() { var x = "qunit-test-string"; try { sessionStorage.setItem( x, x ); sessionStorage.removeItem( x ); return true; } catch ( e ) { return false; } }() ) }; var fileName = ( sourceFromStacktrace( 0 ) || "" ).replace( /(:\d+)+\)?/, "" ).replace( /.+\//, "" ); var globalStartCalled = false; var runStarted = false; var toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty; // Returns a new Array with the elements that are in a but not in b function diff( a, b ) { var i, j, result = a.slice(); for ( i = 0; i < result.length; i++ ) { for ( j = 0; j < b.length; j++ ) { if ( result[ i ] === b[ j ] ) { result.splice( i, 1 ); i--; break; } } } return result; } // From jquery.js function inArray( elem, array ) { if ( array.indexOf ) { return array.indexOf( elem ); } for ( var i = 0, length = array.length; i < length; i++ ) { if ( array[ i ] === elem ) { return i; } } return -1; } /** * Makes a clone of an object using only Array or Object as base, * and copies over the own enumerable properties. * * @param {Object} obj * @return {Object} New object with only the own properties (recursively). */ function objectValues ( obj ) { var key, val, vals = QUnit.is( "array", obj ) ? [] : {}; for ( key in obj ) { if ( hasOwn.call( obj, key ) ) { val = obj[ key ]; vals[ key ] = val === Object( val ) ? objectValues( val ) : val; } } return vals; } function extend( a, b, undefOnly ) { for ( var prop in b ) { if ( hasOwn.call( b, prop ) ) { // Avoid "Member not found" error in IE8 caused by messing with window.constructor // This block runs on every environment, so `global` is being used instead of `window` // to avoid errors on node. if ( prop !== "constructor" || a !== global ) { if ( b[ prop ] === undefined ) { delete a[ prop ]; } else if ( !( undefOnly && typeof a[ prop ] !== "undefined" ) ) { a[ prop ] = b[ prop ]; } } } } return a; } function objectType( obj ) { if ( typeof obj === "undefined" ) { return "undefined"; } // Consider: typeof null === object if ( obj === null ) { return "null"; } var match = toString.call( obj ).match( /^\[object\s(.*)\]$/ ), type = match && match[ 1 ]; switch ( type ) { case "Number": if ( isNaN( obj ) ) { return "nan"; } return "number"; case "String": case "Boolean": case "Array": case "Set": case "Map": case "Date": case "RegExp": case "Function": case "Symbol": return type.toLowerCase(); } if ( typeof obj === "object" ) { return "object"; } } // Safe object type checking function is( type, obj ) { return QUnit.objectType( obj ) === type; } // Doesn't support IE6 to IE9, it will return undefined on these browsers // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack function extractStacktrace( e, offset ) { offset = offset === undefined ? 4 : offset; var stack, include, i; if ( e.stack ) { stack = e.stack.split( "\n" ); if ( /^error$/i.test( stack[ 0 ] ) ) { stack.shift(); } if ( fileName ) { include = []; for ( i = offset; i < stack.length; i++ ) { if ( stack[ i ].indexOf( fileName ) !== -1 ) { break; } include.push( stack[ i ] ); } if ( include.length ) { return include.join( "\n" ); } } return stack[ offset ]; // Support: Safari <=6 only } else if ( e.sourceURL ) { // Exclude useless self-reference for generated Error objects if ( /qunit.js$/.test( e.sourceURL ) ) { return; } // For actual exceptions, this is useful return e.sourceURL + ":" + e.line; } } function sourceFromStacktrace( offset ) { var error = new Error(); // Support: Safari <=7 only, IE <=10 - 11 only // Not all browsers generate the `stack` property for `new Error()`, see also #636 if ( !error.stack ) { try { throw error; } catch ( err ) { error = err; } } return extractStacktrace( error, offset ); } /** * Config object: Maintain internal state * Later exposed as QUnit.config * `config` initialized at top of scope */ var config = { // The queue of tests to run queue: [], // Block until document ready blocking: true, // By default, run previously failed tests first // very useful in combination with "Hide passed tests" checked reorder: true, // By default, modify document.title when suite is done altertitle: true, // HTML Reporter: collapse every test except the first failing test // If false, all failing tests will be expanded collapse: true, // By default, scroll to top of the page when suite is done scrolltop: true, // Depth up-to which object will be dumped maxDepth: 5, // When enabled, all tests must call expect() requireExpects: false, // Placeholder for user-configurable form-exposed URL parameters urlConfig: [], // Set of all modules. modules: [], // Stack of nested modules moduleStack: [], // The first unnamed module currentModule: { name: "", tests: [] }, callbacks: {} }; // Push a loose unnamed module to the modules collection config.modules.push( config.currentModule ); var loggingCallbacks = {}; // Register logging callbacks function registerLoggingCallbacks( obj ) { var i, l, key, callbackNames = [ "begin", "done", "log", "testStart", "testDone", "moduleStart", "moduleDone" ]; function registerLoggingCallback( key ) { var loggingCallback = function( callback ) { if ( objectType( callback ) !== "function" ) { throw new Error( "QUnit logging methods require a callback function as their first parameters." ); } config.callbacks[ key ].push( callback ); }; // DEPRECATED: This will be removed on QUnit 2.0.0+ // Stores the registered functions allowing restoring // at verifyLoggingCallbacks() if modified loggingCallbacks[ key ] = loggingCallback; return loggingCallback; } for ( i = 0, l = callbackNames.length; i < l; i++ ) { key = callbackNames[ i ]; // Initialize key collection of logging callback if ( objectType( config.callbacks[ key ] ) === "undefined" ) { config.callbacks[ key ] = []; } obj[ key ] = registerLoggingCallback( key ); } } function runLoggingCallbacks( key, args ) { var i, l, callbacks; callbacks = config.callbacks[ key ]; for ( i = 0, l = callbacks.length; i < l; i++ ) { callbacks[ i ]( args ); } } // DEPRECATED: This will be removed on 2.0.0+ // This function verifies if the loggingCallbacks were modified by the user // If so, it will restore it, assign the given callback and print a console warning function verifyLoggingCallbacks() { var loggingCallback, userCallback; for ( loggingCallback in loggingCallbacks ) { if ( QUnit[ loggingCallback ] !== loggingCallbacks[ loggingCallback ] ) { userCallback = QUnit[ loggingCallback ]; // Restore the callback function QUnit[ loggingCallback ] = loggingCallbacks[ loggingCallback ]; // Assign the deprecated given callback QUnit[ loggingCallback ]( userCallback ); if ( global.console && global.console.warn ) { global.console.warn( "QUnit." + loggingCallback + " was replaced with a new value.\n" + "Please, check out the documentation on how to apply logging callbacks.\n" + "Reference: https://api.qunitjs.com/category/callbacks/" ); } } } } ( function() { if ( !defined.document ) { return; } // `onErrorFnPrev` initialized at top of scope // Preserve other handlers var onErrorFnPrev = window.onerror; // Cover uncaught exceptions // Returning true will suppress the default browser handler, // returning false will let it run. window.onerror = function( error, filePath, linerNr ) { var ret = false; if ( onErrorFnPrev ) { ret = onErrorFnPrev( error, filePath, linerNr ); } // Treat return value as window.onerror itself does, // Only do our handling if not suppressed. if ( ret !== true ) { if ( QUnit.config.current ) { if ( QUnit.config.current.ignoreGlobalErrors ) { return true; } QUnit.pushFailure( error, filePath + ":" + linerNr ); } else { QUnit.test( "global failure", extend( function() { QUnit.pushFailure( error, filePath + ":" + linerNr ); }, { validTest: true } ) ); } return false; } return ret; }; }() ); // Figure out if we're running the tests from a server or not QUnit.isLocal = !( defined.document && window.location.protocol !== "file:" ); // Expose the current QUnit version QUnit.version = "1.23.0"; extend( QUnit, { // Call on start of module test to prepend name to all tests module: function( name, testEnvironment, executeNow ) { var module, moduleFns; var currentModule = config.currentModule; if ( arguments.length === 2 ) { if ( testEnvironment instanceof Function ) { executeNow = testEnvironment; testEnvironment = undefined; } } // DEPRECATED: handles setup/teardown functions, // beforeEach and afterEach should be used instead if ( testEnvironment && testEnvironment.setup ) { testEnvironment.beforeEach = testEnvironment.setup; delete testEnvironment.setup; } if ( testEnvironment && testEnvironment.teardown ) { testEnvironment.afterEach = testEnvironment.teardown; delete testEnvironment.teardown; } module = createModule(); moduleFns = { beforeEach: setHook( module, "beforeEach" ), afterEach: setHook( module, "afterEach" ) }; if ( executeNow instanceof Function ) { config.moduleStack.push( module ); setCurrentModule( module ); executeNow.call( module.testEnvironment, moduleFns ); config.moduleStack.pop(); module = module.parentModule || currentModule; } setCurrentModule( module ); function createModule() { var parentModule = config.moduleStack.length ? config.moduleStack.slice( -1 )[ 0 ] : null; var moduleName = parentModule !== null ? [ parentModule.name, name ].join( " > " ) : name; var module = { name: moduleName, parentModule: parentModule, tests: [], moduleId: generateHash( moduleName ) }; var env = {}; if ( parentModule ) { extend( env, parentModule.testEnvironment ); delete env.beforeEach; delete env.afterEach; } extend( env, testEnvironment ); module.testEnvironment = env; config.modules.push( module ); return module; } function setCurrentModule( module ) { config.currentModule = module; } }, // DEPRECATED: QUnit.asyncTest() will be removed in QUnit 2.0. asyncTest: asyncTest, test: test, skip: skip, only: only, // DEPRECATED: The functionality of QUnit.start() will be altered in QUnit 2.0. // In QUnit 2.0, invoking it will ONLY affect the `QUnit.config.autostart` blocking behavior. start: function( count ) { var globalStartAlreadyCalled = globalStartCalled; if ( !config.current ) { globalStartCalled = true; if ( runStarted ) { throw new Error( "Called start() outside of a test context while already started" ); } else if ( globalStartAlreadyCalled || count > 1 ) { throw new Error( "Called start() outside of a test context too many times" ); } else if ( config.autostart ) { throw new Error( "Called start() outside of a test context when " + "QUnit.config.autostart was true" ); } else if ( !config.pageLoaded ) { // The page isn't completely loaded yet, so bail out and let `QUnit.load` handle it config.autostart = true; return; } } else { // If a test is running, adjust its semaphore config.current.semaphore -= count || 1; // If semaphore is non-numeric, throw error if ( isNaN( config.current.semaphore ) ) { config.current.semaphore = 0; QUnit.pushFailure( "Called start() with a non-numeric decrement.", sourceFromStacktrace( 2 ) ); return; } // Don't start until equal number of stop-calls if ( config.current.semaphore > 0 ) { return; } // Throw an Error if start is called more often than stop if ( config.current.semaphore < 0 ) { config.current.semaphore = 0; QUnit.pushFailure( "Called start() while already started (test's semaphore was 0 already)", sourceFromStacktrace( 2 ) ); return; } } resumeProcessing(); }, // DEPRECATED: QUnit.stop() will be removed in QUnit 2.0. stop: function( count ) { // If there isn't a test running, don't allow QUnit.stop() to be called if ( !config.current ) { throw new Error( "Called stop() outside of a test context" ); } // If a test is running, adjust its semaphore config.current.semaphore += count || 1; pauseProcessing(); }, config: config, is: is, objectType: objectType, extend: extend, load: function() { config.pageLoaded = true; // Initialize the configuration options extend( config, { stats: { all: 0, bad: 0 }, moduleStats: { all: 0, bad: 0 }, started: 0, updateRate: 1000, autostart: true, filter: "" }, true ); config.blocking = false; if ( config.autostart ) { resumeProcessing(); } }, stack: function( offset ) { offset = ( offset || 0 ) + 2; return sourceFromStacktrace( offset ); } } ); registerLoggingCallbacks( QUnit ); function begin() { var i, l, modulesLog = []; // If the test run hasn't officially begun yet if ( !config.started ) { // Record the time of the test run's beginning config.started = now(); verifyLoggingCallbacks(); // Delete the loose unnamed module if unused. if ( config.modules[ 0 ].name === "" && config.modules[ 0 ].tests.length === 0 ) { config.modules.shift(); } // Avoid unnecessary information by not logging modules' test environments for ( i = 0, l = config.modules.length; i < l; i++ ) { modulesLog.push( { name: config.modules[ i ].name, tests: config.modules[ i ].tests } ); } // The test run is officially beginning now runLoggingCallbacks( "begin", { totalTests: Test.count, modules: modulesLog } ); } config.blocking = false; process( true ); } function process( last ) { function next() { process( last ); } var start = now(); config.depth = ( config.depth || 0 ) + 1; while ( config.queue.length && !config.blocking ) { if ( !defined.setTimeout || config.updateRate <= 0 || ( ( now() - start ) < config.updateRate ) ) { if ( config.current ) { // Reset async tracking for each phase of the Test lifecycle config.current.usedAsync = false; } config.queue.shift()(); } else { setTimeout( next, 13 ); break; } } config.depth--; if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { done(); } } function pauseProcessing() { config.blocking = true; if ( config.testTimeout && defined.setTimeout ) { clearTimeout( config.timeout ); config.timeout = setTimeout( function() { if ( config.current ) { config.current.semaphore = 0; QUnit.pushFailure( "Test timed out", sourceFromStacktrace( 2 ) ); } else { throw new Error( "Test timed out" ); } resumeProcessing(); }, config.testTimeout ); } } function resumeProcessing() { runStarted = true; // A slight delay to allow this iteration of the event loop to finish (more assertions, etc.) if ( defined.setTimeout ) { setTimeout( function() { if ( config.current && config.current.semaphore > 0 ) { return; } if ( config.timeout ) { clearTimeout( config.timeout ); } begin(); }, 13 ); } else { begin(); } } function done() { var runtime, passed; config.autorun = true; // Log the last module results if ( config.previousModule ) { runLoggingCallbacks( "moduleDone", { name: config.previousModule.name, tests: config.previousModule.tests, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all, runtime: now() - config.moduleStats.started } ); } delete config.previousModule; runtime = now() - config.started; passed = config.stats.all - config.stats.bad; runLoggingCallbacks( "done", { failed: config.stats.bad, passed: passed, total: config.stats.all, runtime: runtime } ); } function setHook( module, hookName ) { if ( module.testEnvironment === undefined ) { module.testEnvironment = {}; } return function( callback ) { module.testEnvironment[ hookName ] = callback; }; } var focused = false; var priorityCount = 0; var unitSampler; function Test( settings ) { var i, l; ++Test.count; extend( this, settings ); this.assertions = []; this.semaphore = 0; this.usedAsync = false; this.module = config.currentModule; this.stack = sourceFromStacktrace( 3 ); // Register unique strings for ( i = 0, l = this.module.tests; i < l.length; i++ ) { if ( this.module.tests[ i ].name === this.testName ) { this.testName += " "; } } this.testId = generateHash( this.module.name, this.testName ); this.module.tests.push( { name: this.testName, testId: this.testId } ); if ( settings.skip ) { // Skipped tests will fully ignore any sent callback this.callback = function() {}; this.async = false; this.expected = 0; } else { this.assert = new Assert( this ); } } Test.count = 0; Test.prototype = { before: function() { if ( // Emit moduleStart when we're switching from one module to another this.module !== config.previousModule || // They could be equal (both undefined) but if the previousModule property doesn't // yet exist it means this is the first test in a suite that isn't wrapped in a // module, in which case we'll just emit a moduleStart event for 'undefined'. // Without this, reporters can get testStart before moduleStart which is a problem. !hasOwn.call( config, "previousModule" ) ) { if ( hasOwn.call( config, "previousModule" ) ) { runLoggingCallbacks( "moduleDone", { name: config.previousModule.name, tests: config.previousModule.tests, failed: config.moduleStats.bad, passed: config.moduleStats.all - config.moduleStats.bad, total: config.moduleStats.all, runtime: now() - config.moduleStats.started } ); } config.previousModule = this.module; config.moduleStats = { all: 0, bad: 0, started: now() }; runLoggingCallbacks( "moduleStart", { name: this.module.name, tests: this.module.tests } ); } config.current = this; if ( this.module.testEnvironment ) { delete this.module.testEnvironment.beforeEach; delete this.module.testEnvironment.afterEach; } this.testEnvironment = extend( {}, this.module.testEnvironment ); this.started = now(); runLoggingCallbacks( "testStart", { name: this.testName, module: this.module.name, testId: this.testId } ); if ( !config.pollution ) { saveGlobal(); } }, run: function() { var promise; config.current = this; if ( this.async ) { QUnit.stop(); } this.callbackStarted = now(); if ( config.notrycatch ) { runTest( this ); return; } try { runTest( this ); } catch ( e ) { this.pushFailure( "Died on test #" + ( this.assertions.length + 1 ) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) ); // Else next test will carry the responsibility saveGlobal(); // Restart the tests if they're blocking if ( config.blocking ) { QUnit.start(); } } function runTest( test ) { promise = test.callback.call( test.testEnvironment, test.assert ); test.resolvePromise( promise ); } }, after: function() { checkPollution(); }, queueHook: function( hook, hookName ) { var promise, test = this; return function runHook() { config.current = test; if ( config.notrycatch ) { callHook(); return; } try { callHook(); } catch ( error ) { test.pushFailure( hookName + " failed on " + test.testName + ": " + ( error.message || error ), extractStacktrace( error, 0 ) ); } function callHook() { promise = hook.call( test.testEnvironment, test.assert ); test.resolvePromise( promise, hookName ); } }; }, // Currently only used for module level hooks, can be used to add global level ones hooks: function( handler ) { var hooks = []; function processHooks( test, module ) { if ( module.parentModule ) { processHooks( test, module.parentModule ); } if ( module.testEnvironment && QUnit.objectType( module.testEnvironment[ handler ] ) === "function" ) { hooks.push( test.queueHook( module.testEnvironment[ handler ], handler ) ); } } // Hooks are ignored on skipped tests if ( !this.skip ) { processHooks( this, this.module ); } return hooks; }, finish: function() { config.current = this; if ( config.requireExpects && this.expected === null ) { this.pushFailure( "Expected number of assertions to be defined, but expect() was " + "not called.", this.stack ); } else if ( this.expected !== null && this.expected !== this.assertions.length ) { this.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack ); } else if ( this.expected === null && !this.assertions.length ) { this.pushFailure( "Expected at least one assertion, but none were run - call " + "expect(0) to accept zero assertions.", this.stack ); } var i, bad = 0; this.runtime = now() - this.started; config.stats.all += this.assertions.length; config.moduleStats.all += this.assertions.length; for ( i = 0; i < this.assertions.length; i++ ) { if ( !this.assertions[ i ].result ) { bad++; config.stats.bad++; config.moduleStats.bad++; } } runLoggingCallbacks( "testDone", { name: this.testName, module: this.module.name, skipped: !!this.skip, failed: bad, passed: this.assertions.length - bad, total: this.assertions.length, runtime: this.runtime, // HTML Reporter use assertions: this.assertions, testId: this.testId, // Source of Test source: this.stack, // DEPRECATED: this property will be removed in 2.0.0, use runtime instead duration: this.runtime } ); // QUnit.reset() is deprecated and will be replaced for a new // fixture reset function on QUnit 2.0/2.1. // It's still called here for backwards compatibility handling QUnit.reset(); config.current = undefined; }, queue: function() { var priority, test = this; if ( !this.valid() ) { return; } function run() { // Each of these can by async synchronize( [ function() { test.before(); }, test.hooks( "beforeEach" ), function() { test.run(); }, test.hooks( "afterEach" ).reverse(), function() { test.after(); }, function() { test.finish(); } ] ); } // Prioritize previously failed tests, detected from sessionStorage priority = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem( "qunit-test-" + this.module.name + "-" + this.testName ); return synchronize( run, priority, config.seed ); }, pushResult: function( resultInfo ) { // Destructure of resultInfo = { result, actual, expected, message, negative } var source, details = { module: this.module.name, name: this.testName, result: resultInfo.result, message: resultInfo.message, actual: resultInfo.actual, expected: resultInfo.expected, testId: this.testId, negative: resultInfo.negative || false, runtime: now() - this.started }; if ( !resultInfo.result ) { source = sourceFromStacktrace(); if ( source ) { details.source = source; } } runLoggingCallbacks( "log", details ); this.assertions.push( { result: !!resultInfo.result, message: resultInfo.message } ); }, pushFailure: function( message, source, actual ) { if ( !( this instanceof Test ) ) { throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace( 2 ) ); } var details = { module: this.module.name, name: this.testName, result: false, message: message || "error", actual: actual || null, testId: this.testId, runtime: now() - this.started }; if ( source ) { details.source = source; } runLoggingCallbacks( "log", details ); this.assertions.push( { result: false, message: message } ); }, resolvePromise: function( promise, phase ) { var then, message, test = this; if ( promise != null ) { then = promise.then; if ( QUnit.objectType( then ) === "function" ) { QUnit.stop(); then.call( promise, function() { QUnit.start(); }, function( error ) { message = "Promise rejected " + ( !phase ? "during" : phase.replace( /Each$/, "" ) ) + " " + test.testName + ": " + ( error.message || error ); test.pushFailure( message, extractStacktrace( error, 0 ) ); // Else next test will carry the responsibility saveGlobal(); // Unblock QUnit.start(); } ); } } }, valid: function() { var filter = config.filter, regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec( filter ), module = config.module && config.module.toLowerCase(), fullName = ( this.module.name + ": " + this.testName ); function moduleChainNameMatch( testModule ) { var testModuleName = testModule.name ? testModule.name.toLowerCase() : null; if ( testModuleName === module ) { return true; } else if ( testModule.parentModule ) { return moduleChainNameMatch( testModule.parentModule ); } else { return false; } } function moduleChainIdMatch( testModule ) { return inArray( testModule.moduleId, config.moduleId ) > -1 || testModule.parentModule && moduleChainIdMatch( testModule.parentModule ); } // Internally-generated tests are always valid if ( this.callback && this.callback.validTest ) { return true; } if ( config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch( this.module ) ) { return false; } if ( config.testId && config.testId.length > 0 && inArray( this.testId, config.testId ) < 0 ) { return false; } if ( module && !moduleChainNameMatch( this.module ) ) { return false; } if ( !filter ) { return true; } return regexFilter ? this.regexFilter( !!regexFilter[ 1 ], regexFilter[ 2 ], regexFilter[ 3 ], fullName ) : this.stringFilter( filter, fullName ); }, regexFilter: function( exclude, pattern, flags, fullName ) { var regex = new RegExp( pattern, flags ); var match = regex.test( fullName ); return match !== exclude; }, stringFilter: function( filter, fullName ) { filter = filter.toLowerCase(); fullName = fullName.toLowerCase(); var include = filter.charAt( 0 ) !== "!"; if ( !include ) { filter = filter.slice( 1 ); } // If the filter matches, we need to honour include if ( fullName.indexOf( filter ) !== -1 ) { return include; } // Otherwise, do the opposite return !include; } }; // Resets the test setup. Useful for tests that modify the DOM. /* DEPRECATED: Use multiple tests instead of resetting inside a test. Use testStart or testDone for custom cleanup. This method will throw an error in 2.0, and will be removed in 2.1 */ QUnit.reset = function() { // Return on non-browser environments // This is necessary to not break on node tests if ( !defined.document ) { return; } var fixture = defined.document && document.getElementById && document.getElementById( "qunit-fixture" ); if ( fixture ) { fixture.innerHTML = config.fixture; } }; QUnit.pushFailure = function() { if ( !QUnit.config.current ) { throw new Error( "pushFailure() assertion outside test context, in " + sourceFromStacktrace( 2 ) ); } // Gets current test obj var currentTest = QUnit.config.current; return currentTest.pushFailure.apply( currentTest, arguments ); }; // Based on Java's String.hashCode, a simple but not // rigorously collision resistant hashing function function generateHash( module, testName ) { var hex, i = 0, hash = 0, str = module + "\x1C" + testName, len = str.length; for ( ; i < len; i++ ) { hash = ( ( hash << 5 ) - hash ) + str.charCodeAt( i ); hash |= 0; } // Convert the possibly negative integer hash code into an 8 character hex string, which isn't // strictly necessary but increases user understanding that the id is a SHA-like hash hex = ( 0x100000000 + hash ).toString( 16 ); if ( hex.length < 8 ) { hex = "0000000" + hex; } return hex.slice( -8 ); } function synchronize( callback, priority, seed ) { var last = !priority, index; if ( QUnit.objectType( callback ) === "array" ) { while ( callback.length ) { synchronize( callback.shift() ); } return; } if ( priority ) { config.queue.splice( priorityCount++, 0, callback ); } else if ( seed ) { if ( !unitSampler ) { unitSampler = unitSamplerGenerator( seed ); } // Insert into a random position after all priority items index = Math.floor( unitSampler() * ( config.queue.length - priorityCount + 1 ) ); config.queue.splice( priorityCount + index, 0, callback ); } else { config.queue.push( callback ); } if ( config.autorun && !config.blocking ) { process( last ); } } function unitSamplerGenerator( seed ) { // 32-bit xorshift, requires only a nonzero seed // http://excamera.com/sphinx/article-xorshift.html var sample = parseInt( generateHash( seed ), 16 ) || -1; return function() { sample ^= sample << 13; sample ^= sample >>> 17; sample ^= sample << 5; // ECMAScript has no unsigned number type if ( sample < 0 ) { sample += 0x100000000; } return sample / 0x100000000; }; } function saveGlobal() { config.pollution = []; if ( config.noglobals ) { for ( var key in global ) { if ( hasOwn.call( global, key ) ) { // In Opera sometimes DOM element ids show up here, ignore them if ( /^qunit-test-output/.test( key ) ) { continue; } config.pollution.push( key ); } } } } function checkPollution() { var newGlobals, deletedGlobals, old = config.pollution; saveGlobal(); newGlobals = diff( config.pollution, old ); if ( newGlobals.length > 0 ) { QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join( ", " ) ); } deletedGlobals = diff( old, config.pollution ); if ( deletedGlobals.length > 0 ) { QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join( ", " ) ); } } // Will be exposed as QUnit.asyncTest function asyncTest( testName, expected, callback ) { if ( arguments.length === 2 ) { callback = expected; expected = null; } QUnit.test( testName, expected, callback, true ); } // Will be exposed as QUnit.test function test( testName, expected, callback, async ) { if ( focused ) { return; } var newTest; if ( arguments.length === 2 ) { callback = expected; expected = null; } newTest = new Test( { testName: testName, expected: expected, async: async, callback: callback } ); newTest.queue(); } // Will be exposed as QUnit.skip function skip( testName ) { if ( focused ) { return; } var test = new Test( { testName: testName, skip: true } ); test.queue(); } // Will be exposed as QUnit.only function only( testName, expected, callback, async ) { var newTest; if ( focused ) { return; } QUnit.config.queue.length = 0; focused = true; if ( arguments.length === 2 ) { callback = expected; expected = null; } newTest = new Test( { testName: testName, expected: expected, async: async, callback: callback } ); newTest.queue(); } function Assert( testContext ) { this.test = testContext; } // Assert helpers QUnit.assert = Assert.prototype = { // Specify the number of expected assertions to guarantee that failed test // (no assertions are run at all) don't slip through. expect: function( asserts ) { if ( arguments.length === 1 ) { this.test.expected = asserts; } else { return this.test.expected; } }, // Increment this Test's semaphore counter, then return a function that // decrements that counter a maximum of once. async: function( count ) { var test = this.test, popped = false, acceptCallCount = count; if ( typeof acceptCallCount === "undefined" ) { acceptCallCount = 1; } test.semaphore += 1; test.usedAsync = true; pauseProcessing(); return function done() { if ( popped ) { test.pushFailure( "Too many calls to the `assert.async` callback", sourceFromStacktrace( 2 ) ); return; } acceptCallCount -= 1; if ( acceptCallCount > 0 ) { return; } test.semaphore -= 1; popped = true; resumeProcessing(); }; }, // Exports test.push() to the user API // Alias of pushResult. push: function( result, actual, expected, message, negative ) { var currentAssert = this instanceof Assert ? this : QUnit.config.current.assert; return currentAssert.pushResult( { result: result, actual: actual, expected: expected, message: message, negative: negative } ); }, pushResult: function( resultInfo ) { // Destructure of resultInfo = { result, actual, expected, message, negative } var assert = this, currentTest = ( assert instanceof Assert && assert.test ) || QUnit.config.current; // Backwards compatibility fix. // Allows the direct use of global exported assertions and QUnit.assert.* // Although, it's use is not recommended as it can leak assertions // to other tests from async tests, because we only get a reference to the current test, // not exactly the test where assertion were intended to be called. if ( !currentTest ) { throw new Error( "assertion outside test context, in " + sourceFromStacktrace( 2 ) ); } if ( currentTest.usedAsync === true && currentTest.semaphore === 0 ) { currentTest.pushFailure( "Assertion after the final `assert.async` was resolved", sourceFromStacktrace( 2 ) ); // Allow this assertion to continue running anyway... } if ( !( assert instanceof Assert ) ) { assert = currentTest.assert; } return assert.test.pushResult( resultInfo ); }, ok: function( result, message ) { message = message || ( result ? "okay" : "failed, expected argument to be truthy, was: " + QUnit.dump.parse( result ) ); this.pushResult( { result: !!result, actual: result, expected: true, message: message } ); }, notOk: function( result, message ) { message = message || ( !result ? "okay" : "failed, expected argument to be falsy, was: " + QUnit.dump.parse( result ) ); this.pushResult( { result: !result, actual: result, expected: false, message: message } ); }, equal: function( actual, expected, message ) { /*jshint eqeqeq:false */ this.pushResult( { result: expected == actual, actual: actual, expected: expected, message: message } ); }, notEqual: function( actual, expected, message ) { /*jshint eqeqeq:false */ this.pushResult( { result: expected != actual, actual: actual, expected: expected, message: message, negative: true } ); }, propEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); this.pushResult( { result: QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message } ); }, notPropEqual: function( actual, expected, message ) { actual = objectValues( actual ); expected = objectValues( expected ); this.pushResult( { result: !QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message, negative: true } ); }, deepEqual: function( actual, expected, message ) { this.pushResult( { result: QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message } ); }, notDeepEqual: function( actual, expected, message ) { this.pushResult( { result: !QUnit.equiv( actual, expected ), actual: actual, expected: expected, message: message, negative: true } ); }, strictEqual: function( actual, expected, message ) { this.pushResult( { result: expected === actual, actual: actual, expected: expected, message: message } ); }, notStrictEqual: function( actual, expected, message ) { this.pushResult( { result: expected !== actual, actual: actual, expected: expected, message: message, negative: true } ); }, "throws": function( block, expected, message ) { var actual, expectedType, expectedOutput = expected,