Fake Ajax request - javascript

In order to check the submission of an ajax request, when a user submit a form, I implemented the following test using jasmine (1).
The test is successfully passed but looking at javascript console I get the following error 500 (Internal Server Error).
Since I don't care the server response, my questions are:
1) Should I or should I not care about this error.
2) Will it be better to fake the ajax request to avoid Internal Server Error? if yes, how in my context?
(1)
define([
'backendController'
], function (backendController) {
// some code
describe('When button handler fired', function () {
beforeEach(function () {
spyOn(backendController, 'submitRequest1').andCallThrough();
this.view = new MyView({
el: $('<div><form><input type="submit" value="Submit" /></form></div>')
});
this.view.$el.find('form').submit();
});
it('backendController.submitRequest1 should be called', function () {
expect(backendController.submitRequest1).toHaveBeenCalled();
});
});
});
(2)
// backendController object
return {
submitRequest1: function (message) {
return $.ajax({
url: 'someUrl',
type: 'POST',
dataType: 'json',
data: {
message: message
}
}).done(function () {
// some code
});
}
}
(3)
// submitForm in MyView
submitForm: function (event) {
event.preventDefault();
var formElement = event.currentTarget;
if (formElement.checkValidity && !formElement.checkValidity()) {
this.onError();
} else {
backendController. submitRequest1('some data').done(this.onSuccess).fail(this.onError);
}
}

Your test says "backendController.submitRequest1 should be called" so you only care about the method to be called. So, just don't andCallThrough() and you'll be free of troubles.
Update
Maybe you have to preventDefault() in the submit event so:
spyOn(backendController, 'submitRequest1').andCallFake(function(e) { e.preventDefault(); });
Update 2
After checking the rest of your code you have added to the question I still suggest to stop your code execution as sooner as possible. Just until the test in arriving, and this is still until backendController.submitRequest1() is called.
In this case is not enough with a simple mock in the method due the return of this method is used subsequently and your mock should respect this:
// code no tested
var fakeFunction = function(){
this.done = function(){ return this };
this.fail = function(){ return this };
return this;
}
spyOn(backendController, 'submitRequest1').andCallFake( fakeFunction );
When things start to be so complicate to test is a symptom that maybe they can be organized better.

Related

Callback called when a task finish OR already finished

I have a simple code that involves asynchronous tasks:
// The NewsFeed Class
function NewsFeed() {
this.loadFeed = function() {
$.ajax({
url: "http://www.example.com",
success: function() {
// doSomething here, and call onload.
}
});
}
// Need to implement onload here somehow
this.onload = ?;
this.loadFeed();
return this;
}
NewsFeed.constructor = NewsFeed;
// In main JS file
var newsFeed = new NewsFeed();
$(function() {
// do something
newsFeed.onload = function() { // do something when news feed is loaded };
}
My requirement is that, onload of NewsFeed needed to be executed in both case:
If the loadFeed's ajax is finished, run it immediately.
If the loadFeed's ajax is not done yet, run after it.
There's really no need to use new or constructor when you don't need new instances, all you really need is to run a simple ajax function that gets the result from cache if it hasn't changed.
function newsFeed() {
return $.ajax({
url : "http://www.example.com",
cache : true // let the browser handle caching for you
});
}
// In main JS file
$(function() {
newsFeed().then(function() {
// do something when news feed is loaded
});
});
The new pattern instead of callback is using Promises
see:
https://github.com/kriskowal/q
With jquery you can use:
https://api.jquery.com/category/deferred-object/
now the code:
function NewsFeed() {
function loadFeed() {
var deferred = $.Deferred();
$.ajax({
url: "http://www.example.com",
success: function(data) {
deferred.resolve(data);
},
error: function(data) {
deferred.reject(data);
}
});
return deferred.promise();
}
this.loadFeed = loadFeed;
return this;
}
NewsFeed.constructor = NewsFeed;
// In main JS file
var newsFeed = new NewsFeed();
newsFeed.loadFeed().done(function(data){
//data loaded successfully
})
.fail(function(data){
//ajax request failed
})
.always(function(){
//finally:
});

Angular 1.5.4 $http progress event

Now Angular 1.5.4 finally allows you to track progress event on $http provider but for some reason I keep getting the $rootScope as a response instead of an actual progress (I'm using it for uploads) information. Because of lack of examples I found some tests in the Angular repo and followed that but to no success.
restClientInstance.post = function (requestParams) {
var postParams = {
method: "POST",
url: API_URL + requestParams.url,
headers: requestParams.headers,
data: requestParams.data,
eventHandlers: {
progress: function (c) {
console.log(c);
}
},
uploadEventHandlers: {
progress: function (e) {
console.log(e);
}
}
};
var promise = $http(postParams)
$rootScope.$apply();
return promise;
};
In both cases it consoles $rootScope rather than the lengthComputable
In AngularJS v1.5.7 works fine. If you have the chance I recommend upgrade!
...//formData = new FormData(); etc...
var postParams = {
method: 'POST',
url: yourURLWS,
transformRequest: angular.identity,
uploadEventHandlers: {
progress: function (e) {
if (e.lengthComputable) {
$scope.progressBar = (e.loaded / e.total) * 100;
$scope.progressCounter = $scope.progressBar;
}
}
},
data: formData,
headers: {'Content-Type': undefined }
};
var sendPost = $http(postParams); //etc...
in HTML you have:
<progress id="progress" max="100" value="{{progressBar}}"></progress>{{progressCounter}}%
Result:
progress result
The feature is broken for now: https://github.com/angular/angular.js/issues/14436
Well I ended up doing something like this and just handle it myself as the XHR events added to $http dont work for me.
var xhttp = new XMLHttpRequest();
var promise = $q.defer();
xhttp.upload.addEventListener("progress",function (e) {
promise.notify(e);
});
xhttp.upload.addEventListener("load",function (e) {
promise.resolve(e);
});
xhttp.upload.addEventListener("error",function (e) {
promise.reject(e);
});
xhttp.open("post",API_URL + requestParams.url,true);
xhttp.send(requestParams.data);
return promise.promise;
note - I have not worked with NG 1.5.4, the example below is for leveraging existing pre 1.5.4 APIs
The notify(event) API is part of the deferred object when you call $q.defer(). I'm not sure what a practical implementation of this would be in terms of a typical get/put/post call via $http. But if you want to see it in action you can do something like this:
some service API
var mockRqst = function(){
var d = $q.defer()
var crnt = 0
$off = $interval( function(){
d.notify( crnt )
crnt += 5
if (crnt >= 100)
{
$interval.cancel( $off ) //cancel the interval callback
d.resolve( "complete" )
}
}
return d.promise
}
using the notification
someService.mockRqst()
.then( thenCallback, catchCallback, function( update ){
console.log("update", update)
})
codepen - http://codepen.io/jusopi/pen/eZMjrK?editors=1010
Again, I must stress that I'm not entirely sure how you can tie this into an actual external http call.
As seen in the docs here, the third parameter in a promise is a notify function.
notify(value) - provides updates on the status of the promise's execution. This may be called multiple times before the promise is either resolved or rejected.
It can be used like this:
$http(requestData)
.then(
function success() {
console.log('success');
},
function error() {
console.log('error');
},
function notify() {
console.log('notified');
}
);

Is it possible to mock an ajax request with jasmine simulating a network failure?

I have a web app that needs to check if the user is connected to the internet. In the implementation, the check() function promises to true if an ajax ping to a known endpoint succeeds and false if the ajax call fails in any way.
In Jasmine, I can use request.respondWith({status:400, etc}) to simulate a failure, but I can't work out how to simulate the more fundamental error of the call not being made at all.
In practice, browsers seem to 'return' a status code 0 and readyState 4 when the call could not even be made.
How should I approach this in my Jasmine tests?
This is an interesting question. Though I might not provide you a straight forward answer for it, I'd like to take a stab at it.
The code below illustrates three such examples (tests rather). See it in action
var errorFunction = function(jqXHR, status, error) {
console.log('This is errorFunction')
}
var makeAPICall = function(url) {
return $.ajax({
url: url,
fail: errorFunction,
error: errorFunction
});
}
var testFunction = function() {
makeAPICall().done(function(data, status, xh) {
if (xh.status === 0 && xh.readyState == 4) {
errorFunction();
return;
}
console.log("this is successFunction");
}).fail(errorFunction);
}
var getData = function() {
var str = 'ABCDEFGHIJ'
var returnStr = '';
for (var i = 0; i < 3000; i++) {
returnStr += str;
}
return returnStr;
}
describe('test ajax errors', function() {
it('tests a failed call due to huge payload ', function() {
var xhrObj = $.ajax({
url: 'https://jsonplaceholder.typicode.com/posts?userId=' + getData(),
async: false
});
console.log('Below is the xhr object that is breing returned via spy');
console.log(xhrObj);
spyOn(window, 'makeAPICall').and.returnValue(xhrObj);
spyOn(window, 'errorFunction').and.callThrough();
testFunction();
expect(window.errorFunction).toHaveBeenCalled();
});
it('tests a failed call due to some n/w error scenario (readyState->4, status->0)', function() {
var xhrObj = $.ajax({
url: '',
async: false
});
xhrObj.status = 0;
xhrObj.statusText = 'error';
console.log('Below is the xhr object that is breing returned via spy');
console.log(xhrObj);
spyOn(window, 'makeAPICall').and.returnValue(xhrObj);
spyOn(window, 'errorFunction').and.callThrough();
testFunction();
expect(window.errorFunction).toHaveBeenCalled();
});
it('tests a failed call (bad url pattern)', function() {
var xhrObj = $.ajax({
url: 'https://jsonplaceholder.typicode.com/postssss/1',
async: false
});
console.log('Below is the xhr object that is breing returned via spy');
console.log(xhrObj);
spyOn(window, 'makeAPICall').and.returnValue(xhrObj);
spyOn(window, 'errorFunction').and.callThrough();
testFunction();
expect(window.errorFunction).toHaveBeenCalled();
});
});
Notes:
errorFunction is the errorFunction that gets called from the done
if the readySatate === 4 && status === 0 and also all other
error/fail callbacks
makeAPICall returns the jqXHR on which done & fail callbacks are used.
getData is a utility function which generates a huge payload: used in test1
second test is about making a dummy ajax call with empty url so as to simulate readyState->4 & status->0
the third test simulates the usual bad request url pattern test.
for all the 3 scenarios, I've used a jqXHR object that I created by $.ajax({some params & async false})
setting async: false helps me to generate a dummy object and pass it over to the function via a spy

Synchronous call

I have a web application where my app's front-end uses angular js. I have a method in the angular js controller which is called on-click of a button. And in that method, I call the angular service to send a request to the Java controller. The logic looks like this
var submitted= true;
submitted= sampleService.sampleMethod(sampleParam);
alert(submitted);
if(!submitted){
//some action
}
The service will return true if the request was successful, false if it failed.
The issue that i'm having is that I get the alert before the request is sent (the alert says undefined). So regardless the response, the if condition fails.
Is there a solution for this issue?
edit :
The sample method
this.sampleMethod = function (sampleServiceMethod, obj){
var modalInstance = $modal.open({
templateUrl: 'example.html',
controller: 'anotherController',
resolve: {
modalServiceMethod: function () {
return sampleServiceMethod;
}
}
});
modalInstance.result.then(function (modalServiceMethod) {
modalServiceMethod(obj).then(function(response){
$window.location = response.sampleUrl;
}, function(err) {
$log.error("error occured", err);
});
}, function () {
$log.debug('error on: ' + new Date());
}).finally(function() {
return false;
});
};
Basically if the request is successful, the page will be redirected. But I need the return value of false during failure to make necessary changes.
** Update #1 **
I could help better if you organize and rename the code a bit. Hard to follow with all the names when they don't mean anything.
As a first thing try adding this return statement before the modalInstance::
return modalInstance.result.then(function (modalServiceMethod) {
modalServiceMethod(obj).then(function(response){
$window.location = response.sampleUrl;
}, function(err) {
$log.error("error occured", err);
});
}, function () {
$log.debug('error on: ' + new Date());
}).finally(function() {
return false;
});
** Original answer **
Why do you declare submitted as true and then run run the function on it?
It looks like this function:
sampleService.sampleMethod(sampleParam);
return undefined.
Add the code of that function so we can look into it.
If that function sends a request to the server to fetch data, it will be returned as a promise. in that case your code should be:
sampleService.sampleMethod(sampleParam).then(function(response){
submitted = response.data;
conole.log(submitted)
});
but the fact you get undefined in your alert and not a promise object, indicates you probably missed a "return" statement in your sampleService.sampleMethod(sampleParam) method. that method should look something like this:
function sampleMethod(param) {
return $http.get('url', param)
}

Prototype validation not working correctly

I use Prototype.js to validate a form. For one of the fields, I have the prototype script ajax a request to a file. The file is a simple PHP file and will return '1' if the value is OK and '0' if the value is not OK. I have the script as below, which should work perfectly. The prototype validation is supposed to show a validation error message when a field does not pass validation, and not display / remove the message once the field passes validation. But in this case, even when the ajax file returns '1', the validation will display the error message anyway. Anyone able to help would be greatly appreciated!
['validate-number3', numessage3, function(v) {
new Ajax.Request('test.php?nr='+v, {
method:'get',
onSuccess: function(transport) {
var response = transport.responseText;
if(response == '1'){return true;}else{return false};
}
});
}],
the return value from Ajax.Request is the Ajax.Request object and returns as soon as the request is setup - the onsuccess callback is called after the request has been completed - so checking the results of Ajax.Request is not useful for what you want to accomplish.
The reason that this doesn't work as you expect, this is an asynchronous call which means it will start the call and then return control to the script while it is processing and then run the callbacks when it is completed.
Try it this way
new Ajax.Request('test.php?nr='+v, {
method:'get',
onSuccess: handleResponse
});
function handleResponse( transport ){
var response = transport.responseText;
if(response == '1'){
//everything is OK
}else{
//value is not OK
};
}
I was able to solve my question!
Thanks to this teriffic page: http://inchoo.net/ecommerce/magento/magento-frontend/magento-form-field-ajax-validation/ it was no problem. This is what I ended up with:
var ok = false;
new Ajax.Request('test.php?nr='+v, {
method:'get',
asynchronous: false,
onSuccess: function(transport) {
var response = transport.responseText;
if(response == '1'){ok = true;}else{ok = false;};
},
onComplete: function() {
if ($('advice-validate-number-pay_bank_no')) {
$('advice-validate-number-pay_bank_no').remove();
}
}
});
return ok;

Categories