How to write unit test to embeded ajax requests? - qunit

I have the handleDownload method which starts a file download. The function posts to the backend, which gives back a response, based on that a new request posts to the server where the files are. I saw that I can use mockjax to mock the requests, but how to handle the different paths like success, error, etc. How should I know which response triggers which path (success,error, complete, ...). What would be a great startegy to test the handleDownload function, and how? For mocking I use Sinon.js I don't have a really deep understanding yet. I should also check either the handleDownloadFinal function is called.
handleDownload: function(data, url) {
$.ajax({
type: "POST",
url: url,
data: {},
success: function(response) {
if (response.success) {
var start_token = response.token;
$.ajax({
type: start_token.method,
url: start_token.url,
beforeSend: function(xhr) {
xhr.setRequestHeader('Authorization', start_token.header);
},
success: function(start_response) {
handleDownloadFinal(start_response.status_token);
},
error: function(start_response) {
$.ajax({
type: "POST",
url: url + 'proxy/',
success: function(fallback_response) {
if (fallback_response.success) {
handleDownloadFinal(fallback_response.status_token, true, fallback_response.job_uuid);
} else {
errorDownload(response.error);
}
},
error: function(fallback_response) {
// Now this is some real error
generalErrorDownload();
},
dataType: 'json'
});
},
dataType: 'json',
xhrFields: {
withCredentials: true
}
});
} else {
errorDownload(response.error);
}
},
error: function(response) {
generalErrorDownload();
},
complete: function() {
},
dataType: "json"
});
}

You should use the fake server coming with sinon.
before(function(){
//create the server
this.server = sinon.fakeServer.create();
// let the server automatically respond for every request
server.autoRespond = true;
})
it('test something', function(){
//let the server respond for specific url with 200-ok
this.server.respondWith("POST", "/some/article/comments.json", [200, {
"Content-Type": "application/json"
}, '[{ "id": 12, "comment": "Hey there" }]']);
})
As you have a bunch of requests and you have to check all combinations I would suggest to have helper function for every request fail success so you could test the cases like this:
function letFirstRequestSucceed() {
this.server.respondWith("POST", "urlForFirstRequest", [200, {
"Content-Type": "application/json"
}, '[{ "id": 12, "comment": "Hey there" }]']);
}
function letSecondRequestFail() {
this.server.respondWith("POST", "urlForSecondRequest", [404, {
"Content-Type": "application/json"
}, '{error: "some error message"}');
}
function letThirdRequestFail() {
this.server.respondWith("POST", "urlForThirdRequest", [404, {
"Content-Type": "application/json"
}, '{error: "some error message"}');
}
it("should to something when the second and third request fails", function () {
sinon.spy(window, 'generalErrorDownload');
letFirstRequestSucceed();
letSecondRequestFail();
letThirdRequestFail();
handleDownload('someDate', 'aUrl');
expect(generalErrorDownload)
})
Btw you should think about to refactor you code using jquerys deferred which is supported by api ajax calls, this would make your could much more readable.

Related

QUnit testing: how to test the response of an Ajax call within a method?

so I want to test a method called 'getTheValues', which makes an Ajax call. I've mocked the server response with the values but when I run the test I get two failures but no explanation of why they failed. What am I doing wrong here?
test("Test - call Ajax within method.", function () {
var callback = sandbox.spy();
server.respondWith("GET", "/ajaxtest/getmethod",
[200, { "Content-Type": "application/json" },
'[{ "id": 123, "name": "John" }]']);
getTheValues();
server.respond();
ok(callback.calledOnce, "Callback was called once");
ok(callback.calledWith([{ id: 123, name: "John" }]), "Callback with correct values.");
});
function getTheValues(callback) {
// do some stuff including an ajax call:
$.ajax({
type: 'GET',
dataType: 'json',
cache: false,
url: '/ajaxtest/getmethod',
success: function(data) {
} });
}
first the (working) code:
QUnit.module('Test Module', {
before: function () {
this.server = sinon.fakeServer.create();
},
after: function () {
this.server.restore();
delete this.server;
}
});
QUnit.test("Test - call Ajax within method.", function (assert) {
var callback = sinon.spy(jQuery, "ajax");
this.server.respondWith("GET", "/ajaxtest/getmethod", /*?id=123&name=-John*/
[200, { "Content-Type": "application/json" },
'[{ "id": 123, "name": "John" }]']);
var a = getTheValues();
this.server.respond();
assert.ok(callback.calledOnce, "Callback was called once");
var callArgs = callback.args[0][0];
assert.equal(JSON.stringify(callArgs.data), JSON.stringify({ "id": 123, "name": "John" }));
});
function getTheValues() {
// do some stuff including an ajax call:
$.ajax({
data: { id: 123, name: "John" },
method: 'GET',
dataType: 'json',
cache: false,
url: '/ajaxtest/getmethod',
success: function (data) {
}
});
next explanations
this seems to me this is not pure QUnit test. I assume you're using SinonJS as well.
on the spy method you need to point what will be spied
sinon.spy(jQuery, "ajax")
you cannot use callback.calledWith like this for it returns true/false and comparing objects ({}==={}) will (almost) always return false. So this test will constantly fail. Instead you need spy arguments (in your case callback.args[0][0];) and compare particular part with desired result. More about this you could find here
Actually you do not need to extract those but I did it to make code (hopefully) more clear.
So
1st extract arguments,
next strignify objects to be comparable
without any other code and
then use equal method to compare with
expected values
And finally your mock object lacks data object with will be send along with the GET request.
HTH

Extjs 5 ajax PUT and DELETE methods throw 403 errors (csrf token included)

I am building a web application with django-rest-framework and extjs5.
Obviously i faced problems with django's csrf token, which i had to inlude in Extjs's Ajax requests.
But while i implemented POST method successfully, it seems that my implementation doesn't work for PUT and DELETE method.
My POST method code:
onSaveRecordBtnClick: function(){
Job_Name = this.lookupReference('Job_Name').getValue();
var csrf = Ext.util.Cookies.get('csrftoken');
Ext.Ajax.request({
url: '/jobs_api/job/',
method: "POST",
params: {
Job_Name: Job_Name,
'csrfmiddlewaretoken': csrf
},
success: function(conn, response, options, eOpts) {
var result = MyApp.util.Util.decodeJSON(conn.responseText);
if (result.success) {
alert('Job Submission Successfull');
}
else {
MyApp.util.Util.showErrorMsg(conn.responseText);
}
},
failure: function(conn, response, options, eOpts) {
MyApp.util.Util.showErrorMsg(conn.responseText);
}
});
}
This works perfectly, but when i try PUT or DELETE method i keep getting:
Request Method:DELETE
Status Code:403 FORBIDDEN
{"detail":"CSRF Failed: CSRF token missing or incorrect."}
My DELETE method:
onJobDblClick : function(grid, record, index, eOpts) {
var job_id = record.id;
var csrf = Ext.util.Cookies.get('csrftoken');
Ext.Ajax.request({
url: '/jobs_api/job/' + job_id + '/',
method: "DELETE",
params: {
'id': job_id,
'csrfmiddlewaretoken': csrf
},
success: function(conn, response, options, eOpts) {
var result = MyApp.util.Util.decodeJSON(conn.responseText);
if (result.success) {
alert('Job Deleted Successfully');
}
else {
MyApp.util.Util.showErrorMsg(conn.responseText);
}
},
failure: function(conn, response, options, eOpts) {
MyApp.util.Util.showErrorMsg(conn.responseText);
}
});
}
My job model is:
Ext.define('MyApp.model.Job', {
extend: 'MyApp.model.Base',
fields: [
{ name: 'id', type: 'int' },
{ name: 'Job_Name', type: 'string' },
],
proxy: {
type: 'rest',
url: '/jobs_api/job/',
reader: {
type: 'json',
rootProperty: 'data'
}
}
});
I don't know why this is happening. Please help!!

Ajax success: function(data) is undefined

Edit: could've researched better... reading this post now: How do I return the response from an asynchronous call?
I have an ajax request which returns JSON data. When I watch it in fiddler, it does go out to the service and get the JSON data, but when I try to set a variable to it's response, that variable is "undefined". If I alert in the success method, it alerts, but the variable is still undefined.
I tried changing the function(data) to function(something) incase that had anything to do with it... same story.
var returndata
$.ajax({
type: "GET",
url: "GetSecurables/",
data: { etaNumber: etaNumber },
success: function (data) {
returndata = data; //undefined
alert('haaalp');
}
});
The JSON is like below
[
{
"DelegateSid":null,
"DisplayName":"Tom",
"HasDelegation":true,
"HasEtaManagement":false
},
{
"DelegateSid":null,
"DisplayName":"Tim",
"HasDelegation":true,
"HasEtaManagement":false
},
{
"DelegateSid":null,
"DisplayName":"Jake",
"HasDelegation":true,
"HasEtaManagement":false
},
{
"DelegateSid":null,
"DisplayName":"Ryan",
"HasDelegation":true,
"HasEtaManagement":false
}
]
Try:
var returndata;
$.ajax({
type: "GET",
url: "GetSecurables/",
data: { etaNumber: etaNumber },
success: function (data) {
console.log(data);
returndata = data;
console.log(returndata);
}
});
If the 2 outputs are the same it might be the case that you're trying to access returndata from outside its scope, hence the undefined, or that you're accessing returndata before the Ajax call completes.

401 (Unauthorized) error with ajax request (requires username and password)

I'm making an ajax request to retrieve json data from webtrends - a service that requires a login. I'm passing the username and password in my ajax request, but still gives me a 401 unauthorized error. I've tried 3 different methods - but no luck. Can someone pls help me find a solution?
1. $.getJSON('https://ws.webtrends.com/..?jsoncallback=?', { format: 'jsonp', suppress_error_codes: 'true', username: 'xxx', password: 'xxx', cache: 'false' }, function(json) {
console.log(json);
alert(json);
});
2. $.ajax({
url: "https://ws.webtrends.com/../?callback=?",
type: 'GET',
cache: false,
dataType: 'jsonp',
processData: false,
data: 'get=login',
username: "xxx",
password: "xxx",
beforeSend: function (req) {
req.setRequestHeader('Authorization', "xxx:xxx");
},
success: function (response) {
alert("success");
},
error: function(error) {
alert("error");
}
});
3. window.onload=function() {
var url = "https://ws.webtrends.com/...?username=xxx&password=xxx&callback=?";
var script = document.createElement('script');
script.setAttribute('src', url);
document.getElementsByTagName('head')[0].appendChild(script);
}
function parseRequest(response) {
try {
alert(response);
}
catch(an_exception) {
alert('error');
}
}
Method 3 might work when you use a named callback function and use basic authentication in the url. Mind though that a lot of browsers don't accept url-authentication (or whatever the name is). If you want to try it, you can rewrite it like this:
window.onload = function() {
var url = "https://xxx:xxx#ws.webtrends.com/...?callback=parseRequest";
var script = document.createElement('script');
script.setAttribute('src', url);
document.getElementsByTagName('head')[0].appendChild(script);
}
function parseRequest(response) {
try {
alert(response);
}
catch(an_exception) {
alert('error');
}
}

How to make an AJAX request to jsFiddle's echo api using Ext.Ajax?

I'm trying to make a simple AJAX call to jsFiddle's JSON echo api using Ext.Ajax. It makes the request successfully, but the object returned is empty, not the JSON object I send (which it should be).
Code:
Ext.Ajax.request({
url: '/echo/json/',
jsonData: { foo: 'bar' },
success: function(resp) {
console.log('success!');
console.log(Ext.decode(resp.responseText)); //empty object..? why?
},
failure: function(resp) {
console.log('failure!');
},
});
Fiddle: http://jsfiddle.net/nANE7/
How come the response is just an empty object? Where's the foobar JSON information that it's meant to echo back?
Try this:
Ext.Ajax.request({
url: '/echo/json/',
params: {
json: Ext.encode(
{
param1:'bar'
}
)
},
success: function(resp) {
console.log('success!');
console.log( resp );
console.log(Ext.decode(resp.responseText)); //empty object..? why?
},
failure: function(resp) {
console.log('failure!');
},
});

Resources