QUnit with Ajax, QUnit passes the failing tests - javascript

I am looking into QUnit for JavaScript unit testing. I am in a strange situation where I am checking against the value returned from the Ajax call.
For the following test I am purposely trying to fail it.
// test to check if the persons are returned!
test("getPersons", function() {
getPersons(function(response) {
// persons = $.evalJSON(response.d);
equals("boo", "Foo", "The name is valid");
});
});
But it ends up passing all the time. Here is the getPersons method that make the Ajax call.
function getPersons(callback) {
var persons = null;
$.ajax({
type: "POST",
dataType: "json",
data: {},
contentType: "application/json",
url: "AjaxService.asmx/GetPersons",
success: function(response) {
callback(response);
}
});
}

Starting and stopping using the QUnit library seems to be working!
// test to check if the persons are returned!
test("getPersons", function() {
stop();
getPersons(function(response) {
persons = $.evalJSON(response.d);
equals(persons[0].FirstName, "Mohammad");
start();
});
});

The real problem here isn't needing to call the start() and stop() methods - in fact you could get into trouble using that approach if you are not careful in calling stop() again at the end of your callback if you have additional .ajax() methods. Which then means you find yourself in some snarled mess of having to keep track if all the callbacks have been fired to know if you still need to call stop() again.
The root of the problem involves the default behavior of asynchronous requests - and the simple solution is to make the .ajax() request happen synchronously by setting the async property to false:
test("synchronous test", function() {
$.ajax({
url:'Sample.asmx/Service',
async:false,
success: function(d,s,x) { ok(true, "Called synchronously"); }
});
});
Even still, the best approach is to allow the asynchronous behavior and use the right test method invocation: asyncTest(). According to the docs "Asynchronous tests are queued and run one after the other. Equivalent to calling a normal test() and immediately calling stop()."
asyncTest("a test", function() {
$.ajax({
url: 'Sample.asmx/Service',
success: function(d,s,x) {
ok(true, "asynchronous PASS!");
start();
}
});
});

There a lot of qunit test in my project. like:
module("comment");
asyncTest("comment1", function() {
expect(6);
$.ajax({
url: 'http://xxx.com/comment',
dataType: "json",
type: "GET",
timeout: 1000
}).done(function(data) {
ok(true, "loaded");
ok(data.data.length>1, "array size");
ok(data.total, "attr total");
var c = data.data[0];
ok(c.id, "attr c.id");
ok(c.user_id, "attr c.user_id");
ok(c.type == 4, "attr c.type equal 4");
}).fail(function(x, text, thrown) {
ok(false, "ajax failed: " + text);
}).always(function(){
start();
});
});

ive done some qunit testing with ajax. its not pretty. the best thing i could come with is stopping the test when ajax is fired, and starting it again in the success callback. (using start() and stop()) methods. This meant one ajax request at a time, but i could live with that. Good luck

Related

Perform AJAX call only if there isn't another AJAX request in progress from a previous function invokement

I've landed on a new messy code base without any testing available and I'm struggling for quite some time with this issue. I can't use any ES6 code, just plain old vanilla JS and jQuery.
The context: on a web page there are several pairs of inputs, the first input from the pair triggers the firstFunction() on blur and the second input the secondFunction() also on the blur event. As you can see from the sample code bellow, each function then calls another function, ajaxFunction() which performs an AJAX request.
The goal: I want to be able to invoke the ajaxFunction() only if there isn't another AJAX request in progress from a previous function invokement. The thing is that when it's run for the first time on a pair of inputs, a new row is created in the database and the request returns the ID of the row. So when it's called the second time, it will be an UPDATE in the database instead of an INSERT because of the ID. If the second call from the pair runs before the first one finished, I end up having two distinct rows in the database, which is not desired.
Example: if the user enters some text in the first input, then the firstFunction() is triggered on blur, then he quickly moves to the second input and enters something so the secondFunction() is triggered on blur, but it may be the case that the AJAX request from the firstFunction() call hasn't been completed yet. So the AJAX call from the secondFunction() should start only if the one from the firstFunction() has been completed. The same logic applies for the case when the user starts typing in the second input and the in the first one.
Note: this aren't the only AJAX requests on the page, there are several others as well.
I've done some research and I feel like $.when() is what I need, but it beats me when it comes to integrating it in this logic. I also thought about $.active but it doesn't really help.
function firstFunction() {
// Some logic goes here then we call the AJAX function.
ajaxFunction(1, 2, 3);
}
function secondFunction() {
// Some logic goes here then we call the AJAX function.
ajaxFunction(4, 5, 6);
}
function ajaxFunction(param1, param2, param3) {
$.ajax({
method: "POST",
url: "https://website.localhost",
data: {
param1: param1
param2: param2
param3: param3
}
}).done(function() {
console.log('Done!');
});
}
As you have jQuery available on your scope, I would use the queue feature to cover this use case. The basic idea behind it is to leverage this available feature and organize your XHR requests into a FIFO line.
It was very nicely explained by #gnarf on this answer.
This is a quick way of making a queue, there are ways to make it cleaner, but it is the basic concept
function firstFunction() {
addToQueue({
param1: 1,
param1: 2,
param1: 3
});
}
function secondFunction() {
addToQueue({
param1: 1,
param1: 2,
param1: 3
});
}
var activeQueue = []
var activeRequest = false
function addToQueue(data) {
// Add item to the queue of things needing to run
activeQueue.push(data);
// if the request is not active than run it
if (!activeRequest) {
executeQueue()
}
}
function executeQueue() {
// if nothing to run, exit
if (!activeQueue.length) return
// set the request is active
activeRequest = true
// get the data for this request
var data = activeQueue.shift()
// make the call
$.ajax({
method: "POST",
url: "https://website.localhost",
data: data
}).done(function() {
console.log('Done!');
// mark call is done
activeRequest = activeQueue.length > 0
// run it again
executeQueue()
});
}
If you don't want to allow to make another ajax, if one is already in progress and haven't commpleted yet (with error or success) then just make a global bool variable, like:
var ajaxInProgress = false.
You can set it to true in ajaxFunction and set it to false on ajax completion. Then you place your ajax call in a if(!ajaxInProgress) {...}.
This way you won't make a second ajax call if one is already being made.
I suspect that you want to put your ajax calls in a queue, so that if you do something like:
firstFunction();
secondFunction();
Your second function will send the ajax, but only after the first finished. To make this happen I would write a simple queue for your code:
var queue = [];
var ajaxInProgress = false;
function ajaxFunction(param1, param2, param3) {
if (!ajaxInProgress) {
ajaxInProgress = true;
$.ajax({
method: "POST",
url: "https://website.localhost",
data: {
param1: param1,
param2: param2,
param3: param3
}
}).done(function () {
console.log('Done!');
ajaxInProgress = false;
executeNextAjaxInQueue();
});
} else {
ajaxInProgress.push([param1, param2, param3]);
}
}
function executeNextAjaxInQueue(ajaxParams) {
if (queue.length === 0) {
return;
}
var params = queue.shift();
ajaxFunction(params[0], params[1], params[2]);
}
Depends on exactly what you want.
If all ajax requests are to be completely ignored when a ajax request is already in-flight, then:
var ajaxFuntion = (function() {
var inFlight = null;
return function(param1, param2, param3) {
if(!inFlight) {
inFlight = $.ajax({
'method': "POST",
'url': "https://website.localhost",
'data': {
'param1': param1,
'param2': param2,
'param3': param3
}
}).done(function(val) {
inFlight = null;
});
};
return inFlight;
};
})();
If ajax requests are to be queued to any that were previously queued (any of which may be in-flight), then:
var ajaxFuntion = (function() {
var inFlight = jQuery.when(); // dummy starter promise.
return function(param1, param2, param3) {
inFlight = inflight.then(function() {
return $.ajax({
'method': "POST",
'url': "https://website.localhost",
'data': {
'param1': param1,
'param2': param2,
'param3': param3
}
});
};
return inFlight;
});
})();
As you can see, both versions involve an IIFE which returns a Function.
By using an IIFE, the inFlight variable is "self-contained", avoiding the need for another member in the same scope as ajaxFuntion or higher.
In the second version, note that queuing is really simple, because jQuery.ajax() returns a promise allowing a queue to be formed by chaining .then().then().then() as many times as you like.

More compact way to write ajax queries

I have been writing many web apps with php & mysql & jquery & bootstrap and now it's time address this problem. How to write shorter ajax queries(posting) ?
If I want to write code that works and takes care of many problems, it's too long for every ajax call.
Is there a better way or some library / wrapper that makes the code SHORTER and FASTER to write, but does atleast all these stuff
I looked popular axios, but it seems even worse
//JUST an example code, too complicated
var $btnStatusElem = $("#passwordreset").button('loading');
$.ajax({
type: "POST",
cache: false,
url: "pwreset.php",
data: postdata
success: function(data) {
$btnStatusElem.button('reset');
try {
var datajson = JSON.parse(data);
}
catch (e) {
alert('Unexpected server error');
return false;
};
if (datajson['success'] == true) {
//do the OK stuff
} else {
//show the error code, and stuff
return false;
}
},//success
error: function(msg) {
alert('ERROR');
$('#passwordreset_result').html(msg);
}
});
For my code, ajax query, i want it to do these steps:
1. Disable the submit button while posting (re-enable also after 15 seconds and not just leave it disabled until page refresh)
2. It sends json, expects json to return
3. If server has some error, it DOES NOT return json but error. Then the code will halt all js execution if i dont use try...catch. This is pain to write each time
4. If server returns validation error or some other expected error, i have to detect this and show to the user
5. If all ok, do the stuff
As with any refactoring, identify and isolate the repetitive code and pass in the unique bits. In this case, for example, you could isolate the ajax call and json parsing into a function and pass in the url, data, etc.
That function could return a promise that resolves/rejects as appropriate.
Given the doRequest function below (pseudocode, untested and would probably need a bit of tweaking for real-world use), you could then use it all over the place with fewer keystrokes:
doRequest('pwreset.php', postdata, button)
.then(result => {
// do something with the result
})
.catch(error => {
// deal with the error
});
or
try {
const result = await doRequest('pwreset.php', postdata);
// do something with result
}
catch (e) {
// handle error
}
All of the boilerplate stuff is isolated in doRequest.
async function doRequest(url, data, button, type = "POST") {
return new Promise((fulfill, reject) => {
$.ajax({
type,
url,
data,
cache: false,
success: function(data) {
$btnStatusElem.button('reset');
try {
const datajson = JSON.parse(data);
} catch (e) {
return reject(e);
};
return datajson['success'] == true ?
fulfill(datajson) :
reject(datajson);
}, //success
error: function(msg) {
return reject(msg);
}
});
})
}
As #mister-jojo says, you might also want to consider using the [fetch api] instead of jQuery, but the same principle applies.

Qunit ajax async calls with mockajax

I am trying to construct a Qunit test to test an ajax post call. I am using mockajax to mock the call. However, I can't get the test to work with the asynchronous call.
Here is my source code:
source.js:
ajaxFunc = function(element){
$.post({
type:'POST',
url: '/temp/',
dataType:'json',
success: function(data){
element.text("changed in func");
},
error: function(data){
//do something
},
timeout: 60000
});
}
syncFun = function(element){
element.text("changed in func");
}
Here is my test code:
tests2.js
function mockSuccess(){
$.mockjax({
url: "/temp/",
responseText: { success: true }
});
}
QUnit.test( "test ajax async", function( assert ) {
mockSuccess();
var done = assert.async();
var element = document.createElement("div");
ajaxFunc($(element));
$(element).text("old");
setTimeout(function() {
assert.strictEqual($(element).text(), 'changed in func');
done();
});
});
QUnit.test( "test sync", function( assert ) {
var element = document.createElement("div");
$(element).text("old");
syncFun($(element));
assert.strictEqual($(element).text(), 'changed in func');
});
The first tests fails, and the second one succeeds. If I put a comment in the source.js within the success callback, I see in fact that the the post succeeds and changes the elements text.
In other words, everything works correctly except testing the results of the post.
I have looked at https://qunitjs.com/cookbook/ and the examples on stack overflow with no luck.
It looks like I just need to add a number of milliseconds to get the async call to work:
QUnit.test( "test ajax async", function( assert ) {
mockSuccess();
var done = assert.async();
var element = document.createElement("div");
ajaxFunc($(element));
$(element).text("old");
setTimeout(function() {
assert.strictEqual($(element).text(), 'changed in func');
done();
}, 800);
// ^^^
});
I have seen examples like this in the github for qunit, so I guess this is correct, but I can see problems where you don't set the time function high enough.
I have already moved all of the processing that occurs in success: and error: outside the post function, so I can test it independently of the ajax calls, but I still wanted to test the actual post for various types of errors.

Why aren't ajax calls being made with a specified delay

I am trying to call an action on a controller with ajax: 10 times with a 2 second delay in my MVC5 application.
Here is the code I've written:
$(document).ready(function () {
(function loop(i) {
setTimeout(function() {
var d = new Date();
console.log(d.getTime());
callAjax();
console.log("works " + i);
if (--i) loop(i);
},
2000); // ms
})(10);
function callAjax() {
$.ajax({
url: '/Home/StartElection',
type: 'POST',
data: "test",
async: true
})
.done(function (partialViewResult) {
$("#partialTable").html(partialViewResult);
});
};
});
The console log is as expected (with a 2 second delay) but the calls to the controller happen instantly - when I set a break in Visual Studio on the controller action the next call after continuing takes 2ms
I can't see why this is happening - can any one help?
Edit: I added a console log of Date.getTime() just before the ajax call & there are 2000 ms between each
You have to change this line alone async: true -> async: false
because the AJAX calls will be made asynchronous if this property is set to true.
And so your ajax calls have no time delay.
Hope this helps.
As far as your client side code is concerned it seems to be working well as far as calls are concerned. With that said here are a few things to consider when dealing with ajax:
1) You have control over the number of times that you can call a remote service but you have no control over the time it will take for that service to respond.
2) As such it is usual good practise for most cases to not make ajax calls in a loop (this somewhat defeats the general purpose of ajax). Rather use the response to each call to then make the next call (but then of course we would need to know exactly what it is you are trying to build to suggest an exact solution).
So the closest thing to what you are looking for using ajax I think would be more of something like this:
$(document).ready(function () {
/*(function loop(i) {
setTimeout(function() {
var d = new Date();
console.log(d.getTime());
callAjax();
console.log("works " + i);
if (--i) loop(i);
},
2000); // ms
})(10);*/
var i=0;
function callAjax() {
var requestTimeMessage = "fetch #"+i+" at: "+new Date().getTime();
console.log(requestMessage);
$.ajax({
url: '/Home/StartElection',
type: 'POST',
data: "test",
async: true
})
.done(function (partialViewResult) {
var requestTimeMessage = "response #"+i+" at: "+new Date().getTime();
console.log(requestMessage);
$("#partialTable").html(partialViewResult);
i++;
if(i<10) {
/*Note this assumes a quick response for each call.
* Otherwise the response together with this delay will total a wait longer than 2 seconds.
*/
setTimeout(function() {
callAjax();
2000);
}
});
};
});
But as I said. I would need to know exactly what you are trying to achieve to give more appropriate answer to your question.

How to have jQuery.ajax cover both error() & complete() in single clause?

I have a slideshow that uses an ajax call to fetch my data like this:
jQuery.ajax({
dataType: "json",
timeout:8000,
url: full_fallback_uri,
success: function(result) {
//do stuff
},
error: function() {
self.skip();
},
complete: function(xhr, data) {
if (xhr.status != 200) {
self.skip();
}
},
asynch: false,
});
So in case the ajax call fails, skip() is called in order to skip current slide.
Now above setup doesn't work, since in the case of an error, skip() is called twice, causing a double-skip (skipping two slides not just the current one).
And if I leave out either the
error()
or the
complete() (with the check for http status != 200)
it's not enough, the one doesn't cover all possible errors.
How could I write a clause that covers both error and complete cases?
Would something like this work?
jQuery.ajax({
dataType: "json",
timeout:8000,
url: full_fallback_uri,
success: function(result) {
//do stuff
},
error: function() {
this.myErrorSign = true; //DEFINING MY OWN VARIABLE ON THIS jQuery.ajax object
this.complete(); //MANUALLY CALL THE COMPLETE FUNCTION, IN CASE THIS ISN'T DONE AUTOMATICALLY UPON FAIL
},
complete: function(xhr, data) {
if (xhr.status != 200 || this.myErrorSign == true) { //LOOK NOT ONLY FOR STS!=200 BUT ALSO FOR THE VARIABLE THAT WAS DEFINED IN THE ERROR CASE ABOVE
self.skip();
}
},
asynch: false,
});
Or perhaps this design will be similarly flawed, and in some cases call the skip() twice just as well?
You could only .always and differ wether the arguments come from an error or not:
var ajaxRequest = jQuery.ajax({
/* only settings, no callbacks, no error: fn(){}! */
});
ajaxRequest.always(function (jqXhr, statusText) {
// gets called on complete and on error
if (statusText === 'error') {
skip();
}
});
Works on my local machine, when called server is down.
Check out jQuerys documentation on that.
Use the following simple logic and you cannot go wrong - you are quite close.
The error callback always fires (whenever it does) before the complete callback. Have a variable that can be seen by both callbacks, say error = false; initially set to false before the ajax call.
In the error callback set error = true; that's it.
In the complete callback - this callback always fires - use the following:
if( error || (xhr.status != 200) ) {
self.skip();
}
//done ... you may want to have a way to reset error = false before a re-run
Please note that this within each callback points to the callback function and is therefore local ... have a variable with a wider scope ... outside of the ajax call. Since the complete callback fires on error and on success, you do not need to call it explicitly. :)

Categories

Resources