Js test fixes for common, common-requirejs, xmodule.

This commit is contained in:
muzaffaryousaf
2016-04-06 21:19:32 +05:00
committed by Usman Khalid
parent 375f2bfc59
commit 9cbfea2edd
80 changed files with 2611 additions and 2166 deletions

View File

@@ -1,15 +1,6 @@
function callPeriodicallyUntil(block, delay, condition, i) { // i is optional
i = i || 0;
block(i);
waits(delay);
runs(function () {
if (!condition()) {
callPeriodicallyUntil(block, delay, condition, i + 1);
}
});
}
describe("Formula Equation Preview", function () {
'use strict';
var formulaEquationPreview = window.formulaEquationPreview;
beforeEach(function () {
// Simulate an environment conducive to a FormulaEquationInput
var $fixture = this.$fixture = $('\
@@ -37,7 +28,7 @@ describe("Formula Equation Preview", function () {
// Call old function.
return old$find.apply(this, arguments);
}
};
$.find.matchesSelector = old$find.matchesSelector;
this.oldDGEBI = document.getElementById;
@@ -50,8 +41,8 @@ describe("Formula Equation Preview", function () {
this.oldProblem = window.Problem;
window.Problem = {};
Problem.inputAjax = jasmine.createSpy('Problem.inputAjax')
.andCallFake(function () {
window.Problem.inputAjax = jasmine.createSpy('Problem.inputAjax')
.and.callFake(function () {
ajaxTimes.push(Date.now());
});
@@ -60,19 +51,19 @@ describe("Formula Equation Preview", function () {
this.oldMathJax = window.MathJax;
window.MathJax = {Hub: {}};
MathJax.Hub.getAllJax = jasmine.createSpy('MathJax.Hub.getAllJax')
.andReturn([this.jax]);
MathJax.Hub.Queue = function (callback) {
window.MathJax.Hub.getAllJax = jasmine.createSpy('MathJax.Hub.getAllJax')
.and.returnValue([this.jax]);
window.MathJax.Hub.Queue = function (callback) {
if (typeof (callback) == 'function') {
callback();
}
}
spyOn(MathJax.Hub, 'Queue').andCallThrough()
MathJax.Hub.Startup = jasmine.createSpy('MathJax.Hub.Startup');
MathJax.Hub.Startup.signal = jasmine.createSpy('MathJax.Hub.Startup.signal');
MathJax.Hub.Startup.signal.Interest = function (callback) {
};
spyOn(window.MathJax.Hub, 'Queue').and.callThrough();
window.MathJax.Hub.Startup = jasmine.createSpy('MathJax.Hub.Startup');
window.MathJax.Hub.Startup.signal = jasmine.createSpy('MathJax.Hub.Startup.signal');
window.MathJax.Hub.Startup.signal.Interest = function (callback) {
callback('End');
}
};
});
it('(the test) is able to swap out the behavior of $', function () {
@@ -90,22 +81,22 @@ describe("Formula Equation Preview", function () {
});
describe('Ajax requests', function () {
beforeEach(function () {
beforeEach(function (done) {
// This is common to all tests on ajax requests.
formulaEquationPreview.enable();
// This part may be asynchronous, so wait.
waitsFor(function () {
return Problem.inputAjax.wasCalled;
}, "AJAX never called initially", 1000);
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() > 0;
}).then(done);
});
it('has an initial request with the correct parameters', function () {
expect(Problem.inputAjax.callCount).toEqual(1);
expect(window.Problem.inputAjax.calls.count()).toEqual(1);
// Use `.toEqual` rather than `.toHaveBeenCalledWith`
// since it supports `jasmine.any`.
expect(Problem.inputAjax.mostRecentCall.args).toEqual([
expect(window.Problem.inputAjax.calls.mostRecent().args).toEqual([
"THE_URL",
"THE_ID",
"preview_formcalc",
@@ -115,65 +106,59 @@ describe("Formula Equation Preview", function () {
]);
});
it('does not request again if the initial request has already been made', function () {
it('does not request again if the initial request has already been made', function (done) {
// jshint undef:false
expect(Problem.inputAjax.callCount).toEqual(1);
expect(window.Problem.inputAjax.calls.count()).toEqual(1);
// Reset the spy in order to check calls again.
Problem.inputAjax.reset();
window.Problem.inputAjax.calls.reset();
// Enabling the formulaEquationPreview again to see if this will
// reinitialize input request once again.
formulaEquationPreview.enable();
// This part may be asynchronous, so wait.
waitsFor(function () {
return !Problem.inputAjax.wasCalled;
}, "times out in case of AJAX call", 1000);
// Expect Problem.inputAjax was not called as input request was
// initialized before.
expect(Problem.inputAjax).not.toHaveBeenCalled();
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() === 0;
}).then(function () {
// Expect window.Problem.inputAjax was not called as input request was
// initialized before.
expect(window.Problem.inputAjax).not.toHaveBeenCalled();
}).always(done);
});
it('makes a request on user input', function () {
Problem.inputAjax.reset();
it('makes a request on user input', function (done) {
window.Problem.inputAjax.calls.reset();
$('#input_THE_ID').val('user_input').trigger('input');
// This part is probably asynchronous
waitsFor(function () {
return Problem.inputAjax.wasCalled;
}, "AJAX never called on user input", 1000);
runs(function () {
expect(Problem.inputAjax.mostRecentCall.args[3].formula
).toEqual('user_input');
});
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() > 0;
}).then(function () {
expect(window.Problem.inputAjax.calls.mostRecent().args[3].formula).toEqual('user_input');
}).always(done);
});
it("isn't requested for empty input", function () {
Problem.inputAjax.reset();
it("isn't requested for empty input", function (done) {
window.Problem.inputAjax.calls.reset();
// When we make an input of '',
$('#input_THE_ID').val('').trigger('input');
// Either it makes a request or jumps straight into displaying ''.
waitsFor(function () {
jasmine.waitUntil(function () {
// (Short circuit if `inputAjax` is indeed called)
return Problem.inputAjax.wasCalled || // jshint ignore:line
MathJax.Hub.Queue.wasCalled;
}, "AJAX never called on user input", 1000);
runs(function () {
return window.Problem.inputAjax.calls.count() > 0 ||
window.MathJax.Hub.Queue.calls.count() > 0;
}).then(function () {
// Expect the request not to have been called.
expect(Problem.inputAjax).not.toHaveBeenCalled();
});
expect(window.Problem.inputAjax).not.toHaveBeenCalled();
}).always(done);
});
it('limits the number of requests per second', function () {
it('limits the number of requests per second', function (done) {
var minDelay = formulaEquationPreview.minDelay;
var end = Date.now() + minDelay * 1.1;
var step = 10; // ms
var $input = $('#input_THE_ID');
var value;
@@ -182,41 +167,42 @@ describe("Formula Equation Preview", function () {
$input.val(value).trigger('input');
}
callPeriodicallyUntil(inputAnother, step, function () {
var self = this;
var iter = 0;
jasmine.waitUntil(function () {
inputAnother(iter++);
return Date.now() > end; // Stop when we get to `end`.
});
}).then(function () {
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() > 0 &&
window.Problem.inputAjax.calls.mostRecent().args[3].formula === value;
}).then(_.bind(function () {
// There should be 2 or 3 calls (depending on leading edge).
expect(window.Problem.inputAjax.calls.count()).not.toBeGreaterThan(3);
waitsFor(function () {
return Problem.inputAjax.wasCalled &&
Problem.inputAjax.mostRecentCall.args[3].formula == value;
}, "AJAX never called with final value from input", 1000);
runs(function () {
// There should be 2 or 3 calls (depending on leading edge).
expect(Problem.inputAjax.callCount).not.toBeGreaterThan(3);
// The calls should happen approximately `minDelay` apart.
for (var i =1; i < this.ajaxTimes.length; i ++) {
var diff = this.ajaxTimes[i] - this.ajaxTimes[i - 1];
expect(diff).toBeGreaterThan(minDelay - 10);
}
// The calls should happen approximately `minDelay` apart.
for (var i =1; i < this.ajaxTimes.length; i ++) {
var diff = this.ajaxTimes[i] - this.ajaxTimes[i - 1];
expect(diff).toBeGreaterThan(minDelay - 10);
}
}, self)).then(function () {
done();
});
});
});
});
describe("Visible results (icon and mathjax)", function () {
it('displays a loading icon when requests are open', function () {
it('displays a loading icon when requests are open', function (done) {
var $img = $("img.loading");
expect($img.css('visibility')).toEqual('hidden');
formulaEquationPreview.enable();
expect($img.css('visibility')).toEqual('visible');
// This part could be asynchronous
waitsFor(function () {
return Problem.inputAjax.wasCalled;
}, "AJAX never called initially", 1000);
runs(function () {
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() > 0;
}).then(function () {
expect($img.css('visibility')).toEqual('visible');
// Reset and send another request.
@@ -224,23 +210,23 @@ describe("Formula Equation Preview", function () {
$("#input_THE_ID").val("different").trigger('input');
expect($img.css('visibility')).toEqual('visible');
});
// Don't let it fail later.
waitsFor(function () {
var args = Problem.inputAjax.mostRecentCall.args;
return args[3].formula == "different";
}).then(function () {
return jasmine.waitUntil(function () {
var args = window.Problem.inputAjax.calls.mostRecent().args;
return args[3].formula === "different";
}).then(done);
});
});
it('updates MathJax and loading icon on callback', function () {
it('updates MathJax and loading icon on callback', function (done) {
formulaEquationPreview.enable();
waitsFor(function () {
return Problem.inputAjax.wasCalled;
}, "AJAX never called initially", 1000);
runs(function () {
var args = Problem.inputAjax.mostRecentCall.args;
var jax = this.jax;
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() > 0;
}).then(function () {
var args = window.Problem.inputAjax.calls.mostRecent().args;
var callback = args[4];
callback({
preview: 'THE_FORMULA',
@@ -252,29 +238,27 @@ describe("Formula Equation Preview", function () {
// We should look in the preview div for the MathJax.
var previewDiv = $("#input_THE_ID_preview")[0];
expect(MathJax.Hub.getAllJax).toHaveBeenCalledWith(previewDiv);
expect(window.MathJax.Hub.getAllJax).toHaveBeenCalledWith(previewDiv);
// Refresh the MathJax.
expect(MathJax.Hub.Queue).toHaveBeenCalledWith(
['Text', this.jax, 'THE_FORMULA']
expect(window.MathJax.Hub.Queue).toHaveBeenCalledWith(
['Text', jax, 'THE_FORMULA']
);
});
}).always(done);
});
it('finds alternatives if MathJax hasn\'t finished loading', function () {
it('finds alternatives if MathJax hasn\'t finished loading', function (done) {
formulaEquationPreview.enable();
$('#input_THE_ID').val('user_input').trigger('input');
waitsFor(function () {
return Problem.inputAjax.wasCalled;
}, "AJAX never called initially", 1000);
runs(function () {
var args = Problem.inputAjax.mostRecentCall.args;
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() > 0;
}).then(function () {
var args = window.Problem.inputAjax.calls.mostRecent().args;
var callback = args[4];
// Cannot find MathJax.
MathJax.Hub.getAllJax.andReturn([]);
window.MathJax.Hub.getAllJax.and.returnValue([]);
spyOn(console, 'log');
callback({
@@ -290,74 +274,68 @@ describe("Formula Equation Preview", function () {
expect(previewElement.firstChild.data).toEqual("\\(THE_FORMULA\\)");
// Refresh the MathJax.
expect(MathJax.Hub.Queue).toHaveBeenCalledWith(
expect(window.MathJax.Hub.Queue).toHaveBeenCalledWith(
['Typeset', jasmine.any(Object), jasmine.any(Element)]
);
});
}).always(done);
});
it('displays errors from the server well', function () {
it('displays errors from the server well', function (done) {
var $img = $("img.loading");
formulaEquationPreview.enable();
waitsFor(function () {
return Problem.inputAjax.wasCalled;
}, "AJAX never called initially", 1000);
var jax = this.jax;
runs(function () {
var args = Problem.inputAjax.mostRecentCall.args;
formulaEquationPreview.enable();
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() > 0;
}).then(function () {
var args = window.Problem.inputAjax.calls.mostRecent().args;
var callback = args[4];
callback({
error: 'OOPSIE',
request_start: args[3].request_start
});
expect(MathJax.Hub.Queue).not.toHaveBeenCalled();
expect(window.MathJax.Hub.Queue).not.toHaveBeenCalled();
expect($img.css('visibility')).toEqual('visible');
});
var errorDelay = formulaEquationPreview.errorDelay * 1.1;
waitsFor(function () {
return MathJax.Hub.Queue.wasCalled;
}, "Error message never displayed", 2000);
runs(function () {
// Refresh the MathJax.
expect(MathJax.Hub.Queue).toHaveBeenCalledWith(
['Text', this.jax, '\\text{OOPSIE}']
);
expect($img.css('visibility')).toEqual('hidden');
}).then(function () {
jasmine.waitUntil(function () {
return window.MathJax.Hub.Queue.calls.count() > 0;
}).then(function () {
// Refresh the MathJax.
expect(window.MathJax.Hub.Queue).toHaveBeenCalledWith(
['Text', jax, '\\text{OOPSIE}']
);
expect($img.css('visibility')).toEqual('hidden');
}).then(done);
});
});
});
describe('Multiple callbacks', function () {
beforeEach(function () {
beforeEach(function (done) {
formulaEquationPreview.enable();
waitsFor(function () {
return Problem.inputAjax.wasCalled;
});
runs(function () {
var self = this;
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() > 0;
}).then(function () {
$('#input_THE_ID').val('different').trigger('input');
});
jasmine.waitUntil(function () {
return window.Problem.inputAjax.calls.count() > 1;
}).then(_.bind(function () {
var args0 = window.Problem.inputAjax.calls.argsFor(0);
var args1 = window.Problem.inputAjax.calls.argsFor(1);
var response0 = {
preview: 'THE_FORMULA_0',
request_start: args0[3].request_start
};
var response1 = {
preview: 'THE_FORMULA_1',
request_start: args1[3].request_start
};
waitsFor(function () {
return Problem.inputAjax.callCount > 1;
});
runs(function () {
var args = Problem.inputAjax.argsForCall;
var response0 = {
preview: 'THE_FORMULA_0',
request_start: args[0][3].request_start
};
var response1 = {
preview: 'THE_FORMULA_1',
request_start: args[1][3].request_start
};
this.callbacks = [args[0][4], args[1][4]];
this.responses = [response0, response1];
this.callbacks = [args0[4], args0[4]];
this.responses = [response0, response1];
}, self)).then(done);
});
});
@@ -367,13 +345,13 @@ describe("Formula Equation Preview", function () {
expect($img.css('visibility')).toEqual('visible');
this.callbacks[0](this.responses[0]);
expect(MathJax.Hub.Queue).toHaveBeenCalledWith(
expect(window.MathJax.Hub.Queue).toHaveBeenCalledWith(
['Text', this.jax, 'THE_FORMULA_0']
);
expect($img.css('visibility')).toEqual('visible');
this.callbacks[1](this.responses[1]);
expect(MathJax.Hub.Queue).toHaveBeenCalledWith(
expect(window.MathJax.Hub.Queue).toHaveBeenCalledWith(
['Text', this.jax, 'THE_FORMULA_1']
);
expect($img.css('visibility')).toEqual('hidden');
@@ -386,40 +364,38 @@ describe("Formula Equation Preview", function () {
// Switch the order (1 returns before 0)
this.callbacks[1](this.responses[1]);
expect(MathJax.Hub.Queue).toHaveBeenCalledWith(
expect(window.MathJax.Hub.Queue).toHaveBeenCalledWith(
['Text', this.jax, 'THE_FORMULA_1']
);
expect($img.css('visibility')).toEqual('hidden');
MathJax.Hub.Queue.reset();
window.MathJax.Hub.Queue.calls.reset();
this.callbacks[0](this.responses[0]);
expect(MathJax.Hub.Queue).not.toHaveBeenCalled();
expect(window.MathJax.Hub.Queue).not.toHaveBeenCalled();
expect($img.css('visibility')).toEqual('hidden');
});
it("doesn't show an error if the responses are close together",
function () {
this.callbacks[0]({
error: 'OOPSIE',
request_start: this.responses[0].request_start
});
expect(MathJax.Hub.Queue).not.toHaveBeenCalled();
// Error message waiting to be displayed
it("doesn't show an error if the responses are close together", function (done) {
this.callbacks[0]({
error: 'OOPSIE',
request_start: this.responses[0].request_start
});
expect(window.MathJax.Hub.Queue).not.toHaveBeenCalled();
this.callbacks[1](this.responses[1]);
expect(MathJax.Hub.Queue).toHaveBeenCalledWith(
['Text', this.jax, 'THE_FORMULA_1']
);
// Error message waiting to be displayed
this.callbacks[1](this.responses[1]);
expect(window.MathJax.Hub.Queue).toHaveBeenCalledWith(
['Text', this.jax, 'THE_FORMULA_1']
);
// Make sure that it doesn't indeed show up later
MathJax.Hub.Queue.reset();
var errorDelay = formulaEquationPreview.errorDelay * 1.1;
waits(errorDelay);
runs(function () {
expect(MathJax.Hub.Queue).not.toHaveBeenCalled();
})
});
// Make sure that it doesn't indeed show up later
window.MathJax.Hub.Queue.calls.reset();
jasmine.waitUntil(function () {
return formulaEquationPreview.errorDelay * 1.1;
}).then(function () {
expect(window.MathJax.Hub.Queue).not.toHaveBeenCalled();
}).then(done);
});
});
afterEach(function () {
@@ -428,15 +404,15 @@ describe("Formula Equation Preview", function () {
document.getElementById = this.oldDGEBI;
// Return Problem
Problem = this.oldProblem;
if (Problem === undefined) {
delete Problem;
window.Problem = this.oldProblem;
if (window.Problem === undefined) {
delete window.Problem;
}
// Return MathJax
MathJax = this.oldMathJax;
if (MathJax === undefined) {
delete MathJax;
window.MathJax = this.oldMathJax;
if (window.MathJax === undefined) {
delete window.MathJax;
}
});
});

View File

@@ -0,0 +1,248 @@
// Extensions to Jasmine.
//
// This file adds the following:
// 1. Custom matchers that may be helpful project-wise.
// 2. Copies of some matchers from Jasmine-jQuery.
// Because Jasmine-Jquery uses its own version of JQuery, events registered in the code
// using the platform version of JQuery are not "noticed" by Jasmine-jQuery matchers.
// Similarly equality matching does not work either. So after the platform version of
// jQuery has been loaded, we set these matchers up again in this module.
(function(root, factory) {
/* jshint strict: false */
factory(root, root.jQuery);
}((function() {
/* jshint strict: false */
return this;
}()), function(window, $) {
'use strict';
// Add custom Jasmine matchers.
beforeEach(function() {
jasmine.addMatchers(window.imagediff.jasmine);
jasmine.addMatchers({
toHaveAttrs: function() {
return {
compare: function(actual, attrs) {
var result = {},
element = actual;
if ($.isEmptyObject(attrs)) {
return {
pass: false
};
}
result.pass = _.every(attrs, function(value, name) {
return element.attr(name) === value;
});
return result;
}
};
},
toBeInRange: function() {
return {
compare: function(actual, min, max) {
return {
pass: min <= actual && actual <= max
};
}
};
},
toBeInArray: function() {
return {
compare: function(actual, array) {
return {
pass: $.inArray(actual, array) > -1
};
}
};
}
});
});
/* jshint ignore:start */
// All the code below is taken from:
// https://github.com/velesin/jasmine-jquery/blob/2.1.1/lib/jasmine-jquery.js
beforeEach(function() {
jasmine.addMatchers({
toHandle: function() {
return {
compare: function(actual, event) {
if (!actual || actual.length === 0) return {
pass: false
};
var events = $._data($(actual).get(0), "events");
if (!events || !event || typeof event !== "string") {
return {
pass: false
};
}
var namespaces = event.split("."),
eventType = namespaces.shift(),
sortedNamespaces = namespaces.slice(0).sort(),
namespaceRegExp = new RegExp("(^|\\.)" + sortedNamespaces.join("\\.(?:.*\\.)?") + "(\\.|$)");
if (events[eventType] && namespaces.length) {
for (var i = 0; i < events[eventType].length; i++) {
var namespace = events[eventType][i].namespace;
if (namespaceRegExp.test(namespace))
return {
pass: true
};
}
} else {
return {
pass: (events[eventType] && events[eventType].length > 0)
};
}
return {
pass: false
};
}
};
},
toHandleWith: function() {
return {
compare: function(actual, eventName, eventHandler) {
if (!actual || actual.length === 0) return {
pass: false
};
var normalizedEventName = eventName.split('.')[0],
stack = $._data($(actual).get(0), "events")[normalizedEventName];
for (var i = 0; i < stack.length; i++) {
if (stack[i].handler == eventHandler) return {
pass: true
};
}
return {
pass: false
};
}
};
}
});
jasmine.addCustomEqualityTester(function(a, b) {
if (a && b) {
if (a instanceof $ || jasmine.isDomNode(a)) {
var $a = $(a);
if (b instanceof $)
return $a.length == b.length && a.is(b);
return $a.is(b);
}
if (b instanceof $ || jasmine.isDomNode(b)) {
var $b = $(b);
if (a instanceof $)
return a.length == $b.length && $b.is(a);
return $(b).is(a);
}
}
});
jasmine.addCustomEqualityTester(function(a, b) {
if (a instanceof $ && b instanceof $ && a.size() == b.size())
return a.is(b);
});
});
var data = {
spiedEvents: {},
handlers: []
};
jasmine.jQuery.events = {
spyOn: function(selector, eventName) {
var handler = function(e) {
var calls = (typeof data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] !== 'undefined') ? data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0;
data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] = {
args: jasmine.util.argsToArray(arguments),
calls: ++calls
};
};
$(selector).on(eventName, handler);
data.handlers.push(handler);
return {
selector: selector,
eventName: eventName,
handler: handler,
reset: function() {
delete data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)];
},
calls: {
count: function() {
return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ?
data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : 0;
},
any: function() {
return data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)] ?
!!data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].calls : false;
}
}
};
},
args: function(selector, eventName) {
var actualArgs = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)].args;
if (!actualArgs) {
throw "There is no spy for " + eventName + " on " + selector.toString() + ". Make sure to create a spy using spyOnEvent.";
}
return actualArgs;
},
wasTriggered: function(selector, eventName) {
return !!(data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)]);
},
wasTriggeredWith: function(selector, eventName, expectedArgs, util, customEqualityTesters) {
var actualArgs = jasmine.jQuery.events.args(selector, eventName).slice(1);
if (Object.prototype.toString.call(expectedArgs) !== '[object Array]')
actualArgs = actualArgs[0];
return util.equals(actualArgs, expectedArgs, customEqualityTesters);
},
wasPrevented: function(selector, eventName) {
var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)],
args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args,
e = args ? args[0] : undefined;
return e && e.isDefaultPrevented();
},
wasStopped: function(selector, eventName) {
var spiedEvent = data.spiedEvents[jasmine.spiedEventsKey(selector, eventName)],
args = (jasmine.util.isUndefined(spiedEvent)) ? {} : spiedEvent.args,
e = args ? args[0] : undefined;
return e && e.isPropagationStopped();
},
cleanUp: function() {
data.spiedEvents = {};
data.handlers = [];
}
};
/* jshint ignore:end */
}));

View File

@@ -0,0 +1,64 @@
// Custom library to replace the legacy non jasmine 2.0 compatible jasmine-stealth
// jshint ignore: start
(function (root, factory) {
factory(root, root.jasmine, root._);
}((function () {
return this;
}()), function (window, jasmine, _) {
var fake, clearSpies, spyOnConstructor, unfakes = [];
clearSpies = function () {
_.each(unfakes, function (u) {
return u();
});
return unfakes = [];
};
fake = function (owner, thingToFake, newThing) {
var originalThing;
originalThing = owner[thingToFake];
owner[thingToFake] = newThing;
return unfakes.push(function () {
return owner[thingToFake] = originalThing;
});
};
spyOnConstructor = function (owner, classToFake, methodsToSpy) {
var fakeClass, spies;
fakeClass = (function () {
function _Class() {
spies.constructor.apply(this, arguments);
}
return _Class;
})();
if (!methodsToSpy) {
methodsToSpy = [];
}
if (_.isString(methodsToSpy)) {
methodsToSpy = [methodsToSpy];
}
spies = {
constructor: jasmine.createSpy('' + classToFake + '\'s constructor')
};
_.each(methodsToSpy, function (methodName) {
spies[methodName] = jasmine.createSpy('' + classToFake + '#' + methodName);
return fakeClass.prototype[methodName] = function () {
return spies[methodName].apply(this, arguments);
};
});
fake(owner, classToFake, fakeClass);
return spies;
};
jasmine.stealth = {
spyOnConstructor: spyOnConstructor,
clearSpies: clearSpies
};
}));

View File

@@ -0,0 +1,48 @@
// Takes a latch function and optionally timeout and error message.
// Polls the latch function until the it returns true or the maximum timeout expires
// whichever comes first.
(function(root, factory) {
/* jshint strict: false */
factory(root, root.jQuery);
}((function() {
/* jshint strict: false */
return this;
}()), function(window, $) {
'use strict';
var MAX_TIMEOUT = jasmine.DEFAULT_TIMEOUT_INTERVAL;
var realSetTimeout = setTimeout;
var realClearTimeout = clearTimeout;
jasmine.waitUntil = function(conditionalFn, maxTimeout, message) {
var deferred = $.Deferred(),
elapsedTimeInMs = 0,
timeout;
maxTimeout = maxTimeout || MAX_TIMEOUT;
message = message || 'Timeout has expired';
var fn = function() {
elapsedTimeInMs += 50;
if (conditionalFn()) {
if (timeout) { realClearTimeout(timeout); }
deferred.resolve();
} else {
if (elapsedTimeInMs >= maxTimeout) {
// explicitly fail the spec with the given message
fail(message); // jshint ignore:line
// clear timeout and reject the promise
realClearTimeout(timeout);
deferred.reject();
return;
}
timeout = realSetTimeout(fn, 50);
}
};
realSetTimeout(fn, 50);
return deferred.promise();
};
}));

View File

@@ -1,4 +1,6 @@
describe("CSS3 workarounds", function() {
'use strict';
var pointerEventsNone = window.pointerEventsNone;
describe("pointer-events", function() {
beforeEach(function() {
var html = "<a href='#' class='is-disabled'>What wondrous life in this I lead</a>";
@@ -21,17 +23,7 @@ describe("CSS3 workarounds", function() {
});
it("should prevent default when pointerEvents is not Supported", function() {
// mock document.body.style so it does not include 'pointerEvents'
var mockBodyStyle = {},
bodyStyleKeys = Object.keys(document.body.style);
for (var index = 0; index < bodyStyleKeys.length; index++) {
var key = bodyStyleKeys[index];
if (key !== "pointerEvents") {
mockBodyStyle[key] = document.body.style[key];
};
};
pointerEventsNone(".is-disabled", mockBodyStyle);
pointerEventsNone(".is-disabled", {});
spyOnEvent(".is-disabled", "click");
$(".is-disabled").click();
expect("click").toHaveBeenPreventedOn(".is-disabled");

View File

@@ -19,34 +19,34 @@
});
it("should make an AJAX request to the correct URL", function () {
spyOn($, 'ajax').andReturn(deferred);
spyOn($, 'ajax').and.returnValue(deferred);
Language.init();
lang_selector.trigger('change');
expect($.ajax.mostRecentCall.args[0].url).toEqual("/api/user/v1/preferences/test1/");
expect($.ajax.calls.mostRecent().args[0].url).toEqual("/api/user/v1/preferences/test1/");
});
it("should make an AJAX request with correct type", function () {
spyOn($, 'ajax').andReturn(deferred);
spyOn($, 'ajax').and.returnValue(deferred);
Language.init();
lang_selector.trigger('change');
expect($.ajax.mostRecentCall.args[0].type).toEqual("PATCH");
expect($.ajax.calls.mostRecent().args[0].type).toEqual("PATCH");
});
it("should make an AJAX request with correct data", function () {
spyOn($, 'ajax').andReturn(deferred);
spyOn($, 'ajax').and.returnValue(deferred);
Language.init();
lang_selector.val('ar');
lang_selector.trigger('change');
expect($.ajax.mostRecentCall.args[0].data).toEqual('{"pref-lang":"ar"}');
expect($.ajax.calls.mostRecent().args[0].data).toEqual('{"pref-lang":"ar"}');
// change to 'en' from 'ar'
lang_selector.val('en');
lang_selector.trigger('change');
expect($.ajax.mostRecentCall.args[0].data).toEqual('{"pref-lang":"en"}');
expect($.ajax.calls.mostRecent().args[0].data).toEqual('{"pref-lang":"en"}');
});
it("should call refresh on ajax failure", function () {
spyOn($, 'ajax').andCallFake(function () {
spyOn($, 'ajax').and.callFake(function () {
var d = $.Deferred();
d.reject();
return d.promise();

View File

@@ -11,24 +11,37 @@ describe('TooltipManager', function () {
this.element = $('#test-id');
this.tooltip = new TooltipManager(document.body);
jasmine.Clock.useMock();
jasmine.clock().install();
// Set default dimensions to make testing easer.
$('.tooltip').height(HEIGHT).width(WIDTH);
// Re-write default jasmine-jquery to consider opacity.
this.addMatchers({
jasmine.addMatchers({
toBeVisible: function() {
return this.actual.is(':visible') || parseFloat(this.actual.css('opacity'));
return {
compare: function (actual) {
return {
pass: actual.is(':visible') || parseFloat(actual.css('opacity'))
};
}
};
},
toBeHidden: function() {
return this.actual.is(':hidden') || !parseFloat(this.actual.css('opacity'));
},
toBeHidden: function () {
return {
compare: function (actual) {
return {
pass: actual.is(':hidden') || !parseFloat(actual.css('opacity'))
};
}
};
}
});
});
afterEach(function () {
this.tooltip.destroy();
jasmine.clock().uninstall();
});
showTooltip = function (element) {
@@ -36,7 +49,7 @@ describe('TooltipManager', function () {
pageX: PAGE_X,
pageY: PAGE_Y
}));
jasmine.Clock.tick(500);
jasmine.clock().tick(500);
};
it('can destroy itself', function () {
@@ -58,7 +71,7 @@ describe('TooltipManager', function () {
showTooltip(this.element);
expect($('.tooltip')).toBeVisible();
this.element.trigger($.Event("mouseout"));
jasmine.Clock.tick(50);
jasmine.clock().tick(50);
expect($('.tooltip')).toBeHidden();
});
@@ -66,7 +79,7 @@ describe('TooltipManager', function () {
showTooltip(this.element);
expect($('.tooltip')).toBeVisible();
this.element.trigger($.Event("click"));
jasmine.Clock.tick(50);
jasmine.clock().tick(50);
expect($('.tooltip')).toBeHidden();
});

View File

@@ -2,7 +2,7 @@
// supported in older browsers
var pointerEventsNone = function (selector, supportedStyles) {
// Check to see if the brower supports 'pointer-events' css rule.
// Check to see if the browser supports 'pointer-events' css rule.
// If it doesn't, use javascript to stop the link from working
// when clicked.
$(selector).click(function (event) {

View File

@@ -268,52 +268,71 @@
return element;
}
function imageDiffEqualMessage (actual, expected) {
return function () {
var
div = get('div'),
a = get('div', '<div>Actual:</div>'),
b = get('div', '<div>Expected:</div>'),
c = get('div', '<div>Diff:</div>'),
diff = imagediff.diff(actual, expected),
canvas = getCanvas(),
context;
canvas.height = diff.height;
canvas.width = diff.width;
div.style.overflow = 'hidden';
a.style.float = 'left';
b.style.float = 'left';
c.style.float = 'left';
context = canvas.getContext('2d');
context.putImageData(diff, 0, 0);
a.appendChild(toCanvas(actual));
b.appendChild(toCanvas(expected));
c.appendChild(canvas);
div.appendChild(a);
div.appendChild(b);
div.appendChild(c);
return div;
};
}
jasmine = {
toBeImageData : function () {
return imagediff.isImageData(this.actual);
return {
compare: function () {
return {
pass: imagediff.isImageData(this.actual)
}
}
};
},
toImageDiffEqual : function (expected, tolerance) {
if (typeof (document) !== UNDEFINED) {
this.message = function () {
toImageDiffEqual: function () {
return {
compare: function (actual, expected, tolerance) {
var
div = get('div'),
a = get('div', '<div>Actual:</div>'),
b = get('div', '<div>Expected:</div>'),
c = get('div', '<div>Diff:</div>'),
diff = imagediff.diff(this.actual, expected),
canvas = getCanvas(),
context;
result = {};
canvas.height = diff.height;
canvas.width = diff.width;
div.style.overflow = 'hidden';
a.style.float = 'left';
b.style.float = 'left';
c.style.float = 'left';
context = canvas.getContext('2d');
context.putImageData(diff, 0, 0);
a.appendChild(toCanvas(this.actual));
b.appendChild(toCanvas(expected));
c.appendChild(canvas);
div.appendChild(a);
div.appendChild(b);
div.appendChild(c);
return [
div,
"Expected not to be equal."
];
};
}
return imagediff.equal(this.actual, expected, tolerance);
result.pass = imagediff.equal(actual, expected, tolerance);
if (typeof (document) !== UNDEFINED) {
result.message = imageDiffEqualMessage(actual, expected);
}
return result;
},
negativeCompare: function (actual, expected, tolerance) {
return {
pass: !imagediff.equal(actual, expected, tolerance),
message: 'Expected not to be equal.'
};
}
};
}
};

View File

@@ -1,244 +0,0 @@
// Generated by CoffeeScript 1.3.3
/*
jasmine-stealth 0.0.12
Makes Jasmine spies a bit more robust
site: https://github.com/searls/jasmine-stealth
*/
(function() {
var Captor, fake, root, unfakes, whatToDoWhenTheSpyGetsCalled, _,
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
root = this;
_ = function(obj) {
return {
each: function(iterator) {
var item, _i, _len, _results;
_results = [];
for (_i = 0, _len = obj.length; _i < _len; _i++) {
item = obj[_i];
_results.push(iterator(item));
}
return _results;
},
isFunction: function() {
return Object.prototype.toString.call(obj) === "[object Function]";
},
isString: function() {
return Object.prototype.toString.call(obj) === "[object String]";
}
};
};
root.spyOnConstructor = function(owner, classToFake, methodsToSpy) {
var fakeClass, spies;
if (methodsToSpy == null) {
methodsToSpy = [];
}
if (_(methodsToSpy).isString()) {
methodsToSpy = [methodsToSpy];
}
spies = {
constructor: jasmine.createSpy("" + classToFake + "'s constructor")
};
fakeClass = (function() {
function _Class() {
spies.constructor.apply(this, arguments);
}
return _Class;
})();
_(methodsToSpy).each(function(methodName) {
spies[methodName] = jasmine.createSpy("" + classToFake + "#" + methodName);
return fakeClass.prototype[methodName] = function() {
return spies[methodName].apply(this, arguments);
};
});
fake(owner, classToFake, fakeClass);
return spies;
};
unfakes = [];
afterEach(function() {
_(unfakes).each(function(u) {
return u();
});
return unfakes = [];
});
fake = function(owner, thingToFake, newThing) {
var originalThing;
originalThing = owner[thingToFake];
owner[thingToFake] = newThing;
return unfakes.push(function() {
return owner[thingToFake] = originalThing;
});
};
root.stubFor = root.spyOn;
jasmine.createStub = jasmine.createSpy;
jasmine.createStubObj = function(baseName, stubbings) {
var name, obj, stubbing;
if (stubbings.constructor === Array) {
return jasmine.createSpyObj(baseName, stubbings);
} else {
obj = {};
for (name in stubbings) {
stubbing = stubbings[name];
obj[name] = jasmine.createSpy(baseName + "." + name);
if (_(stubbing).isFunction()) {
obj[name].andCallFake(stubbing);
} else {
obj[name].andReturn(stubbing);
}
}
return obj;
}
};
whatToDoWhenTheSpyGetsCalled = function(spy) {
var matchesStub, priorStubbing;
matchesStub = function(stubbing, args, context) {
switch (stubbing.type) {
case "args":
return jasmine.getEnv().equals_(stubbing.ifThis, jasmine.util.argsToArray(args));
case "context":
return jasmine.getEnv().equals_(stubbing.ifThis, context);
}
};
priorStubbing = spy.plan();
return spy.andCallFake(function() {
var i, stubbing;
i = 0;
while (i < spy._stealth_stubbings.length) {
stubbing = spy._stealth_stubbings[i];
if (matchesStub(stubbing, arguments, this)) {
if (Object.prototype.toString.call(stubbing.thenThat) === "[object Function]") {
return stubbing.thenThat();
} else {
return stubbing.thenThat;
}
}
i++;
}
return priorStubbing;
});
};
jasmine.Spy.prototype.whenContext = function(context) {
var addStubbing, spy;
spy = this;
spy._stealth_stubbings || (spy._stealth_stubbings = []);
whatToDoWhenTheSpyGetsCalled(spy);
addStubbing = function(thenThat) {
spy._stealth_stubbings.push({
type: 'context',
ifThis: context,
thenThat: thenThat
});
return spy;
};
return {
thenReturn: addStubbing,
thenCallFake: addStubbing
};
};
jasmine.Spy.prototype.when = function() {
var addStubbing, ifThis, spy;
spy = this;
ifThis = jasmine.util.argsToArray(arguments);
spy._stealth_stubbings || (spy._stealth_stubbings = []);
whatToDoWhenTheSpyGetsCalled(spy);
addStubbing = function(thenThat) {
spy._stealth_stubbings.push({
type: 'args',
ifThis: ifThis,
thenThat: thenThat
});
return spy;
};
return {
thenReturn: addStubbing,
thenCallFake: addStubbing
};
};
jasmine.Spy.prototype.mostRecentCallThat = function(callThat, context) {
var i;
i = this.calls.length - 1;
while (i >= 0) {
if (callThat.call(context || this, this.calls[i]) === true) {
return this.calls[i];
}
i--;
}
};
jasmine.Matchers.ArgThat = (function(_super) {
__extends(ArgThat, _super);
function ArgThat(matcher) {
this.matcher = matcher;
}
ArgThat.prototype.jasmineMatches = function(actual) {
return this.matcher(actual);
};
return ArgThat;
})(jasmine.Matchers.Any);
jasmine.Matchers.ArgThat.prototype.matches = jasmine.Matchers.ArgThat.prototype.jasmineMatches;
jasmine.argThat = function(expected) {
return new jasmine.Matchers.ArgThat(expected);
};
jasmine.Matchers.Capture = (function(_super) {
__extends(Capture, _super);
function Capture(captor) {
this.captor = captor;
}
Capture.prototype.jasmineMatches = function(actual) {
this.captor.value = actual;
return true;
};
return Capture;
})(jasmine.Matchers.Any);
jasmine.Matchers.Capture.prototype.matches = jasmine.Matchers.Capture.prototype.jasmineMatches;
Captor = (function() {
function Captor() {}
Captor.prototype.capture = function() {
return new jasmine.Matchers.Capture(this);
};
return Captor;
})();
jasmine.captor = function() {
return new Captor();
};
}).call(this);

View File

@@ -1,51 +0,0 @@
// Jasmine.Async, v0.1.0
// Copyright (c)2012 Muted Solutions, LLC. All Rights Reserved.
// Distributed under MIT license
// http://github.com/derickbailey/jasmine.async
this.AsyncSpec = (function(global){
// Private Methods
// ---------------
function runAsync(block){
return function(){
var done = false;
var complete = function(){ done = true; };
runs(function(){
block(complete);
});
waitsFor(function(){
return done;
});
};
}
// Constructor Function
// --------------------
function AsyncSpec(spec){
this.spec = spec;
}
// Public API
// ----------
AsyncSpec.prototype.beforeEach = function(block){
this.spec.beforeEach(runAsync(block));
};
AsyncSpec.prototype.afterEach = function(block){
this.spec.afterEach(runAsync(block));
};
AsyncSpec.prototype.it = function(description, block){
// For some reason, `it` is not attached to the current
// test suite, so it has to be called from the global
// context.
global.it(description, runAsync(block));
};
return AsyncSpec;
})(this);

View File

@@ -1,199 +1,752 @@
/*
Jasmine-Ajax : a set of helpers for testing AJAX requests under the Jasmine
BDD framework for JavaScript.
Supports jQuery.
Jasmine-Ajax - v3.2.0: a set of helpers for testing AJAX requests under the Jasmine
BDD framework for JavaScript.
http://github.com/pivotal/jasmine-ajax
http://github.com/jasmine/jasmine-ajax
Jasmine Home page: http://pivotal.github.com/jasmine
Jasmine Home page: http://jasmine.github.io/
Copyright (c) 2008-2013 Pivotal Labs
Copyright (c) 2008-2015 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
*/
// Jasmine-Ajax interface
var ajaxRequests = [];
//Module wrapper to support both browser and CommonJS environment
(function (root, factory) {
if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
// CommonJS
var jasmineRequire = require('jasmine-core');
module.exports = factory(root, function() {
return jasmineRequire;
});
} else {
// Browser globals
window.MockAjax = factory(root, getJasmineRequireObj);
}
}(typeof window !== 'undefined' ? window : global, function (global, getJasmineRequireObj) {
function mostRecentAjaxRequest() {
if (ajaxRequests.length > 0) {
return ajaxRequests[ajaxRequests.length - 1];
} else {
return null;
//
getJasmineRequireObj().ajax = function(jRequire) {
var $ajax = {};
$ajax.RequestStub = jRequire.AjaxRequestStub();
$ajax.RequestTracker = jRequire.AjaxRequestTracker();
$ajax.StubTracker = jRequire.AjaxStubTracker();
$ajax.ParamParser = jRequire.AjaxParamParser();
$ajax.event = jRequire.AjaxEvent();
$ajax.eventBus = jRequire.AjaxEventBus($ajax.event);
$ajax.fakeRequest = jRequire.AjaxFakeRequest($ajax.eventBus);
$ajax.MockAjax = jRequire.MockAjax($ajax);
return $ajax.MockAjax;
};
getJasmineRequireObj().AjaxEvent = function() {
function now() {
return new Date().getTime();
}
}
function clearAjaxRequests() {
ajaxRequests = [];
}
function noop() {
}
// Fake XHR for mocking Ajax Requests & Responses
function FakeXMLHttpRequest() {
var extend = Object.extend || jQuery.extend;
extend(this, {
requestHeaders: {},
// Event object
// https://dom.spec.whatwg.org/#concept-event
function XMLHttpRequestEvent(xhr, type) {
this.type = type;
this.bubbles = false;
this.cancelable = false;
this.timeStamp = now();
open: function() {
this.method = arguments[0];
this.url = arguments[1];
this.username = arguments[3];
this.password = arguments[4];
this.readyState = 1;
this.isTrusted = false;
this.defaultPrevented = false;
// Event phase should be "AT_TARGET"
// https://dom.spec.whatwg.org/#dom-event-at_target
this.eventPhase = 2;
this.target = xhr;
this.currentTarget = xhr;
}
XMLHttpRequestEvent.prototype.preventDefault = noop;
XMLHttpRequestEvent.prototype.stopPropagation = noop;
XMLHttpRequestEvent.prototype.stopImmediatePropagation = noop;
function XMLHttpRequestProgressEvent() {
XMLHttpRequestEvent.apply(this, arguments);
this.lengthComputable = false;
this.loaded = 0;
this.total = 0;
}
// Extend prototype
XMLHttpRequestProgressEvent.prototype = XMLHttpRequestEvent.prototype;
return {
event: function(xhr, type) {
return new XMLHttpRequestEvent(xhr, type);
},
setRequestHeader: function(header, value) {
this.requestHeaders[header] = value;
},
progressEvent: function(xhr, type) {
return new XMLHttpRequestProgressEvent(xhr, type);
}
};
};
getJasmineRequireObj().AjaxEventBus = function(eventFactory) {
function EventBus(source) {
this.eventList = {};
this.source = source;
}
abort: function() {
this.readyState = 0;
},
function ensureEvent(eventList, name) {
eventList[name] = eventList[name] || [];
return eventList[name];
}
readyState: 0,
function findIndex(list, thing) {
if (list.indexOf) {
return list.indexOf(thing);
}
onload: function() {
},
onreadystatechange: function(isTimeout) {
},
status: null,
send: function(data) {
this.params = data;
this.readyState = 2;
},
data: function() {
var data = {};
if (typeof this.params !== 'string') return data;
var params = this.params.split('&');
for (var i = 0; i < params.length; ++i) {
var kv = params[i].replace(/\+/g, ' ').split('=');
var key = decodeURIComponent(kv[0]);
data[key] = data[key] || [];
data[key].push(decodeURIComponent(kv[1]));
data[key].sort();
for(var i = 0; i < list.length; i++) {
if (thing === list[i]) {
return i;
}
return data;
},
}
getResponseHeader: function(name) {
return this.responseHeaders[name];
},
return -1;
}
getAllResponseHeaders: function() {
var responseHeaders = [];
for (var i in this.responseHeaders) {
if (this.responseHeaders.hasOwnProperty(i)) {
responseHeaders.push(i + ': ' + this.responseHeaders[i]);
EventBus.prototype.addEventListener = function(event, callback) {
ensureEvent(this.eventList, event).push(callback);
};
EventBus.prototype.removeEventListener = function(event, callback) {
var index = findIndex(this.eventList[event], callback);
if (index >= 0) {
this.eventList[event].splice(index, 1);
}
};
EventBus.prototype.trigger = function(event) {
var evt;
// Event 'readystatechange' is should be a simple event.
// Others are progress event.
// https://xhr.spec.whatwg.org/#events
if (event === 'readystatechange') {
evt = eventFactory.event(this.source, event);
} else {
evt = eventFactory.progressEvent(this.source, event);
}
var eventListeners = this.eventList[event];
if (eventListeners) {
for (var i = 0; i < eventListeners.length; i++) {
eventListeners[i].call(this.source, evt);
}
}
};
return function(source) {
return new EventBus(source);
};
};
getJasmineRequireObj().AjaxFakeRequest = function(eventBusFactory) {
function extend(destination, source, propertiesToSkip) {
propertiesToSkip = propertiesToSkip || [];
for (var property in source) {
if (!arrayContains(propertiesToSkip, property)) {
destination[property] = source[property];
}
}
return destination;
}
function arrayContains(arr, item) {
for (var i = 0; i < arr.length; i++) {
if (arr[i] === item) {
return true;
}
}
return false;
}
function wrapProgressEvent(xhr, eventName) {
return function() {
if (xhr[eventName]) {
xhr[eventName].apply(xhr, arguments);
}
};
}
function initializeEvents(xhr) {
xhr.eventBus.addEventListener('readystatechange', wrapProgressEvent(xhr, 'onreadystatechange'));
xhr.eventBus.addEventListener('loadstart', wrapProgressEvent(xhr, 'onloadstart'));
xhr.eventBus.addEventListener('load', wrapProgressEvent(xhr, 'onload'));
xhr.eventBus.addEventListener('loadend', wrapProgressEvent(xhr, 'onloadend'));
xhr.eventBus.addEventListener('progress', wrapProgressEvent(xhr, 'onprogress'));
xhr.eventBus.addEventListener('error', wrapProgressEvent(xhr, 'onerror'));
xhr.eventBus.addEventListener('abort', wrapProgressEvent(xhr, 'onabort'));
xhr.eventBus.addEventListener('timeout', wrapProgressEvent(xhr, 'ontimeout'));
}
function unconvertibleResponseTypeMessage(type) {
var msg = [
"Can't build XHR.response for XHR.responseType of '",
type,
"'.",
"XHR.response must be explicitly stubbed"
];
return msg.join(' ');
}
function fakeRequest(global, requestTracker, stubTracker, paramParser) {
function FakeXMLHttpRequest() {
requestTracker.track(this);
this.eventBus = eventBusFactory(this);
initializeEvents(this);
this.requestHeaders = {};
this.overriddenMimeType = null;
}
function findHeader(name, headers) {
name = name.toLowerCase();
for (var header in headers) {
if (header.toLowerCase() === name) {
return headers[header];
}
}
return responseHeaders.join('\r\n');
},
responseText: null,
response: function(response) {
this.status = response.status;
this.responseText = response.responseText || "";
this.readyState = 4;
this.responseHeaders = response.responseHeaders ||
{"Content-type": response.contentType || "application/json" };
// uncomment for jquery 1.3.x support
// jasmine.Clock.tick(20);
this.onload();
this.onreadystatechange();
},
responseTimeout: function() {
this.readyState = 4;
jasmine.Clock.tick(jQuery.ajaxSettings.timeout || 30000);
this.onreadystatechange('timeout');
}
});
return this;
}
function normalizeHeaders(rawHeaders, contentType) {
var headers = [];
if (rawHeaders) {
if (rawHeaders instanceof Array) {
headers = rawHeaders;
} else {
for (var headerName in rawHeaders) {
if (rawHeaders.hasOwnProperty(headerName)) {
headers.push({ name: headerName, value: rawHeaders[headerName] });
}
}
}
} else {
headers.push({ name: "Content-Type", value: contentType || "application/json" });
}
jasmine.Ajax = {
isInstalled: function() {
return jasmine.Ajax.installed === true;
},
assertInstalled: function() {
if (!jasmine.Ajax.isInstalled()) {
throw new Error("Mock ajax is not installed, use jasmine.Ajax.useMock()");
return headers;
}
},
useMock: function() {
if (!jasmine.Ajax.isInstalled()) {
var spec = jasmine.getEnv().currentSpec;
spec.after(jasmine.Ajax.uninstallMock);
jasmine.Ajax.installMock();
function parseXml(xmlText, contentType) {
if (global.DOMParser) {
return (new global.DOMParser()).parseFromString(xmlText, 'text/xml');
} else {
var xml = new global.ActiveXObject("Microsoft.XMLDOM");
xml.async = "false";
xml.loadXML(xmlText);
return xml;
}
}
},
installMock: function() {
if (typeof jQuery != 'undefined') {
jasmine.Ajax.installJquery();
} else {
throw new Error("jasmine.Ajax currently only supports jQuery");
var xmlParsables = ['text/xml', 'application/xml'];
function getResponseXml(responseText, contentType) {
if (arrayContains(xmlParsables, contentType.toLowerCase())) {
return parseXml(responseText, contentType);
} else if (contentType.match(/\+xml$/)) {
return parseXml(responseText, 'text/xml');
}
return null;
}
jasmine.Ajax.installed = true;
},
installJquery: function() {
jasmine.Ajax.mode = 'jQuery';
jasmine.Ajax.real = jQuery.ajaxSettings.xhr;
jQuery.ajaxSettings.xhr = jasmine.Ajax.jQueryMock;
var iePropertiesThatCannotBeCopied = ['responseBody', 'responseText', 'responseXML', 'status', 'statusText', 'responseTimeout', 'responseURL'];
extend(FakeXMLHttpRequest.prototype, new global.XMLHttpRequest(), iePropertiesThatCannotBeCopied);
extend(FakeXMLHttpRequest.prototype, {
open: function() {
this.method = arguments[0];
this.url = arguments[1];
this.username = arguments[3];
this.password = arguments[4];
this.readyState = 1;
this.requestHeaders = {};
this.eventBus.trigger('readystatechange');
},
},
setRequestHeader: function(header, value) {
if(this.requestHeaders.hasOwnProperty(header)) {
this.requestHeaders[header] = [this.requestHeaders[header], value].join(', ');
} else {
this.requestHeaders[header] = value;
}
},
uninstallMock: function() {
jasmine.Ajax.assertInstalled();
if (jasmine.Ajax.mode == 'jQuery') {
jQuery.ajaxSettings.xhr = jasmine.Ajax.real;
}
jasmine.Ajax.reset();
},
overrideMimeType: function(mime) {
this.overriddenMimeType = mime;
},
reset: function() {
jasmine.Ajax.installed = false;
jasmine.Ajax.mode = null;
jasmine.Ajax.real = null;
},
abort: function() {
this.readyState = 0;
this.status = 0;
this.statusText = "abort";
this.eventBus.trigger('readystatechange');
this.eventBus.trigger('progress');
this.eventBus.trigger('abort');
this.eventBus.trigger('loadend');
},
jQueryMock: function() {
var newXhr = new FakeXMLHttpRequest();
ajaxRequests.push(newXhr);
return newXhr;
},
readyState: 0,
installed: false,
mode: null
onloadstart: null,
onprogress: null,
onabort: null,
onerror: null,
onload: null,
ontimeout: null,
onloadend: null,
onreadystatechange: null,
addEventListener: function() {
this.eventBus.addEventListener.apply(this.eventBus, arguments);
},
removeEventListener: function(event, callback) {
this.eventBus.removeEventListener.apply(this.eventBus, arguments);
},
status: null,
send: function(data) {
this.params = data;
this.eventBus.trigger('loadstart');
var stub = stubTracker.findStub(this.url, data, this.method);
if (stub) {
if (stub.isReturn()) {
this.respondWith(stub);
} else if (stub.isError()) {
this.responseError();
} else if (stub.isTimeout()) {
this.responseTimeout();
}
}
},
contentType: function() {
return findHeader('content-type', this.requestHeaders);
},
data: function() {
if (!this.params) {
return {};
}
return paramParser.findParser(this).parse(this.params);
},
getResponseHeader: function(name) {
name = name.toLowerCase();
var resultHeader;
for(var i = 0; i < this.responseHeaders.length; i++) {
var header = this.responseHeaders[i];
if (name === header.name.toLowerCase()) {
if (resultHeader) {
resultHeader = [resultHeader, header.value].join(', ');
} else {
resultHeader = header.value;
}
}
}
return resultHeader;
},
getAllResponseHeaders: function() {
var responseHeaders = [];
for (var i = 0; i < this.responseHeaders.length; i++) {
responseHeaders.push(this.responseHeaders[i].name + ': ' +
this.responseHeaders[i].value);
}
return responseHeaders.join('\r\n') + '\r\n';
},
responseText: null,
response: null,
responseType: null,
responseURL: null,
responseValue: function() {
switch(this.responseType) {
case null:
case "":
case "text":
return this.readyState >= 3 ? this.responseText : "";
case "json":
return JSON.parse(this.responseText);
case "arraybuffer":
throw unconvertibleResponseTypeMessage('arraybuffer');
case "blob":
throw unconvertibleResponseTypeMessage('blob');
case "document":
return this.responseXML;
}
},
respondWith: function(response) {
if (this.readyState === 4) {
throw new Error("FakeXMLHttpRequest already completed");
}
this.status = response.status;
this.statusText = response.statusText || "";
this.responseHeaders = normalizeHeaders(response.responseHeaders, response.contentType);
this.readyState = 2;
this.eventBus.trigger('readystatechange');
this.responseText = response.responseText || "";
this.responseType = response.responseType || "";
this.responseURL = response.responseURL || null;
this.readyState = 4;
this.responseXML = getResponseXml(response.responseText, this.getResponseHeader('content-type') || '');
if (this.responseXML) {
this.responseType = 'document';
}
if ('response' in response) {
this.response = response.response;
} else {
this.response = this.responseValue();
}
this.eventBus.trigger('readystatechange');
this.eventBus.trigger('progress');
this.eventBus.trigger('load');
this.eventBus.trigger('loadend');
},
responseTimeout: function() {
if (this.readyState === 4) {
throw new Error("FakeXMLHttpRequest already completed");
}
this.readyState = 4;
jasmine.clock().tick(30000);
this.eventBus.trigger('readystatechange');
this.eventBus.trigger('progress');
this.eventBus.trigger('timeout');
this.eventBus.trigger('loadend');
},
responseError: function() {
if (this.readyState === 4) {
throw new Error("FakeXMLHttpRequest already completed");
}
this.readyState = 4;
this.eventBus.trigger('readystatechange');
this.eventBus.trigger('progress');
this.eventBus.trigger('error');
this.eventBus.trigger('loadend');
}
});
return FakeXMLHttpRequest;
}
return fakeRequest;
};
getJasmineRequireObj().MockAjax = function($ajax) {
function MockAjax(global) {
var requestTracker = new $ajax.RequestTracker(),
stubTracker = new $ajax.StubTracker(),
paramParser = new $ajax.ParamParser(),
realAjaxFunction = global.XMLHttpRequest,
mockAjaxFunction = $ajax.fakeRequest(global, requestTracker, stubTracker, paramParser);
this.install = function() {
if (global.XMLHttpRequest === mockAjaxFunction) {
throw "MockAjax is already installed.";
}
global.XMLHttpRequest = mockAjaxFunction;
};
this.uninstall = function() {
if (global.XMLHttpRequest !== mockAjaxFunction) {
throw "MockAjax not installed.";
}
global.XMLHttpRequest = realAjaxFunction;
this.stubs.reset();
this.requests.reset();
paramParser.reset();
};
this.stubRequest = function(url, data, method) {
var stub = new $ajax.RequestStub(url, data, method);
stubTracker.addStub(stub);
return stub;
};
this.withMock = function(closure) {
this.install();
try {
closure();
} finally {
this.uninstall();
}
};
this.addCustomParamParser = function(parser) {
paramParser.add(parser);
};
this.requests = requestTracker;
this.stubs = stubTracker;
}
return MockAjax;
};
getJasmineRequireObj().AjaxParamParser = function() {
function ParamParser() {
var defaults = [
{
test: function(xhr) {
return (/^application\/json/).test(xhr.contentType());
},
parse: function jsonParser(paramString) {
return JSON.parse(paramString);
}
},
{
test: function(xhr) {
return true;
},
parse: function naiveParser(paramString) {
var data = {};
var params = paramString.split('&');
for (var i = 0; i < params.length; ++i) {
var kv = params[i].replace(/\+/g, ' ').split('=');
var key = decodeURIComponent(kv[0]);
data[key] = data[key] || [];
data[key].push(decodeURIComponent(kv[1]));
}
return data;
}
}
];
var paramParsers = [];
this.add = function(parser) {
paramParsers.unshift(parser);
};
this.findParser = function(xhr) {
for(var i in paramParsers) {
var parser = paramParsers[i];
if (parser.test(xhr)) {
return parser;
}
}
};
this.reset = function() {
paramParsers = [];
for(var i in defaults) {
paramParsers.push(defaults[i]);
}
};
this.reset();
}
return ParamParser;
};
getJasmineRequireObj().AjaxRequestStub = function() {
var RETURN = 0,
ERROR = 1,
TIMEOUT = 2;
function RequestStub(url, stubData, method) {
var normalizeQuery = function(query) {
return query ? query.split('&').sort().join('&') : undefined;
};
if (url instanceof RegExp) {
this.url = url;
this.query = undefined;
} else {
var split = url.split('?');
this.url = split[0];
this.query = split.length > 1 ? normalizeQuery(split[1]) : undefined;
}
this.data = (stubData instanceof RegExp) ? stubData : normalizeQuery(stubData);
this.method = method;
this.andReturn = function(options) {
this.action = RETURN;
this.status = options.status || 200;
this.contentType = options.contentType;
this.response = options.response;
this.responseText = options.responseText;
this.responseHeaders = options.responseHeaders;
this.responseURL = options.responseURL;
};
this.isReturn = function() {
return this.action === RETURN;
};
this.andError = function() {
this.action = ERROR;
};
this.isError = function() {
return this.action === ERROR;
};
this.andTimeout = function() {
this.action = TIMEOUT;
};
this.isTimeout = function() {
return this.action === TIMEOUT;
};
this.matches = function(fullUrl, data, method) {
var urlMatches = false;
fullUrl = fullUrl.toString();
if (this.url instanceof RegExp) {
urlMatches = this.url.test(fullUrl);
} else {
var urlSplit = fullUrl.split('?'),
url = urlSplit[0],
query = urlSplit[1];
urlMatches = this.url === url && this.query === normalizeQuery(query);
}
var dataMatches = false;
if (this.data instanceof RegExp) {
dataMatches = this.data.test(data);
} else {
dataMatches = !this.data || this.data === normalizeQuery(data);
}
return urlMatches && dataMatches && (!this.method || this.method === method);
};
}
return RequestStub;
};
getJasmineRequireObj().AjaxRequestTracker = function() {
function RequestTracker() {
var requests = [];
this.track = function(request) {
requests.push(request);
};
this.first = function() {
return requests[0];
};
this.count = function() {
return requests.length;
};
this.reset = function() {
requests = [];
};
this.mostRecent = function() {
return requests[requests.length - 1];
};
this.at = function(index) {
return requests[index];
};
this.filter = function(url_to_match) {
var matching_requests = [];
for (var i = 0; i < requests.length; i++) {
if (url_to_match instanceof RegExp &&
url_to_match.test(requests[i].url)) {
matching_requests.push(requests[i]);
} else if (url_to_match instanceof Function &&
url_to_match(requests[i])) {
matching_requests.push(requests[i]);
} else {
if (requests[i].url === url_to_match) {
matching_requests.push(requests[i]);
}
}
}
return matching_requests;
};
}
return RequestTracker;
};
getJasmineRequireObj().AjaxStubTracker = function() {
function StubTracker() {
var stubs = [];
this.addStub = function(stub) {
stubs.push(stub);
};
this.reset = function() {
stubs = [];
};
this.findStub = function(url, data, method) {
for (var i = stubs.length - 1; i >= 0; i--) {
var stub = stubs[i];
if (stub.matches(url, data, method)) {
return stub;
}
}
};
}
return StubTracker;
};
var jRequire = getJasmineRequireObj();
var MockAjax = jRequire.ajax(jRequire);
jasmine.Ajax = new MockAjax(global);
return MockAjax;
}));