I want to write unit tests with QUnit and Sinon.Js. I have an application, where the user can click on a button and a modal dialog appers to handle downloading some files. The user can close the dialog and it triggers a method to run to reset some variables. My test code:
$(function() {
$.fn.copy_button = function(){};
ln_download_view = new DownloadModalView();
ln_download_view.modal = {'modal': function() {}};
var download_modal_dialog = $('.download-modal');
download_modal_dialog.modal = function(param){};
var modal_mock = sinon.mock(ln_download_view.modal);
var download_modal_dialog_mock = sinon.mock(download_modal_dialog);
//Should be inserted, because ln_download_view.modal is mocked
//The close button even handler
$('#btn_close_modal').click(function(){
download_modal_dialog.modal('hide');
});
//Dirty stuff to do after the window closes
//Basicly the click triggers this event handler
$('.download-modal').on('hide',function() {
window.clearInterval(window.periodicalTimer);
});
$('div .option-container').click(function() {
if(!$(this).hasClass("selected-option"))
{
$('div #option-presenting').toggleClass("selected-option");
$('div #option-editing-and-presenting').toggleClass("selected-option");
$('.image').toggle();
}
});
module("views");
test("Download modal dialog is displayed", function(){
var modal_triggered = modal_mock.expects("modal").once();
ln_download_view.handleDownloadClick();
ok(modal_triggered.verify());
});
test("Download modal dialog is closed",function(){
var modal_triggered = download_modal_dialog_mock.expects("modal");
$('#btn_close_modal').trigger('click');
ok(modal_triggered.verify());
});
});
What I do not understand is, how can I test/mock/stub this piece of code:
$('.download-modal').on('hide',function() {
window.clearInterval(window.periodicalTimer);
});
I do not have the deep understanding yet.
You can't mock/stub an anonymous function. But you can make a refactoring and stub/mock the named callback.
$('.download-modal').on('hide', onHide);
var onHide = function() {
window.clearInterval(window.periodicalTimer);
};
// ...
sinon.stub(onHide);
Here's my method for this:
In your before each, make a function that doesn't do anything:
var doNothing = function(){};
Then in your test, spy on that:
var spy = sinon.spy(this, 'doNothing');
Then call your method, passing in a callback that fires the doNothing method:
var self = this;
whatever.doSomethingAwesome(
{
finished: function(){
self.doNothing();
}
});
expect(spy.callCount).toEqual(1);
Related
How can I ask mocha/chai to wait for a modal to be shown before it expects the test to be complete?
I am wrapping bootstrap modal and emitting my own events. show works fine, but I need to wait for the modal to be shown for my next test. I can't get done() to work at all.
describe("Modal", function() {
describe("wrapModalEvents", function() {
it("should wrap the show.bs.modal event", function() {
var m = new Modal();
m.wrapModalEvents();
var res = {e:1};
m.on('show', function(){
res.e = 555;
});
m.modal();
expect(res.e).to.equal(555);
});
it("should wrap the shown.bs.modal event", function() {
var m = new Modal();
m.wrapModalEvents();
var res = {e:1};
m.on('shown', function(){
res.e = 123;
});
// need to wait at least one second
expect(res.e).to.equal(123);
});
});
});
I'm trying to create a method that will copy the HTML from another part of the page and then set a click handler for the elements inside of it. Problem I'm having is that the handler is not firing when clicked.
Its funny because when I set the handler as an anonymous function, it fires when clicked, but when I set it to either self.initSizeSZ or myModule.initSizeSZ it won't fire.
// Elements are cached for performance.
var $selectSizes = $('#someSelector');
var $catalogWheels = $('#someOtherSelector');
var myModule = {
// This is the module that calls renderSizeSZ
loadWheels: function(){
if(!this.loaded){
this.renderSizesSZ();
this.loaded = true;
}
},
renderSizesSZ: function(){
var self = this;
var $clone = $selectSizes.clone(false);
var html = $selectSizes.html();
$catalogWheels
.find('#icf_catalog-sizes')
.html(html)
.find('li a')
// if this is set as an anonymous function, it will fire, but as a named function it won't
.click(self.initSizeSZ);
},
initSizeSZ: function(event){
console.log('firing')
}
}
var tap = {tap:"tap tap tap",
trrap : function (){
alert(this.tap);
}
};
var tab = {tap:"tab tab tab",
trrab: function (){tap.trrap.bind(this);};
};
tab.trrab();
I want to pass tab object to a function in tap object,it would be like alert(tab.tap); . Both of it have tap property.
You never called the bound function. bind returns a function it does not call it.
var tap = {
tap:"tap tap tap",
trrap : function (){
alert(this.tap);
}
};
var tab = {
tap:"tab tab tab",
trrab: function (){
tap.trrap.bind(this)();
}
};
tab.trrab();
http://jsfiddle.net/hqEXs/
Also you could use call to set the context and call it immediately.
tap.trrap.call(this);
or apply
tap.trrap.apply(this);
I have built a dom object Engine that has private/public fields/methods that I have simplified below:
function Engine(args){
this.display = args.display;
this.getDisplay = function(){return this.display;}
this.alertMsg = function(msg){
console.log(this.display);
alert(msg);
}
}
What I would like to do is build a custom event that would be triggered after the alert(msg) such as $(this.display).trigger("afterAlert");
function Engine(args){
this.display = args.display;
this.getDisplay = function(){return this.display;}
this.alertMsg = function(msg){
console.log(this.display);
alert(msg);
// trigger custom event here
$(this.display).trigger("afterAlert");
}
}
Now, this event could be empty or not. How would one or more objects declared later register to the "afterAlert" event? In my case, additional javascript files are loaded by the main file dynamically and could contain a code ressembling :
function new_obj(){
bind("afterAlert", function(){
alert("alert was called");
});
}
See my answer from this question...repeated for clarity
I will tackle the register, triggering and unbinding of custom events.
jQuery has all the tools you need to register, bind and unbind to custom events.
Below is an example of hooking up two divs to a custom event called customAjaxStart. I can then trigger this function and both handlers will get called.
Quick Demo Here - Have the firebug/ie8 console enabled.
e.g
$( function() {
$('#div1').bind('customAjaxStart', function(){
console.log('#div1 ajax start fired');
$(this).fadeTo('slow', 0.3);
});
$('#div2').bind('customAjaxStart', function(){
console.log('#div1 ajax start fired');
$(this).fadeTo('slow', 0.3);
});
//fire the custom event
$.event.trigger('customAjaxStart');
//unbind div1 from custom event
$('#div1').unbind('customAjaxStart');
//again trigger custom event - div1 handler will not fire this time
$.event.trigger('customAjaxStart');
});
Taking the above as an example I would trigger the customAjaxStart from the global ajaxStart. Any listeners would be triggered automatically whenever an xhr call is about to be made (ideal for disabling your widgets or showing a loading gif etc) e.g
$.ajaxStart( function(){
$.event.trigger('customAjaxStart');
});
I think what you are looking for is the Observer pattern. At least that's how I would implement it. The following code snippet uses different names but it does essentially what you want (allows registering for events, and even triggering):
Observable = {
addObserver: function(observer) {
if (!this.__observers) this.__observers = [];
this.__observers.push(observer);
},
addGlobalObserver: function(observer) {
if (!this.__global_observers) this.__global_observers = [];
this.__global_observers.push(observer);
},
removeObserver: function(observer) {
var newObservers = [];
var co;
while (co = this.__observers.pop()) {
if (co != observer) newObservers.push(co)
}
this.__observers = newObservers;
newObservers = [];
while (co = this.__global_observers.pop()) {
if (co != observer) newObservers.push(co)
}
this.__global_observers = newObservers;
},
notify: function(event) {
var allObservers = this.__global_observers.concat(this.__observers);
for (var i=0; i < allObservers.length; i++) {
var o = allObservers[i];
if (o[event]) {
var args = []
for (var j=1; j < arguments.length; j++) {
args.push(arguments[j])
};
o[event].apply(this, args);
}
};
},
__global_observers: [],
__initializer: function() {
this.__observers = [];
}
};
If you include this code into your class, you can register for events using addObserver() (addGlobalObserver() for "class level" events). Inside the object you trigger an event using notify().
Code taken from Coltrane.
when using the ckeditor link dialog, I have custom code for some extra options. I would also like to grab the selected text to use - so I have called:
selectedContents = CKEDITOR.instances['my_editor'].getSelection().getSelectedText();
I want this to happen when the dialog is loaded. So I wrote an "onShow()" handler function... but that messes up the customizations that I have made to the dialog. I'm guessing that my onShow is grabbing the normal process for that event - how can I continue with the normal processing at that point?
dialogDefinition.onShow = function(evt)
{
contents = CKEDITOR.instances['my_editor'].getSelection().getSelectedText();
// now here, continue as you were...
}
Ok, I still have some issues, but the answer to this question is to grab the existing "onShow" handler before overwriting it. Use a global, then it can be called within the new handler:
var dialogDefinition = ev.data.definition;
var oldOnShow = dialogDefinition.onShow;
dialogDefinition.onShow = function(evt) {
// do some stuff
// do some more stuff
// call old function
oldOnShow();
}
Depending on Andy Wallace code:
var oldOnShow = dialogDefinition.onShow;
var newOnShow = function () {
//your code
}
and then:
dialogDefinition.onShow = function(){
oldOnShow.call(this, arguments);
newOnShow.call(this, arguments);
}
It helps me!
Correct syntax is:
/* if new picture, then open the Upload tab */
CKEDITOR.on('dialogDefinition', function(ev) {
var dialogName = ev.data.name;
var dialogDefinition = ev.data.definition;
var dialog = dialogDefinition.dialog;
if (dialogName == 'image2') {
dialogDefinition.onShow = CKEDITOR.tools.override(dialogDefinition.onShow, function(original) {
return function() {
original.call(this);
CKEDITOR.tools.setTimeout( function() {
if (dialog.getContentElement('info', 'src').getValue() == '') {
dialog.selectPage('Upload');
}
}, 0);
}
});
}
});