Karma - Spied function error handling - javascript

I have a service like this:
app.service('usersService.v2', ['$http', '$q', '$exceptionHandler', 'User', 'userValidator.v2', 'CRUD', function($http, $q, $exceptionHandler, User, userValidator, CRUD){
function dummyPromise(){
var dummyDeferred = $q.defer();
dummyDeferred.resolve('dummy');
return deferred.promise;
}
this.getUser = function(userID, companyID){
try{
userValidator.validateId(userID);
userValidator.validateId(companyID);
}
catch(e){
$exceptionHandler(e);
return dummyPromise();
}
return $http.get(apiUrl + 'api/v2/companies/' + companyID + '/users/' + userID)
.then(function(response){
var user = new User(response.data);
try{
userValidator.validateUser(CRUD.READ, user);
}
catch(e){
$exceptionHandler(e);
return;
}
return user;
})
};
}]);
And basically I want to test the behaviour of this service depending on what the validation functions do.
userValidator.* function are if/else blocks throwing errors.
In Karma I have something like this:
describe('Service: usersService.v2', function () {
var usersService, httpBackend, state, userValidator;
const url = 'address'
function _inject() {
angular.mock.inject(function ($injector) {
usersService = $injector.get('usersService.v2');
userValidator = $injector.get('userValidator.v2');
httpBackend = $injector.get('$httpBackend');
});
}
beforeEach(function () {
angular.mock.module('app');
_inject();
});
describe('getUser', function () {
beforeEach(function () {
httpBackend.when('GET', url);
});
afterEach(function () {
httpBackend.verifyNoOutstandingExpectation();
httpBackend.verifyNoOutstandingRequest();
});
it('should return a dummy promise if ID validation fails', function(){
spyOn(userValidator, 'validateId').and.throwError('Missing or wrong ID thrown');
usersService.getUser()
.then(function(data){expect(data).toBe('dummy');})
});
)};
})
but when I'm running Karma I get an error, as it would be if I didn't put the catch to handle the expection and the following block of code is not executed.
What am I doing wrong?
Cheers,
Manuel
UPDATE:
Validate methods are something like this:
... code code code ...
this.validateId = function(ID){
if(!ID || !angular.isNumber(ID)) throw 'Missing or wrong ID';
}
Thus my problem is that Karma is trying to handle the error thrown by validation instead of let the userService do it.

You are testing usersService.v2.You can not test the userValidator.v2 at the same time.But you can mock the userValidator.v2 service.
var userValidator;
beforeEach(function() {
module(function($provider) {
userValidator = {
validateId: function(id) {
if (id === 123 ||id === 456 ) { //put your mock test id here for PASS case
return true
}
return false;
}
}
$provider.value('userValidator.v2', userValidator);
})
});
describe('getUser', function () {
beforeEach(function () {
httpBackend.when('GET', url)
.respond(200, {
data: "dummy"
});;
});
it('should return a dummy promise if ID validation fails', function() {
usersService.getUser(9879,8798) //Its FAILED case
.then(function(data) { expect(data).toBe('dummy'); })
})
})

Related

List all elements from a list created with $http.get in AngularJS

I'm new into AngularJS programming and I wrote some code, I don't get any errors, in console it gives me what I want but it dose not show on the web page. I am trying to make 2 HTTP calls, get and post, get shows me all the element I have in a list and post add an element in this list. They are working, but when I try to list all the elements it dose not show me anything :(
Here are my code:
dataContext.js
(function () {
'use strict';
angular.module('app').service('dataContext', ['httpService', function (httpService) {
var service = {
getAllUsers: getAllUsers,
addUser: addUser,
saveMessage: saveMessage
};
function getAllUsers() {
return httpService.get("api/users");
}
function addUser(name) {
return httpService.post("api/users", { name: name });
}
dashboard.js
(function () {
'use strict';
angular.module('app').controller("dashboardController", ['dataContext', function (dataContext) {
var vm = this;
vm.getUser = function () {
dataContext.getAllUsers().then(
function (response) {
alert(response.data[0].Name);
},
function (error) {
console.log(error);
}
);
};
vm.postUser = function () {
dataContext.addUser(vm.username).then(
function (response) {
alert("User, " + vm.username + " was added!");
},
function (error) {
console.log(error);
}
);
};
})();
httpService.js
(function () {
'use strict';
angular.module('app').service('httpService', ['$http', '$q', 'backendConfig', function ($http, $q, backendConfig) {
var service = {
get: get,
post: post
};
function get(path) {
var deferred = $q.defer();
$http.get(backendConfig.url + path).then(
function (response) {
deferred.resolve(response);
},
function (err, status) {
deferred.reject(err);
}
);
return deferred.promise;
}
function post(path, data) {
var deferred = $q.defer();
$http.post(backendConfig.url + path, data).then(
function (response) {
deferred.resolve(response);
},
function (err, status) {
deferred.reject(err);
}
);
return deferred.promise;
}
return service;
}]);})();
and my dashboard.html
<div ng-controller="dashboardController as vm">
<button ng-click="vm.getUser()">Get</button>
<button ng-click="vm.postUser()">Post</button>
<input ng-model="vm.username" />
<ul>
<li ng-repeat="obj in vm.users">{{obj.Name}}</li>
</ul>
List's name is users, I have a class Users with a string Name, everything should be on point but I don't know why it dose not work. Any help will be awesome
Thank you
Replace
alert(response.data[0].Name);
with
vm.users = response.data;

How to get username of currenlty logged on user authservice

I would like to get the currently loged in username so i can display it. But i dont know how to do it ? Any ideas ? I am using authservice Here is my angular controller in which i would like to get the username.
myApp.controller('meetupsController', ['$scope', '$resource', function ($scope, $resource) {
var Meetup = $resource('/api/meetups');
$scope.meetups = []
Meetup.query(function (results) {
$scope.meetups = results;
});
$scope.createMeetup = function () {
var meetup = new Meetup();
meetup.name = $scope.meetupName;
meetup.$save(function (result) {
$scope.meetups.push(result);
$scope.meetupName = '';
});
}
}]);
my main angular controller code
var myApp = angular.module('myApp', ['ngResource', 'ngRoute']);
myApp.config(function ($routeProvider) {
$routeProvider
.when('/', {
templateUrl: 'partials/main.html',
access: {restricted: true}
})
.when('/api/meetups', {
templateUrl: 'partials/main.html',
access: {restricted: true}
})
.when('/login', {
templateUrl: 'partials/login.html',
controller: 'loginController',
access: {restricted: false}
})
.when('/prive', {
templateUrl: 'partials/prive.html',
controller: 'userController',
access: {restricted: true}
})
.when('/logout', {
controller: 'logoutController',
access: {restricted: true}
})
.when('/register', {
templateUrl: 'partials/register.html',
controller: 'registerController',
access: {restricted: false}
})
.when('/one', {
template: '<h1>This is page one!</h1>',
access: {restricted: true}
})
.when('/two', {
template: '<h1>This is page two!</h1>',
access: {restricted: false}
})
.otherwise({
redirectTo: '/'
});
});
myApp.run(function ($rootScope, $location, $route, AuthService) {
$rootScope.$on('$routeChangeStart',
function (event, next, current) {
AuthService.getUserStatus()
.then(function(){
if (next.access.restricted && !AuthService.isLoggedIn()){
$location.path('/login');
$route.reload();
}
});
});
});
myApp.controller('meetupsController', ['$scope', '$resource', function ($scope, $resource) {
var Meetup = $resource('/api/meetups');
$scope.meetups = []
Meetup.query(function (results) {
$scope.meetups = results;
});
$scope.createMeetup = function () {
var meetup = new Meetup();
meetup.name = $scope.meetupName;
meetup.$save(function (result) {
$scope.meetups.push(result);
$scope.meetupName = '';
});
}
}]);
my second angular code :
var app = angular.module('myApp');
app.controller('loginController',
['$scope', '$location', 'AuthService',
function ($scope, $location, AuthService) {
$scope.login = function () {
// initial values
$scope.error = false;
$scope.disabled = true;
// call login from service
AuthService.login($scope.loginForm.username, $scope.loginForm.password)
// handle success
.then(function () {
$location.path('/');
$scope.disabled = false;
$scope.loginForm = {};
})
// handle error
.catch(function () {
$scope.error = true;
$scope.errorMessage = "Invalid username and/or password";
$scope.disabled = false;
$scope.loginForm = {};
});
};
$scope.posts = [];
$scope.newPost = {created_by: '', text: '', created_at: ''};
$scope.post = function(){
$scope.newPost.created_at = Date.now();
$scope.posts.push($scope.newPost);
$scope.newPost = {created_by: '', text: '', created_at: ''};
};
}]);
app.controller('logoutController',
['$scope', '$location', 'AuthService',
function ($scope, $location, AuthService) {
$scope.logout = function () {
// call logout from service
AuthService.logout()
.then(function () {
$location.path('/login');
});
};
$scope.gotoregister = function () {
$location.path('/register');
};
$scope.gotoprive = function () {
$location.path('/prive');
};
}]);
app.controller('registerController',
['$scope', '$location', 'AuthService',
function ($scope, $location, AuthService) {
$scope.register = function () {
// initial values
$scope.error = false;
$scope.disabled = true;
// call register from service
AuthService.register($scope.registerForm.username, $scope.registerForm.password)
// handle success
.then(function () {
$location.path('/login');
$scope.disabled = false;
$scope.registerForm = {};
})
// handle error
.catch(function () {
$scope.error = true;
$scope.errorMessage = "Something went wrong!";
$scope.disabled = false;
$scope.registerForm = {};
});
};
}]);
and my services
angular.module('myApp').factory('AuthService',
['$q', '$timeout', '$http',
function ($q, $timeout, $http) {
// create user variable
var user = null;
// return available functions for use in the controllers
return ({
isLoggedIn: isLoggedIn,
getUserStatus: getUserStatus,
login: login,
logout: logout,
register: register
});
function isLoggedIn() {
if(user) {
return true;
} else {
return false;
}
}
function getUserStatus() {
return $http.get('/user/status')
// handle success
.success(function (data) {
if(data.status){
user = true;
} else {
user = false;
}
})
// handle error
.error(function (data) {
user = false;
});
}
function login(username, password) {
// create a new instance of deferred
var deferred = $q.defer();
// send a post request to the server
$http.post('/user/login',
{username: username, password: password})
// handle success
.success(function (data, status) {
if(status === 200 && data.status){
user = true;
deferred.resolve();
} else {
user = false;
deferred.reject();
}
})
// handle error
.error(function (data) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
function logout() {
// create a new instance of deferred
var deferred = $q.defer();
// send a get request to the server
$http.get('/user/logout')
// handle success
.success(function (data) {
user = false;
deferred.resolve();
})
// handle error
.error(function (data) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
function register(username, password) {
// create a new instance of deferred
var deferred = $q.defer();
// send a post request to the server
$http.post('/user/register',
{username: username, password: password})
// handle success
.success(function (data, status) {
if(status === 200 && data.status){
deferred.resolve();
} else {
deferred.reject();
}
})
// handle error
.error(function (data) {
deferred.reject();
});
// return promise object
return deferred.promise;
}
}]);
So this should probably work, maybe you will need to make some small adjustments because i don't know how exactly is your app structured, but this will work.
First you need to change your AuthService to look like this
angular.module('myApp').factory('AuthService',
['$q', '$timeout', '$http',
function ($q, $timeout, $http, $cookies) {
// create user variable
var user = null;
// we must create authMemberDefer var so we can get promise anywhere in app
var authenticatedMemberDefer = $q.defer();
// return available functions for use in the controllers
return ({
isLoggedIn: isLoggedIn,
getUserStatus: getUserStatus,
login: login,
logout: logout,
register: register,
getAuthMember: getAuthMember,
setAuthMember: setAuthMember
});
function isLoggedIn() {
if(user) {
return true;
} else {
return false;
}
}
//this is function that we will call each time when we need auth member data
function getAuthMember() {
return authenticatedMemberDefer.promise;
}
//this is setter function to set member from coockie that we create on login
function setAuthMember(member) {
authenticatedMemberDefer.resolve(member);
}
function getUserStatus() {
return $http.get('/user/status')
// handle success
.success(function (data) {
if(data.status){
user = true;
} else {
user = false;
}
})
// handle error
.error(function (data) {
user = false;
});
}
function login(username, password) {
// create a new instance of deferred
var deferred = $q.defer();
// send a post request to the server
$http.post('/user/login',
{username: username, password: password})
// handle success
.success(function (data, status) {
if(status === 200 && data.status){
user = true;
deferred.resolve();
//**
$cookies.putObject('loginSession', data);
// here create coockie for your logged user that you get from this response, im not sure if its just "data" or data.somethingElse, check you response you should have user object there
} else {
user = false;
deferred.reject();
}
})
// handle error
.error(function (data) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
function logout() {
// create a new instance of deferred
var deferred = $q.defer();
// send a get request to the server
$http.get('/user/logout')
// handle success
.success(function (data) {
user = false;
deferred.resolve();
//on log out remove coockie
$cookies.remove('loginSession');
})
// handle error
.error(function (data) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
function register(username, password) {
// create a new instance of deferred
var deferred = $q.defer();
// send a post request to the server
$http.post('/user/register',
{username: username, password: password})
// handle success
.success(function (data, status) {
if(status === 200 && data.status){
deferred.resolve();
} else {
deferred.reject();
}
})
// handle error
.error(function (data) {
deferred.reject();
});
// return promise object
return deferred.promise;
}
}]);
after that changes in authService, you must make this on your app run, so each time application run (refresh) it first check coockie to see if there is active session(member) and if there is it will set it inside our AuthService.
myApp.run(function($rootScope, $location, $route, AuthService, $cookies) {
$rootScope.$on('$routeChangeStart',
function(event, next, current) {
if ($cookies.get('loginSession')) {
var session = JSON.parse($cookies.get('loginSession'));
AuthService.setAuthMember(session);
} else {
$location.path('/login');
}
});
});
And simply anywhere where you want to get auth member you have to do this, first include in your controller/directive AuthService and do this
AuthService.getAuthMember().then(function(member){
console.log(member);
//here your member should be and you can apply any logic or use that data where u want
});
I hope this helps you, if you find any difficulties i'm happy to help
just a demo example
in login controller
var login = function(credentials) {
AuthService.login(credentials).then(function(result) {
var user = result.data;
AuthService.setCurrentUser(user);
$rootScope.$broadcast(AUTH_EVENTS.loginSuccess);
}).catch(function(err) {
if (err.status < 0) {
comsole.error('Please check your internet connection!');
} else {
$rootScope.$broadcast(AUTH_EVENTS.loginFailed);
}
});
};
in AuthService
.factory('AuthService', function($http, $cookies, BASE_URL) {
var service = {
login: function(formdata) {
return $http.post(BASE_URL + '/login', formdata);
},
setCurrentUser: function(user) {
$cookies.putObject('currentUser', user);
},
isAuthenticated: function() {
return angular.isDefined($cookies.getObject('currentUser'));
},
getFullName: function() {
return $cookies.getObject('currentUser').firstName + ' ' + $cookies.getObject('currentUser').lastName;
}
}
return service;
});
in the controller which attached with your dashboard view
$scope.$watch(AuthService.isAuthenticated, function(value) {
vm.isAuthenticated = value;
if (vm.isAuthenticated) {
vm.fullName = AuthService.getFullName();
vm.currentUser = AuthService.getCurrentUser();
}
});
There are few methods how you can get currently logged user, it mostly depends on you app structure and API, you probably should have API end point to get authenticated member and that call is made on each app refresh.
Also if you can show us your authservice.
Edit:
Also on successful login you can store information about logged user in coockie like this
function doLogin(admin) {
return authMemberResources.login(details).then(function(response) {
if (response) {
$cookies.putObject('loginSession', response);
} else {
console.log('wrong details');
}
});
So basically you can use angularjs coockies service and make loginSession coockie like that, and on app refresh or anywhere where you need logged user info, you can get that like this:
if ($cookies.get('loginSession')) {
var session = JSON.parse($cookies.get('loginSession'));
console.log(session);
}
.factory('AuthService', function($http, $cookies, BASE_URL) {
var service = {
login: function(formdata) {
return $http.post(BASE_URL + '/login', formdata);
},
setCurrentUser: function(user) {
$cookies.putObject('currentUser', user);
},
isAuthenticated: function() {
return angular.isDefined($cookies.getObject('currentUser'));
},
getFullName: function() {
return $cookies.getObject('currentUser').firstName + ' ' + $cookies.getObject('currentUser').lastName;
},
getAuthenticatedMember: function() {
if ($cookies.get('currentUser')) {
return JSON.parse($cookies.get('currentUser'));
}
}
}
return service;
});
That should work, i added new function getAuthenticatedMember and you can use it where you need it. And you can use it like this:
$scope.$watch(AuthService.isAuthenticated, function(value) {
vm.isAuthenticated = value;
if (vm.isAuthenticated) {
vm.currentUser = AuthService.getAuthenticatedMember();
}
});

resolving values inside of a function in angular js

I have this code inside of a file called Auth.js .
hasRole: function(){
console.log(currentUser);
return currentUser.role; //role is an array of objects but gives undefined
}
I need to resolve the value of hasRole before redirecting anywhere just after login.
login looks like this:
var role=[];
login: function(user, callback) {
var cb = callback || angular.noop;
var deferred = $q.defer();
$http.post('/auth/local', {
email: user.email,
password: user.password
}).
success(function(data) {
$cookieStore.put('token', data.token);
currentUser = User.get();
console.log(currentUser);//gives an unresolved promise (output is given after this piece of code.
role = currentUser.role; //gives undefined
deferred.resolve(data);
return cb();
}).
error(function(err) {
this.logout();
deferred.reject(err);
return cb(err);
}.bind(this));
return deferred.promise;
}}
and is called as follows:
$scope.login = function(form) {
$scope.submitted = true;
if(form.$valid) {
Auth.login({
email: $scope.user.email,
password: $scope.user.password
})
.then( function() {
// Logged in, redirect to home
var role = Auth.hasRole();
console.log(role) //gives undefined.
//Need to redirect on basis of value of role
/*if(role.priority >= 1){
$location.path('/admincontrol');
}else{
$location.path('/');
}*/
})
.catch( function(err) {
$scope.errors.other = err.message;
});
}
};
How do I access currentUser.role here? I need to have its value in $scope.login just after login and just before redirecting so that I can redirect on basis of its value.
Edit: User service looks like this:
'use strict';
angular.module('createProjectApp')
.factory('User', function ($resource) {
// return $resource('/api/users/:id/:controller', {
return $resource('/api/customUsers/:id/:controller', {
id: '#_id'
},
{
changePassword: {
method: 'PUT',
params: {
controller:'password'
}
},
get: {
method: 'GET',
params: {
id:'me'
}
}
});
});
currentUser when consoled in login looked like this:
User.get() is itself a function that returns a promise. You are going to have to wait on the promise fullfillment before continuing, as in
currentUser.then(function() {
console.log(currentUser);
role = currentUser.role; //gives undefined
deferred.resolve(data);
return cb();
}
cb() itself is not needed since you are using promises - but you may have something special in mind.

Mock promises with jasmine

I've been struggling with a simple test that involves mocking promises with no luck. I'm using jasmine spies. Hope somebody can help me. I've successfully mocked findByUserName method but there appears to be something wrong with the promise. The tests just fails without any error. Please see my code:
Module CertificationSettingsManager.FindUser being tested:
'use strict';
var CertificationSettingsManagerFindUser = function(usersRepository, accountsRepository, userName, next) {
if (userName) {
usersRepository.findByUserName(userName)
.then(function(user) {
if (user && user.password) {
delete user.password;
}
return user;
})
.then(function(user) {
if (user) {
accountsRepository.findAllAssignedTo(user.userId).then(function(results) {
user.hasAccountsAssigned = (results.count > 0);
user.belongsRepHierarchy = true;
user.isMemberOfReconOrPrep = true;
next(null, user);
});
}
})
.catch(function(err) {
if (err) {
next(err);
}
});
} else {
next();
}
};
module.exports = CertificationSettingsManagerFindUser;
And this is the test spec:
'use strict';
var findUserModule = require('./CertificationSettingsManager.FindUser');
var Q = require('q');
describe('CertificationSettingsManager findUser module', function() {
var userSpy;
var accounstSpy;
var nextCallbackSpy;
var inputUserTemplate;
var accountsResult;
beforeEach(function() {
inputUserTemplate = {
userId: 1234
};
accountsResult = {
count: 0
};
userSpy = jasmine.createSpyObj('UserRepository', ['findByUserName']);
accounstSpy = jasmine.createSpyObj('ProfileRepository', ['findAllAssignedTo']);
nextCallbackSpy = jasmine.createSpy('nextCallback spy');
});
it('when userName was not supplied it should call next with no parameters', function() {
findUserModule(null, null, null, nextCallbackSpy);
expect(nextCallbackSpy).toHaveBeenCalledWith();
});
//done callback passed so it can be asynchronous
it('if user has password, it should remove the password', function(done) {
inputUserTemplate.password = 'there is some password here';
accounstSpy.findAllAssignedTo.and.returnValue(Q.resolve(accountsResult));
userSpy.findByUserName.and.returnValue(Q.resolve(inputUserTemplate));
findUserModule(userSpy, accounstSpy, 'rassiel', function(){
expect(inputUserTemplate.password).toBeUndefined();
// done being called
done();
});
});
});
The second test: it('if user has password, it should remove the password' is failing, but when I try to debug the test it never hits the then inside:
usersRepository.findByUserName(userName)
.then(function(user) {
**UPDATED!!!****
After adding the done callback to the it method, the test is working now... that's what I was missing. Then done should be called after the expects statements.
Is there a better way of doing this?

Angular Testing controller with factory

Hello I have this controller that handles login:
angular.module('fancyApp')
.controller('MainCtrl', function ($scope, $location, $http, LoginFactory) {
$scope.loginForm = function(isValid) {
if (isValid) {
var status;
LoginFactory.login($scope.person).then(function(d) {
console.log(d);
$scope.data = d;
if (d.status === 200) {
//login success
$('.loginFail').hide();
$location.path('/student');
} else {
$scope.loginFail = 'Failed to login';
$('.loginFail').show();
}
});
} else {
$scope.login_form.submitted = true;
}
};
});
And this is my LoginFactory login function:
angular.module('fancyApp')
.factory('LoginFactory', function ($http, $q) {
return {
login: function(user) {
var promise = $http.post( 'api/v1/login', user).then(function (response) {
return response;
}, function(error) {
return error;
});
return promise;
};
});
This is my beforeEach:
beforeEach(inject(function ($controller, $provide, $location, $rootScope) {
location = $location;
rootScope = $rootScope;
var testService = {
getStudents: function() {
return ['student1', 'student2'];
},
getAdmins: function() {
return ['admin1', 'admin2'];
},
login: function(person) {
console.log(testService.getStudents().indexOf(person.user));
if(testService.getStudents().indexOf(person.user) !== -1) {
return {status:200, token:'xxx', role:'student'};
}
else if(testService.getAdmins().indexOf(person.user) !== -1) {
return {status:200, token:'xxx', role:'admin'};
}
else {
return {status:401, token:'xxx', role:'student'};
}
}
};
scope = $rootScope.$new();
$provide.service('LoginService', testService);
MainCtrl = $controller('MainCtrl', {
$scope: scope
});
}));
And here is my test:
it('Login should succeed', inject(function($httpBackend){
spyOn(testService, 'login');
scope.person.user = 'student1';
scope.person.pass = '123456';
scope.login(true);
expect(testService.login).toHaveBeenCalledWith('student1', '123456');
}));
The error I get is:
Error: [$injector:unpr] Unknown provider: $provideProvider < $provide
I'm not sure if this is the correct way of testing this, any suggestions would be nice.
Thank you.
You can't use $provide within the inject function because the former registers providers for the latter to use. Do it like this:
describe('...', function() {
beforeEach(function() {
module(function($provide) {
$provide.service('LoginService', testService);
});
inject(function(someValue) {
//Rest of the code within the inject..........
});
});
});

Categories