Jasmine 2.0 how to handle ajax requests - ajax

I want to test a function, that includes a ajax request. The test should wait for the ajax-request to success/fail. Running the test doesn't work, because it doesn't wait right now.
this I want to be tested:
this.requestServerBoxId = function()
{
//ajax-request
$.ajax({
url: this.host_addr+"/?getid="+this.name,
type: 'POST',
data: {_data:this._data},
success: function(data) {
return IdSuccess(data);
},
error: function(data){
return false;
}
});
}
function IdSuccess(data){
if(typeof data != undefined)
return true;
return false;
}
This is my test:
it("should call a function after ajax-success", function(){
expect(this.Process.requestServerBoxId()).toBe(true);
});
I tried out spies, but I guess I'm using them wrong:
spyOn($, 'ajax' ).and.returnValue(123);
I was hoping that this spy return 123 every time a ajax request is beeing made. But it doesn't work.

With Jasmine 2.0, there is a completely new API for testing ajax (and just about everything else).
Instead of:
spyOn($, 'ajax').and.returnValue(123);
Setup the beforeEach and afterEach methods before your it test:
beforeEach(function() {
jasmine.Ajax.install();
jasmine.Ajax.stubRequest('YOUR_URL_HERE').andReturn({
responseText: 'YOUR_RAW_STUBBED_DATA_HERE'
});
});
afterEach(function() {
jasmine.Ajax.uninstall();
});
it('My Ajax Test', function() {
// . . . code that makes an ajax request . . .
});
The it test will then execute its ajax request as expected.
Note that this makes your ajax call essentially synchronous but immediate.

You might want to look at something like https://github.com/pivotal/jasmine-ajax. This will let you stub out the ajax request and specify what you want it to return, behaving more like a normal ajax request.
The problem with spyOn in this instance is that the jQuery $.ajax method actually returns something more like a promise which you then have to send the correct result to, so a simple .and.returnValue won't do the right thing.

Related

First ajax call on Phonegap + Zepto returns undefined even after device ready

I'm doing a jsonp call in my mobile app at startup to connect to my server. I'm using Phonegap 2.1 and Zepto 1.0-rc1. At the bottom of my html page, I do the init stuff on DOM ready.
<script type="text/javascript">
if (!$) {$ = Zepto};
$(init);
document.addEventListener('deviceready', Perksea.deviceReady);
</script>
...
function init() {
var router = new Backbone.Router();
...
}
function deviceReady() {
isConnected();
isConnected();
}
function isConnected() {
$.ajaxJSONP({
url: 'http://localhost/isconnected',
success: function(response) {
console.log('response is ' + response);
}
});
}
The first JSONP call will print "response is undefined" but the second JSONP call works. I've even tried putting the JSONP call in a setTimeout(isConnected, 5000) with the same result. Have already checked that the url is correct etc.
Has anyone seen something like this?
Thanks
Steve
since you are getting into the "success" callback function on the first call (where response is undefined), are you sure that your server is properly responding to the first call? Sounds like it is returning a 200 response, but with no data for that first call.
You can try adding an 'error' callback to see if that provides anything useful as well
$.ajaxJSONP({
url: 'http://localhost/isconnected',
success: function(response) {
console.log('response is ' + response);
}
error: function(response) {
console.log('error is ' + response);
}
});
Finally, because AJAX is Asynchronous, your 2 calls to isConnected() are going to fire one immediately after the other, not waiting for the first to respond. I'm curious what it looks like on the server side (see above).

Ajax request error when changepage

guys. I have a juerymobile multi-page, and I have a button in #page-index, when click it, will send a ajax request to server, and changepage to #page-column, It run will in PC, but when i deploy the multi-page in phonegap, the button click can just run only twice, code is below:
function test()
{
$.mobile.changePage('#page_column');
$.ajax({
url: "http://192.168.168.120:8090/fcmobile/getTest",
dataType: "json"
}).done(function(data) {
alert(data.content);
});
}
I found if I remove $.mobile.changePage('#page_column');, the ajax request can be run well any times. but when I add the changePage code, it only can be run twice, in third time, ajax request can't even be send. Dose anybody know reason?
AJAX is made to be asynchronous, so no need to set async to false to get it working. Use events instead.
For example:
function test () {
$.ajax({
'url': "http://192.168.168.120:8090/fcmobile/getTest",
'dataType': 'json',
'success': function (json_data) {
$(document).trigger('test_json_data_loaded');
console.log(data);
}
});
}
$(document).on('test_json_data_loaded', function () {
$.mobile.changePage('#page_column');
});
When you set async to false, you're basically making it so that every time this AJAX request is made, the user will have to wait until all the data is fully loaded before the application/website can do/execute anything else...not good.

How to create stub for ajax function using Jasmine BDD

I'm struggling to find any examples on how to fake an ajax call using Jasmine BDD?
I have a custom ajax function that works like so...
ajax({
url: 'JSON.php',
dataType: 'json',
onSuccess: function(resp) {
console.log(resp);
}
});
...and I've no idea how to create a stub to fake calling the actual ajax function.
I want to avoid calling the ajax function as it could slow down my test suite if a real ajax call to the server takes some time to respond and I've loads of specs in my test suite.
I've heard that you can use spyOn(namespace, 'ajax') but that is annoying straight away as it requires me to wrap my ajax function in an object just to use the spyOn function (but regardless I wasn't able to follow along as I couldn't find any specific examples to fake an ajax call).
I've also heard that you can use createSpy() but again the documentation isn't very helpful (neither is the corresponding wiki on GitHub).
Any help explaining how to use spies to create a fake ajax call would be greatly appreciated!
You can use SinonJS mocking framework, which has a build in fake server. You can easily use it with jasmine:
beforeEach(function() {
server = sinon.fakeServer.create();
server.respondWith([200, { "Content-Type": "text/html", "Content-Length": 2 }, "OK"])
});
Btw. if your ajax function is in the global namespace why not call spyOn(window, 'ajax')
Regarding a single function, you may use 'createSpy':
/*var */ajax = createSpy('foo');
var is absent because you want to redefine it, but it is then required that the block where you define this spy is bound to the same scope where real ajax was defined. Or, if you confused with that, use spyOn(window, foo), because you are anyway testing it in the browser.
See this answer for details.
Regarding the ajax call, see Asynchronous Support section in new docs, or better use Clock:
window.ajax = function() {};
var response;
var ajaxSpy = spyOn(window, 'ajax').andCallFake(function(url, callback) {
setTimeout(function() { callback({ 'foo': 'bar' }); }, 1000);
});
jasmine.Clock.useMock();
function callback(resp) { response = resp; }
ajax('fake.url', callback);
jasmine.Clock.tick(1500);
expect(ajaxSpy).toHaveBeenCalled();
expect(response).toBeDefined();
expect(response.foo).toBeDefined();
If you're ok with not using spies, but instead the add-on jasmine-ajax. To mock for a single spec use withMock:
it("allows use in a single spec", function() {
var onSuccess = jasmine.createSpy('success');
jasmine.Ajax.withMock(function() {
ajax({
url: 'JSON.php',
dataType: 'json',
onSuccess: onSuccess
});
expect(onSuccess).not.toHaveBeenCalled();
jasmine.Ajax.requests.mostRecent().respondWith({
"status": 200,
"responseText": '{"some": "json"}'
});
expect(onSuccess).toHaveBeenCalledWith('{"some": "json"}');
});
});
The response is only sent when you use respondWith. The link above has some directions how to install

jQuery - AJAX request using both native XHR and flXHR (Flash) fallback - how to minimize code duplication?

I need to retrieve data via cross-domain XMLHttpRequest. To make this work in (almost) all browsers, I use native XHR first and, if that fails, flXHR.
The (working) code I currently have for this is as follows:
jQuery.support.cors = true; // must set this for IE to work
$.ajax({
url: 'http://site.com/dataToGet',
transport : 'xhr',
success: function(data) {
console.log('Got data via XHR');
doStuff(data);
},
error: function(xhr, textStatus, error) {
console.log('Error in xhr:', error.message);
console.log('Trying flXHR...');
$.ajax({
url: 'http://site.com/dataToGet',
transport : 'flXHRproxy',
success: function (data) {
console.log('Got data via flXHR');
doStuff(data);
},
error: function (xhr, textStatus, error) {
console.log('Error in flXHR:', error.message);
console.log('Both methods failed, data not retrieved.');
}
});
}
});
This feels like a lot of code duplication to me, especially in the success handlers. Is there a more efficient way to do this? I'd really prefer to make one $.ajax call that would try both transports in turn, instead of having to use the error handler to make the call a second time. It's not too bad in this example, but rapidly gets more complicated if the success handler is longer or if the success handler has to itself issue another $.ajax call.
I've created a jquery-specific and slimmed-down fork of flxhr that simplifies your code sample above. You can see an example of usage in the "Usage" section in the README.
https://github.com/b9chris/flxhr-jquery-packed
In particular, you don't want to waste time waiting for a standard CORS request to fail. It's easy to determine whether flxhr is necessary by testing $.support.cors upfront (no need to override it). Then just use flxhr explicitly where necessary.
Why don't you just wrap this in a function by itself? That's after all, how you end up reusing code. You can even pass functions as arguments to make sure that you don't have to repeat this code more than once.
To me this is pretty straight forward but maybe I've misunderstood.
function xhr(success) {
$.ajax({
success: success,
error: function() {
$.ajax({ success: success })
}
});
}
Then just pass the success handler once
xhr(function(data){/*magic*/});
Or if you wanna basically avoid redundant configuration of the ajax call use the first object as a template, like this:
function xhr(success) {
var ajaxParams = { success: success };
ajaxParams.error = function() {
$.ajax($.extend(ajaxParams, { transport: 'xhr' }));
}
$.ajax(ajaxParams);
}
I simplified the whole thing a bit, but I hope you get the point.
Edit
Reading that last bit, maybe this will give you some ideas... it's a variation of that last snippet.
function xhr(success) {
var ajaxParams = { success: success };
ajaxParams.error = function() {
var newParams = $.extend(ajaxParams, { transport: 'xhr' });
newParams.success = function() {
// do something
// arguments is a special array, even if no parameters were
// defined in any arguments where passed they will be found
// in the order they were passed in the arguments array
// this makes it possible to forward the call to another
// function
success.apply(this, arguments);
}
$.ajax(newParams);
}
$.ajax(ajaxParams);
}

How can I manipulate an Ajax response before it's injected into the DOM?

Here is what I have so far:
$(function () {
dataValModify('body');
$('body').bind('ajaxSuccess', function (e, xhr, settings) {
dataValModify(xhr.responseText);
});
});
function dataValModify(elem) {
// Code to modify elements within the response.
}
How can I take the Ajax response and modify it before it is injected into the DOM? Previously, I was binding ajaxComplete and modifying the DOM directly after injection, but I would like to modify the response instead. I don't think it makes a lot of sense to find elements in the Ajax response and use them to modify the DOM. I send the xhr.responseText into my function so that I don't reapply the modifications to the rest of the body, which will have already been modified by the time of an Ajax call. Also, is there something better than xhr.responseText to use for this? I couldn't get xhr.responseHTML to work.
EDIT: Right now I'm just using a simple test Ajax call to return an MVC partial view:
$('#ajaxTest').load('<MVC Route>')
If I'm understanding your requirements correctly, they are as follows:
Make an asynchronous HTTP request to get some HTML
Modify the returned HTML using the dataValModify() function
Insert the modified HTML into your element with the ID: 'ajaxTest'
If so then it sounds to me like you need to make a lower level ajax call than what you're using at present i.e. $(elem).load()
Essentially the call to .load() is a wrapper for $.get() followed by a call to $(elem).html(someContent) where "someContent" is the responseText from the HTTP request.
Therefore if you want to modify the response before it's injected into the DOM, then you can do something similar to the following:
$.ajax({
type: "GET",
url: "<MVC Route>",
dataType: "html",
success: function(jqXHR, textStatus, errorThrown){
// Your HTTP call was successful but nothing else has happened with the response yet
// Therefore you can now do whatever you want with the it...
// First modify the HTML using the dataValModify function
// Assumption being that your function returns the modified HTML string
var myModifiedHTML = dataValModify(jqXHR.responseText);
// Inject the modified HTML
$('#ajaxTest').html(myModifiedHTML);
}
});
You can use ajaxComplete to modify the responseHTML itself.
$('body').ajaxComplete(function(e, xhr, settings) {
dataValModify(xhr.responseHTML);
});
Update: I haven't tried it, but it might help:
$.ajaxSetup({
converters: {
"text html": function( textValue ) {
if ( valid( textValue ) ) {
// Some parsing logic here
return dataValModify(textValue );
} else {
// This will notify a parsererror for current request
throw exceptionObject;
}
}
}
});
More info here: http://api.jquery.com/extending-ajax/

Resources