How can I unit test this knockoutjs binding where I call a certain function 'myValueAccessor' when the element is swiped on my touchpad?
I am also unsure what the unit should or is able to test at all here.
It would be ok for the first time to assert wether myValueAccessor is called.
But how can I trigger/imitate or should I say mock... the swiperight event?
ko.bindingHandlers.tap = {
'init': function (element, valueAccessor) {
var value = valueAccessor();
var hammertime1 = Hammer(element).on("swiperight", function (event) {
$(element).fadeOut('fast', function () {
value();
});
});
}
};
self.myValueAccessor = function () {
location.href = 'set a new url'
};
UPDATE
I have put here my unit test with mocha.js
I can outcomment the 'value()' in the binding but still the test succeeded thats odd.
Is it not correct to put this (as a test):
function (element,args) {
alert('assertion here');
}
as a 3rd parameter into the ko.test function?
ko.bindingHandlers.tap = {
'init': function (element, valueAccessor) {
var value = valueAccessor();
var hammertime1 = $(element).on("swiperight", function (event) {
$(element).fadeOut('fast', function () {
//value();
});
});
}
};
// Subscribe the swiperight event to the jquery on function
$.fn.on = function (event, callback) {
if (event === "swiperight") {
callback();
}
};
// Subscribe the fadeOut event to the jquery fadeOut function
$.fn.fadeOut = function (speed, callback) {
callback();
};
ko.test("div", {
tap: function () {
assert.ok(true, "It should call the accessor");
}
}, function () {
});
UPDATE:
custom.bindings.js:
define(['knockout','hammer'], function (ko,Hammer) {
return function Bindings() {
ko.bindingHandlers.tap = {
'init': function (element, valueAccessor) {
var value = valueAccessor();
var hammertime1 = Hammer(element).on("swiperight", function (event) {
$(element).fadeOut('fast', function () {
value();
});
});
}
};
};
});
unittest.js:
how can I connect this code to knockout in my test?
UPDATE
The Bindings is injected via require.js from my bindings.js file:
describe("When swiping left or right", function () {
it("then the accessor function should be called", function () {
ko.bindingHandlers.tap = new Bindings();
Hammer.Instance.prototype.on = function (event, callback) {
if (event === "swiperight") {
callback();
}
};
$.fn.fadeOut = function (speed, callback) {
callback();
};
var accessorCalled = false;
ko.test("div", {
tap: function () {
accessorCalled = true;
}
}, function () {
assert.ok(accessorCalled, "It should call the accessor");
});
});
});
bindings.js
define(['knockout','hammer'], function (ko,Hammer) {
return function () {
return {
'init': function (element, valueAccessor) {
var value = valueAccessor();
var hammertime1 = Hammer(element).on("swiperight", function (event) {
$(element).fadeOut('fast', function () {
value();
});
});
}
};
};
});
myviewmodel.js
...
ko.bindingHandlers.tap = new Bindings();
...
You can check my binding collection for how to test
https://github.com/AndersMalmgren/Knockout.Bindings/tree/master/test
This is my function that all my tests using
ko.test = function (tag, binding, test) {
var element = $("<" + tag + "/>");
element.appendTo("body");
ko.applyBindingsToNode(element[0], binding);
var args = {
clean: function () {
element.remove();
}
};
test(element, args);
if (!args.async) {
args.clean();
}
};
edit: Sorry forgot mocking, you just do
$.fn.on = function() {
}
I do not know what exact logic you want to test in that code since there hardly is any, but something like this
http://jsfiddle.net/Akqur/
edit:
May you get confused where i hook up your binding? Its done here
{
tap: function() {
ok(true, "It should call the accessor");
}
}
I tell ko to hook up a "tap" binding and instead of hooking in up to a observable I use a mocking function, when your custom bindnig calls value() from the fadeout function the test assert will fire
edit:
Maybe this approuch makes more sense to you?
http://jsfiddle.net/Akqur/5/
Note that it only works if your code is executed synchronous
edit:
Here i use the third argument of ko.test
http://jsfiddle.net/Akqur/8/
Related
I want to test an async function for its exception behavior and side effects.
abstract class Async {
int count();
Future<void> throwExceptionAfter(int sec);
}
class ImplAsync extends Async {
int _count = 0;
#override
int count() => _count;
#override
Future<void> throwExceptionAfter(int sec) async {
await Future.delayed(Duration(seconds: sec));
_count++;
throw Exception();
}
}
The test:
void main() {
Async impl;
setUp(() {
impl = ImplAsync();
});
group('throwExeptionAfter', () {
test('simple call with waiting', () async {
expect(impl.throwExceptionAfter(0), throwsException);
await Future.delayed(Duration(seconds: 1));
var count = impl.count();
expect(count, 1);
});
test('simple call', () async {
expect(impl.throwExceptionAfter(1), throwsException);
var count = impl.count();
expect(count, 1);
});
});
}
The first test 'simple call with waiting' works, but in this test i wait a certain time to make sure that the method is completed. the second test does not work because first the test of the count is checked before the method is completed.
Is there a way to wait for the expect like that:
test('simple call', () async {
await expect(impl.throwExceptionAfter(1), throwsException);
var count = impl.count();
expect(count, 1);
});
I have already tried several possibilities but could not find a solution so far. The documentation has not helped me either. Asynchronous Tests
My tests can be found here: Github
Thanks for your help.
I have found several solutions.
One solution is to pack the method into a try-catch block:
test('simple call wiht try catch', () async {
try {
await impl.throwExceptionAfter(1);
} catch (e) {
expect(e, isInstanceOf<Exception>());
}
var count = impl.count();
expect(count, 1);
});
The other solution is to put the check on in the method whenComplete:
test('simple call with whenComplete', () async {
expect(
impl.throwExceptionAfter(1).whenComplete(() {
var count = impl.count();
expect(count, 1);
}),
throwsException);
});
I hope the answer helps others.
I've been searching a long time for an answer to the question I'm about to ask without success.
Let's say I have the following directive :
(function () {
'use strict';
angular
.module('com.acme.mymod')
.directive('myDirective', myDirective);
function myDirective() {
var directive = {
restrict: 'EA',
scope: {},
replace: true,
templateUrl: 'folder/myDirective/myDirective.tpl.html',
controller: "myDirectiveController",
controllerAs: 'vm',
bindToController: {
somedata: "#?",
endpoint: "#?"
},
link: link
};
return directive;
function link(scope, element) {
activate();
function activate() {
scope.$on('$destroy', destroy);
element.on('$destroy', destroy);
}
}
function destroy() {
}
}
})();
myDirectiveController is as follow:
(function () {
'use strict';
angular
.module('com.acme.mymod')
.controller('myDirectiveController', myDirectiveController);
myDirectiveController.$inject = ['utils', '$log'];
// utils is an external library factory in another module
function myDirectiveController(utils, $log) {
var vm = this;
vm.menuIsOpen = false;
function activate() {
vm.dataPromise = utils.getValuePromise(null, vm.somedata, vm.endpoint);
vm.dataPromise.then(function (result) {
vm.data = result.data;
}, function () {
$log.debug("data is not Valid");
});
}
activate();
}
})();
The spec file is as follow:
describe('myDirective Spec', function () {
'use strict';
angular.module('com.acme.mymod', []);
var compile, scope, directiveElem, utils;
beforeEach(function(){
module('com.acme.mymod');
inject(function($compile, $rootScope, $injector,utils){
compile = $compile;
scope = $rootScope.$new();
scope.$digest();
directiveElem = getCompiledElement();
//utils=utils;
console.log(utils.test());
});
});
function getCompiledElement(){
var element = angular.element('<div my-directive="" data-some-data=\' lorem\'></div>');
var compiledElement = compile(element)(scope);
scope.$digest();
return compiledElement;
}
it('should have a nav element of class', function () {
var navElement = directiveElem.find('nav');
expect(navElement.attr('class')).toBeDefined();
});
it('should have a valid json data-model' ,function () {
var data=directiveElem.attr('data-somedata');
var validJSON=false;
try{
validJSON=JSON.parse(dataNav);
}
catch(e){
}
expect(validJSON).toBeTruthy();
});
});
What I can't quite figure out is that every test I try to run, the directive is not compiled or created correctly I'm not sure.
I get :
Error: [$injector:unpr] Unknown provider: utilsProvider <- utils
I tried:
Injecting the controller or utils with $injector.get()
Looked at this post $injector:unpr Unknown provider error when testing with Jasmine
Any tips, pointers or clues as to what I'm doing wrong would be more than welcome
I found the solution after feeling a hitch in my brain :)
In the beforeEach function, all I needed to do is to reference my utils module name this way:
module('com.acme.myutilsmod');
This line "expose" modules components so consumers can use it.
describe("/test" , ()=> {
// separate class 2
class2 = {
// function that i wanna stub
hi: function () {
return "hi";
}
}
// separate class 1
class1 = {
// function that i have stubbed and tested
method1: function() {
return new Promise((resolve, reject) => {
resolve(num);
})
}
}
// method that i will execute
var parent= function (){
class1.method1().then(()=>{
class2.hi();
})
}
// the test
it("should stub hi method",()=>{
var hiTest = sinon.stub(class2, 'hi').resolves(5);
var method1Test = sinon.stub(class1 , 'method1').resolves(5);
// this start the execution of the promise with then call
parent();
// this works fine and test pass
expect(method1Test.calledOnce.should.be.true);
// this doesn't work although i executed the function
expect(hiTest.calledOnce.should.be.true);
})
})
what i wanna do is test the hi method correctly .. because when i test if the method is executed once or not
although i executed it in the then call of the promise it doesn't show that and it make the calledOnce test fail
The problem here is that you are testing the code as if it is synchronous, when it is not (as you are using Promise).
To be able to test this properly we need to be able to hook onto the promise chain that is started with parentcalling class1.method1.
We can do this by returning the promise that calling class1.method1 returns.
In terms of the test itself, we need to make sure Mocha doesnt end the test while we are waiting for the promises, so we use the done callback parameter to tell Mocha when we think the test is finished.
describe("/test", ()=> {
class2 = {
hi: function () {
return "hi";
}
}
class1 = {
method1: function() {
return new Promise((resolve, reject) => {
resolve(num);
})
}
}
var parent = function (){
return class1.method1().then(()=>{
class2.hi();
})
}
it("should stub hi method", (done)=> {
var hiTest = sinon.stub(class2, 'hi').returns(5);
var method1Test = sinon.stub(class1 , 'method1').resolves(5);
parent().then(() => {
expect(method1Test.calledOnce.should.be.true);
expect(hiTest.calledOnce.should.be.true);
done();
});
})
})
I still have the problem with jQuery datepicker in Emberjs. This is my code
If I go away from page with my datepicker, a console gave me error: ui.destroy is not a function.
JQ.Widget = Em.Mixin.create({
didInsertElement: function () {
"use strict";
var options = this._gatherOptions(), ui;
this._gatherEvents(options);
if (typeof jQuery.ui[this.get('uiType')] === 'function') {
ui = jQuery.ui[this.get('uiType')](options, this.get('element'));
} else {
ui = this.$()[this.get('uiType')](options);
}
this.set('ui', ui);
},
willDestroyElement: function () {
"use strict";
var ui = this.get('ui'), observers, prop;
if (ui) {
observers = this._observers;
for (prop in observers) {
if (observers.hasOwnProperty(prop)) {
this.removeObserver(prop, observers[prop]);
}
}
ui._destroy();
}
},
_gatherOptions: function () {
"use strict";
var uiOptions = this.get('uiOptions'), options = {};
uiOptions.forEach(function (key) {
options[key] = this.get(key);
var observer = function () {
var value = this.get(key);
this.get('ui')._setOption(key, value);
};
this.addObserver(key, observer);
this._observers = this._observers || {};
this._observers[key] = observer;
}, this);
return options;
},
_gatherEvents: function (options) {
"use strict";
var uiEvents = this.get('uiEvents') || [], self = this;
uiEvents.forEach(function (event) {
var callback = self[event];
if (callback) {
options[event] = function (event, ui) { callback.call(self, event, ui); };
}
});
}
});
Ember calls willDestroyElement function, but "ui._destroy() is not a function" Why?
This code works fine with outher jQuery elements (Autocomplete,Button ...)
I've had success with the following change:
Replace:
ui._destroy();
With:
if (ui._destroy) ui._destroy();
else if (ui.datepicker) ui.datepicker('destroy');
You may have to add more code if you want to support more UI controls that don't have _destroy().
I am just starting with angular and I wanted to write some simple unit tests for my controllers, here is what I got.
app.js:
'use strict';
// Declare app level module which depends on filters, and services
angular.module('Prototype', ['setsAndCollectionsService']).
config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/dashboard', {templateUrl: 'partials/dashboard.html', controller: 'DashboardController'});
$routeProvider.when('/setsAndCollections', {templateUrl: 'partials/setsAndCollections.html', controller: SetsAndCollectionsController});
$routeProvider.when('/repetition', {templateUrl: 'partials/repetition.html', controller: RepetitionController});
$routeProvider.otherwise({redirectTo: '/dashboard'});
}]);
and controllers.js
'use strict';
/* Controllers */
var myApp = angular.module('Prototype');
myApp.controller('DashboardController', ['$scope', function (scope) {
scope.repeats = 6;
}]);
/*function DashboardController($scope) {
$scope.repeats = 5;
};*/
function SetsAndCollectionsController($scope, $location, collectionsService, repetitionService) {
$scope.id = 3;
$scope.collections = collectionsService.getCollections();
$scope.selectedCollection;
$scope.repetitionService = repetitionService;
$scope.switchCollection = function (collection) {
$scope.selectedCollection = collection;
};
$scope.addCollection = function () {
$scope.collections.push({
name: "collection" + $scope.id,
sets: []
});
++($scope.id);
};
$scope.addSet = function () {
$scope.selectedCollection.sets.push({
name: "set" + $scope.id,
questions: []
});
++($scope.id);
};
$scope.modifyRepetition = function (set) {
if (set.isSelected) {
$scope.repetitionService.removeSet(set);
} else {
$scope.repetitionService.addSet(set);
}
set.isSelected = !set.isSelected;
};
$scope.selectAllSets = function () {
var selectedCollectionSets = $scope.selectedCollection.sets;
for (var set in selectedCollectionSets) {
if (selectedCollectionSets[set].isSelected == false) {
$scope.repetitionService.addSet(set);
}
selectedCollectionSets[set].isSelected = true;
}
};
$scope.deselectAllSets = function () {
var selectedCollectionSets = $scope.selectedCollection.sets;
for (var set in selectedCollectionSets) {
if (selectedCollectionSets[set].isSelected) {
$scope.repetitionService.removeSet(set);
}
selectedCollectionSets[set].isSelected = false;
}
};
$scope.startRepetition = function () {
$location.path("/repetition");
};
}
function RepetitionController($scope, $location, repetitionService) {
$scope.sets = repetitionService.getSets();
$scope.questionsLeft = $scope.sets.length;
$scope.questionsAnswered = 0;
$scope.percentageLeft = ($scope.questionsLeft == 0 ? 100 : 0);
$scope.endRepetition = function () {
$location.path("/setsAndCollections");
};
}
now I am in process of converting global function controllers to ones defined by angular API as you can see by example of DashboardController.
Now in my test:
describe("DashboardController", function () {
var ctrl, scope;
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('DashboardController', {$scope: scope});
}));
it("has repeats attribute set to 5", function () {
expect(scope.repeats).toBe(5);
});
});
I am getting
Error: Argument 'DashboardController' is not a function, got undefined
I am wondering then, where is my mistake? If I understand this right, ctrl = $controller('DashboardController', {$scope: scope}); should inject my newly created scope to my DashboardController to populate it with attributes - in this case, repeats.
You need to set up your Prototype module first.
beforeEach(module('Prototype'));
Add that to your test, above the current beforeEach would work.
describe("DashboardController", function () {
var ctrl, scope;
beforeEach(module('Prototype'));
beforeEach(inject(function ($rootScope, $controller) {
scope = $rootScope.$new();
ctrl = $controller('DashboardController', {$scope: scope});
}));
it("has repeats attribute set to 5", function () {
expect(scope.repeats).toBe(5);
});
});