I got a problem with mocking the location reload functionality within Jasmine. I tried several methods (method 1 , method 2) to mock any location reload events but with no luck.
My situation is the following thing. I have a rather simple function:
function TestCall(xhr) {
if (xhr === 401) {
location.reload();
}
}
I tried creating the following Jasmine tests:
it("FakeCall", function (){
spyOn(TestCall, 'reload').and.callFake(function(){});
TestCall(401);
expect(TestCall).toHaveBeenCalled(); // this should check if reload functionality have been called
});
I want to mock the location reload function but I have no clue why this does not work. Can anyone guide/tell me what I do wrong?
Total code:
describe("multiple scripts", function () {
describe("2# FakeCall", function() {
function TestCall(xhr) {
if (xhr === 401) {
location.reload();
}
}
it("2.1 # Reload", function (){
spyOn(location, 'reload');
TestCall(401);
expect(location.reload).toHaveBeenCalled();
});
});
});
When you use spyOn, you give object as the first argument and a name of its method (it is attribute of that object) as the second.
So instead of spyOn(TestCall, 'reload') use this spyOn(location, 'reload'). Now it should work.
In your case it could look like this
it("FakeCall", function (){
spyOn(location, 'reload');
TestCall(401);
expect(location.reload).toHaveBeenCalled();
});
Related
I use jquery-ujs for ajax requests (data-remote="true"). My problem is, that the first request is okay, and while the second is running, it breaks. Whenever I call in the events $('#modal').empty() or $('#modal').text('') or $('#modal').html(''), no more events are going to be called.
To be clear, here's the code:
$(document).on('ajax:beforeSend', function () {
console.log('beforeSend');
$('#modal').empty().modal('show');
});
$(document).on('ajax:send', function () {
console.log('send');
});
$(document).on('ajax:success', function (e, xhr) {
console.log('success');
$('#modal').html(xhr).drags({ handle: '.modal-header' }).modal('show');
if (typeof doWork === 'function') {
doWork();
}
});
$(document).on('ajax:complete', function () {
console.log('complete');
});
And the console output:
beforeSend
send
success
complete
beforeSend
If I move $('#modal').empty().modal('show'); to the send event, then it is going to be called, but the success method is never called anymore (neither error neither complete, nothing more).
This problem annoys me for more hours now... Thank you for your help.
How about to move empty() to the ajax:complete?
In this case, when your modal closes, it opens empty for the next use and is ready for reuse.
I suggest that you put this command in the last step so that it does not cause any problems. Like the following:
$(document).on('ajax:complete', function () { console.log('complete'); . . . . $('#modal').empty(); });
I believe that this is not the best solution and there are definitely other solutions. But I hope this solution will solve your problem.
$('#modal').on('hidden.bs.modal', function () {
$(this).empty()
})
My workaround was to avoid using jquery-ujs events and using global ajax events, so my final working code is:
$(document).ajaxSend(function () {
$('#modal').empty().modal('show');
});
$(document).ajaxSuccess(function (e, xhr) {
$('#modal').html(xhr.responseText).drags({ handle: '.modal-header' }).modal('show');
if (typeof doWork === 'function') {
doWork();
}
});
I have this code for make a Ajax call:
$.post($form.attr('action'), $form.serialize(), 'json').done(function (data, textStatus, jqXHR) {
// Some logic here for done callback
// Redirect to another URL after 5 seconds
window.setTimeout(function () {
window.location = data.redirect_to;
}, 5000);
}).fail(function () {
// Some logic here for fail callback
})
That code works fine. Now I need to attach a "loader" to this Ajax call. I knew that I can attach this to $(document) as of jQuery 1.8+ docs expose (also found the same on this topic) so I have done the following:
$(function () {
// a bunch of logic here
}).ajaxStart(function () {
$('#loaderModal').modal('show');
$('#loaderModal').on('show.bs.modal', function (e) {
$('#loaderDiv').loader();
})
}).ajaxStop(function () {
$('#loaderModal').modal('hide');
$('#loaderModal').on('hidden.bs.modal', function (e) {
$('#loaderDiv').loader('destroy');
})
});
And once again that works but happens that I have other Ajax calls on the page (for validation purposes on some fields) and every time any of them run then the loader is show which is not correct since I need to attach only to the code shown above (the first piece of code). How I can accomplish this?
As a second part, an related to this post, some of the logic on done and fail callback trigger a Bootstrap Growl message and as you must have noticed I'm using the Twitter Bootstrap Modal component in which the loader is contained, so I'm fear that the call made to run the loader block in some way the second call when the callback for done is invoked, is that even possible? how fast is the execution of these events?
PS: If any of yours have a better solution to this then say and thanks in advance
Instead of using the global event handlers, show/hide the loader for the current ajax request like
//show the loader before the request
showLoader();
$.post($form.attr('action'), $form.serialize(), 'json').done(function (data, textStatus, jqXHR) {
// Some logic here for done callback
// Redirect to another URL after 5 seconds
window.setTimeout(function () {
window.location = data.redirect_to;
}, 5000);
}).fail(function () {
// Some logic here for fail callback
}).always(hideLoader) //hide the loader after the ajax request
function showLoader() {
$('#loaderModal').modal('show');
$('#loaderModal').on('show.bs.modal', function (e) {
$('#loaderDiv').loader();
})
}
function hideLoader() {
$('#loaderModal').modal('hide');
$('#loaderModal').on('hidden.bs.modal', function (e) {
$('#loaderDiv').loader('destroy');
})
}
Why not put your loading scripts into a function so you can call them at will? That way they fit into the logic of your code instead of running every time.
function startLoad() {
.ajaxStart(function () {
$('#loaderModal').modal('show');
$('#loaderModal').on('show.bs.modal', function (e) {
$('#loaderDiv').loader();
})
})
}
function stopLoad() {
.ajaxStop(function () {
$('#loaderModal').modal('hide');
$('#loaderModal').on('hidden.bs.modal', function (e) {
$('#loaderDiv').loader('destroy');
})
});
}
I've found a lot of questions about deferring, promises, running javascript synchronously, etc. and I've tried numerous things already but still can't get this to work.
Edit Here's a little more explanation on the problem. fetchData has a routine that depends on all the code inside showStuff being complete. In particular, there's divs that get created using percentage of screen size, and we need to get the height of those divs so we can draw gauges inside them. fetchData is running before slideDown() is complete. Please see the additional console.log code I've added directly below.
My button onClick() calls showOverlay().
function showOverlay() {
showStuff().promise().done( function() {
console.log($("#gauge1").height()); //returns -0.5625 or something close
fetchData(); //ajax call
});
}
function showStuff() {
$("#overlay").fadeIn(200);
$("#gauges").slideDown(800);
$(".gauge").each(function() {
$( this ).show(); //unhides #gauge1 div
});
}
The error I'm getting says: cannot call method 'promise' of undefined.
I'm not showing my fetchData() function but it basically uses ajax to call a web service and then creates gauges on the screen using Raphael. If fetchData runs before the animations are complete the gauges are not displayed correctly because their size is relative to the .gauge div's.
Edit1
Neither of the examples below work. They both run without errors but return too quickly.
function showOverlay() {
showStuff().promise().done(function() {
fetchData();
});
}
function showStuff() {
var def = $.Deferred();
$("#overlay").fadeIn(200);
$("#gauges").slideDown(800);
$(".gauge").each(function() {
$( this ).show();
});
def.resolve();
return def;
}
Doesn't work either:
function showOverlay() {
$.when(showStuff()).done(function() {
fetchData();
});
}
function showStuff() {
$("#overlay").fadeIn(200);
$("#gauges").slideDown(800);
$(".gauge").each(function() {
$( this ).show();
});
}
You've 2 issues, the deferred and thats not how you run animations one after the other.
This will get you part of the way:
function showStuff() {
var deferred = $.Deferred();
$("#overlay").fadeIn(300,function(){
$("#gauges").slideDown(800,function(){
$(".gauge").show(); //doing this one after another takes more code.
deferred.resolve();
});
});
return deferred;
}
Heres the codepen: http://codepen.io/krismeister/pen/pvgKj
If you need to do sophisticated animations like this. You might find better results with GSAP.
Heres how to stagger:
http://www.greensock.com/jump-start-js/#stagger
Try to use $.when() instead:
$.when(showStuff()).done(function() {
fetchData();
});
You a) need to return something from showStuff b) should return a promise directly, so that the .promise() method is unnecessary:
function showOverlay() {
showStuff().done(function() {
fetchData();
});
}
function showStuff() {
return $("#overlay").fadeIn(200).promise().then(function() {
return $("#gauges").slideDown(800).promise();
}).then(function() {
return $(".gauge").show().promise();
});
}
In my angular code, I load some data in ajax which fill my template, so far so good...
Then, once the data are loaded, I want to call a javascript function.
I couldn't find a way to have callback once the template is rendered. I have try several events but none are working.
My solution is to call the javascript method after a timeout:
$http.post('url').success(function (data) {
$timeout(function () {/* process data */ }, 200);
});
It seems to work, but that's a timeout, nothing guarantee me that at the end of the timeout everything is rendered. Maybe it is working only because my PC is fast...
Is there an event based solution? Or any solution better than this one...
The jsfiddle of my solution : http://jsfiddle.net/ZCT4K/5/
You need to $watch() your data. So you could try the following:
$http.post('url')
.success(function (data) {
$scope.postData = data;
});
//Watch the value of postData
$scope.$watch('postData', function () {
if ($scope.postData === undefined || $scope.postData === null || $scope.postData === "") {
return;
}
/*Else process your data*/
});
I have a big problem writing a small piece of code using JS/jQuery (don't know which of these is causing the problem). Anyhow, here we go:
$('#themePicker').unbind().click(function() {
var t = $(this);
modalwindow2(t, function() {
console.log(1);
}, function(w) {
console.log(w);
});
return false;
});
and the function itself:
function modalwindow2(w, callbackOnSHow, callbackOnHide) {
if (typeof(callbackOnSHow) == 'function') {
callbackOnSHow.call();
}
// do some stuff //
$('form').submit(function() {
ajaxSubmit(function(data) {
if (typeof(callbackOnHide) == 'function') {
console.log('---------------');
console.log(data);
console.log('---------------');
callbackOnHide.call(data);
}
});
return false
});
}
The function is called modalwindow2 and I want to call a function when the modal is shown and another function when the modal will be hidden.
The first is not a problem.
The second… Well… Let's just say it's a problem. Why?
I want a parameter sent to the second function. The paramenter is an ajax response, similar to other jQuery stuff (ajax action, sortable, etc).
I hope I made myself clear enough.
Thanks!
Edit:
I'm using jQuery 1.1.2 (or 1.1.3) and upgrading or using jQuery UI is NOT a solution. I have some dependencies (interface is one of them) and i don't have enough time (nor motivation) to upgrade to 1.3 & UI 1.7.
I noticed that you have a typo on .submit:
$('form').submti(function(){
Was that just an entry error to SO?
EDIT:
Ok, so after looking at your code and doing a short test, I've come up with this (pardon the pun):
function modalwindow2(w, callbackOnShow, callbackOnHide) {
if(typeof callbackOnShow == 'function') {
callbackOnShow.call();
}
$('form').submit(function() {
if(typeof callbackOnHide == 'function') {
callbackOnHide.call(this, "second");
}
});
}
$(document).ready(function (){
$('#themePicker').click(function(){
var t=$(this);
modalwindow2(t, function() { alert("first"); }, function(x) { alert(x); });
return false;
});
});
It looks like you may have just been missing the "this" in your call() statement. Try using callbackOnHide.call(this, data);
Let me know if that works any better!
I understand what you are trying to do, but you will need to store the newly created window so that you can access it on the close callback function.
You might want to look at jQuery UI Dialog. It provides some really basic functionality for dialog windows (modal and otherwise) and handles some of the callback implementation.