Mocking jQuery to test basic use - javascript

I'm having a very hard time understanding how to setup an object that allows me to test my jQuery calls. I don't need to mock any Async calls or anything, just basic use. So let me set out my function that I want to test (truncated for simplicity):
listGamesCallback : function(data) {
var gameList = $("#gameList select");
gameList.empty();
$.each(data, function() {
var newOption = $('<option>', {value : this.gameId });
newOption.text(string);
newOption.data("isJoinable", isJoinable);
// Add it to the list
gameList.append(newOption);
});
}
I need to mock the jQuery here to unit test this method, but I'm unable to figure out how to do this in javascript. Even without jsMockito, I don't know how to create an object with the properties that jQuery has in this situation. Any help with this would be appreciated.
I am using jsTestDriver, jsHamcrest, jsMockito and jQuery. However a generalized approach to create a $ object that has these properties would be awesome as well. Thank you!
For those that asked, here is what I came up with that seemed to kinda work..but I don't understand why.
var saved$ = $;
var mockContruct = mockFunction();
var mockedGamelist = mock(jQuery);
var mockedOption = mock(jQuery);
mocked$ = (function() {
var test = function(name) {
var args = jQuery.makeArray(arguments);
return mockContruct.call(test, args);
};
$.extend(test, $);
// This is what confuses me. This worked, but it's wierd
// It allows me to use the regular jQuery functions like
// $.each, while returning mocked objects when selectors are used.
test.prototype.constructor = test;
return test;
})();
$ = mocked$;
when(mockContruct).call(anything(), hasItem(containsString("#gameList")))
.thenReturn(mockedGamelist);
when(mockContruct).call(anything(), hasItems(containsString("<option>"), both(object()).and(hasMember("value"))))
.thenReturn(mockedOption);
headerFunctions.listGamesCallback([ {
gameId : 1,
isWhitesTurn : false,
isGameOver : false,
whiteUserName : "foobar",
blackUserName : "barfoo"
} ]);
JsMockito.verify(mockedGamelist).empty();
JsMockito.verify(mockedGamelist).append(mockedOption);
$ = saved$;

Ok, here what I came up with that does the job with minimal setup. The .extend is completely necessary here so that the jQuery object is setup correctly. This allows you to mock the constructor to return mocked jQuery objects that you can use to run your tests on. As a spy, jQuery will work as expected in all situations except when you want it to do something else. Here it is:
TestCase("HeaderTest", {
testListGamesCallback : function () {
var saved$ = $;
$ = $.prototype.construct = jQuery.extend(spy(jQuery), jQuery);
var mockGameList = mock(jQuery);
when($)(containsString("#gameList")).thenReturn(mockGameList);
headerFunctions.listGamesCallback([ {
gameId : 1,
isWhitesTurn : false,
isGameOver : false,
whiteUserName : "foobar",
blackUserName : "barfoo"
} ]);
verify(mockGameList).empty();
verify(mockGameList).append(object());
$ = saved$;
}
});
The caveat to this solution is that mocking anything other than the constructor is a bit tricky. You will have to set each individual function that you want to mock, then program the behavior. So:
$.each = mockFunction();
when($.each)(...matchers...).thenReturn(...);
But it still allows for testing what you need to.

As an extension to alpian's answer, you can create DOM elements without having to add them to the page. Make your JS functions take the relevant elements as parameters:
listGamesCallback : function(data, gameListSelectElem) {
var gameList = $(gameListSelectElem);
...
and test them like so:
var fakeSelect = $('<select>'),
data = ...;
listGamesCallback(data, fakeSelect[0]);
equal(fakeSelect.find('option').length, 1, 'must have exactly 1 option');
...
The last line of code above is for qUnit. Take whatever you need, the point is to say you can pass a DOM element that was never added to the page and afterwards investigate that DOM element using jQuery to find whether it was manipulated right.

No sure if i understand what you mean but if you want to create 'data' for you example , this is the method i know:
var data = [ { id : 1 , name : 'foo' } , { id : 2, name : 'bar' ] ​
but - if you wanted to create a list of options, than you code needs a couple of fixes:
see http://jsfiddle.net/7MMap/
var data = [ { gameId : 1 , name : 'foo' ,isJoinable:true} , { gameId : 2, name : 'bar' ,isJoinable:false}]
listGamesCallback = function(data) {
var gameList = $("#gameList select")
.empty();
$.each(data, function(i,d) {
var newOption = $('<option>', {value : d.gameId })
.text(d.name)
.data("isJoinable", d.isJoinable);
// Add it to the list
gameList.append(newOption);
})
};
listGamesCallback(data);

Mocking jQuery is not what mocking is for. You should only ever be mocking your object's collaborators. jQuery is providing you with some utilities - it's not a collaborator and hence should not be mocked.
What you are collaborating with here is the DOM, or some intermediate object between your code and the DOM. data is a value object and can simply be created in your test as Avi suggests.
In my JS tests, i don't mock the DOM, i use the real DOM and am sure to tear down anything i created between tests and this seems to work pretty well.

Related

Select2: Tags not working properly with custom DataAdapter

I am using a custom dataAdapter and also trying to use the 'tags' attribute in Select2 jQuery plugin. But the exemple found in the documentation is not working at all, the 'tags' attribute is simply ignored (this is only happening when using a custom dataAdapter, otherwise it's working fine).
So this is not working:
$(".js-example-tags").select2({
tags: true
});
As a solution for this, I've found out we can use a decorator for Tags in the dataAdapter, and it really works! Problem is, it will always work. So if I have two 'select' tags in HTML, and I want one of them to have 'tags:true' and the other one to have 'tags:false', they'll both have tagging enabled because of this decorator. I've tried setting 'tags:false', but it's not working.
I'm thinking a solution would be in the dataAdapter, to create an if statement for the decorator, for it to be applied or not. But then the problem is that this specific code is executed only once, when the first 'select' is created.
So I'm thinking that if I use a dataAdapter for creating multiple selects, all of them will have the same decorators. And I don't think having multiple dataAdapters would be a solution for me.
So my question is, if I have multiple 'select' elements, how can I use different decorators applied for each of them? Also using the same dataAdapter?
I also have a JSFiddle for this:
Tags with dataAdapter
Thanks!
We have just run into this as well and I have been unable to find anything on how to "correctly" implement this. Feel there is a pattern or hook I'm missing that select2 should provide here.
I've come up with 2 ways of handling this.
1. Handle adding the decorators up front of calling select2
(function ($) {
var CustomDataAdapter = $.fn.select2.amd.require('select2/data/customDataAdapter');
var Utils = $.fn.select2.amd.require('select2/utils');
var Tags = $.fn.select2.amd.require('select2/data/tags');
var MinimumInputLength = $.fn.select2.amd.require('select2/data/minimumInputLength');
$.fn.mySelect2 = function(options) {
if (!options.dataAdapter && options.customDataAdapterOptions) {
options.dataAdapter = CustomDataAdapter;
if (options.minimumInputLength > 0) {
options.dataAdapter = Utils.Decorate(options.dataAdapter, MinimumInputLength);
}
if (options.tags) {
options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
}
}
return this.select2(options);
};
}(jQuery));
Usage changes to: $('selector').mySelect2(options);
2. Override the Defaults.apply method that handles this for built in dataAdapters
(function() {
var CustomDataAdapter = $.fn.select2.amd.require('select2/data/customDataAdapter');
var Utils = $.fn.select2.amd.require('select2/utils');
var Tags = $.fn.select2.amd.require('select2/data/tags');
var MinimumInputLength = $.fn.select2.amd.require('select2/data/minimumInputLength');
var baseApply = $.fn.select2.defaults.apply;
$.fn.select2.defaults.apply = function (options) {
if (!options.dataAdapter && options.customDataAdapterOptions) {
options.dataAdapter = CustomDataAdapter;
if (options.minimumInputLength > 0) {
options.dataAdapter = Utils.Decorate(options.dataAdapter, MinimumInputLength);
}
if (options.tags) {
options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
}
}
return baseApply.apply(this, arguments);
};
}());
Usage does not change: $('selector').select2(options);

Ember - Custom Computed Property to check if all dependent fields exists

I am creating a form and I am trying to find a simple, elegant way of handling to see if all inputs exist.
Form = Ember.Object.extend({
// section 1
name: null,
age: null,
isABoolean: null,
// section 2
job: null,
numberOfSiblings: null,
isComplete: Ember.computed.and('_isSection1Complete', '_isSection2Complete'),
_isSection1Complete: function() {
var isPresent = Ember.isPresent;
return isPresent(this.get('name')) && isPresent(this.get('age')) && isPresent(this.get('isABoolean'));
}.property('name', 'age', 'isABoolean'),
_isSection2Complete: function() {
var isPresent = Ember.isPresent;
return isPresent(this.get('job')) && isPresent(this.get('numberOfSiblings'));
}.property('job', 'numberOfSiblings')
});
However, this doesn't seem to scale. My actual application will have many sections (over 20 sections).
I am looking into trying to create a re-usable computed property that fits my needs. Take for example the code of what I am going for:
Form = Ember.Object.extend({
// properties...
isComplete: Ember.computed.and('_isSection1Complete', '_isSection2Complete'),
_isSection1Complete: Ember.computed.allPresent('name', 'age', 'isABoolean'),
_isSection2Complete: Ember.computed.allPresent('job', 'numberOfSiblings')
});
I feel that this is a common case, but I'm failing to find the correct computed properties on how to execute this, so I would like to make my own.
Two questions:
Where's the best place to define the custom computed property? Can I just attach a function to Ember.computed?
Is there an easier way to solve this? I feel like I'm overlooking something simple.
As for Question #1,
You can define a custom computed helper in the App namespace. In this example, I created a new computed helper called allPresent that checks each property passed in against Ember.isPresent.
App.computed = {
allPresent: function (propertyNames) {
// copy the array
var computedArgs = propertyNames.slice(0);
computedArgs.push(function () {
return propertyNames.map(function (propertyName) {
// get the value for each property name
return this.get(propertyName);
}, this).every(Ember.isPresent);
});
return Ember.computed.apply(Ember.computed, computedArgs);
}
};
It can be used like this, per your example code:
_isSection2Complete: App.computed.allPresent(['job', 'numberOfSiblings'])
I adapted this from the approach here: http://robots.thoughtbot.com/custom-ember-computed-properties
As for Question #2, I can't think of a simpler solution.
I had to make a minor adjustment to Evan's solution, but this works perfectly for anyone else that needs it:
App.computed = {
allPresent: function () {
var propertyNames = Array.prototype.slice.call(arguments, 0);
var computedArgs = propertyNames.slice(0); // copy the array
computedArgs.push(function () {
return propertyNames.map(function (propertyName) {
// get the value for each property name
return this.get(propertyName);
}, this).every(Ember.isPresent);
});
return Ember.computed.apply(Ember.computed, computedArgs);
}
};
This can now be used as such:
_isSection2Complete: App.computed.allPresent('job', 'numberOfSiblings')

Making call jquery object function called history

I have this code:
var history = {
stack : [],
counter : -1,
add : function(item){
this.stack[++this.counter] = item;
this.doSomethingWith(item);
// delete anything forward of the counter
this.stack.splice(this.counter+1);
},
undo : function(){
this.doSomethingWith(this.stack[--this.counter]);
},
redo : function(){
this.doSomethingWith(this.stack[++this.counter]);
},
doSomethingWith : function(item){
// show item
}
};
When I try use that on this way it's not working;
var item = $('#myDiv');
history.add(item);
I have this error:
Uncaught TypeError: undefined is not a function
How can I solve the problem? Thanks!
The problem is that you picked a bad name for your object. There is build in History object available as window.history. So rename yours to something else and it should work. For example:
var appHistory = {
stack : [],
counter : -1,
...
};
You could also fix it by wrapping your code into IIFE for example, so that history would become a local variable. But I would still recommend picking less confusing name.
It works well if you put this behind the other code:
var item = $('myDiv');
history.add(item);
If you put it in front the function is not yet defined.
Working example: http://jsfiddle.net/LqrpW/
$('myDiv') replace with $('.myDiv') or $('#myDiv')

Is there any way to use Jasmine default matchers within custom matchers?

I have a custom matcher in some Jasmine test specs of the form:
this.addMatchers({
checkContains: function(elem){
var found = false;
$.each( this.actual, function( actualItem ){
// Check if these objects contain the same properties.
found = found || actualItem.thing == elem;
});
return found;
}
});
Of course, actualItem.thing == elem doesn't actually compare object contents- I have to use one of the more complex solutions in Object comparison in JavaScript.
I can't help but notice, though, that Jasmine already has a nice object equality checker: expect(x).toEqual(y). Is there any way to use that within a custom matcher? Is there any general way to use matchers within custom matchers?
Yes, it is slightly hacky but entirely possible.
The first thing we need to do is make the Jasmine.Env class available. Personally I have done this in my SpecRunner.html since its already setup there anyway. On the load of my SpecRunner I have the following script that runs:
(function() {
var jasmineEnv = jasmine.getEnv();
jasmineEnv.updateInterval = 1000;
var trivialReporter = new jasmine.TrivialReporter();
jasmineEnv.addReporter(trivialReporter);
jasmineEnv.specFilter = function(spec) {
return trivialReporter.specFilter(spec);
};
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
execJasmine();
};
function execJasmine() {
jasmineEnv.execute();
};
})();
So after the execJasmine function declaration I push the jasmineEnv into the global namespace by adding this:
this.jasmineEnv = jasmineEnv;
Now, in any of my spec files I can access the jasmineEnv variable and that is what contains the matchers core code.
Looking at toEqual specifically, toEqual calls the jasmine.Env.prototype.equals_ function. This means that in your customMatcher you can do the following:
beforeEach(function(){
this.addMatchers({
isJasmineAwesome : function(expected){
return jasmineEnv.equals_(this.actual, expected);
}
});
});
Unfortunately, using this method will only give you access to the following methods:
compareObjects_
equals_
contains_
The rest of the matchers reside the jasmine.Matchers class but I have not been able to make that public yet. I hope this helps you out in someway or another

How to observe value changes in JS variables

Im wondering if someone might be able to help me with something that i think it fairly straight forward:
Essentially i want to extend the prototypes of all datatypes (including intrinsic types), to allow some kind of custom functions, consider:
var x = "some string";
var y = 101;
x = "some other value";
y++;
x.onChange();
y.onChange();
This is the basic idea im after, but really what i want is to actually have the onChange (in this example) to be different so a new function for the actual variable (rather than a stardard prototype extension), ie:
x.onChange = function() {
alert("x.onChange");
}
y.onChange = function() {
alert("y.onChange");
}
This doesnt seem to work but i must be missing something quite simple no? I mean surely i can extend all object and types and add on new functions... no?
Any help would be greatly appreciated!
I might be tempted to approach this not by trying to add methods to existing types, but to create an object that can wrap a primative type. I would call this "observing" a value, and might implement it something like this:
function observable(v){
this.value = v;
this.valueChangedCallback = null;
this.setValue = function(v){
if(this.value != v){
this.value = v;
this.raiseChangedEvent(v);
}
};
this.getValue = function(){
return this.value;
};
this.onChange = function(callback){
this.valueChangedCallback = callback;
};
this.raiseChangedEvent = function(v){
if(this.valueChangedCallback){
this.valueChangedCallback(v);
}
};
}
This can then be used to observe changes in any value (so long as that value is then changed only by methods on the observable class - a small detraction IMO).
Something like this would work with the above code:
var obs = new observable(123);
obs.onChange(function(v){
alert("value changed to: " + v);
});
// the onChange callback would be called after something like obs.setValue(456);
Live example here --> http://jsfiddle.net/MeAhz/
Extend the object prototype:
Object.prototype.foo = function() { alert('hello world'); };
var a = 1;
a.foo();
The standard DEPRECATED way : Object.observe()
The Object.observe() method was used for asynchronously observing the
changes to an object. It provided a stream of changes in the order in
which they occur. However, this API has been deprecated and removed
from browsers.
let myObservdObject = Object.observe( { a : 'foo' }, e=>console.log('change!', e) );
myObservdObject.a = 'bee';
// callback gets executed
// and prints 'changed! in console, with the change event data
But proxies arrived to the Standard (ES6) an Object.Observe became deprecated and, in consecuence, unsupported by the browsers.
Proxies are the new way to observe... but implement a generic observer requires a more complex implementation, in comparsion with the way Object.observe used to provide us.
Observe value changes with third party libraries
You can find arround many implementations based in proxies.
Some of them implement the Observer pattern, wich forces you to set or get the values using specific methods :
Observe :
https://www.npmjs.com/package/observe
// define your object
var object = {a:'bee'};
// generate an observer
var observer = observe(object);
// declare the onchange event handler
observer.on( 'change', change=> console.log(change) );
// ready!
// set the value of 'a' and see how the callback is executed...
observer.set('a', 'foo')
// get the new value
observer.get('a') // returns 'foo'
Other libraries instead, let you interact with your variables using a more natural way:
WatchJS :
https://github.com/melanke/Watch.JS/
// define your object
var object = {a:'bee'};
// generate an observer and declare de hadler
watch(object , "a" , e=>console.log(e) );
// ready!
// set the value of 'a' and see how the callback is executed...
object.a = 'foo';
// get the new value
object.a // returns 'foo'
My own apprach : deep-observer
All the implementaions have their own caveats, and none of them was working for my purposes, so i had to implement my own approach.
The result is a highly customizable Observer method with a really small footprint ( <100 bytes gziped)
Deep-observer : https://www.npmjs.com/package/deep-observer
// create an observable object
const myObserved = new Observer( { a : 'bee' } , e=>console.log(e) ),
// perform a modification
myObserved.a = 'foo';
// console : { action:'update', oldValue:'bee', object:{a:'foo'}, name:'a' }
myObserved.a; // returns 'foo'

Categories

Resources