| | |
| | | /* |
| | | * QUnit - A JavaScript Unit Testing Framework |
| | | * |
| | | * |
| | | * http://docs.jquery.com/QUnit |
| | | * |
| | | * Copyright (c) 2011 John Resig, Jörn Zaefferer |
| | |
| | | // allow utility functions to access the current test environment |
| | | // TODO why?? |
| | | QUnit.current_testEnvironment = this.testEnvironment; |
| | | |
| | | |
| | | try { |
| | | if ( !config.pollution ) { |
| | | saveGlobal(); |
| | |
| | | if ( this.expected && this.expected != this.assertions.length ) { |
| | | QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); |
| | | } |
| | | |
| | | |
| | | var good = 0, bad = 0, |
| | | tests = id("qunit-tests"); |
| | | |
| | |
| | | |
| | | var b = document.createElement("strong"); |
| | | b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>"; |
| | | |
| | | |
| | | addEvent(b, "click", function() { |
| | | var next = b.nextSibling, display = next.style.display; |
| | | next.style.display = display === "none" ? "block" : "none"; |
| | | }); |
| | | |
| | | |
| | | addEvent(b, "dblclick", function(e) { |
| | | var target = e && e.target ? e.target : window.event.srcElement; |
| | | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { |
| | |
| | | total: this.assertions.length |
| | | } ); |
| | | }, |
| | | |
| | | |
| | | queue: function() { |
| | | var test = this; |
| | | synchronize(function() { |
| | |
| | | synchronize(run); |
| | | }; |
| | | } |
| | | |
| | | |
| | | } |
| | | |
| | | var QUnit = { |
| | |
| | | |
| | | QUnit.test(testName, expected, callback, true); |
| | | }, |
| | | |
| | | |
| | | test: function(testName, expected, callback, async) { |
| | | var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg; |
| | | |
| | |
| | | if ( !validTest(config.currentModule + ": " + testName) ) { |
| | | return; |
| | | } |
| | | |
| | | |
| | | var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); |
| | | test.module = config.currentModule; |
| | | test.moduleTestEnvironment = config.currentModuleTestEnviroment; |
| | | test.queue(); |
| | | }, |
| | | |
| | | |
| | | /** |
| | | * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. |
| | | */ |
| | |
| | | notEqual: function(actual, expected, message) { |
| | | QUnit.push(expected != actual, actual, expected, message); |
| | | }, |
| | | |
| | | |
| | | deepEqual: function(actual, expected, message) { |
| | | QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); |
| | | }, |
| | |
| | | |
| | | raises: function(block, expected, message) { |
| | | var actual, ok = false; |
| | | |
| | | |
| | | if (typeof expected === 'string') { |
| | | message = expected; |
| | | expected = null; |
| | | } |
| | | |
| | | |
| | | try { |
| | | block(); |
| | | } catch (e) { |
| | | actual = e; |
| | | } |
| | | |
| | | |
| | | if (actual) { |
| | | // we don't want to validate thrown error |
| | | if (!expected) { |
| | | ok = true; |
| | | // expected is a regexp |
| | | // expected is a regexp |
| | | } else if (QUnit.objectType(expected) === "regexp") { |
| | | ok = expected.test(actual); |
| | | // expected is a constructor |
| | | // expected is a constructor |
| | | } else if (actual instanceof expected) { |
| | | ok = true; |
| | | // expected is a validation function which returns true is validation passed |
| | | // expected is a validation function which returns true is validation passed |
| | | } else if (expected.call({}, actual) === true) { |
| | | ok = true; |
| | | } |
| | | } |
| | | |
| | | |
| | | QUnit.ok(ok, message); |
| | | }, |
| | | |
| | |
| | | process(); |
| | | } |
| | | }, |
| | | |
| | | |
| | | stop: function(timeout) { |
| | | config.semaphore++; |
| | | config.blocking = true; |
| | |
| | | i--; |
| | | } |
| | | } |
| | | |
| | | |
| | | // restrict modules/tests by get parameters |
| | | config.filters = GETParams; |
| | | |
| | | |
| | | // Figure out if we're running the tests from a server or not |
| | | QUnit.isLocal = !!(location.protocol === 'file:'); |
| | | })(); |
| | |
| | | result.parentNode.removeChild( result ); |
| | | } |
| | | }, |
| | | |
| | | |
| | | /** |
| | | * Resets the test setup. Useful for tests that modify the DOM. |
| | | * |
| | | * |
| | | * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. |
| | | */ |
| | | reset: function() { |
| | |
| | | } |
| | | } |
| | | }, |
| | | |
| | | |
| | | /** |
| | | * Trigger an event on an element. |
| | | * |
| | |
| | | elem.fireEvent("on"+type); |
| | | } |
| | | }, |
| | | |
| | | |
| | | // Safe object type checking |
| | | is: function( type, obj ) { |
| | | return QUnit.objectType( obj ) == type; |
| | | }, |
| | | |
| | | |
| | | objectType: function( obj ) { |
| | | if (typeof obj === "undefined") { |
| | | return "undefined"; |
| | |
| | | } |
| | | return undefined; |
| | | }, |
| | | |
| | | |
| | | push: function(result, actual, expected, message) { |
| | | var details = { |
| | | result: result, |
| | |
| | | actual: actual, |
| | | expected: expected |
| | | }; |
| | | |
| | | |
| | | message = escapeHtml(message) || (result ? "okay" : "failed"); |
| | | message = '<span class="test-message">' + message + "</span>"; |
| | | expected = escapeHtml(QUnit.jsDump.parse(expected)); |
| | |
| | | } |
| | | } |
| | | output += "</table>"; |
| | | |
| | | |
| | | QUnit.log(details); |
| | | |
| | | |
| | | config.current.assertions.push({ |
| | | result: !!result, |
| | | message: output |
| | | }); |
| | | }, |
| | | |
| | | |
| | | // Logging callbacks; all receive a single argument with the listed properties |
| | | // run test/logs.html for any related changes |
| | | begin: function() {}, |
| | |
| | | |
| | | addEvent(window, "load", function() { |
| | | QUnit.begin({}); |
| | | |
| | | |
| | | // Initialize the config, saving the execution queue |
| | | var oldconfig = extend({}, config); |
| | | QUnit.init(); |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | var toolbar = id("qunit-testrunner-toolbar"); |
| | | if ( toolbar ) { |
| | | var filter = document.createElement("input"); |
| | |
| | | banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); |
| | | } |
| | | |
| | | if ( tests ) { |
| | | if ( tests ) { |
| | | var result = id("qunit-testresult"); |
| | | |
| | | if ( !result ) { |
| | |
| | | |
| | | QUnit.done( { |
| | | failed: config.stats.bad, |
| | | passed: passed, |
| | | passed: passed, |
| | | total: config.stats.all, |
| | | runtime: runtime |
| | | } ); |
| | |
| | | if ( !i ) { |
| | | return true; |
| | | } |
| | | |
| | | |
| | | while ( i-- ) { |
| | | var filter = config.filters[i], |
| | | not = filter.charAt(0) == '!'; |
| | |
| | | |
| | | function saveGlobal() { |
| | | config.pollution = []; |
| | | |
| | | |
| | | if ( config.noglobals ) { |
| | | for ( var key in window ) { |
| | | config.pollution.push( key ); |
| | |
| | | function checkPollution( name ) { |
| | | var old = config.pollution; |
| | | saveGlobal(); |
| | | |
| | | |
| | | var newGlobals = diff( old, config.pollution ); |
| | | if ( newGlobals.length > 0 ) { |
| | | ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | var callbacks = function () { |
| | | |
| | | // for string, boolean, number and null |
| | |
| | | // b could be an object literal here |
| | | if ( ! (QUnit.objectType(b) === "array")) { |
| | | return false; |
| | | } |
| | | |
| | | } |
| | | |
| | | len = a.length; |
| | | if (len !== b.length) { // safe and faster |
| | | return false; |
| | | } |
| | | |
| | | |
| | | //track reference to avoid circular references |
| | | parents.push(a); |
| | | for (i = 0; i < len; i++) { |
| | |
| | | callers.push(a.constructor); |
| | | //track reference to avoid circular references |
| | | parents.push(a); |
| | | |
| | | |
| | | for (i in a) { // be strict: don't ensures hasOwnProperty and go deep |
| | | loop = false; |
| | | for(j=0;j<parents.length;j++){ |
| | |
| | | return '"' + str.toString().replace(/"/g, '\\"') + '"'; |
| | | }; |
| | | function literal( o ) { |
| | | return o + ''; |
| | | return o + ''; |
| | | }; |
| | | function join( pre, arr, post ) { |
| | | var s = jsDump.separator(), |
| | |
| | | return [ pre, inner + arr, base + post ].join(s); |
| | | }; |
| | | function array( arr ) { |
| | | var i = arr.length, ret = Array(i); |
| | | var i = arr.length, ret = Array(i); |
| | | this.up(); |
| | | while ( i-- ) |
| | | ret[i] = this.parse( arr[i] ); |
| | | ret[i] = this.parse( arr[i] ); |
| | | this.down(); |
| | | return join( '[', ret, ']' ); |
| | | }; |
| | | |
| | | |
| | | var reName = /^function (\w+)/; |
| | | |
| | | |
| | | var jsDump = { |
| | | parse:function( obj, type ) { //type is used mostly internally, you can fix a (custom)type in advance |
| | | var parser = this.parsers[ type || this.typeOf(obj) ]; |
| | | type = typeof parser; |
| | | |
| | | type = typeof parser; |
| | | |
| | | return type == 'function' ? parser.call( this, obj ) : |
| | | type == 'string' ? parser : |
| | | this.parsers.error; |
| | |
| | | this.parsers[name] = parser; |
| | | }, |
| | | // The next 3 are exposed so you can use them |
| | | quote:quote, |
| | | quote:quote, |
| | | literal:literal, |
| | | join:join, |
| | | // |
| | |
| | | if ( name ) |
| | | ret += ' ' + name; |
| | | ret += '('; |
| | | |
| | | |
| | | ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); |
| | | return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); |
| | | }, |
| | |
| | | node:function( node ) { |
| | | var open = QUnit.jsDump.HTML ? '<' : '<', |
| | | close = QUnit.jsDump.HTML ? '>' : '>'; |
| | | |
| | | |
| | | var tag = node.nodeName.toLowerCase(), |
| | | ret = open + tag; |
| | | |
| | | |
| | | for ( var a in QUnit.jsDump.DOMAttrs ) { |
| | | var val = node[QUnit.jsDump.DOMAttrs[a]]; |
| | | if ( val ) |
| | |
| | | }, |
| | | functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function |
| | | var l = fn.length; |
| | | if ( !l ) return ''; |
| | | |
| | | if ( !l ) return ''; |
| | | |
| | | var args = Array(l); |
| | | while ( l-- ) |
| | | args[l] = String.fromCharCode(97+l);//97 is 'a' |
| | |
| | | * |
| | | * More Info: |
| | | * http://ejohn.org/projects/javascript-diff-algorithm/ |
| | | * |
| | | * |
| | | * Usage: QUnit.diff(expected, actual) |
| | | * |
| | | * |
| | | * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over" |
| | | */ |
| | | QUnit.diff = (function() { |
| | | function diff(o, n){ |
| | | var ns = new Object(); |
| | | var os = new Object(); |
| | | |
| | | |
| | | for (var i = 0; i < n.length; i++) { |
| | | if (ns[n[i]] == null) |
| | | if (ns[n[i]] == null) |
| | | ns[n[i]] = { |
| | | rows: new Array(), |
| | | o: null |
| | | }; |
| | | ns[n[i]].rows.push(i); |
| | | } |
| | | |
| | | |
| | | for (var i = 0; i < o.length; i++) { |
| | | if (os[o[i]] == null) |
| | | if (os[o[i]] == null) |
| | | os[o[i]] = { |
| | | rows: new Array(), |
| | | n: null |
| | | }; |
| | | os[o[i]].rows.push(i); |
| | | } |
| | | |
| | | |
| | | for (var i in ns) { |
| | | if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { |
| | | n[ns[i].rows[0]] = { |
| | |
| | | }; |
| | | } |
| | | } |
| | | |
| | | |
| | | for (var i = 0; i < n.length - 1; i++) { |
| | | if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && |
| | | n[i + 1] == o[n[i].row + 1]) { |
| | |
| | | }; |
| | | } |
| | | } |
| | | |
| | | |
| | | for (var i = n.length - 1; i > 0; i--) { |
| | | if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && |
| | | n[i - 1] == o[n[i].row - 1]) { |
| | |
| | | }; |
| | | } |
| | | } |
| | | |
| | | |
| | | return { |
| | | o: o, |
| | | n: n |
| | | }; |
| | | } |
| | | |
| | | |
| | | return function(o, n){ |
| | | o = o.replace(/\s+$/, ''); |
| | | n = n.replace(/\s+$/, ''); |
| | | var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); |
| | | |
| | | var str = ""; |
| | | |
| | | |
| | | var oSpace = o.match(/\s+/g); |
| | | if (oSpace == null) { |
| | | oSpace = [" "]; |
| | |
| | | else { |
| | | nSpace.push(" "); |
| | | } |
| | | |
| | | |
| | | if (out.n.length == 0) { |
| | | for (var i = 0; i < out.o.length; i++) { |
| | | str += '<del>' + out.o[i] + oSpace[i] + "</del>"; |
| | |
| | | str += '<del>' + out.o[n] + oSpace[n] + "</del>"; |
| | | } |
| | | } |
| | | |
| | | |
| | | for (var i = 0; i < out.n.length; i++) { |
| | | if (out.n[i].text == null) { |
| | | str += '<ins>' + out.n[i] + nSpace[i] + "</ins>"; |
| | | } |
| | | else { |
| | | var pre = ""; |
| | | |
| | | |
| | | for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { |
| | | pre += '<del>' + out.o[n] + oSpace[n] + "</del>"; |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | return str; |
| | | }; |
| | | })(); |