I'm using https://github.com/danialfarid/angular-file-upload for my file uploads. It provides a progress method that is called, when the xhr requests receives the progress event. This is from the source code of angular-file-upload:
xhr.upload.addEventListener('progress', function(e) {
deferred.notify(e);
}, false);
My problem is now, how should I test this using $httpBackend? I can test the success and error methods with
$httpBackend.expectPOST("http://localhost:9001/").respond('ok');
$httpBackend.expectPOST("http://localhost:9001/").respond(500, 'some error');
but I can't get the notify of the promise to fire. Is there a way to do this?
EDIT
The part that I want to test, is inside the progress method:
$upload.http({url: url, method: 'POST', data: file})
.progress(function(evt) {
// here is more code, that needs to be tested
self.$deferreds.upload.notify((100.0 * evt.loaded / evt.total).toFixed(2));
}).success(function(data, status, headers, config) {
self.$deferreds.upload.resolve(data);
}).error(function(response) {
self.$deferreds.upload.reject(response);
});
You want to spy on the $upload.http method and return a mocked object that lets you register progress, success and error callbacks.
spyOn($upload, 'http').andReturn({
progress: function (progressCallback) {
this.progressCallback = progressCallback;
},
success: function (errorCallback) {
this.errorCallback = errorCallback;
},
error: function (errorCallback) {
this.errorCallback = errorCallback;
}
});
Then you can call these callbacks synchronously in the tests:
it('should do something', function () {
$upload.progressCallback();
// or $upload.successCallback();
// or $upload.errorCallback();
expect('this').toBe('that');
});
We are speaking about unit-tests?
Why do you need to test 3rd-party at all?
Just mock this all part.
Probably I did not get the idea of the question, so need more code that should be covered by tests.
Related
I'm having some trouble handling multiple jquery ajax call, there are timeout error, but how do I know which request error this is?
$.when(ajaxDeferred1, ajaxDeferred2)
.done(function(result1, result2) {})
.fail(function(response, statusText) {
// how do I know which(ajaxDeferred1 or ajaxDeferred2) ajax call error this is?
});
ajaxDeferred1
$.ajax({
method: 'GET',
url: xxx,
timeout: 5000,
});
I know I can handle error by putting '.fail()' on each request, but is there any way handle error above way?
Going further, can I handle like this?
$.when(ajaxDeferred1, ajaxDeferred2)
.done(function(result1, result2) {})
.fail(function(errorForRequest1, errorForRequest2) {
// handle error
});
EDIT:
I think I need to make my question more clear. In the above example:
$.when(ajaxDeferred1, ajaxDeferred2)
.done(function(result1, result2) {})
.fail(function(errorForRequest1, errorForRequest2) {
// if all request has done, but one or more request has got error, return the error here then!
// So I can handle which part UI should show and which UI part I should hide due to the error!
});
I want to get 'done' result when all is done without error, and get 'error' when all is done but one or more request has encountered problem and return them all together into '.fail()'.
You can use .always() and within it iterate over each promise and check whether it resolved or failed.
var promises = [ajaxDeferred1, ajaxDeferred2]
$.when.apply($, promises)
.always(function() {
$.each(promises, function(i) {
this.done(function(result) {
console.log('promise', i, 'resolved');
}).fail(function(error) {
console.log('promise', i, 'failed');
});
});
});
JSFiddle demo: https://jsfiddle.net/pr45eov1/3/
I like #Miguel Mota's answer. But as an alternative, you are going to get your deferred object back if it fails. So you can add some data to it:
var dfd1 = jQuery.ajax({
method: 'GET',
url: 'some request that fails first',
timeout: 1
});
dfd1.id = 1;
var dfd2 = jQuery.ajax({
method: 'GET',
url: 'some request that fails second',
timeout: 1000
});
dfd2.id = 2;
jQuery.when(dfd1, dfd2)
.done(function () { })
.fail(function (request) {
// id: 1
console.log('id: ' + request.id);
});
i have an ember service that provide ajax post
import Ember from 'ember';
import ENV from '../config/environment';
export default Ember.Service.extend({
save(data) {
return new Ember.RSVP.Promise(
function (resolve, reject) {
function progress(e){
if(e.lengthComputable){
let max = e.total;
let current = e.loaded;
let Percentage = (current * 100)/max;
console.log(Percentage);
if(Percentage >= 100)
{
// process completed
}
}
}
Ember.$.ajax({
type: 'POST',
enctype: 'multipart/form-data',
url: ENV.APP.API_HOST + '/profile/lampiran-desas',
processData: false,
contentType: false,
cache: false,
data: data,
xhr: function() {
let myXhr = $.ajaxSettings.xhr();
if(myXhr.upload){
myXhr.upload.addEventListener('progress',progress, false);
}
return myXhr;
},
success: function (response) {
resolve(response);
},
error: function (error) {
reject(error);
}
});
});
}
});
the success response return normally in my component. But how to get my function progress return. so i can make a loading progress bar on my template..?
thanks..
As far as I see, you are making a remote call in a service and you desire to display the progress within a component. In order to do that, you need to save the relevant data within the service itself; and display the progress in component.
Here is the twiddle I have prepared for you to illustrate the case. mock-remote-call-service is a mock service that updates process 10% every 200 ms. The result is directly saved to the service itself. progress-displayer-component has the responsibility to display the progress, start the remote call task and even displaying the returned data.
This was just a mock example to explain my point. I hope this helps.
Even though i am able to pass promise as parameter into error function, it does not trigger then & error function on the caller.?
if i change the code to put the AJAXError function inline, everything works fine.
Is it possible to pass around the promise around as parameter and still be able to trigger back then and fail on the caller up in the chain.
NOTE : promise is returned from palletizerService.addSerialNumberToPallet but but resolved/rejected in a different function AjaxFailed.
palletizerService.addSerialNumberToPallet ('p1','s1')
.then( function (response){
console.log('success');
},
function(error){
console.error(error);
});
Hereunder is the service that i am invoking from the above code
palletizerService.addSerialNumberToPallet = function (palletID, serialNumber) {
var deferred = $q.defer();
$.ajax({
url: url,
type: "GET",
dataType: "json",
methodName: methodName,
deferred: deferred.promise,
data: "palletID=" + palletID + "&serialNumber=" + serialNumber,
timeout: 20000, // 20 seconds for getting result, otherwise error.
error: AjaxFailed,
complete: function (jqXHR, textStatus) {
console.log("Ajax-finally : " + methodName);
},
beforeSend: function (qXHR, settings) {
},
success: function (data, textStatus, jqXHR) {
console.log("Ajax-Sucess " + methodName);
deferred.resolve(data);
}
});
return deferred.promise;
};
This is the external function
function AjaxFailed(qXHR, textStatus, errorThrown) {
try {
console.error('Ajax-Error in ' + methodName, qXHR.status, qXHR.responseText);
} catch (e) {
console.log("LocalMethod-Error in " + methodName);
console.error(e);
}
finally {
this.deferred.reject(qXHR);
}
}
Your problem is that you are passing the promise as the .deferred property of your options object. You cannot resolve/reject promises, they don't have a .reject method (and your code should throw when trying to call it). You would instead need to pass the deferred object you are holding.
However, this is a bad idea anyway. As jQuery.ajax does already return a promise, you should simply assimilate it using $q.when:
function (palletID, serialNumber) {
return $q.when($.ajax({
…
}));
}
(you will be able to omit all success and errror handlers, angular-promise does take care of them; if you want to add transformations or logging do those on the promise).
Yet that's still not optimal, in fact there hardly is a reason to use jQuery.ajax at all. Just use Angular's $http service, which also returns an angular promise right away.
I am using Jasmine and Sinon to test an ajax call. The test looks like this:
describe("function initialize", function(){
var server;
beforeEach(function(){
server = sinon.fakeServer.create();
});
afterEach(function() {
server.restore();
});
it ("should make an ajax call successfully", function(){
server.respondWith(session.apipath + "/login/refreshToken/",
[200, { 'Content-Type': 'application/json' },
'{user:"asdf",loginToken:"bla"}']);
session.initialize();
server.respond();
expect(localStorage.getItem("loginToken")).toBe("bla");
});
});
I want it to call the success function.
$.ajax(this.apipath + "/login/refreshToken/", {
success: function (response) {
alert("success!!!");
localStorage.setItem("loginToken", response.loginToken);
},
error: function(){
alert("error!!!");
}
});
But only error is called. What am I doing wrong?
You can try this. (Although this is late, I still want to suggest you.)
'{user:"asdf",loginToken:"bla"}' -> '{"user":"asdf", "loginToken":"bla"}'
If you want to know why, read this.
I have a custom sync method in my backbone.js app. All my models call this method, but since I override success in this method, my success methods from the individual models are no longer being called. Here's what I mean - Below is my custom sync method:
app.customSync = function(method, model, options) {
var success = options.success,
error = options.error,
customSuccess = function(resp, status, xhr) {
//call original, trigger custom event
if(con)console.log('in custom success');
success(resp, status, xhr);
},
customError = function(resp, status, xhr) {
if(con)console.log('in custom error');
error(resp, status, xhr);
};
options.success = customSuccess;
options.error = customError;
Backbone.sync(method, model, options);
};
Backbone.Model.prototype.sync = app.customSync;
Here is an example me trying to call success from a model save:
this.model.save({
success:function(model, response){
if(con)console.log('this is never called');
}
});
Does anyone know how I can still the custom sync with the custom success methods, and call success from my individual saves?
As a side note, I tried calling success msuccess in the model.save, but the msuccess was undefined in the custom sync.
The first argument to Model.save is a hash of attributes you wish to modify, the options come second and hold the success/error callbacks.
Try
this.model.save({}, {
success: function() {
console.log('save success');
}
});
And a Fiddle to see this at work http://jsfiddle.net/nikoshr/XwfTB/