Jasmine + AngularJS: How do i test a firebaseio call - angularjs

I have an angular service class : -
'use strict';
angular.module('triggerTips')
.service('userData', function ($rootScope, $http, $log, $firebase) {
this._log = {
service : 'userData'
};
// Synchronized objects storing the user data
var config;
var userState;
// Loads the user data from firebase
this.init = function(readyCallback) {
var log = angular.extend({}, this._log);
log.funct = 'init';
var fireRef = new Firebase('https://luminous-inferno-1740.firebaseio.com/' + $rootScope.clientName);
config = $firebase(fireRef.child('config')).$asObject();
userState = $firebase(fireRef.child('userState').child($rootScope.userName)).$asObject();
Promise.all([config.$loaded(), userState.$loaded()]).
then(
function() {
if(config == null || Object.keys(config).length < 4) {
log.message = 'Invalid config';
$log.error(log);
return;
}
if(!userState.userProperties) {
userState.userProperties = {};
}
if(!userState.contentProperties) {
userState.contentProperties = {};
}
log.message = 'User Properties: ' + JSON.stringify(userState.userProperties);
$log.debug(log);
log.message = 'Content Properties: ' + JSON.stringify(userState.contentProperties);
$log.debug(log);
log.message = 'Loaded user data from firebase';
$log.debug(log);
readyCallback();
},
function() {
log.message = 'Unable to load user data from firebase';
$log.error(log);
}
);
};
// Returns the initial tip configuration
this.getConfig = function() {
return config;
};
});
I am trying to unit test this service using jasmine:-
my unit test is :-
describe('Service: userData', function () {
// load the service's module
beforeEach(function() {
module('triggerTips');
});
// instantiate service
var userData;
var rootScope;
beforeEach(inject(function (_userData_, $rootScope) {
rootScope = $rootScope;
userData = _userData_;
}));
it('should load correctly', function () {
expect(!!userData).toBe(true);
});
describe('after being initialized', function () {
beforeEach(function(done) {
// Unable to get this working because the callback is never called
userData.init(function() {
done();
});
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
});
it('should have a valid config', function (done) {
setTimeout(function() {
expect(Object.keys(userData.getConfig()).length > 3);
done();
}, 1500);
});
});
});
I am new with this I am receiving an error :
Error: Firebase.child failed: First argument was an invalid path: "undefiend".
Can somebody help me providing working example of my code with some explanation?

Related

TypeError: service.getForums() is not a function - jasmine unit testing for AngularJS

Hello I am new to AngularJS. I am trying to test angular service ForumService. For that I have to mock the response that I get while calling the service method. I did something but I don't know if its right or not. I am getting an error
"ForumService should use Function FAILED"
"TypeError: service.getForums is not a function"
This is the service that I am testing
(function() {
'use strict';
function ForumService($q, $http, config, Forum) {
var service = {};
/**
* Sends a GET request to backend API for all forums.
*
* #return {Promise}
*/
service.getForums = function(onSuccessCallback, onErrorCallback) {
$http.get(config.apiBaseUrl + '/api/forums')
.then(
function handleSuccess(response) {
onSuccessCallback(response.data.data);
},
function handleError(response) {
onErrorCallback(response.data.error);
}
);
};
/**
* Sends a GET request to backend API for all forums.
*
* #return {Promise}
*/
service.getForumsPromise = function() {
var q = $q.defer();
$http.get(config.apiBaseUrl + '/api/forums')
.then(
function success(response) {
q.resolve(buildForumArray(response.data.data));
},
function error(response) {
q.reject(response.data.error);
}
);
return q.promise;
};
function buildForumArray(data) {
var forumArray = [];
data.forEach(function(forumData) {
forumArray.push(new Forum(forumData));
});
return forumArray;
}
return service;
}
ForumService.$inject = [
'$q',
'$http',
'config',
'Forum'
];
angular
.module('app.services')
.factory('ForumService', ForumService);
})();
The following is the code where I am testing the first method service.getForums()
'use strict';
describe('ForumService', function() {
var service, $q, config, httpBackend;
beforeEach(module('app.services'));
beforeEach(module('app.models'));
beforeEach(module(function($provide) {
$provide.service('config', function() {
this.apiBaseUrl = "localhost";
});
$provide.service('ForumService', function() {
this.constructor = jasmine.createSpy('ForumService')
});
$provide.service('Forum', function() {
this.constructor = jasmine.createSpy('Forum')
});
}));
//2.
beforeEach(function() {
inject(function($injector) {
service = $injector.get('ForumService');
httpBackend = $injector.get('$httpBackend');
$q = $injector.get('$q');
});
});
// 5. make sure no expectations were missed in your tests.
afterEach(function() {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should use Function', function() {
var returnData = [
{
id: 1,
name: "Programming Questions",
description: "Please post all Questions you have in regards to programming here"
}, {
id: 2,
name: "OOP",
description: "Object Oriented Programming"
}
];
console.info('foo');
httpBackend.when('GET', 'localhost/api/forums').respond(200, returnData);
service.getForums().then(function(response) {
console.info(response); // to see the response
expect(response.data.id).toBe(1);
expect(response.data.name).toBe("Programming Questions");
expect(response.data.description).toBe("Please post all Questions you have in regards to programming here");
});
httpBackend.flush();
});
});
And this is my model class
(function() {
'use strict';
angular
.module('app.models')
.factory('Forum', Forum);
Forum.$inject = [];
function Forum() {
/**
* Forum prototype (constructor function).
*
* #param data
* #constructor
*/
function Forum(data) {
var self = this;
if (angular.isDefined(data)) {
self.id = data.id;
self.name = data.name;
self.description = data.description;
} else {
self.id = 0;
self.name = '';
self.description = '';
}
}
return Forum;
}
})();
There were are lot of issues with the test cases and the code you've written. Fixed a few of them here
Do read the inline comments for the explainations
This should be the definition of you getForums method:
service.getForums = function(onSuccessCallback, onErrorCallback) {
$http.get(config.apiBaseUrl + '/api/forums').then(function handleSuccess(response) {
// You'll get the data inside response.data and not response.data.data
// onSuccessCallback(response.data.data);
onSuccessCallback(response.data);
}, function handleError(response) {
onErrorCallback(response.data.error);
});
};
If you really wanted to return promise from getForumsPromise method, you could have simply done this:
service.getForumsPromise = function() {
return $http.get(config.apiBaseUrl + '/api/forums');
};
$http.get returns a promise anyways.
And this is how you should be writing the test case:
'use strict';
describe('ForumService', function() {
var returnData = [{
id: 1,
name: "Programming Questions",
description: "Please post all Questions you have in regards to programming here"
}, {
id: 2,
name: "OOP",
description: "Object Oriented Programming"
}];
//Below line of code is not required.
// var service, $q, config, httpBackend;
beforeEach(module('app.services'));
beforeEach(module('app.models'));
beforeEach(module(function($provide) {
$provide.service('config', function() {
this.apiBaseUrl = "localhost";
});
// Below line of code is not required.
// $provide.service('ForumService', function() {
// this.constructor = jasmine.createSpy('ForumService')
// });
// $provide.service('Forum', function() {
// this.constructor = jasmine.createSpy('Forum')
// });
}));
//2.
// Instead of injecting the services like this
// beforeEach(function() {
// inject(function($injector) {
// service = $injector.get('ForumService');
// httpBackend = $injector.get('$httpBackend');
// $q = $injector.get('$q');
// });
// });
// Inject them like this
beforeEach(inject(function(_ForumService_, _$httpBackend_) {
ForumService = _ForumService_;
$httpBackend = _$httpBackend_;
$httpBackend.when('GET', 'localhost/api/forums').respond(200, returnData);
}))
// 5. make sure no expectations were missed in your tests.
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should use Function', function() {
// This data should be outside a specific it block so that it could be reused.
// Moved it outside.
// var returnData = [{
// id: 1,
// name: "Programming Questions",
// description: "Please post all Questions you have in regards to programming here"
// }, {
// id: 2,
// name: "OOP",
// description: "Object Oriented Programming"
// }];
console.info('foo');
// This should be inside beforeEach block so that it could be reused.
// httpBackend.when('GET', 'localhost/api/forums').respond(200, returnData);
// You call httpBackend's flush right after the call to your service's method and then expect.
// Also your expectations are wrong. So you might get errors.
// Fixing those.
// service.getForums().then(function(response) {
// console.info(response); // to see the response
// expect(response.data.id).toBe(1);
// expect(response.data.name).toBe("Programming Questions");
// expect(response.data.description).toBe("Please post all Questions you have in regards to programming here");
// });
// httpBackend.flush();
// Like this.
var successCallback = function(data) {
expect(data.length).toEqual(2);
expect(data[0].id).toBe(1);
expect(data[0].name).toBe("Programming Questions");
expect(data[0].description).toBe("Please post all Questions you have in regards to programming here");
}
var errorCallback = function(error) {
}
ForumService.getForums(successCallback, errorCallback);
$httpBackend.flush();
});
});
Hope this helps

Accessing Angular directive controller from Protractor

Is there a way to access a directive's controller using the .controller() method inside a Protractor test? In unit tests I can do
var el = angular.element(...);
$compile(el)(scope);
var component = el.controller('nameOfDirective');
but I can't seem to figure out how to do that in a Protractor test.
Here is the code I'm trying to test...
//registration.route.js
(function () {
'use strict';
angular.module('app.account.registration').run(appRun);
appRun.$inject = ['routerHelper'];
/* #ngInject */
function appRun(routerHelper) {
routerHelper.configureStates(getStates());
}
function getStates() {
return [
{
state: 'registration',
config: {
url: '/registration',
template: '<registration layout="column" flex/>',
title: 'registration'
}
}
];
}
})();
//registration.component.js
(function () {
'use strict';
angular.module('app.account.registration').component('registration', {
templateUrl: 'app/account/registration/registration.html',
controller: Controller
});
/* #ngInject */
function Controller($state, UserService, SweetAlert) {
var self = this;
this.$onInit = function () {
this.state = 'unverified';
this.processing = false;
};
this.register = function (formData) {
this.processing = true;
UserService.register(formData.email).then(function (response) {
self.state = 'verify';
self.email = response.data.email;
//response.data.token will only be returned on localhost for security reasons
self.token = response.data.token;
self.processing = false;
}).catch(function (error) {
if (error.status === 422) {
SweetAlert.error('Error!', formData.email + ' already exists. Please use a unique email.');
}
self.processing = false;
});
};
}
})();
If anyone needs the answer for this, here it is:
browser.executeScript(function () {
return window.angular.element(document.body)
.find('DIRECTIVE-TAG-NAME').controller('directiveName');
}).then(function (directiveControllerInstance) {
console.log(directiveControllerInstance);
});
For example, if your directive name is "testDirective", you would do:
browser.executeScript(function () {
return window.angular.element(document.body)
.find('test-directive').controller('testDirective');
}).then(function (directiveControllerInstance) {
console.log(directiveControllerInstance);
});

Jasmine Testing angularjs controller

I am trying to test my controller but I get undefined when running karma start
This is my controller Code:
(function() {
'use strict';
angular
.module('tariffOptionsExtras')
.controller('tariffOptionsExtrasBookCtrl', tariffOptionsExtrasBookCtrl);
tariffOptionsExtrasBookCtrl.$inject = ['tariffOptionsExtrasSrv', '$location', 'cache', 'authSrv', '$stateParams',
'tariffOptionsExtrasBookSrv', 'minimumDuration', '$rootScope', 'userTypes', 'messageCtrl', '$state'];
function tariffOptionsExtrasBookCtrl(tariffOptionsExtrasSrv, $location, cache, authSrv, stateParams, tariffOptionsExtrasBookSrv, minimumDuration, $rootScope, userTypes, messageCtrl, $state) {
var vm = this;
var cacheName = 'tariffOptionsExtras';
var cacheCollection = cache.getCollection(cacheName);
vm.date = new Date();
vm.minimumDuration = minimumDuration;
//if statement commented out. prepaid users should be able to enter the flow.
Date(vm.bookOption.startDate)
}
if (!vm.bookOption) {
$state.go(pagesConfig.tariffOptionsExtras.name);
} else {
vm.infoLink = vm.bookOption.infoUrl;
}
/**
* Runs booking tarif extra post and in case of success the view is changed
*/
vm.book = function() {
//If bookoption not present, redirect to chooser
tariffOptionsExtrasBookSrv.bookTariffExtra(vm.bookOption).then(function(response) {
$rootScope.$broadcast('transactionFinished');
var item = response['salesOrderVBO'][0]['orderItems']['orderItem'][0];
if (item.product.action == 'BOOKED') {
vm.success = true;
}
}, function(reason) {
vm.errorMessage = reason.data.faultMessage;
});
};
vm.success = false;
vm.subscription = authSrv.getUserContract();
vm.msisdn = vm.subscription.subscription.msisdn;
}
})();
Unit Test with jasmine
describe('tariffOptionsExtras module', function() {
describe('tariffOptionsExtrasBook Controller', function() {
var tariffOptionsExtrasBookCtrl, tariffOptionsExtrasSrv, tariffOptionsExtrasBookSrv, authSrv;
// Step 1: Import the module this controller belongs to, and its dependencies
beforeEach(function() {
module('app.common');
module('tariffOptionsExtras');
});
// Step 2: Mock any service that initially used when the controller instantiate
// beforeEach(module(function($provide) {
// $provide.factory('tariffOptionsExtrasBookSrv', function() {
// var getSync;
// getBookedExtras = function() {
// return {
// then:function(){}
// };
// };
// getTariffBookableOptions = function() {
// return {
// then:function(){}
// };
// };
// return {
// getBookedExtras: getBookedExtras,
// getTariffBookableOptions:getTariffBookableOptions
// };
// });
// }));
beforeEach(module(function($provide) {
$provide.factory('authSrv', function() {
getUserContract = function() {
return {
subscription:{
msisdn:'491741660390',
mboName:'27434975'
},
contract:{
mboName:'27434975',
ban:'106491816',
}
};
};
return {
getUserContract: getUserContract,
};
});
}));
// Step 3: Inject $controller with its dependencies
beforeEach(function() {
// 1. Import the module
module('app');
// 2. Inject $controller
inject(function($controller, $rootScope, _authSrv_, _tariffOptionsExtrasSrv_, _tariffOptionsExtrasBookSrv_) {
authSrv = _authSrv_;
tariffOptionsExtrasSrv = _tariffOptionsExtrasSrv_;
tariffOptionsExtrasBookSrv = _tariffOptionsExtrasBookSrv_;
// 3. Use $controller to instantiate the controller
tariffOptionsExtrasBookCtrl = $controller('tariffOptionsExtrasBookCtrl', {
'authSrv': authSrv,
'tariffOptionsExtrasSrv': tariffOptionsExtrasSrv,
'tariffOptionsExtrasBookSrv': tariffOptionsExtrasBookSrv
});
});
});
// Step 4: Test the controller
it('Should return sum of 1+1', function() {
expect(1+1).toBe(2);
});
});
});
When running karma Start I get this screen:
enter image description here
Also when I comment this block of code it works:
tariffOptionsExtrasBookCtrl = $controller('tariffOptionsExtrasBookCtrl', {
'authSrv': authSrv,
'tariffOptionsExtrasSrv': tariffOptionsExtrasSrv,
'tariffOptionsExtrasBookSrv': tariffOptionsExtrasBookSrv
});
first you have to inject the $controller param must be enclosed with underscores
then assign it to the $controller variable as this example
beforeEach(inject(function ($rootScope, _$controller_) {
scope = $rootScope.$new();
$controller = _$controller_;
$controller('nameController', {$scope: scope});
}));

Angular Unit Test Jasmine Spy error

The following controller is getting a TypeError: 'undefined' is not a function (evaluating sessionService.getCurrentPlace()). I have a Mock Service with that method being spied on. The other method on the mock service works fine. I've tried .AndReturns({..}) on the spy as well as .AndCallThrough() but no luck. Any idea what I'm missing, or am I going about this wrong? Much Thanks!
CONTROLLER:
'use strict';
angular.module('wallyApp')
.controller('addGatewayCtrl', function ($scope, $location, $filter, sessionService) {
/*
private members
*/
//set scope from session data
$scope.processSession = function (places) {
$scope.currentPlaceId = sessionService.getCurrentPlace();
if (!$scope.currentPlaceId) {
$scope.currentPlaceId = places[0].id;
}
$scope.place = $filter("getById")(places, $scope.currentPlaceId);
$scope.ready = true;
};
/*
setup our scope
*/
$scope.currentPlaceId = null;
$scope.place = {};
$scope.videoSrc = "/videos/gateway-poster.gif";
$scope.loaded = true;
/*
setup controller behaivors
*/
//set video or gif to show or hide video
$scope.setVideo = function () {
$scope.videoSrc = "/videos/gateway.gif";
};
$scope.setPoster = function () {
$scope.videoSrc = "/videos/gateway-poster.gif";
};
//initialize scope
$scope.setVideo();
//submit form
$scope.continue = function () {
$location.path("/setup/pair-gateway");
return false;
};
//cancel
$scope.back = function () {
$location.path("/setup/plan-locations");
return false;
};
//wifi
$scope.gotoWifi = function () {
$location.path("/setup/wifi");
return false;
};
/*
setup our services, etc
*/
//get our places from the cache
sessionService.get("places").then(function (places) {
if (!places || places.length < 1) {
sessionService.refreshPlaces(); //Note we don't care about the promise as our broadcast watch will pick up when ready
} else {
$scope.processSession(places);
}
}).catch(function (error) {
//TODO:SSW Call Alert Service??
});
//Watch broadcast for changes
$scope.$on("draco.placesRefreshed", function (event, data) {
sessionService.get("places").then(function (places) {
$scope.processSession(places);
});
});
});
UNIT TEST:
'use strict';
describe('addGatewayCtrl', function () {
var $q,
$rootScope,
$location,
$scope,
$filter,
mockSessionService,
completePath = "/setup/pair-gateway",
backPath = "/setup/plan-locations",
wifiPath = "/setup/wifi",
sessionDeferred,
sessionInitDeferred,
mockPlaces = [{ id: "0001" }];
beforeEach(module('wallyApp'));
beforeEach(inject(function (_$q_, _$rootScope_, _$location_, _$filter_) {
$q = _$q_;
$location = _$location_;
$rootScope = _$rootScope_;
$filter = _$filter_;
}));
beforeEach(inject(function ($controller) {
$scope = $rootScope.$new();
mockSessionService = {
get: function (contact) {
sessionDeferred = $q.defer();
return sessionDeferred.promise;
},
getCurrentPlace: function () {
return mockPlaces[0].id;
},
refreshPlaces: function () {
sessionInitDeferred = $q.defer();
return sessionInitDeferred.promise;
}
};
spyOn(mockSessionService, 'get').andCallThrough();
spyOn(mockSessionService, 'getCurrentPlace').andReturn(mockPlaces[0].id);
spyOn(mockSessionService, 'refreshPlaces').andCallThrough();
$controller('addGatewayCtrl', {
'$scope': $scope,
'$location': $location,
'$filter':$filter,
'sessionService': mockSessionService
});
}));
describe('call session service to get place data ', function () {
//resolve our mock place and session services
beforeEach(function () {
//resolve mocks
sessionDeferred.resolve(mockPlaces);
$rootScope.$apply();
});
//run tests
it('should have called sessionService get places', function () {
expect(mockSessionService.get).toHaveBeenCalledWith("places");
});
it('should have called sessionService get currentPlaceId', function () {
expect(mockSessionService.getCurrentPlace).toHaveBeenCalled();
});
it('should have set scope', function () {
expect($scope.place).toEqual(mockPlaces[0]);
});
});
});
So I figured it out. With nested deferred's you have to call $scope.$apply() in between. The following fixed it up (along with a few minor changes to the mock data responses, but those were trivial):
//resolve promises
activityMessagesDeferred.resolve(mockActivityMessages);
$rootScope.$apply();
$rootScope.$broadcast("draco.sessionRefreshed");
activityCountDeferred.resolve(mockActivityCount);
$rootScope.$apply();
placesDeferred.resolve(mockPlaces);
activityListDeferred.resolve(mockActivities);
$rootScope.$apply();

Failed to instantiate module ng due to: Error: [$injector:unpr] http://errors.angularjs.org/undefined/$injector/unpr?p0=%24logProvider

I create this to decorate the $log:
window.fofr = window.fofr || {};
window.fofr.library = window.fofr.library || {};
window.fofr.library.logging = window.fofr.library.logging || {};
window.fofr.library.logging.errorLogViewerService = function () {
var configure = function (angularJsModule) {
angularJsModule.config(function ($provide) {
$provide.decorator('$log', function ($delegate, $sniffer) {
var _error = $delegate.error; // Saving original function
var _log = $delegate.log;
$delegate.logs = [];
$delegate.error = function (msg) {
_error(msg);
};
$delegate.log = function (msg) {
_log(msg);
$delegate.logs.push(msg);
};
return $delegate;
});
});
};
return {
configure: configure
};
} ();
I create a unit test with qunit:
module('Library - Logging - ErrorLogViewer', {
setup: function () {
this.app = angular.module('app', []);
}
});
test('Log - check the logs is filled with log', function () {
window.fofr.library.logging.errorLogViewerService.configure(this.app);
var injector = angular.injector(['app', 'ng']);
injector.invoke(function ($log) {
$log.log('test');
equal($log.hasOwnProperty('logs'), true, 'The property logs must exists');
equal($log.logs.length, 1, 'The logs must contain one log');
});
});
But it crash in the config saying that it doesn't know the logProvider???
ok, I found, the $log is defined in ng module, angular search in module defined by the order set in the code : angular.injector(['app', 'ng']);
so I set this angular.injector(['ng', 'app']); and now it works!

Resources