Sinon fakeserver test times out on promise - javascript

I am just getting into unit testing and currently exploring the Mocha, Chai, Sinon setup, testing this in a browser.
I have a javascript block that makes an ajax call to the server like so
PromptBase.prototype.fetchAndSetTemplateString = function() {
var deferred = $.Deferred();
$.ajax({
url: "/someurl",
type: "GET"
})
.done(function(response) {
if (response.status.toLowerCase() === "success") {
deferred.resolve(response.templateString);
}
});
return deferred.promise();
};
PromptBase.prototype.fetchPromptTemplate = function() {
var promise = this.fetchAndSetTemplateString();
$.when(promise).done(function(promptHtml) {
//set the templateString to promptHtml here
});
};
//Register a custom event on document to fire ajax request.
$(document).on("customevent", function(event) {
promptInstance.fetchPromptTemplate(promptParams.fetchTemplateOnEvent);
});
Now I have a test case that looks like this using fakeServer
describe("TemplateFetch", function() {
before(function() {
this.server = sinon.fakeServer.create(); // and other setup
});
after(function() {
this.server.restore(); // and other clean up
});
it("should fetch template string from server, when fetchTemplateEvent is fired", function() {
var expectedTemplateString = "templateStringFromServer";
var templateAjaxUrl = '/someurl';
//faking a server response
this.server.respondWith("GET", templateAjaxUrl,
[200, {"Content-Type": "application/json"},
'{"status": "success", "templateString": "'+ expectedTemplateString +'"}']);
// Now trigger even that fetches the template
$(document).trigger("customevent");
// This calls the ajax done function, which resolves the promise
this.server.respond();
//debugger shows that templateString is set here
expect(this.instance.templateString).to.eqaul("something");
});
});
However my test outputs the timeout error
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
So the fakeServer.respond() call ensures that the ajax->done function is called resulting in the promise being resolved, And when I debug I can see that the expect test should match the two strings. How can I fix the time out issue here? I have also tried adding a done callback but to no avail.
Any input is appreciated. Thanks for reading.

Related

JavaScript: Retry request on fail and continue normal app flow

Quick context:
The user has a problem with the network and I need to repeat requests to the server to continue loading app instead of showing an error.
Existing code in the app:
ajaxPostWithRootData = function (url, rootData) {
//prepare request
var request = decoratorFunction(
$.ajax({
//standard data required to send request
})
)
.done(function (result) {
// normal flow
})
.fail(function (e) {
//I want to repeat call after 1 second
});
return request;
};
Example call:
return ajaxPostWithRootData(url, data)
.done(function (result) {
//do some stuff
})
.fail(function (e) {
//hide loading, show error message
});
};
I thought to write in fail() something like that:
//repeatOnFail is a new function's param to prevent infinite loop
if (shouldRepeatOnConnectionProblem(repeatOnFail, e)) {
setTimeout(() => ajaxPostWithRootData(url, rootData, false), 1000);
}
Unfortunately JS works asynchronously here and even I get success response in second request, it's too late because app executes code from second fail().
Do you know how to change this code to "back" to the correct flow when I got a success response after the second call?
My knowledge about JS is poor, so even I know have an idea how to fix it, I don't know how to implement a solution ;/
If I understand correctly, you would like to invoke $.ajax({..}) with a fixed configuration and, if that first invocation fails, immediately retry the $.ajax({..}) request with the same configuration, in which case the following changes to ajaxPostWithRootData should achieve what you require:
var ajaxPostWithRootData = function(url, rootData) {
// Define reusable function that sets up and invokes
// ajax request
var doRequest = function() {
return decoratorFunction(
$.ajax({
//standard data required to send request
}))
}
// Return a promise that is controlled by this deferred behavior
return $.Deferred(function(deferred) {
doRequest()
.fail(function (){
doRequest ()
.fail(function () {
// On fail after second attempt, throw error
// to external app flow
deferred.reject();
}).done(function (result){
// If second attempt succeed, continue external
// app flow
deferred.resolve(result);
});
})
.done(function (result){
// If first attempt succeed, continue external
// app flow
deferred.resolve(result);
});
}).promise();
};
There updates should also work nicely with your external code and preserve the overall flow of events that you're after.
Hope that helps!

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.

Generating AJAX Request Dynamically Based on Scenario

Simply put, I'm trying to dynamically generate an AJAX request based off a scenario that I'm retrieving via an AJAX request from a server.
The idea is that:
A server provides a "Scenario" for me to generate an AJAX Request.
I generate an AJAX Request based off the Scenario.
I then repeat this process, over and over in a Loop.
The big idea is that I can change the second AJAX request dynamically, based off the Scenario given from the server.
I have this working, but I feel like the way I'm doing this is very messy. Is there any better way to go about thinking through this problem? Perhaps promises? If anyone could please review this and provide feedback or suggestions on how to clean it up--that would be greatly appreciated.
Here is my code: http://jsfiddle.net/6jph0e98/
(please open the console to see everything in action)
As a reference, here is the scenario data I'm currently working with:
var scenario = {
"base": {
"frequency": "5000"
},
"endpoints": [
{
"method": "GET",
"type": "JSON",
"endPoint": "https://api.github.com/users/alvarengarichard",
"queryParams": {
"objectives": "objective1, objective2, objective3"
}
}
]
}
Here are my 2 cents: http://jsfiddle.net/3Lddzp9j/6/.
Yes, I think you can do this more elegantly by chaining promises. So I figured out what I think your app does, and how you can do it by chaining these promises. What is interesting that certain steps already return promises ( the jQuery AJAX calls ) but others don't. For those - we have to create our own promise that instantly resolves. And then there was the timeout which we wrapped in a promise.
Also, I tried to use some JS best practices, like keeping things out of the global space by wrapping them in an IIFE and applying the module pattern.
This makes the overall control flow of your application nice and clean IMHO:
var run = function() {
getScenario()
.then(mapToInstruction)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(run);
};
And also hides the private members and only exposes the run() method:
return {
// This will expose only the run method
// and will keep all other functions private
run : run
}
Hope it helps - let me know what you think. Here's the full source, with comments:
// First of all - I'm using the javascript module pattern here
// this will all be much more easy once ES6 it out, but this will
// have to do for now.
// Also, I'm importing jQuery into the module as you can see, which
// is wrapped inside the IIFE ( Google it ) which keeps things nicely
// out of the global scope.
var App = (function ($) {
// Gets the scenario from the API - $.get is just some syntactic
// sugar for $.ajax with GET as method - NOTE: this returns a promise
var getScenario = function () {
console.log('Getting scenario ...');
return $.get('http://demo3858327.mockable.io/scenario');
};
// The result of the previous promise is passed into the
// next as we're chaining. So the data will contain the
// result of getScenario
var mapToInstruction = function (data) {
// We map it onto a new instruction object
var instruction = {
method: data.endpoints[0].method,
type: data.endpoints[0].type,
endpoint: data.endpoints[0].endPoint,
frequency: data.base.frequency
};
console.log('Instructions recieved:');
console.log(instruction);
// And now we create a promise from this
// instruction so we can chain it
var deferred = $.Deferred();
deferred.resolve(instruction);
return deferred.promise();
};
// This wraps the setTimeout into a promise, again
// so we can chain it
var waitForTimeout = function(instruction) {
console.log('Waiting for ' + instruction.frequency + ' ms');
var deferred = $.Deferred();
setTimeout(function() {
deferred.resolve(instruction)
}, instruction.frequency);
return deferred.promise();
};
// Final step: call the API from the
// provided instructions
var callApi = function(instruction) {
console.log('Calling API with given instructions ...');
return $.ajax({
type: instruction.method,
dataType: instruction.type,
url: instruction.endpoint
});
};
var handleResults = function(data) {
console.log("Handling data ...");
var deferred = $.Deferred();
deferred.resolve();
return deferred.promise();
};
// The 'run' method
var run = function() {
getScenario()
.then(mapToInstruction)
.then(waitForTimeout)
.then(callApi)
.then(handleResults)
.then(run);
};
return {
// This will expose only the run method
// and will keep all other functions private
run : run
}
})($);
// ... And start the app
App.run();

how to use deferred with https.request in nodejs

I use github to authenticate in my node application. I have constructed the following code:
var req = request(postOptions, function (res) {
res.on('data', function (d) {
...
var getOptions = parseUrl('https://api.github.com/user?access_token=' + accessToken);
...
var req = request(getOptions, function (resp) {
...
resp.on('data', function (d) {
...
})
.on('end', function () {
...
})
});
req.end();
});
});
req.write(postData);
req.end();
I've removed some code, because the point here is that I have a request in a request. Now, nodejs has deferreds The question is if this can be used to simplify the above code ?
Well, you have no error handling. Promises significantly cleans up code that correctly propagates errors and doesn't leak resources because those become automatic. So it's impossible to make a fair comparison because promise code that doesn't handle errors still propagates them.
var Promise = require("bluebird");
var request = Promise.promisifyAll(require("request"));
function githubAuthenticate() {
return request.postAsync(postOptions, postData)
.spread(function(response, body) {
var accessToken = ...
var getOptions = parseUrl('https://api.github.com/user?access_token=' + accessToken);
return request.getAsync(getOptions);
})
.spread(function(response, body) {
});
}
Now imagine if something failed here? You would add a .catch only once, in one place, and handle it there. Since errors automatically propagate, the code above doesn't need to do anything. The consumer code can just do:
gitHubAuthenticate().then(function() {
}).catch(function(err) {
// Any error that happened with the post, get or your code gets here
// automatically
});

Ember Mocha tests fail when async (using ember-mocha-adapter)

I can't get mocha working with Ember due to the fact that it fails when a test of the following nature is executed:
describe('Location Panel', function () {
beforeEach(function () {
App.reset();
visit('/map/41.76721,-72.66907');
});
it('Have proper address', function () {
var $title = find('.panel-header h2');
expect($title).to.have.text('476 Columbus Blvd, Hartford');
});
});
Basically it can't find any of the DOM elements, because it runs the test before the route has finished loading.. The same happens if I visit from within the test, and use andThen, etc..
Here's a jsbin for debugging.
Edit
In the jsbin, I'm using a mocked ajax call, but in my tests the ajax calls are real. I am using Ember.$.ajax wrapped in the following:
function ajax (url, options) {
return new Ember.RSVP.Promise(function (resolve, reject) {
options = options || {};
options.url = url;
options.success = function (data) {
Ember.run(null, resolve, data);
};
options.error = function (jqxhr, status, something) {
Ember.run(null, reject, arguments);
};
Ember.$.ajax(options);
});
}
Should I be using Ember.run.later as well?
You should use Ember.run.later instead of setTimeout so that the wait helper knows that it should wait.
Alternatively you can use Ember.test.registerWaiter though I don't think you need it here.
Updated JSBIN: http://emberjs.jsbin.com/gahe/1/edit

Categories

Resources